Updated VGMStream to r1050-1001-gebdfed99.

CQTexperiment
Christopher Snowhill 2018-01-27 20:32:27 -08:00
parent ce1d938b3a
commit 56ed9c3429
56 changed files with 3097 additions and 1342 deletions

View File

@ -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 = "<group>"; };
833A7A2D1ED11961003EC53E /* xau.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xau.c; sourceTree = "<group>"; };
8349A8DC1FE6251E00E26435 /* vorbis_custom_utils_vid1.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vorbis_custom_utils_vid1.c; sourceTree = "<group>"; };
8349A8DD1FE6251F00E26435 /* ffmpeg_decoder_utils_bgw_atrac3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ffmpeg_decoder_utils_bgw_atrac3.c; sourceTree = "<group>"; };
8349A8DE1FE6251F00E26435 /* ea_mt_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ea_mt_decoder.c; sourceTree = "<group>"; };
8349A8E21FE6253800E26435 /* blocked_dec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_dec.c; sourceTree = "<group>"; };
8349A8E31FE6253800E26435 /* blocked_ea_1snh.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_ea_1snh.c; sourceTree = "<group>"; };
@ -775,7 +782,6 @@
836F6E7E18BDC2180095E648 /* ngc_ymf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ngc_ymf.c; sourceTree = "<group>"; };
836F6E7F18BDC2180095E648 /* ngca.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ngca.c; sourceTree = "<group>"; };
836F6E8118BDC2180095E648 /* nwa.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nwa.c; sourceTree = "<group>"; };
836F6E8218BDC2180095E648 /* ogg_vorbis_file.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ogg_vorbis_file.c; sourceTree = "<group>"; };
836F6E8318BDC2180095E648 /* otm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = otm.c; sourceTree = "<group>"; };
836F6E8418BDC2180095E648 /* p3d.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = p3d.c; sourceTree = "<group>"; };
836F6E8518BDC2180095E648 /* pc_adp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pc_adp.c; sourceTree = "<group>"; };
@ -957,6 +963,16 @@
839E21DE1F2EDAF000EE54D7 /* mpeg_decoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mpeg_decoder.h; sourceTree = "<group>"; };
839E21DF1F2EDAF000EE54D7 /* vorbis_custom_utils_sk.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vorbis_custom_utils_sk.c; sourceTree = "<group>"; };
839E21EA1F2EDB0500EE54D7 /* sk_aud.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sk_aud.c; sourceTree = "<group>"; };
83A21F7A201D895B000F04B9 /* blocked_xvag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_xvag.c; sourceTree = "<group>"; };
83A21F7C201D897F000F04B9 /* atx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = atx.c; sourceTree = "<group>"; };
83A21F7D201D8980000F04B9 /* xwc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xwc.c; sourceTree = "<group>"; };
83A21F7E201D8980000F04B9 /* fsb_keys.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fsb_keys.h; sourceTree = "<group>"; };
83A21F7F201D8980000F04B9 /* ogg_vorbis.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ogg_vorbis.c; sourceTree = "<group>"; };
83A21F80201D8980000F04B9 /* atsl3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = atsl3.c; sourceTree = "<group>"; };
83A21F81201D8981000F04B9 /* fsb_encrypted.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fsb_encrypted.c; sourceTree = "<group>"; };
83A21F82201D8981000F04B9 /* sps_n1.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sps_n1.c; sourceTree = "<group>"; };
83A21F83201D8981000F04B9 /* kma9.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = kma9.c; sourceTree = "<group>"; };
83A21F84201D8981000F04B9 /* sqex_sead.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sqex_sead.c; sourceTree = "<group>"; };
83A3F0711E3AD8B900D6A794 /* formats.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = formats.c; sourceTree = "<group>"; };
83A3F0731E3AD8B900D6A794 /* stack_alloc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = stack_alloc.h; sourceTree = "<group>"; };
83A5F75E198DF021009AF94C /* bfwav.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bfwav.c; sourceTree = "<group>"; };
@ -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 */,

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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;
}

View File

@ -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) */

View File

@ -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;
}

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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[] = {

View File

@ -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);

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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 */
}

View File

@ -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;

View File

@ -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,8 +37,6 @@ 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;
@ -43,7 +46,6 @@ VGMSTREAM * init_vgmstream_bgw(STREAMFILE *streamFile) {
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;
@ -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,7 +142,6 @@ 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;
@ -154,7 +151,6 @@ VGMSTREAM * init_vgmstream_spw(STREAMFILE *streamFile) {
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;
@ -184,6 +180,7 @@ VGMSTREAM * init_vgmstream_spw(STREAMFILE *streamFile) {
}
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;
}

View File

@ -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?

View File

@ -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) {

View File

@ -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) */
/* 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;
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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_ */

View File

@ -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;
}

View File

@ -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 {

View File

@ -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_*/

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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*/

View File

@ -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;
start_offset = offset + 0x28;
channel_count = read_8bit(offset + 0x09,streamFile); /* assumed */
/* 0x0a: packet size if CBR?, other values: no idea */
channel_count = read_8bit(offset + 0x09, streamFile);
/* 0x0a: packet size if CBR, 0 if VBR */
data_offset = offset + read_32bitLE(offset + 0x10, streamFile);
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);

View File

@ -0,0 +1,453 @@
#include "../vgmstream.h"
#ifdef VGM_USE_VORBIS
#include <stdio.h>
#include <string.h>
#include "meta.h"
#include <vorbis/vorbisfile.h>
#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

View File

@ -1,461 +0,0 @@
#include "../vgmstream.h"
#ifdef VGM_USE_VORBIS
#include <stdio.h>
#include <string.h>
#include "meta.h"
#include "../util.h"
#include <vorbis/vorbisfile.h>
#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;i<num_crypt;i++)
((uint8_t*)ptr)[i] ^= 0xff;
}
ov_streamfile->offset += 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;i<bytes_read;i++)
((uint8_t*)ptr)[i] += 0x23;
items_read = bytes_read / size;
ov_streamfile->offset += 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;i<comment->comments;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

View File

@ -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;

View File

@ -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 (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 */
}
}

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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);
/* 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;
/* 0x00(2): table1/4 (unknown) entries */
/* 0x02(2): table2 (unknown) entries */
/* 0x04(2): table3 (headers) entries */
/* 0x06(2): unknown, varies even for clone files */
headers_offset = read_32bit(tables_offset+0x0c,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 */
/** header table entries (each is an uint32_t offset to stream header) **/
meta_offset = read_32bit(headers_offset + (target_stream-1)*4,streamFile);
/* 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);
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);
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,74 +131,53 @@ 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 */
/* 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;
}
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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) {

View File

@ -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;
}

View File

@ -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:

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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) {

View File

@ -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 {