From 56ed9c34295a01bceeda761c4e531989f4ea4a13 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Sat, 27 Jan 2018 20:32:27 -0800 Subject: [PATCH] Updated VGMStream to r1050-1001-gebdfed99. --- .../vgmstream.xcodeproj/project.pbxproj | 270 +++++----- .../vgmstream/src/coding/atrac9_decoder.c | 4 +- .../vgmstream/src/coding/ffmpeg_decoder.c | 6 - .../src/coding/ffmpeg_decoder_utils.h | 4 - .../coding/ffmpeg_decoder_utils_bgw_atrac3.c | 58 --- .../src/coding/vorbis_custom_utils_wwise.c | 20 +- Frameworks/vgmstream/vgmstream/src/formats.c | 41 +- .../vgmstream/vgmstream/src/layout/blocked.c | 3 + .../vgmstream/src/layout/blocked_ea_schl.c | 6 +- .../vgmstream/src/layout/blocked_xvag.c | 19 + .../vgmstream/vgmstream/src/layout/layout.h | 1 + .../vgmstream/src/meta/aax_streamfile.h | 4 - Frameworks/vgmstream/vgmstream/src/meta/adx.c | 2 +- .../vgmstream/vgmstream/src/meta/adx_keys.h | 3 + Frameworks/vgmstream/vgmstream/src/meta/ahx.c | 2 +- .../vgmstream/src/meta/aix_streamfile.h | 4 - .../vgmstream/vgmstream/src/meta/atsl3.c | 76 +++ Frameworks/vgmstream/vgmstream/src/meta/atx.c | 109 ++++ Frameworks/vgmstream/vgmstream/src/meta/awc.c | 19 +- .../vgmstream/src/meta/bar_streamfile.h | 13 - Frameworks/vgmstream/vgmstream/src/meta/bgw.c | 103 +++- Frameworks/vgmstream/vgmstream/src/meta/bik.c | 41 +- Frameworks/vgmstream/vgmstream/src/meta/flx.c | 19 +- Frameworks/vgmstream/vgmstream/src/meta/fsb.c | 356 +++++++------ .../vgmstream/vgmstream/src/meta/fsb5.c | 19 +- .../vgmstream/src/meta/fsb_encrypted.c | 158 ++++++ .../vgmstream/vgmstream/src/meta/fsb_keys.h | 126 +++++ Frameworks/vgmstream/vgmstream/src/meta/gtd.c | 10 +- Frameworks/vgmstream/vgmstream/src/meta/hca.c | 2 +- .../vgmstream/vgmstream/src/meta/hca_keys.h | 6 + .../vgmstream/vgmstream/src/meta/kma9.c | 79 +++ .../vgmstream/vgmstream/src/meta/ktss.c | 19 +- .../vgmstream/vgmstream/src/meta/meta.h | 22 +- .../vgmstream/vgmstream/src/meta/nsw_opus.c | 32 +- .../vgmstream/vgmstream/src/meta/ogg_vorbis.c | 453 ++++++++++++++++ .../vgmstream/src/meta/ogg_vorbis_file.c | 461 ---------------- .../vgmstream/vgmstream/src/meta/ps2_rxws.c | 29 +- .../vgmstream/vgmstream/src/meta/psx_cdxa.c | 27 +- .../vgmstream/vgmstream/src/meta/riff.c | 165 +++++- Frameworks/vgmstream/vgmstream/src/meta/rws.c | 40 +- Frameworks/vgmstream/vgmstream/src/meta/sab.c | 40 +- .../vgmstream/vgmstream/src/meta/sgxd.c | 35 +- .../vgmstream/vgmstream/src/meta/sps_n1.c | 82 +++ .../vgmstream/vgmstream/src/meta/sqex_scd.c | 276 +++++----- .../vgmstream/vgmstream/src/meta/sqex_sead.c | 273 ++++++++++ Frameworks/vgmstream/vgmstream/src/meta/sxd.c | 33 +- .../vgmstream/vgmstream/src/meta/ubi_sb.c | 1 + Frameworks/vgmstream/vgmstream/src/meta/vxn.c | 18 +- .../vgmstream/vgmstream/src/meta/wwise.c | 32 +- .../vgmstream/vgmstream/src/meta/xvag.c | 66 ++- Frameworks/vgmstream/vgmstream/src/meta/xwb.c | 39 +- Frameworks/vgmstream/vgmstream/src/meta/xwc.c | 108 ++++ .../vgmstream/vgmstream/src/streamfile.c | 490 ++++++++++++++++-- .../vgmstream/vgmstream/src/streamfile.h | 51 +- .../vgmstream/vgmstream/src/vgmstream.c | 24 +- .../vgmstream/vgmstream/src/vgmstream.h | 40 +- 56 files changed, 3097 insertions(+), 1342 deletions(-) delete mode 100644 Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_utils_bgw_atrac3.c create mode 100644 Frameworks/vgmstream/vgmstream/src/layout/blocked_xvag.c create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/atsl3.c create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/atx.c create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/fsb_encrypted.c create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/kma9.c create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c delete mode 100644 Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis_file.c create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/sps_n1.c create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/sqex_sead.c create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/xwc.c diff --git a/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj b/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj index 96cbb0acc..bf528f3fc 100644 --- a/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj +++ b/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj @@ -43,7 +43,6 @@ 83345A521F8AEB2800B2EAA4 /* xvag.c in Sources */ = {isa = PBXBuildFile; fileRef = 83345A4E1F8AEB2800B2EAA4 /* xvag.c */; }; 833A7A2E1ED11961003EC53E /* xau.c in Sources */ = {isa = PBXBuildFile; fileRef = 833A7A2D1ED11961003EC53E /* xau.c */; }; 8349A8DF1FE6251F00E26435 /* vorbis_custom_utils_vid1.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8DC1FE6251E00E26435 /* vorbis_custom_utils_vid1.c */; }; - 8349A8E01FE6251F00E26435 /* ffmpeg_decoder_utils_bgw_atrac3.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8DD1FE6251F00E26435 /* ffmpeg_decoder_utils_bgw_atrac3.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 */; }; 8349A8E91FE6253900E26435 /* blocked_ea_1snh.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8E31FE6253800E26435 /* blocked_ea_1snh.c */; }; @@ -221,7 +220,6 @@ 836F6FBA18BDC2190095E648 /* ngc_ymf.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E7E18BDC2180095E648 /* ngc_ymf.c */; }; 836F6FBB18BDC2190095E648 /* ngca.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E7F18BDC2180095E648 /* ngca.c */; }; 836F6FBD18BDC2190095E648 /* nwa.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E8118BDC2180095E648 /* nwa.c */; }; - 836F6FBE18BDC2190095E648 /* ogg_vorbis_file.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E8218BDC2180095E648 /* ogg_vorbis_file.c */; }; 836F6FBF18BDC2190095E648 /* otm.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E8318BDC2180095E648 /* otm.c */; }; 836F6FC018BDC2190095E648 /* p3d.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E8418BDC2180095E648 /* p3d.c */; }; 836F6FC118BDC2190095E648 /* pc_adp.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E8518BDC2180095E648 /* pc_adp.c */; }; @@ -405,6 +403,16 @@ 839E21E81F2EDAF100EE54D7 /* mpeg_decoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 839E21DE1F2EDAF000EE54D7 /* mpeg_decoder.h */; }; 839E21E91F2EDAF100EE54D7 /* vorbis_custom_utils_sk.c in Sources */ = {isa = PBXBuildFile; fileRef = 839E21DF1F2EDAF000EE54D7 /* vorbis_custom_utils_sk.c */; }; 839E21EB1F2EDB0600EE54D7 /* sk_aud.c in Sources */ = {isa = PBXBuildFile; fileRef = 839E21EA1F2EDB0500EE54D7 /* sk_aud.c */; }; + 83A21F7B201D895B000F04B9 /* blocked_xvag.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A21F7A201D895B000F04B9 /* blocked_xvag.c */; }; + 83A21F85201D8981000F04B9 /* atx.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A21F7C201D897F000F04B9 /* atx.c */; }; + 83A21F86201D8981000F04B9 /* xwc.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A21F7D201D8980000F04B9 /* xwc.c */; }; + 83A21F87201D8981000F04B9 /* fsb_keys.h in Headers */ = {isa = PBXBuildFile; fileRef = 83A21F7E201D8980000F04B9 /* fsb_keys.h */; }; + 83A21F88201D8981000F04B9 /* ogg_vorbis.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A21F7F201D8980000F04B9 /* ogg_vorbis.c */; }; + 83A21F89201D8982000F04B9 /* atsl3.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A21F80201D8980000F04B9 /* atsl3.c */; }; + 83A21F8A201D8982000F04B9 /* fsb_encrypted.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A21F81201D8981000F04B9 /* fsb_encrypted.c */; }; + 83A21F8B201D8982000F04B9 /* sps_n1.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A21F82201D8981000F04B9 /* sps_n1.c */; }; + 83A21F8C201D8982000F04B9 /* kma9.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A21F83201D8981000F04B9 /* kma9.c */; }; + 83A21F8D201D8982000F04B9 /* sqex_sead.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A21F84201D8981000F04B9 /* sqex_sead.c */; }; 83A3F0741E3AD8B900D6A794 /* formats.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A3F0711E3AD8B900D6A794 /* formats.c */; }; 83A3F0761E3AD8B900D6A794 /* stack_alloc.h in Headers */ = {isa = PBXBuildFile; fileRef = 83A3F0731E3AD8B900D6A794 /* stack_alloc.h */; }; 83A5F75F198DF021009AF94C /* bfwav.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A5F75E198DF021009AF94C /* bfwav.c */; }; @@ -595,7 +603,6 @@ 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 = ""; }; 8349A8DC1FE6251E00E26435 /* vorbis_custom_utils_vid1.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vorbis_custom_utils_vid1.c; sourceTree = ""; }; - 8349A8DD1FE6251F00E26435 /* ffmpeg_decoder_utils_bgw_atrac3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ffmpeg_decoder_utils_bgw_atrac3.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 = ""; }; 8349A8E31FE6253800E26435 /* blocked_ea_1snh.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_ea_1snh.c; sourceTree = ""; }; @@ -775,7 +782,6 @@ 836F6E7E18BDC2180095E648 /* ngc_ymf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ngc_ymf.c; sourceTree = ""; }; 836F6E7F18BDC2180095E648 /* ngca.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ngca.c; sourceTree = ""; }; 836F6E8118BDC2180095E648 /* nwa.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nwa.c; sourceTree = ""; }; - 836F6E8218BDC2180095E648 /* ogg_vorbis_file.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ogg_vorbis_file.c; sourceTree = ""; }; 836F6E8318BDC2180095E648 /* otm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = otm.c; sourceTree = ""; }; 836F6E8418BDC2180095E648 /* p3d.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = p3d.c; sourceTree = ""; }; 836F6E8518BDC2180095E648 /* pc_adp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pc_adp.c; sourceTree = ""; }; @@ -957,6 +963,16 @@ 839E21DE1F2EDAF000EE54D7 /* mpeg_decoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mpeg_decoder.h; sourceTree = ""; }; 839E21DF1F2EDAF000EE54D7 /* vorbis_custom_utils_sk.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vorbis_custom_utils_sk.c; sourceTree = ""; }; 839E21EA1F2EDB0500EE54D7 /* sk_aud.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sk_aud.c; sourceTree = ""; }; + 83A21F7A201D895B000F04B9 /* blocked_xvag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_xvag.c; sourceTree = ""; }; + 83A21F7C201D897F000F04B9 /* atx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = atx.c; sourceTree = ""; }; + 83A21F7D201D8980000F04B9 /* xwc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xwc.c; sourceTree = ""; }; + 83A21F7E201D8980000F04B9 /* fsb_keys.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fsb_keys.h; sourceTree = ""; }; + 83A21F7F201D8980000F04B9 /* ogg_vorbis.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ogg_vorbis.c; sourceTree = ""; }; + 83A21F80201D8980000F04B9 /* atsl3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = atsl3.c; sourceTree = ""; }; + 83A21F81201D8981000F04B9 /* fsb_encrypted.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fsb_encrypted.c; sourceTree = ""; }; + 83A21F82201D8981000F04B9 /* sps_n1.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sps_n1.c; sourceTree = ""; }; + 83A21F83201D8981000F04B9 /* kma9.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = kma9.c; sourceTree = ""; }; + 83A21F84201D8981000F04B9 /* sqex_sead.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sqex_sead.c; sourceTree = ""; }; 83A3F0711E3AD8B900D6A794 /* formats.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = formats.c; sourceTree = ""; }; 83A3F0731E3AD8B900D6A794 /* stack_alloc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = stack_alloc.h; sourceTree = ""; }; 83A5F75E198DF021009AF94C /* bfwav.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bfwav.c; sourceTree = ""; }; @@ -1134,51 +1150,40 @@ 836F6DDF18BDC2180095E648 /* coding */ = { isa = PBXGroup; children = ( - 830EBE0F2004655D0023AA10 /* atrac9_decoder.c */, - 8349A8DE1FE6251F00E26435 /* ea_mt_decoder.c */, - 8349A8DD1FE6251F00E26435 /* ffmpeg_decoder_utils_bgw_atrac3.c */, - 8349A8DC1FE6251E00E26435 /* vorbis_custom_utils_vid1.c */, - 83345A491F8AEAF900B2EAA4 /* ffmpeg_decoder_utils_switch_opus.c */, - 8374EE361F787AB500033E90 /* ffmpeg_decoder_utils_ea_xma.c */, - 8374EE3D1F787AB600033E90 /* ffmpeg_decoder_utils.c */, - 8374EE3C1F787AB600033E90 /* ffmpeg_decoder_utils.h */, - 83AA5D0E1F6E2F5F0020821C /* ea_xa_decoder.c */, - 83AA5D151F6E2F600020821C /* ea_xas_decoder.c */, - 83AA5D141F6E2F600020821C /* mpeg_custom_utils_awc.c */, - 83AA5D131F6E2F5F0020821C /* mpeg_custom_utils_ealayer3.c */, - 83E56BA01F2EE3500026BC60 /* vorbis_custom_utils_ogl.c */, - 839E21D91F2EDAF000EE54D7 /* mpeg_custom_utils_ahx.c */, - 839E21DD1F2EDAF000EE54D7 /* mpeg_custom_utils.c */, - 839E21DE1F2EDAF000EE54D7 /* mpeg_decoder.h */, - 839E21D61F2EDAF000EE54D7 /* vorbis_custom_data_fsb.h */, - 839E21DC1F2EDAF000EE54D7 /* vorbis_custom_data_wwise.h */, - 839E21D71F2EDAF000EE54D7 /* vorbis_custom_decoder.c */, - 839E21DB1F2EDAF000EE54D7 /* vorbis_custom_decoder.h */, - 839E21DA1F2EDAF000EE54D7 /* vorbis_custom_utils_fsb.c */, - 839E21DF1F2EDAF000EE54D7 /* vorbis_custom_utils_sk.c */, - 839E21D81F2EDAF000EE54D7 /* vorbis_custom_utils_wwise.c */, - 835027121ED119E000C25929 /* mta2_decoder.c */, - 83709E0B1ECBC1C3005C03D3 /* mc3_decoder.c */, - 83709E0C1ECBC1C3005C03D3 /* psv_decoder.c */, - 831BA6221EAC61CB00CF89B0 /* coding_utils.c */, - 838BDB6B1D3AFAB10022CA6F /* ffmpeg_decoder.c */, - 832389511D224C0800482226 /* hca_decoder.c */, - 83D7318B1A749EEE00CA1366 /* g719_decoder.c */, 836F6DE018BDC2180095E648 /* acm_decoder.c */, 836F6DE118BDC2180095E648 /* acm_decoder.h */, 836F6DE218BDC2180095E648 /* adx_decoder.c */, 836F6DE318BDC2180095E648 /* aica_decoder.c */, 836F6DE418BDC2180095E648 /* at3_decoder.c */, + 830EBE0F2004655D0023AA10 /* atrac9_decoder.c */, + 831BA6221EAC61CB00CF89B0 /* coding_utils.c */, 836F6DE518BDC2180095E648 /* coding.h */, + 8349A8DE1FE6251F00E26435 /* ea_mt_decoder.c */, + 83AA5D0E1F6E2F5F0020821C /* ea_xa_decoder.c */, + 83AA5D151F6E2F600020821C /* ea_xas_decoder.c */, + 8374EE361F787AB500033E90 /* ffmpeg_decoder_utils_ea_xma.c */, + 83345A491F8AEAF900B2EAA4 /* ffmpeg_decoder_utils_switch_opus.c */, + 8374EE3D1F787AB600033E90 /* ffmpeg_decoder_utils.c */, + 8374EE3C1F787AB600033E90 /* ffmpeg_decoder_utils.h */, + 838BDB6B1D3AFAB10022CA6F /* ffmpeg_decoder.c */, + 836F6DE918BDC2180095E648 /* g72x_state.h */, + 83D7318B1A749EEE00CA1366 /* g719_decoder.c */, 836F6DE718BDC2180095E648 /* g721_decoder.c */, 836F6DE818BDC2180095E648 /* g7221_decoder.c */, - 836F6DE918BDC2180095E648 /* g72x_state.h */, + 832389511D224C0800482226 /* hca_decoder.c */, 836F6DEA18BDC2180095E648 /* ima_decoder.c */, 836F6DEB18BDC2180095E648 /* l5_555_decoder.c */, 836F6DEC18BDC2180095E648 /* lsf_decoder.c */, + 83709E0B1ECBC1C3005C03D3 /* mc3_decoder.c */, 836F6DEE18BDC2180095E648 /* mp4_aac_decoder.c */, + 839E21D91F2EDAF000EE54D7 /* mpeg_custom_utils_ahx.c */, + 83AA5D141F6E2F600020821C /* mpeg_custom_utils_awc.c */, + 83AA5D131F6E2F5F0020821C /* mpeg_custom_utils_ealayer3.c */, + 839E21DD1F2EDAF000EE54D7 /* mpeg_custom_utils.c */, 836F6DEF18BDC2180095E648 /* mpeg_decoder.c */, + 839E21DE1F2EDAF000EE54D7 /* mpeg_decoder.h */, 836F6DF018BDC2180095E648 /* msadpcm_decoder.c */, + 835027121ED119E000C25929 /* mta2_decoder.c */, 836F6DF118BDC2180095E648 /* mtaf_decoder.c */, 836F6DF218BDC2180095E648 /* nds_procyon_decoder.c */, 836F6DF318BDC2180095E648 /* ngc_afc_decoder.c */, @@ -1188,9 +1193,19 @@ 836F6DF718BDC2180095E648 /* nwa_decoder.h */, 836F6DF818BDC2180095E648 /* ogg_vorbis_decoder.c */, 836F6DF918BDC2180095E648 /* pcm_decoder.c */, + 83709E0C1ECBC1C3005C03D3 /* psv_decoder.c */, 836F6DFA18BDC2180095E648 /* psx_decoder.c */, 836F6DFB18BDC2180095E648 /* SASSC_decoder.c */, 836F6DFC18BDC2180095E648 /* sdx2_decoder.c */, + 839E21D61F2EDAF000EE54D7 /* vorbis_custom_data_fsb.h */, + 839E21DC1F2EDAF000EE54D7 /* vorbis_custom_data_wwise.h */, + 839E21D71F2EDAF000EE54D7 /* vorbis_custom_decoder.c */, + 839E21DB1F2EDAF000EE54D7 /* vorbis_custom_decoder.h */, + 839E21DA1F2EDAF000EE54D7 /* vorbis_custom_utils_fsb.c */, + 83E56BA01F2EE3500026BC60 /* vorbis_custom_utils_ogl.c */, + 839E21DF1F2EDAF000EE54D7 /* vorbis_custom_utils_sk.c */, + 8349A8DC1FE6251E00E26435 /* vorbis_custom_utils_vid1.c */, + 839E21D81F2EDAF000EE54D7 /* vorbis_custom_utils_wwise.c */, 836F6DFD18BDC2180095E648 /* ws_decoder.c */, 836F6DFE18BDC2180095E648 /* xa_decoder.c */, ); @@ -1200,26 +1215,26 @@ 836F6DFF18BDC2180095E648 /* layout */ = { isa = PBXGroup; children = ( + 836F6E0018BDC2180095E648 /* aax_layout.c */, + 836F6E0118BDC2180095E648 /* aix_layout.c */, + 836F6E0218BDC2180095E648 /* ast_blocked.c */, + 836F6E0318BDC2180095E648 /* bdsp_blocked.c */, + 83AA5D1B1F6E2F7F0020821C /* blocked_awc.c */, 8349A8E21FE6253800E26435 /* blocked_dec.c */, 8349A8E31FE6253800E26435 /* blocked_ea_1snh.c */, 8349A8E41FE6253800E26435 /* blocked_ea_schl.c */, 8349A8E71FE6253900E26435 /* blocked_ea_sns.c */, 8349A8E51FE6253800E26435 /* blocked_ivaud.c */, 8349A8E61FE6253900E26435 /* blocked_vawx.c */, - 83AA5D1B1F6E2F7F0020821C /* blocked_awc.c */, 83AA5D1A1F6E2F7F0020821C /* blocked_vgs.c */, - 830165A11F256BF400CA0941 /* hwas_blocked.c */, - 831BD1201EEE1D2A00198540 /* rws_blocked.c */, - 836F6E0018BDC2180095E648 /* aax_layout.c */, - 836F6E0118BDC2180095E648 /* aix_layout.c */, - 836F6E0218BDC2180095E648 /* ast_blocked.c */, - 836F6E0318BDC2180095E648 /* bdsp_blocked.c */, + 83A21F7A201D895B000F04B9 /* blocked_xvag.c */, 836F6E0418BDC2180095E648 /* blocked.c */, 836F6E0518BDC2180095E648 /* caf_blocked.c */, 836F6E0818BDC2180095E648 /* emff_blocked.c */, 836F6E0918BDC2180095E648 /* filp_blocked.c */, 836F6E0A18BDC2180095E648 /* gsb_blocked.c */, 836F6E0B18BDC2180095E648 /* halpst_blocked.c */, + 830165A11F256BF400CA0941 /* hwas_blocked.c */, 836F6E0C18BDC2180095E648 /* ims_block.c */, 836F6E0D18BDC2180095E648 /* interleave.c */, 836F6E1018BDC2180095E648 /* layout.h */, @@ -1230,6 +1245,7 @@ 836F6E1618BDC2180095E648 /* ps2_iab_blocked.c */, 836F6E1718BDC2180095E648 /* ps2_strlr_blocked.c */, 836F6E1818BDC2180095E648 /* psx_mgav_blocked.c */, + 831BD1201EEE1D2A00198540 /* rws_blocked.c */, 836F6E1918BDC2180095E648 /* scd_int_layout.c */, 836F6E1A18BDC2180095E648 /* str_snds_blocked.c */, 836F6E1B18BDC2180095E648 /* thp_blocked.c */, @@ -1246,95 +1262,36 @@ 836F6E2718BDC2180095E648 /* meta */ = { isa = PBXGroup; children = ( - 830EBE122004656E0023AA10 /* ktss.c */, - 830EBE112004656E0023AA10 /* xnb.c */, - 8349A9041FE6258100E26435 /* aax_streamfile.h */, - 8349A9021FE6258100E26435 /* adx_keys.h */, - 8349A9001FE6258000E26435 /* afc.c */, - 8349A8F61FE6257E00E26435 /* aix_streamfile.h */, - 8349A8F81FE6257E00E26435 /* bar_streamfile.h */, - 8349A9051FE6258100E26435 /* bar.c */, - 8349A8EE1FE6257C00E26435 /* dec.c */, - 8349A8FF1FE6258000E26435 /* ea_1snh.c */, - 8349A8F71FE6257E00E26435 /* ea_eaac.c */, - 8349A8EF1FE6257C00E26435 /* ezw.c */, - 8349A8FD1FE6257F00E26435 /* flx.c */, - 8349A9031FE6258100E26435 /* mogg.c */, - 8349A9061FE6258100E26435 /* naac.c */, - 8349A8FA1FE6257E00E26435 /* ngc_vid1.c */, - 8349A8FB1FE6257F00E26435 /* omu.c */, - 8349A8FE1FE6257F00E26435 /* pc_adp_otns.c */, - 8349A8F01FE6257C00E26435 /* pc_ast.c */, - 8349A8F21FE6257D00E26435 /* ps2_pcm.c */, - 8349A8FC1FE6257F00E26435 /* ps2_xa2_rrp.c */, - 8349A8F11FE6257D00E26435 /* sab.c */, - 8349A8F51FE6257D00E26435 /* scd_pcm.c */, - 8349A8F31FE6257D00E26435 /* sqex_scd_streamfile.h */, - 8349A8F41FE6257D00E26435 /* ubi_sb.c */, - 8349A8F91FE6257E00E26435 /* vsf_tta.c */, - 8349A9011FE6258000E26435 /* vxn.c */, - 83345A4D1F8AEB2800B2EAA4 /* nsw_opus.c */, - 83345A4B1F8AEB2700B2EAA4 /* nub_xma.c */, - 83345A4C1F8AEB2700B2EAA4 /* pc_al2.c */, - 83345A4E1F8AEB2800B2EAA4 /* xvag.c */, - 83AA5D201F6E2F9B0020821C /* awc.c */, - 83AA5D211F6E2F9C0020821C /* hca_keys.h */, - 83AA5D231F6E2F9C0020821C /* stm.c */, - 839E21EA1F2EDB0500EE54D7 /* sk_aud.c */, - 830165971F256BD000CA0941 /* txth.c */, - 830165981F256BD000CA0941 /* ea_schl_fixed.c */, - 830165991F256BD000CA0941 /* nds_strm_ffta2.c */, - 83CAB8DC1F0B0744001BC993 /* pc_xa30.c */, - 83CAB8E11F0B0745001BC993 /* wii_04sw.c */, - 831BD11F1EEE1CF200198540 /* ngc_ulw.c */, - 8350270C1ED119D200C25929 /* ps3_mta2.c */, - 833A7A2D1ED11961003EC53E /* xau.c */, - 83709DFF1ECBC1A4005C03D3 /* gtd.c */, - 83709E001ECBC1A4005C03D3 /* mc3.c */, - 83709E011ECBC1A4005C03D3 /* mss.c */, - 83709E021ECBC1A4005C03D3 /* ps2_rxws.c */, - 83709E031ECBC1A4005C03D3 /* ta_aac.c */, - 83709E041ECBC1A4005C03D3 /* waa_wac_wad_wam.c */, - 831BA60E1EAC61A500CF89B0 /* adx.c */, - 831BA60F1EAC61A500CF89B0 /* ogl.c */, - 831BA6101EAC61A500CF89B0 /* ps2_vds_vdm.c */, - 831BA6111EAC61A500CF89B0 /* sgxd.c */, - 831BA6121EAC61A500CF89B0 /* sxd.c */, - 831BA6131EAC61A500CF89B0 /* ubi_raki.c */, - 831BA6141EAC61A500CF89B0 /* vawx.c */, - 831BA6151EAC61A500CF89B0 /* x360_cxs.c */, - 831BA6171EAC61A500CF89B0 /* x360_pasx.c */, - 83FF0EBB1E93282100C58054 /* wwise.c */, - 83AB8C731E8072A100086084 /* nub_vag.c */, - 83AB8C741E8072A100086084 /* x360_ast.c */, - 83299FCE1E7660C7003A3242 /* bik.c */, - 83299FCF1E7660C7003A3242 /* dsp_adx.c */, - 8350C0591E071990009E0A93 /* ps2_svag_snk.c */, - 8350C0541E071881009E0A93 /* xma.c */, - 838BDB6D1D3B043C0022CA6F /* ffmpeg.c */, - 8323894F1D2246C300482226 /* hca.c */, - 83EDE5D61A70951A005F5D84 /* mca.c */, - 83EDE5D71A70951A005F5D84 /* btsnd.c */, - 834D3A6D19F47C98001C54F6 /* g1l.c */, - 83BAFB6B19F45EB3005DAB60 /* bfstm.c */, - 83A5F75E198DF021009AF94C /* bfwav.c */, - 83F5F8821908D0A400C8E65F /* fsb5.c */, 836F6E2918BDC2180095E648 /* 2dx9.c */, + 8349A9041FE6258100E26435 /* aax_streamfile.h */, 836F6E2A18BDC2180095E648 /* aax.c */, 836F6E2B18BDC2180095E648 /* acm.c */, 836F6E2C18BDC2180095E648 /* ads.c */, + 8349A9021FE6258100E26435 /* adx_keys.h */, + 831BA60E1EAC61A500CF89B0 /* adx.c */, + 8349A9001FE6258000E26435 /* afc.c */, 836F6E2F18BDC2180095E648 /* agsc.c */, 836F6E3018BDC2180095E648 /* ahx.c */, 836F6E3118BDC2180095E648 /* aifc.c */, + 8349A8F61FE6257E00E26435 /* aix_streamfile.h */, 836F6E3218BDC2180095E648 /* aix.c */, 836F6E3318BDC2180095E648 /* akb.c */, 836F6E3418BDC2180095E648 /* apple_caff.c */, 836F6E3518BDC2180095E648 /* ast.c */, + 83A21F80201D8980000F04B9 /* atsl3.c */, + 83A21F7C201D897F000F04B9 /* atx.c */, + 83AA5D201F6E2F9B0020821C /* awc.c */, 836F6E3618BDC2180095E648 /* baf.c */, + 8349A8F81FE6257E00E26435 /* bar_streamfile.h */, + 8349A9051FE6258100E26435 /* bar.c */, 836F6E3718BDC2180095E648 /* bcstm.c */, + 83BAFB6B19F45EB3005DAB60 /* bfstm.c */, + 83A5F75E198DF021009AF94C /* bfwav.c */, 836F6E3818BDC2180095E648 /* bgw.c */, + 83299FCE1E7660C7003A3242 /* bik.c */, 836F6E3918BDC2180095E648 /* bnsf.c */, 836F6E3A18BDC2180095E648 /* brstm.c */, + 83EDE5D71A70951A005F5D84 /* btsnd.c */, 836F6E3B18BDC2180095E648 /* capdsp.c */, 836F6E3C18BDC2180095E648 /* Cstr.c */, 836F6E3D18BDC2180095E648 /* dc_asd.c */, @@ -1342,43 +1299,66 @@ 836F6E3F18BDC2180095E648 /* dc_idvi.c */, 836F6E4018BDC2180095E648 /* dc_kcey.c */, 836F6E4118BDC2180095E648 /* dc_str.c */, + 8349A8EE1FE6257C00E26435 /* dec.c */, 836F6E4318BDC2180095E648 /* dmsg_segh.c */, + 83299FCF1E7660C7003A3242 /* dsp_adx.c */, 836F6E4418BDC2180095E648 /* dsp_bdsp.c */, 836F6E4518BDC2180095E648 /* dsp_sth_str.c */, + 8349A8FF1FE6258000E26435 /* ea_1snh.c */, + 8349A8F71FE6257E00E26435 /* ea_eaac.c */, + 830165981F256BD000CA0941 /* ea_schl_fixed.c */, 836F6E4618BDC2180095E648 /* ea_schl.c */, 836F6E4818BDC2180095E648 /* emff.c */, 836F6E4918BDC2180095E648 /* exakt_sc.c */, 836F6E4A18BDC2180095E648 /* excitebots.c */, + 8349A8EF1FE6257C00E26435 /* ezw.c */, + 838BDB6D1D3B043C0022CA6F /* ffmpeg.c */, 836F6E4B18BDC2180095E648 /* ffw.c */, + 8349A8FD1FE6257F00E26435 /* flx.c */, + 83A21F81201D8981000F04B9 /* fsb_encrypted.c */, + 83A21F7E201D8980000F04B9 /* fsb_keys.h */, 836F6E4C18BDC2180095E648 /* fsb.c */, + 83F5F8821908D0A400C8E65F /* fsb5.c */, + 834D3A6D19F47C98001C54F6 /* g1l.c */, 836F6E4D18BDC2180095E648 /* gca.c */, 836F6E4E18BDC2180095E648 /* gcsw.c */, 836F6E4F18BDC2180095E648 /* genh.c */, 836F6E5118BDC2180095E648 /* gsp_gsb.c */, + 83709DFF1ECBC1A4005C03D3 /* gtd.c */, 836F6E5218BDC2180095E648 /* halpst.c */, + 83AA5D211F6E2F9C0020821C /* hca_keys.h */, + 8323894F1D2246C300482226 /* hca.c */, 836F6E5318BDC2180095E648 /* his.c */, 836F6E5418BDC2180095E648 /* idsp.c */, 836F6E5518BDC2180095E648 /* ios_psnd.c */, 836F6E5618BDC2180095E648 /* ish_isd.c */, 836F6E5718BDC2180095E648 /* ivaud.c */, 836F6E5818BDC2180095E648 /* ivb.c */, + 83A21F83201D8981000F04B9 /* kma9.c */, 836F6E5918BDC2180095E648 /* kraw.c */, + 830EBE122004656E0023AA10 /* ktss.c */, 836F6E5A18BDC2180095E648 /* lsf.c */, 836F6E5C18BDC2180095E648 /* mattel_hyperscan.c */, 836F6E5D18BDC2180095E648 /* maxis_xa.c */, + 83709E001ECBC1A4005C03D3 /* mc3.c */, + 83EDE5D61A70951A005F5D84 /* mca.c */, 836F6E5E18BDC2180095E648 /* meta.h */, 836F6E5F18BDC2180095E648 /* mn_str.c */, + 8349A9031FE6258100E26435 /* mogg.c */, 836F6E6018BDC2180095E648 /* mp4.c */, + 83709E011ECBC1A4005C03D3 /* mss.c */, 836F6E6118BDC2180095E648 /* msvp.c */, 836F6E6218BDC2180095E648 /* mus_acm.c */, 836F6E6318BDC2180095E648 /* musc.c */, 836F6E6418BDC2180095E648 /* musx.c */, 836F6E6518BDC2180095E648 /* myspd.c */, + 8349A9061FE6258100E26435 /* naac.c */, 836F6E6618BDC2180095E648 /* naomi_adpcm.c */, 836F6E6718BDC2180095E648 /* naomi_spsd.c */, 836F6E6818BDC2180095E648 /* nds_hwas.c */, 836F6E6918BDC2180095E648 /* nds_rrds.c */, 836F6E6A18BDC2180095E648 /* nds_sad.c */, + 830165991F256BD000CA0941 /* nds_strm_ffta2.c */, 836F6E6B18BDC2180095E648 /* nds_strm.c */, 836F6E6C18BDC2180095E648 /* nds_swav.c */, 836F6E6D18BDC2180095E648 /* ngc_adpdtk.c */, @@ -1397,16 +1377,27 @@ 836F6E7A18BDC2180095E648 /* ngc_sck_dsp.c */, 836F6E7B18BDC2180095E648 /* ngc_ssm.c */, 836F6E7C18BDC2180095E648 /* ngc_tydsp.c */, + 831BD11F1EEE1CF200198540 /* ngc_ulw.c */, + 8349A8FA1FE6257E00E26435 /* ngc_vid1.c */, 836F6E7E18BDC2180095E648 /* ngc_ymf.c */, 836F6E7F18BDC2180095E648 /* ngca.c */, + 83345A4D1F8AEB2800B2EAA4 /* nsw_opus.c */, + 83AB8C731E8072A100086084 /* nub_vag.c */, + 83345A4B1F8AEB2700B2EAA4 /* nub_xma.c */, 836F6E8118BDC2180095E648 /* nwa.c */, - 836F6E8218BDC2180095E648 /* ogg_vorbis_file.c */, + 83A21F7F201D8980000F04B9 /* ogg_vorbis.c */, + 831BA60F1EAC61A500CF89B0 /* ogl.c */, + 8349A8FB1FE6257F00E26435 /* omu.c */, 836F6E8318BDC2180095E648 /* otm.c */, 836F6E8418BDC2180095E648 /* p3d.c */, + 8349A8FE1FE6257F00E26435 /* pc_adp_otns.c */, 836F6E8518BDC2180095E648 /* pc_adp.c */, + 83345A4C1F8AEB2700B2EAA4 /* pc_al2.c */, + 8349A8F01FE6257C00E26435 /* pc_ast.c */, 836F6E8618BDC2180095E648 /* pc_mxst.c */, 836F6E8718BDC2180095E648 /* pc_smp.c */, 836F6E8818BDC2180095E648 /* pc_snds.c */, + 83CAB8DC1F0B0744001BC993 /* pc_xa30.c */, 836F6E8B18BDC2180095E648 /* pona.c */, 836F6E8C18BDC2180095E648 /* pos.c */, 836F6E8D18BDC2180095E648 /* ps2_2pfs.c */, @@ -1446,12 +1437,13 @@ 836F6EB018BDC2180095E648 /* ps2_mtaf.c */, 836F6EB118BDC2180095E648 /* ps2_npsf.c */, 836F6EB218BDC2180095E648 /* ps2_p2bt.c */, + 8349A8F21FE6257D00E26435 /* ps2_pcm.c */, 836F6EB318BDC2180095E648 /* ps2_pnb.c */, 836F6EB418BDC2180095E648 /* ps2_psh.c */, 836F6EB518BDC2180095E648 /* ps2_psw.c */, 836F6EB618BDC2180095E648 /* ps2_rnd.c */, 836F6EB718BDC2180095E648 /* ps2_rstm.c */, - 836F6EB818BDC2180095E648 /* rws.c */, + 83709E021ECBC1A4005C03D3 /* ps2_rxws.c */, 836F6EBA18BDC2180095E648 /* ps2_sfs.c */, 836F6EBB18BDC2180095E648 /* ps2_sl3.c */, 836F6EBC18BDC2180095E648 /* ps2_smpl.c */, @@ -1461,12 +1453,14 @@ 836F6EC018BDC2190095E648 /* ps2_ster.c */, 836F6EC218BDC2190095E648 /* ps2_str.c */, 836F6EC318BDC2190095E648 /* ps2_strlr.c */, + 8350C0591E071990009E0A93 /* ps2_svag_snk.c */, 836F6EC418BDC2190095E648 /* ps2_svag.c */, 836F6EC518BDC2190095E648 /* ps2_tec.c */, 836F6EC618BDC2190095E648 /* ps2_tk5.c */, 836F6EC718BDC2190095E648 /* ps2_vag.c */, 836F6EC818BDC2190095E648 /* ps2_vas.c */, 836F6EC918BDC2190095E648 /* ps2_vbk.c */, + 831BA6101EAC61A500CF89B0 /* ps2_vds_vdm.c */, 836F6ECA18BDC2190095E648 /* ps2_vgs.c */, 836F6ECB18BDC2190095E648 /* ps2_vgv.c */, 836F6ECC18BDC2190095E648 /* ps2_vms.c */, @@ -1475,12 +1469,14 @@ 836F6ECF18BDC2190095E648 /* ps2_wad.c */, 836F6ED018BDC2190095E648 /* ps2_wb.c */, 836F6ED118BDC2190095E648 /* ps2_wmus.c */, + 8349A8FC1FE6257F00E26435 /* ps2_xa2_rrp.c */, 836F6ED218BDC2190095E648 /* ps2_xa2.c */, 836F6ED318BDC2190095E648 /* ps2_xa30.c */, 836F6ED518BDC2190095E648 /* ps3_cps.c */, 836F6ED618BDC2190095E648 /* ps3_ivag.c */, 836F6ED718BDC2190095E648 /* ps3_klbs.c */, 836F6ED818BDC2190095E648 /* ps3_msf.c */, + 8350270C1ED119D200C25929 /* ps3_mta2.c */, 836F6ED918BDC2190095E648 /* ps3_past.c */, 836F6EDD18BDC2190095E648 /* psx_cdxa.c */, 836F6EDE18BDC2190095E648 /* psx_fag.c */, @@ -1493,29 +1489,48 @@ 836F6EE518BDC2190095E648 /* rs03.c */, 836F6EE618BDC2190095E648 /* rsd.c */, 836F6EE718BDC2190095E648 /* rsf.c */, + 836F6EB818BDC2180095E648 /* rws.c */, 836F6EE818BDC2190095E648 /* rwsd.c */, 836F6EE918BDC2190095E648 /* rwx.c */, 836F6EEA18BDC2190095E648 /* s14_sss.c */, + 8349A8F11FE6257D00E26435 /* sab.c */, 836F6EEB18BDC2190095E648 /* sat_baka.c */, 836F6EEC18BDC2190095E648 /* sat_dvi.c */, 836F6EED18BDC2190095E648 /* sat_sap.c */, + 8349A8F51FE6257D00E26435 /* scd_pcm.c */, 836F6EEE18BDC2190095E648 /* sd9.c */, 836F6EEF18BDC2190095E648 /* sdt.c */, 836F6EF018BDC2190095E648 /* seg.c */, 836F6EF118BDC2190095E648 /* sfl.c */, + 831BA6111EAC61A500CF89B0 /* sgxd.c */, + 839E21EA1F2EDB0500EE54D7 /* sk_aud.c */, 836F6EF218BDC2190095E648 /* sli.c */, + 83A21F82201D8981000F04B9 /* sps_n1.c */, 836F6EF318BDC2190095E648 /* spt_spd.c */, + 8349A8F31FE6257D00E26435 /* sqex_scd_streamfile.h */, 836F6EF418BDC2190095E648 /* sqex_scd.c */, + 83A21F84201D8981000F04B9 /* sqex_sead.c */, + 83AA5D231F6E2F9C0020821C /* stm.c */, 836F6EF618BDC2190095E648 /* str_asr.c */, 836F6EF718BDC2190095E648 /* str_snds.c */, 836F6EF818BDC2190095E648 /* stx.c */, 836F6EF918BDC2190095E648 /* svs.c */, + 831BA6121EAC61A500CF89B0 /* sxd.c */, + 83709E031ECBC1A4005C03D3 /* ta_aac.c */, 836F6EFA18BDC2190095E648 /* thp.c */, 836F6EFB18BDC2190095E648 /* tun.c */, + 830165971F256BD000CA0941 /* txth.c */, 836F6EFC18BDC2190095E648 /* ubi_ckd.c */, + 831BA6131EAC61A500CF89B0 /* ubi_raki.c */, + 8349A8F41FE6257D00E26435 /* ubi_sb.c */, + 831BA6141EAC61A500CF89B0 /* vawx.c */, 836F6EFD18BDC2190095E648 /* vgs.c */, 836F6EFE18BDC2190095E648 /* vs.c */, + 8349A8F91FE6257E00E26435 /* vsf_tta.c */, 836F6EFF18BDC2190095E648 /* vsf.c */, + 8349A9011FE6258000E26435 /* vxn.c */, + 83709E041ECBC1A4005C03D3 /* waa_wac_wad_wam.c */, + 83CAB8E11F0B0745001BC993 /* wii_04sw.c */, 836F6F0018BDC2190095E648 /* wii_bns.c */, 836F6F0118BDC2190095E648 /* wii_mus.c */, 836F6F0218BDC2190095E648 /* wii_ras.c */, @@ -1526,15 +1541,24 @@ 836F6F0718BDC2190095E648 /* wpd.c */, 836F6F0818BDC2190095E648 /* ws_aud.c */, 836F6F0918BDC2190095E648 /* wvs.c */, + 83FF0EBB1E93282100C58054 /* wwise.c */, + 83AB8C741E8072A100086084 /* x360_ast.c */, + 831BA6151EAC61A500CF89B0 /* x360_cxs.c */, + 831BA6171EAC61A500CF89B0 /* x360_pasx.c */, 836F6F0A18BDC2190095E648 /* x360_tra.c */, + 833A7A2D1ED11961003EC53E /* xau.c */, 836F6F0B18BDC2190095E648 /* xbox_hlwav.c */, 836F6F0C18BDC2190095E648 /* xbox_ims.c */, 836F6F0E18BDC2190095E648 /* xbox_wavm.c */, 836F6F0F18BDC2190095E648 /* xbox_xmu.c */, 836F6F1018BDC2190095E648 /* xbox_xvas.c */, 836F6F1118BDC2190095E648 /* xbox_xwav.c */, + 8350C0541E071881009E0A93 /* xma.c */, + 830EBE112004656E0023AA10 /* xnb.c */, 836F6F1218BDC2190095E648 /* xss.c */, + 83345A4E1F8AEB2800B2EAA4 /* xvag.c */, 836F6F1318BDC2190095E648 /* xwb.c */, + 83A21F7D201D8980000F04B9 /* xwc.c */, 836F6F1418BDC2190095E648 /* ydsp.c */, 836F6F1518BDC2190095E648 /* zsd.c */, 836F6F1618BDC2190095E648 /* zwdsp.c */, @@ -1580,6 +1604,7 @@ 836F6F1F18BDC2190095E648 /* acm_decoder.h in Headers */, 836F6F3518BDC2190095E648 /* nwa_decoder.h in Headers */, 8349A91D1FE6258200E26435 /* aax_streamfile.h in Headers */, + 83A21F87201D8981000F04B9 /* fsb_keys.h in Headers */, 8349A90F1FE6258200E26435 /* aix_streamfile.h in Headers */, 8349A9111FE6258200E26435 /* bar_streamfile.h in Headers */, 836F6F2718BDC2190095E648 /* g72x_state.h in Headers */, @@ -1775,9 +1800,9 @@ 83CAB8E31F0B0755001BC993 /* pc_xa30.c in Sources */, 83CAB8E21F0B0752001BC993 /* wii_04sw.c in Sources */, 8349A8DF1FE6251F00E26435 /* vorbis_custom_utils_vid1.c in Sources */, + 83A21F8D201D8982000F04B9 /* sqex_sead.c in Sources */, 839B54571EEE1DA000048A2D /* rws_blocked.c in Sources */, 839B54521EEE1D9600048A2D /* ngc_ulw.c in Sources */, - 8349A8E01FE6251F00E26435 /* ffmpeg_decoder_utils_bgw_atrac3.c in Sources */, 836F6FAD18BDC2190095E648 /* ngc_dsp_konami.c in Sources */, 836F6FF818BDC2190095E648 /* ps2_smpl.c in Sources */, 836F6F8118BDC2190095E648 /* dsp_sth_str.c in Sources */, @@ -1873,6 +1898,7 @@ 836F6FEC18BDC2190095E648 /* ps2_mtaf.c in Sources */, 83D7318C1A749EEE00CA1366 /* g719_decoder.c in Sources */, 836F701118BDC2190095E648 /* ps3_cps.c in Sources */, + 83A21F85201D8981000F04B9 /* atx.c in Sources */, 836F701418BDC2190095E648 /* ps3_msf.c in Sources */, 836F6F6518BDC2190095E648 /* 2dx9.c in Sources */, 830EBE102004655D0023AA10 /* atrac9_decoder.c in Sources */, @@ -1997,6 +2023,7 @@ 836F701518BDC2190095E648 /* ps3_past.c in Sources */, 836F6F7C18BDC2190095E648 /* dc_kcey.c in Sources */, 836F6FED18BDC2190095E648 /* ps2_npsf.c in Sources */, + 83A21F8B201D8982000F04B9 /* sps_n1.c in Sources */, 836F6F9E18BDC2190095E648 /* mus_acm.c in Sources */, 83709E0E1ECBC1C3005C03D3 /* psv_decoder.c in Sources */, 831BA6191EAC61A500CF89B0 /* ogl.c in Sources */, @@ -2033,6 +2060,7 @@ 836F6F8918BDC2190095E648 /* gca.c in Sources */, 836F6F5718BDC2190095E648 /* str_snds_blocked.c in Sources */, 836F6FA418BDC2190095E648 /* nds_hwas.c in Sources */, + 83A21F8A201D8982000F04B9 /* fsb_encrypted.c in Sources */, 836F6FD018BDC2190095E648 /* ps2_b1s.c in Sources */, 836F701218BDC2190095E648 /* ps3_ivag.c in Sources */, 83AA5D181F6E2F600020821C /* mpeg_custom_utils_awc.c in Sources */, @@ -2052,6 +2080,7 @@ 836F6F9518BDC2190095E648 /* kraw.c in Sources */, 836F6FB718BDC2190095E648 /* ngc_ssm.c in Sources */, 83709E051ECBC1A4005C03D3 /* gtd.c in Sources */, + 83A21F86201D8981000F04B9 /* xwc.c in Sources */, 83AA5D1E1F6E2F800020821C /* blocked_awc.c in Sources */, 836F704A18BDC2190095E648 /* xbox_wavm.c in Sources */, 836F6F8618BDC2190095E648 /* excitebots.c in Sources */, @@ -2080,9 +2109,9 @@ 8349A9081FE6258200E26435 /* ezw.c in Sources */, 836F6FE118BDC2190095E648 /* ps2_jstm.c in Sources */, 836F6FD918BDC2190095E648 /* ps2_gcm.c in Sources */, + 83A21F88201D8981000F04B9 /* ogg_vorbis.c in Sources */, 836F6F8E18BDC2190095E648 /* halpst.c in Sources */, 836F6FEE18BDC2190095E648 /* ps2_p2bt.c in Sources */, - 836F6FBE18BDC2190095E648 /* ogg_vorbis_file.c in Sources */, 836F702618BDC2190095E648 /* s14_sss.c in Sources */, 836F6F4818BDC2190095E648 /* halpst_blocked.c in Sources */, 83AA5D1D1F6E2F800020821C /* blocked_vgs.c in Sources */, @@ -2111,6 +2140,7 @@ 836F6FBF18BDC2190095E648 /* otm.c in Sources */, 836F6FDD18BDC2190095E648 /* ps2_ikm.c in Sources */, 836F6FBD18BDC2190095E648 /* nwa.c in Sources */, + 83A21F8C201D8982000F04B9 /* kma9.c in Sources */, 836F6FC418BDC2190095E648 /* pc_snds.c in Sources */, 836F704E18BDC2190095E648 /* xss.c in Sources */, 836F6FD118BDC2190095E648 /* ps2_bg00.c in Sources */, @@ -2127,7 +2157,9 @@ 836F703518BDC2190095E648 /* svs.c in Sources */, 836F6FA318BDC2190095E648 /* naomi_spsd.c in Sources */, 836F6F9D18BDC2190095E648 /* msvp.c in Sources */, + 83A21F7B201D895B000F04B9 /* blocked_xvag.c in Sources */, 836F6FBA18BDC2190095E648 /* ngc_ymf.c in Sources */, + 83A21F89201D8982000F04B9 /* atsl3.c in Sources */, 836F705018BDC2190095E648 /* ydsp.c in Sources */, 836F702718BDC2190095E648 /* sat_baka.c in Sources */, 83345A4A1F8AEAF900B2EAA4 /* ffmpeg_decoder_utils_switch_opus.c in Sources */, diff --git a/Frameworks/vgmstream/vgmstream/src/coding/atrac9_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/atrac9_decoder.c index eb0b61166..95c8c0020 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/atrac9_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/atrac9_decoder.c @@ -119,7 +119,9 @@ void decode_atrac9(VGMSTREAM *vgmstream, sample * outbuf, int32_t samples_to_do, /* postadjust */ //todo improve switch(data->config.type) { - case ATRAC9_XVAG: /* skip other subsong blocks in XVAG */ + case ATRAC9_XVAG: + case ATRAC9_KMA9: + /* skip other subsong blocks */ if (data->config.interleave_skip && ((stream->offset - stream->channel_start_offset) % data->config.interleave_skip == 0)) { stream->offset += data->config.interleave_skip * (data->config.subsong_skip - 1); } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c index ed3c3d7d2..0e3652626 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c @@ -222,7 +222,6 @@ static int ffmpeg_read(void *opaque, uint8_t *buf, int buf_size) { switch(data->config.type) { case FFMPEG_EA_XMA: ret = ffmpeg_custom_read_eaxma(data, buf, buf_size); break; case FFMPEG_SWITCH_OPUS: ret = ffmpeg_custom_read_switch_opus(data, buf, buf_size); break; - case FFMPEG_BGW_ATRAC3: ret = ffmpeg_custom_read_bgw_atrac3(data, buf, buf_size); break; //case FFMPEG_EA_SCHL: ret = ffmpeg_custom_read_ea_schl(data, buf, buf_size); break; //case FFMPEG_SFH: ret = ffmpeg_custom_read_sfh(data, buf, buf_size); break; default: ret = ffmpeg_custom_read_standard(data, buf, buf_size); break; @@ -291,7 +290,6 @@ static int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence) { switch(data->config.type) { case FFMPEG_EA_XMA: offset = ffmpeg_custom_seek_eaxma(data, offset); break; case FFMPEG_SWITCH_OPUS: offset = ffmpeg_custom_seek_switch_opus(data, offset); break; - case FFMPEG_BGW_ATRAC3: offset = ffmpeg_custom_seek_bgw_atrac3(data, offset); break; //case FFMPEG_EA_SCHL: offset = ffmpeg_custom_seek_ea_schl(data, offset); break; //case FFMPEG_SFH: offset = ffmpeg_custom_seek_sfh(data, offset); break; default: offset = ffmpeg_custom_seek_standard(data, offset); break; @@ -309,7 +307,6 @@ static int64_t ffmpeg_size(ffmpeg_codec_data * data) { switch(data->config.type) { case FFMPEG_EA_XMA: bytes = ffmpeg_custom_size_eaxma(data); break; case FFMPEG_SWITCH_OPUS: bytes = ffmpeg_custom_size_switch_opus(data); break; - case FFMPEG_BGW_ATRAC3: bytes = ffmpeg_custom_size_bgw_atrac3(data); break; //case FFMPEG_EA_SCHL: bytes = ffmpeg_custom_size_ea_schl(data); break; //case FFMPEG_SFH: bytes = ffmpeg_custom_size_sfh(data); break; default: bytes = ffmpeg_custom_size_standard(data); break; @@ -806,9 +803,6 @@ void free_ffmpeg(ffmpeg_codec_data *data) { close_streamfile(data->streamfile); data->streamfile = NULL; } - if (data->config.key) { - free(data->config.key); - } free(data); } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_utils.h b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_utils.h index a7e129123..64461fe9f 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_utils.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_utils.h @@ -31,10 +31,6 @@ int ffmpeg_custom_read_switch_opus(ffmpeg_codec_data *data, uint8_t *buf, int bu int64_t ffmpeg_custom_seek_switch_opus(ffmpeg_codec_data *data, int64_t virtual_offset); int64_t ffmpeg_custom_size_switch_opus(ffmpeg_codec_data *data); -int ffmpeg_custom_read_bgw_atrac3(ffmpeg_codec_data *data, uint8_t *buf, int buf_size); -int64_t ffmpeg_custom_seek_bgw_atrac3(ffmpeg_codec_data *data, int64_t virtual_offset); -int64_t ffmpeg_custom_size_bgw_atrac3(ffmpeg_codec_data *data); - //int ffmpeg_custom_read_ea_schl(ffmpeg_codec_data *data, uint8_t *buf, int buf_size); //int64_t ffmpeg_custom_seek_ea_schl(ffmpeg_codec_data *data, int64_t virtual_offset); //int64_t ffmpeg_custom_size_ea_schl(ffmpeg_codec_data *data); diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_utils_bgw_atrac3.c b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_utils_bgw_atrac3.c deleted file mode 100644 index 5b6bd441d..000000000 --- a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_utils_bgw_atrac3.c +++ /dev/null @@ -1,58 +0,0 @@ -#if 1 -#include "coding.h" -#include "ffmpeg_decoder_utils.h" - -#ifdef VGM_USE_FFMPEG - -#define BGM_ATRAC3_FRAME_SIZE 0xC0 - -/** - * Encrypted ATRAC3 used in BGW (Final Fantasy XI PC). - * Info from Moogle Toolbox: https://sourceforge.net/projects/mogbox/ - */ - -int ffmpeg_custom_read_bgw_atrac3(ffmpeg_codec_data *data, uint8_t *buf, int buf_size) { - int i, ch; - size_t bytes; - size_t block_align = BGM_ATRAC3_FRAME_SIZE * data->config.channels; - - - /* init key: first frame + modified channel header */ - if (data->config.key == NULL) { - data->config.key = malloc(block_align); - if (!data->config.key) return 0; - - read_streamfile(data->config.key, data->real_start, block_align, data->streamfile); - for (ch = 0; ch < data->config.channels; ch++) { - uint32_t xor = get_32bitBE(data->config.key + ch*BGM_ATRAC3_FRAME_SIZE); - put_32bitBE(data->config.key + ch*BGM_ATRAC3_FRAME_SIZE, xor ^ 0xA0024E9F); - } - } - - - /* read normally and unXOR the data */ - bytes = read_streamfile(buf, data->real_offset, buf_size, data->streamfile); - for (i = 0; i < bytes; i++) { - int key_pos = (data->real_offset - data->real_start + i) % block_align; - buf[i] = buf[i] ^ data->config.key[key_pos]; - } - - - data->real_offset += bytes; - return bytes; -} - -int64_t ffmpeg_custom_seek_bgw_atrac3(ffmpeg_codec_data *data, int64_t virtual_offset) { - int64_t seek_virtual_offset = virtual_offset - data->header_size; - - data->real_offset = data->real_start + seek_virtual_offset; - return virtual_offset; -} - -int64_t ffmpeg_custom_size_bgw_atrac3(ffmpeg_codec_data *data) { - return data->real_size + data->header_size; -} - - -#endif -#endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_wwise.c b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_wwise.c index 7fba94174..73ee1508c 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_wwise.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_wwise.c @@ -47,7 +47,7 @@ int vorbis_custom_setup_init_wwise(STREAMFILE *streamFile, off_t start_offset, v size_t header_size, packet_size; vorbis_custom_config cfg = data->config; - if (cfg.setup_type == HEADER_TRIAD) { + if (cfg.setup_type == WWV_HEADER_TRIAD) { /* read 3 Wwise packets with triad (id/comment/setup), each with a Wwise header */ off_t offset = start_offset; @@ -132,17 +132,17 @@ static int get_packet_header(STREAMFILE *streamFile, off_t offset, wwise_header_ /* packet size doesn't include header size */ switch(header_type) { - case TYPE_8: /* size 4+4 */ + case WWV_TYPE_8: /* size 4+4 */ *packet_size = (uint32_t)read_32bit(offset, streamFile); *granulepos = read_32bit(offset+4, streamFile); return 8; - case TYPE_6: /* size 4+2 */ + case WWV_TYPE_6: /* size 4+2 */ *packet_size = (uint16_t)read_16bit(offset, streamFile); *granulepos = read_32bit(offset+2, streamFile); return 6; - case TYPE_2: /* size 2 */ + case WWV_TYPE_2: /* size 2 */ *packet_size = (uint16_t)read_16bit(offset, streamFile); *granulepos = 0; /* granule is an arbitrary unit so we could use offset instead; libvorbis has no actually need it actually */ return 2; @@ -306,7 +306,7 @@ static int ww2ogg_generate_vorbis_packet(vgm_bitstream * ow, vgm_bitstream * iw, //VGM_ASSERT(granule < 0, "Wwise Vorbis: negative granule %i @ 0x%lx\n", granule, offset); - if (data->config.packet_type == MODIFIED) { + if (data->config.packet_type == WWV_MODIFIED) { /* rebuild first bits of packet type and window info (for the i-MDCT) */ uint32_t packet_type = 0, mode_number = 0, remainder = 0; @@ -423,13 +423,13 @@ static int ww2ogg_generate_vorbis_setup(vgm_bitstream * ow, vgm_bitstream * iw, w_bits(ow, 8, codebook_count_less1); codebook_count = codebook_count_less1 + 1; - if (data->config.setup_type == FULL_SETUP) { + if (data->config.setup_type == WWV_FULL_SETUP) { /* rebuild Wwise codebooks: untouched */ for (i = 0; i < codebook_count; i++) { if(!ww2ogg_codebook_library_copy(ow, iw)) goto fail; } } - else if (data->config.setup_type == INLINE_CODEBOOKS) { + else if (data->config.setup_type == WWV_INLINE_CODEBOOKS) { /* rebuild Wwise codebooks: inline in simplified format */ for (i = 0; i < codebook_count; i++) { if(!ww2ogg_codebook_library_rebuild(ow, iw, 0, streamFile)) goto fail; @@ -456,7 +456,7 @@ static int ww2ogg_generate_vorbis_setup(vgm_bitstream * ow, vgm_bitstream * iw, w_bits(ow, 16, dummy_time_value); - if (data->config.setup_type == FULL_SETUP) { + if (data->config.setup_type == WWV_FULL_SETUP) { /* rest of setup is untouched, copy bits */ uint32_t bitly = 0; uint32_t total_bits_read = iw->b_off; @@ -1174,11 +1174,11 @@ static int load_wvc_array(uint8_t * buf, size_t bufsize, uint32_t codebook_id, w const wvc_info * wvc_list; switch (setup_type) { - case EXTERNAL_CODEBOOKS: + case WWV_EXTERNAL_CODEBOOKS: wvc_list = wvc_list_standard; list_length = sizeof(wvc_list_standard) / sizeof(wvc_info); break; - case AOTUV603_CODEBOOKS: + case WWV_AOTUV603_CODEBOOKS: wvc_list = wvc_list_aotuv603; list_length = sizeof(wvc_list_standard) / sizeof(wvc_info); break; diff --git a/Frameworks/vgmstream/vgmstream/src/formats.c b/Frameworks/vgmstream/vgmstream/src/formats.c index 975a3c543..08b683cf0 100644 --- a/Frameworks/vgmstream/vgmstream/src/formats.c +++ b/Frameworks/vgmstream/vgmstream/src/formats.c @@ -17,7 +17,7 @@ static const char* extension_list[] = { "aaap", "aax", //"ac3", //FFmpeg, not parsed //common? - "ace", //fake, for tri-Ace's formats + "ace", //fake, for tri-Ace's formats (to be removed) "acm", "adm", "adp", @@ -33,7 +33,7 @@ static const char* extension_list[] = { "aix", "akb", "al2", - "amts", //fake extension (to be removed) + "amts", //fake extension/header id for .stm (to be removed) "ao", //txth/reserved [Cloudphobia (PC)] "as4", "asd", @@ -43,6 +43,8 @@ static const char* extension_list[] = { "ast", "at3", "at9", + "atsl3", + "atx", "aud", "aus", "awc", @@ -138,7 +140,7 @@ static const char* extension_list[] = { "iab", "iadp", "idsp", - "idvi", //fake extension (to be removed) + "idvi", //fake extension for .pcm (to be removed) "ikm", "ild", "int", @@ -153,9 +155,10 @@ static const char* extension_list[] = { "jstm", "kces", - "kcey", //fake extension (to be removed) + "kcey", //fake extension/header id (to be removed) "khv", - "kovs", + "km9", + "kovs", //.kvs header id "kraw", "ktss", "kvs", @@ -172,6 +175,7 @@ static const char* extension_list[] = { "lstm", //fake extension, for STMs "lwav", //fake extension, for WAVs + "mab", "matx", "mc3", "mca", @@ -205,8 +209,9 @@ static const char* extension_list[] = { "naac", "ndp", "ngca", + "nop", "nps", - "npsf", //fake extension (to be removed) + "npsf", //fake extension/header id for .nps (to be removed) "nus3bank", "nwa", @@ -226,7 +231,7 @@ static const char* extension_list[] = { "pnb", "pona", "pos", - "ps2stm", //fake extension (to be removed) + "ps2stm", //fake extension for .stm (to be removed) "psh", "psnd", "psw", @@ -261,6 +266,7 @@ static const char* extension_list[] = { "sb5", "sb6", "sb7", + "sbin", "sc", "scd", "sck", @@ -282,6 +288,7 @@ static const char* extension_list[] = { "snd", "snds", "sng", + "sngw", "snr", "sns", "snu", @@ -374,6 +381,7 @@ static const char* extension_list[] = { "xvas", "xwav", "xwb", + "xwc", "xwm", //FFmpeg, not parsed (XWMA) "xwma", //FFmpeg, not parsed (XWMA) "xws", @@ -471,7 +479,7 @@ static const coding_info coding_info_list[] = { {coding_APPLE_IMA4, "Apple Quicktime 4-bit IMA ADPCM"}, {coding_SNDS_IMA, "Heavy Iron .snds 4-bit IMA ADPCM"}, {coding_OTNS_IMA, "Omikron: The Nomad Soul 4-bit IMA ADPCM"}, - {coding_FSB_IMA, "FSB multichannel 4-bit IMA ADPCM"}, + {coding_FSB_IMA, "FSB 4-bit IMA ADPCM"}, {coding_WWISE_IMA, "Audiokinetic Wwise 4-bit IMA ADPCM"}, {coding_REF_IMA, "Reflections 4-bit IMA ADPCM"}, {coding_AWC_IMA, "Rockstar AWC 4-bit IMA ADPCM"}, @@ -574,6 +582,7 @@ static const layout_info layout_info_list[] = { {layout_blocked_awc, "blocked (AWC)"}, {layout_blocked_vgs, "blocked (VGS)"}, {layout_blocked_vawx, "blocked (VAWX)"}, + {layout_blocked_xvag_subsong, "blocked (XVAG subsong)"}, #ifdef VGM_USE_VORBIS {layout_ogg_vorbis, "Ogg"}, #endif @@ -933,16 +942,20 @@ static const meta_info meta_info_list[] = { {meta_NGC_VID1, "Neversoft VID1 header"}, {meta_PC_FLX, "Ultima IX .FLX header"}, {meta_MOGG, "Harmonix Music Systems MOGG Vorbis"}, - -#ifdef VGM_USE_VORBIS {meta_OGG_VORBIS, "Ogg Vorbis"}, {meta_OGG_SLI, "Ogg Vorbis with .sli (start,length) for looping"}, {meta_OGG_SLI2, "Ogg Vorbis with .sli (from,to) for looping"}, {meta_OGG_SFL, "Ogg Vorbis with SFPL for looping"}, - {meta_OGG_UM3, "Ogg Vorbis, Ultramarine3 'encryption'"}, - {meta_OGG_KOVS, "Ogg Vorbis, KOVS header"}, - {meta_OGG_PSYCH, "Ogg Vorbis, Psychic Software obfuscation"}, - #endif + {meta_OGG_UM3, "Ogg Vorbis (Ultramarine3)"}, + {meta_OGG_KOVS, "Ogg Vorbis (KOVS header)"}, + {meta_OGG_PSYCHIC, "Ogg Vorbis (Psychic Software)"}, + {meta_OGG_SNGW, "Ogg Vorbis (Capcom)"}, + {meta_OGG_ISD, "Ogg Vorbis (ISD)"}, + {meta_KMA9, "Koei Tecmo KMA9 header"}, + {meta_XWC, "Starbreeze XWC header"}, + {meta_SQEX_SAB, "Square-Enix SAB header"}, + {meta_SQEX_MAB, "Square-Enix MAB header"}, + #ifdef VGM_USE_MP4V2 {meta_MP4, "AAC header"}, #endif diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked.c index 645ec6946..b8bd1c60e 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/blocked.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked.c @@ -164,6 +164,9 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM * case layout_blocked_vawx: block_update_vawx(vgmstream->next_block_offset,vgmstream); break; + case layout_blocked_xvag_subsong: + block_update_xvag_subsong(vgmstream->next_block_offset,vgmstream); + break; default: break; } diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_schl.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_schl.c index 5e222339c..e71844d0e 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_schl.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_schl.c @@ -95,6 +95,7 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) { /* id, size, samples, hists-per-channel, stereo/interleaved data */ case coding_EA_XA: + //case coding_EA_XA_V2: /* handled in default */ case coding_EA_XA_int: for (i = 0; i < vgmstream->channels; i++) { int is_interleaved = vgmstream->coding_type == coding_EA_XA_int; @@ -106,7 +107,9 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) { /* the block can have padding so find the channel size from num_samples */ interleave = is_interleaved ? (block_samples / 28 * 0x0f) : 0; - vgmstream->ch[i].offset = block_offset + 0x0c + vgmstream->channels*0x04 + i*interleave; + + /* NOT channels*0x04, as seen in Superbike 2000 (PC) EA-XA v1 mono vids */ + vgmstream->ch[i].offset = block_offset + 0x0c + 2*0x04 + i*interleave; } break; @@ -146,6 +149,7 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) { for (i = 0; i < vgmstream->channels; i++) { off_t channel_start = read_32bit(block_offset + 0x0C + (0x04*i),streamFile); vgmstream->ch[i].offset = block_offset + 0x0C + (0x04*vgmstream->channels) + channel_start; + VGM_LOG("ch=%x, off=%lx\n", i, vgmstream->ch[i].offset); } /* read ADPCM history before each channel if needed (not actually read in sx.exe) */ diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked_xvag.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_xvag.c new file mode 100644 index 000000000..1c1ab2d7b --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_xvag.c @@ -0,0 +1,19 @@ +#include "layout.h" +#include "../coding/coding.h" +#include "../vgmstream.h" + + +/* XVAG with subsongs layers, interleaves chunks of each subsong (a hack to support them) */ +void block_update_xvag_subsong(off_t block_offset, VGMSTREAM * vgmstream) { + int i; + size_t channel_size = 0x10; + + /* set offsets */ + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = block_offset + channel_size*i; + } + + //vgmstream->current_block_size = ; /* fixed */ + vgmstream->current_block_offset = block_offset; + vgmstream->next_block_offset = block_offset + vgmstream->full_block_size; +} diff --git a/Frameworks/vgmstream/vgmstream/src/layout/layout.h b/Frameworks/vgmstream/vgmstream/src/layout/layout.h index 9e946ed2c..41a40c4a9 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/layout.h +++ b/Frameworks/vgmstream/vgmstream/src/layout/layout.h @@ -66,6 +66,7 @@ void block_update_ea_sns(off_t block_offset, VGMSTREAM * vgmstream); void block_update_awc(off_t block_offset, VGMSTREAM * vgmstream); void block_update_vgs(off_t block_offset, VGMSTREAM * vgmstream); void block_update_vawx(off_t block_offset, VGMSTREAM * vgmstream); +void block_update_xvag_subsong(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/aax_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/aax_streamfile.h index 9caacab6e..1490e7c4a 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/aax_streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/aax_streamfile.h @@ -72,10 +72,6 @@ static STREAMFILE *open_aax_with_STREAMFILE(STREAMFILE *file,off_t start_offset, streamfile->sf.get_realname = (void*)get_name_aax; streamfile->sf.open = (void*)open_aax_impl; streamfile->sf.close = (void*)close_aax; -#ifdef PROFILE_STREAMFILE - streamfile->sf.get_bytes_read = NULL; - streamfile->sf.get_error_count = NULL; -#endif streamfile->real_file = file; streamfile->start_physical_offset = start_offset; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/adx.c b/Frameworks/vgmstream/vgmstream/src/meta/adx.c index 683e5cc0b..5e6a875c2 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/adx.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/adx.c @@ -249,7 +249,7 @@ static int find_key(STREAMFILE *file, uint8_t type, uint16_t *xor_start, uint16_ { uint8_t keybuf[6]; - if ( read_key_file(keybuf, 6, file) ) { + if (read_key_file(keybuf, 6, file) == 6) { *xor_start = get_16bitBE(keybuf+0); *xor_mult = get_16bitBE(keybuf+2); *xor_add = get_16bitBE(keybuf+4); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h index 29cdbbcd9..9d5b750e8 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h @@ -176,6 +176,9 @@ static const adxkey_info adxkey8_list[] = { /* Storm Lover Natsu Koi!! (2011-08-04)(Vridge)(D3 Publisher)[PSP] */ {0x4133,0x5a01,0x5723, NULL,0}, // ? + /* Shounen Onmyouji: Tsubasa yo Ima, Sora e Kaere [PS2] */ + {0x55d9,0x46d3,0x5b01, "SONMYOJI",0}, + }; static const adxkey_info adxkey9_list[] = { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ahx.c b/Frameworks/vgmstream/vgmstream/src/meta/ahx.c index 516773e02..595707504 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ahx.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ahx.c @@ -56,7 +56,7 @@ VGMSTREAM * init_vgmstream_ahx(STREAMFILE *streamFile) { if (cfg.encryption) { uint8_t keybuf[6]; - if (read_key_file(keybuf, 6, streamFile)) { + if (read_key_file(keybuf, 6, streamFile) == 6) { cfg.cri_key1 = get_16bitBE(keybuf+0); cfg.cri_key2 = get_16bitBE(keybuf+2); cfg.cri_key3 = get_16bitBE(keybuf+4); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/aix_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/aix_streamfile.h index 785ba8b9f..7fd36fd83 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/aix_streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/aix_streamfile.h @@ -151,10 +151,6 @@ static STREAMFILE *open_aix_impl(AIXSTREAMFILE *streamfile,const char * const fi streamfile->sf.get_realname = (void*)get_name_aix; streamfile->sf.open = (void*)open_aix_impl; streamfile->sf.close = (void*)close_aix; -#ifdef PROFILE_STREAMFILE - streamfile->sf.get_bytes_read = NULL; - streamfile->sf.get_error_count = NULL; -#endif streamfile->real_file = file; streamfile->current_physical_offset = start_offset; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/atsl3.c b/Frameworks/vgmstream/vgmstream/src/meta/atsl3.c new file mode 100644 index 000000000..722c3f77f --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/atsl3.c @@ -0,0 +1,76 @@ +#include "meta.h" +#include "../coding/coding.h" + +static STREAMFILE* setup_atsl3_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size); + +/* .ATSL3 - Koei Tecmo container of multiple .AT3 [One Piece Pirate Warriors (PS3)] */ +VGMSTREAM * init_vgmstream_atsl3(STREAMFILE *streamFile) { + VGMSTREAM *vgmstream = NULL; + STREAMFILE *temp_streamFile = NULL; + int total_subsongs, target_subsong = streamFile->stream_index; + off_t subfile_offset; + size_t subfile_size, header_size, entry_size; + + /* check extensions */ + if ( !check_extensions(streamFile,"atsl3")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x4154534C) /* "ATSL" */ + goto fail; + + /* main header (LE) */ + header_size = read_32bitLE(0x04,streamFile); + /* 0x08/0c: flags?, 0x10: some size? */ + total_subsongs = read_32bitLE(0x14,streamFile); + entry_size = read_32bitLE(0x18,streamFile); + /* 0x1c: null, 0x20: subheader size, 0x24/28: null */ + + if (target_subsong == 0) target_subsong = 1; + if (target_subsong > total_subsongs || total_subsongs <= 0) goto fail; + + /* entry header (BE) */ + /* 0x00: id */ + subfile_offset = read_32bitBE(header_size + (target_subsong-1)*entry_size + 0x04,streamFile); + subfile_size = read_32bitBE(header_size + (target_subsong-1)*entry_size + 0x08,streamFile); + /* 0x08+: sample rate/num_samples/loop_start/etc, matching subfile header */ + /* some kind of seek/switch table follows */ + + temp_streamFile = setup_atsl3_streamfile(streamFile, subfile_offset,subfile_size); + if (!temp_streamFile) goto fail; + + /* init the VGMSTREAM */ + vgmstream = init_vgmstream_riff(temp_streamFile); + if (!vgmstream) goto fail; + vgmstream->num_streams = total_subsongs; + + close_streamfile(temp_streamFile); + return vgmstream; + +fail: + close_streamfile(temp_streamFile); + close_vgmstream(vgmstream); + return NULL; +} + + +static STREAMFILE* setup_atsl3_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size) { + 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,"at3"); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/atx.c b/Frameworks/vgmstream/vgmstream/src/meta/atx.c new file mode 100644 index 000000000..a58a88337 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/atx.c @@ -0,0 +1,109 @@ +#include "meta.h" +#include "../coding/coding.h" + +#define ATX_MAX_SEGMENTS 2 + +static STREAMFILE* setup_atx_streamfile(STREAMFILE *streamFile); + +/* .ATX - Media.Vision's segmented RIFF AT3 wrapper [Senjo no Valkyria 3 (PSP), Shining Blade (PSP)] */ +VGMSTREAM * init_vgmstream_atx(STREAMFILE *streamFile) { + VGMSTREAM *vgmstream = NULL; + STREAMFILE *temp_streamFile = NULL; + + + /* check extensions */ + if ( !check_extensions(streamFile,"atx")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x41504133) /* "APA3" */ + goto fail; + + /* .ATX is made of subfile segments, handled by the streamFile. + * Each segment has a header/footer, and part of the whole data + * (i.e. ATRAC3 data ends in a subfile and continues in the next) */ + temp_streamFile = setup_atx_streamfile(streamFile); + 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; +} + +static STREAMFILE* setup_atx_streamfile(STREAMFILE *streamFile) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + STREAMFILE *segment_streamFiles[ATX_MAX_SEGMENTS] = {0}; + char filename[PATH_LIMIT]; + size_t filename_len; + int i, num_segments = 0; + size_t riff_size; +VGM_LOG("1\n"); + + if (read_16bitLE(0x1c,streamFile) != 0) goto fail; /* this must be first segment */ + if (read_16bitLE(0x1e,streamFile) < 1 || read_16bitLE(0x1e,streamFile) > ATX_MAX_SEGMENTS) goto fail; + num_segments = read_16bitLE(0x1e,streamFile); + + /* expected segment name: X_XXX_XXX_0n.ATX, starting from n=1 */ + get_streamfile_name(streamFile, filename,PATH_LIMIT); + filename_len = strlen(filename); + if (filename_len < 7 || filename[filename_len - 5] != '1') goto fail; + + /* setup segments (could avoid reopening first segment but meh) */ + for (i = 0; i < num_segments; i++) { + off_t subfile_offset; + size_t subfile_size; +VGM_LOG("loop\n"); + filename[filename_len - 5] = ('0'+i+1); /* ghetto digit conversion */ + new_streamFile = open_stream_name(streamFile, filename); + if (!new_streamFile) goto fail; + segment_streamFiles[i] = new_streamFile; + + if (read_32bitBE(0x00,segment_streamFiles[i]) != 0x41504133) /* "APA3" */ + goto fail; + + /* parse block/segment header (other Media.Vision's files use it too) */ + subfile_offset = read_32bitLE(0x08,segment_streamFiles[i]); /* header size */ + subfile_size = read_32bitLE(0x14,segment_streamFiles[i]); /* can be 0 in other containers */ +VGM_LOG("subfile: %lx, %x\n", subfile_offset, subfile_size); + if (read_16bitLE(0x1c,segment_streamFiles[i]) != i) + goto fail; /* segment sequence */ + /* 0x04: block size (should match subfile_size in .ATX) */ + /* 0x0c: flags? also in other files, 0x10/18: null, 0x1e: segments */ + + /* clamp to ignore header/footer during next reads */ + new_streamFile = open_clamp_streamfile(segment_streamFiles[i], subfile_offset,subfile_size); + if (!new_streamFile) goto fail; + segment_streamFiles[i] = new_streamFile; + } + + /* setup with all segments and clamp further using riff_size (last segment has padding) */ + riff_size = read_32bitLE(read_32bitLE(0x08,streamFile) + 0x04,streamFile) + 0x08; + + new_streamFile = open_multifile_streamfile(segment_streamFiles, num_segments); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_clamp_streamfile(temp_streamFile, 0,riff_size); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_fakename_streamfile(temp_streamFile, NULL, "at3"); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + if (!temp_streamFile) { + for (i = 0; i < num_segments; i++) + close_streamfile(segment_streamFiles[i]); + } else { + close_streamfile(temp_streamFile); /* closes all segments */ + } + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/awc.c b/Frameworks/vgmstream/vgmstream/src/meta/awc.c index ab2a605bd..a102d1b47 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/awc.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/awc.c @@ -7,7 +7,7 @@ typedef struct { int is_encrypted; int is_music; - int total_streams; + int total_subsongs; int channel_count; int sample_rate; @@ -47,7 +47,8 @@ VGMSTREAM * init_vgmstream_awc(STREAMFILE *streamFile) { vgmstream->sample_rate = awc.sample_rate; vgmstream->num_samples = awc.num_samples; - vgmstream->num_streams = awc.total_streams; + vgmstream->num_streams = awc.total_subsongs; + vgmstream->stream_size = awc.stream_size; vgmstream->meta_type = meta_AWC; @@ -113,7 +114,7 @@ static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc) { int i, ch, entries; uint32_t flags, info_header, tag_count = 0, tags_skip = 0; off_t off; - int target_stream = streamFile->stream_index; + int target_subsong = streamFile->stream_index; memset(awc,0,sizeof(awc_header)); @@ -161,13 +162,13 @@ static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc) { * Music seems layered (N-1/2 stereo pairs), maybe set with events? */ awc->is_music = (read_32bit(off + 0x00,streamFile) & 0x1FFFFFFF) == 0x00000000; if (awc->is_music) { /* all streams except id 0 is a channel */ - awc->total_streams = 1; - target_stream = 1; /* we only need id 0, though channels may have its own tags/chunks */ + awc->total_subsongs = 1; + target_subsong = 1; /* we only need id 0, though channels may have its own tags/chunks */ } else { /* each stream is a single sound */ - awc->total_streams = entries; - if (target_stream == 0) target_stream = 1; - if (target_stream < 0 || target_stream > awc->total_streams || awc->total_streams < 1) goto fail; + awc->total_subsongs = entries; + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > awc->total_subsongs || awc->total_subsongs < 1) goto fail; } @@ -176,7 +177,7 @@ static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc) { info_header = read_32bit(off + 0x04*i, streamFile); tag_count = (info_header >> 29) & 0x7; /* 3b */ //id = (info_header >> 0) & 0x1FFFFFFF; /* 29b */ - if (target_stream-1 == i) + if (target_subsong-1 == i) break; tags_skip += tag_count; /* tags to skip to reach target's tags, in the next header */ } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bar_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/bar_streamfile.h index 8305020c5..81d43befc 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/bar_streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/bar_streamfile.h @@ -62,15 +62,6 @@ static void close_bar(BARSTREAMFILE *streamFile) { return; } -#ifdef PROFILE_STREAMFILE -size_t get_bytes_read_bar(BARSTREAMFILE *streamFile) { - return streamFile->real_file->get_bytes_read(streamFile->real_file); -} - -int (*get_error_count)(BARSTREAMFILE *streamFile) { - return streamFile->real_file->get_error_count(streamFile->real_file); -} -#endif /*static*/ STREAMFILE *wrap_bar_STREAMFILE(STREAMFILE *file) { BARSTREAMFILE *streamfile = malloc(sizeof(BARSTREAMFILE)); @@ -87,10 +78,6 @@ int (*get_error_count)(BARSTREAMFILE *streamFile) { streamfile->sf.get_realname = (void*)get_realname_bar; streamfile->sf.open = (void*)open_bar; streamfile->sf.close = (void*)close_bar; -#ifdef PROFILE_STREAMFILE - streamfile->sf.get_bytes_read = get_bytes_read_bar; - streamfile->sf.get_error_count = get_error_count_bar; -#endif streamfile->real_file = file; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bgw.c b/Frameworks/vgmstream/vgmstream/src/meta/bgw.c index f95ac9940..0be6ebabc 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/bgw.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/bgw.c @@ -1,9 +1,14 @@ #include "meta.h" #include "../coding/coding.h" + +static STREAMFILE* setup_bgw_atrac3_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, size_t frame_size, int channels); + + /* BGW - from Final Fantasy XI (PC) music files */ VGMSTREAM * init_vgmstream_bgw(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; + STREAMFILE *temp_streamFile = NULL; uint32_t codec, file_size, block_size, sample_rate, block_align; int32_t loop_start; off_t start_offset; @@ -32,18 +37,15 @@ VGMSTREAM * init_vgmstream_bgw(STREAMFILE *streamFile) { channel_count = read_8bit(0x2e,streamFile); block_align = read_8bit(0x2f,streamFile); - - /* check file size with header value */ if (file_size != get_streamfile_size(streamFile)) goto fail; loop_flag = (loop_start > 0); - /* build the VGMSTREAM */ + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ vgmstream->meta_type = meta_FFXI_BGW; vgmstream->sample_rate = sample_rate; @@ -65,7 +67,7 @@ VGMSTREAM * init_vgmstream_bgw(STREAMFILE *streamFile) { case 3: { /* ATRAC3 (encrypted) */ uint8_t buf[0x100]; int bytes, joint_stereo, skip_samples; - ffmpeg_custom_config cfg; + size_t data_size = file_size - start_offset; vgmstream->num_samples = block_size; /* atrac3_bytes_to_samples gives the same value */ if (loop_flag) { @@ -77,18 +79,18 @@ VGMSTREAM * init_vgmstream_bgw(STREAMFILE *streamFile) { joint_stereo = 0; skip_samples = 0; - bytes = ffmpeg_make_riff_atrac3(buf, 0x100, vgmstream->num_samples, file_size - start_offset, vgmstream->channels, vgmstream->sample_rate, block_align, joint_stereo, skip_samples); + bytes = ffmpeg_make_riff_atrac3(buf, 0x100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_align, joint_stereo, skip_samples); if (bytes <= 0) goto fail; - memset(&cfg, 0, sizeof(ffmpeg_custom_config)); - cfg.type = FFMPEG_BGW_ATRAC3; - cfg.channels = vgmstream->channels; + temp_streamFile = setup_bgw_atrac3_streamfile(streamFile, start_offset,data_size, 0xC0,channel_count); + if (!temp_streamFile) goto fail; - vgmstream->codec_data = init_ffmpeg_config(streamFile, buf,bytes, start_offset,file_size - start_offset, &cfg); + vgmstream->codec_data = init_ffmpeg_header_offset(temp_streamFile, buf,bytes, 0,data_size); if (!vgmstream->codec_data) goto fail; - vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; + + close_streamfile(temp_streamFile); break; } #endif @@ -98,17 +100,17 @@ VGMSTREAM * init_vgmstream_bgw(STREAMFILE *streamFile) { } - /* open the file for reading */ if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) goto fail; - return vgmstream; fail: + close_streamfile(temp_streamFile); close_vgmstream(vgmstream); return NULL; } + /* SPW (SEWave) - from PlayOnline viewer for Final Fantasy XI (PC) */ VGMSTREAM * init_vgmstream_spw(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; @@ -118,8 +120,8 @@ VGMSTREAM * init_vgmstream_spw(STREAMFILE *streamFile) { int channel_count, loop_flag = 0; - /* check extensions */ - if ( !check_extensions(streamFile, "spw") ) + /* check extensions */ + if ( !check_extensions(streamFile, "spw") ) goto fail; /* check header */ @@ -127,10 +129,6 @@ VGMSTREAM * init_vgmstream_spw(STREAMFILE *streamFile) { read_32bitBE(4,streamFile) != 0x76650000) /* "ve\0\0" */ goto fail; - /* check file size with header value */ - if (read_32bitLE(0x8,streamFile) != get_streamfile_size(streamFile)) - goto fail; - file_size = read_32bitLE(0x08,streamFile); codec = read_32bitLE(0x0c,streamFile); /*file_id = read_32bitLE(0x10,streamFile);*/ @@ -144,17 +142,15 @@ VGMSTREAM * init_vgmstream_spw(STREAMFILE *streamFile) { block_align = read_8bit(0x2b,streamFile); /*0x2c: unk (0x01 when PCM, 0x10 when VAG?) */ - /* check file size with header value */ if (file_size != get_streamfile_size(streamFile)) goto fail; loop_flag = (loop_start > 0); - /* build the VGMSTREAM */ + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ vgmstream->meta_type = meta_FFXI_SPW; vgmstream->sample_rate = sample_rate; @@ -171,7 +167,7 @@ VGMSTREAM * init_vgmstream_spw(STREAMFILE *streamFile) { } break; - + case 1: /* PCM */ vgmstream->coding_type = coding_PCM16LE; vgmstream->layout_type = layout_interleave; @@ -182,8 +178,9 @@ VGMSTREAM * init_vgmstream_spw(STREAMFILE *streamFile) { vgmstream->loop_start_sample = (loop_start-1); vgmstream->loop_end_sample = vgmstream->num_samples; } - + break; + default: goto fail; } @@ -199,3 +196,61 @@ fail: close_vgmstream(vgmstream); return NULL; } + + +#define BGW_KEY_MAX (0xC0*2) + +typedef struct { + uint8_t key[BGW_KEY_MAX]; + size_t key_size; +} bgw_decryption_data; + +/* Encrypted ATRAC3 info from Moogle Toolbox (https://sourceforge.net/projects/mogbox/) */ +static size_t bgw_decryption_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, bgw_decryption_data* data) { + size_t bytes_read; + int i; + + bytes_read = streamfile->read(streamfile, dest, offset, length); + + /* decrypt data (xor) */ + for (i = 0; i < bytes_read; i++) { + dest[i] ^= data->key[(offset + i) % data->key_size]; + } + + return bytes_read; +} + +static STREAMFILE* setup_bgw_atrac3_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, size_t frame_size, int channels) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + bgw_decryption_data io_data = {0}; + size_t io_data_size = sizeof(bgw_decryption_data); + int ch; + + /* setup decryption with key (first frame + modified channel header) */ + if (frame_size*channels == 0 || frame_size*channels > BGW_KEY_MAX) goto fail; + + io_data.key_size = read_streamfile(io_data.key, subfile_offset, frame_size*channels, streamFile); + for (ch = 0; ch < channels; ch++) { + uint32_t xor = get_32bitBE(io_data.key + frame_size*ch); + put_32bitBE(io_data.key + frame_size*ch, xor ^ 0xA0024E9F); + } + + /* 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_io_streamfile(temp_streamFile, &io_data,io_data_size, bgw_decryption_read); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bik.c b/Frameworks/vgmstream/vgmstream/src/meta/bik.c index f703b2a6a..3e3595762 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/bik.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/bik.c @@ -2,13 +2,14 @@ #include "../coding/coding.h" #include "../util.h" -static int bink_get_info(STREAMFILE *streamFile, int * out_total_streams, int * out_channel_count, int * out_sample_rate, int * out_num_samples); +static int bink_get_info(STREAMFILE *streamFile, int * out_total_streams, size_t *out_stream_size, int * out_channel_count, int * out_sample_rate, int * out_num_samples); /* BINK 1/2 - RAD Game Tools movies (audio/video format) */ VGMSTREAM * init_vgmstream_bik(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - int channel_count = 0, loop_flag = 0, sample_rate = 0, num_samples = 0, total_streams = 0; - int stream_index = streamFile->stream_index; + int channel_count = 0, loop_flag = 0, sample_rate = 0, num_samples = 0; + int total_subsongs = 0, stream_index = streamFile->stream_index; + size_t stream_size; /* check extension, case insensitive (bika = manually demuxed audio) */ @@ -19,7 +20,7 @@ VGMSTREAM * init_vgmstream_bik(STREAMFILE *streamFile) { (read_32bitBE(0x00,streamFile) & 0xffffff00) != 0x4B423200 ) goto fail; /* find target stream info and samples */ - if (!bink_get_info(streamFile, &total_streams, &channel_count, &sample_rate, &num_samples)) + if (!bink_get_info(streamFile, &total_subsongs, &stream_size, &channel_count, &sample_rate, &num_samples)) goto fail; /* build the VGMSTREAM */ @@ -29,7 +30,8 @@ VGMSTREAM * init_vgmstream_bik(STREAMFILE *streamFile) { vgmstream->layout_type = layout_none; vgmstream->sample_rate = sample_rate; vgmstream->num_samples = num_samples; - vgmstream->num_streams = total_streams; + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; vgmstream->meta_type = meta_BINK; #ifdef VGM_USE_FFMPEG @@ -58,12 +60,13 @@ fail: * as they are not in the main header. The header for BINK1 and 2 is the same. * (a ~3 min movie needs ~6000-7000 frames = fseeks, should be fast enough) */ -static int bink_get_info(STREAMFILE *streamFile, int * out_total_streams, int * out_channel_count, int * out_sample_rate, int * out_num_samples) { +static int bink_get_info(STREAMFILE *streamFile, int * out_total_subsongs, size_t * out_stream_size, int * out_channel_count, int * out_sample_rate, int * out_num_samples) { uint32_t *offsets = NULL; uint32_t num_frames, num_samples_b = 0; off_t cur_offset; int i, j, sample_rate, channel_count; - int total_streams, target_stream = streamFile->stream_index; + int total_subsongs, target_subsong = streamFile->stream_index; + size_t stream_size = 0; size_t filesize = get_streamfile_size(streamFile); uint32_t signature = (read_32bitBE(0x00,streamFile) & 0xffffff00); @@ -76,20 +79,20 @@ static int bink_get_info(STREAMFILE *streamFile, int * out_total_streams, int * if (num_frames == 0 || num_frames > 0x100000) goto fail; /* something must be off (avoids big allocs below) */ /* multichannel/multilanguage audio is usually N streams of stereo/mono, no way to know channel layout */ - total_streams = read_32bitLE(0x28,streamFile); - if (target_stream == 0) target_stream = 1; - if (target_stream < 0 || target_stream > total_streams || total_streams < 1 || total_streams > 255) goto fail; + total_subsongs = read_32bitLE(0x28,streamFile); + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1 || total_subsongs > 255) goto fail; /* find stream info and position in offset table */ cur_offset = 0x2c; if ((signature == 0x42494B00 && (revision == 0x6b)) || /* k */ (signature == 0x4B423200 && (revision == 0x69 || revision == 0x6a || revision == 0x6b))) /* i,j,k */ cur_offset += 0x04; /* unknown v2 header field */ - cur_offset += 0x04*total_streams; /* skip streams max packet bytes */ - sample_rate = (uint16_t)read_16bitLE(cur_offset+0x04*(target_stream-1)+0x00,streamFile); - channel_count = (uint16_t)read_16bitLE(cur_offset+0x04*(target_stream-1)+0x02,streamFile) & 0x2000 ? 2 : 1; /* stereo flag */ - cur_offset += 0x04*total_streams; /* skip streams info */ - cur_offset += 0x04*total_streams; /* skip streams ids */ + cur_offset += 0x04*total_subsongs; /* skip streams max packet bytes */ + sample_rate = (uint16_t)read_16bitLE(cur_offset+0x04*(target_subsong-1)+0x00,streamFile); + channel_count = (uint16_t)read_16bitLE(cur_offset+0x04*(target_subsong-1)+0x02,streamFile) & 0x2000 ? 2 : 1; /* stereo flag */ + cur_offset += 0x04*total_subsongs; /* skip streams info */ + cur_offset += 0x04*total_subsongs; /* skip streams ids */ /* read frame offsets in a buffer, to avoid fseeking to the table back and forth */ @@ -111,10 +114,11 @@ static int bink_get_info(STREAMFILE *streamFile, int * out_total_streams, int * cur_offset = offsets[i]; /* read audio packet headers per stream */ - for (j=0; j < total_streams; j++) { + for (j=0; j < total_subsongs; j++) { uint32_t ap_size = read_32bitLE(cur_offset+0x00,streamFile); /* not counting this int */ - if (j == target_stream-1) { + if (j == target_subsong-1) { + stream_size += 0x04 + ap_size; if (ap_size > 0) num_samples_b += read_32bitLE(cur_offset+0x04,streamFile); /* decoded samples in bytes */ break; /* next frame */ @@ -128,7 +132,8 @@ static int bink_get_info(STREAMFILE *streamFile, int * out_total_streams, int * free(offsets); - if (out_total_streams) *out_total_streams = total_streams; + if (out_total_subsongs) *out_total_subsongs = total_subsongs; + if (out_stream_size) *out_stream_size = stream_size; if (out_sample_rate) *out_sample_rate = sample_rate; if (out_channel_count) *out_channel_count = channel_count; //todo returns a few more samples (~48) than binkconv.exe? diff --git a/Frameworks/vgmstream/vgmstream/src/meta/flx.c b/Frameworks/vgmstream/vgmstream/src/meta/flx.c index ab28a3221..d37762e86 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/flx.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/flx.c @@ -7,7 +7,8 @@ VGMSTREAM * init_vgmstream_flx(STREAMFILE *streamFile) { off_t start_offset, stream_offset = 0; size_t data_size; int loop_flag, channel_count, codec; - int total_streams = 0, target_stream = streamFile->stream_index; + int total_subsongs = 0, target_subsong = streamFile->stream_index; + size_t stream_size = 0; /* check extensions (.flx: name of archive, files inside don't have extensions) */ @@ -24,23 +25,26 @@ VGMSTREAM * init_vgmstream_flx(STREAMFILE *streamFile) { || read_32bitLE(0x58,streamFile) != get_streamfile_size(streamFile)) goto fail; - if (target_stream == 0) target_stream = 1; + if (target_subsong == 0) target_subsong = 1; for (i = 0; i < entries; i++) { off_t entry_offset = read_32bitLE(offset + 0x00, streamFile); - /* 0x04: stream size */ + size_t entry_size = read_32bitLE(offset + 0x04, streamFile); offset += 0x08; if (entry_offset != 0x00) - total_streams++; /* many entries are empty */ - if (total_streams == target_stream && stream_offset == 0) + total_subsongs++; /* many entries are empty */ + if (total_subsongs == target_subsong && stream_offset == 0) { stream_offset = entry_offset; /* found but let's keep adding total_streams */ + stream_size = entry_size; + } } - if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; if (stream_offset == 0x00) goto fail; } else { stream_offset = 0x00; + stream_size = get_streamfile_size(streamFile); } if (read_32bitLE(stream_offset + 0x30,streamFile) != 0x10) @@ -57,7 +61,8 @@ VGMSTREAM * init_vgmstream_flx(STREAMFILE *streamFile) { if (!vgmstream) goto fail; vgmstream->sample_rate = read_32bitLE(stream_offset + 0x2c,streamFile); - vgmstream->num_streams = total_streams; + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; vgmstream->meta_type = meta_PC_FLX; switch(codec) { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb.c b/Frameworks/vgmstream/vgmstream/src/meta/fsb.c index 5b0e12e7e..6a6bbf53b 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb.c @@ -1,9 +1,6 @@ #include "meta.h" #include "../coding/coding.h" -#define FAKE_RIFF_BUFFER_SIZE 100 - -static VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset); /* ************************************************************************************************************ * FSB defines, copied from the public spec (https://www.fmod.org/questions/question/forum-4928/) @@ -65,9 +62,7 @@ static VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offse #define FSOUND_CHANNELMODE_MASK (FSOUND_CHANNELMODE_ALLMONO | FSOUND_CHANNELMODE_ALLSTEREO | FSOUND_CHANNELMODE_PROTOOLS) #define FSOUND_CHANNELMODE_DEFAULT 0x00000000 /* Determine channel assignment automatically from channel count. */ #define FSOUND_CHANNELMODE_RESERVED 0x00000C00 - #define FSOUND_NORMAL (FSOUND_16BITS | FSOUND_SIGNED | FSOUND_MONO) - #define FSB_SAMPLE_DATA_ALIGN 32 @@ -75,23 +70,22 @@ static VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offse typedef struct { /* main header */ uint32_t id; - int32_t numsamples; /* number of samples(streams) in the file */ - uint32_t shdrsize; /* size in bytes of all of the sample headers including extended information */ - uint32_t datasize; /* size in bytes of compressed sample data */ - /* main header: FSB 3/3.1/4 */ - uint32_t version; /* extended fsb version */ - uint32_t flags; /* flags that apply to all samples(streams) in the fsb */ + int32_t total_subsongs; + uint32_t sample_header_size; /* all of the sample headers including extended information */ + uint32_t data_size; + uint32_t version; /* extended fsb version (in FSB 3/3.1/4) */ + uint32_t flags; /* flags common to all streams (in FSB 3/3.1/4)*/ /* sample header */ - uint32_t lengthsamples; - uint32_t lengthcompressedbytes; - uint32_t loopstart; - uint32_t loopend; + uint32_t num_samples; + uint32_t stream_size; + uint32_t loop_start; + uint32_t loop_end; uint32_t mode; - int32_t deffreq; - uint16_t numchannels; + int32_t sample_rate; + uint16_t channels; /* extra */ - uint32_t hdrsize; - uint32_t shdrsize_min; + uint32_t base_header_size; + uint32_t sample_header_min; meta_t meta_type; off_t name_offset; @@ -102,294 +96,328 @@ typedef struct { /* FSB4 */ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) { - return init_vgmstream_fsb_offset(streamFile, 0x0); -} - -/* FSB4 with "\0WAV" Header, found in Deadly Creatures (Wii) - * 16 byte header which holds the filesize - * (unsure if this is from a proper rip) */ -VGMSTREAM * init_vgmstream_fsb4_wav(STREAMFILE *streamFile) { - if (read_32bitBE(0x00,streamFile) != 0x00574156) /* "\0WAV" */ - return NULL; - return init_vgmstream_fsb_offset(streamFile, 0x10); -} - -VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) { VGMSTREAM * vgmstream = NULL; off_t start_offset; size_t custom_data_offset; int loop_flag = 0; - int target_stream = streamFile->stream_index; + int target_subsong = streamFile->stream_index; + fsb_header fsb = {0}; - fsb_header fsbh; /* check extensions (.bnk = Hard Corps Uprising PS3) */ - if ( !check_extensions(streamFile, "fsb,wii,bnk") ) + if ( !check_extensions(streamFile, "fsb,bnk") ) goto fail; /* check header */ - fsbh.id = read_32bitBE(offset+0x00,streamFile); - if (fsbh.id == 0x46534231) { /* "FSB1" (somewhat different from other fsbs) */ - fsbh.meta_type = meta_FSB1; - fsbh.hdrsize = 0x10; - fsbh.shdrsize_min = 0x40; + fsb.id = read_32bitBE(0x00,streamFile); + if (fsb.id == 0x46534231) { /* "FSB1" (somewhat different from other fsbs) */ + fsb.meta_type = meta_FSB1; + fsb.base_header_size = 0x10; + fsb.sample_header_min = 0x40; /* main header */ - fsbh.numsamples = read_32bitLE(offset+0x04,streamFile); - fsbh.datasize = read_32bitLE(offset+0x08,streamFile); - fsbh.shdrsize = 0x40; - fsbh.version = 0; - fsbh.flags = 0; + fsb.total_subsongs = read_32bitLE(0x04,streamFile); + fsb.data_size = read_32bitLE(0x08,streamFile); + fsb.sample_header_size = 0x40; + fsb.version = 0; + fsb.flags = 0; - if (fsbh.numsamples > 1) goto fail; + if (fsb.total_subsongs > 1) goto fail; /* sample header (first stream only, not sure if there are multi-FSB1) */ { - off_t s_off = offset+fsbh.hdrsize; + off_t s_off = fsb.base_header_size; - fsbh.name_offset = s_off; - fsbh.name_size = 0x20; - fsbh.lengthsamples = read_32bitLE(s_off+0x20,streamFile); - fsbh.lengthcompressedbytes = read_32bitLE(s_off+0x24,streamFile); - fsbh.deffreq = read_32bitLE(s_off+0x28,streamFile); + fsb.name_offset = s_off; + fsb.name_size = 0x20; + fsb.num_samples = read_32bitLE(s_off+0x20,streamFile); + fsb.stream_size = read_32bitLE(s_off+0x24,streamFile); + fsb.sample_rate = read_32bitLE(s_off+0x28,streamFile); /* 0x2c:? 0x2e:? 0x30:? 0x32:? */ - fsbh.mode = read_32bitLE(s_off+0x34,streamFile); - fsbh.loopstart = read_32bitLE(s_off+0x38,streamFile); - fsbh.loopend = read_32bitLE(s_off+0x3c,streamFile); + fsb.mode = read_32bitLE(s_off+0x34,streamFile); + fsb.loop_start = read_32bitLE(s_off+0x38,streamFile); + fsb.loop_end = read_32bitLE(s_off+0x3c,streamFile); - fsbh.numchannels = (fsbh.mode & FSOUND_STEREO) ? 2 : 1; - if (fsbh.loopend > fsbh.lengthsamples) /* this seems common... */ - fsbh.lengthsamples = fsbh.loopend; + fsb.channels = (fsb.mode & FSOUND_STEREO) ? 2 : 1; + if (fsb.loop_end > fsb.num_samples) /* this seems common... */ + fsb.num_samples = fsb.loop_end; - start_offset = offset + fsbh.hdrsize + fsbh.shdrsize; - custom_data_offset = offset + fsbh.hdrsize + fsbh.shdrsize_min; /* DSP coefs, seek tables, etc */ + start_offset = fsb.base_header_size + fsb.sample_header_size; + custom_data_offset = fsb.base_header_size + fsb.sample_header_min; /* DSP coefs, seek tables, etc */ } } else { /* other FSBs (common/extended format) */ - if (fsbh.id == 0x46534232) { /* "FSB2" */ - fsbh.meta_type = meta_FSB2; - fsbh.hdrsize = 0x10; - fsbh.shdrsize_min = 0x40; /* guessed */ - } else if (fsbh.id == 0x46534233) { /* "FSB3" */ - fsbh.meta_type = meta_FSB3; - fsbh.hdrsize = 0x18; - fsbh.shdrsize_min = 0x40; - } else if (fsbh.id == 0x46534234) { /* "FSB4" */ - fsbh.meta_type = meta_FSB4; - fsbh.hdrsize = 0x30; - fsbh.shdrsize_min = 0x50; + if (fsb.id == 0x46534232) { /* "FSB2" */ + fsb.meta_type = meta_FSB2; + fsb.base_header_size = 0x10; + fsb.sample_header_min = 0x40; /* guessed */ + } else if (fsb.id == 0x46534233) { /* "FSB3" */ + fsb.meta_type = meta_FSB3; + fsb.base_header_size = 0x18; + fsb.sample_header_min = 0x40; + } else if (fsb.id == 0x46534234) { /* "FSB4" */ + fsb.meta_type = meta_FSB4; + fsb.base_header_size = 0x30; + fsb.sample_header_min = 0x50; } else { goto fail; } /* main header */ - fsbh.numsamples = read_32bitLE(offset+0x04,streamFile); - fsbh.shdrsize = read_32bitLE(offset+0x08,streamFile); - fsbh.datasize = read_32bitLE(offset+0x0c,streamFile); - if (fsbh.hdrsize > 0x10) { - fsbh.version = read_32bitLE(offset+0x10,streamFile); - fsbh.flags = read_32bitLE(offset+0x14,streamFile); + fsb.total_subsongs = read_32bitLE(0x04,streamFile); + fsb.sample_header_size = read_32bitLE(0x08,streamFile); + fsb.data_size = read_32bitLE(0x0c,streamFile); + if (fsb.base_header_size > 0x10) { + fsb.version = read_32bitLE(0x10,streamFile); + fsb.flags = read_32bitLE(0x14,streamFile); /* FSB4: 0x18:hash 0x20:guid */ } else { - fsbh.version = 0; - fsbh.flags = 0; + fsb.version = 0; + fsb.flags = 0; } - if (fsbh.version == FMOD_FSB_VERSION_3_1) { - fsbh.shdrsize_min = 0x50; - } else if (fsbh.version != 0 /* FSB2 */ - && fsbh.version != FMOD_FSB_VERSION_3_0 - && fsbh.version != FMOD_FSB_VERSION_4_0) { + if (fsb.version == FMOD_FSB_VERSION_3_1) { + fsb.sample_header_min = 0x50; + } else if (fsb.version != 0 /* FSB2 */ + && fsb.version != FMOD_FSB_VERSION_3_0 + && fsb.version != FMOD_FSB_VERSION_4_0) { goto fail; } - if (fsbh.shdrsize < fsbh.shdrsize_min) goto fail; - if (target_stream == 0) target_stream = 1; - if (target_stream < 0 || target_stream > fsbh.numsamples || fsbh.numsamples < 1) goto fail; + if (fsb.sample_header_size < fsb.sample_header_min) goto fail; + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > fsb.total_subsongs || fsb.total_subsongs < 1) goto fail; /* sample header (N-stream) */ { int i; - off_t s_off = offset + fsbh.hdrsize; - off_t d_off = offset + fsbh.hdrsize + fsbh.shdrsize; + off_t s_off = fsb.base_header_size; + off_t d_off = fsb.base_header_size + fsb.sample_header_size; /* find target_stream header (variable sized) */ - for(i = 0; i < fsbh.numsamples; i++) { + for (i = 0; i < fsb.total_subsongs; i++) { size_t stream_header_size = (uint16_t)read_16bitLE(s_off+0x00,streamFile); - fsbh.name_offset = s_off+0x02; - fsbh.name_size = 0x20-0x02; - fsbh.lengthsamples = read_32bitLE(s_off+0x20,streamFile); - fsbh.lengthcompressedbytes = read_32bitLE(s_off+0x24,streamFile); - fsbh.loopstart = read_32bitLE(s_off+0x28,streamFile); - fsbh.loopend = read_32bitLE(s_off+0x2c,streamFile); - fsbh.mode = read_32bitLE(s_off+0x30,streamFile); - fsbh.deffreq = read_32bitLE(s_off+0x34,streamFile); + fsb.name_offset = s_off+0x02; + fsb.name_size = 0x20-0x02; + fsb.num_samples = read_32bitLE(s_off+0x20,streamFile); + fsb.stream_size = read_32bitLE(s_off+0x24,streamFile); + fsb.loop_start = read_32bitLE(s_off+0x28,streamFile); + fsb.loop_end = read_32bitLE(s_off+0x2c,streamFile); + fsb.mode = read_32bitLE(s_off+0x30,streamFile); + fsb.sample_rate = read_32bitLE(s_off+0x34,streamFile); /* 0x38:defvol 0x3a:defpan 0x3c:defpri */ - fsbh.numchannels = read_16bitLE(s_off+0x3e,streamFile); - /* FSB3.1/4: 0x40:mindistance 0x44:maxdistance 0x48:varfreq/size_32bits 0x4c:varvol 0x4e:fsbh.varpan */ + fsb.channels = read_16bitLE(s_off+0x3e,streamFile); + /* FSB3.1/4: 0x40:mindistance 0x44:maxdistance 0x48:varfreq/size_32bits 0x4c:varvol 0x4e:fsb.varpan */ /* FSB3/4: 0x50:extended_data size_32bits (not always given) */ - if (i+1 == target_stream) /* d_off found */ + if (i+1 == target_subsong) /* d_off found */ break; s_off += stream_header_size; - d_off += fsbh.lengthcompressedbytes; /* there is no offset so manually count */ + d_off += fsb.stream_size; /* there is no offset so manually count */ /* IMAs streams have weird end padding (maybe: FSB3=no padding, FSB4=always padding) */ - if ((fsbh.mode & FSOUND_IMAADPCM) && (fsbh.flags & FMOD_FSB_SOURCE_MPEG_PADDED4)) { + if ((fsb.mode & FSOUND_IMAADPCM) && (fsb.flags & FMOD_FSB_SOURCE_MPEG_PADDED4)) { if (d_off % 0x20) d_off += 0x20 - (d_off % 0x20); } } - if (i > fsbh.numsamples) goto fail; /* not found */ + if (i > fsb.total_subsongs) goto fail; /* not found */ start_offset = d_off; - custom_data_offset = s_off + fsbh.shdrsize_min; /* DSP coefs, seek tables, etc */ + custom_data_offset = s_off + fsb.sample_header_min; /* DSP coefs, seek tables, etc */ } } /* XOR encryption for some FSB4, though the flag is only seen after decrypting */ - //VGM_ASSERT(fsbh.flags & FMOD_FSB_SOURCE_ENCRYPTED, "FSB ENCRYPTED found\n"); + //VGM_ASSERT(fsb.flags & FMOD_FSB_SOURCE_ENCRYPTED, "FSB ENCRYPTED found\n"); /* sometimes there is garbage at the end or missing bytes due to improper demuxing */ - VGM_ASSERT(fsbh.hdrsize + fsbh.shdrsize + fsbh.datasize != streamFile->get_size(streamFile) - offset, - "FSB wrong head/datasize found (expected 0x%x vs 0x%lx)\n", - fsbh.hdrsize + fsbh.shdrsize + fsbh.datasize, streamFile->get_size(streamFile) - offset); + VGM_ASSERT(fsb.base_header_size + fsb.sample_header_size + fsb.data_size != streamFile->get_size(streamFile), + "FSB wrong head/data_size found (expected 0x%x vs 0x%x)\n", + fsb.base_header_size + fsb.sample_header_size + fsb.data_size, streamFile->get_size(streamFile)); /* Loops unless disabled. FMOD default seems full loops (0/num_samples-1) without flags, for repeating tracks - * that should loop and jingles/sfx that shouldn't. We'll try to disable looping is it looks jingly enough. */ - loop_flag = !(fsbh.mode & FSOUND_LOOP_OFF); - if(!(fsbh.mode & FSOUND_LOOP_NORMAL) /* rarely set */ - && fsbh.loopstart+fsbh.loopend+1 == fsbh.lengthsamples /* full loop */ - && fsbh.lengthsamples < 20*fsbh.deffreq) /* seconds, lame but no other way to know */ + * that should loop and jingles/sfx that shouldn't. We'll try to disable looping if it looks jingly enough. */ + loop_flag = !(fsb.mode & FSOUND_LOOP_OFF); + if(!(fsb.mode & FSOUND_LOOP_NORMAL) /* rarely set */ + && fsb.loop_start+fsb.loop_end+1 == fsb.num_samples /* full loop */ + && fsb.num_samples < 20*fsb.sample_rate) /* seconds, lame but no other way to know */ loop_flag = 0; /* ping-pong looping = no looping? (forward > reverse > forward) */ - VGM_ASSERT(fsbh.mode & FSOUND_LOOP_BIDI, "FSB BIDI looping found\n"); + VGM_ASSERT(fsb.mode & FSOUND_LOOP_BIDI, "FSB BIDI looping found\n"); /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(fsbh.numchannels,loop_flag); + vgmstream = allocate_vgmstream(fsb.channels,loop_flag); if (!vgmstream) goto fail; - vgmstream->sample_rate = fsbh.deffreq; - vgmstream->num_samples = fsbh.lengthsamples; - vgmstream->loop_start_sample = fsbh.loopstart; - vgmstream->loop_end_sample = fsbh.loopend; - vgmstream->num_streams = fsbh.numsamples; - vgmstream->meta_type = fsbh.meta_type; - if (fsbh.name_offset) - read_string(vgmstream->stream_name,fsbh.name_size+1, fsbh.name_offset,streamFile); + vgmstream->sample_rate = fsb.sample_rate; + vgmstream->num_samples = fsb.num_samples; + vgmstream->loop_start_sample = fsb.loop_start; + vgmstream->loop_end_sample = fsb.loop_end; + vgmstream->num_streams = fsb.total_subsongs; + vgmstream->stream_size = fsb.stream_size; + vgmstream->meta_type = fsb.meta_type; + if (fsb.name_offset) + read_string(vgmstream->stream_name,fsb.name_size+1, fsb.name_offset,streamFile); - /* parse format */ - if (fsbh.mode & FSOUND_MPEG) { - /* FSB3: ?; FSB4: Shatter, Way of the Samurai 3/4, Forza Horizon 1/2, Dragon Age Origins */ + /* parse codec */ + if (fsb.mode & FSOUND_MPEG) { /* FSB4: Shatter, Way of the Samurai 3/4 (PS3) */ #if defined(VGM_USE_MPEG) mpeg_custom_config cfg = {0}; cfg.fsb_padding = (vgmstream->channels > 2 ? 16 : - (fsbh.flags & FMOD_FSB_SOURCE_MPEG_PADDED4 ? 4 : - (fsbh.flags & FMOD_FSB_SOURCE_MPEG_PADDED ? 2 : 0))); + (fsb.flags & FMOD_FSB_SOURCE_MPEG_PADDED4 ? 4 : + (fsb.flags & FMOD_FSB_SOURCE_MPEG_PADDED ? 2 : 0))); - //VGM_ASSERT(fsbh.mode & FSOUND_MPEG_LAYER2, "FSB FSOUND_MPEG_LAYER2 found\n");/* not always set anyway */ - VGM_ASSERT(fsbh.mode & FSOUND_IGNORETAGS, "FSB FSOUND_IGNORETAGS found\n"); + //VGM_ASSERT(fsb.mode & FSOUND_MPEG_LAYER2, "FSB FSOUND_MPEG_LAYER2 found\n");/* not always set anyway */ + VGM_ASSERT(fsb.mode & FSOUND_IGNORETAGS, "FSB FSOUND_IGNORETAGS found\n"); vgmstream->codec_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_FSB, &cfg); if (!vgmstream->codec_data) goto fail; vgmstream->layout_type = layout_none; - #else goto fail; /* FFmpeg can't properly read FSB4 or FMOD's 0-padded MPEG data @ start_offset */ #endif } - else if (fsbh.mode & FSOUND_IMAADPCM) { /* (codec 0x69, Voxware Byte Aligned) */ + else if (fsb.mode & FSOUND_IMAADPCM) { /* FSB3: Bioshock (PC); FSB4: Blade Kitten (PC) */ /* FSOUND_IMAADPCMSTEREO is "noninterleaved, true stereo IMA", but doesn't seem to be any different * (found in FSB4: Shatter, Blade Kitten (PC), Hard Corps: Uprising (PS3)) */ - /* FSB3: Bioshock (PC); FSB4: Blade Kitten (PC) */ vgmstream->coding_type = coding_XBOX; vgmstream->layout_type = layout_none; - /* "interleaved header" IMA, which seems only used with >2ch (ex. Blade Kitten 5.1) */ - if (vgmstream->channels > 2) + /* "interleaved header" IMA, only used with >2ch (ex. Blade Kitten 6ch) + * or (seemingly) when flag is used (ex. Dead to Rights 2 (Xbox) 2ch in FSB3.1 */ + if (vgmstream->channels > 2 || (fsb.mode & FSOUND_MULTICHANNEL)) vgmstream->coding_type = coding_FSB_IMA; } - else if (fsbh.mode & FSOUND_VAG) { - /* FSB1: Jurassic Park Operation Genesis - * FSB3: ?; FSB4: Spider Man Web of Shadows, Speed Racer, Silent Hill: Shattered Memories (PS2) */ + else if (fsb.mode & FSOUND_VAG) { /* FSB1: Jurassic Park Operation Genesis (PS2), FSB4: Spider Man Web of Shadows (PSP) */ vgmstream->coding_type = coding_PSX; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x10; } - else if (fsbh.mode & FSOUND_XMA) { + else if (fsb.mode & FSOUND_XMA) { /* FSB4: Armored Core V (X360), Hard Corps (X360) */ #if defined(VGM_USE_FFMPEG) - /* FSB4: Xbox360 Armored Core V, Hard Corps, Halo Anniversary */ - ffmpeg_codec_data *ffmpeg_data = NULL; - uint8_t buf[FAKE_RIFF_BUFFER_SIZE]; + uint8_t buf[0x100]; size_t bytes, block_size, block_count; - /* not accurate but not needed by FFmpeg */ + block_size = 0x8000; /* FSB default */ - block_count = fsbh.datasize / block_size; /* read_32bitLE(custom_data_offset +0x14) -1? */ + block_count = fsb.stream_size / block_size; /* not accurate but not needed (custom_data_offset+0x14 -1?) */ - /* make a fake riff so FFmpeg can parse the XMA2 */ - bytes = ffmpeg_make_riff_xma2(buf, FAKE_RIFF_BUFFER_SIZE, fsbh.lengthsamples, fsbh.datasize, fsbh.numchannels, fsbh.deffreq, block_count, block_size); - if (bytes <= 0) - goto fail; + bytes = ffmpeg_make_riff_xma2(buf, 0x100, fsb.num_samples, fsb.stream_size, fsb.channels, fsb.sample_rate, block_count, block_size); + if (bytes <= 0) goto fail; - ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,fsbh.datasize); - if ( !ffmpeg_data ) goto fail; - vgmstream->codec_data = ffmpeg_data; + vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,fsb.stream_size); + if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - #else goto fail; #endif } - else if (fsbh.mode & FSOUND_GCADPCM) { + else if (fsb.mode & FSOUND_GCADPCM) { /* FSB3: ?; FSB4: de Blob (Wii), Night at the Museum, M. Night Shyamalan Avatar: The Last Airbender */ vgmstream->coding_type = coding_NGC_DSP_subint; vgmstream->layout_type = layout_none; vgmstream->interleave_block_size = 0x2; dsp_read_coefs_be(vgmstream, streamFile, custom_data_offset, 0x2e); } - else if (fsbh.mode & FSOUND_CELT) { /* || fsbh.mode & FSOUND_OGG (same flag, unused) */ - /* FSB4: War Thunder (PC), The Witcher 2 (PC) */ + else if (fsb.mode & FSOUND_CELT) { /* FSB4: War Thunder (PC), The Witcher 2 (PC) */ VGM_LOG("FSB4 FSOUND_CELT found\n"); goto fail; } else { /* PCM */ - if (fsbh.mode & FSOUND_8BITS) { - vgmstream->coding_type = (fsbh.mode & FSOUND_UNSIGNED) ? coding_PCM8_U : coding_PCM8; + if (fsb.mode & FSOUND_8BITS) { + vgmstream->coding_type = (fsb.mode & FSOUND_UNSIGNED) ? coding_PCM8_U : coding_PCM8; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x1; } - else { /* Rocket Knight (PC), Another Century's Episode R (PS3), Toy Story 3 (Wii) */ + else { /* Rocket Knight (PC), Another Century's Episode R (PS3), Toy Story 3 (Wii) */ /* sometimes FSOUND_STEREO/FSOUND_MONO is not set (ex. Dead Space iOS), * or only STEREO/MONO but not FSOUND_8BITS/FSOUND_16BITS is set */ - vgmstream->coding_type = (fsbh.flags & FMOD_FSB_SOURCE_BIGENDIANPCM) ? coding_PCM16BE : coding_PCM16LE; + vgmstream->coding_type = (fsb.flags & FMOD_FSB_SOURCE_BIGENDIANPCM) ? coding_PCM16BE : coding_PCM16LE; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x2; } } /* full channel interleave, used in short streams (ex. de Blob Wii SFXs) */ - if (fsbh.numchannels > 1 && (fsbh.flags & FMOD_FSB_SOURCE_NOTINTERLEAVED)) { + if (fsb.channels > 1 && (fsb.flags & FMOD_FSB_SOURCE_NOTINTERLEAVED)) { if (vgmstream->coding_type == coding_NGC_DSP_subint) vgmstream->coding_type = coding_NGC_DSP; vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = fsbh.lengthcompressedbytes / fsbh.numchannels; + vgmstream->interleave_block_size = fsb.stream_size / fsb.channels; } /* open the file for reading */ if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) goto fail; - return vgmstream; fail: close_vgmstream(vgmstream); return NULL; } + + +static STREAMFILE* setup_fsb4_wav_streamfile(STREAMFILE *streamfile, off_t subfile_offset, size_t subfile_size); + +/* FSB4 with "\0WAV" Header, found in Deadly Creatures (Wii). + * Has a 0x10 BE header that holds the filesize (unsure if this is from a proper rip). */ +VGMSTREAM * init_vgmstream_fsb4_wav(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + STREAMFILE *test_streamFile = NULL; + off_t subfile_start = 0x10; + size_t subfile_size = get_streamfile_size(streamFile) - 0x10 - 0x10; //todo + + /* check extensions */ + if ( !check_extensions(streamFile, "fsb,wii") ) + goto fail; + + if (read_32bitBE(0x00,streamFile) != 0x00574156) /* "\0WAV" */ + goto fail; + + /* parse FSB subfile */ + test_streamFile = setup_fsb4_wav_streamfile(streamFile, subfile_start,subfile_size); + if (!test_streamFile) goto fail; + + vgmstream = init_vgmstream_fsb(test_streamFile); + if (!vgmstream) goto fail; + + /* init the VGMSTREAM */ + close_streamfile(test_streamFile); + return vgmstream; + +fail: + close_streamfile(test_streamFile); + close_vgmstream(vgmstream); + return NULL; +} + +static STREAMFILE* setup_fsb4_wav_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size) { + 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,"fsb"); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c b/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c index a29fa70e2..0d79502b1 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c @@ -11,7 +11,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { uint32_t NumSamples = 0, LoopStart = 0, LoopEnd = 0; int LoopFlag = 0, ChannelCount = 0, Version, SampleRate = 0, CodingID; - int TotalStreams, TargetStream = streamFile->stream_index; + int TotalSubsongs, TargetSubsong = streamFile->stream_index; int i; /* check extension, case insensitive */ @@ -25,7 +25,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { Version = read_32bitLE(0x04,streamFile); if (Version != 0x00 && Version != 0x01) goto fail; - TotalStreams = read_32bitLE(0x08,streamFile); + TotalSubsongs = read_32bitLE(0x08,streamFile); SampleHeaderLength = read_32bitLE(0x0C,streamFile); NameTableLength = read_32bitLE(0x10,streamFile); SampleDataLength = read_32bitLE(0x14,streamFile); @@ -37,14 +37,14 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { if ((SampleHeaderLength + NameTableLength + SampleDataLength + BaseHeaderLength) != get_streamfile_size(streamFile)) goto fail; - if (TargetStream == 0) TargetStream = 1; /* default to 1 */ - if (TargetStream > TotalStreams || TotalStreams <= 0) goto fail; + if (TargetSubsong == 0) TargetSubsong = 1; /* default to 1 */ + if (TargetSubsong > TotalSubsongs || TotalSubsongs <= 0) goto fail; SampleHeaderStart = BaseHeaderLength; /* find target stream header and data offset, and read all needed values for later use * (reads one by one as the size of a single stream header is variable) */ - for (i = 1; i <= TotalStreams; i++) { + for (i = 1; i <= TotalSubsongs; i++) { off_t DataStart = 0; size_t StreamHeaderLength = 0; uint32_t SampleMode1, SampleMode2; @@ -156,11 +156,11 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { } /* stream found */ - if (i == TargetStream) { + if (i == TargetSubsong) { StartOffset = BaseHeaderLength + SampleHeaderLength + NameTableLength + DataStart; /* get stream size from next stream or datasize if there is only one */ - if (i == TotalStreams) { + if (i == TotalSubsongs) { StreamSize = SampleDataLength - DataStart; } else { uint32_t NextSampleMode = (uint32_t)read_32bitLE(SampleHeaderStart+StreamHeaderLength+0x00,streamFile); @@ -178,7 +178,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { /* get stream name */ if (NameTableLength) { - NameOffset = BaseHeaderLength + SampleHeaderLength + read_32bitLE(BaseHeaderLength + SampleHeaderLength + 0x04*(TargetStream-1),streamFile); + NameOffset = BaseHeaderLength + SampleHeaderLength + read_32bitLE(BaseHeaderLength + SampleHeaderLength + 0x04*(TargetSubsong-1),streamFile); } @@ -187,12 +187,13 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { if (!vgmstream) goto fail; vgmstream->sample_rate = SampleRate; - vgmstream->num_streams = TotalStreams; vgmstream->num_samples = NumSamples; if (LoopFlag) { vgmstream->loop_start_sample = LoopStart; vgmstream->loop_end_sample = LoopEnd; } + vgmstream->num_streams = TotalSubsongs; + vgmstream->stream_size = StreamSize; vgmstream->meta_type = meta_FSB5; if (NameOffset) read_string(vgmstream->stream_name,STREAM_NAME_SIZE, NameOffset,streamFile); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb_encrypted.c b/Frameworks/vgmstream/vgmstream/src/meta/fsb_encrypted.c new file mode 100644 index 000000000..98af7df96 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb_encrypted.c @@ -0,0 +1,158 @@ +#include "meta.h" +#include "fsb_keys.h" + +#define FSB_KEY_MAX 128 /* probably 32 */ + +static STREAMFILE* setup_fsb_streamfile(STREAMFILE *streamFile, const uint8_t * key, size_t key_size, int is_alt); + + +/* fully encrypted FSBs */ +VGMSTREAM * init_vgmstream_fsb_encrypted(STREAMFILE * streamFile) { + VGMSTREAM * vgmstream = NULL; + + /* check extensions */ + if ( !check_extensions(streamFile, "fsb") ) + goto fail; + + /* ignore non-encrypted FSB */ + if ((read_32bitBE(0x00,streamFile) & 0xFFFFFF00) == 0x46534200) /* "FSB\0" */ + goto fail; + + + /* try fsbkey + all combinations of FSB4/5 and decryption algorithms */ + { + STREAMFILE *temp_streamFile = NULL; + uint8_t key[FSB_KEY_MAX]; + size_t key_size = read_key_file(key, FSB_KEY_MAX, streamFile); + + if (key_size) { + { + temp_streamFile = setup_fsb_streamfile(streamFile, key,key_size, 0); + if (!temp_streamFile) goto fail; + + if (!vgmstream) vgmstream = init_vgmstream_fsb(temp_streamFile); + if (!vgmstream) vgmstream = init_vgmstream_fsb5(temp_streamFile); + + close_streamfile(temp_streamFile); + } + + if (!vgmstream) { + temp_streamFile = setup_fsb_streamfile(streamFile, key,key_size, 1); + if (!temp_streamFile) goto fail; + + if (!vgmstream) vgmstream = init_vgmstream_fsb(temp_streamFile); + if (!vgmstream) vgmstream = init_vgmstream_fsb5(temp_streamFile); + + close_streamfile(temp_streamFile); + } + } + } + + + /* try all keys until one works */ + if (!vgmstream) { + int i; + STREAMFILE *temp_streamFile = NULL; + + for (i = 0; i < fsbkey_list_count; i++) { + fsbkey_info entry = fsbkey_list[i]; + ;VGM_LOG("fsbkey: size=%i, is_fsb5=%i, is_alt=%i\n", entry.fsbkey_size,entry.is_fsb5, entry.is_alt); + + temp_streamFile = setup_fsb_streamfile(streamFile, entry.fsbkey, entry.fsbkey_size, entry.is_alt); + if (!temp_streamFile) goto fail; + + if (fsbkey_list[i].is_fsb5) { + vgmstream = init_vgmstream_fsb5(temp_streamFile); + } else { + vgmstream = init_vgmstream_fsb(temp_streamFile); + } + + close_streamfile(temp_streamFile); + if (vgmstream) break; + } + } + + if (!vgmstream) + goto fail; + + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + + +typedef struct { + uint8_t key[FSB_KEY_MAX]; + size_t key_size; + int is_alt; +} fsb_decryption_data; + +/* Encrypted FSB info from guessfsb and fsbext */ +static size_t fsb_decryption_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, fsb_decryption_data* data) { + static const unsigned char reverse_bits_table[] = { /* LUT to simplify, could use some bitswap function */ + 0x00,0x80,0x40,0xC0,0x20,0xA0,0x60,0xE0,0x10,0x90,0x50,0xD0,0x30,0xB0,0x70,0xF0, + 0x08,0x88,0x48,0xC8,0x28,0xA8,0x68,0xE8,0x18,0x98,0x58,0xD8,0x38,0xB8,0x78,0xF8, + 0x04,0x84,0x44,0xC4,0x24,0xA4,0x64,0xE4,0x14,0x94,0x54,0xD4,0x34,0xB4,0x74,0xF4, + 0x0C,0x8C,0x4C,0xCC,0x2C,0xAC,0x6C,0xEC,0x1C,0x9C,0x5C,0xDC,0x3C,0xBC,0x7C,0xFC, + 0x02,0x82,0x42,0xC2,0x22,0xA2,0x62,0xE2,0x12,0x92,0x52,0xD2,0x32,0xB2,0x72,0xF2, + 0x0A,0x8A,0x4A,0xCA,0x2A,0xAA,0x6A,0xEA,0x1A,0x9A,0x5A,0xDA,0x3A,0xBA,0x7A,0xFA, + 0x06,0x86,0x46,0xC6,0x26,0xA6,0x66,0xE6,0x16,0x96,0x56,0xD6,0x36,0xB6,0x76,0xF6, + 0x0E,0x8E,0x4E,0xCE,0x2E,0xAE,0x6E,0xEE,0x1E,0x9E,0x5E,0xDE,0x3E,0xBE,0x7E,0xFE, + 0x01,0x81,0x41,0xC1,0x21,0xA1,0x61,0xE1,0x11,0x91,0x51,0xD1,0x31,0xB1,0x71,0xF1, + 0x09,0x89,0x49,0xC9,0x29,0xA9,0x69,0xE9,0x19,0x99,0x59,0xD9,0x39,0xB9,0x79,0xF9, + 0x05,0x85,0x45,0xC5,0x25,0xA5,0x65,0xE5,0x15,0x95,0x55,0xD5,0x35,0xB5,0x75,0xF5, + 0x0D,0x8D,0x4D,0xCD,0x2D,0xAD,0x6D,0xED,0x1D,0x9D,0x5D,0xDD,0x3D,0xBD,0x7D,0xFD, + 0x03,0x83,0x43,0xC3,0x23,0xA3,0x63,0xE3,0x13,0x93,0x53,0xD3,0x33,0xB3,0x73,0xF3, + 0x0B,0x8B,0x4B,0xCB,0x2B,0xAB,0x6B,0xEB,0x1B,0x9B,0x5B,0xDB,0x3B,0xBB,0x7B,0xFB, + 0x07,0x87,0x47,0xC7,0x27,0xA7,0x67,0xE7,0x17,0x97,0x57,0xD7,0x37,0xB7,0x77,0xF7, + 0x0F,0x8F,0x4F,0xCF,0x2F,0xAF,0x6F,0xEF,0x1F,0x9F,0x5F,0xDF,0x3F,0xBF,0x7F,0xFF + }; + size_t bytes_read; + int i; + + bytes_read = streamfile->read(streamfile, dest, offset, length); + + /* decrypt data (inverted bits and xor) */ + for (i = 0; i < bytes_read; i++) { + uint8_t xor = data->key[(offset + i) % data->key_size]; + uint8_t val = dest[i]; + if (data->is_alt) { + dest[i] = reverse_bits_table[val ^ xor]; + } + else { + dest[i] = reverse_bits_table[val] ^ xor; + } + } + + return bytes_read; +} + +static STREAMFILE* setup_fsb_streamfile(STREAMFILE *streamFile, const uint8_t * key, size_t key_size, int is_alt) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + fsb_decryption_data io_data = {0}; + size_t io_data_size = sizeof(fsb_decryption_data); + + /* setup decryption with key (external) */ + if (!key_size || key_size > FSB_KEY_MAX) goto fail; + + memcpy(io_data.key, key, key_size); + io_data.key_size = key_size; + io_data.is_alt = is_alt; + + /* setup subfile */ + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, fsb_decryption_read); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h new file mode 100644 index 000000000..1b3a6081d --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h @@ -0,0 +1,126 @@ +#ifndef _FSB_KEYS_H_ +#define _FSB_KEYS_H_ + +typedef struct { + int is_fsb5; + int is_alt; + size_t fsbkey_size; + const uint8_t *fsbkey; +} fsbkey_info; + +/** + * List of known keys, found in aluigi's site (http://aluigi.altervista.org), forums and with guessfsb.exe + */ + +/* DJ Hero 2 (X360) */ //"nos71RiT" +static const uint8_t key_dj2[] = { 0x6E,0x6F,0x73,0x37,0x31,0x52,0x69,0x54 }; + +/* Double Fine Productions: Brutal Legend, Massive Chalice, etc (multi) */ //"DFm3t4lFTW" +static const uint8_t key_dfp[] = { 0x44,0x46,0x6D,0x33,0x74,0x34,0x6C,0x46,0x54,0x57 }; + +/* N++ (PC?) */ //"H$#FJa%7gRZZOlxLiN50&g5Q" +static const uint8_t key_npp[] = { 0x48,0x24,0x23,0x46,0x4A,0x61,0x25,0x37,0x67,0x52,0x5A,0x5A,0x4F,0x6C,0x78,0x4C,0x69,0x4E,0x35,0x30,0x26,0x67,0x35,0x51 }; + +/* Slightly Mad Studios: Project CARS (PC?), World of Speed (PC) */ //"sTOoeJXI2LjK8jBMOk8h5IDRNZl3jq3I" +static const uint8_t key_sms[] = { 0x73,0x54,0x4F,0x6F,0x65,0x4A,0x58,0x49,0x32,0x4C,0x6A,0x4B,0x38,0x6A,0x42,0x4D,0x4F,0x6B,0x38,0x68,0x35,0x49,0x44,0x52,0x4E,0x5A,0x6C,0x33,0x6A,0x71,0x33,0x49 }; + +/* Ghost in the Shell: First Assault (PC) */ //"%lAn2{Pi*Lhw3T}@7*!kV=?qS$@iNlJ" +static const uint8_t key_gfs[] = { 0x25,0x6C,0x41,0x6E,0x32,0x7B,0x50,0x69,0x2A,0x4C,0x68,0x77,0x33,0x54,0x7D,0x40,0x37,0x2A,0x21,0x6B,0x56,0x3D,0x3F,0x71,0x53,0x24,0x40,0x69,0x4E,0x6C,0x4A }; + +/* RevHeadz Engine Sounds (Mobile) */ //"1^7%82#&5$~/8sz" +static const uint8_t key_rev[] = { 0x31,0x5E,0x37,0x25,0x38,0x32,0x23,0x26,0x35,0x24,0x7E,0x2F,0x38,0x73,0x7A }; + +/* Dark Souls 3 (PC) */ //"FDPrVuT4fAFvdHJYAgyMzRF4EcBAnKg" +static const uint8_t key_ds3[] = { 0x46,0x44,0x50,0x72,0x56,0x75,0x54,0x34,0x66,0x41,0x46,0x76,0x64,0x48,0x4A,0x59,0x41,0x67,0x79,0x4D,0x7A,0x52,0x46,0x34,0x45,0x63,0x42,0x41,0x6E,0x4B,0x67 }; + +/* Mortal Kombat X */ +static const uint8_t key_mkx[] = { 0x99,0x61,0x64,0xB5,0xFC,0x0F,0x40,0x29,0x83,0xF6,0x1F,0x22,0x0B,0xB5,0x1D,0xC6 }; + +/* Xian Xia Chuan (PC) */ //"gat@tcqs2010" +static const uint8_t key_xxc[] = { 0x67,0x61,0x74,0x40,0x74,0x63,0x71,0x73,0x32,0x30,0x31,0x30 }; + +/* Mirror War Reincarnation of Holiness (PC) */ //"logicsounddesignmwsdev" +static const uint8_t key_mwr[] = { 0x6C,0x6F,0x67,0x69,0x63,0x73,0x6F,0x75,0x6E,0x64,0x64,0x65,0x73,0x69,0x67,0x6E,0x6D,0x77,0x73,0x64,0x65,0x76 }; + +/* Need for Speed Shift 2 Unleashed (PC demo?) */ //"p&oACY^c4LK5C2v^x5nIO6kg5vNH$tlj" +static const uint8_t key_n2u[] = { 0x70,0x26,0x6F,0x41,0x43,0x59,0x5E,0x63,0x34,0x4C,0x4B,0x35,0x43,0x32,0x76,0x5E,0x78,0x35,0x6E,0x49,0x4F,0x36,0x6B,0x67,0x35,0x76,0x4E,0x48,0x24,0x74,0x6C,0x6A }; + +/* Critter Crunch, Superbrothers: Sword & Sworcery */ //"j1$Mk0Libg3#apEr42mo" +static const uint8_t key_ccr[] = { 0x6A,0x31,0x24,0x4D,0x6B,0x30,0x4C,0x69,0x62,0x67,0x33,0x23,0x61,0x70,0x45,0x72,0x34,0x32,0x6D,0x6F }; + +/* Cyphers */ //"@kdj43nKDN^k*kj3ndf02hd95nsl(NJG" +static const uint8_t key_cyp[] = { 0x40,0x6B,0x64,0x6A,0x34,0x33,0x6E,0x4B,0x44,0x4E,0x5E,0x6B,0x2A,0x6B,0x6A,0x33,0x6E,0x64,0x66,0x30,0x32,0x68,0x64,0x39,0x35,0x6E,0x73,0x6C,0x28,0x4E,0x4A,0x47 }; + +/* Xuan Dou Zhi Wang / King of Combat */ //"Xiayuwu69252.Sonicli81223#$*@*0" +static const uint8_t key_xdz[] = { 0x58,0x69,0x61,0x79,0x75,0x77,0x75,0x36,0x39,0x32,0x35,0x32,0x2E,0x53,0x6F,0x6E,0x69,0x63,0x6C,0x69,0x38,0x31,0x32,0x32,0x33,0x23,0x24,0x2A,0x40,0x2A,0x30 }; + +/* Ji Feng Zhi Ren / Kritika Online */ //"kri_tika_5050_" +static const uint8_t key_jzz[] = { 0x6B,0x72,0x69,0x5F,0x74,0x69,0x6B,0x61,0x5F,0x35,0x30,0x35,0x30,0x5F }; + +/* Invisible Inc. */ //"mint78run52" +static const uint8_t key_inv[] = { 0x6D,0x69,0x6E,0x74,0x37,0x38,0x72,0x75,0x6E,0x35,0x32 }; + +/* Guitar Hero 3 */ //"5atu6w4zaw" +static const uint8_t key_gh3[] = { 0x35,0x61,0x74,0x75,0x36,0x77,0x34,0x7A,0x61,0x77 }; + +// Unknown: +// - Battle: Los Angeles +// - Guitar Hero: Warriors of Rock, DJ hero FSB +// - Longmenkezhan +// - Gas Guzzlers: Combat Carnage (PC?) "C5FA83EA64B34EC2BFE" hex or text? [FSB5] + +static const fsbkey_info fsbkey_list[] = { + { 0,0, sizeof(key_dj2),key_dj2 }, + { 0,0, sizeof(key_dfp),key_dfp }, + { 1,0, sizeof(key_dfp),key_dfp },//untested + { 1,1, sizeof(key_dfp),key_dfp },//untested + { 1,0, sizeof(key_npp),key_npp }, + { 1,0, sizeof(key_sms),key_sms }, + { 1,0, sizeof(key_gfs),key_gfs }, + { 1,0, sizeof(key_rev),key_rev }, + { 1,0, sizeof(key_ds3),key_ds3 },//untested + { 1,1, sizeof(key_ds3),key_ds3 }, + { 1,0, sizeof(key_mkx),key_mkx },//untested + { 1,1, sizeof(key_mkx),key_mkx },//untested + { 0,0, sizeof(key_xxc),key_xxc },//untested + { 0,1, sizeof(key_xxc),key_xxc },//untested + { 1,0, sizeof(key_xxc),key_xxc },//untested + { 1,1, sizeof(key_xxc),key_xxc },//untested + { 0,0, sizeof(key_mwr),key_mwr },//untested + { 0,1, sizeof(key_mwr),key_mwr },//untested + { 1,0, sizeof(key_mwr),key_mwr },//untested + { 1,1, sizeof(key_mwr),key_mwr },//untested + { 0,0, sizeof(key_n2u),key_n2u },//untested + { 0,1, sizeof(key_n2u),key_n2u },//untested + { 1,0, sizeof(key_n2u),key_n2u },//untested + { 1,1, sizeof(key_n2u),key_n2u },//untested + { 0,0, sizeof(key_ccr),key_ccr },//untested + { 0,1, sizeof(key_ccr),key_ccr },//untested + { 1,0, sizeof(key_ccr),key_ccr },//untested + { 1,1, sizeof(key_ccr),key_ccr },//untested + { 0,0, sizeof(key_cyp),key_cyp },//untested + { 0,1, sizeof(key_cyp),key_cyp },//untested + { 1,0, sizeof(key_cyp),key_cyp },//untested + { 1,1, sizeof(key_cyp),key_cyp },//untested + { 0,0, sizeof(key_xdz),key_xdz },//untested + { 0,1, sizeof(key_xdz),key_xdz },//untested + { 1,0, sizeof(key_xdz),key_xdz },//untested + { 1,1, sizeof(key_xdz),key_xdz },//untested + { 0,0, sizeof(key_jzz),key_jzz },//untested + { 0,1, sizeof(key_jzz),key_jzz },//untested + { 1,0, sizeof(key_jzz),key_jzz },//untested + { 1,1, sizeof(key_jzz),key_jzz },//untested + { 0,0, sizeof(key_inv),key_inv },//untested + { 0,1, sizeof(key_inv),key_inv },//untested + { 1,0, sizeof(key_inv),key_inv },//untested + { 1,1, sizeof(key_inv),key_inv },//untested + { 0,0, sizeof(key_gh3),key_gh3 },//untested + { 0,1, sizeof(key_gh3),key_gh3 },//untested + { 1,0, sizeof(key_gh3),key_gh3 },//untested + { 1,1, sizeof(key_gh3),key_gh3 },//untested + +}; +static const int fsbkey_list_count = sizeof(fsbkey_list) / sizeof(fsbkey_list[0]); + + +#endif /* _FSB_KEYS_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/gtd.c b/Frameworks/vgmstream/vgmstream/src/meta/gtd.c index c334ad5cd..9002fdd20 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/gtd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/gtd.c @@ -6,7 +6,7 @@ typedef enum { XMA2, ATRAC9 } gtd_codec; /* GTD - found in Knights Contract (X360, PS3), Valhalla Knights 3 (PSV) */ VGMSTREAM * init_vgmstream_gtd(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - off_t start_offset, chunk_offset, stpr_offset, name_offset = 0; + off_t start_offset, chunk_offset, stpr_offset, name_offset = 0, loop_start_offset, loop_end_offset; size_t data_size, chunk_size; int loop_flag, channel_count, sample_rate; int num_samples, loop_start_sample, loop_end_sample; @@ -48,6 +48,9 @@ VGMSTREAM * init_vgmstream_gtd(STREAMFILE *streamFile) { start_offset = 0x34 + read_32bitLE(0x30,streamFile); channel_count = read_32bitLE(0x10,streamFile); sample_rate = read_32bitLE(0x14,streamFile); + loop_start_offset = read_32bitLE(0x1c, streamFile); + loop_end_offset = read_32bitLE(0x20, streamFile); + loop_flag = loop_end_offset > loop_start_offset; at9_config_data = read_32bitBE(0x28,streamFile); /* 0x18-0x28: fixed/unknown values */ @@ -105,7 +108,10 @@ VGMSTREAM * init_vgmstream_gtd(STREAMFILE *streamFile) { if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_ATRAC9; vgmstream->layout_type = layout_none; - + if (loop_flag) { + vgmstream->loop_start_sample = atrac9_bytes_to_samples(loop_start_offset - start_offset, vgmstream->codec_data); + vgmstream->loop_end_sample = atrac9_bytes_to_samples(loop_end_offset - start_offset, vgmstream->codec_data); + } vgmstream->num_samples = atrac9_bytes_to_samples(data_size, vgmstream->codec_data); break; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/hca.c b/Frameworks/vgmstream/vgmstream/src/meta/hca.c index 102614604..fcd599be5 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/hca.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/hca.c @@ -54,7 +54,7 @@ VGMSTREAM * init_vgmstream_hca(STREAMFILE *streamFile) { /* find decryption key in external file or preloaded list */ { uint8_t keybuf[8]; - if ( read_key_file(keybuf, 8, streamFile) ) { + if (read_key_file(keybuf, 8, streamFile) == 8) { ciphKey2 = get_32bitBE(keybuf+0); ciphKey1 = get_32bitBE(keybuf+4); } else { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h index d83949070..231307cda 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h @@ -222,6 +222,12 @@ static const hcakey_info hcakey_list[] = { // Shin Tennis no Ouji-sama: Rising Beat (iOS/Android) {4902201417679}, // 0000047561F95FCF + // Kai-ri-Sei Million Arthur (Vita) + {1782351729464341796}, // 18BC2F7463867524 + + // Dx2 Shin Megami Tensei Liberation (iOS/Android) + {118714477}, // 000000000713706D + }; #endif/*_HCA_KEYS_H_*/ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/kma9.c b/Frameworks/vgmstream/vgmstream/src/meta/kma9.c new file mode 100644 index 000000000..16f2e7835 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/kma9.c @@ -0,0 +1,79 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* KMA9 - Koei Tecmo's custom ATRAC9 [Nobunaga no Yabou - Souzou (Vita)] */ +VGMSTREAM * init_vgmstream_kma9(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + size_t stream_size; + int loop_flag, channel_count; + int total_subsongs = 0, target_subsong = streamFile->stream_index; + + + /* check extension */ + if ( !check_extensions(streamFile,"km9") ) + goto fail; + + /* check header */ + if (read_32bitBE(0x00,streamFile) != 0x4B4D4139) /* "KMA9" */ + goto fail; + + start_offset = read_32bitLE(0x04,streamFile); + channel_count = read_16bitLE(0x32,streamFile); + loop_flag = (read_32bitLE(0x28,streamFile) != 0); + + total_subsongs = read_32bitLE(0x08,streamFile); + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + /* 0x0c: unknown */ + stream_size = read_32bitLE(0x14,streamFile); /* per subsong */ + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = read_32bitLE(0x34,streamFile); + vgmstream->num_samples = read_32bitLE(0x18,streamFile); /* without skip_samples? */ + vgmstream->loop_start_sample = read_32bitLE(0x24,streamFile); /* with skip_samples? */ + vgmstream->loop_end_sample = vgmstream->num_samples; /* 0x28 looks like end samples but isn't, no idea */ + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; + vgmstream->meta_type = meta_KMA9; + +#ifdef VGM_USE_ATRAC9 + { + atrac9_config cfg = {0}; + + cfg.type = ATRAC9_KMA9; + cfg.channels = vgmstream->channels; + cfg.config_data = read_32bitBE(0x5c,streamFile); + cfg.encoder_delay = read_32bitLE(0x20,streamFile); + + cfg.interleave_skip = read_32bitLE(0x10,streamFile); /* 1 superframe */ + cfg.subsong_skip = total_subsongs; + start_offset += (target_subsong-1) * cfg.interleave_skip * (cfg.subsong_skip-1); + + vgmstream->codec_data = init_atrac9(&cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_ATRAC9; + vgmstream->layout_type = layout_none; + + if (loop_flag) { /* seems correct but must double check */ + vgmstream->loop_start_sample -= cfg.encoder_delay; + //vgmstream->loop_end_sample -= cfg.encoder_delay; + } + } +#else + goto fail; +#endif + + /* open the file for reading */ + if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ktss.c b/Frameworks/vgmstream/vgmstream/src/meta/ktss.c index 632285945..9884b3b42 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ktss.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ktss.c @@ -6,7 +6,8 @@ VGMSTREAM * init_vgmstream_ktss(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; int loop_flag, channel_count; - int32_t loop_length; + int8_t version; + int32_t loop_length, coef_start_offset, coef_spacing; off_t start_offset; if (!check_extensions(streamFile, "ktss")) @@ -15,6 +16,19 @@ VGMSTREAM * init_vgmstream_ktss(STREAMFILE *streamFile) { if (read_32bitBE(0, streamFile) != 0x4B545353) /* "KTSS" */ goto fail; + /* check type details */ + version = read_8bit(0x22, streamFile); + if (version == 1) { + coef_start_offset = 0x40; + coef_spacing = 0x2e; + } + else if (version == 3) { // Fire Emblem Warriors (Switch) + coef_start_offset = 0x5c; + coef_spacing = 0x60; + } + else + goto fail; + loop_length = read_32bitLE(0x38, streamFile); loop_flag = loop_length > 0; channel_count = read_8bit(0x29, streamFile); @@ -35,7 +49,7 @@ VGMSTREAM * init_vgmstream_ktss(STREAMFILE *streamFile) { vgmstream->interleave_block_size = 0x8; - dsp_read_coefs_le(vgmstream, streamFile, 0x40, 0x2e); + dsp_read_coefs_le(vgmstream, streamFile, coef_start_offset, coef_spacing); start_offset = read_32bitLE(0x24, streamFile) + 0x20; if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) @@ -46,4 +60,3 @@ fail: close_vgmstream(vgmstream); return NULL; } - diff --git a/Frameworks/vgmstream/vgmstream/src/meta/meta.h b/Frameworks/vgmstream/vgmstream/src/meta/meta.h index cd7a78c7a..8088dc7c6 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/meta.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/meta.h @@ -99,11 +99,14 @@ typedef struct { meta_t meta_type; layout_t layout_type; - /* XOR setup (SCD) */ - int decryption_enabled; - void (*decryption_callback)(void *ptr, size_t size, size_t nmemb, void *datasource, int bytes_read); + off_t stream_size; + int total_subsongs; + + /* decryption setup */ + void (*decryption_callback)(void *ptr, size_t size, size_t nmemb, void *datasource); uint8_t scd_xor; off_t scd_xor_length; + uint32_t sngw_xor; } vgm_vorbis_info_t; @@ -690,4 +693,17 @@ VGMSTREAM * init_vgmstream_flx(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_mogg(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_kma9(STREAMFILE * streamFile); + +VGMSTREAM * init_vgmstream_fsb_encrypted(STREAMFILE * streamFile); + +VGMSTREAM * init_vgmstream_xwc(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_atsl3(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_sps_n1(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_atx(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_sqex_sead(STREAMFILE * streamFile); #endif /*_META_H*/ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/nsw_opus.c b/Frameworks/vgmstream/vgmstream/src/meta/nsw_opus.c index a95a18f1d..9b6bd83fd 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/nsw_opus.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/nsw_opus.c @@ -8,10 +8,11 @@ VGMSTREAM * init_vgmstream_nsw_opus(STREAMFILE *streamFile) { off_t start_offset; int loop_flag = 0, channel_count; int num_samples = 0, loop_start = 0, loop_end = 0; - off_t offset = 0; + off_t offset = 0, data_offset; + size_t data_size; /* check extension, case insensitive */ - if ( !check_extensions(streamFile,"opus,lopus")) /* no relation to Ogg Opus */ + if ( !check_extensions(streamFile,"opus,lopus,nop")) /* no relation to Ogg Opus */ goto fail; /* variations, maybe custom */ @@ -32,17 +33,33 @@ VGMSTREAM * init_vgmstream_nsw_opus(STREAMFILE *streamFile) { loop_start = read_32bitLE(0x08,streamFile); loop_end = read_32bitLE(0x0c,streamFile); } + else if (read_32bitBE(0x00, streamFile) == 0x73616466 && read_32bitBE(0x08, streamFile) == 0x6f707573) { /* Xenoblade Chronicles 2 */ + offset = read_32bitLE(0x1c, streamFile); + + num_samples = read_32bitLE(0x28, streamFile); + loop_flag = read_8bit(0x19, streamFile); + if (loop_flag) { + loop_start = read_32bitLE(0x2c, streamFile); + loop_end = read_32bitLE(0x30, streamFile); + } + } else { offset = 0x00; } - if (read_32bitBE(offset + 0x00,streamFile) != 0x01000080) + if ((uint32_t)read_32bitLE(offset + 0x00,streamFile) != 0x80000001) goto fail; + + channel_count = read_8bit(offset + 0x09, streamFile); + /* 0x0a: packet size if CBR, 0 if VBR */ + data_offset = offset + read_32bitLE(offset + 0x10, streamFile); - start_offset = offset + 0x28; - channel_count = read_8bit(offset + 0x09,streamFile); /* assumed */ - /* 0x0a: packet size if CBR?, other values: no idea */ + if ((uint32_t)read_32bitLE(data_offset, streamFile) != 0x80000004) + goto fail; + + data_size = read_32bitLE(data_offset + 0x04, streamFile); + start_offset = data_offset + 0x08; loop_flag = (loop_end > 0); /* -1 when not set */ @@ -60,10 +77,9 @@ VGMSTREAM * init_vgmstream_nsw_opus(STREAMFILE *streamFile) { #ifdef VGM_USE_FFMPEG { uint8_t buf[0x100]; - size_t bytes, skip, data_size; + size_t bytes, skip; ffmpeg_custom_config cfg; - data_size = get_streamfile_size(streamFile) - start_offset; skip = 0; //todo bytes = ffmpeg_make_opus_header(buf,0x100, vgmstream->channels, skip, vgmstream->sample_rate); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c b/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c new file mode 100644 index 000000000..a0a72ff22 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c @@ -0,0 +1,453 @@ +#include "../vgmstream.h" + +#ifdef VGM_USE_VORBIS +#include +#include +#include "meta.h" +#include + +#define OGG_DEFAULT_BITSTREAM 0 + +static size_t ov_read_func(void *ptr, size_t size, size_t nmemb, void * datasource) { + ogg_vorbis_streamfile * const ov_streamfile = datasource; + size_t bytes_read, items_read; + + off_t real_offset = ov_streamfile->start + ov_streamfile->offset; + size_t max_bytes = size * nmemb; + + /* clamp for virtual filesize */ + if (max_bytes > ov_streamfile->size - ov_streamfile->offset) + max_bytes = ov_streamfile->size - ov_streamfile->offset; + + bytes_read = read_streamfile(ptr, real_offset, max_bytes, ov_streamfile->streamfile); + items_read = bytes_read / size; + + /* may be encrypted */ + if (ov_streamfile->decryption_callback) { + ov_streamfile->decryption_callback(ptr, size, items_read, ov_streamfile); + } + + ov_streamfile->offset += items_read * size; + + return items_read; +} + +static int ov_seek_func(void *datasource, ogg_int64_t offset, int whence) { + ogg_vorbis_streamfile * const ov_streamfile = datasource; + ogg_int64_t base_offset, new_offset; + + switch (whence) { + case SEEK_SET: + base_offset = 0; + break; + case SEEK_CUR: + base_offset = ov_streamfile->offset; + break; + case SEEK_END: + base_offset = ov_streamfile->size; + break; + default: + return -1; + break; + } + + + new_offset = base_offset + offset; + if (new_offset < 0 || new_offset > ov_streamfile->size) { + return -1; /* *must* return -1 if stream is unseekable */ + } else { + ov_streamfile->offset = new_offset; + return 0; + } +} + +static long ov_tell_func(void * datasource) { + ogg_vorbis_streamfile * const ov_streamfile = datasource; + return ov_streamfile->offset; +} + +static int ov_close_func(void * datasource) { + /* needed as setting ov_close_func in ov_callbacks to NULL doesn't seem to work + * (closing the streamfile is done in free_ogg_vorbis) */ + return 0; +} + + +static void um3_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; + + /* first 0x800 bytes are xor'd with 0xff */ + if (ov_streamfile->offset < 0x800) { + int num_crypt = 0x800 - ov_streamfile->offset; + if (num_crypt > bytes_read) + num_crypt = bytes_read; + + for (i = 0; i < num_crypt; i++) + ((uint8_t*)ptr)[i] ^= 0xff; + } +} + +static void kovs_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; + + /* first 0x100 bytes are xor'd with offset */ + if (ov_streamfile->offset < 0x100) { + int max_offset = ov_streamfile->offset + bytes_read; + if (max_offset > 0x100) + max_offset = 0x100; + + for (i = ov_streamfile->offset; i < max_offset; i++) { + ((uint8_t*)ptr)[i-ov_streamfile->offset] ^= i; + } + } +} + +static void psychic_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) { + size_t bytes_read = size*nmemb; + int i; + + /* add 0x23 ('#') */ + { + for (i = 0; i < bytes_read; i++) + ((uint8_t*)ptr)[i] += 0x23; + } +} + +static void sngw_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; + char *header_id = "OggS"; + uint8_t key[4]; + + put_32bitBE(key, ov_streamfile->sngw_xor); + + /* bytes are xor'd with key and nibble-swapped */ + { + for (i = 0; i < bytes_read; i++) { + if (ov_streamfile->offset+i < 0x04) { + /* replace key in the first 4 bytes with "OggS" */ + ((uint8_t*)ptr)[i] = (uint8_t)header_id[(ov_streamfile->offset + i) % 4]; + } + else { + uint8_t val = ((uint8_t*)ptr)[i] ^ key[(ov_streamfile->offset + i) % 4]; + ((uint8_t*)ptr)[i] = ((val << 4) & 0xf0) | ((val >> 4) & 0x0f); + } + } + } +} + +static void isd_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) { + static const uint8_t key[16] = { + 0xe0,0x00,0xe0,0x00,0xa0,0x00,0x00,0x00,0xe0,0x00,0xe0,0x80,0x40,0x40,0x40,0x00 + }; + 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] ^= key[(ov_streamfile->offset + i) % 16]; + } +} + + +/* Ogg Vorbis, by way of libvorbisfile; may contain loop comments */ +VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { + char filename[PATH_LIMIT]; + vgm_vorbis_info_t inf = {0}; + off_t start_offset = 0; + + int is_ogg = 0; + int is_um3 = 0; + int is_kovs = 0; + int is_psychic = 0; + int is_sngw = 0; + int is_isd = 0; + + + /* check extension */ + if (check_extensions(streamFile,"ogg,logg")) { /* .ogg: standard/psychic, .logg: renamed for plugins */ + is_ogg = 1; + } else if (check_extensions(streamFile,"um3")) { + is_um3 = 1; + } else if (check_extensions(streamFile,"kvs,kovs")) { /* .kvs: Atelier Sophie (PC), kovs: header id only? */ + is_kovs = 1; + } else if (check_extensions(streamFile,"sngw")) { /* .sngw: Devil May Cry 4 SE (PC), Biohazard 6 (PC) */ + is_sngw = 1; + } else if (check_extensions(streamFile,"isd")) { /* .isd: Azure Striker Gunvolt (PC) */ + is_isd = 1; + } else { + goto fail; + } + streamFile->get_name(streamFile,filename,sizeof(filename)); + + /* check standard Ogg Vorbis */ + if (is_ogg) { + + /* check Psychic Software obfuscation (Darkwind: War on Wheels PC) */ + if (read_32bitBE(0x00,streamFile) == 0x2c444430) { + is_psychic = 1; + inf.decryption_callback = psychic_ogg_decryption_callback; + } + else if (read_32bitBE(0x00,streamFile) != 0x4f676753) { /* "OggS" */ + goto fail; /* not known (ex. Wwise) */ + } + } + + /* check "Ultramarine3" (???), may be encrypted */ + if (is_um3) { + if (read_32bitBE(0x00,streamFile) != 0x4f676753) { /* "OggS" */ + inf.decryption_callback = um3_ogg_decryption_callback; + } + } + + /* check KOVS (Koei Tecmo games), encrypted and has an actual header */ + if (is_kovs) { + if (read_32bitBE(0x00,streamFile) != 0x4b4f5653) { /* "KOVS" */ + goto fail; + } + inf.loop_start = read_32bitLE(0x08,streamFile); + inf.loop_flag = (inf.loop_start != 0); + inf.decryption_callback = kovs_ogg_decryption_callback; + + start_offset = 0x20; + } + + /* check SNGW (Capcom's MT Framework PC games), may be encrypted */ + if (is_sngw) { + if (read_32bitBE(0x00,streamFile) != 0x4f676753) { /* "OggS" */ + inf.sngw_xor = read_32bitBE(0x00,streamFile); + inf.decryption_callback = sngw_ogg_decryption_callback; + } + } + + /* check ISD (Gunvolt PC) */ + if (is_isd) { + inf.decryption_callback = isd_ogg_decryption_callback; + + //todo looping unknown, not in Ogg comments + // game has sound/GV_steam.* files with info about sound/stream/*.isd + //- .ish: constant id/names + //- .isl: unknown table, maybe looping? + //- .isf: format table, ordered like file numbers, 0x18 header with: + // 0x00(2): ?, 0x02(2): channels, 0x04: sample rate, 0x08: skip samples (in PCM bytes), always 32000 + // 0x0c(2): PCM block size, 0x0e(2): PCM bps, 0x10: null, 0x18: samples (in PCM bytes) + } + + + if (is_um3) { + inf.meta_type = meta_OGG_UM3; + } else if (is_kovs) { + inf.meta_type = meta_OGG_KOVS; + } else if (is_psychic) { + inf.meta_type = meta_OGG_PSYCHIC; + } else if (is_sngw) { + inf.meta_type = meta_OGG_SNGW; + } else if (is_isd) { + inf.meta_type = meta_OGG_ISD; + } else { + inf.meta_type = meta_OGG_VORBIS; + } + inf.layout_type = layout_ogg_vorbis; + + return init_vgmstream_ogg_vorbis_callbacks(streamFile, filename, NULL, start_offset, &inf); + +fail: + return NULL; +} + +VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, const char * filename, ov_callbacks *callbacks_p, off_t start, const vgm_vorbis_info_t *vgm_inf) { + VGMSTREAM * vgmstream = NULL; + ogg_vorbis_codec_data * data = NULL; + OggVorbis_File *ovf = NULL; + vorbis_info *vi; + + int loop_flag = vgm_inf->loop_flag; + int32_t loop_start = vgm_inf->loop_start; + int loop_length_found = vgm_inf->loop_length_found; + int32_t loop_length = vgm_inf->loop_length; + int loop_end_found = vgm_inf->loop_end_found; + int32_t loop_end = vgm_inf->loop_end; + size_t stream_size = vgm_inf->stream_size ? + vgm_inf->stream_size : + get_streamfile_size(streamFile) - start; + + ov_callbacks default_callbacks; + + if (!callbacks_p) { + default_callbacks.read_func = ov_read_func; + default_callbacks.seek_func = ov_seek_func; + default_callbacks.close_func = ov_close_func; + default_callbacks.tell_func = ov_tell_func; + + callbacks_p = &default_callbacks; + } + + /* test if this is a proper Ogg Vorbis file, with the current (from init_x) STREAMFILE */ + { + OggVorbis_File temp_ovf; + ogg_vorbis_streamfile temp_streamfile; + + temp_streamfile.streamfile = streamFile; + + temp_streamfile.start = start; + temp_streamfile.offset = 0; + temp_streamfile.size = stream_size; + + temp_streamfile.decryption_callback = vgm_inf->decryption_callback; + temp_streamfile.scd_xor = vgm_inf->scd_xor; + temp_streamfile.scd_xor_length = vgm_inf->scd_xor_length; + temp_streamfile.sngw_xor = vgm_inf->sngw_xor; + + /* open the ogg vorbis file for testing */ + memset(&temp_ovf, 0, sizeof(temp_ovf)); + if (ov_test_callbacks(&temp_streamfile, &temp_ovf, NULL, 0, *callbacks_p)) + goto fail; + + /* we have to close this as it has the init_vgmstream meta-reading STREAMFILE */ + ov_clear(&temp_ovf); + } + + + /* proceed to init codec_data and reopen a STREAMFILE for this stream */ + { + data = calloc(1,sizeof(ogg_vorbis_codec_data)); + if (!data) goto fail; + + data->ov_streamfile.streamfile = streamFile->open(streamFile,filename, STREAMFILE_DEFAULT_BUFFER_SIZE); + if (!data->ov_streamfile.streamfile) goto fail; + + data->ov_streamfile.start = start; + data->ov_streamfile.offset = 0; + data->ov_streamfile.size = stream_size; + + data->ov_streamfile.decryption_callback = vgm_inf->decryption_callback; + data->ov_streamfile.scd_xor = vgm_inf->scd_xor; + data->ov_streamfile.scd_xor_length = vgm_inf->scd_xor_length; + data->ov_streamfile.sngw_xor = vgm_inf->sngw_xor; + + /* open the ogg vorbis file for real */ + if (ov_open_callbacks(&data->ov_streamfile, &data->ogg_vorbis_file, NULL, 0, *callbacks_p)) + goto fail; + ovf = &data->ogg_vorbis_file; + } + + /* get info from bitstream 0 */ + data->bitstream = OGG_DEFAULT_BITSTREAM; + vi = ov_info(ovf,OGG_DEFAULT_BITSTREAM); + + /* search for loop comments */ + { + int i; + vorbis_comment *comment = ov_comment(ovf,OGG_DEFAULT_BITSTREAM); + + for (i = 0; i < comment->comments; i++) { + const char * user_comment = comment->user_comments[i]; + if (strstr(user_comment,"loop_start=")==user_comment || /* PSO4 */ + strstr(user_comment,"LOOP_START=")==user_comment || /* PSO4 */ + strstr(user_comment,"COMMENT=LOOPPOINT=")==user_comment || + strstr(user_comment,"LOOPSTART=")==user_comment || + strstr(user_comment,"um3.stream.looppoint.start=")==user_comment || + strstr(user_comment,"LOOP_BEGIN=")==user_comment || /* Hatsune Miku: Project Diva F (PS3) */ + strstr(user_comment,"LoopStart=")==user_comment) { /* Devil May Cry 4 (PC) */ + loop_start = atol(strrchr(user_comment,'=')+1); + loop_flag = (loop_start >= 0); + } + else if (strstr(user_comment,"LOOPLENGTH=")==user_comment) {/* (LOOPSTART pair) */ + loop_length = atol(strrchr(user_comment,'=')+1); + loop_length_found = 1; + } + else if (strstr(user_comment,"title=-lps")==user_comment) { /* Memories Off #5 (PC) */ + loop_start = atol(user_comment+10); + loop_flag = (loop_start >= 0); + } + else if (strstr(user_comment,"album=-lpe")==user_comment) { /* (title=-lps pair) */ + loop_end = atol(user_comment+10); + loop_flag = 1; + loop_end_found = 1; + } + else if (strstr(user_comment,"LoopEnd=")==user_comment) { /* (LoopStart pair) */ + if(loop_flag) { + loop_length = atol(strrchr(user_comment,'=')+1)-loop_start; + loop_length_found = 1; + } + } + else if (strstr(user_comment,"LOOP_END=")==user_comment) { /* (LOOP_BEGIN pair) */ + if(loop_flag) { + loop_length = atol(strrchr(user_comment,'=')+1)-loop_start; + loop_length_found = 1; + } + } + else if (strstr(user_comment,"lp=")==user_comment) { + sscanf(strrchr(user_comment,'=')+1,"%d,%d", &loop_start,&loop_end); + loop_flag = 1; + loop_end_found = 1; + } + else if (strstr(user_comment,"LOOPDEFS=")==user_comment) { /* Fairy Fencer F: Advent Dark Force */ + sscanf(strrchr(user_comment,'=')+1,"%d,%d", &loop_start,&loop_end); + loop_flag = 1; + loop_end_found = 1; + } + else if (strstr(user_comment,"COMMENT=loop(")==user_comment) { /* Zero Time Dilemma (PC) */ + sscanf(strrchr(user_comment,'(')+1,"%d,%d", &loop_start,&loop_end); + loop_flag = 1; + loop_end_found = 1; + } + } + } + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(vi->channels,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->codec_data = data; /* store our fun extra datas */ + vgmstream->channels = vi->channels; + vgmstream->sample_rate = vi->rate; + vgmstream->num_streams = vgm_inf->total_subsongs; + vgmstream->stream_size = stream_size; + + vgmstream->num_samples = ov_pcm_total(ovf,-1); /* let libvorbisfile find total samples */ + if (loop_flag) { + vgmstream->loop_start_sample = loop_start; + if (loop_length_found) + vgmstream->loop_end_sample = loop_start+loop_length; + else if (loop_end_found) + vgmstream->loop_end_sample = loop_end; + else + vgmstream->loop_end_sample = vgmstream->num_samples; + vgmstream->loop_flag = loop_flag; + + if (vgmstream->loop_end_sample > vgmstream->num_samples) + vgmstream->loop_end_sample = vgmstream->num_samples; + } + + vgmstream->coding_type = coding_ogg_vorbis; + vgmstream->layout_type = vgm_inf->layout_type; + vgmstream->meta_type = vgm_inf->meta_type; + + return vgmstream; + +fail: + /* clean up anything we may have opened */ + if (data) { + if (ovf) + ov_clear(&data->ogg_vorbis_file);//same as ovf + if (data->ov_streamfile.streamfile) + close_streamfile(data->ov_streamfile.streamfile); + free(data); + } + if (vgmstream) { + vgmstream->codec_data = NULL; + close_vgmstream(vgmstream); + } + return NULL; +} + +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis_file.c b/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis_file.c deleted file mode 100644 index 02aaade46..000000000 --- a/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis_file.c +++ /dev/null @@ -1,461 +0,0 @@ -#include "../vgmstream.h" - -#ifdef VGM_USE_VORBIS - -#include -#include -#include "meta.h" -#include "../util.h" -#include - - -#define DEFAULT_BITSTREAM 0 - -static size_t read_func(void *ptr, size_t size, size_t nmemb, void * datasource) -{ - ogg_vorbis_streamfile * const ov_streamfile = datasource; - size_t items_read; - - size_t bytes_read; - - bytes_read = read_streamfile(ptr, ov_streamfile->offset + ov_streamfile->other_header_bytes, size * nmemb, - ov_streamfile->streamfile); - - items_read = bytes_read / size; - - ov_streamfile->offset += items_read * size; - - return items_read; -} - -static size_t read_func_um3(void *ptr, size_t size, size_t nmemb, void * datasource) -{ - ogg_vorbis_streamfile * const ov_streamfile = datasource; - size_t items_read; - - size_t bytes_read; - - bytes_read = read_streamfile(ptr, ov_streamfile->offset + ov_streamfile->other_header_bytes, size * nmemb, - ov_streamfile->streamfile); - - items_read = bytes_read / size; - - /* first 0x800 bytes of um3 are xor'd with 0xff */ - if (ov_streamfile->offset < 0x800) { - int num_crypt = 0x800-ov_streamfile->offset; - int i; - - if (num_crypt > bytes_read) num_crypt=bytes_read; - for (i=0;ioffset += items_read * size; - - return items_read; -} - -static size_t read_func_kovs(void *ptr, size_t size, size_t nmemb, void * datasource) -{ - ogg_vorbis_streamfile * const ov_streamfile = datasource; - size_t items_read; - - size_t bytes_read; - - bytes_read = read_streamfile(ptr, ov_streamfile->offset+ov_streamfile->other_header_bytes, size * nmemb, - ov_streamfile->streamfile); - - items_read = bytes_read / size; - - /* first 0x100 bytes of KOVS are xor'd with offset */ - if (ov_streamfile->offset < 0x100) { - int i; - - for (i=ov_streamfile->offset;i<0x100;i++) - ((uint8_t*)ptr)[i-ov_streamfile->offset] ^= i; - } - - ov_streamfile->offset += items_read * size; - - return items_read; -} - -static size_t read_func_scd(void *ptr, size_t size, size_t nmemb, void * datasource) -{ - ogg_vorbis_streamfile * const ov_streamfile = datasource; - size_t items_read; - - size_t bytes_read; - - bytes_read = read_streamfile(ptr, ov_streamfile->offset + ov_streamfile->other_header_bytes, size * nmemb, - ov_streamfile->streamfile); - - items_read = bytes_read / size; - - /* may be encrypted */ - if (ov_streamfile->decryption_enabled) { - ov_streamfile->decryption_callback(ptr, size, nmemb, ov_streamfile, bytes_read); - } - - ov_streamfile->offset += items_read * size; - - return items_read; -} - -static size_t read_func_psych(void *ptr, size_t size, size_t nmemb, void * datasource) -{ - ogg_vorbis_streamfile * const ov_streamfile = datasource; - size_t items_read; - - size_t bytes_read; - size_t i; - - bytes_read = read_streamfile(ptr, ov_streamfile->offset + ov_streamfile->other_header_bytes, size * nmemb, - ov_streamfile->streamfile); - - /* add 0x23 ('#') */ - for (i=0;ioffset += items_read * size; - - return items_read; -} - -static int seek_func(void *datasource, ogg_int64_t offset, int whence) { - ogg_vorbis_streamfile * const ov_streamfile = datasource; - ogg_int64_t base_offset; - ogg_int64_t new_offset; - - switch (whence) { - case SEEK_SET: - base_offset = 0; - break; - case SEEK_CUR: - base_offset = ov_streamfile->offset; - break; - case SEEK_END: - base_offset = ov_streamfile->size - ov_streamfile->other_header_bytes; - break; - default: - return -1; - break; - } - - new_offset = base_offset + offset; - if (new_offset < 0 || new_offset > (ov_streamfile->size - ov_streamfile->other_header_bytes)) { - return -1; - } else { - ov_streamfile->offset = new_offset; - return 0; - } -} - -static long tell_func(void * datasource) { - ogg_vorbis_streamfile * const ov_streamfile = datasource; - return ov_streamfile->offset; -} - -/* setting close_func in ov_callbacks to NULL doesn't seem to work */ -static int close_func(void * datasource) { - return 0; -} - -/* Ogg Vorbis, by way of libvorbisfile */ - -VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { - char filename[PATH_LIMIT]; - - ov_callbacks callbacks; - - off_t other_header_bytes = 0; - int um3_ogg = 0; - int kovs_ogg = 0; - int psych_ogg = 0; - - vgm_vorbis_info_t inf; - memset(&inf, 0, sizeof(inf)); - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - - /* It is only interesting to use oggs with vgmstream if they are looped. - To prevent such files from being played by other plugins and such they - may be renamed to .logg. This meta reader should still support .ogg, - though. */ - if (strcasecmp("logg",filename_extension(filename)) && - strcasecmp("ogg",filename_extension(filename))) { - if (!strcasecmp("um3",filename_extension(filename))) { - um3_ogg = 1; - } else if (check_extensions(streamFile,"kvs,kovs")) { /* .kvs: Atelier Sophie, kovs: header id only? */ - kovs_ogg = 1; - } else { - goto fail; - } - } - - /* not all um3-ogg are crypted */ - if (um3_ogg && read_32bitBE(0x0,streamFile)==0x4f676753) { - um3_ogg = 0; - } - - /* use KOVS header */ - if (kovs_ogg) { - if (read_32bitBE(0x0,streamFile)!=0x4b4f5653) { /* "KOVS" */ - goto fail; - } - if (read_32bitLE(0x8,streamFile)!=0) { - inf.loop_start = read_32bitLE(0x8,streamFile); - inf.loop_flag = 1; - } - - other_header_bytes = 0x20; - } - - /* detect Psychic Software obfuscation (as seen in "Darkwind") */ - if (read_32bitBE(0x0,streamFile)==0x2c444430) { - psych_ogg = 1; - } - - if (um3_ogg) { - callbacks.read_func = read_func_um3; - } else if (kovs_ogg) { - callbacks.read_func = read_func_kovs; - } else if (psych_ogg) { - callbacks.read_func = read_func_psych; - } else { - callbacks.read_func = read_func; - } - callbacks.seek_func = seek_func; - callbacks.close_func = close_func; - callbacks.tell_func = tell_func; - - if (um3_ogg) { - inf.meta_type = meta_OGG_UM3; - } else if (kovs_ogg) { - inf.meta_type = meta_OGG_KOVS; - } else if (psych_ogg) { - inf.meta_type = meta_OGG_PSYCH; - } else { - inf.meta_type = meta_OGG_VORBIS; - } - - inf.layout_type = layout_ogg_vorbis; - - return init_vgmstream_ogg_vorbis_callbacks(streamFile, filename, &callbacks, other_header_bytes, &inf); - -fail: - return NULL; -} - -VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, const char * filename, ov_callbacks *callbacks_p, off_t other_header_bytes, const vgm_vorbis_info_t *vgm_inf) { - VGMSTREAM * vgmstream = NULL; - - OggVorbis_File temp_ovf; - ogg_vorbis_streamfile temp_streamfile; - - ogg_vorbis_codec_data * data = NULL; - OggVorbis_File *ovf; - int inited_ovf = 0; - vorbis_info *info; - - int loop_flag = vgm_inf->loop_flag; - int32_t loop_start = vgm_inf->loop_start; - int loop_length_found = vgm_inf->loop_length_found; - int32_t loop_length = vgm_inf->loop_length; - int loop_end_found = vgm_inf->loop_end_found; - int32_t loop_end = vgm_inf->loop_end; - - ov_callbacks default_callbacks; - - if (!callbacks_p) { - default_callbacks.read_func = read_func; - default_callbacks.seek_func = seek_func; - default_callbacks.close_func = close_func; - default_callbacks.tell_func = tell_func; - - if (vgm_inf->decryption_enabled) { - default_callbacks.read_func = read_func_scd; - } - - callbacks_p = &default_callbacks; - } - - temp_streamfile.streamfile = streamFile; - temp_streamfile.offset = 0; - temp_streamfile.size = get_streamfile_size(temp_streamfile.streamfile); - temp_streamfile.other_header_bytes = other_header_bytes; - temp_streamfile.decryption_enabled = vgm_inf->decryption_enabled; - temp_streamfile.decryption_callback = vgm_inf->decryption_callback; - temp_streamfile.scd_xor = vgm_inf->scd_xor; - temp_streamfile.scd_xor_length = vgm_inf->scd_xor_length; - - /* can we open this as a proper ogg vorbis file? */ - memset(&temp_ovf, 0, sizeof(temp_ovf)); - if (ov_test_callbacks(&temp_streamfile, &temp_ovf, NULL, - 0, *callbacks_p)) goto fail; - - /* we have to close this as it has the init_vgmstream meta-reading - STREAMFILE */ - ov_clear(&temp_ovf); - - /* proceed to open a STREAMFILE just for this stream */ - data = calloc(1,sizeof(ogg_vorbis_codec_data)); - if (!data) goto fail; - - data->ov_streamfile.streamfile = streamFile->open(streamFile,filename, - STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!data->ov_streamfile.streamfile) goto fail; - data->ov_streamfile.offset = 0; - data->ov_streamfile.size = get_streamfile_size(data->ov_streamfile.streamfile); - data->ov_streamfile.other_header_bytes = other_header_bytes; - data->ov_streamfile.decryption_enabled = vgm_inf->decryption_enabled; - data->ov_streamfile.decryption_callback = vgm_inf->decryption_callback; - data->ov_streamfile.scd_xor = vgm_inf->scd_xor; - data->ov_streamfile.scd_xor_length = vgm_inf->scd_xor_length; - - /* open the ogg vorbis file for real */ - if (ov_open_callbacks(&data->ov_streamfile, &data->ogg_vorbis_file, NULL, - 0, *callbacks_p)) goto fail; - ovf = &data->ogg_vorbis_file; - inited_ovf = 1; - - data->bitstream = DEFAULT_BITSTREAM; - - info = ov_info(ovf,DEFAULT_BITSTREAM); - - /* grab the comments */ - { - int i; - vorbis_comment *comment; - - comment = ov_comment(ovf,DEFAULT_BITSTREAM); - - /* search for a "loop_start" comment */ - for (i=0;icomments;i++) { - if (strstr(comment->user_comments[i],"loop_start=")== - comment->user_comments[i] || - strstr(comment->user_comments[i],"LOOP_START=")== - comment->user_comments[i] || - strstr(comment->user_comments[i],"COMMENT=LOOPPOINT=")== - comment->user_comments[i] || - strstr(comment->user_comments[i],"LOOPSTART=")== - comment->user_comments[i] || - strstr(comment->user_comments[i],"um3.stream.looppoint.start=")== - comment->user_comments[i] || - strstr(comment->user_comments[i],"LOOP_BEGIN=")== - comment->user_comments[i] || - strstr(comment->user_comments[i],"LoopStart=")== - comment->user_comments[i] - ) { - loop_start=atol(strrchr(comment->user_comments[i],'=')+1); - if (loop_start >= 0) - loop_flag=1; - } - else if (strstr(comment->user_comments[i],"LOOPLENGTH=")== - comment->user_comments[i]) { - loop_length=atol(strrchr(comment->user_comments[i],'=')+1); - loop_length_found=1; - } - else if (strstr(comment->user_comments[i],"title=-lps")== - comment->user_comments[i]) { - loop_start=atol(comment->user_comments[i]+10); - if (loop_start >= 0) - loop_flag=1; - } - else if (strstr(comment->user_comments[i],"album=-lpe")== - comment->user_comments[i]) { - loop_end=atol(comment->user_comments[i]+10); - loop_flag=1; - loop_end_found=1; - } - else if (strstr(comment->user_comments[i],"LoopEnd=")== - comment->user_comments[i]) { - if(loop_flag) { - loop_length=atol(strrchr(comment->user_comments[i],'=')+1)-loop_start; - loop_length_found=1; - } - } - else if (strstr(comment->user_comments[i],"LOOP_END=")== - comment->user_comments[i]) { - if(loop_flag) { - loop_length=atol(strrchr(comment->user_comments[i],'=')+1)-loop_start; - loop_length_found=1; - } - } - else if (strstr(comment->user_comments[i],"lp=")== - comment->user_comments[i]) { - sscanf(strrchr(comment->user_comments[i],'=')+1,"%d,%d", - &loop_start,&loop_end); - loop_flag=1; - loop_end_found=1; - } - else if (strstr(comment->user_comments[i],"LOOPDEFS=")== - comment->user_comments[i]) { - sscanf(strrchr(comment->user_comments[i],'=')+1,"%d,%d", - &loop_start,&loop_end); - loop_flag=1; - loop_end_found=1; - } - else if (strstr(comment->user_comments[i],"COMMENT=loop(")== - comment->user_comments[i]) { - sscanf(strrchr(comment->user_comments[i],'(')+1,"%d,%d", - &loop_start,&loop_end); - loop_flag=1; - loop_end_found=1; - } - } - } - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(info->channels,loop_flag); - if (!vgmstream) goto fail; - - /* store our fun extra datas */ - vgmstream->codec_data = data; - - /* fill in the vital statistics */ - vgmstream->channels = info->channels; - vgmstream->sample_rate = info->rate; - - /* let's play the whole file */ - vgmstream->num_samples = ov_pcm_total(ovf,-1); - - if (loop_flag) { - vgmstream->loop_start_sample = loop_start; - if (loop_length_found) - vgmstream->loop_end_sample = loop_start+loop_length; - else if (loop_end_found) - vgmstream->loop_end_sample = loop_end; - else - vgmstream->loop_end_sample = vgmstream->num_samples; - vgmstream->loop_flag = loop_flag; - - if (vgmstream->loop_end_sample > vgmstream->num_samples) - vgmstream->loop_end_sample = vgmstream->num_samples; - } - vgmstream->coding_type = coding_ogg_vorbis; - vgmstream->layout_type = vgm_inf->layout_type; - vgmstream->meta_type = vgm_inf->meta_type; - - return vgmstream; - - /* clean up anything we may have opened */ -fail: - if (data) { - if (inited_ovf) - ov_clear(&data->ogg_vorbis_file); - if (data->ov_streamfile.streamfile) - close_streamfile(data->ov_streamfile.streamfile); - free(data); - } - if (vgmstream) { - vgmstream->codec_data = NULL; - close_vgmstream(vgmstream); - } - return NULL; -} - -#endif diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_rxws.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_rxws.c index d59062431..d32b69e03 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_rxws.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_rxws.c @@ -7,10 +7,10 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; STREAMFILE * streamHeader = NULL; off_t start_offset, chunk_offset, name_offset = 0; - size_t data_size, chunk_size; + size_t stream_size, chunk_size; int loop_flag = 0, channel_count, is_separate = 0, type, sample_rate; int32_t loop_start, loop_end; - int total_streams, target_stream = streamFile->stream_index; + int total_subsongs, target_subsong = streamFile->stream_index; /* check extensions */ /* .xws: header and data, .xwh+xwb: header + data (.bin+dat are also found in Wild Arms 4/5) */ @@ -46,14 +46,14 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) { /* check multi-streams */ - total_streams = read_32bitLE(chunk_offset+0x00,streamHeader); - if (target_stream == 0) target_stream = 1; - if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail; + total_subsongs = read_32bitLE(chunk_offset+0x00,streamHeader); + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; /* read stream header */ { - off_t header_offset = chunk_offset + 0x4 + 0x1c * (target_stream-1); /* position in FORM */ + off_t header_offset = chunk_offset + 0x4 + 0x1c * (target_subsong-1); /* position in FORM */ off_t stream_offset, next_stream_offset, data_offset = 0; type = read_8bit(header_offset+0x00, streamHeader); @@ -83,22 +83,22 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) { if (!data_offset) goto fail; } - if (target_stream == total_streams) { + if (target_subsong == total_subsongs) { next_stream_offset = data_offset + get_streamfile_size(is_separate ? streamFile : streamHeader); } else { - off_t next_header_offset = chunk_offset + 0x4 + 0x1c * (target_stream); + off_t next_header_offset = chunk_offset + 0x4 + 0x1c * (target_subsong); next_stream_offset = read_32bitLE(next_header_offset+0x10,streamHeader); } - data_size = next_stream_offset - stream_offset; + stream_size = next_stream_offset - stream_offset; start_offset = data_offset + stream_offset; } /* get stream name (always follows FORM) */ if (read_32bitBE(0x10+0x10 + chunk_size,streamHeader) == 0x46545854) { /* "FTXT" */ chunk_offset = 0x10+0x10 + chunk_size + 0x10; - if (read_32bitLE(chunk_offset+0x00,streamHeader) == total_streams) { - name_offset = chunk_offset + read_32bitLE(chunk_offset+0x04 + (target_stream-1)*0x04,streamHeader); + if (read_32bitLE(chunk_offset+0x00,streamHeader) == total_subsongs) { + name_offset = chunk_offset + read_32bitLE(chunk_offset+0x04 + (target_subsong-1)*0x04,streamHeader); } } @@ -108,7 +108,8 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) { if (!vgmstream) goto fail; vgmstream->sample_rate = sample_rate; - vgmstream->num_streams = total_streams; + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; vgmstream->meta_type = meta_PS2_RXWS; if (name_offset) read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamHeader); @@ -143,10 +144,10 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) { joint_stereo = 0; encoder_delay = 0x0; - bytes = ffmpeg_make_riff_atrac3(buf, 0x100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_size, joint_stereo, encoder_delay); + bytes = ffmpeg_make_riff_atrac3(buf, 0x100, vgmstream->num_samples, stream_size, vgmstream->channels, vgmstream->sample_rate, block_size, joint_stereo, encoder_delay); if (bytes <= 0) goto fail; - vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size); + vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,stream_size); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/psx_cdxa.c b/Frameworks/vgmstream/vgmstream/src/meta/psx_cdxa.c index 6e612c09a..1dd04d61d 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/psx_cdxa.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/psx_cdxa.c @@ -39,20 +39,27 @@ VGMSTREAM * init_vgmstream_cdxa(STREAMFILE *streamFile) { } } - /* test first block (except when RIFF) */ + /* test some blocks (except when RIFF) since other .XA/STR may start blank */ if (start_offset == 0) { - int i, j; + int i, j, block; + off_t test_offset = start_offset; + size_t sector_size = (is_blocked ? 0x900 : 0x800); + size_t block_size = 0x80; - /* 0x80 frames for 1 sector (max ~0x800 for ISO mode) */ - for (i = 0; i < (0x800/0x80); i++) { - off_t test_offset = start_offset + (is_blocked ? 0x18 : 0x00) + 0x80*i; + for (block = 0; block < 3; block++) { + test_offset += (is_blocked ? 0x18 : 0x00); /* header */ - /* ADPCM predictors should be 0..3 index */ - for (j = 0; j < 16; j++) { - uint8_t header = read_8bit(test_offset + i, streamFile); - if (((header >> 4) & 0xF) > 3) - goto fail; + for (i = 0; i < (sector_size/block_size); i++) { + /* first 0x10 ADPCM predictors should be 0..3 index */ + for (j = 0; j < 16; j++) { + uint8_t header = read_8bit(test_offset + i, streamFile); + if (((header >> 4) & 0xF) > 3) + goto fail; + } + test_offset += 0x80; } + + test_offset += (is_blocked ? 0x18 : 0x00); /* footer */ } } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/riff.c b/Frameworks/vgmstream/vgmstream/src/meta/riff.c index 31412d5ec..00520b3f5 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/riff.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/riff.c @@ -6,6 +6,10 @@ /* RIFF - Resource Interchange File Format, standard container used in many games */ +#ifdef VGM_USE_VORBIS +static VGMSTREAM *parse_riff_ogg(STREAMFILE * streamFile, off_t start_offset, size_t data_size); +#endif + /* return milliseconds */ static long parse_adtl_marker(unsigned char * marker) { long hh,mm,ss,ms; @@ -76,16 +80,17 @@ static void parse_adtl(off_t adtl_offset, off_t adtl_length, STREAMFILE *stream typedef struct { off_t offset; off_t size; + uint32_t codec; int sample_rate; int channel_count; uint32_t block_size; + int bps; int coding_type; int interleave; } riff_fmt_chunk; static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk, riff_fmt_chunk * fmt, int sns, int mwv) { - int codec, bps; int32_t (*read_32bit)(off_t,STREAMFILE*) = big_endian ? read_32bitBE : read_32bitLE; int16_t (*read_16bit)(off_t,STREAMFILE*) = big_endian ? read_16bitBE : read_16bitLE; @@ -97,12 +102,12 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk fmt->block_size = read_16bit(current_chunk+0x14,streamFile); fmt->interleave = 0; - bps = read_16bit(current_chunk+0x16,streamFile); - codec = (uint16_t)read_16bit(current_chunk+0x8,streamFile); + fmt->bps = read_16bit(current_chunk+0x16,streamFile); + fmt->codec = (uint16_t)read_16bit(current_chunk+0x8,streamFile); - switch (codec) { + switch (fmt->codec) { case 0x01: /* PCM */ - switch (bps) { + switch (fmt->bps) { case 16: fmt->coding_type = big_endian ? coding_PCM16BE : coding_PCM16LE; fmt->interleave = 2; @@ -117,17 +122,17 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk break; case 0x02: /* MS ADPCM */ - if (bps != 4) goto fail; + if (fmt->bps != 4) goto fail; fmt->coding_type = coding_MSADPCM; break; case 0x11: /* MS IMA ADPCM */ - if (bps != 4) goto fail; + if (fmt->bps != 4) goto fail; fmt->coding_type = coding_MS_IMA; break; case 0x69: /* MS IMA ADPCM (XBOX) - Rayman Raving Rabbids 2 (PC) */ - if (bps != 4) goto fail; + if (fmt->bps != 4) goto fail; fmt->coding_type = coding_MS_IMA; break; @@ -136,9 +141,9 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk if (!check_extensions(streamFile,"med")) goto fail; - if (bps == 4) /* normal MS IMA */ + if (fmt->bps == 4) /* normal MS IMA */ fmt->coding_type = coding_MS_IMA; - else if (bps == 3) /* 3-bit MS IMA, used in a very few files */ + else if (fmt->bps == 3) /* 3-bit MS IMA, used in a very few files */ goto fail; //fmt->coding_type = coding_MS_IMA_3BIT; else goto fail; @@ -150,12 +155,20 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk fmt->interleave = 0x12; break; - case 0x5050: /* Ubisoft .sns uses this for DSP */ + case 0x5050: /* Ubisoft LyN engine's DSP */ if (!sns) goto fail; fmt->coding_type = coding_NGC_DSP; fmt->interleave = 0x08; break; +#ifdef VGM_USE_VORBIS + case 0x6771: /* Ogg Vorbis (mode 3+) */ + fmt->coding_type = coding_ogg_vorbis; + break; +#else + goto fail; +#endif + case 0x270: /* ATRAC3 */ #ifdef VGM_USE_FFMPEG fmt->coding_type = coding_FFmpeg; @@ -264,8 +277,12 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { riff_size = read_32bitLE(0x04,streamFile); file_size = get_streamfile_size(streamFile); + /* for some of Liar-soft's buggy RIFF+Ogg made with Soundforge [Shikkoku no Sharnoth (PC)] */ + if (riff_size+0x08+0x01 == file_size) + riff_size += 0x01; + /* check for truncated RIFF */ - if (file_size < riff_size+8) goto fail; + if (file_size < riff_size+0x08) goto fail; /* read through chunks to verify format and find metadata */ { @@ -273,7 +290,10 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { while (current_chunk < file_size && current_chunk < riff_size+8) { uint32_t chunk_type = read_32bitBE(current_chunk,streamFile); - off_t chunk_size = read_32bitLE(current_chunk+4,streamFile); + size_t chunk_size = read_32bitLE(current_chunk+4,streamFile); + + if (fmt.codec == 0x6771 && chunk_type == 0x64617461) /* Liar-soft again */ + chunk_size += (chunk_size%2) ? 0x01 : 0x00; if (current_chunk+8+chunk_size > file_size) goto fail; @@ -370,6 +390,14 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { goto fail; +#ifdef VGM_USE_VORBIS + /* special case using init_vgmstream_ogg_vorbis */ + if (fmt.coding_type == coding_ogg_vorbis) { + return parse_riff_ogg(streamFile, start_offset, data_size); + } +#endif + + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(fmt.channel_count,loop_flag); if (!vgmstream) goto fail; @@ -385,10 +413,11 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { vgmstream->num_samples = pcm_bytes_to_samples(data_size, vgmstream->channels, 8); break; case coding_L5_555: + if (!mwv) goto fail; vgmstream->num_samples = data_size / 0x12 / fmt.channel_count * 32; /* coefs */ - if (mwv) { + { int i, ch; const int filter_order = 3; int filter_count = read_32bitLE(mwv_pflt_offset+0x0c, streamFile); @@ -415,10 +444,13 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { vgmstream->num_samples = ms_ima_bytes_to_samples(data_size, fmt.block_size, fmt.channel_count); break; case coding_NGC_DSP: - //sample_count = dsp_bytes_to_samples(data_size, fmt.channel_count); /* expected from the "fact" chunk */ + if (!sns) goto fail; + if (fact_sample_count <= 0) goto fail; + vgmstream->num_samples = fact_sample_count; + //vgmstream->num_samples = dsp_bytes_to_samples(data_size, fmt.channel_count); /* coefs */ - if (sns) { + { int i, ch; static const int16_t coef[16] = { /* common codebook? */ 0x04ab,0xfced,0x0789,0xfedf,0x09a2,0xfae5,0x0c90,0xfac1, @@ -433,6 +465,7 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { } break; + #ifdef VGM_USE_FFMPEG case coding_FFmpeg: { ffmpeg_codec_data *ffmpeg_data = init_ffmpeg_offset(streamFile, 0x00, streamFile->get_size(streamFile)); @@ -492,11 +525,6 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { default: goto fail; } - /* .sns uses fact chunk */ - if (sns) { - if (-1 == fact_sample_count) goto fail; - vgmstream->num_samples = fact_sample_count; - } /* coding, layout, interleave */ vgmstream->coding_type = fmt.coding_type; @@ -688,3 +716,98 @@ fail: close_vgmstream(vgmstream); return NULL; } + + +#ifdef VGM_USE_VORBIS +typedef struct { + off_t patch_offset; +} riff_ogg_io_data; + +static size_t riff_ogg_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, riff_ogg_io_data* data) { + size_t bytes_read = streamfile->read(streamfile, dest, offset, length); + + /* has garbage init Oggs pages, patch bad flag */ + if (data->patch_offset && data->patch_offset >= offset && data->patch_offset < offset + bytes_read) { + VGM_ASSERT(dest[data->patch_offset - offset] != 0x02, "RIFF Ogg: bad patch offset\n"); + dest[data->patch_offset - offset] = 0x00; + } + + return bytes_read; +} + +/* special handling of Liar-soft's buggy RIFF+Ogg made with Soundforge [Shikkoku no Sharnoth (PC)] */ +static VGMSTREAM *parse_riff_ogg(STREAMFILE * streamFile, off_t start_offset, size_t data_size) { + off_t patch_offset = 0; + size_t real_size = data_size; + + /* initial page flag is repeated and causes glitches in decoders, find bad offset */ + { + off_t offset = start_offset + 0x04+0x02; + off_t offset_limit = start_offset + data_size; /* usually in the first 0x3000 but can be +0x100000 */ + + while (offset < offset_limit) { + if (read_32bitBE(offset+0x00, streamFile) == 0x4f676753 && /* "OggS" */ + read_16bitBE(offset+0x04, streamFile) == 0x0002) { /* start page flag */ + + //todo callback should patch on-the-fly by analyzing all "OggS", but is problematic due to arbitrary offsets + if (patch_offset) { + VGM_LOG("RIFF Ogg: found multiple repeated start pages\n"); + return NULL; + } + + patch_offset = offset /*- start_offset*/ + 0x04+0x01; + } + offset++; //todo could be optimized to do OggS page sizes + } + } + + /* last pages don't have the proper flag and confuse decoders, find actual end */ + { + size_t max_size = data_size; + off_t offset_limit = start_offset + data_size - 0x1000; /* not worth checking more, let decoder try */ + off_t offset = start_offset + data_size - 0x1a; + + while (offset > offset_limit) { + if (read_32bitBE(offset+0x00, streamFile) == 0x4f676753) { /* "OggS" */ + if (read_16bitBE(offset+0x04, streamFile) == 0x0004) { /* last page flag */ + real_size = max_size; + break; + } else { + max_size = offset - start_offset; /* ignore bad pages */ + } + } + offset--; + } + } + + /* Soundforge includes fact_samples but should be equal to Ogg samples */ + + /* actual Ogg init with custom callback to patch weirdness */ + { + VGMSTREAM *vgmstream = NULL; + STREAMFILE *custom_streamFile = NULL; + char filename[PATH_LIMIT]; + vgm_vorbis_info_t inf = {0}; + riff_ogg_io_data io_data = {0}; + size_t io_data_size = sizeof(riff_ogg_io_data); + + + inf.layout_type = layout_ogg_vorbis; + inf.meta_type = meta_RIFF_WAVE; + inf.stream_size = real_size; + //inf.loop_flag = 0; /* not observed */ + + io_data.patch_offset = patch_offset; + + custom_streamFile = open_io_streamfile(open_wrap_streamfile(streamFile), &io_data,io_data_size, riff_ogg_io_read); + if (!custom_streamFile) return NULL; + + streamFile->get_name(streamFile,filename,sizeof(filename)); + vgmstream = init_vgmstream_ogg_vorbis_callbacks(custom_streamFile, filename, NULL, start_offset, &inf); + + close_streamfile(custom_streamFile); + + return vgmstream; + } +} +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/meta/rws.c b/Frameworks/vgmstream/vgmstream/src/meta/rws.c index fee8a1709..3594eb45f 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/rws.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/rws.c @@ -15,7 +15,7 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { size_t block_size = 0, block_size_total = 0; int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; int i, total_segments; - int total_streams, target_stream = streamFile->stream_index; + int total_subsongs, target_subsong = streamFile->stream_index; if (!check_extensions(streamFile,"rws")) @@ -49,7 +49,7 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { * 0x1c: null? 0x30: 0x800?, 0x34: block_size_total?, 0x38: data offset, 0x3c: 0?, 0x40-50: file uuid */ read_32bit = (read_32bitLE(off+0x00,streamFile) > header_size) ? read_32bitBE : read_32bitLE; /* GC/Wii/X360 = BE */ total_segments = read_32bit(off+0x20,streamFile); - total_streams = read_32bit(off+0x28,streamFile); + total_subsongs = read_32bit(off+0x28,streamFile); /* skip audio file name */ off += 0x50 + get_rws_string_size(off+0x50, streamFile); @@ -58,8 +58,8 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { /* Data is divided into "segments" (cues/divisions within data, ex. intro+main, voice1+2+..N) and "streams" * of interleaved blocks (for multichannel?). last stream (only?) has padding. Segments divide all streams. * ex.- 0x1800 data + 0 pad of stream_0 2ch, 0x1800 data + 0x200 pad of stream1 2ch (xN). */ - if (target_stream == 0) target_stream = 1; - if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail; + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; /* get segment info, for all streams */ /* 0x00/04/0c: command?, 0x18: full segment size (including all streams), 0x1c: offset, others: ?) */ @@ -70,9 +70,9 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { /* get usable segment sizes (usually ok but sometimes > stream_size), per stream */ for (i = 0; i < total_segments; i++) { /* sum usable segment sizes (no padding) */ - stream_size += read_32bit(off + 0x04*i + 0x04*total_segments*(target_stream-1),streamFile); + stream_size += read_32bit(off + 0x04*i + 0x04*total_segments*(target_subsong-1),streamFile); } - off += 0x04 * (total_segments * total_streams); + off += 0x04 * (total_segments * total_subsongs); /* skip segment uuids */ off += 0x10 * total_segments; @@ -85,21 +85,21 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { /* get stream layout */ /* 0x00/04/14: command?, 0x08: null? 0x0c: spf related? (XADPCM=07, VAG=1C, DSP=0E, PCM=01) * 0x24: offset within data chunk, 0x1c: codec related?, others: ?) */ - for (i = 0; i < total_streams; i++) { /* get block_sizes */ + for (i = 0; i < total_subsongs; i++) { /* get block_sizes */ block_size_total += read_32bit(off + 0x10 + 0x28*i, streamFile); /* for all streeams, to skip during decode */ - if (i+1 == target_stream) { + if (i+1 == target_subsong) { //block_size_full = read_32bit(off + 0x10 + 0x28*i, streamFile); /* with padding, can be different per stream */ block_size = read_32bit(off + 0x20 + 0x28*i, streamFile); /* without padding */ stream_offset = read_32bit(off + 0x24 + 0x28*i, streamFile); /* within data */ } } - off += 0x28 * total_streams; + off += 0x28 * total_subsongs; /* get stream config */ /* 0x04: command?, 0x0c(1): bits per sample, others: null? */ - for (i = 0; i < total_streams; i++) { /* size depends on codec so we must parse it */ + for (i = 0; i < total_subsongs; i++) { /* size depends on codec so we must parse it */ int prev_codec = 0; - if (i+1 == target_stream) { + if (i+1 == target_subsong) { sample_rate = read_32bit(off+0x00, streamFile); //unk_size = read_32bit(off+0x08, streamFile); /* segment size again? loop-related? */ channel_count = read_8bit(off+0x0d, streamFile); @@ -110,7 +110,7 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { if (prev_codec == 0xF86215B0) { /* if codec is DSP there is an extra field per stream */ /* 0x00: approx num samples? 0x04: approx size/loop related? (can be 0) */ - if (i+1 == target_stream) { + if (i+1 == target_subsong) { coefs_offset = off + 0x1c; } off += 0x60; @@ -120,11 +120,11 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { } /* skip stream uuids */ - off += 0x10 * total_streams; + off += 0x10 * total_subsongs; /* get stream name */ - for (i = 0; i < total_streams; i++) { - if (i+1 == target_stream) { + for (i = 0; i < total_subsongs; i++) { + if (i+1 == target_subsong) { name_offset = off; } off += get_rws_string_size(off, streamFile); @@ -137,7 +137,7 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { start_offset = 0x0c + 0x0c + header_size + 0x0c + stream_offset; /* sometimes it's wrong for no apparent reason (probably a bug in RWS) */ - stream_size_expected = (stream_size_full / block_size_total) * (block_size * total_streams) / total_streams; + stream_size_expected = (stream_size_full / block_size_total) * (block_size * total_subsongs) / total_subsongs; if (stream_size > stream_size_expected) { VGM_LOG("RWS: readjusting wrong stream size %x vs expected %x\n", stream_size, stream_size_expected); stream_size = stream_size_expected; @@ -149,7 +149,8 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { if (!vgmstream) goto fail; vgmstream->sample_rate = sample_rate; - vgmstream->num_streams = total_streams; + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; vgmstream->meta_type = meta_RWS; if (name_offset) read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamFile); @@ -159,10 +160,11 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { vgmstream->full_block_size = block_size_total; switch(codec) { + case 0x17D21BD0: /* PCM PC (17D21BD0 8735ED4E B9D9B8E8 6EA9B995) */ case 0xD01BD217: /* PCM X360 (D01BD217 35874EED B9D9B8E8 6EA9B995) */ - /* ex. The Legend of Spyro (X360) */ + /* ex. D.i.R.T. - Origin of the Species (PC), The Legend of Spyro (X360) */ vgmstream->coding_type = coding_PCM16_int; - vgmstream->codec_endian = 1; /* big */ + vgmstream->codec_endian = (codec == 0xD01BD217); vgmstream->interleave_block_size = 0x02; /* only used to setup channels */ vgmstream->num_samples = pcm_bytes_to_samples(stream_size, channel_count, 16); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sab.c b/Frameworks/vgmstream/vgmstream/src/meta/sab.c index 450fd8d4b..8532e4787 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sab.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sab.c @@ -8,8 +8,8 @@ static void get_stream_name(char * stream_name, STREAMFILE *streamFile, int targ VGMSTREAM * init_vgmstream_sab(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset; - int loop_flag, channel_count = 0, is_stream, align, codec, sample_rate, data_size, loop_start, loop_end; - int total_streams, target_stream = streamFile->stream_index; + int loop_flag, channel_count = 0, is_stream, align, codec, sample_rate, stream_size, loop_start, loop_end; + int total_subsongs, target_subsong = streamFile->stream_index; /* .sab: main, .sob: config/names */ if (!check_extensions(streamFile,"sab")) @@ -20,29 +20,29 @@ VGMSTREAM * init_vgmstream_sab(STREAMFILE *streamFile) { goto fail; is_stream = read_32bitLE(0x04,streamFile) & 0x04; /* other flags don't seem to matter */ - total_streams = is_stream ? 1 : read_32bitLE(0x08,streamFile); - if (target_stream == 0) target_stream = 1; - if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail; + total_subsongs = is_stream ? 1 : read_32bitLE(0x08,streamFile); + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; align = read_32bitLE(0x0c,streamFile); /* doubles as interleave */ /* stream config */ - codec = read_32bitLE(0x18 + 0x1c*(target_stream-1) + 0x00,streamFile); - channel_count = read_32bitLE(0x18 + 0x1c*(target_stream-1) + 0x04,streamFile); - sample_rate = read_32bitLE(0x18 + 0x1c*(target_stream-1) + 0x08,streamFile); - data_size = read_32bitLE(0x18 + 0x1c*(target_stream-1) + 0x0c,streamFile); - loop_start = read_32bitLE(0x18 + 0x1c*(target_stream-1) + 0x10,streamFile); - loop_end = read_32bitLE(0x18 + 0x1c*(target_stream-1) + 0x14,streamFile); + codec = read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x00,streamFile); + channel_count = read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x04,streamFile); + sample_rate = read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x08,streamFile); + stream_size = read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x0c,streamFile); + loop_start = read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x10,streamFile); + loop_end = read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x14,streamFile); loop_flag = (loop_end > 0); - start_offset = 0x18 + 0x1c*total_streams; + start_offset = 0x18 + 0x1c*total_subsongs; if (start_offset % align) start_offset += align - (start_offset % align); - start_offset += read_32bitLE(0x18 + 0x1c*(target_stream-1) + 0x18,streamFile); + start_offset += read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x18,streamFile); if (is_stream) { channel_count = read_32bitLE(0x08,streamFile); /* uncommon, but non-stream stereo exists */ - data_size *= channel_count; + stream_size *= channel_count; } /* build the VGMSTREAM */ @@ -50,8 +50,8 @@ VGMSTREAM * init_vgmstream_sab(STREAMFILE *streamFile) { if (!vgmstream) goto fail; vgmstream->sample_rate = sample_rate; - - vgmstream->num_streams = total_streams; + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; vgmstream->meta_type = meta_SAB; switch(codec) { @@ -60,7 +60,7 @@ VGMSTREAM * init_vgmstream_sab(STREAMFILE *streamFile) { vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = is_stream ? align : 0x02; - vgmstream->num_samples = pcm_bytes_to_samples(data_size, vgmstream->channels, 16); + vgmstream->num_samples = pcm_bytes_to_samples(stream_size, vgmstream->channels, 16); vgmstream->loop_start_sample = pcm_bytes_to_samples(loop_start, vgmstream->channels, 16); vgmstream->loop_end_sample = pcm_bytes_to_samples(loop_end, vgmstream->channels, 16); @@ -71,7 +71,7 @@ VGMSTREAM * init_vgmstream_sab(STREAMFILE *streamFile) { vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = is_stream ? align : 0x10; - vgmstream->num_samples = ps_bytes_to_samples(data_size, vgmstream->channels); + vgmstream->num_samples = ps_bytes_to_samples(stream_size, vgmstream->channels); vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start, vgmstream->channels); vgmstream->loop_end_sample = ps_bytes_to_samples(loop_end, vgmstream->channels); break; @@ -81,7 +81,7 @@ VGMSTREAM * init_vgmstream_sab(STREAMFILE *streamFile) { vgmstream->layout_type = is_stream ? layout_interleave : layout_none; vgmstream->interleave_block_size = is_stream ? align : 0x00; - vgmstream->num_samples = ms_ima_bytes_to_samples(data_size, 0x24*vgmstream->channels, vgmstream->channels); + vgmstream->num_samples = ms_ima_bytes_to_samples(stream_size, 0x24*vgmstream->channels, vgmstream->channels); vgmstream->loop_start_sample = ms_ima_bytes_to_samples(loop_start, 0x24*vgmstream->channels, vgmstream->channels); vgmstream->loop_end_sample = ms_ima_bytes_to_samples(loop_end, 0x24*vgmstream->channels, vgmstream->channels); break; @@ -91,7 +91,7 @@ VGMSTREAM * init_vgmstream_sab(STREAMFILE *streamFile) { goto fail; } - get_stream_name(vgmstream->stream_name, streamFile, target_stream); + get_stream_name(vgmstream->stream_name, streamFile, target_subsong); if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) goto fail; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c b/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c index 07cfba50d..33c04628c 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c @@ -2,19 +2,17 @@ #include "../coding/coding.h" -/* SGXD - Sony/SCEI's format (SGB+SGH / SGD / SGX), found in: - * PS3: Genji, Folklore, Afrika (Short VAG), Tokyo Jungle - * PSP: Brave Story, Sarugetchu Sarusaru Daisakusen, Kurohyo 1/2, Pathwork Heroes */ +/* SGXD - Sony/SCEI's format (SGB+SGH / SGD / SGX) */ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; STREAMFILE * streamHeader = NULL; off_t start_offset, data_offset, chunk_offset, name_offset = 0; - size_t data_size; + size_t stream_size; int is_sgx, is_sgb = 0; int loop_flag, channels, type; int sample_rate, num_samples, loop_start_sample, loop_end_sample; - int total_streams, target_stream = streamFile->stream_index; + int total_subsongs, target_subsong = streamFile->stream_index; /* check extension, case insensitive */ @@ -59,14 +57,14 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { /* 0x04 SGX: unknown; SGD/SGH: chunk length, 0x08 null */ /* check multi-streams (usually only SE containers; Puppeteer) */ - total_streams = read_32bitLE(chunk_offset+0x04,streamHeader); - if (target_stream == 0) target_stream = 1; - if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail; + total_subsongs = read_32bitLE(chunk_offset+0x04,streamHeader); + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; /* read stream header */ { off_t stream_offset; - chunk_offset += 0x08 + 0x38 * (target_stream-1); /* position in target header*/ + chunk_offset += 0x08 + 0x38 * (target_subsong-1); /* position in target header*/ /* 0x00 ? (00/01/02) */ if (!is_sgx) /* meaning unknown in .sgx; offset 0 = not a stream (a RGND sample) */ @@ -85,7 +83,7 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { num_samples = read_32bitLE(chunk_offset+0x20,streamHeader); loop_start_sample = read_32bitLE(chunk_offset+0x24,streamHeader); loop_end_sample = read_32bitLE(chunk_offset+0x28,streamHeader); - data_size = read_32bitLE(chunk_offset+0x2c,streamHeader); /* stream size (without padding) / interleave (for type3) */ + stream_size = read_32bitLE(chunk_offset+0x2c,streamHeader); /* stream size (without padding) / interleave (for type3) */ if (is_sgx) { stream_offset = 0x0; @@ -107,7 +105,8 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { vgmstream->num_samples = num_samples; vgmstream->loop_start_sample = loop_start_sample; vgmstream->loop_end_sample = loop_end_sample; - vgmstream->num_streams = total_streams; + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; vgmstream->meta_type = meta_SGXD; if (name_offset) read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamHeader); @@ -118,23 +117,23 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { vgmstream->loop_end_sample -= 1; switch (type) { - case 0x03: /* PS-ADPCM */ + case 0x03: /* PS-ADPCM [Genji (PS3), Ape Escape Move (PS3)]*/ vgmstream->coding_type = coding_PSX; vgmstream->layout_type = layout_interleave; if (is_sgx || is_sgb) { vgmstream->interleave_block_size = 0x10; } else { /* this only seems to happen with SFX */ - vgmstream->interleave_block_size = data_size; + vgmstream->interleave_block_size = stream_size; } break; #ifdef VGM_USE_FFMPEG - case 0x04: { /* ATRAC3plus */ + case 0x04: { /* ATRAC3plus [Kurohyo 1/2 (PSP), BraveStory (PSP)] */ ffmpeg_codec_data *ffmpeg_data; /* internally has a RIFF header; but the SGXD header / sample rate has priority over it (may not match) */ - ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, data_size); + ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, stream_size); if ( !ffmpeg_data ) goto fail; vgmstream->codec_data = ffmpeg_data; vgmstream->coding_type = coding_FFmpeg; @@ -158,7 +157,7 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { break; } #endif - case 0x05: /* Short PS-ADPCM */ + case 0x05: /* Short PS-ADPCM [Afrika (PS3)] */ vgmstream->coding_type = coding_PSX_cfg; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x4; @@ -166,10 +165,10 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { break; #ifdef VGM_USE_FFMPEG - case 0x06: { /* AC3 */ + case 0x06: { /* AC3 [Tokyo Jungle (PS3), Afrika (PS3)] */ ffmpeg_codec_data *ffmpeg_data; - ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, data_size); + ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, stream_size); if ( !ffmpeg_data ) goto fail; vgmstream->codec_data = ffmpeg_data; vgmstream->coding_type = coding_FFmpeg; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sps_n1.c b/Frameworks/vgmstream/vgmstream/src/meta/sps_n1.c new file mode 100644 index 000000000..5ac38d25f --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/sps_n1.c @@ -0,0 +1,82 @@ +#include "meta.h" +#include "../coding/coding.h" + +static STREAMFILE* setup_sps_streamfile(STREAMFILE *streamfile, off_t subfile_offset, size_t subfile_size, char* extension); + +/* .SPS - Nippon Ichi's RIFF AT3 wrapper [ClaDun (PSP)] */ +VGMSTREAM * init_vgmstream_sps_n1(STREAMFILE *streamFile) { + VGMSTREAM *vgmstream = NULL; + STREAMFILE *temp_streamFile = NULL; + int type, sample_rate; + off_t subfile_offset; + size_t subfile_size; + + /* check extensions */ + if ( !check_extensions(streamFile,"sps")) + goto fail; + + /* mini header */ + type = read_32bitLE(0x00,streamFile); //todo channels? all known VAG are mono and AT3 stereo + subfile_size = read_32bitLE(0x04,streamFile); + sample_rate = (uint16_t)read_16bitLE(0x08,streamFile); + /* 0x0a/0b: stereo+loop flags? */ + //num_samples = read_32bitLE(0x0c,streamFile); + subfile_offset = 0x10; + + /* init the VGMSTREAM */ + switch(type) { + case 1: /* .vag */ + temp_streamFile = setup_sps_streamfile(streamFile, subfile_offset, subfile_size, "vag"); + if (!temp_streamFile) goto fail; + + vgmstream = init_vgmstream_ps2_vag(temp_streamFile); + if (!vgmstream) goto fail; + break; + + case 2: /* .at3 */ + temp_streamFile = setup_sps_streamfile(streamFile, subfile_offset, subfile_size, "at3"); + if (!temp_streamFile) goto fail; + + vgmstream = init_vgmstream_riff(temp_streamFile); + if (!vgmstream) goto fail; + break; + default: + goto fail; + } + + //VGM_LOG(vgmstream->num_samples != num_samples, + // "SPS: sps num_samples and subfile num_samples don't match\n"); + //vgmstream->num_samples = num_samples; //todo adjusted for MAIATRAC3 + vgmstream->sample_rate = sample_rate; /* .vag header doesn't match */ + + close_streamfile(temp_streamFile); + return vgmstream; + +fail: + close_streamfile(temp_streamFile); + close_vgmstream(vgmstream); + return NULL; +} + +static STREAMFILE* setup_sps_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, char* extension) { + 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,extension); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c b/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c index 2dfa87052..203fbd9d5 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c @@ -4,20 +4,19 @@ #ifdef VGM_USE_VORBIS -static void scd_ogg_decrypt_v2_callback(void *ptr, size_t size, size_t nmemb, void *datasource, int bytes_read); -static void scd_ogg_decrypt_v3_callback(void *ptr, size_t size, size_t nmemb, void *datasource, int bytes_read); +static void scd_ogg_v2_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource); +static void scd_ogg_v3_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource); #endif -/* SCD - Square-Enix console games (FF XIII, XIV) */ +/* SCD - Square-Enix games (FF XIII, XIV) */ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; char filename[PATH_LIMIT]; - off_t start_offset, tables_offset, headers_offset, meta_offset, post_meta_offset; - int headers_entries; - int32_t stream_size, loop_start, loop_end; + off_t start_offset, tables_offset, meta_offset, post_meta_offset; + int32_t stream_size, subheader_size, loop_start, loop_end; - int target_stream = streamFile->stream_index; - int loop_flag = 0, channel_count, codec_id, sample_rate; + int total_subsongs, target_subsong = streamFile->stream_index; + int loop_flag = 0, channel_count, codec, sample_rate; int aux_chunk_count; int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; @@ -25,32 +24,34 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { /* check extension, case insensitive */ - if ( !check_extensions(streamFile, "scd") ) goto fail; + if ( !check_extensions(streamFile, "scd") ) + goto fail; streamFile->get_name(streamFile,filename,sizeof(filename)); - /* SEDB */ - if (read_32bitBE(0x00,streamFile) != 0x53454442) goto fail; - /* SSCF */ - if (read_32bitBE(0x04,streamFile) != 0x53534346) goto fail; + /** main header **/ + if (read_32bitBE(0x00,streamFile) != 0x53454442 && /* "SEDB" */ + read_32bitBE(0x04,streamFile) != 0x53534346) /* "SSCF" */ + goto fail; - /** main header section **/ if (read_32bitBE(0x08,streamFile) == 2 || /* version 2 BE, as seen in FFXIII demo for PS3 */ read_32bitBE(0x08,streamFile) == 3) { /* version 3 BE, as seen in FFXIII for PS3 */ - + //size_offset = 0x14; read_32bit = read_32bitBE; read_16bit = read_16bitBE; - //size_offset = 0x14; - } else if (read_32bitLE(0x08,streamFile) == 3 || /* version 2/3 LE, as seen in FFXIV for PC (and others?) */ - read_32bitLE(0x08,streamFile) == 2) { - + } + else if (read_32bitLE(0x08,streamFile) == 2 || /* version 2/3 LE, as seen in FFXIV for PC (and others) */ + read_32bitLE(0x08,streamFile) == 3) { + //size_offset = 0x10; read_32bit = read_32bitLE; read_16bit = read_16bitLE; - //size_offset = 0x10; - } else goto fail; + } + else { + goto fail; + } - /* 0x0c: probably 0=LE, 1=BE */ - /* 0x0d: unk (always 0x04) */ - tables_offset = read_16bit(0xe,streamFile); + /* 0x0c: probably 0=LE, 1=BE */ + /* 0x0d: unknown (always 0x04) */ + tables_offset = read_16bit(0x0e,streamFile); /* usually 0x30 or 0x20 */ #if 0 /* never mind, FFXIII music_68tak.ps3.scd is 0x80 shorter */ @@ -59,43 +60,66 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { goto fail; #endif - /** offset tables **/ - /* 0x00: table1_unknown entries */ - /* 0x02: table2_unknown entries */ - /* 0x04: table_headers entries */ - /* 0x06: unknown (varies) */ - /* 0x08: table1_unknown start offset */ - /* 0x0c: table_headers start offset */ - /* 0x10: table2_unknown start offset */ - /* 0x14: unknown (0x0) */ - /* 0x18: unknown offset */ - /* 0x1c: unknown (0x0) */ - headers_entries = read_16bit(tables_offset+0x04,streamFile); - if (target_stream == 0) target_stream = 1; /* auto: default to 1 */ - if (target_stream < 0 || target_stream > headers_entries || headers_entries < 1) goto fail; - headers_offset = read_32bit(tables_offset+0x0c,streamFile); + /** offset tables **/ + /* 0x00(2): table1/4 (unknown) entries */ + /* 0x02(2): table2 (unknown) entries */ + /* 0x04(2): table3 (headers) entries */ + /* 0x06(2): unknown, varies even for clone files */ - /** header table entries (each is an uint32_t offset to stream header) **/ - meta_offset = read_32bit(headers_offset + (target_stream-1)*4,streamFile); + /* (implicit: table1 starts at 0x20) */ + /* 0x08: table2 (unknown) start offset */ + /* 0x0c: table3 (headers) start offset */ + /* 0x10: table4 (unknown) start offset */ + /* 0x14: always null? */ + /* 0x18: table5? (unknown) start offset? */ + /* 0x1c: unknown, often null */ + /* each table entry is an uint32_t offset */ + /* if a table isn't present entries is 0 and offset points to next table */ + + /* find meta_offset in table3 (headers) and total subsongs */ + { + int i; + int headers_entries = read_16bit(tables_offset+0x04,streamFile); + off_t headers_offset = read_32bit(tables_offset+0x0c,streamFile); + + if (target_subsong == 0) target_subsong = 1; + total_subsongs = 0; + meta_offset = 0; + + /* manually find subsongs as entries can be dummy (ex. sfx banks in FF XIV or FF Type-0) */ + for (i = 0; i < headers_entries; i++) { + off_t header_offset = read_32bit(headers_offset + i*0x04,streamFile); + + if (read_32bit(header_offset+0x0c,streamFile) == -1) + continue; /* codec -1 when dummy */ + + total_subsongs++; + if (!meta_offset && total_subsongs == target_subsong) + meta_offset = header_offset; + } + if (meta_offset == 0) goto fail; + /* SCD can contain 0 entries too */ + } /** stream header **/ - stream_size = read_32bit(meta_offset+0x00,streamFile); - channel_count = read_32bit(meta_offset+0x04,streamFile); - sample_rate = read_32bit(meta_offset+0x08,streamFile); - codec_id = read_32bit(meta_offset+0x0c,streamFile); + stream_size = read_32bit(meta_offset+0x00,streamFile); + channel_count = read_32bit(meta_offset+0x04,streamFile); + sample_rate = read_32bit(meta_offset+0x08,streamFile); + codec = read_32bit(meta_offset+0x0c,streamFile); - loop_start = read_32bit(meta_offset+0x10,streamFile); - loop_end = read_32bit(meta_offset+0x14,streamFile); - loop_flag = (loop_end > 0); - - post_meta_offset = meta_offset + 0x20; - start_offset = post_meta_offset + read_32bit(meta_offset+0x18,streamFile); + loop_start = read_32bit(meta_offset+0x10,streamFile); + loop_end = read_32bit(meta_offset+0x14,streamFile); + subheader_size = read_32bit(meta_offset+0x18,streamFile); aux_chunk_count = read_32bit(meta_offset+0x1c,streamFile); - /* 0x01e(e): unknown, seen in some FF XIV sfx (IMA) */ + /* 0x01e(2): unknown, seen in some FF XIV sfx (MSADPCM) */ + + loop_flag = (loop_end > 0); + post_meta_offset = meta_offset + 0x20; + start_offset = post_meta_offset + subheader_size; /* only "MARK" chunk is known (some FF XIV PS3 have "STBL" but it's not counted) */ - if (aux_chunk_count > 1 && aux_chunk_count < 0xFFFF) { /* some FF XIV Heavensward IMA sfx has 0x01000000 */ + if (aux_chunk_count > 1 && aux_chunk_count < 0xFFFF) { /* some FF XIV Heavensward IMA sfx have 0x01000000 */ VGM_LOG("SCD: unknown aux chunk count %i\n", aux_chunk_count); goto fail; } @@ -107,73 +131,52 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { #ifdef VGM_USE_VORBIS - /* special case using init_vgmstream_ogg_vorbis with callbacks */ - if (codec_id == 0x06) { - VGMSTREAM * result = NULL; - uint32_t seek_table_size, vorb_header_size; - uint8_t xor_version, xor_byte; + /* special case using init_vgmstream_ogg_vorbis */ + if (codec == 0x06) { + uint8_t ogg_version, ogg_byte; vgm_vorbis_info_t inf = {0}; - inf.loop_start = loop_start; - inf.loop_end = loop_end; - inf.loop_flag = loop_flag; - inf.loop_end_found = loop_flag; - inf.loop_length_found = 0; inf.layout_type = layout_ogg_vorbis; inf.meta_type = meta_SQEX_SCD; + inf.total_subsongs = total_subsongs; + /* loop values are in bytes, let init_vgmstream_ogg_vorbis find loop comments instead */ - /* the following could be simplified but it's not clear what field signals that seek table exists - * (seems that encrypted = always seek table, but maybe post_meta_offset+0x01 = 0x20) */ + ogg_version = read_8bit(post_meta_offset + 0x00, streamFile); + /* 0x01(1): 0x20 in v2/3, this ogg miniheader size? */ + ogg_byte = read_8bit(post_meta_offset + 0x02, streamFile); + /* 0x03(1): ? in v3 */ - /* try regular Ogg with default values */ - { - result = init_vgmstream_ogg_vorbis_callbacks(streamFile, filename, NULL, start_offset, &inf); - if (result != NULL) - return result; + if (ogg_version == 0) { /* 0x10? header, then custom Vorbis header before regular Ogg (FF XIV PC v1) */ + inf.stream_size = stream_size; } + else { /* 0x20 header, then seek table */ + size_t seek_table_size = read_32bit(post_meta_offset+0x10, streamFile); + size_t vorb_header_size = read_32bit(post_meta_offset+0x14, streamFile); + /* 0x18(4): ? (can be 0) */ - /* skip seek table and try regular Ogg again */ - { - seek_table_size = read_32bit(post_meta_offset+0x10, streamFile); - vorb_header_size = read_32bit(post_meta_offset+0x14, streamFile); - if ((post_meta_offset-meta_offset) + seek_table_size + vorb_header_size != read_32bit(meta_offset+0x18, streamFile)) { - return NULL; - } + if ((post_meta_offset-meta_offset) + seek_table_size + vorb_header_size != subheader_size) + goto fail; - start_offset = post_meta_offset + 0x20 + seek_table_size; + inf.stream_size = vorb_header_size + stream_size; + start_offset = post_meta_offset + 0x20 + seek_table_size; /* subheader_size skips vorb_header */ - result = init_vgmstream_ogg_vorbis_callbacks(streamFile, filename, NULL, start_offset, &inf); - if (result != NULL) - return result; - } - - /* try encrypted Ogg (with seek table already skipped) */ - { - xor_version = read_8bit(post_meta_offset + 0x00, streamFile); - xor_byte = read_8bit(post_meta_offset + 0x02, streamFile); - if (xor_byte == 0) - return NULL; /* not actually encrypted, happens but should be handled above */ - - if (xor_version == 2) { /* header is XOR'ed using byte */ - inf.decryption_enabled = 1; - inf.decryption_callback = scd_ogg_decrypt_v2_callback; - inf.scd_xor = xor_byte; + if (ogg_version == 2) { /* header is XOR'ed using byte (FF XIV PC) */ + inf.decryption_callback = scd_ogg_v2_decryption_callback; + inf.scd_xor = ogg_byte; inf.scd_xor_length = vorb_header_size; } - else if (xor_version == 3) { /* full file is XOR'ed using table */ - inf.decryption_enabled = 1; - inf.decryption_callback = scd_ogg_decrypt_v3_callback; - inf.scd_xor = stream_size & 0xFF; /* xor_byte is not used? (also there is data at +0x03) */ - inf.scd_xor_length = stream_size; + else if (ogg_version == 3) { /* file is XOR'ed using table (FF XIV Heavensward PC) */ + inf.decryption_callback = scd_ogg_v3_decryption_callback; + inf.scd_xor = stream_size & 0xFF; /* ogg_byte not used? */ + inf.scd_xor_length = vorb_header_size + stream_size; } else { - VGM_LOG("SCD: unknown encryption 0x%x\n", xor_version); - return NULL; + VGM_LOG("SCD: unknown ogg_version 0x%x\n", ogg_version); } - - /* hope this works */ - return init_vgmstream_ogg_vorbis_callbacks(streamFile, filename, NULL, start_offset, &inf); } + + /* actual Ogg init */ + return init_vgmstream_ogg_vorbis_callbacks(streamFile, filename, NULL, start_offset, &inf); } #endif @@ -182,21 +185,33 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - vgmstream->channels = channel_count; vgmstream->sample_rate = sample_rate; - vgmstream->num_streams = headers_entries; + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; vgmstream->meta_type = meta_SQEX_SCD; - switch (codec_id) { + switch (codec) { case 0x01: /* PCM */ - vgmstream->coding_type = coding_PCM16_int; - vgmstream->layout_type = layout_none; - vgmstream->num_samples = stream_size / 2 / channel_count; + vgmstream->coding_type = coding_PCM16LE; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x02; + vgmstream->num_samples = pcm_bytes_to_samples(stream_size, channel_count, 16); if (loop_flag) { - vgmstream->loop_start_sample = loop_start / 2 / channel_count; - vgmstream->loop_end_sample = loop_end / 2 / channel_count; + vgmstream->loop_start_sample = pcm_bytes_to_samples(loop_start, channel_count, 16); + vgmstream->loop_end_sample = pcm_bytes_to_samples(loop_end, channel_count, 16); + } + break; + + case 0x03: /* PS-ADPCM [Final Fantasy Type-0] */ + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x10; + + vgmstream->num_samples = ps_bytes_to_samples(stream_size, channel_count); + if (loop_flag) { + vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start, channel_count); + vgmstream->loop_end_sample = ps_bytes_to_samples(loop_end, channel_count); } break; @@ -234,6 +249,7 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { vgmstream->coding_type = coding_MSADPCM; vgmstream->layout_type = layout_none; vgmstream->interleave_block_size = read_16bit(post_meta_offset+0x0c,streamFile); + /* in post_meta_offset is a WAVEFORMATEX (including coefs and all) */ vgmstream->num_samples = msadpcm_bytes_to_samples(stream_size, vgmstream->interleave_block_size, vgmstream->channels); if (loop_flag) { @@ -324,7 +340,7 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { break; } - case 0x0E: { /* ATRAC3plus [Lord of Arcana (PSP)] */ + case 0x0E: { /* ATRAC3/ATRAC3plus [Lord of Arcana (PSP), Final Fantasy Type-0] */ ffmpeg_codec_data *ffmpeg_data = NULL; /* full RIFF header at start_offset/post_meta_offset (same) */ @@ -357,8 +373,9 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { } #endif + case -1: /* used for dummy entries */ default: - VGM_LOG("SCD: unknown codec_id 0x%x\n", codec_id); + VGM_LOG("SCD: unknown codec 0x%x\n", codec); goto fail; } @@ -375,9 +392,14 @@ fail: #ifdef VGM_USE_VORBIS -static void scd_ogg_decrypt_v2_callback(void *ptr, size_t size, size_t nmemb, void *datasource, int bytes_read) { +static void scd_ogg_v2_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) { + size_t bytes_read = size*nmemb; ogg_vorbis_streamfile * ov_streamfile = (ogg_vorbis_streamfile*)datasource; + /* no encryption, sometimes happens */ + if (ov_streamfile->scd_xor == 0x00) + return; + /* header is XOR'd with a constant byte */ if (ov_streamfile->offset < ov_streamfile->scd_xor_length) { int i, num_crypt; @@ -392,9 +414,9 @@ static void scd_ogg_decrypt_v2_callback(void *ptr, size_t size, size_t nmemb, vo } } -static void scd_ogg_decrypt_v3_callback(void *ptr, size_t size, size_t nmemb, void *datasource, int bytes_read) { - /* V3 decryption table found in the .exe */ - static const uint8_t scd_ogg_v3_lookuptable[256] = { /* FF XIV Heavensward */ +static void scd_ogg_v3_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) { + /* V3 decryption table found in the .exe of FF XIV Heavensward */ + static const uint8_t scd_ogg_v3_lookuptable[256] = { 0x3A, 0x32, 0x32, 0x32, 0x03, 0x7E, 0x12, 0xF7, 0xB2, 0xE2, 0xA2, 0x67, 0x32, 0x32, 0x22, 0x32, // 00-0F 0x32, 0x52, 0x16, 0x1B, 0x3C, 0xA1, 0x54, 0x7B, 0x1B, 0x97, 0xA6, 0x93, 0x1A, 0x4B, 0xAA, 0xA6, // 10-1F 0x7A, 0x7B, 0x1B, 0x97, 0xA6, 0xF7, 0x02, 0xBB, 0xAA, 0xA6, 0xBB, 0xF7, 0x2A, 0x51, 0xBE, 0x03, // 20-2F @@ -412,23 +434,25 @@ static void scd_ogg_decrypt_v3_callback(void *ptr, size_t size, size_t nmemb, vo 0xE2, 0xA2, 0x67, 0x32, 0x32, 0x12, 0x32, 0xB2, 0x32, 0x32, 0x32, 0x32, 0x75, 0xA3, 0x26, 0x7B, // E0-EF 0x83, 0x26, 0xF9, 0x83, 0x2E, 0xFF, 0xE3, 0x16, 0x7D, 0xC0, 0x1E, 0x63, 0x21, 0x07, 0xE3, 0x01, // F0-FF }; + + size_t bytes_read = size*nmemb; ogg_vorbis_streamfile *ov_streamfile = (ogg_vorbis_streamfile*)datasource; /* file is XOR'd with a table (algorithm and table by Ioncannon) */ - if (ov_streamfile->offset < ov_streamfile->scd_xor_length) { + { //if (ov_streamfile->offset < ov_streamfile->scd_xor_length) int i, num_crypt; - uint8_t byte1, byte2, xorByte; + uint8_t byte1, byte2, xor_byte; num_crypt = bytes_read; byte1 = ov_streamfile->scd_xor & 0x7F; byte2 = ov_streamfile->scd_xor & 0x3F; for (i = 0; i < num_crypt; i++) { - xorByte = scd_ogg_v3_lookuptable[(byte2 + ov_streamfile->offset + i) & 0xFF]; - xorByte &= 0xFF; - xorByte ^= ((uint8_t*)ptr)[i]; - xorByte ^= byte1; - ((uint8_t*)ptr)[i] = (uint8_t)xorByte; + xor_byte = scd_ogg_v3_lookuptable[(byte2 + ov_streamfile->offset + i) & 0xFF]; + xor_byte &= 0xFF; + xor_byte ^= ((uint8_t*)ptr)[i]; + xor_byte ^= byte1; + ((uint8_t*)ptr)[i] = (uint8_t)xor_byte; } } } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sqex_sead.c b/Frameworks/vgmstream/vgmstream/src/meta/sqex_sead.c new file mode 100644 index 000000000..2c98eaa2b --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/sqex_sead.c @@ -0,0 +1,273 @@ +#include "meta.h" +#include "../coding/coding.h" + + +static STREAMFILE* setup_sead_hca_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size); + +/* SABF/MABF - Square Enix's "Sead" audio games [Dragon Quest Builders (PS3), Dissidia Opera Omnia (mobile), FF XV (PS4)] */ +VGMSTREAM * init_vgmstream_sqex_sead(STREAMFILE * streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset, mtrl_offset, meta_offset, post_meta_offset; //, info_offset, name_offset = 0; + size_t stream_size, subheader_size; //, name_size = 0; + + int loop_flag = 0, channel_count, codec, sample_rate, loop_start, loop_end; + int is_sab = 0, is_mab = 0; + int total_subsongs, target_subsong = streamFile->stream_index; + + int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; + int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; + + + /* check extensions (.sab: sound/bgm, .mab: music, .sbin: Dissidia Opera Omnia .sab) */ + if ( !check_extensions(streamFile,"sab,mab,sbin")) + goto fail; + + + /** main header **/ + if (read_32bitBE(0x00,streamFile) == 0x73616266) { /* "sabf" */ + is_sab = 1; + } else if (read_32bitBE(0x00,streamFile) == 0x6D616266) { /* "mabf" */ + is_mab = 1; + } else { + goto fail; + } + + //if (read_8bit(0x04,streamFile) != 0x02) /* version? */ + // goto fail; + /* 0x04(1): version? (usually 0x02, rarely 0x01, ex FF XV title) */ + /* 0x05(1): 0x00/01? */ + /* 0x06(2): chunk size? (usually 0x10, rarely 0x20) */ + if (read_16bitBE(0x06,streamFile) < 0x100) { /* use size as no apparent flag */ + read_32bit = read_32bitBE; + read_16bit = read_16bitBE; + } else { + read_32bit = read_32bitLE; + read_16bit = read_16bitLE; + } + /* 0x08(1): ?, 0x09(1): ?, 0x0a(2): ? */ + if (read_32bit(0x0c,streamFile) != get_streamfile_size(streamFile)) + goto fail; + /* 0x10(10): file descriptor ("BGM", "Music", "SE", etc) */ + + + /** offset tables **/ + if (is_sab) { + if (read_32bitBE(0x20,streamFile) != 0x736E6420) goto fail; /* "snd " (info) */ + if (read_32bitBE(0x30,streamFile) != 0x73657120) goto fail; /* "seq " (unknown) */ + if (read_32bitBE(0x40,streamFile) != 0x74726B20) goto fail; /* "trk " (unknown) */ + if (read_32bitBE(0x50,streamFile) != 0x6D74726C) goto fail; /* "mtrl" (headers/streams) */ + //info_offset = read_32bit(0x28,streamFile); + //seq_offset = read_32bit(0x38,streamFile); + //trk_offset = read_32bit(0x48,streamFile); + mtrl_offset = read_32bit(0x58,streamFile); + } + else if (is_mab) { + if (read_32bitBE(0x20,streamFile) != 0x6D757363) goto fail; /* "musc" (info) */ + if (read_32bitBE(0x30,streamFile) != 0x696E7374) goto fail; /* "inst" (unknown) */ + if (read_32bitBE(0x40,streamFile) != 0x6D74726C) goto fail; /* "mtrl" (headers/streams) */ + //info_offset = read_32bit(0x28,streamFile); + //inst_offset = read_32bit(0x38,streamFile); + mtrl_offset = read_32bit(0x48,streamFile); + } + else { + goto fail; + } + /* each section starts with: + * 0x00(2): 0x00/01?, 0x02: size? (0x10), 0x04(2): entries, 0x06+: padded to 0x10 + * 0x10+0x04*entry: offset from section start, also padded to 0x10 at the end */ + + /* find meta_offset in mtrl and total subsongs */ + { + int i; + int entries = read_16bit(mtrl_offset+0x04,streamFile); + off_t entries_offset = mtrl_offset + 0x10; + + if (target_subsong == 0) target_subsong = 1; + total_subsongs = 0; + meta_offset = 0; + + /* manually find subsongs as entries can be dummy (ex. sfx banks in Dissidia Opera Omnia) */ + for (i = 0; i < entries; i++) { + off_t entry_offset = mtrl_offset + read_32bit(entries_offset + i*0x04,streamFile); + + if (read_8bit(entry_offset+0x05,streamFile) == 0) + continue; /* codec 0 when dummy */ + + total_subsongs++; + if (!meta_offset && total_subsongs == target_subsong) + meta_offset = entry_offset; + } + if (meta_offset == 0) goto fail; + /* SAB can contain 0 entries too */ + } + + + /** stream header **/ + /* 0x00(2): 0x00/01? */ + /* 0x02(2): base entry size? (0x20) */ + channel_count = read_8bit(meta_offset+0x04,streamFile); + codec = read_8bit(meta_offset+0x05,streamFile); + //entry_id = read_16bit(meta_offset+0x06,streamFile); + sample_rate = read_32bit(meta_offset+0x08,streamFile); + loop_start = read_32bit(meta_offset+0x0c,streamFile); /* in samples but usually ignored */ + + loop_end = read_32bit(meta_offset+0x10,streamFile); + subheader_size = read_32bit(meta_offset+0x14,streamFile); /* including subfile header */ + stream_size = read_32bit(meta_offset+0x18,streamFile); /* not including subfile header */ + /* 0x1c: null? */ + + loop_flag = (loop_end > 0); + post_meta_offset = meta_offset + 0x20; + + + /** info section (get stream name) **/ + //if (is_sab) { //todo load name based on entry id + /* "snd ": unknown flags/sizes and name */ + /* 0x08(2): file number within descriptor */ + /* 0x1a(2): base_entry size (-0x10?) */ + //name_size = read_32bit(snd_offset+0x20,streamFile); + //name_offset = snd_offset+0x70; + /* 0x24(4): unique id? (referenced in "seq" section) */ + //} + //else if (is_mab) { + /* "musc": unknown flags sizes and names, another format */ + //} + + + /* 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 = stream_size; + vgmstream->meta_type = is_sab ? meta_SQEX_SAB : meta_SQEX_MAB; + + switch(codec) { + + case 0x02: { /* MSADPCM [Dragon Quest Builders (Vita) sfx] */ + start_offset = post_meta_offset + subheader_size; + + /* 0x00 (2): null?, 0x02(2): entry size? */ + vgmstream->coding_type = coding_MSADPCM; + vgmstream->layout_type = layout_none; + vgmstream->interleave_block_size = read_16bit(post_meta_offset+0x04,streamFile); + + /* much like AKBs, there are slightly different loop values here, probably more accurate + * (if no loop, loop_end doubles as num_samples) */ + vgmstream->num_samples = msadpcm_bytes_to_samples(stream_size, vgmstream->interleave_block_size, vgmstream->channels); + vgmstream->loop_start_sample = read_32bit(post_meta_offset+0x08, streamFile); //loop_start + vgmstream->loop_end_sample = read_32bit(post_meta_offset+0x0c, streamFile); //loop_end + break; + } + +#ifdef VGM_USE_ATRAC9 + case 0x04: { /* ATRAC9 [Dragon Quest Builders (Vita), Final Fantaxy XV (PS4)] */ + atrac9_config cfg = {0}; + + start_offset = post_meta_offset + subheader_size; + /* post header has various typical ATRAC9 values */ + cfg.channels = vgmstream->channels; + cfg.config_data = read_32bit(post_meta_offset+0x0c,streamFile); + cfg.encoder_delay = read_32bit(post_meta_offset+0x18,streamFile); +VGM_LOG("1\n"); + vgmstream->codec_data = init_atrac9(&cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_ATRAC9; + vgmstream->layout_type = layout_none; +VGM_LOG("2\n"); + vgmstream->sample_rate = read_32bit(post_meta_offset+0x1c,streamFile); /* SAB's sample rate can be different but it's ignored */ + vgmstream->num_samples = read_32bit(post_meta_offset+0x10,streamFile); /* loop values above are also weird and ignored */ + vgmstream->loop_start_sample = read_32bit(post_meta_offset+0x20, streamFile) - (loop_flag ? cfg.encoder_delay : 0); //loop_start + vgmstream->loop_end_sample = read_32bit(post_meta_offset+0x24, streamFile) - (loop_flag ? cfg.encoder_delay : 0); //loop_end + break; + } +#endif + +#ifdef VGM_USE_MPEG + case 0x06: { /* MSF subfile (MPEG mode) [Dragon Quest Builders (PS3)] */ + mpeg_codec_data *mpeg_data = NULL; + mpeg_custom_config cfg = {0}; + + start_offset = post_meta_offset + subheader_size; + /* post header is a proper MSF, but sample rate/loops are ignored in favor of SAB's */ + + mpeg_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg); + if (!mpeg_data) goto fail; + vgmstream->codec_data = mpeg_data; + vgmstream->layout_type = layout_none; + + vgmstream->num_samples = mpeg_bytes_to_samples(stream_size, mpeg_data); + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + break; + } +#endif + + case 0x07: { /* HCA subfile [Dissidia Opera Omnia (Mobile), Final Fantaxy XV (PS4)] */ + //todo there is no easy way to use the HCA decoder; try subfile hack for now + VGMSTREAM *temp_vgmstream = NULL; + STREAMFILE *temp_streamFile = NULL; + off_t subfile_offset = post_meta_offset + 0x10; + size_t subfile_size = stream_size + subheader_size - 0x10; + /* post header has 0x10 unknown + HCA header */ + + + temp_streamFile = setup_sead_hca_streamfile(streamFile, subfile_offset, subfile_size); + if (!temp_streamFile) goto fail; + + temp_vgmstream = init_vgmstream_hca(temp_streamFile); + if (temp_vgmstream) { + /* loops can be slightly different (~1000 samples) but probably HCA's are more accurate */ + temp_vgmstream->num_streams = vgmstream->num_streams; + temp_vgmstream->stream_size = vgmstream->stream_size; + temp_vgmstream->meta_type = vgmstream->meta_type; + + close_streamfile(temp_streamFile); + close_vgmstream(vgmstream); + return temp_vgmstream; + } + else { + close_streamfile(temp_streamFile); + goto fail; + } + } + + case 0x00: /* dummy entry */ + default: + VGM_LOG("SQEX SEAD: unknown codec %x\n", codec); + goto fail; + } + + /* open the file for reading */ + if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + +static STREAMFILE* setup_sead_hca_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size) { + 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,"hca"); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sxd.c b/Frameworks/vgmstream/vgmstream/src/meta/sxd.c index a42ff2876..13801cad6 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sxd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sxd.c @@ -7,13 +7,13 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; STREAMFILE * streamHeader = NULL; off_t start_offset, chunk_offset, first_offset = 0x60, name_offset = 0; - size_t chunk_size; + size_t chunk_size, stream_size = 0; int is_separate; int loop_flag, channels, codec; int sample_rate, num_samples, loop_start_sample, loop_end_sample; uint32_t at9_config_data = 0; - int total_streams, target_stream = streamFile->stream_index; + int total_subsongs, target_subsong = streamFile->stream_index; /* check extension, case insensitive */ @@ -38,16 +38,16 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) { if (!find_chunk_le(streamHeader, 0x57415645,first_offset,0, &chunk_offset,&chunk_size)) goto fail; /* "WAVE" */ /* check multi-streams (usually only in SFX containers) */ - total_streams = read_32bitLE(chunk_offset+0x04,streamHeader); - if (target_stream == 0) target_stream = 1; - if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail; + total_subsongs = read_32bitLE(chunk_offset+0x04,streamHeader); + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; /* read stream header */ { off_t table_offset, header_offset, stream_offset; /* get target offset using table of relative offsets within WAVE */ - table_offset = chunk_offset + 0x08 + 4*(target_stream-1); + table_offset = chunk_offset + 0x08 + 4*(target_subsong-1); header_offset = table_offset + read_32bitLE(table_offset,streamHeader); /* 0x00(4): type/location? (00/01=sxd/RAM?, 02/03=sxd2/stream?) */ @@ -59,7 +59,7 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) { num_samples = read_32bitLE(header_offset+0x14,streamHeader); loop_start_sample = read_32bitLE(header_offset+0x18,streamHeader); loop_end_sample = read_32bitLE(header_offset+0x1c,streamHeader); - /* 0x20(4): data size */ + stream_size = read_32bitLE(header_offset+0x20,streamHeader); stream_offset = read_32bitLE(header_offset+0x24,streamHeader); /* Extra data, variable sized and uses some kind of TLVs (HEVAG's is optional and much smaller). @@ -100,7 +100,7 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) { int num_entries = read_16bitLE(chunk_offset+0x04,streamHeader); /* can be bigger than streams */ for (i = 0; i < num_entries; i++) { uint32_t index = (uint32_t)read_32bitLE(chunk_offset+0x08 + 0x08 + i*0x0c,streamHeader); - if (index+1 == target_stream) { + if (index+1 == target_subsong) { name_offset = chunk_offset+0x08 + 0x00 + i*0x0c + read_32bitLE(chunk_offset+0x08 + 0x00 + i*0x0c,streamHeader); break; } @@ -116,20 +116,27 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) { vgmstream->num_samples = num_samples; vgmstream->loop_start_sample = loop_start_sample; vgmstream->loop_end_sample = loop_end_sample; - vgmstream->num_streams = total_streams; + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; vgmstream->meta_type = meta_SXD; if (name_offset) read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamHeader); switch (codec) { - case 0x21: /* HEVAG */ + case 0x20: /* PS-ADPCM [Hot Shots Golf: World Invitational (Vita) sfx] */ + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x10; + break; + + case 0x21: /* HEVAG [Gravity Rush (Vita) sfx] */ vgmstream->coding_type = coding_HEVAG; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x10; break; #ifdef VGM_USE_ATRAC9 - case 0x42: { /* ATRAC9 */ + case 0x42: { /* ATRAC9 [Soul Sacrifice (Vita), Freedom Wars (Vita)] */ atrac9_config cfg = {0}; cfg.channels = vgmstream->channels; @@ -142,9 +149,9 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) { break; } #endif - + //case 0x28: /* dummy codec? (found with 0 samples) [Hot Shots Golf: World Invitational (Vita) sfx] */ default: - VGM_LOG("SXD: unknown codec 0x%x", codec); + VGM_LOG("SXD: unknown codec 0x%x\n", codec); goto fail; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c b/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c index 27495f354..d0ad0272c 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c @@ -127,6 +127,7 @@ VGMSTREAM * init_vgmstream_ubi_sb(STREAMFILE *streamFile) { vgmstream->sample_rate = sb.sample_rate; vgmstream->num_streams = sb.total_streams; + vgmstream->stream_size = sb.stream_size; vgmstream->meta_type = meta_UBI_SB; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/vxn.c b/Frameworks/vgmstream/vgmstream/src/meta/vxn.c index 238d3e24f..27aacce09 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/vxn.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/vxn.c @@ -7,7 +7,7 @@ VGMSTREAM * init_vgmstream_vxn(STREAMFILE *streamFile) { int loop_flag = 0, channel_count, codec, sample_rate, block_align, bits, num_samples; off_t start_offset, stream_offset, chunk_offset, first_offset = 0x00; size_t stream_size; - int total_streams, target_stream = streamFile->stream_index; + int total_subsongs, target_subsong = streamFile->stream_index; /* check extensions */ if (!check_extensions(streamFile,"vxn")) @@ -31,13 +31,13 @@ VGMSTREAM * init_vgmstream_vxn(STREAMFILE *streamFile) { * (the "Plst" and "Rule" chunks may have order info) */ if (!find_chunk_le(streamFile, 0x5365676D,first_offset,0, &chunk_offset,NULL)) /* "Segm" */ goto fail; - total_streams = read_32bitLE(chunk_offset+0x00, streamFile); - if (target_stream == 0) target_stream = 1; - if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail; + total_subsongs = read_32bitLE(chunk_offset+0x00, streamFile); + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; - stream_offset = read_32bitLE(chunk_offset+0x04 + (target_stream-1)*0x18 + 0x00, streamFile); - stream_size = read_32bitLE(chunk_offset+0x04 + (target_stream-1)*0x18 + 0x04, streamFile); - num_samples = read_32bitLE(chunk_offset+0x04 + (target_stream-1)*0x18 + 0x08, streamFile); + stream_offset = read_32bitLE(chunk_offset+0x04 + (target_subsong-1)*0x18 + 0x00, streamFile); + stream_size = read_32bitLE(chunk_offset+0x04 + (target_subsong-1)*0x18 + 0x04, streamFile); + num_samples = read_32bitLE(chunk_offset+0x04 + (target_subsong-1)*0x18 + 0x08, streamFile); if (!find_chunk_le(streamFile, 0x44617461,first_offset,0, &chunk_offset,NULL)) /* "Data" */ goto fail; @@ -50,8 +50,8 @@ VGMSTREAM * init_vgmstream_vxn(STREAMFILE *streamFile) { vgmstream->sample_rate = sample_rate; vgmstream->num_samples = num_samples; - vgmstream->num_streams = total_streams; - + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; vgmstream->meta_type = meta_VXN; switch (codec) { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/wwise.c b/Frameworks/vgmstream/vgmstream/src/meta/wwise.c index e4619791a..bf5e97bc2 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/wwise.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/wwise.c @@ -233,25 +233,25 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { case 0x28: /* early (~2009), ex. The Lord of the Rings: Conquest PC */ data_offsets = 0x18; block_offsets = 0; /* no need, full headers are present */ - cfg.header_type = TYPE_8; - cfg.packet_type = STANDARD; - cfg.setup_type = HEADER_TRIAD; + cfg.header_type = WWV_TYPE_8; + cfg.packet_type = WWV_STANDARD; + cfg.setup_type = WWV_HEADER_TRIAD; break; //case 0x32: /* ? */ case 0x34: /* common (2010~2011) */ data_offsets = 0x18; block_offsets = 0x30; - cfg.header_type = TYPE_6; - cfg.packet_type = STANDARD; - cfg.setup_type = EXTERNAL_CODEBOOKS; /* setup_type will be corrected later */ + cfg.header_type = WWV_TYPE_6; + cfg.packet_type = WWV_STANDARD; + cfg.setup_type = WWV_EXTERNAL_CODEBOOKS; /* setup_type will be corrected later */ break; case 0x2a: /* uncommon (mid 2011), ex. infamous 2 PS3 */ data_offsets = 0x10; block_offsets = 0x28; - cfg.header_type = TYPE_2; - cfg.packet_type = MODIFIED; - cfg.setup_type = EXTERNAL_CODEBOOKS; + cfg.header_type = WWV_TYPE_2; + cfg.packet_type = WWV_MODIFIED; + cfg.setup_type = WWV_EXTERNAL_CODEBOOKS; break; default: VGM_LOG("WWISE: unknown vorb size 0x%x\n", vorb_size); @@ -277,11 +277,11 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { /* if the setup after header starts with "(data)BCV" it's an inline codebook) */ if ((id & 0x00FFFFFF) == 0x00424356) { /* 0"BCV" */ - cfg.setup_type = FULL_SETUP; + cfg.setup_type = WWV_FULL_SETUP; } /* if the setup is suspiciously big it's probably trimmed inline codebooks */ else if (setup_size > 0x200) { /* an external setup it's ~0x100 max + some threshold */ - cfg.setup_type = INLINE_CODEBOOKS; + cfg.setup_type = WWV_INLINE_CODEBOOKS; } } @@ -297,13 +297,13 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { case 0x30: data_offsets = 0x10; block_offsets = 0x28; - cfg.header_type = TYPE_2; - cfg.packet_type = MODIFIED; + cfg.header_type = WWV_TYPE_2; + cfg.packet_type = WWV_MODIFIED; /* setup not detectable by header, so we'll try both; hopefully libvorbis will reject wrong codebooks * - standard: early (<2012), ex. The King of Fighters XIII X360 (2011/11), .ogg (cbs are from aoTuV, too) * - aoTuV603: later (>2012), ex. Sonic & All-Stars Racing Transformed PC (2012/11), .wem */ - cfg.setup_type = is_wem ? AOTUV603_CODEBOOKS : EXTERNAL_CODEBOOKS; /* aoTuV came along .wem */ + cfg.setup_type = is_wem ? WWV_AOTUV603_CODEBOOKS : WWV_EXTERNAL_CODEBOOKS; /* aoTuV came along .wem */ break; //case 0x2a: /* Rocksmith 2011 X360? */ @@ -326,14 +326,14 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { /* all blocksizes I've seen are 0x08+0x0B except Oddworld PSV, that uses 0x09+0x09 * (maybe lower spec machines = needs simpler packets) */ if (cfg.blocksize_0_exp == cfg.blocksize_1_exp) - cfg.packet_type = STANDARD; + cfg.packet_type = WWV_STANDARD; } /* try with the selected codebooks */ vgmstream->codec_data = init_vorbis_custom(streamFile, start_offset + setup_offset, VORBIS_WWISE, &cfg); if (!vgmstream->codec_data) { /* codebooks failed: try again with the other type */ - cfg.setup_type = is_wem ? EXTERNAL_CODEBOOKS : AOTUV603_CODEBOOKS; + cfg.setup_type = is_wem ? WWV_EXTERNAL_CODEBOOKS : WWV_AOTUV603_CODEBOOKS; vgmstream->codec_data = init_vorbis_custom(streamFile, start_offset + setup_offset, VORBIS_WWISE, &cfg); if (!vgmstream->codec_data) goto fail; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xvag.c b/Frameworks/vgmstream/vgmstream/src/meta/xvag.c index 0a84c1e81..382a4e0ad 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xvag.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xvag.c @@ -1,6 +1,7 @@ #include "meta.h" #include "../coding/coding.h" -#include "../util.h" +#include "../layout/layout.h" + static int ps_adpcm_find_loop_offsets(STREAMFILE *streamFile, int channel_count, off_t start_offset, off_t * loop_start, off_t * loop_end); @@ -10,12 +11,12 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) { int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; int loop_flag = 0, channel_count, codec; int big_endian; - int sample_rate, num_samples, multiplier, multistreams = 0; + int sample_rate, num_samples, interleave_factor, multistreams = 0; int total_subsongs = 0, target_subsong = streamFile->stream_index; - off_t start_offset, loop_start, loop_end, chunk_offset; + off_t start_offset, loop_start = 0, loop_end = 0, chunk_offset; off_t first_offset = 0x20; - size_t chunk_size; + size_t chunk_size, stream_size; /* check extension, case insensitive */ if (!check_extensions(streamFile,"xvag")) @@ -37,7 +38,7 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) { /* 0x08: flags? (&0x01=big endian, 0x02=?, 0x06=full RIFF AT9?) * 0x09: flags2? (0x00/0x01/0x04, speaker mode?) * 0x0a: always 0? - * 0x0b: version-flag? (0x5f/0x60/0x61, last has extra data) */ + * 0x0b: version-flag? (0x5f/0x60/0x61/0x62/etc) */ /* "fmat": base format (always first) */ if (!find_chunk(streamFile, 0x666D6174,first_offset,0, &chunk_offset,&chunk_size, big_endian, 1)) /*"fmat"*/ @@ -48,14 +49,20 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) { /* 0x0c: samples again? playable section? */ VGM_ASSERT(num_samples != read_32bit(chunk_offset+0x0c,streamFile), "XVAG: num_samples values don't match\n"); - multiplier = read_32bit(chunk_offset+0x10,streamFile); /* 'interleave factor' */ + interleave_factor = read_32bit(chunk_offset+0x10,streamFile); sample_rate = read_32bit(chunk_offset+0x14,streamFile); - /* 0x18: datasize */ + stream_size = read_32bit(chunk_offset+0x18,streamFile); - /* extra data, seen in MPEG/ATRAC9 */ + /* extra data, seen in versions 0x61+ */ if (chunk_size > 0x1c) { - total_subsongs = read_32bit(chunk_offset+0x1c,streamFile); /* number of interleaved layers */ - multistreams = read_32bit(chunk_offset+0x20,streamFile); /* number of bitstreams per layer (for multistream Nch MPEG/ATRAC9) */ + /* number of interleaved subsong layers */ + total_subsongs = read_32bit(chunk_offset+0x1c,streamFile); + /* number of interleaved bitstreams per layer (multistreams * channels_per_stream = channels) */ + multistreams = read_32bit(chunk_offset+0x20,streamFile); + } + else { + total_subsongs = 1; + multistreams = 1; } if (target_subsong == 0) target_subsong = 1; if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; @@ -66,9 +73,9 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) { /* "cues": cue/labels (rare) */ /* "0000": end chunk before start_offset */ - /* some XVAG seem to do full loops, this should detect them as looping */ + /* some XVAG seem to do full loops, this should detect them as looping (basically tests is last frame is empty) */ //todo remove, looping seems external and specified in Scream Tool's bank formats - if (codec == 0x06) { + if (codec == 0x06 && total_subsongs == 1) { loop_flag = ps_adpcm_find_loop_offsets(streamFile, channel_count, start_offset, &loop_start, &loop_end); } @@ -79,30 +86,45 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) { vgmstream->sample_rate = sample_rate; vgmstream->num_samples = num_samples; vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = (stream_size / total_subsongs); vgmstream->meta_type = meta_XVAG; switch (codec) { - //case 0x??: /* PCM? */ case 0x06: /* VAG (PS ADPCM): God of War III (PS3), Uncharted 1/2 (PS3), Ratchet and Clank Future (PS3) */ case 0x07: /* SVAG? (PS ADPCM with extended table): inFamous 1 (PS3) */ - if (total_subsongs > 1 || multistreams > 1) goto fail; - if (multiplier > 1) goto fail; + if (multistreams > 1 && multistreams != vgmstream->channels) goto fail; + if (total_subsongs > 1 && multistreams > 1) goto fail; + if (total_subsongs > 1 && vgmstream->channels > 1) goto fail; /* unknown layout */ - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x10; vgmstream->coding_type = coding_PSX; + if (total_subsongs > 1) { /* God of War 3 (PS4) */ + vgmstream->layout_type = layout_blocked_xvag_subsong; + vgmstream->interleave_block_size = 0x10; + vgmstream->full_block_size = 0x10 * interleave_factor * total_subsongs; + vgmstream->current_block_size = 0x10 * interleave_factor; + start_offset += 0x10 * interleave_factor * (target_subsong-1); + } + else { + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x10 * interleave_factor; /* usually 1, bigger in GoW3 PS4 */ + } + if (loop_flag) { vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start, vgmstream->channels); vgmstream->loop_end_sample = ps_bytes_to_samples(loop_end, vgmstream->channels); } + break; #ifdef VGM_USE_MPEG case 0x08: { /* MPEG: The Last of Us (PS3), Uncharted 3 (PS3), Medieval Moves (PS3) */ mpeg_custom_config cfg = {0}; - if (total_subsongs > 1 || (multistreams > 1 && multistreams == vgmstream->channels)) goto fail; + /* often 2ch per MPEG and rarely 1ch (GoW3 PS4) */ + if (multistreams > 1 && !(multistreams*1 == vgmstream->channels || multistreams*2 == vgmstream->channels)) goto fail; + if (total_subsongs > 1) goto fail; + //todo rare test file in The Last of Us PS4 uses 6ch with 1 2ch stream, surround MPEG/mp3pro? /* "mpin": mpeg info */ /* 0x00/04: mpeg version/layer? other: unknown or repeats of "fmat" */ @@ -110,7 +132,7 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) { goto fail; cfg.chunk_size = read_32bit(chunk_offset+0x1c,streamFile); /* fixed frame size */ - cfg.interleave = cfg.chunk_size * multiplier; + cfg.interleave = cfg.chunk_size * interleave_factor; vgmstream->codec_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_XVAG, &cfg); if (!vgmstream->codec_data) goto fail; @@ -138,7 +160,7 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) { } else if (total_subsongs > 1) { /* interleaves 'multiplier' superframes per subsong (all share config_data) */ - cfg.interleave_skip = read_32bit(chunk_offset+0x00,streamFile) * multiplier; + cfg.interleave_skip = read_32bit(chunk_offset+0x00,streamFile) * interleave_factor; cfg.subsong_skip = total_subsongs; /* start in subsong's first superframe */ start_offset += (target_subsong-1) * cfg.interleave_skip * (cfg.subsong_skip-1); @@ -158,6 +180,7 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) { } #endif + //case 0x??: /* PCM? */ default: goto fail; } @@ -167,6 +190,9 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) { if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) goto fail; + if (vgmstream->layout_type == layout_blocked_xvag_subsong) + block_update_xvag_subsong(start_offset, vgmstream); + return vgmstream; fail: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xwb.c b/Frameworks/vgmstream/vgmstream/src/meta/xwb.c index 403f0d98d..88335d956 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xwb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xwb.c @@ -9,7 +9,7 @@ /* the x.x version is just to make it clearer, MS only classifies XACT as 1/2/3 */ #define XACT1_0_MAX 1 /* Project Gotham Racing 2 (v1), Silent Hill 4 (v1) */ -#define XACT1_1_MAX 3 /* The King of Fighters 2003 (v3) */ +#define XACT1_1_MAX 3 /* Unreal Championship (v2), The King of Fighters 2003 (v3) */ #define XACT2_0_MAX 34 /* Dead or Alive 4 (v17), Kameo (v23), Table Tennis (v34) */ // v35/36/37 too? #define XACT2_1_MAX 38 /* Prey (v38) */ // v39 too? #define XACT2_2_MAX 41 /* Blue Dragon (v40) */ @@ -354,47 +354,45 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { vgmstream->loop_start_sample = xwb.loop_start_sample; vgmstream->loop_end_sample = xwb.loop_end_sample; vgmstream->num_streams = xwb.streams; + vgmstream->stream_size = xwb.stream_size; vgmstream->meta_type = meta_XWB; get_xsb_name(vgmstream->stream_name,STREAM_NAME_SIZE, target_stream, &xwb, streamFile); switch(xwb.codec) { - case PCM: - vgmstream->coding_type = xwb.bits_per_sample == 0 ? coding_PCM8 : + case PCM: /* Unreal Championship (Xbox)[PCM8], KOF2003 (Xbox)[PCM16LE], Otomedius (X360)[PCM16BE] */ + vgmstream->coding_type = xwb.bits_per_sample == 0 ? coding_PCM8_U : (xwb.little_endian ? coding_PCM16LE : coding_PCM16BE); vgmstream->layout_type = xwb.channels > 1 ? layout_interleave : layout_none; vgmstream->interleave_block_size = xwb.bits_per_sample == 0 ? 0x01 : 0x02; break; - case XBOX_ADPCM: + case XBOX_ADPCM: /* Silent Hill 4 (Xbox) */ vgmstream->coding_type = coding_XBOX; vgmstream->layout_type = layout_none; break; - case MS_ADPCM: + case MS_ADPCM: /* Persona 4 Ultimax (AC) */ vgmstream->coding_type = coding_MSADPCM; vgmstream->layout_type = layout_none; vgmstream->interleave_block_size = (xwb.block_align + 22) * xwb.channels; /*22=CONVERSION_OFFSET (?)*/ break; #ifdef VGM_USE_FFMPEG - case XMA1: { - ffmpeg_codec_data *ffmpeg_data = NULL; + case XMA1: { /* Kameo (X360) */ uint8_t buf[100]; int bytes; bytes = ffmpeg_make_riff_xma1(buf, 100, vgmstream->num_samples, xwb.stream_size, vgmstream->channels, vgmstream->sample_rate, 0); if (bytes <= 0) goto fail; - ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size); - if ( !ffmpeg_data ) goto fail; - vgmstream->codec_data = ffmpeg_data; + vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size); + if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; break; } - case XMA2: { - ffmpeg_codec_data *ffmpeg_data = NULL; + case XMA2: { /* Blue Dragon (X360) */ uint8_t buf[100]; int bytes, block_size, block_count; @@ -404,15 +402,14 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { bytes = ffmpeg_make_riff_xma2(buf, 100, vgmstream->num_samples, xwb.stream_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); if (bytes <= 0) goto fail; - ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size); - if ( !ffmpeg_data ) goto fail; - vgmstream->codec_data = ffmpeg_data; + vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size); + if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; break; } - case WMA: { /* WMAudio1 (WMA v1) */ + case WMA: { /* WMAudio1 (WMA v1): Prince of Persia 2 port (Xbox) */ ffmpeg_codec_data *ffmpeg_data = NULL; ffmpeg_data = init_ffmpeg_offset(streamFile, xwb.stream_offset,xwb.stream_size); @@ -427,8 +424,7 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { break; } - case XWMA: { /* WMAudio2 (WMA v2), WMAudio3 (WMA Pro) */ - ffmpeg_codec_data *ffmpeg_data = NULL; + case XWMA: { /* WMAudio2 (WMA v2): BlazBlue (X360), WMAudio3 (WMA Pro): ? */ uint8_t buf[100]; int bytes, bps_index, block_align, block_index, avg_bps, wma_codec; @@ -444,15 +440,14 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { bytes = ffmpeg_make_riff_xwma(buf, 100, wma_codec, xwb.stream_size, vgmstream->channels, vgmstream->sample_rate, avg_bps, block_align); if (bytes <= 0) goto fail; - ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size); - if ( !ffmpeg_data ) goto fail; - vgmstream->codec_data = ffmpeg_data; + vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size); + if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; break; } - case ATRAC3: { /* Techland PS3 extension */ + case ATRAC3: { /* Techland PS3 extension: Sniper Ghost Warrior (PS3) */ uint8_t buf[200]; int bytes; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xwc.c b/Frameworks/vgmstream/vgmstream/src/meta/xwc.c new file mode 100644 index 000000000..a9f7b4c54 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/xwc.c @@ -0,0 +1,108 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* .XWC - Starbreeze games [Chronicles of Riddick: Assault on Dark Athena, Syndicate] */ +VGMSTREAM * init_vgmstream_xwc(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + size_t data_size; + int loop_flag, channel_count, codec; + + /* 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 */ + loop_flag = 0; /* seemingly not in the file */ + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->num_samples = read_32bitLE(0x28, streamFile); + vgmstream->meta_type = meta_XWC; + + switch(codec) { +#ifdef VGM_USE_MPEG + case 0x4D504547: { /* "MPEG" (PS3) */ + 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->codec_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->layout_type = layout_none; + + vgmstream->sample_rate = ((mpeg_codec_data*)vgmstream->codec_data)->sample_rate_per_frame; + break; + } +#endif +#ifdef VGM_USE_FFMPEG + case 0x584D4100: { /* "XMA\0" (X360) */ + 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; + 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); + /* 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); + if (bytes <= 0) goto fail; + + vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size - start_offset - 0x28); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + vgmstream->sample_rate = sample_rate; + break; + } + + case 0x564F5242: { /* "VORB" (PC) */ + start_offset = 0x30; + + vgmstream->codec_data = init_ffmpeg_offset(streamFile, start_offset, data_size - start_offset - 0x28); + if ( !vgmstream->codec_data ) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + vgmstream->sample_rate = read_32bitLE(start_offset + 0x28, streamFile); + break; + } +#endif + default: + 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; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/streamfile.c b/Frameworks/vgmstream/vgmstream/src/streamfile.c index 97e549927..f62b78422 100644 --- a/Frameworks/vgmstream/vgmstream/src/streamfile.c +++ b/Frameworks/vgmstream/vgmstream/src/streamfile.c @@ -22,10 +22,6 @@ typedef struct { uint8_t * buffer; /* data buffer */ size_t buffersize; /* max buffer size */ size_t filesize; /* cached file size (max offset) */ -#ifdef PROFILE_STREAMFILE - size_t bytes_read; - int error_count; -#endif } STDIOSTREAMFILE; static STREAMFILE * open_stdio_streamfile_buffer(const char * const filename, size_t buffersize); @@ -77,9 +73,6 @@ static size_t read_the_rest(uint8_t * dest, off_t offset, size_t length, STDIOST /* position to new offset */ if (fseeko(streamfile->infile,offset,SEEK_SET)) { streamfile->offset = streamfile->filesize; -#ifdef PROFILE_STREAMFILE - streamfile->error_count++; -#endif return 0; /* fail miserably (fseek shouldn't fail and reach this) */ } streamfile->offset = offset; @@ -94,15 +87,6 @@ static size_t read_the_rest(uint8_t * dest, off_t offset, size_t length, STDIOST length_read = fread(streamfile->buffer,sizeof(uint8_t),streamfile->buffersize,streamfile->infile); streamfile->validsize = length_read; -#ifdef PROFILE_STREAMFILE - if (ferror(streamfile->infile)) { - clearerr(streamfile->infile); - streamfile->error_count++; - } - - streamfile->bytes_read += length_read; -#endif - /* if we can't get enough to satisfy the request (EOF) we give up */ if (length_read < length_to_read) { memcpy(dest,streamfile->buffer,length_read); @@ -154,10 +138,6 @@ static size_t read_stdio(STDIOSTREAMFILE *streamfile,uint8_t * dest, off_t offse /* request outside buffer: new fread */ { size_t length_read = read_the_rest(dest,offset,length,streamfile); -#ifdef PROFILE_STREAMFILE - if (length_read < length) - streamfile->error_count++; -#endif return length_read; } } @@ -181,15 +161,6 @@ static void get_name_stdio(STDIOSTREAMFILE *streamfile,char *buffer,size_t lengt buffer[length-1]='\0'; } -#ifdef PROFILE_STREAMFILE -static size_t get_bytes_read_stdio(STDIOSTREAMFILE *streamFile) { - return streamFile->bytes_read; -} -static size_t get_error_count_stdio(STDIOSTREAMFILE *streamFile) { - return streamFile->error_count; -} -#endif - static STREAMFILE *open_stdio(STDIOSTREAMFILE *streamFile,const char * const filename,size_t buffersize) { int newfd; FILE *newfile; @@ -237,10 +208,6 @@ static STREAMFILE * open_stdio_streamfile_buffer_by_file(FILE *infile,const char streamfile->sf.get_realname = (void*)get_name_stdio; streamfile->sf.open = (void*)open_stdio; streamfile->sf.close = (void*)close_stdio; -#ifdef PROFILE_STREAMFILE - streamfile->sf.get_bytes_read = (void*)get_bytes_read_stdio; - streamfile->sf.get_error_count = (void*)get_error_count_stdio; -#endif streamfile->infile = infile; streamfile->buffersize = buffersize; @@ -280,6 +247,439 @@ STREAMFILE * open_stdio_streamfile_by_file(FILE * file, const char * filename) { return open_stdio_streamfile_buffer_by_file(file,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); } +/* **************************************************** */ + +//todo stream_index: copy? pass? funtion? external? +//todo use realnames on reopen? simplify? +//todo use safe string ops, this ain't easy + +typedef struct { + STREAMFILE sf; + + STREAMFILE *inner_sf; +} WRAP_STREAMFILE; + +static size_t wrap_read(WRAP_STREAMFILE *streamfile, uint8_t * dest, off_t offset, size_t length) { + return streamfile->inner_sf->read(streamfile->inner_sf, dest, offset, length); /* default */ +} +static size_t wrap_get_size(WRAP_STREAMFILE * streamfile) { + return streamfile->inner_sf->get_size(streamfile->inner_sf); /* default */ +} +static size_t wrap_get_offset(WRAP_STREAMFILE * streamfile) { + return streamfile->inner_sf->get_offset(streamfile->inner_sf); /* default */ +} +static void wrap_get_name(WRAP_STREAMFILE *streamfile, char *buffer, size_t length) { + streamfile->inner_sf->get_name(streamfile->inner_sf, buffer, length); /* default */ +} +static void wrap_get_realname(WRAP_STREAMFILE *streamfile, char *buffer, size_t length) { + streamfile->inner_sf->get_realname(streamfile->inner_sf, buffer, length); /* default */ +} +static void wrap_open(WRAP_STREAMFILE *streamfile, const char * const filename, size_t buffersize) { + streamfile->inner_sf->open(streamfile->inner_sf, filename, buffersize); /* default (don't wrap) */ +} +static void wrap_close(WRAP_STREAMFILE *streamfile) { + //streamfile->inner_sf->close(streamfile->inner_sf); /* don't close */ + free(streamfile); +} + +STREAMFILE *open_wrap_streamfile(STREAMFILE *streamfile) { + WRAP_STREAMFILE *this_sf; + + if (!streamfile) return NULL; + + this_sf = calloc(1,sizeof(WRAP_STREAMFILE)); + if (!this_sf) return NULL; + + /* set callbacks and internals */ + this_sf->sf.read = (void*)wrap_read; + this_sf->sf.get_size = (void*)wrap_get_size; + this_sf->sf.get_offset = (void*)wrap_get_offset; + this_sf->sf.get_name = (void*)wrap_get_name; + this_sf->sf.get_realname = (void*)wrap_get_realname; + this_sf->sf.open = (void*)wrap_open; + this_sf->sf.close = (void*)wrap_close; + this_sf->sf.stream_index = streamfile->stream_index; + + this_sf->inner_sf = streamfile; + + return &this_sf->sf; +} + +/* **************************************************** */ + +typedef struct { + STREAMFILE sf; + + STREAMFILE *inner_sf; + off_t start; + size_t size; +} CLAMP_STREAMFILE; + +static size_t clamp_read(CLAMP_STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length) { + off_t inner_offset = streamfile->start + offset; + size_t clamp_length = length > (streamfile->size - offset) ? (streamfile->size - offset) : length; + return streamfile->inner_sf->read(streamfile->inner_sf, dest, inner_offset, clamp_length); +} +static size_t clamp_get_size(CLAMP_STREAMFILE *streamfile) { + return streamfile->size; +} +static off_t clamp_get_offset(CLAMP_STREAMFILE *streamfile) { + return streamfile->inner_sf->get_offset(streamfile->inner_sf) - streamfile->start; +} +static void clamp_get_name(CLAMP_STREAMFILE *streamfile, char *buffer, size_t length) { + streamfile->inner_sf->get_name(streamfile->inner_sf, buffer, length); /* default */ +} +static void clamp_get_realname(CLAMP_STREAMFILE *streamfile, char *buffer, size_t length) { + streamfile->inner_sf->get_realname(streamfile->inner_sf, buffer, length); /* default */ +} +static STREAMFILE *clamp_open(CLAMP_STREAMFILE *streamfile, const char * const filename, size_t buffersize) { + char original_filename[PATH_LIMIT]; + STREAMFILE *new_inner_sf; + + new_inner_sf = streamfile->inner_sf->open(streamfile->inner_sf,filename,buffersize); + streamfile->inner_sf->get_name(streamfile->inner_sf, original_filename, PATH_LIMIT); + + /* detect re-opening the file */ + if (strcmp(filename, original_filename) == 0) { + return open_clamp_streamfile(new_inner_sf, streamfile->start, streamfile->size); /* clamp again */ + } else { + return new_inner_sf; /**/ + } +} +static void clamp_close(CLAMP_STREAMFILE *streamfile) { + streamfile->inner_sf->close(streamfile->inner_sf); + free(streamfile); +} + +STREAMFILE *open_clamp_streamfile(STREAMFILE *streamfile, off_t start, size_t size) { + CLAMP_STREAMFILE *this_sf; + + if (!streamfile || !size) return NULL; + if (start + size > get_streamfile_size(streamfile)) return NULL; + + this_sf = calloc(1,sizeof(CLAMP_STREAMFILE)); + if (!this_sf) return NULL; + + /* set callbacks and internals */ + this_sf->sf.read = (void*)clamp_read; + this_sf->sf.get_size = (void*)clamp_get_size; + this_sf->sf.get_offset = (void*)clamp_get_offset; + this_sf->sf.get_name = (void*)clamp_get_name; + this_sf->sf.get_realname = (void*)clamp_get_realname; + this_sf->sf.open = (void*)clamp_open; + this_sf->sf.close = (void*)clamp_close; + this_sf->sf.stream_index = streamfile->stream_index; + + this_sf->inner_sf = streamfile; + this_sf->start = start; + this_sf->size = size; + + return &this_sf->sf; +} + +/* **************************************************** */ + +typedef struct { + STREAMFILE sf; + + STREAMFILE *inner_sf; + void* data; + size_t data_size; + size_t (*read_callback)(STREAMFILE *, uint8_t *, off_t, size_t, void*); +} IO_STREAMFILE; + +static size_t io_read(IO_STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length) { + return streamfile->read_callback(streamfile->inner_sf, dest, offset, length, streamfile->data); +} +static size_t io_get_size(IO_STREAMFILE *streamfile) { + return streamfile->inner_sf->get_size(streamfile->inner_sf); /* default */ +} +static off_t io_get_offset(IO_STREAMFILE *streamfile) { + return streamfile->inner_sf->get_offset(streamfile->inner_sf); /* default */ +} +static void io_get_name(IO_STREAMFILE *streamfile, char *buffer, size_t length) { + streamfile->inner_sf->get_name(streamfile->inner_sf, buffer, length); /* default */ +} +static void io_get_realname(IO_STREAMFILE *streamfile, char *buffer, size_t length) { + streamfile->inner_sf->get_realname(streamfile->inner_sf, buffer, length); /* default */ +} +static STREAMFILE *io_open(IO_STREAMFILE *streamfile, const char * const filename, size_t buffersize) { + //todo should have some flag to decide if opening other files with IO + STREAMFILE *new_inner_sf = streamfile->inner_sf->open(streamfile->inner_sf,filename,buffersize); + return open_io_streamfile(new_inner_sf, streamfile->data, streamfile->data_size, streamfile->read_callback); +} +static void io_close(IO_STREAMFILE *streamfile) { + streamfile->inner_sf->close(streamfile->inner_sf); + free(streamfile->data); + free(streamfile); +} + +STREAMFILE *open_io_streamfile(STREAMFILE *streamfile, void* data, size_t data_size, void* read_callback) { + IO_STREAMFILE *this_sf; + + if (!streamfile) return NULL; + if ((data && !data_size) || (!data && data_size)) return NULL; + + this_sf = calloc(1,sizeof(IO_STREAMFILE)); + if (!this_sf) return NULL; + + /* set callbacks and internals */ + this_sf->sf.read = (void*)io_read; + this_sf->sf.get_size = (void*)io_get_size; + this_sf->sf.get_offset = (void*)io_get_offset; + this_sf->sf.get_name = (void*)io_get_name; + this_sf->sf.get_realname = (void*)io_get_realname; + this_sf->sf.open = (void*)io_open; + this_sf->sf.close = (void*)io_close; + this_sf->sf.stream_index = streamfile->stream_index; + + this_sf->inner_sf = streamfile; + if (data) { + this_sf->data = malloc(data_size); + if (!this_sf->data) { + free(this_sf); + return NULL; + } + memcpy(this_sf->data, data, data_size); + } + this_sf->data_size = data_size; + this_sf->read_callback = read_callback; + + return &this_sf->sf; +} + +/* **************************************************** */ + +typedef struct { + STREAMFILE sf; + + STREAMFILE *inner_sf; + char fakename[PATH_LIMIT]; +} FAKENAME_STREAMFILE; + +static size_t fakename_read(FAKENAME_STREAMFILE *streamfile, uint8_t * dest, off_t offset, size_t length) { + return streamfile->inner_sf->read(streamfile->inner_sf, dest, offset, length); /* default */ +} +static size_t fakename_get_size(FAKENAME_STREAMFILE * streamfile) { + return streamfile->inner_sf->get_size(streamfile->inner_sf); /* default */ +} +static size_t fakename_get_offset(FAKENAME_STREAMFILE * streamfile) { + return streamfile->inner_sf->get_offset(streamfile->inner_sf); /* default */ +} +static void fakename_get_name(FAKENAME_STREAMFILE *streamfile, char *buffer, size_t length) { + strncpy(buffer,streamfile->fakename,length); + buffer[length-1]='\0'; +} +static void fakename_get_realname(FAKENAME_STREAMFILE *streamfile, char *buffer, size_t length) { + fakename_get_name(streamfile, buffer, length); +} +static STREAMFILE *fakename_open(FAKENAME_STREAMFILE *streamfile, const char * const filename, size_t buffersize) { + /* detect re-opening the file */ + if (strcmp(filename, streamfile->fakename) == 0) { + STREAMFILE *new_inner_sf; + char original_filename[PATH_LIMIT]; + + streamfile->inner_sf->get_name(streamfile->inner_sf, original_filename, PATH_LIMIT); + new_inner_sf = streamfile->inner_sf->open(streamfile->inner_sf, original_filename, buffersize); + return open_fakename_streamfile(new_inner_sf, streamfile->fakename, NULL); + } + else { + return streamfile->inner_sf->open(streamfile->inner_sf, filename, buffersize); + } +} +static void fakename_close(FAKENAME_STREAMFILE *streamfile) { + streamfile->inner_sf->close(streamfile->inner_sf); + free(streamfile); +} + +STREAMFILE *open_fakename_streamfile(STREAMFILE *streamfile, char * fakename, char* fakeext) { + FAKENAME_STREAMFILE *this_sf; + + if (!streamfile || (!fakename && !fakeext)) return NULL; + + this_sf = calloc(1,sizeof(FAKENAME_STREAMFILE)); + if (!this_sf) return NULL; + + /* set callbacks and internals */ + this_sf->sf.read = (void*)fakename_read; + this_sf->sf.get_size = (void*)fakename_get_size; + this_sf->sf.get_offset = (void*)fakename_get_offset; + this_sf->sf.get_name = (void*)fakename_get_name; + this_sf->sf.get_realname = (void*)fakename_get_realname; + this_sf->sf.open = (void*)fakename_open; + this_sf->sf.close = (void*)fakename_close; + this_sf->sf.stream_index = streamfile->stream_index; + + this_sf->inner_sf = streamfile; + + /* copy passed name or retain current, and swap extension if expected */ + if (fakename) { + strcpy(this_sf->fakename,fakename); + } else { + streamfile->get_name(streamfile, this_sf->fakename, PATH_LIMIT); + } + if (fakeext) { + char * ext = strrchr(this_sf->fakename,'.'); + if (ext != NULL) + ext[1] = '\0'; /* truncate past dot */ + strcat(this_sf->fakename, fakeext); + } + + return &this_sf->sf; +} + +/* **************************************************** */ + + +typedef struct { + STREAMFILE sf; + + STREAMFILE **inner_sfs; + size_t inner_sfs_size; + size_t *sizes; + off_t size; + off_t offset; +} MULTIFILE_STREAMFILE; + +static size_t multifile_read(MULTIFILE_STREAMFILE *streamfile, uint8_t * dest, off_t offset, size_t length) { + int i, segment = 0; + off_t segment_offset = 0; + size_t done = 0; + + if (offset > streamfile->size) { + streamfile->offset = streamfile->size; + return 0; + } + + /* map external offset to multifile offset */ + for (i = 0; i < streamfile->inner_sfs_size; i++) { + size_t segment_size = streamfile->sizes[i]; + /* check if offset falls in this segment */ + if (offset >= segment_offset && offset < segment_offset + segment_size) { + segment = i; + segment_offset = offset - segment_offset; + break; + } + + segment_offset += segment_size; + } + + /* reads can span multiple segments */ + while(done < length) { + if (segment >= streamfile->inner_sfs_size) /* over last segment, not fully done */ + break; + /* reads over segment size are ok, will return smaller value and continue next segment */ + done += streamfile->inner_sfs[segment]->read(streamfile->inner_sfs[segment], dest+done, segment_offset, length - done); + segment++; + segment_offset = 0; + } + + streamfile->offset = offset + done; + return done; +} +static size_t multifile_get_size(MULTIFILE_STREAMFILE *streamfile) { + return streamfile->size; +} +static size_t multifile_get_offset(MULTIFILE_STREAMFILE * streamfile) { + return streamfile->offset; +} +static void multifile_get_name(MULTIFILE_STREAMFILE *streamfile, char *buffer, size_t length) { + streamfile->inner_sfs[0]->get_name(streamfile->inner_sfs[0], buffer, length); +} +static void multifile_get_realname(MULTIFILE_STREAMFILE *streamfile, char *buffer, size_t length) { + multifile_get_name(streamfile, buffer, length); +} +static STREAMFILE *multifile_open(MULTIFILE_STREAMFILE *streamfile, const char * const filename, size_t buffersize) { + char original_filename[PATH_LIMIT]; + STREAMFILE *new_sf = NULL; + STREAMFILE **new_inner_sfs = NULL; + int i; + + streamfile->inner_sfs[0]->get_name(streamfile->inner_sfs[0], original_filename, PATH_LIMIT); + + /* detect re-opening the file */ + if (strcmp(filename, original_filename) == 0) { /* same multifile */ + new_inner_sfs = calloc(streamfile->inner_sfs_size, sizeof(STREAMFILE*)); + if (!new_inner_sfs) goto fail; + + for (i = 0; i < streamfile->inner_sfs_size; i++) { + streamfile->inner_sfs[i]->get_name(streamfile->inner_sfs[i], original_filename, PATH_LIMIT); + new_inner_sfs[i] = streamfile->inner_sfs[i]->open(streamfile->inner_sfs[i], original_filename, buffersize); + if (!new_inner_sfs[i]) goto fail; + } + + new_sf = open_multifile_streamfile(new_inner_sfs, streamfile->inner_sfs_size); + if (!new_sf) goto fail; + + return new_sf; + } + else { + return streamfile->inner_sfs[0]->open(streamfile->inner_sfs[0], filename, buffersize); /* regular file */ + } + +fail: + if (new_inner_sfs) { + for (i = 0; i < streamfile->inner_sfs_size; i++) + close_streamfile(new_inner_sfs[i]); + } + free(new_inner_sfs); + return NULL; +} +static void multifile_close(MULTIFILE_STREAMFILE *streamfile) { + int i; + for (i = 0; i < streamfile->inner_sfs_size; i++) { + for (i = 0; i < streamfile->inner_sfs_size; i++) + close_streamfile(streamfile->inner_sfs[i]); + } + free(streamfile->inner_sfs); + free(streamfile->sizes); + free(streamfile); +} + +STREAMFILE *open_multifile_streamfile(STREAMFILE **streamfiles, size_t streamfiles_size) { + MULTIFILE_STREAMFILE *this_sf; + int i; + + if (!streamfiles || !streamfiles_size) return NULL; + for (i = 0; i < streamfiles_size; i++) { + if (!streamfiles[i]) return NULL; + } + + this_sf = calloc(1,sizeof(MULTIFILE_STREAMFILE)); + if (!this_sf) goto fail; + + /* set callbacks and internals */ + this_sf->sf.read = (void*)multifile_read; + this_sf->sf.get_size = (void*)multifile_get_size; + this_sf->sf.get_offset = (void*)multifile_get_offset; + this_sf->sf.get_name = (void*)multifile_get_name; + this_sf->sf.get_realname = (void*)multifile_get_realname; + this_sf->sf.open = (void*)multifile_open; + this_sf->sf.close = (void*)multifile_close; + this_sf->sf.stream_index = streamfiles[0]->stream_index; + + this_sf->inner_sfs_size = streamfiles_size; + this_sf->inner_sfs = calloc(streamfiles_size, sizeof(STREAMFILE*)); + if (!this_sf->inner_sfs) goto fail; + this_sf->sizes = calloc(streamfiles_size, sizeof(size_t)); + if (!this_sf->sizes) goto fail; + + for (i = 0; i < this_sf->inner_sfs_size; i++) { + this_sf->inner_sfs[i] = streamfiles[i]; + this_sf->sizes[i] = streamfiles[i]->get_size(streamfiles[i]); + this_sf->size += this_sf->sizes[i]; + } + + return &this_sf->sf; + +fail: + if (this_sf) { + free(this_sf->inner_sfs); + free(this_sf->sizes); + } + free(this_sf); + return NULL; +} /* **************************************************** */ @@ -393,17 +793,15 @@ STREAMFILE * open_stream_name(STREAMFILE *streamFile, const char * name) { return streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); } -/** - * open file containing decryption keys and copy to buffer - * tries combinations of keynames based on the original filename - * - * returns true if found and copied - */ -int read_key_file(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile) { +/* Opens a file containing decryption keys and copies to buffer. + * Tries combinations of keynames based on the original filename. + * returns size of key if found and copied */ +size_t read_key_file(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile) { char keyname[PATH_LIMIT]; char filename[PATH_LIMIT]; const char *path, *ext; STREAMFILE * streamFileKey = NULL; + size_t keysize; streamFile->get_name(streamFile,filename,sizeof(filename)); @@ -456,17 +854,17 @@ int read_key_file(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile) { } found: - if (get_streamfile_size(streamFileKey) != bufsize) goto fail; + keysize = get_streamfile_size(streamFileKey); + if (keysize > bufsize) goto fail; - if (read_streamfile(buf, 0, bufsize, streamFileKey)!=bufsize) goto fail; + if (read_streamfile(buf, 0, keysize, streamFileKey) != keysize) + goto fail; close_streamfile(streamFileKey); - - return 1; + return keysize; fail: - if (streamFileKey) close_streamfile(streamFileKey); - + close_streamfile(streamFileKey); return 0; } diff --git a/Frameworks/vgmstream/vgmstream/src/streamfile.h b/Frameworks/vgmstream/vgmstream/src/streamfile.h index 0398b5ae0..4d0aba655 100644 --- a/Frameworks/vgmstream/vgmstream/src/streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/streamfile.h @@ -55,13 +55,8 @@ typedef struct _STREAMFILE { struct _STREAMFILE * (*open)(struct _STREAMFILE *,const char * const filename,size_t buffersize); void (*close)(struct _STREAMFILE *); -#ifdef PROFILE_STREAMFILE - size_t (*get_bytes_read)(struct _STREAMFILE *); - int (*get_error_count)(struct _STREAMFILE *); -#endif - - /* Substream selection for files with multiple streams. Manually used in metas if supported. + /* Substream selection for files with subsongs. Manually used in metas if supported. * Not ideal here, but it's the simplest way to pass to all init_vgmstream_x functions. */ int stream_index; /* 0=default/auto (first), 1=first, N=Nth */ @@ -73,9 +68,34 @@ STREAMFILE * open_stdio_streamfile(const char * filename); /* create a STREAMFILE from pre-opened file path */ STREAMFILE * open_stdio_streamfile_by_file(FILE * file, const char * filename); +/* A STREAMFILE that doesn't close the underlying stream. + * Calls to open won't wrap the new SF (assumes it needs to be closed). + * Can be used in metas to test custom IO without closing the external SF. */ +STREAMFILE *open_wrap_streamfile(STREAMFILE *streamfile); + +/* A STREAMFILE that clamps IO to a section of a larger stream. + * Can be used with subfiles inside a bigger file, so it looks standard to a meta. */ +STREAMFILE *open_clamp_streamfile(STREAMFILE *streamfile, off_t start, size_t size); + +/* A STREAMFILE with custom IO, that clamps IO to a section of a larger stream. + * Can be used with subfiles inside a bigger file, so it looks standard to a meta. */ +STREAMFILE *open_io_streamfile(STREAMFILE *streamfile, void* data, size_t data_size, void* read_callback);//void* size_callback, void* seek_callback); + +/* A STREAMFILE that reports a fake name, but still re-opens itself properly. + * Can be used to trick a meta's extension check (to call from another, with a modified SF). + * When fakename isn't supplied it's read from the streamfile, and the extension swapped with fakeext. + * If the fakename is an existing file, open won't work on it as it'll reopen the fake-named streamfile. */ +STREAMFILE *open_fakename_streamfile(STREAMFILE *streamfile, char * fakename, char * fakeext); + +/* A streamfile formed from multiple streamfiles, their data joined during reads. + * Can be used when data is segmented in multiple separate files. + * The first streamfile is used to get names, stream index and so on. */ +STREAMFILE *open_multifile_streamfile(STREAMFILE **streamfiles, size_t streamfiles_size); + /* close a file, destroy the STREAMFILE object */ static inline void close_streamfile(STREAMFILE * streamfile) { + if (streamfile==NULL) return; streamfile->close(streamfile); } @@ -89,23 +109,6 @@ static inline size_t get_streamfile_size(STREAMFILE * streamfile) { return streamfile->get_size(streamfile); } -#ifdef PROFILE_STREAMFILE -/* return how many bytes we read into buffers */ -static inline size_t get_streamfile_bytes_read(STREAMFILE * streamfile) { - if (streamfile->get_bytes_read) - return streamfile->get_bytes_read(streamfile); - else - return 0; -} - -/* return how many times we encountered a read error */ -static inline int get_streamfile_error_count(STREAMFILE * streamfile) { - if (streamfile->get_error_count) - return streamfile->get_error_count(streamfile); - else - return 0; -} -#endif /* Sometimes you just need an int, and we're doing the buffering. * Note, however, that if these fail to read they'll return -1, @@ -163,7 +166,7 @@ STREAMFILE * open_stream_name(STREAMFILE *streamFile, const char * ext); int read_string(char * buf, size_t bufsize, off_t offset, STREAMFILE *streamFile); -int read_key_file(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile); +size_t read_key_file(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile); int read_pos_file(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile); int check_extensions(STREAMFILE *streamFile, const char * cmp_exts); diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.c b/Frameworks/vgmstream/vgmstream/src/vgmstream.c index 68f8290e3..3677f6fc3 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.c +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.c @@ -373,6 +373,13 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_ngc_vid1, init_vgmstream_flx, init_vgmstream_mogg, + init_vgmstream_kma9, + init_vgmstream_fsb_encrypted, + init_vgmstream_xwc, + init_vgmstream_atsl3, + init_vgmstream_sps_n1, + init_vgmstream_atx, + init_vgmstream_sqex_sead, init_vgmstream_txth, /* should go at the end (lower priority) */ #ifdef VGM_USE_FFMPEG @@ -973,6 +980,7 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre case layout_blocked_awc: case layout_blocked_vgs: case layout_blocked_vawx: + case layout_blocked_xvag_subsong: render_vgmstream_blocked(buffer,sample_count,vgmstream); break; case layout_acm: @@ -2192,6 +2200,11 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { } concatn(length,desc,temp); + snprintf(temp,TEMPSIZE, + "\nbitrate: %d kbps", + get_vgmstream_average_bitrate(vgmstream) / 1000); + concatn(length,desc,temp); + /* only interesting if more than one */ if (vgmstream->num_streams > 1) { snprintf(temp,TEMPSIZE, @@ -2440,9 +2453,11 @@ static STREAMFILE * get_vgmstream_average_bitrate_channel_streamfile(VGMSTREAM * return vgmstream->ch[channel].streamfile; } +static int get_vgmstream_average_bitrate_from_size(size_t size, int sample_rate, int length_samples) { + return (int)((int64_t)size * 8 * sample_rate / length_samples); +} static int get_vgmstream_average_bitrate_from_streamfile(STREAMFILE * streamfile, int sample_rate, int length_samples) { - // todo: not correct in subsongs or formats which only use part of the data - return (int)((int64_t)get_streamfile_size(streamfile) * 8 * sample_rate / length_samples); + return get_vgmstream_average_bitrate_from_size(get_streamfile_size(streamfile), sample_rate, length_samples); } /* Return the average bitrate in bps of all unique files contained within this stream. */ @@ -2460,6 +2475,11 @@ int get_vgmstream_average_bitrate(VGMSTREAM * vgmstream) { if (!sample_rate || !channels || !length_samples) return 0; + /* subsongs need to report this to properly calculate */ + if (vgmstream->stream_size) { + return get_vgmstream_average_bitrate_from_size(vgmstream->stream_size, sample_rate, length_samples); + } + if (channels >= 1) { streamFile = get_vgmstream_average_bitrate_channel_streamfile(vgmstream, 0); if (streamFile) { diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.h b/Frameworks/vgmstream/vgmstream/src/vgmstream.h index 3cbe097b3..c8131ad90 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.h +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.h @@ -258,6 +258,7 @@ typedef enum { layout_blocked_awc, /* Rockstar AWC */ layout_blocked_vgs, /* Guitar Hero II (PS2) */ layout_blocked_vawx, /* No More Heroes 6ch (PS3) */ + layout_blocked_xvag_subsong, /* XVAG subsongs [God of War III (PS4)] */ /* otherwise odd */ layout_acm, /* libacm layout */ @@ -652,16 +653,20 @@ typedef enum { meta_NGC_VID1, /* Neversoft .ogg (Gun GC) */ meta_PC_FLX, /* Ultima IX PC */ meta_MOGG, /* Harmonix Music Systems MOGG Vorbis */ - -#ifdef VGM_USE_VORBIS meta_OGG_VORBIS, /* Ogg Vorbis */ meta_OGG_SLI, /* Ogg Vorbis file w/ companion .sli for looping */ meta_OGG_SLI2, /* Ogg Vorbis file w/ different styled .sli for looping */ meta_OGG_SFL, /* Ogg Vorbis file w/ .sfl (RIFF SFPL) for looping */ - meta_OGG_UM3, /* Ogg Vorbis with first 0x800 bytes XOR 0xFF */ - meta_OGG_KOVS, /* Ogg Vorbis with exta header and 0x100 bytes XOR */ - meta_OGG_PSYCH, /* Ogg Vorbis with all bytes -0x23*/ -#endif + meta_OGG_UM3, /* Ogg Vorbis with optional encryption */ + meta_OGG_KOVS, /* Ogg Vorbis with encryption (Koei Tecmo Games) */ + meta_OGG_PSYCHIC, /* Ogg Vorbis with encryption */ + meta_OGG_SNGW, /* Ogg Vorbis with optional encryption (Capcom PC games) */ + meta_OGG_ISD, /* Ogg Vorbis with encryption (Azure Striker Gunvolt PC) */ + meta_KMA9, /* Koei Tecmo [Nobunaga no Yabou - Souzou (Vita)] */ + meta_XWC, /* Starbreeze games */ + meta_SQEX_SAB, /* Square-Enix newest middleware (sound) */ + meta_SQEX_MAB, /* Square-Enix newest middleware (music) */ + #ifdef VGM_USE_MP4V2 meta_MP4, /* AAC (iOS) */ #endif @@ -739,6 +744,7 @@ typedef struct { int num_streams; /* for multi-stream formats (0=not set/one stream, 1=one stream) */ int stream_index; /* selected stream (also 1-based) */ char stream_name[STREAM_NAME_SIZE]; /* name of the current stream (info), if the file stores it and it's filled */ + size_t stream_size; /* info to properly calculate bitrate */ /* looping */ int loop_flag; /* is this stream looped? */ @@ -801,15 +807,16 @@ typedef struct { /* Ogg with Vorbis */ typedef struct { STREAMFILE *streamfile; - ogg_int64_t offset; - ogg_int64_t size; - ogg_int64_t other_header_bytes; + ogg_int64_t start; /* file offset where the Ogg starts */ + ogg_int64_t offset; /* virtual offset, from 0 to size */ + ogg_int64_t size; /* virtual size of the Ogg */ - /* XOR setup (SCD) */ - int decryption_enabled; - void (*decryption_callback)(void *ptr, size_t size, size_t nmemb, void *datasource, int bytes_read); + /* decryption setup */ + void (*decryption_callback)(void *ptr, size_t size, size_t nmemb, void *datasource); uint8_t scd_xor; off_t scd_xor_length; + uint32_t sngw_xor; + } ogg_vorbis_streamfile; typedef struct { @@ -831,9 +838,9 @@ typedef enum { } vorbis_custom_t; /* config for Wwise Vorbis (3 types for flexibility though not all combinations exist) */ -typedef enum { HEADER_TRIAD, FULL_SETUP, INLINE_CODEBOOKS, EXTERNAL_CODEBOOKS, AOTUV603_CODEBOOKS } wwise_setup_t; /* Vorbis setup style */ -typedef enum { TYPE_8, TYPE_6, TYPE_2 } wwise_header_t; /* size of packet headers */ -typedef enum { STANDARD, MODIFIED } wwise_packet_t; /* type of Vorbis packets */ +typedef enum { WWV_HEADER_TRIAD, WWV_FULL_SETUP, WWV_INLINE_CODEBOOKS, WWV_EXTERNAL_CODEBOOKS, WWV_AOTUV603_CODEBOOKS } wwise_setup_t; +typedef enum { WWV_TYPE_8, WWV_TYPE_6, WWV_TYPE_2 } wwise_header_t; +typedef enum { WWV_STANDARD, WWV_MODIFIED } wwise_packet_t; typedef struct { /* to reconstruct init packets */ @@ -1001,6 +1008,7 @@ typedef struct { typedef enum { ATRAC9_DEFAULT = 0, /* ATRAC9 standard */ ATRAC9_XVAG, /* Sony XVAG: interleaved subsongs, Vita multichannel interleaves 2ch xN superframes */ + ATRAC9_KMA9, /* Koei Tecmo KMA9: interleaved subsongs */ //ATRAC9_FSB, /* FMOD FSB: Vita multichannel interleaves 2ch xN superframes */ //ATRAC9_EATRAX, /* EA EATrax: buffered ATRAC9 in SPS blocks (superframes can be split between blocks) */ } atrac9_custom_t; @@ -1100,7 +1108,6 @@ typedef enum { FFMPEG_STANDARD, /* default FFmpeg */ FFMPEG_SWITCH_OPUS, /* Opus without Ogg layer */ FFMPEG_EA_XMA, /* XMA with padding removed and custom streams in SNS blocks */ - FFMPEG_BGW_ATRAC3, /* Encrypted raw ATRAC3 */ //FFMPEG_EA_SCHL, /* Normal header+data (ex. ATRAC3) in SCxx blocks */ //FFMPEG_SFH, /* ATRAC3plus header+data in SFH blocks */ //FFMPEG_AWC_XMA, /* XMA data in AWC blocks, 1 streams per channel */ @@ -1118,7 +1125,6 @@ typedef struct { /* internal sequences, when needed */ int sequence; int samples_done; - uint8_t * key; } ffmpeg_custom_config; typedef struct {