Updated VGMStream to r1667-70-g3f281cdf
parent
d522730fd3
commit
a93510dd8b
|
@ -233,6 +233,11 @@
|
|||
8351F32D2212B57000A606E4 /* 208.c in Sources */ = {isa = PBXBuildFile; fileRef = 8351F32A2212B57000A606E4 /* 208.c */; };
|
||||
8351F32E2212B57000A606E4 /* ubi_bao_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 8351F32B2212B57000A606E4 /* ubi_bao_streamfile.h */; };
|
||||
8351F32F2212B57000A606E4 /* dsf.c in Sources */ = {isa = PBXBuildFile; fileRef = 8351F32C2212B57000A606E4 /* dsf.c */; };
|
||||
835B9B8F2730BF2D00F87EE3 /* ast_mv.c in Sources */ = {isa = PBXBuildFile; fileRef = 835B9B8A2730BF2C00F87EE3 /* ast_mv.c */; };
|
||||
835B9B902730BF2D00F87EE3 /* ast_mmv.c in Sources */ = {isa = PBXBuildFile; fileRef = 835B9B8B2730BF2C00F87EE3 /* ast_mmv.c */; };
|
||||
835B9B912730BF2D00F87EE3 /* lopu_fb.c in Sources */ = {isa = PBXBuildFile; fileRef = 835B9B8C2730BF2C00F87EE3 /* lopu_fb.c */; };
|
||||
835B9B922730BF2D00F87EE3 /* hca_bf.h in Headers */ = {isa = PBXBuildFile; fileRef = 835B9B8D2730BF2D00F87EE3 /* hca_bf.h */; };
|
||||
835B9B932730BF2D00F87EE3 /* lpcm_shade.c in Sources */ = {isa = PBXBuildFile; fileRef = 835B9B8E2730BF2D00F87EE3 /* lpcm_shade.c */; };
|
||||
835C883622CC17BE001B4B3F /* bwav.c in Sources */ = {isa = PBXBuildFile; fileRef = 835C883122CC17BD001B4B3F /* bwav.c */; };
|
||||
835C883722CC17BE001B4B3F /* ogg_vorbis_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 835C883522CC17BE001B4B3F /* ogg_vorbis_streamfile.h */; };
|
||||
836C052B23F62F1A00FA07C7 /* libatrac9.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 835FC6C623F62AEF006960FA /* libatrac9.framework */; };
|
||||
|
@ -346,7 +351,6 @@
|
|||
836F6FCA18BDC2190095E648 /* ps2_adm.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E8E18BDC2180095E648 /* ps2_adm.c */; };
|
||||
836F6FCB18BDC2190095E648 /* ads.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E8F18BDC2180095E648 /* ads.c */; };
|
||||
836F6FCD18BDC2190095E648 /* ps2_ass.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E9118BDC2180095E648 /* ps2_ass.c */; };
|
||||
836F6FCE18BDC2190095E648 /* ps2_ast.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E9218BDC2180095E648 /* ps2_ast.c */; };
|
||||
836F6FD018BDC2190095E648 /* ps2_b1s.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E9418BDC2180095E648 /* ps2_b1s.c */; };
|
||||
836F6FD118BDC2190095E648 /* ps2_bg00.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E9518BDC2180095E648 /* ps2_bg00.c */; };
|
||||
836F6FD218BDC2190095E648 /* ps2_bmdx.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E9618BDC2180095E648 /* ps2_bmdx.c */; };
|
||||
|
@ -363,7 +367,6 @@
|
|||
836F6FE018BDC2190095E648 /* ps2_joe.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EA418BDC2180095E648 /* ps2_joe.c */; };
|
||||
836F6FE218BDC2190095E648 /* ps2_kces.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EA618BDC2180095E648 /* ps2_kces.c */; };
|
||||
836F6FE418BDC2190095E648 /* ps2_leg.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EA818BDC2180095E648 /* ps2_leg.c */; };
|
||||
836F6FE518BDC2190095E648 /* ps2_lpcm.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EA918BDC2180095E648 /* ps2_lpcm.c */; };
|
||||
836F6FE618BDC2190095E648 /* ps2_mcg.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EAA18BDC2180095E648 /* ps2_mcg.c */; };
|
||||
836F6FE818BDC2190095E648 /* ps2_mic.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EAC18BDC2180095E648 /* ps2_mic.c */; };
|
||||
836F6FE918BDC2190095E648 /* ps2_mihb.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EAD18BDC2180095E648 /* ps2_mihb.c */; };
|
||||
|
@ -519,7 +522,6 @@
|
|||
83997F5B22D9569E00633184 /* rad.c in Sources */ = {isa = PBXBuildFile; fileRef = 83997F5722D9569E00633184 /* rad.c */; };
|
||||
839B54521EEE1D9600048A2D /* ngc_ulw.c in Sources */ = {isa = PBXBuildFile; fileRef = 831BD11F1EEE1CF200198540 /* ngc_ulw.c */; };
|
||||
839C3D27270D49FF00E13653 /* lpcm_fb.c in Sources */ = {isa = PBXBuildFile; fileRef = 839C3D22270D49FF00E13653 /* lpcm_fb.c */; };
|
||||
839C3D28270D49FF00E13653 /* lopu.c in Sources */ = {isa = PBXBuildFile; fileRef = 839C3D26270D49FF00E13653 /* lopu.c */; };
|
||||
839E21E01F2EDAF100EE54D7 /* vorbis_custom_data_fsb.h in Headers */ = {isa = PBXBuildFile; fileRef = 839E21D61F2EDAF000EE54D7 /* vorbis_custom_data_fsb.h */; };
|
||||
839E21E11F2EDAF100EE54D7 /* vorbis_custom_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 839E21D71F2EDAF000EE54D7 /* vorbis_custom_decoder.c */; };
|
||||
839E21E21F2EDAF100EE54D7 /* vorbis_custom_utils_wwise.c in Sources */ = {isa = PBXBuildFile; fileRef = 839E21D81F2EDAF000EE54D7 /* vorbis_custom_utils_wwise.c */; };
|
||||
|
@ -1045,6 +1047,11 @@
|
|||
8351F32A2212B57000A606E4 /* 208.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = 208.c; sourceTree = "<group>"; };
|
||||
8351F32B2212B57000A606E4 /* ubi_bao_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ubi_bao_streamfile.h; sourceTree = "<group>"; };
|
||||
8351F32C2212B57000A606E4 /* dsf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dsf.c; sourceTree = "<group>"; };
|
||||
835B9B8A2730BF2C00F87EE3 /* ast_mv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ast_mv.c; sourceTree = "<group>"; };
|
||||
835B9B8B2730BF2C00F87EE3 /* ast_mmv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ast_mmv.c; sourceTree = "<group>"; };
|
||||
835B9B8C2730BF2C00F87EE3 /* lopu_fb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = lopu_fb.c; sourceTree = "<group>"; };
|
||||
835B9B8D2730BF2D00F87EE3 /* hca_bf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hca_bf.h; sourceTree = "<group>"; };
|
||||
835B9B8E2730BF2D00F87EE3 /* lpcm_shade.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = lpcm_shade.c; sourceTree = "<group>"; };
|
||||
835C883122CC17BD001B4B3F /* bwav.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bwav.c; sourceTree = "<group>"; };
|
||||
835C883522CC17BE001B4B3F /* ogg_vorbis_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ogg_vorbis_streamfile.h; sourceTree = "<group>"; };
|
||||
835FC6C123F62AEE006960FA /* libatrac9.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = libatrac9.xcodeproj; path = ../libatrac9/libatrac9.xcodeproj; sourceTree = "<group>"; };
|
||||
|
@ -1159,7 +1166,6 @@
|
|||
836F6E8E18BDC2180095E648 /* ps2_adm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_adm.c; sourceTree = "<group>"; };
|
||||
836F6E8F18BDC2180095E648 /* ads.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ads.c; sourceTree = "<group>"; };
|
||||
836F6E9118BDC2180095E648 /* ps2_ass.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_ass.c; sourceTree = "<group>"; };
|
||||
836F6E9218BDC2180095E648 /* ps2_ast.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_ast.c; sourceTree = "<group>"; };
|
||||
836F6E9418BDC2180095E648 /* ps2_b1s.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_b1s.c; sourceTree = "<group>"; };
|
||||
836F6E9518BDC2180095E648 /* ps2_bg00.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_bg00.c; sourceTree = "<group>"; };
|
||||
836F6E9618BDC2180095E648 /* ps2_bmdx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_bmdx.c; sourceTree = "<group>"; };
|
||||
|
@ -1176,7 +1182,6 @@
|
|||
836F6EA418BDC2180095E648 /* ps2_joe.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_joe.c; sourceTree = "<group>"; };
|
||||
836F6EA618BDC2180095E648 /* ps2_kces.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_kces.c; sourceTree = "<group>"; };
|
||||
836F6EA818BDC2180095E648 /* ps2_leg.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_leg.c; sourceTree = "<group>"; };
|
||||
836F6EA918BDC2180095E648 /* ps2_lpcm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_lpcm.c; sourceTree = "<group>"; };
|
||||
836F6EAA18BDC2180095E648 /* ps2_mcg.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_mcg.c; sourceTree = "<group>"; };
|
||||
836F6EAC18BDC2180095E648 /* ps2_mic.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_mic.c; sourceTree = "<group>"; };
|
||||
836F6EAD18BDC2180095E648 /* ps2_mihb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_mihb.c; sourceTree = "<group>"; };
|
||||
|
@ -1331,7 +1336,6 @@
|
|||
8399335E2591E8C0001855AF /* sbk.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sbk.c; sourceTree = "<group>"; };
|
||||
83997F5722D9569E00633184 /* rad.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rad.c; sourceTree = "<group>"; };
|
||||
839C3D22270D49FF00E13653 /* lpcm_fb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = lpcm_fb.c; sourceTree = "<group>"; };
|
||||
839C3D26270D49FF00E13653 /* lopu.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = lopu.c; sourceTree = "<group>"; };
|
||||
839E21D61F2EDAF000EE54D7 /* vorbis_custom_data_fsb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vorbis_custom_data_fsb.h; sourceTree = "<group>"; };
|
||||
839E21D71F2EDAF000EE54D7 /* vorbis_custom_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vorbis_custom_decoder.c; sourceTree = "<group>"; };
|
||||
839E21D81F2EDAF000EE54D7 /* vorbis_custom_utils_wwise.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vorbis_custom_utils_wwise.c; sourceTree = "<group>"; };
|
||||
|
@ -1827,6 +1831,8 @@
|
|||
834FE0C7215C79E7000A5D3D /* apc.c */,
|
||||
836F6E3418BDC2180095E648 /* apple_caff.c */,
|
||||
8315958820FEC83F007002F0 /* asf.c */,
|
||||
835B9B8B2730BF2C00F87EE3 /* ast_mmv.c */,
|
||||
835B9B8A2730BF2C00F87EE3 /* ast_mv.c */,
|
||||
836F6E3518BDC2180095E648 /* ast.c */,
|
||||
8306B0D520984590000302D4 /* atsl.c */,
|
||||
83A21F7C201D897F000F04B9 /* atx.c */,
|
||||
|
@ -1919,6 +1925,7 @@
|
|||
83709DFF1ECBC1A4005C03D3 /* gtd.c */,
|
||||
8342469020C4D22F00926E48 /* h4m.c */,
|
||||
836F6E5218BDC2180095E648 /* halpst.c */,
|
||||
835B9B8D2730BF2D00F87EE3 /* hca_bf.h */,
|
||||
832BF81A21E0514A006F50F1 /* hca_keys_awb.h */,
|
||||
83AA5D211F6E2F9C0020821C /* hca_keys.h */,
|
||||
8323894F1D2246C300482226 /* hca.c */,
|
||||
|
@ -1949,8 +1956,9 @@
|
|||
83D20074248DDB760048BD24 /* ktsr.c */,
|
||||
830EBE122004656E0023AA10 /* ktss.c */,
|
||||
8373342423F60CDB00DE14DC /* kwb.c */,
|
||||
839C3D26270D49FF00E13653 /* lopu.c */,
|
||||
835B9B8C2730BF2C00F87EE3 /* lopu_fb.c */,
|
||||
839C3D22270D49FF00E13653 /* lpcm_fb.c */,
|
||||
835B9B8E2730BF2D00F87EE3 /* lpcm_shade.c */,
|
||||
8373341F23F60CDB00DE14DC /* lrmd_streamfile.h */,
|
||||
8373342223F60CDB00DE14DC /* lrmd.c */,
|
||||
836F6E5A18BDC2180095E648 /* lsf.c */,
|
||||
|
@ -2041,7 +2049,6 @@
|
|||
836F6E8D18BDC2180095E648 /* ps2_2pfs.c */,
|
||||
836F6E8E18BDC2180095E648 /* ps2_adm.c */,
|
||||
836F6E9118BDC2180095E648 /* ps2_ass.c */,
|
||||
836F6E9218BDC2180095E648 /* ps2_ast.c */,
|
||||
836F6E9418BDC2180095E648 /* ps2_b1s.c */,
|
||||
836F6E9518BDC2180095E648 /* ps2_bg00.c */,
|
||||
836F6E9618BDC2180095E648 /* ps2_bmdx.c */,
|
||||
|
@ -2059,7 +2066,6 @@
|
|||
836F6EA418BDC2180095E648 /* ps2_joe.c */,
|
||||
836F6EA618BDC2180095E648 /* ps2_kces.c */,
|
||||
836F6EA818BDC2180095E648 /* ps2_leg.c */,
|
||||
836F6EA918BDC2180095E648 /* ps2_lpcm.c */,
|
||||
836F6EAA18BDC2180095E648 /* ps2_mcg.c */,
|
||||
836F6EAC18BDC2180095E648 /* ps2_mic.c */,
|
||||
836F6EAD18BDC2180095E648 /* ps2_mihb.c */,
|
||||
|
@ -2354,6 +2360,7 @@
|
|||
839E21E01F2EDAF100EE54D7 /* vorbis_custom_data_fsb.h in Headers */,
|
||||
83031EDB243C510500C3F3E0 /* xnb_lz4mg.h in Headers */,
|
||||
836F705418BDC2190095E648 /* streamfile.h in Headers */,
|
||||
835B9B922730BF2D00F87EE3 /* hca_bf.h in Headers */,
|
||||
83A3F0761E3AD8B900D6A794 /* stack_alloc.h in Headers */,
|
||||
8373342723F60CDC00DE14DC /* lrmd_streamfile.h in Headers */,
|
||||
83C7281122BC893D00678B4A /* 9tav_streamfile.h in Headers */,
|
||||
|
@ -2763,7 +2770,6 @@
|
|||
83AA7F812519C042004C5298 /* silence.c in Sources */,
|
||||
834FE0B3215C798C000A5D3D /* acm_decoder_util.c in Sources */,
|
||||
837CEA7A23487E2500E62A4A /* ffmpeg_decoder_utils.c in Sources */,
|
||||
839C3D28270D49FF00E13653 /* lopu.c in Sources */,
|
||||
837CEAF823487F2C00E62A4A /* xmv_valve.c in Sources */,
|
||||
836F6F6718BDC2190095E648 /* acm.c in Sources */,
|
||||
834FE0FD215C79ED000A5D3D /* vpk.c in Sources */,
|
||||
|
@ -2774,6 +2780,7 @@
|
|||
8306B0A720984552000302D4 /* blocked_xvas.c in Sources */,
|
||||
8349A9101FE6258200E26435 /* ea_eaac.c in Sources */,
|
||||
836F700F18BDC2190095E648 /* ps2_xa30.c in Sources */,
|
||||
835B9B912730BF2D00F87EE3 /* lopu_fb.c in Sources */,
|
||||
8346D97B25BF838C00D1A8B0 /* ktac.c in Sources */,
|
||||
8349A8ED1FE6253900E26435 /* blocked_ea_sns.c in Sources */,
|
||||
836F6F6F18BDC2190095E648 /* akb.c in Sources */,
|
||||
|
@ -2892,7 +2899,6 @@
|
|||
836F6F8818BDC2190095E648 /* fsb.c in Sources */,
|
||||
83F2CCE525A5B41600F46FA8 /* acx.c in Sources */,
|
||||
83FC176D23AC58D100E1025F /* xma_ue3.c in Sources */,
|
||||
836F6FE518BDC2190095E648 /* ps2_lpcm.c in Sources */,
|
||||
8375737321F9507D00F01AF5 /* oki_decoder.c in Sources */,
|
||||
836F6FB318BDC2190095E648 /* ngc_lps.c in Sources */,
|
||||
836F6FC018BDC2190095E648 /* p3d.c in Sources */,
|
||||
|
@ -2946,7 +2952,6 @@
|
|||
836F6FFA18BDC2190095E648 /* spm.c in Sources */,
|
||||
83C7281B22BC893D00678B4A /* strm_abylight.c in Sources */,
|
||||
834D3A6E19F47C98001C54F6 /* g1l.c in Sources */,
|
||||
836F6FCE18BDC2190095E648 /* ps2_ast.c in Sources */,
|
||||
832BF82A21E0514B006F50F1 /* vs_square.c in Sources */,
|
||||
834FE0B4215C798C000A5D3D /* ffmpeg_decoder_custom_opus.c in Sources */,
|
||||
8349A9091FE6258200E26435 /* pc_ast.c in Sources */,
|
||||
|
@ -2986,6 +2991,7 @@
|
|||
8349A9161FE6258200E26435 /* flx.c in Sources */,
|
||||
832BF82921E0514B006F50F1 /* msf_banpresto.c in Sources */,
|
||||
834FE0BE215C79A9000A5D3D /* blocked_xa_aiff.c in Sources */,
|
||||
835B9B902730BF2D00F87EE3 /* ast_mmv.c in Sources */,
|
||||
836F702A18BDC2190095E648 /* sd9.c in Sources */,
|
||||
836F6FB418BDC2190095E648 /* ngc_nst_dsp.c in Sources */,
|
||||
836F6FDB18BDC2190095E648 /* ps2_hsf.c in Sources */,
|
||||
|
@ -3050,6 +3056,7 @@
|
|||
836F6FEE18BDC2190095E648 /* ps2_p2bt.c in Sources */,
|
||||
836F702618BDC2190095E648 /* s14_sss.c in Sources */,
|
||||
834FE102215C79ED000A5D3D /* rfrm.c in Sources */,
|
||||
835B9B8F2730BF2D00F87EE3 /* ast_mv.c in Sources */,
|
||||
83AA5D1D1F6E2F800020821C /* blocked_vgs.c in Sources */,
|
||||
836F702E18BDC2190095E648 /* sli.c in Sources */,
|
||||
83AA7F822519C042004C5298 /* ktsc.c in Sources */,
|
||||
|
@ -3099,6 +3106,7 @@
|
|||
836F6F4118BDC2190095E648 /* blocked.c in Sources */,
|
||||
836F6F3B18BDC2190095E648 /* ws_decoder.c in Sources */,
|
||||
839933612591E8C1001855AF /* sbk.c in Sources */,
|
||||
835B9B932730BF2D00F87EE3 /* lpcm_shade.c in Sources */,
|
||||
838BDB6E1D3B043C0022CA6F /* ffmpeg.c in Sources */,
|
||||
832BF82B21E0514B006F50F1 /* xopus.c in Sources */,
|
||||
836F6F3418BDC2190095E648 /* nwa_decoder.c in Sources */,
|
||||
|
|
|
@ -0,0 +1,140 @@
|
|||
/* Copyright (c) 2003-2008 Timothy B. Terriberry
|
||||
Copyright (c) 2008 Xiph.Org Foundation */
|
||||
/*
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
- Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
- Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
- Neither the name of the Xiph.org Foundation nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*Some common macros for potential platform-specific optimization.*/
|
||||
#include <math.h>
|
||||
#include <limits.h>
|
||||
#if !defined(_ecintrin_H)
|
||||
# define _ecintrin_H (1)
|
||||
|
||||
/*Some specific platforms may have optimized intrinsic or inline assembly
|
||||
versions of these functions which can substantially improve performance.
|
||||
We define macros for them to allow easy incorporation of these non-ANSI
|
||||
features.*/
|
||||
|
||||
/*Note that we do not provide a macro for abs(), because it is provided as a
|
||||
library function, which we assume is translated into an intrinsic to avoid
|
||||
the function call overhead and then implemented in the smartest way for the
|
||||
target platform.
|
||||
With modern gcc (4.x), this is true: it uses cmov instructions if the
|
||||
architecture supports it and branchless bit-twiddling if it does not (the
|
||||
speed difference between the two approaches is not measurable).
|
||||
Interestingly, the bit-twiddling method was patented in 2000 (US 6,073,150)
|
||||
by Sun Microsystems, despite prior art dating back to at least 1996:
|
||||
http://web.archive.org/web/19961201174141/www.x86.org/ftp/articles/pentopt/PENTOPT.TXT
|
||||
On gcc 3.x, however, our assumption is not true, as abs() is translated to a
|
||||
conditional jump, which is horrible on deeply piplined architectures (e.g.,
|
||||
all consumer architectures for the past decade or more) when the sign cannot
|
||||
be reliably predicted.*/
|
||||
|
||||
/*Modern gcc (4.x) can compile the naive versions of min and max with cmov if
|
||||
given an appropriate architecture, but the branchless bit-twiddling versions
|
||||
are just as fast, and do not require any special target architecture.
|
||||
Earlier gcc versions (3.x) compiled both code to the same assembly
|
||||
instructions, because of the way they represented ((_b)>(_a)) internally.*/
|
||||
#define EC_MAXI(_a,_b) ((_a)-((_a)-(_b)&-((_b)>(_a))))
|
||||
#define EC_MINI(_a,_b) ((_a)+((_b)-(_a)&-((_b)<(_a))))
|
||||
/*This has a chance of compiling branchless, and is just as fast as the
|
||||
bit-twiddling method, which is slightly less portable, since it relies on a
|
||||
sign-extended rightshift, which is not guaranteed by ANSI (but present on
|
||||
every relevant platform).*/
|
||||
#define EC_SIGNI(_a) (((_a)>0)-((_a)<0))
|
||||
/*Slightly more portable than relying on a sign-extended right-shift (which is
|
||||
not guaranteed by ANSI), and just as fast, since gcc (3.x and 4.x both)
|
||||
compile it into the right-shift anyway.*/
|
||||
#define EC_SIGNMASK(_a) (-((_a)<0))
|
||||
/*Clamps an integer into the given range.
|
||||
If _a>_c, then the lower bound _a is respected over the upper bound _c (this
|
||||
behavior is required to meet our documented API behavior).
|
||||
_a: The lower bound.
|
||||
_b: The value to clamp.
|
||||
_c: The upper boud.*/
|
||||
#define EC_CLAMPI(_a,_b,_c) (EC_MAXI(_a,EC_MINI(_b,_c)))
|
||||
|
||||
|
||||
/*Count leading zeros.
|
||||
This macro should only be used for implementing ec_ilog(), if it is defined.
|
||||
All other code should use EC_ILOG() instead.*/
|
||||
#if defined(_MSC_VER)
|
||||
# include <intrin.h>
|
||||
static __inline int ec_bsr(unsigned long _x){
|
||||
unsigned long ret;
|
||||
_BitScanReverse(&ret,_x);
|
||||
return (int)ret;
|
||||
}
|
||||
# define EC_CLZ0 (1)
|
||||
# define EC_CLZ(_x) (-ec_bsr(_x))
|
||||
#elif defined(ENABLE_TI_DSPLIB)
|
||||
# include "dsplib.h"
|
||||
# define EC_CLZ0 (31)
|
||||
# define EC_CLZ(_x) (_lnorm(x))
|
||||
#elif defined(__GNUC_PREREQ)
|
||||
# if __GNUC_PREREQ(3,4)
|
||||
# if INT_MAX>=2147483647
|
||||
# define EC_CLZ0 ((int)sizeof(unsigned)*CHAR_BIT)
|
||||
# define EC_CLZ(_x) (__builtin_clz(_x))
|
||||
# elif LONG_MAX>=2147483647L
|
||||
# define EC_CLZ0 ((int)sizeof(unsigned long)*CHAR_BIT)
|
||||
# define EC_CLZ(_x) (__builtin_clzl(_x))
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(EC_CLZ)
|
||||
/*Note that __builtin_clz is not defined when _x==0, according to the gcc
|
||||
documentation (and that of the BSR instruction that implements it on x86).
|
||||
The majority of the time we can never pass it zero.
|
||||
When we need to, it can be special cased.*/
|
||||
# define EC_ILOG(_x) (EC_CLZ0-EC_CLZ(_x))
|
||||
#else
|
||||
static int ec_ilog2(celt_uint32 _v){
|
||||
int ret;
|
||||
int m;
|
||||
ret=!!_v;
|
||||
m=!!(_v&0xFFFF0000)<<4;
|
||||
_v>>=m;
|
||||
ret|=m;
|
||||
m=!!(_v&0xFF00)<<3;
|
||||
_v>>=m;
|
||||
ret|=m;
|
||||
m=!!(_v&0xF0)<<2;
|
||||
_v>>=m;
|
||||
ret|=m;
|
||||
m=!!(_v&0xC)<<1;
|
||||
_v>>=m;
|
||||
ret|=m;
|
||||
ret+=!!(_v&0x2);
|
||||
return ret;
|
||||
}
|
||||
# define EC_ILOG(_x) (ec_ilog2(_x))
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -115,7 +115,7 @@ void decode_hevag(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing
|
|||
|
||||
|
||||
/* xa_decoder */
|
||||
void decode_xa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_xa8);
|
||||
void decode_xa(VGMSTREAM* v, sample_t* outbuf, int32_t samples_to_do);
|
||||
size_t xa_bytes_to_samples(size_t bytes, int channels, int is_blocked, int is_form2, int bps);
|
||||
|
||||
|
||||
|
@ -172,6 +172,7 @@ int msadpcm_check_coefs(STREAMFILE* sf, off_t offset);
|
|||
|
||||
/* yamaha_decoder */
|
||||
void decode_aica(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo);
|
||||
void decode_cp_ym(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo);
|
||||
void decode_aska(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, size_t frame_size);
|
||||
void decode_nxap(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
size_t yamaha_bytes_to_samples(size_t bytes, int channels);
|
||||
|
@ -324,9 +325,21 @@ void decode_hca(hca_codec_data* data, sample_t* outbuf, int32_t samples_to_do);
|
|||
void reset_hca(hca_codec_data* data);
|
||||
void loop_hca(hca_codec_data* data, int32_t num_sample);
|
||||
void free_hca(hca_codec_data* data);
|
||||
int test_hca_key(hca_codec_data* data, unsigned long long keycode);
|
||||
void hca_set_encryption_key(hca_codec_data* data, uint64_t keycode);
|
||||
clHCA_stInfo* hca_get_info(hca_codec_data* data);
|
||||
|
||||
typedef struct {
|
||||
/* config + output */
|
||||
uint64_t key;
|
||||
uint16_t subkey;
|
||||
uint64_t best_key;
|
||||
int best_score;
|
||||
/* internals */
|
||||
uint32_t start_offset;
|
||||
} hca_keytest_t;
|
||||
|
||||
void test_hca_key(hca_codec_data* data, hca_keytest_t* hk);
|
||||
void hca_set_encryption_key(hca_codec_data* data, uint64_t keycode);
|
||||
|
||||
STREAMFILE* hca_get_streamfile(hca_codec_data* data);
|
||||
|
||||
|
||||
|
@ -465,7 +478,6 @@ void decode_mpeg(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do,
|
|||
void reset_mpeg(mpeg_codec_data* data);
|
||||
void seek_mpeg(VGMSTREAM* vgmstream, int32_t num_sample);
|
||||
void free_mpeg(mpeg_codec_data* data);
|
||||
void flush_mpeg(mpeg_codec_data* data);
|
||||
|
||||
int mpeg_get_sample_rate(mpeg_codec_data* data);
|
||||
long mpeg_bytes_to_samples(long bytes, const mpeg_codec_data* data);
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
#include "coding.h"
|
||||
#include <g719/g719.h>
|
||||
|
||||
#ifdef VGM_USE_G719
|
||||
#include <g719/g719.h>
|
||||
|
||||
#define G719_MAX_CODES ((1280/8)) /* in int16, so max frame size is (value/8)*2 (0xF0=common, 0x140=decoder max 2560b, rare) */
|
||||
|
||||
struct g719_codec_data {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
#include "coding.h"
|
||||
#include "g7221_decoder_lib.h"
|
||||
|
||||
#ifdef VGM_USE_G7221
|
||||
#include "g7221_decoder_lib.h"
|
||||
|
||||
#define G7221_MAX_FRAME_SIZE 0x78 /* 960/8 */
|
||||
#define G7221_MAX_FRAME_SAMPLES 640 /* 32000/50 */
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
|
||||
struct hca_codec_data {
|
||||
STREAMFILE* streamfile;
|
||||
STREAMFILE* sf;
|
||||
clHCA_stInfo info;
|
||||
|
||||
signed short* sample_buffer;
|
||||
|
@ -58,8 +58,8 @@ hca_codec_data* init_hca(STREAMFILE* sf) {
|
|||
if (!data->sample_buffer) goto fail;
|
||||
|
||||
/* load streamfile for reads */
|
||||
data->streamfile = reopen_streamfile(sf, 0);
|
||||
if (!data->streamfile) goto fail;
|
||||
data->sf = reopen_streamfile(sf, 0);
|
||||
if (!data->sf) goto fail;
|
||||
|
||||
/* set initial values */
|
||||
reset_hca(data);
|
||||
|
@ -115,7 +115,7 @@ void decode_hca(hca_codec_data* data, sample_t* outbuf, int32_t samples_to_do) {
|
|||
}
|
||||
|
||||
/* read frame */
|
||||
bytes = read_streamfile(data->data_buffer, offset, blockSize, data->streamfile);
|
||||
bytes = read_streamfile(data->data_buffer, offset, blockSize, data->sf);
|
||||
if (bytes != blockSize) {
|
||||
VGM_LOG("HCA: read %x vs expected %x bytes at %x\n", bytes, blockSize, (uint32_t)offset);
|
||||
break;
|
||||
|
@ -171,7 +171,7 @@ void loop_hca(hca_codec_data* data, int32_t num_sample) {
|
|||
void free_hca(hca_codec_data* data) {
|
||||
if (!data) return;
|
||||
|
||||
close_streamfile(data->streamfile);
|
||||
close_streamfile(data->sf);
|
||||
clHCA_done(data->handle);
|
||||
free(data->handle);
|
||||
free(data->data_buffer);
|
||||
|
@ -179,6 +179,14 @@ void free_hca(hca_codec_data* data) {
|
|||
free(data);
|
||||
}
|
||||
|
||||
clHCA_stInfo* hca_get_info(hca_codec_data* data) {
|
||||
return &data->info;
|
||||
}
|
||||
|
||||
STREAMFILE* hca_get_streamfile(hca_codec_data* data) {
|
||||
if (!data) return NULL;
|
||||
return data->sf;
|
||||
}
|
||||
|
||||
/* ************************************************************************* */
|
||||
|
||||
|
@ -192,19 +200,23 @@ void free_hca(hca_codec_data* data) {
|
|||
/* ignores beginning frames (~10 is not uncommon, Dragalia Lost vocal layers have lots) */
|
||||
#define HCA_KEY_MAX_SKIP_BLANKS 1200
|
||||
/* 5~15 should be enough, but almost silent or badly mastered files may need tweaks
|
||||
* (ex. newer Tales of the Rays files clip a lot and need +6 as some keys give almost-ok results) */
|
||||
#define HCA_KEY_MIN_TEST_FRAMES 7
|
||||
#define HCA_KEY_MAX_TEST_FRAMES 12
|
||||
* (ex. newer Tales of the Rays files clip a lot) */
|
||||
#define HCA_KEY_MIN_TEST_FRAMES 3 //7
|
||||
#define HCA_KEY_MAX_TEST_FRAMES 7 //12
|
||||
/* score of 10~30 isn't uncommon in a single frame, too many frames over that is unlikely */
|
||||
#define HCA_KEY_MAX_FRAME_SCORE 150
|
||||
#define HCA_KEY_MAX_TOTAL_SCORE (HCA_KEY_MAX_TEST_FRAMES * 50*HCA_KEY_SCORE_SCALE)
|
||||
|
||||
/* Test a number of frames if key decrypts correctly.
|
||||
* Returns score: <0: error/wrong, 0: unknown/silent file, >0: good (the closest to 1 the better). */
|
||||
int test_hca_key(hca_codec_data* data, unsigned long long keycode) {
|
||||
static int test_hca_score(hca_codec_data* data, hca_keytest_t* hk, unsigned long long keycode) {
|
||||
size_t test_frames = 0, current_frame = 0, blank_frames = 0;
|
||||
int total_score = 0, found_regular_frame = 0;
|
||||
const unsigned int blockSize = data->info.blockSize;
|
||||
int total_score = 0;
|
||||
const unsigned int block_size = data->info.blockSize;
|
||||
uint32_t offset = hk->start_offset;
|
||||
|
||||
if (!offset)
|
||||
offset = data->info.headerSize;
|
||||
|
||||
/* Due to the potentially large number of keys this must be tuned for speed.
|
||||
* Buffered IO seems fast enough (not very different reading a large block once vs frame by frame).
|
||||
|
@ -216,19 +228,25 @@ int test_hca_key(hca_codec_data* data, unsigned long long keycode) {
|
|||
/* A final score of 0 (=silent) is only possible for short files with all blank frames */
|
||||
|
||||
while (test_frames < HCA_KEY_MAX_TEST_FRAMES && current_frame < data->info.blockCount) {
|
||||
off_t offset = data->info.headerSize + current_frame * blockSize;
|
||||
int score;
|
||||
size_t bytes;
|
||||
|
||||
/* read and test frame */
|
||||
bytes = read_streamfile(data->data_buffer, offset, blockSize, data->streamfile);
|
||||
if (bytes != blockSize) {
|
||||
bytes = read_streamfile(data->data_buffer, offset, block_size, data->sf);
|
||||
if (bytes != block_size) {
|
||||
/* normally this shouldn't happen, but pre-fetch ACB stop with frames in half, so just keep score */
|
||||
//total_score = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
score = clHCA_TestBlock(data->handle, (void*)(data->data_buffer), blockSize);
|
||||
score = clHCA_TestBlock(data->handle, data->data_buffer, block_size);
|
||||
|
||||
/* get first non-blank frame */
|
||||
if (!hk->start_offset && score != 0) {
|
||||
hk->start_offset = offset;
|
||||
}
|
||||
offset += bytes;
|
||||
|
||||
if (score < 0 || score > HCA_KEY_MAX_FRAME_SCORE) {
|
||||
total_score = -1;
|
||||
break;
|
||||
|
@ -236,13 +254,13 @@ int test_hca_key(hca_codec_data* data, unsigned long long keycode) {
|
|||
|
||||
current_frame++;
|
||||
|
||||
/* ignore silent frames at the beginning, up to a point */
|
||||
if (score == 0 && blank_frames < HCA_KEY_MAX_SKIP_BLANKS && !found_regular_frame) {
|
||||
/* ignore silent frames at the beginning, up to a point (keep skipping as
|
||||
* in rare cases there are one non-blank frame then a bunch, that skew results) */
|
||||
if (score == 0 && blank_frames < HCA_KEY_MAX_SKIP_BLANKS /*&& !hk->start_offset*/) {
|
||||
blank_frames++;
|
||||
continue;
|
||||
}
|
||||
|
||||
found_regular_frame = 1;
|
||||
test_frames++;
|
||||
|
||||
/* scale values to make scores of perfect frames more detectable */
|
||||
|
@ -254,7 +272,6 @@ int test_hca_key(hca_codec_data* data, unsigned long long keycode) {
|
|||
|
||||
total_score += score;
|
||||
|
||||
|
||||
/* don't bother checking more frames, other keys will get better scores */
|
||||
if (total_score > HCA_KEY_MAX_TOTAL_SCORE)
|
||||
break;
|
||||
|
@ -270,15 +287,37 @@ int test_hca_key(hca_codec_data* data, unsigned long long keycode) {
|
|||
return total_score;
|
||||
}
|
||||
|
||||
void test_hca_key(hca_codec_data* data, hca_keytest_t* hk) {
|
||||
int score;
|
||||
uint64_t key = hk->key;
|
||||
uint16_t subkey = hk->subkey;
|
||||
|
||||
//;VGM_LOG("HCA: test key=%08x%08x, subkey=%04x\n",
|
||||
// (uint32_t)((key >> 32) & 0xFFFFFFFF), (uint32_t)(key & 0xFFFFFFFF), subkey);
|
||||
|
||||
if (subkey) {
|
||||
key = key * ( ((uint64_t)subkey << 16u) | ((uint16_t)~subkey + 2u) );
|
||||
}
|
||||
|
||||
score = test_hca_score(data, hk, (unsigned long long)key);
|
||||
|
||||
//;VGM_LOG("HCA: test key=%08x%08x, subkey=%04x, score=%i\n",
|
||||
// (uint32_t)((key >> 32) & 0xFFFFFFFF), (uint32_t)(key & 0xFFFFFFFF), subkey, score);
|
||||
|
||||
/* wrong key */
|
||||
if (score < 0)
|
||||
return;
|
||||
|
||||
//;VGM_LOG("HCA: ok key=%08x%08x, subkey=%04x, score=%i\n",
|
||||
// (uint32_t)((key >> 32) & 0xFFFFFFFF), (uint32_t)(key & 0xFFFFFFFF), subkey, score);
|
||||
|
||||
/* update if something better is found */
|
||||
if (hk->best_score <= 0 || (score < hk->best_score && score > 0)) {
|
||||
hk->best_score = score;
|
||||
hk->best_key = key;
|
||||
}
|
||||
}
|
||||
|
||||
void hca_set_encryption_key(hca_codec_data* data, uint64_t keycode) {
|
||||
clHCA_SetKey(data->handle, (unsigned long long)keycode);
|
||||
}
|
||||
|
||||
clHCA_stInfo* hca_get_info(hca_codec_data* data) {
|
||||
return &data->info;
|
||||
}
|
||||
|
||||
STREAMFILE* hca_get_streamfile(hca_codec_data* data) {
|
||||
if (!data) return NULL;
|
||||
return data->streamfile;
|
||||
}
|
||||
|
|
|
@ -57,9 +57,9 @@
|
|||
#define HCA_MAX_FRAME_SIZE 0xFFFF /* lib max */
|
||||
|
||||
#define HCA_MASK 0x7F7F7F7F /* chunk obfuscation when the HCA is encrypted with key */
|
||||
#define HCA_SUBFRAMES_PER_FRAME 8
|
||||
#define HCA_SUBFRAMES 8
|
||||
#define HCA_SAMPLES_PER_SUBFRAME 128 /* also spectrum points/etc */
|
||||
#define HCA_SAMPLES_PER_FRAME (HCA_SUBFRAMES_PER_FRAME*HCA_SAMPLES_PER_SUBFRAME)
|
||||
#define HCA_SAMPLES_PER_FRAME (HCA_SUBFRAMES*HCA_SAMPLES_PER_SUBFRAME)
|
||||
#define HCA_MDCT_BITS 7 /* (1<<7) = 128 */
|
||||
|
||||
#define HCA_MIN_CHANNELS 1
|
||||
|
@ -88,7 +88,7 @@ typedef struct stChannel {
|
|||
unsigned int coded_count; /* encoded scales/resolutions/coefs */
|
||||
|
||||
/* subframe state */
|
||||
unsigned char intensity[HCA_SUBFRAMES_PER_FRAME]; /* intensity indexes for joins stereo (value max: 15 / 4b) */
|
||||
unsigned char intensity[HCA_SUBFRAMES]; /* intensity indexes for joins stereo (value max: 15 / 4b) */
|
||||
unsigned char scalefactors[HCA_SAMPLES_PER_SUBFRAME]; /* scale indexes (value max: 64 / 6b)*/
|
||||
unsigned char resolution[HCA_SAMPLES_PER_SUBFRAME]; /* resolution indexes (value max: 15 / 4b) */
|
||||
unsigned char noises[HCA_SAMPLES_PER_SUBFRAME]; /* indexes to coefs that need noise fill + coefs that don't (value max: 128 / 8b) */
|
||||
|
@ -96,13 +96,14 @@ typedef struct stChannel {
|
|||
unsigned int valid_count; /* resolutions with valid values saved in 'noises' */
|
||||
|
||||
float gain[HCA_SAMPLES_PER_SUBFRAME]; /* gain to apply to quantized spectral data */
|
||||
float spectra[HCA_SAMPLES_PER_SUBFRAME]; /* resulting dequantized data */
|
||||
float spectra[HCA_SUBFRAMES][HCA_SAMPLES_PER_SUBFRAME]; /* resulting dequantized data */
|
||||
|
||||
float temp[HCA_SAMPLES_PER_SUBFRAME]; /* temp for DCT-IV */
|
||||
float dct[HCA_SAMPLES_PER_SUBFRAME]; /* result of DCT-IV */
|
||||
float imdct_previous[HCA_SAMPLES_PER_SUBFRAME]; /* IMDCT */
|
||||
|
||||
/* frame state */
|
||||
float wave[HCA_SUBFRAMES_PER_FRAME][HCA_SAMPLES_PER_SUBFRAME]; /* resulting samples */
|
||||
float wave[HCA_SUBFRAMES][HCA_SAMPLES_PER_SUBFRAME]; /* resulting samples */
|
||||
} stChannel;
|
||||
|
||||
typedef struct clHCA {
|
||||
|
@ -333,7 +334,7 @@ void clHCA_ReadSamples16(clHCA* hca, signed short *samples) {
|
|||
unsigned int i, j, k;
|
||||
|
||||
/* PCM output is generally unused, but lib functions seem to use SIMD for f32 to s32 + round to zero */
|
||||
for (i = 0; i < HCA_SUBFRAMES_PER_FRAME; i++) {
|
||||
for (i = 0; i < HCA_SUBFRAMES; i++) {
|
||||
for (j = 0; j < HCA_SAMPLES_PER_SUBFRAME; j++) {
|
||||
for (k = 0; k < hca->channels; k++) {
|
||||
f = hca->channel[k].wave[i][j];
|
||||
|
@ -989,8 +990,12 @@ void clHCA_SetKey(clHCA* hca, unsigned long long keycode) {
|
|||
}
|
||||
}
|
||||
|
||||
static int clHCA_DecodeBlock_unpack(clHCA* hca, void *data, unsigned int size);
|
||||
static void clHCA_DecodeBlock_transform(clHCA* hca);
|
||||
|
||||
|
||||
int clHCA_TestBlock(clHCA* hca, void *data, unsigned int size) {
|
||||
const int frame_samples = HCA_SUBFRAMES_PER_FRAME * HCA_SAMPLES_PER_SUBFRAME;
|
||||
const int frame_samples = HCA_SUBFRAMES * HCA_SAMPLES_PER_SUBFRAME;
|
||||
const float scale = 32768.0f;
|
||||
unsigned int i, ch, sf, s;
|
||||
int status;
|
||||
|
@ -1014,7 +1019,7 @@ int clHCA_TestBlock(clHCA* hca, void *data, unsigned int size) {
|
|||
}
|
||||
|
||||
/* return if decode fails (happens often with wrong keys due to bad bitstream values) */
|
||||
status = clHCA_DecodeBlock(hca, data, size);
|
||||
status = clHCA_DecodeBlock_unpack(hca, data, size);
|
||||
if (status < 0)
|
||||
return -1;
|
||||
|
||||
|
@ -1042,8 +1047,9 @@ int clHCA_TestBlock(clHCA* hca, void *data, unsigned int size) {
|
|||
}
|
||||
|
||||
/* check decode results as (rarely) bad keys may still get here */
|
||||
clHCA_DecodeBlock_transform(hca);
|
||||
for (ch = 0; ch < hca->channels; ch++) {
|
||||
for (sf = 0; sf < HCA_SUBFRAMES_PER_FRAME; sf++) {
|
||||
for (sf = 0; sf < HCA_SUBFRAMES; sf++) {
|
||||
for (s = 0; s < HCA_SAMPLES_PER_SUBFRAME; s++) {
|
||||
float fsample = hca->channel[ch].wave[sf][s];
|
||||
|
||||
|
@ -1095,15 +1101,15 @@ void clHCA_DecodeReset(clHCA * hca) {
|
|||
stChannel* ch = &hca->channel[i];
|
||||
|
||||
/* most values get overwritten during decode */
|
||||
//memset(ch->intensity, 0, sizeof(ch->intensity[0]) * HCA_SUBFRAMES_PER_FRAME);
|
||||
//memset(ch->intensity, 0, sizeof(ch->intensity[0]) * HCA_SUBFRAMES);
|
||||
//memset(ch->scalefactors, 0, sizeof(ch->scalefactors[0]) * HCA_SAMPLES_PER_SUBFRAME);
|
||||
//memset(ch->resolution, 0, sizeof(ch->resolution[0]) * HCA_SAMPLES_PER_SUBFRAME);
|
||||
//memset(ch->gain, 0, sizeof(ch->gain[0]) * HCA_SAMPLES_PER_SUBFRAME);
|
||||
//memset(ch->spectra, 0, sizeof(ch->spectra[0]) * HCA_SAMPLES_PER_SUBFRAME);
|
||||
//memset(ch->spectra, 0, sizeof(ch->spectra[0]) * HCA_SUBFRAMES * HCA_SAMPLES_PER_SUBFRAME);
|
||||
//memset(ch->temp, 0, sizeof(ch->temp[0]) * HCA_SAMPLES_PER_SUBFRAME);
|
||||
//memset(ch->dct, 0, sizeof(ch->dct[0]) * HCA_SAMPLES_PER_SUBFRAME);
|
||||
memset(ch->imdct_previous, 0, sizeof(ch->imdct_previous[0]) * HCA_SAMPLES_PER_SUBFRAME);
|
||||
//memset(ch->wave, 0, sizeof(ch->wave[0][0]) * HCA_SUBFRAMES_PER_FRAME * HCA_SUBFRAMES_PER_FRAME);
|
||||
//memset(ch->wave, 0, sizeof(ch->wave[0][0]) * HCA_SUBFRAMES * HCA_SUBFRAMES);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1119,23 +1125,21 @@ static void calculate_resolution(stChannel* ch, unsigned int packed_noise_level,
|
|||
|
||||
static void calculate_gain(stChannel* ch);
|
||||
|
||||
static void dequantize_coefficients(stChannel* ch, clData* br);
|
||||
static void dequantize_coefficients(stChannel* ch, clData* br, int subframe);
|
||||
|
||||
static void reconstruct_noise(stChannel* ch, unsigned int min_resolution, unsigned int ms_stereo, unsigned int* random_p);
|
||||
static void reconstruct_noise(stChannel* ch, unsigned int min_resolution, unsigned int ms_stereo, unsigned int* random_p, int subframe);
|
||||
|
||||
static void reconstruct_high_frequency(stChannel* ch, unsigned int hfr_group_count, unsigned int bands_per_hfr_group,
|
||||
unsigned int stereo_band_count, unsigned int base_band_count, unsigned int total_band_count, unsigned int version);
|
||||
unsigned int stereo_band_count, unsigned int base_band_count, unsigned int total_band_count, unsigned int version, int subframe);
|
||||
|
||||
static void apply_intensity_stereo(stChannel* ch_pair, int subframe, unsigned int base_band_count, unsigned int total_band_count);
|
||||
|
||||
static void apply_ms_stereo(stChannel* ch_pair, unsigned int ms_stereo, unsigned int base_band_count, unsigned int total_band_count);
|
||||
static void apply_ms_stereo(stChannel* ch_pair, unsigned int ms_stereo, unsigned int base_band_count, unsigned int total_band_count, int subframe);
|
||||
|
||||
static void imdct_transform(stChannel* ch, int subframe);
|
||||
|
||||
|
||||
/* takes HCA data and decodes all of a frame's samples */
|
||||
//hcadecoder_decode_block
|
||||
int clHCA_DecodeBlock(clHCA* hca, void *data, unsigned int size) {
|
||||
static int clHCA_DecodeBlock_unpack(clHCA* hca, void *data, unsigned int size) {
|
||||
clData br;
|
||||
unsigned short sync;
|
||||
unsigned int subframe, ch;
|
||||
|
@ -1180,19 +1184,29 @@ int clHCA_DecodeBlock(clHCA* hca, void *data, unsigned int size) {
|
|||
}
|
||||
|
||||
/* lib seems to use a state value to skip parts (unpacking/subframe N/etc) as needed */
|
||||
for (subframe = 0; subframe < HCA_SUBFRAMES_PER_FRAME; subframe++) {
|
||||
for (subframe = 0; subframe < HCA_SUBFRAMES; subframe++) {
|
||||
|
||||
/* unpack channel data and get dequantized spectra */
|
||||
for (ch = 0; ch < hca->channels; ch++){
|
||||
dequantize_coefficients(&hca->channel[ch], &br);
|
||||
dequantize_coefficients(&hca->channel[ch], &br, subframe);
|
||||
}
|
||||
|
||||
/* original code transforms subframe here, but we have it for later */
|
||||
}
|
||||
|
||||
return br.bit; /* numbers of read bits for validations */
|
||||
}
|
||||
|
||||
static void clHCA_DecodeBlock_transform(clHCA* hca) {
|
||||
unsigned int subframe, ch;
|
||||
|
||||
for (subframe = 0; subframe < HCA_SUBFRAMES; subframe++) {
|
||||
/* restore missing bands from spectra */
|
||||
for (ch = 0; ch < hca->channels; ch++) {
|
||||
reconstruct_noise(&hca->channel[ch], hca->min_resolution, hca->ms_stereo, &hca->random);
|
||||
reconstruct_noise(&hca->channel[ch], hca->min_resolution, hca->ms_stereo, &hca->random, subframe);
|
||||
|
||||
reconstruct_high_frequency(&hca->channel[ch], hca->hfr_group_count, hca->bands_per_hfr_group,
|
||||
hca->stereo_band_count, hca->base_band_count, hca->total_band_count, hca->version);
|
||||
hca->stereo_band_count, hca->base_band_count, hca->total_band_count, hca->version, subframe);
|
||||
}
|
||||
|
||||
/* restore missing joint stereo bands */
|
||||
|
@ -1200,7 +1214,7 @@ int clHCA_DecodeBlock(clHCA* hca, void *data, unsigned int size) {
|
|||
for (ch = 0; ch < hca->channels - 1; ch++) {
|
||||
apply_intensity_stereo(&hca->channel[ch], subframe, hca->base_band_count, hca->total_band_count);
|
||||
|
||||
apply_ms_stereo(&hca->channel[ch], hca->ms_stereo, hca->base_band_count, hca->total_band_count);
|
||||
apply_ms_stereo(&hca->channel[ch], hca->ms_stereo, hca->base_band_count, hca->total_band_count, subframe);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1209,9 +1223,27 @@ int clHCA_DecodeBlock(clHCA* hca, void *data, unsigned int size) {
|
|||
imdct_transform(&hca->channel[ch], subframe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return br.bit; /* numbers of read bits for validations */
|
||||
/* takes HCA data and decodes all of a frame's samples */
|
||||
//hcadecoder_decode_block
|
||||
int clHCA_DecodeBlock(clHCA* hca, void *data, unsigned int size) {
|
||||
int res;
|
||||
|
||||
/* Original HCA code doesn't separate unpack + transform (unpacks most data,
|
||||
* reads a subframe's spectra, transforms that subframe.
|
||||
*
|
||||
* Unpacking first takes a bit more memory (1 spectra per subframe) but test keys faster
|
||||
* (since unpack may fail with bad keys we can skip transform). For regular decoding, this
|
||||
* way somehow is slightly faster? (~3-5%, extra compiler optimizations with reduced scope?) */
|
||||
|
||||
res = clHCA_DecodeBlock_unpack(hca, data, size);
|
||||
if (res < 0)
|
||||
return res;
|
||||
clHCA_DecodeBlock_transform(hca);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
|
@ -1330,7 +1362,7 @@ static int unpack_intensity(stChannel* ch, clData* br, unsigned int hfr_group_co
|
|||
ch->intensity[0] = value;
|
||||
if (value < 15) {
|
||||
bitreader_skip(br, 4);
|
||||
for (i = 1; i < HCA_SUBFRAMES_PER_FRAME; i++) {
|
||||
for (i = 1; i < HCA_SUBFRAMES; i++) {
|
||||
ch->intensity[i] = bitreader_read(br, 4);
|
||||
}
|
||||
}
|
||||
|
@ -1352,7 +1384,7 @@ static int unpack_intensity(stChannel* ch, clData* br, unsigned int hfr_group_co
|
|||
ch->intensity[0] = value;
|
||||
if (delta_bits == 3) { /* 3+1 = 4b */
|
||||
/* fixed intensities */
|
||||
for (i = 1; i < HCA_SUBFRAMES_PER_FRAME; i++) {
|
||||
for (i = 1; i < HCA_SUBFRAMES; i++) {
|
||||
ch->intensity[i] = bitreader_read(br, 4);
|
||||
}
|
||||
}
|
||||
|
@ -1361,7 +1393,7 @@ static int unpack_intensity(stChannel* ch, clData* br, unsigned int hfr_group_co
|
|||
unsigned char bmax = (2 << delta_bits) - 1;
|
||||
unsigned char bits = delta_bits + 1;
|
||||
|
||||
for (i = 1; i < HCA_SUBFRAMES_PER_FRAME; i++) {
|
||||
for (i = 1; i < HCA_SUBFRAMES; i++) {
|
||||
unsigned char delta = bitreader_read(br, bits);
|
||||
if (delta == bmax) {
|
||||
value = bitreader_read(br, 4); /* encoded */
|
||||
|
@ -1378,7 +1410,7 @@ static int unpack_intensity(stChannel* ch, clData* br, unsigned int hfr_group_co
|
|||
}
|
||||
else {
|
||||
bitreader_skip(br, 4);
|
||||
for (i = 0; i < HCA_SUBFRAMES_PER_FRAME; i++) {
|
||||
for (i = 0; i < HCA_SUBFRAMES; i++) {
|
||||
ch->intensity[i] = 7;
|
||||
}
|
||||
}
|
||||
|
@ -1498,7 +1530,7 @@ static const float hcatbdecoder_read_val_table[128] = {
|
|||
};
|
||||
|
||||
/* read spectral coefficients in the bitstream */
|
||||
static void dequantize_coefficients(stChannel* ch, clData* br) {
|
||||
static void dequantize_coefficients(stChannel* ch, clData* br, int subframe) {
|
||||
int i;
|
||||
unsigned int cc_count = ch->coded_count;
|
||||
|
||||
|
@ -1524,11 +1556,11 @@ static void dequantize_coefficients(stChannel* ch, clData* br) {
|
|||
}
|
||||
|
||||
/* dequantize coef with gain */
|
||||
ch->spectra[i] = ch->gain[i] * qc;
|
||||
ch->spectra[subframe][i] = ch->gain[i] * qc;
|
||||
}
|
||||
|
||||
/* clean rest of spectra */
|
||||
memset(&ch->spectra[cc_count], 0, sizeof(ch->spectra[0]) * (HCA_SAMPLES_PER_SUBFRAME - cc_count));
|
||||
memset(&ch->spectra[subframe][cc_count], 0, sizeof(ch->spectra[subframe][0]) * (HCA_SAMPLES_PER_SUBFRAME - cc_count));
|
||||
}
|
||||
|
||||
|
||||
|
@ -1560,7 +1592,7 @@ static const float* hcadecoder_scale_conversion_table = (const float*)hcadecoder
|
|||
|
||||
/* recreate resolution 0 coefs (not encoded) with pseudo-random noise based on
|
||||
* other coefs/scales (probably similar to AAC's perceptual noise substitution) */
|
||||
static void reconstruct_noise(stChannel* ch, unsigned int min_resolution, unsigned int ms_stereo, unsigned int* random_p) {
|
||||
static void reconstruct_noise(stChannel* ch, unsigned int min_resolution, unsigned int ms_stereo, unsigned int* random_p, int subframe) {
|
||||
if (min_resolution > 0) /* added in v3.0 */
|
||||
return;
|
||||
if (ch->valid_count <= 0 || ch->noise_count <= 0)
|
||||
|
@ -1587,7 +1619,8 @@ static void reconstruct_noise(stChannel* ch, unsigned int min_resolution, unsign
|
|||
sf_valid = ch->scalefactors[valid_index];
|
||||
sc_index = (sf_noise - sf_valid + 62) & ~((sf_noise - sf_valid + 62) >> 31);
|
||||
|
||||
ch->spectra[noise_index] = hcadecoder_scale_conversion_table[sc_index] * ch->spectra[valid_index];
|
||||
ch->spectra[subframe][noise_index] =
|
||||
hcadecoder_scale_conversion_table[sc_index] * ch->spectra[subframe][valid_index];
|
||||
}
|
||||
|
||||
*random_p = random; /* lib saves this in the bitreader, maybe for simplified passing around */
|
||||
|
@ -1596,7 +1629,7 @@ static void reconstruct_noise(stChannel* ch, unsigned int min_resolution, unsign
|
|||
|
||||
/* recreate missing coefs in high bands based on lower bands (probably similar to AAC's spectral band replication) */
|
||||
static void reconstruct_high_frequency(stChannel* ch, unsigned int hfr_group_count, unsigned int bands_per_hfr_group,
|
||||
unsigned int stereo_band_count, unsigned int base_band_count, unsigned int total_band_count, unsigned int version) {
|
||||
unsigned int stereo_band_count, unsigned int base_band_count, unsigned int total_band_count, unsigned int version, int subframe) {
|
||||
if (bands_per_hfr_group == 0) /* added in v2.0, skipped in v2.0 files with 0 bands too */
|
||||
return;
|
||||
if (ch->type == STEREO_SECONDARY)
|
||||
|
@ -1630,7 +1663,7 @@ static void reconstruct_high_frequency(stChannel* ch, unsigned int hfr_group_cou
|
|||
sc_index = hfr_scales[group] - ch->scalefactors[lowband] + 63;
|
||||
sc_index = sc_index & ~(sc_index >> 31); /* clamped in v3.0 lib (in theory 6b sf are 0..128) */
|
||||
|
||||
ch->spectra[highband] = hcadecoder_scale_conversion_table[sc_index] * ch->spectra[lowband];
|
||||
ch->spectra[subframe][highband] = hcadecoder_scale_conversion_table[sc_index] * ch->spectra[subframe][lowband];
|
||||
|
||||
highband += 1;
|
||||
lowband -= lowband_sub;
|
||||
|
@ -1638,7 +1671,7 @@ static void reconstruct_high_frequency(stChannel* ch, unsigned int hfr_group_cou
|
|||
}
|
||||
|
||||
/* last spectrum coefficient is 0 (normally highband = 128, but perhaps could 'break' before) */
|
||||
ch->spectra[highband - 1] = 0.0f;
|
||||
ch->spectra[subframe][highband - 1] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1661,8 +1694,8 @@ static void apply_intensity_stereo(stChannel* ch_pair, int subframe, unsigned in
|
|||
int band;
|
||||
float ratio_l = hcadecoder_intensity_ratio_table[ ch_pair[1].intensity[subframe] ];
|
||||
float ratio_r = 2.0f - ratio_l; /* correct, though other decoders substract 2.0 (it does use 'fsubr 2.0' and such) */
|
||||
float* sp_l = ch_pair[0].spectra;
|
||||
float* sp_r = ch_pair[1].spectra;
|
||||
float* sp_l = &ch_pair[0].spectra[subframe][0];
|
||||
float* sp_r = &ch_pair[1].spectra[subframe][0];
|
||||
|
||||
for (band = base_band_count; band < total_band_count; band++) {
|
||||
float coef_l = sp_l[band] * ratio_l;
|
||||
|
@ -1674,7 +1707,7 @@ static void apply_intensity_stereo(stChannel* ch_pair, int subframe, unsigned in
|
|||
}
|
||||
|
||||
/* restore L/R bands based on mid channel + side differences */
|
||||
static void apply_ms_stereo(stChannel* ch_pair, unsigned int ms_stereo, unsigned int base_band_count, unsigned int total_band_count) {
|
||||
static void apply_ms_stereo(stChannel* ch_pair, unsigned int ms_stereo, unsigned int base_band_count, unsigned int total_band_count, int subframe) {
|
||||
if (!ms_stereo) /* added in v3.0 */
|
||||
return;
|
||||
if (ch_pair[0].type != STEREO_PRIMARY)
|
||||
|
@ -1683,8 +1716,8 @@ static void apply_ms_stereo(stChannel* ch_pair, unsigned int ms_stereo, unsigned
|
|||
{
|
||||
int band;
|
||||
const float ratio = 0.70710676908493; /* 0x3F3504F3 */
|
||||
float* sp_l = ch_pair[0].spectra;
|
||||
float* sp_r = ch_pair[1].spectra;
|
||||
float* sp_l = &ch_pair[0].spectra[subframe][0];
|
||||
float* sp_r = &ch_pair[1].spectra[subframe][0];
|
||||
|
||||
for (band = base_band_count; band < total_band_count; band++) {
|
||||
float coef_l = (sp_l[band] + sp_r[band]) * ratio;
|
||||
|
@ -1867,8 +1900,8 @@ static void imdct_transform(stChannel* ch, int subframe) {
|
|||
{
|
||||
unsigned int count1 = 1;
|
||||
unsigned int count2 = half;
|
||||
float* temp1 = ch->spectra;
|
||||
float* temp2 = ch->temp;
|
||||
float* temp1 = &ch->spectra[subframe][0];
|
||||
float* temp2 = &ch->temp[0];
|
||||
|
||||
for (i = 0; i < mdct_bits; i++) {
|
||||
float* swap;
|
||||
|
@ -1897,8 +1930,8 @@ static void imdct_transform(stChannel* ch, int subframe) {
|
|||
{
|
||||
unsigned int count1 = half;
|
||||
unsigned int count2 = 1;
|
||||
float* temp1 = ch->temp;
|
||||
float* temp2 = ch->spectra;
|
||||
float* temp1 = &ch->temp[0];
|
||||
float* temp2 = &ch->spectra[subframe][0];
|
||||
|
||||
for (i = 0; i < mdct_bits; i++) {
|
||||
const float* sin_table = (const float*) sin_tables_hex[i];//todo cleanup
|
||||
|
@ -1934,15 +1967,15 @@ static void imdct_transform(stChannel* ch, int subframe) {
|
|||
/* copy dct */
|
||||
/* (with the above optimization spectra is already modified, so this is redundant) */
|
||||
for (i = 0; i < size; i++) {
|
||||
ch->dct[i] = ch->spectra[i];
|
||||
ch->dct[i] = ch->spectra[subframe][i];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* update output/imdct with overlapped window (lib fuses this with the above) */
|
||||
{
|
||||
const float* dct = ch->spectra; //ch->dct;
|
||||
const float* prev = ch->imdct_previous;
|
||||
const float* dct = &ch->spectra[subframe][0]; //ch->dct;
|
||||
const float* prev = &ch->imdct_previous[0];
|
||||
|
||||
for (i = 0; i < half; i++) {
|
||||
ch->wave[subframe][i] = hcaimdct_window_float[i] * dct[i + half] + prev[i];
|
||||
|
|
|
@ -87,8 +87,10 @@ int mpeg_custom_setup_init_default(STREAMFILE* sf, off_t start_offset, mpeg_code
|
|||
//case MPEG_P3D: data->skip_samples = info.frame_samples; break; /* matches Radical ADPCM (PC) output */
|
||||
|
||||
/* FSBs (with FMOD DLLs) don't seem to need it. Particularly a few games (all from Wayforward?)
|
||||
* contain audible garbage at the beginning, but it's actually there in-game too */
|
||||
//case MPEG_FSB: data->skip_samples = 0; break;
|
||||
* contain audible garbage at the beginning, but it's actually there in-game too.
|
||||
* Games doing full loops also must not have delay (reuses mpeg state on loop) */
|
||||
case MPEG_FSB:
|
||||
data->skip_samples = 0; break;
|
||||
|
||||
case MPEG_XVAG: /* set in header and needed for gapless looping */
|
||||
data->skip_samples = data->config.skip_samples; break;
|
||||
|
|
|
@ -481,6 +481,8 @@ decode_fail:
|
|||
/* UTILS */
|
||||
/*********/
|
||||
|
||||
static void flush_mpeg(mpeg_codec_data* data, int is_loop);
|
||||
|
||||
void free_mpeg(mpeg_codec_data* data) {
|
||||
if (!data)
|
||||
return;
|
||||
|
@ -513,7 +515,7 @@ void free_mpeg(mpeg_codec_data* data) {
|
|||
void reset_mpeg(mpeg_codec_data* data) {
|
||||
if (!data) return;
|
||||
|
||||
flush_mpeg(data);
|
||||
flush_mpeg(data, 0);
|
||||
|
||||
#if 0
|
||||
/* flush_mpeg properly resets mpg123 with mpg123_open_feed, and
|
||||
|
@ -550,7 +552,7 @@ void seek_mpeg(VGMSTREAM* vgmstream, int32_t num_sample) {
|
|||
else {
|
||||
int i;
|
||||
|
||||
flush_mpeg(data);
|
||||
flush_mpeg(data, 1);
|
||||
|
||||
/* restart from 0 and manually discard samples, since we don't really know the correct offset */
|
||||
for (i = 0; i < data->streams_size; i++) {
|
||||
|
@ -566,7 +568,7 @@ void seek_mpeg(VGMSTREAM* vgmstream, int32_t num_sample) {
|
|||
}
|
||||
|
||||
/* resets mpg123 decoder and its internals without seeking, useful when a new MPEG substream starts */
|
||||
void flush_mpeg(mpeg_codec_data* data) {
|
||||
static void flush_mpeg(mpeg_codec_data* data, int is_loop) {
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
|
@ -578,7 +580,10 @@ void flush_mpeg(mpeg_codec_data* data) {
|
|||
int i;
|
||||
/* re-start from 0 */
|
||||
for (i=0; i < data->streams_size; i++) {
|
||||
mpg123_open_feed(data->streams[i]->m);
|
||||
/* On loop FSB retains MDCT state so it mixes with next/loop frame (confirmed with recordings).
|
||||
* This only matters on full loops and if there is no encoder delay (since loops use discard right now) */
|
||||
if (is_loop && data->custom && !(data->type == MPEG_FSB))
|
||||
mpg123_open_feed(data->streams[i]->m);
|
||||
data->streams[i]->bytes_in_buffer = 0;
|
||||
data->streams[i]->buffer_full = 0;
|
||||
data->streams[i]->buffer_used = 0;
|
||||
|
|
|
@ -206,7 +206,7 @@ static uint32_t read_ubits(uint8_t bits, uint32_t offset, uint8_t* buf) {
|
|||
shift = offset - 8 * (offset / 8);
|
||||
mask = (1 << bits) - 1;
|
||||
pos = offset / 8;
|
||||
val = (buf[pos+0]) | (buf[pos+1]<<8) | (buf[pos+2]<<16) | (buf[pos+3]<<24);
|
||||
val = ((uint32_t)buf[pos+0]) | ((uint32_t)buf[pos+1]<<8) | ((uint32_t)buf[pos+2]<<16) | ((uint32_t)buf[pos+3]<<24);
|
||||
return (val >> shift) & mask;
|
||||
}
|
||||
|
||||
|
|
|
@ -1037,15 +1037,15 @@ static uint16_t crc16(const uint8_t* data, int length) {
|
|||
/* ************************************************************************* */
|
||||
|
||||
static uint32_t get_u32be(const uint8_t* mem) {
|
||||
return (mem[0] << 24) | (mem[1] << 16) | (mem[2] << 8) | mem[3];
|
||||
return ((uint32_t)mem[0] << 24) | ((uint32_t)mem[1] << 16) | ((uint32_t)mem[2] << 8) | (uint32_t)mem[3];
|
||||
}
|
||||
|
||||
static uint32_t get_u32le(const uint8_t* mem) {
|
||||
return (mem[3] << 24) | (mem[2] << 16) | (mem[1] << 8) | mem[0];
|
||||
return ((uint32_t)mem[3] << 24) | ((uint32_t)mem[2] << 16) | ((uint32_t)mem[1] << 8) | (uint32_t)mem[0];
|
||||
}
|
||||
|
||||
static uint16_t get_u16le(const uint8_t* mem) {
|
||||
return (mem[1] << 8) | mem[0];
|
||||
return ((uint16_t)mem[1] << 8) | (uint16_t)mem[0];
|
||||
}
|
||||
|
||||
static int init_header(tac_header_t* header, const uint8_t* buf) {
|
||||
|
|
|
@ -162,73 +162,57 @@ static int build_header_setup(uint8_t* buf, size_t bufsize, uint32_t setup_id, S
|
|||
static int load_fvs_file_single(uint8_t* buf, size_t bufsize, uint32_t setup_id, STREAMFILE* sf) {
|
||||
STREAMFILE* sf_setup = NULL;
|
||||
|
||||
/* get from artificial external file (used if compiled without codebooks) */
|
||||
{
|
||||
char setupname[PATH_LIMIT];
|
||||
char pathname[PATH_LIMIT];
|
||||
char *path;
|
||||
char setupname[0x20];
|
||||
|
||||
/* read "(dir/).fvs_{setup_id}" */
|
||||
sf->get_name(sf,pathname,sizeof(pathname));
|
||||
path = strrchr(pathname,DIR_SEPARATOR);
|
||||
if (path)
|
||||
*(path+1) = '\0';
|
||||
else
|
||||
pathname[0] = '\0';
|
||||
|
||||
snprintf(setupname,PATH_LIMIT,"%s.fvs_%08x", pathname, setup_id);
|
||||
sf_setup = sf->open(sf,setupname,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
snprintf(setupname, sizeof(setupname), ".fvs_%08x", setup_id);
|
||||
sf_setup = open_streamfile_by_filename(sf, setupname);
|
||||
}
|
||||
|
||||
/* get codebook and copy to buffer */
|
||||
if (sf_setup) {
|
||||
/* file found, get contents into the buffer */
|
||||
size_t bytes = sf_setup->get_size(sf_setup);
|
||||
if (bytes > bufsize) goto fail;
|
||||
|
||||
if (read_streamfile(buf, 0, bytes, sf_setup) != bytes)
|
||||
goto fail;
|
||||
|
||||
sf_setup->close(sf_setup);
|
||||
close_streamfile(sf_setup);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
fail:
|
||||
if (sf_setup) sf_setup->close(sf_setup);
|
||||
close_streamfile(sf_setup);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_fvs_file_multi(uint8_t* buf, size_t bufsize, uint32_t setup_id, STREAMFILE* sf) {
|
||||
STREAMFILE* sf_setup = NULL;
|
||||
|
||||
/* from to get from artificial external file (used if compiled without codebooks) */
|
||||
{
|
||||
char setupname[PATH_LIMIT];
|
||||
char pathname[PATH_LIMIT];
|
||||
char* path;
|
||||
char setupname[0x20];
|
||||
|
||||
/* read "(dir/).fvs" */
|
||||
sf->get_name(sf,pathname,sizeof(pathname));
|
||||
path = strrchr(pathname,DIR_SEPARATOR);
|
||||
if (path)
|
||||
*(path+1) = '\0';
|
||||
else
|
||||
pathname[0] = '\0';
|
||||
|
||||
snprintf(setupname,PATH_LIMIT,"%s.fvs", pathname);
|
||||
sf_setup = sf->open(sf,setupname,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
snprintf(setupname, sizeof(setupname), ".fvs");
|
||||
sf_setup = open_streamfile_by_filename(sf, setupname);
|
||||
}
|
||||
|
||||
/* find codebook in mini-header (format by bnnm, feel free to change) */
|
||||
if (sf_setup) {
|
||||
/* file found: read mini-header (format by bnnm, feel free to change) and locate FVS */
|
||||
int entries, i;
|
||||
uint32_t offset = 0, size = 0;
|
||||
|
||||
if (read_32bitBE(0x0, sf_setup) != 0x56465653) goto fail; /* "VFVS" */
|
||||
entries = read_32bitLE(0x08, sf_setup); /* 0x04=v0, 0x0c-0x20: reserved */
|
||||
if (!is_id32be(0x00, sf_setup, "VFVS"))
|
||||
goto fail;
|
||||
|
||||
entries = read_u32le(0x08, sf_setup); /* 0x04=v0, 0x0c-0x20: reserved */
|
||||
if (entries <= 0) goto fail;
|
||||
|
||||
for (i=0; i < entries; i++) { /* entry = id, offset, size, reserved */
|
||||
if ((uint32_t)read_32bitLE(0x20 + i*0x10, sf_setup) == setup_id) {
|
||||
offset = read_32bitLE(0x24 + i*0x10, sf_setup);
|
||||
size = read_32bitLE(0x28 + i*0x10, sf_setup);
|
||||
for (i = 0; i < entries; i++) { /* entry = id, offset, size, reserved */
|
||||
if (read_u32le(0x20 + i*0x10, sf_setup) == setup_id) {
|
||||
offset = read_u32le(0x24 + i*0x10, sf_setup);
|
||||
size = read_u32le(0x28 + i*0x10, sf_setup);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1144,27 +1144,18 @@ static int load_wvc(uint8_t* ibuf, size_t ibufsize, uint32_t codebook_id, wwise_
|
|||
}
|
||||
|
||||
static int load_wvc_file(uint8_t* buf, size_t bufsize, uint32_t codebook_id, STREAMFILE* sf) {
|
||||
STREAMFILE* sfWvc = NULL;
|
||||
STREAMFILE* sf_setup = NULL;
|
||||
size_t wvc_size = 0;
|
||||
|
||||
/* get from artificial external file (used if compiled without codebooks) */
|
||||
{
|
||||
char setupname[PATH_LIMIT];
|
||||
char pathname[PATH_LIMIT];
|
||||
char *path;
|
||||
char setupname[0x20];
|
||||
|
||||
/* read "(dir/).wvc" */
|
||||
sf->get_name(sf,pathname,sizeof(pathname));
|
||||
path = strrchr(pathname,DIR_SEPARATOR);
|
||||
if (path)
|
||||
*(path+1) = '\0';
|
||||
else
|
||||
pathname[0] = '\0';
|
||||
snprintf(setupname, sizeof(setupname), ".wvc");
|
||||
sf_setup = open_streamfile_by_filename(sf, setupname);
|
||||
if (!sf_setup) goto fail;
|
||||
|
||||
snprintf(setupname,PATH_LIMIT,"%s.wvc", pathname);
|
||||
sfWvc = sf->open(sf,setupname,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
if (!sfWvc) goto fail;
|
||||
|
||||
wvc_size = sfWvc->get_size(sfWvc);
|
||||
wvc_size = get_streamfile_size(sf_setup);
|
||||
}
|
||||
|
||||
/* find codebook and copy to buffer */
|
||||
|
@ -1174,24 +1165,24 @@ static int load_wvc_file(uint8_t* buf, size_t bufsize, uint32_t codebook_id, STR
|
|||
int codebook_count;
|
||||
|
||||
/* at the end of the WVC is an offset table, and we need to find codebook id (number) offset */
|
||||
table_start = read_u32le(wvc_size - 4, sfWvc); /* last offset */
|
||||
table_start = read_u32le(wvc_size - 4, sf_setup); /* last offset */
|
||||
codebook_count = ((wvc_size - table_start) / 4) - 1;
|
||||
if (table_start > wvc_size || codebook_id >= codebook_count) goto fail;
|
||||
|
||||
codebook_offset = read_u32le(table_start + codebook_id*4, sfWvc);
|
||||
codebook_size = read_u32le(table_start + codebook_id*4 + 4, sfWvc) - codebook_offset;
|
||||
codebook_offset = read_u32le(table_start + codebook_id*4, sf_setup);
|
||||
codebook_size = read_u32le(table_start + codebook_id*4 + 4, sf_setup) - codebook_offset;
|
||||
if (codebook_size > bufsize) goto fail;
|
||||
|
||||
if (read_streamfile(buf, codebook_offset, codebook_size, sfWvc) != codebook_size)
|
||||
if (read_streamfile(buf, codebook_offset, codebook_size, sf_setup) != codebook_size)
|
||||
goto fail;
|
||||
sfWvc->close(sfWvc);
|
||||
|
||||
close_streamfile(sf_setup);
|
||||
return codebook_size;
|
||||
}
|
||||
|
||||
|
||||
fail:
|
||||
if (sfWvc) sfWvc->close(sfWvc);
|
||||
close_streamfile(sf_setup);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,130 +1,99 @@
|
|||
#include "coding.h"
|
||||
#include "../util.h"
|
||||
|
||||
// todo this is based on Kazzuya's old code; different emus (PCSX, Mame, Mednafen, etc) do
|
||||
// XA coefs int math in different ways (see comments below), not 100% accurate.
|
||||
// May be implemented like the SNES/SPC700 BRR.
|
||||
|
||||
/* XA ADPCM gain values */
|
||||
#if 0
|
||||
static const float K0[4] = { 0.0, 0.9375, 1.796875, 1.53125 };
|
||||
static const float K1[4] = { 0.0, 0.0, -0.8125, -0.859375 };
|
||||
//#define XA_FLOAT 1
|
||||
#if XA_FLOAT
|
||||
/* floats as defined by the spec, but PS1's SPU would use int math */
|
||||
static const float K0[4+12] = { 0.0, 0.9375, 1.796875, 1.53125 };
|
||||
static const float K1[4+12] = { 0.0, 0.0, -0.8125, -0.859375 };
|
||||
#else
|
||||
/* K0/1 floats to int with N=6: K*2^6 = K*(1<<6) = K*64 (upper ranges are supposedly 0)*/
|
||||
static const int K0[4+12] = { 0, 60, 115, 98 };
|
||||
static const int K1[4+12] = { 0, 0, -52, -55 };
|
||||
#endif
|
||||
/* K0/1 floats to int, -K*2^10 = -K*(1<<10) = -K*1024 */
|
||||
static const int IK0[4] = { 0, -960, -1840, -1568 };
|
||||
static const int IK1[4] = { 0, 0, 832, 880 };
|
||||
|
||||
/* EA's extended XA (N=8), reverse engineered from SAT exes. Basically the same with minor
|
||||
* diffs and extra steps probably for the SH2 CPU (only does 1/2/8 shifts) */
|
||||
static const int16_t EA_TABLE[16][2] = {
|
||||
{ 0, 0 },
|
||||
{ 240, 0 },
|
||||
{ 460, -208 },
|
||||
{ 392, -220 },
|
||||
{ 488, -240 },
|
||||
{ 328, -208 },
|
||||
{ 440, -168 },
|
||||
{ 420, -188 },
|
||||
{ 432, -176 },
|
||||
{ 240, -16 },
|
||||
{ 416, -192 },
|
||||
{ 424, -160 },
|
||||
{ 288, -8 },
|
||||
{ 436, -188 },
|
||||
{ 224, -1 },
|
||||
{ 272, -16 },
|
||||
};
|
||||
|
||||
|
||||
/* Sony XA ADPCM, defined for CD-DA/CD-i in the "Red Book" (private) or "Green Book" (public) specs.
|
||||
* The algorithm basically is BRR (Bit Rate Reduction) from the SNES SPC700, while the data layout is new.
|
||||
*
|
||||
* Decoding is defined in diagrams, roughly as:
|
||||
* pcm = clamp( signed_nibble * 2^(12-range) + K0[index]*hist1 + K1[index]*hist2 )
|
||||
* - Range (12-range=shift) and filter index are renewed every ~28 samples.
|
||||
* - nibble is expanded to a signed 16b sample, reimplemented as:
|
||||
* short sample = ((nibble << 12) & 0xf000) >> shift
|
||||
* or: int sample = ((nibble << 28) & 0xf0000000) >> (shift + N)
|
||||
* - K0/K1 are float coefs are typically redefined with int math in various ways, with non-equivalent rounding:
|
||||
* (sample + K0*2^N*hist1 + K1*2^N*hist2 + [(2^N)/2]) / 2^N
|
||||
* (sample + K0*2^N*hist1 + K1*2^N*hist2 + [(2^N)/2]) >> N
|
||||
* sample + (K0*2^N*hist1 + K1*2^N*hist2)>>N
|
||||
* sample + (K0*2^N*hist1)>>N + (K1*2^N*hist2)>>N
|
||||
* etc
|
||||
* (rounding differences should be inaudible, so public implementations may be approximations)
|
||||
*
|
||||
* Various XA descendants (PS-ADPCM, EA-XA, NGC DTK, FADPCM, etc) do filters/rounding slightly
|
||||
* differently, maybe using one of the above methods in software/CPU, but in XA's case may be done
|
||||
* like the SNES/SPC700 BRR, with specific per-filter ops.
|
||||
* int coef tables commonly use N = 6 or 8, so K0 0.9375*64 = 60 or 0.9375*256 = 240
|
||||
* PS1 XA is apparently upsampled and interpolated to 44100, vgmstream doesn't simulate this.
|
||||
*
|
||||
* XA has an 8-bit decoding and "emphasis" modes, that no PS1 game actually uses, but apparently
|
||||
* are supported by the CD hardware and will play if found. Some odd CD-i game does use 8-bit mode.
|
||||
* Official "sound quality level" modes:
|
||||
* - Level A: 37.8hz, 8-bit
|
||||
* - Level B: 37.8hz, 4-bit
|
||||
* - Level C: 18.9hz, 4-bit
|
||||
*
|
||||
* Info (Green Book): https://www.lscdweb.com/data/downloadables/2/8/cdi_may94_r2.pdf
|
||||
* BRR info (no$sns): http://problemkaputt.de/fullsnes.htm#snesapudspbrrsamples
|
||||
* (bsnes): https://github.com/byuu/bsnes/blob/master/bsnes/sfc/dsp/SPC_DSP.cpp#L316
|
||||
*/
|
||||
* See end for accuracy information and layout info. */
|
||||
|
||||
/* data layout (mono):
|
||||
* - CD-XA audio is divided into sectors ("audio blocks"), each with 18*0x80 frames
|
||||
* (sectors handled externally, this decoder only sees N frames)
|
||||
* - each frame ("sound group") is divided into 8 interleaved subframes ("sound unit"), with
|
||||
* 8*0x01 subframe headers x2 ("sound parameters") first then 28*0x04 subframe nibbles ("sound data")
|
||||
* - subframe headers: 0..3 + repeat 0..3 + 4..7 + repeat 4..7 (where N = subframe N header)
|
||||
* (repeats may be for error correction, though probably unused)
|
||||
* header has a "range" N (gain of 2^N, or simplified as a shift) and a "filter" (control gains K0 and K1)
|
||||
* - subframe nibbles: 32b with nibble0 for subframes 0..8, 32b with nibble1 for subframes 0..8, etc
|
||||
* (low first: 32b = sf1-n0 sf0-n0 sf3-n0 sf2-n0 sf5-n0 sf4-n0 sf7-n0 sf6-n0, etc)
|
||||
*
|
||||
* stereo layout is the same but alternates channels: subframe 0/2/4/6=L, subframe 1/3/5/7=R
|
||||
*
|
||||
* example:
|
||||
* subframe 0: header @ 0x00 or 0x04, 28 nibbles (low) @ 0x10,14,18,1c,20 ... 7c
|
||||
* subframe 1: header @ 0x01 or 0x05, 28 nibbles (high) @ 0x10,14,18,1c,20 ... 7c
|
||||
* subframe 2: header @ 0x02 or 0x06, 28 nibbles (low) @ 0x11,15,19,1d,21 ... 7d
|
||||
* ...
|
||||
* subframe 7: header @ 0x0b or 0x0f, 28 nibbles (high) @ 0x13,17,1b,1f,23 ... 7f
|
||||
*
|
||||
* 8-bit layout is similar but only has 4 subframes + subframe bytes, so half the samples:
|
||||
* subframe 0: header @ 0x00 or 0x04/08/0c, 28 bytes @ 0x10,14,18,1c,20 ... 7c
|
||||
* subframe 1: header @ 0x01 or 0x05/09/0d, 28 bytes @ 0x11,16,19,1d,21 ... 7d
|
||||
* ...
|
||||
* subframe 3: header @ 0x03 or 0x07/0b/0f, 28 bytes @ 0x13,17,1b,1f,23 ... 7f
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t frame[0x80];
|
||||
int16_t* sbuf;
|
||||
int channels;
|
||||
int32_t hist1;
|
||||
int32_t hist2;
|
||||
int subframes;
|
||||
int is_xa8;
|
||||
int is_ea;
|
||||
} xa_t;
|
||||
|
||||
void decode_xa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_xa8) {
|
||||
uint8_t frame[0x80] = {0};
|
||||
off_t frame_offset;
|
||||
int i,j, sp_pos, frames_in, samples_done = 0, sample_count = 0;
|
||||
size_t bytes_per_frame, samples_per_frame;
|
||||
int32_t hist1 = stream->adpcm_history1_32;
|
||||
int32_t hist2 = stream->adpcm_history2_32;
|
||||
int subframes = (is_xa8) ? 4 : 8;
|
||||
|
||||
|
||||
/* external interleave (fixed size), mono/stereo */
|
||||
bytes_per_frame = 0x80;
|
||||
samples_per_frame = 28*subframes / channelspacing;
|
||||
frames_in = first_sample / samples_per_frame;
|
||||
first_sample = first_sample % samples_per_frame;
|
||||
|
||||
/* parse frame header */
|
||||
frame_offset = stream->offset + bytes_per_frame * frames_in;
|
||||
read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */
|
||||
|
||||
VGM_ASSERT(get_u32be(frame+0x0) != get_u32be(frame+0x4) || get_u32be(frame+0x8) != get_u32be(frame+0xC),
|
||||
"bad frames at %x\n", (uint32_t)frame_offset);
|
||||
static void decode_xa_frame(xa_t* xa, int32_t first_sample, int32_t samples_to_do, int channel) {
|
||||
int i,j, samples_done = 0, sample_count = 0;
|
||||
int shift_max = xa->is_xa8 ? 8 : 12;
|
||||
int shift_limit = xa->is_xa8 ? 8 : 9; /* from Nocash PSX docs (in 8-bit mode max range should be 8 though) */
|
||||
|
||||
/* decode subframes */
|
||||
for (i = 0; i < subframes / channelspacing; i++) {
|
||||
for (i = 0; i < xa->subframes / xa->channels; i++) {
|
||||
uint8_t sp;
|
||||
int index, shift, sp_pos;
|
||||
#ifdef XA_FLOAT
|
||||
float coef1, coef2;
|
||||
#else
|
||||
int32_t coef1, coef2;
|
||||
uint8_t coef_index, shift_factor;
|
||||
#endif
|
||||
|
||||
/* parse current subframe (sound unit)'s header (sound parameters) */
|
||||
sp_pos = is_xa8 ?
|
||||
i*channelspacing + channel:
|
||||
0x04 + i*channelspacing + channel;
|
||||
coef_index = (frame[sp_pos] >> 4) & 0xf;
|
||||
shift_factor = (frame[sp_pos] >> 0) & 0xf;
|
||||
sp_pos = xa->is_xa8 ?
|
||||
i*xa->channels + channel :
|
||||
0x04 + i*xa->channels + channel;
|
||||
sp = xa->frame[sp_pos];
|
||||
index = (sp >> 4) & 0xf;
|
||||
shift = (sp >> 0) & 0xf;
|
||||
|
||||
/* mastered values like 0xFF exist [Micro Machines (CDi), demo and release] */
|
||||
VGM_ASSERT(coef_index > 4 || shift_factor > (is_xa8 ? 8 : 12), "XA: incorrect coefs/shift at %x\n", (uint32_t)frame_offset + sp_pos);
|
||||
if (coef_index > 4)
|
||||
coef_index = 0; /* only 4 filters are used, rest is apparently 0 */
|
||||
if (shift_factor > (is_xa8 ? 8 : 12))
|
||||
shift_factor = (is_xa8 ? 8 : 9); /* supposedly, from Nocash PSX docs (in 8-bit mode max range should be 8 though) */
|
||||
VGM_ASSERT_ONCE(shift > shift_max, "XA: incorrect shift %x\n", sp);
|
||||
if (shift > shift_max)
|
||||
shift = shift_limit;
|
||||
|
||||
coef1 = IK0[coef_index];
|
||||
coef2 = IK1[coef_index];
|
||||
if (xa->is_ea) {
|
||||
coef1 = EA_TABLE[index][0];
|
||||
coef2 = EA_TABLE[index][1];
|
||||
}
|
||||
else {
|
||||
VGM_ASSERT_ONCE(index > 4, "XA: incorrect coefs %x\n", sp);
|
||||
coef1 = K0[index];
|
||||
coef2 = K1[index];
|
||||
}
|
||||
|
||||
|
||||
/* decode subframe nibbles */
|
||||
for(j = 0; j < 28; j++) {
|
||||
int32_t sample;
|
||||
uint8_t su;
|
||||
|
||||
/* skip half decodes to make sure hist isn't touched (kinda hack-ish) */
|
||||
if (!(sample_count >= first_sample && samples_done < samples_to_do)) {
|
||||
|
@ -132,47 +101,97 @@ void decode_xa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, i
|
|||
continue;
|
||||
}
|
||||
|
||||
if (is_xa8) {
|
||||
int su_pos = (channelspacing==1) ?
|
||||
if (xa->is_xa8) {
|
||||
int su_pos = (xa->channels==1) ?
|
||||
0x10 + j*0x04 + i : /* mono */
|
||||
0x10 + j*0x04 + i*2 + channel; /* stereo */
|
||||
|
||||
sample = frame[su_pos];
|
||||
sample = (int16_t)((sample << 8) & 0xff00) >> shift_factor; /* 16b sign extend + scale */
|
||||
su = xa->frame[su_pos];
|
||||
sample = (int16_t)((su << 8) & 0xff00) >> shift; /* 16b sign extend + scale */
|
||||
}
|
||||
else {
|
||||
uint8_t nibbles;
|
||||
int su_pos = (channelspacing==1) ?
|
||||
int su_pos = (xa->channels==1) ?
|
||||
0x10 + j*0x04 + (i/2) : /* mono */
|
||||
0x10 + j*0x04 + i; /* stereo */
|
||||
int get_high_nibble = (channelspacing==1) ?
|
||||
int get_high_nibble = (xa->channels==1) ?
|
||||
(i&1) : /* mono (even subframes = low, off subframes = high) */
|
||||
(channel == 1); /* stereo (L channel / even subframes = low, R channel / odd subframes = high) */
|
||||
|
||||
nibbles = frame[su_pos];
|
||||
sample = get_high_nibble ?
|
||||
(nibbles >> 4) & 0x0f :
|
||||
(nibbles >> 0) & 0x0f;
|
||||
sample = (int16_t)((sample << 12) & 0xf000) >> shift_factor; /* 16b sign extend + scale */
|
||||
su = xa->frame[su_pos];
|
||||
su = get_high_nibble ?
|
||||
(su >> 4) & 0x0f :
|
||||
(su >> 0) & 0x0f;
|
||||
sample = (int16_t)((su << 12) & 0xf000) >> shift; /* 16b sign extend + scale */
|
||||
}
|
||||
|
||||
sample = sample << 4; /* scale for current IK */
|
||||
sample = sample - ((coef1*hist1 + coef2*hist2) >> 10);
|
||||
#if XA_FLOAT
|
||||
sample = sample + (coef1 * xa->hist1 + coef2 * xa->hist2);
|
||||
#else
|
||||
if (xa->is_ea) /* sample << 8 actually but UB on negatives */
|
||||
sample = (sample * 256 + coef1 * xa->hist1 + coef2 * xa->hist2) >> 8;
|
||||
else
|
||||
sample = sample + ((coef1 * xa->hist1 + coef2 * xa->hist2 + 32) >> 6);
|
||||
#endif
|
||||
|
||||
hist2 = hist1;
|
||||
hist1 = sample; /* must go before clamp, somehow */
|
||||
sample = sample >> 4;
|
||||
sample = clamp16(sample);
|
||||
xa->hist2 = xa->hist1;
|
||||
xa->hist1 = sample;
|
||||
sample = clamp16(sample); /* don't clamp hist */
|
||||
if (xa->is_ea)
|
||||
xa->hist1 = sample; /* do clamp hist */
|
||||
|
||||
outbuf[samples_done * channelspacing] = sample;
|
||||
xa->sbuf[samples_done * xa->channels] = sample;
|
||||
samples_done++;
|
||||
|
||||
sample_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stream->adpcm_history1_32 = hist1;
|
||||
stream->adpcm_history2_32 = hist2;
|
||||
|
||||
void decode_xa(VGMSTREAM* v, sample_t* outbuf, int32_t samples_to_do) {
|
||||
uint32_t offset = v->ch[0].offset; /* L/R share offsets */
|
||||
STREAMFILE* sf = v->ch[0].streamfile;
|
||||
|
||||
int ch;
|
||||
int frames_in, bytes, samples_per_frame;
|
||||
uint32_t frame_offset, bytes_per_frame;
|
||||
int32_t first_sample = v->samples_into_block;
|
||||
xa_t xa;
|
||||
|
||||
xa.channels = v->channels > 1 ? 2 : 1; /* only stereo/mono modes */
|
||||
xa.is_xa8 = (v->coding_type == coding_XA8);
|
||||
xa.is_ea = (v->coding_type == coding_XA_EA);
|
||||
xa.subframes = (xa.is_xa8) ? 4 : 8;
|
||||
|
||||
/* external interleave (fixed size), mono/stereo */
|
||||
bytes_per_frame = sizeof(xa.frame);
|
||||
samples_per_frame = 28 * xa.subframes / v->channels;
|
||||
frames_in = first_sample / samples_per_frame;
|
||||
first_sample = first_sample % samples_per_frame;
|
||||
|
||||
/* parse frame header */
|
||||
frame_offset = offset + bytes_per_frame * frames_in;
|
||||
bytes = read_streamfile(xa.frame, frame_offset, bytes_per_frame, sf);
|
||||
if (bytes != sizeof(xa.frame)) /* ignore EOF errors */
|
||||
memset(xa.frame + bytes, 0, bytes_per_frame - bytes);
|
||||
|
||||
/* headers should repeat in pairs, except in EA's modified XA */
|
||||
VGM_ASSERT_ONCE(!xa.is_ea &&
|
||||
(get_u32be(xa.frame+0x0) != get_u32be(xa.frame+0x4) || get_u32be(xa.frame+0x8) != get_u32be(xa.frame+0xC)),
|
||||
"bad frames at %x\n", frame_offset);
|
||||
|
||||
for (ch = 0; ch < xa.channels; ch++) {
|
||||
VGMSTREAMCHANNEL* stream = &v->ch[ch];
|
||||
|
||||
xa.sbuf = outbuf+ch;
|
||||
xa.hist1 = stream->adpcm_history1_32;
|
||||
xa.hist2 = stream->adpcm_history2_32;
|
||||
|
||||
decode_xa_frame(&xa, first_sample, samples_to_do, ch);
|
||||
|
||||
stream->adpcm_history1_32 = xa.hist1;
|
||||
stream->adpcm_history2_32 = xa.hist2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -185,3 +204,85 @@ size_t xa_bytes_to_samples(size_t bytes, int channels, int is_blocked, int is_fo
|
|||
return (bytes / 0x80) * (28*subframes / channels);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Official XA decoding is defined in diagrams (not implementation), roughly as:
|
||||
* pcm = signed_nibble * 2^(12-range) + K0[index]*hist1 + K1[index]*hist2
|
||||
* - K0 = 0.0, 0.9375, 1.796875, 1.53125 / K1 = 0.0, 0.0, -0.8125, -0.859375
|
||||
* - int coef tables commonly use N = 6 or 8, so K0 0.9375*64 = 60 or 0.9375*256 = 240
|
||||
* - Range (12-range=shift) and filter index are renewed every ~28 samples.
|
||||
* - nibble is expanded to a signed 16b sample, reimplemented as:
|
||||
* short sample = ((nibble << 12) & 0xf000) >> shift
|
||||
* or: int sample = ((nibble << 28) & 0xf0000000) >> (shift + N)
|
||||
* - K0/K1 are float coefs are typically redefined with int math in various ways, with non-equivalent rounding:
|
||||
* (sample + K0*2^N*hist1 + K1*2^N*hist2 + [(2^N)/2]) / 2^N
|
||||
* (sample + K0*2^N*hist1 + K1*2^N*hist2 + [(2^N)/2]) >> N
|
||||
* sample + (K0*2^N*hist1 + K1*2^N*hist2)>>N
|
||||
* sample + (K0*2^N*hist1)>>N + (K1*2^N*hist2)>>N
|
||||
* ...
|
||||
* (rounding differences should be inaudible)
|
||||
*
|
||||
* There isn't an official implementation, but supposedly CD-ROM controller (which reads CD-XA) pushes
|
||||
* audio data to the SPU directly (http://wiki.psxdev.ru/index.php/SPU), so probably the same as PS-ADPCM.
|
||||
* Emus all seem to use approximations:
|
||||
* - duckstation: N=6, "s + ((h1 * K0) + (h2 * K1) + 32) / 64"; no hist clamp; s clamp;
|
||||
* - mednafen: N=6, "s + ((h1 * K0) >> 6) + ((h2 * K1) >> 6)"; hist / s clamp;
|
||||
* - mame: N=6, "s + ((h1 * K0) + (h2 * K1) + 32) >> 6)"; no hist / s clamp;
|
||||
* - peops: N=10, "s + ((h1 * K0) + (h2 * K1) + 32) >> 10)"; no hist / s clamp;
|
||||
* - pcsc: N=10, "s + ((h1 * K0) + (h2 * K1)) >> 10)"; hist / s clamp;
|
||||
* - cedimu: f32, "s + (h1 * k0 + h2 * k1)"; no hist / s clamp
|
||||
* It's not clear from the diagrams if hist should be clamped (seemingly not), but reportedly not
|
||||
* clamping improves output waveform spikes, while N=6 seems most common, and +32
|
||||
* (note <-0 / 64 != <0 >> 6)
|
||||
*
|
||||
* PS1 XA is apparently upsampled and interpolated to 44100, vgmstream doesn't simulate this.
|
||||
* Various XA descendants (PS-ADPCM, EA-XA, NGC DTK, FADPCM, etc) do filters/rounding slightly
|
||||
* differently too, maybe using one of the above methods in software/CPU, but in XA's case may
|
||||
* be done like the SNES/SPC700 BRR, with specific per-filter ops rather than a formula.
|
||||
*
|
||||
* XA has an 8-bit decoding and "emphasis" modes, that no PS1 game actually uses, but apparently
|
||||
* are supported by the CD hardware and will play if found. Some odd CD-i game does use 8-bit mode.
|
||||
* Official "sound quality level" modes:
|
||||
* - Level A: 37.8hz, 8-bit
|
||||
* - Level B: 37.8hz, 4-bit
|
||||
* - Level C: 18.9hz, 4-bit
|
||||
*
|
||||
* Info (Green Book): https://www.lscdweb.com/data/downloadables/2/8/cdi_may94_r2.pdf
|
||||
* BRR info (no$sns): http://problemkaputt.de/fullsnes.htm#snesapudspbrrsamples
|
||||
* (bsnes): https://github.com/byuu/bsnes/blob/master/bsnes/sfc/dsp/SPC_DSP.cpp#L316
|
||||
* Note that this is just called "ADPCM" in the "CD-ROM XA" spec (rather than "XA ADPCM" being the actual name).
|
||||
*/
|
||||
|
||||
/* data layout (mono):
|
||||
* - CD XA audio is divided into sectors ("audio blocks"), each with 18*0x80 frames
|
||||
* (sectors are handled externally, this decoder only sees 0x80 frames)
|
||||
* - each frame ("sound group") is divided into 8 interleaved subframes ("sound unit"), with
|
||||
* 8*0x01 subframe headers x2 ("sound parameters") first then 28*0x04 subframe nibbles ("sound data")
|
||||
* - subframe headers: 0..3 + repeat 0..3 + 4..7 + repeat 4..7 (where N = subframe N header)
|
||||
* (repeats may be for error correction, though probably unused)
|
||||
* header has a "range" N (gain of 2^N, or simplified as a shift) and a "filter" (control gains K0 and K1)
|
||||
* - subframe nibbles: 32b with nibble0 for subframes 0..8, 32b with nibble1 for subframes 0..8, etc
|
||||
* (low first: 32b = sf1-n0 sf0-n0 sf3-n0 sf2-n0 sf5-n0 sf4-n0 sf7-n0 sf6-n0, etc)
|
||||
*
|
||||
* stereo layout is the same but alternates channels: subframe 0/2/4/6=L, subframe 1/3/5/7=R
|
||||
*
|
||||
* example:
|
||||
* 2A29282A 2A29282A 29292929 29292929
|
||||
* 0D0D20FB 0D8B011C 0EFA103B 5FEC2F42
|
||||
* ...
|
||||
*
|
||||
* subframe 0: header @ 0x00 or 0x04, 28 nibbles (low) @ 0x10,14,18,1c,20 ... 7c
|
||||
* subframe 1: header @ 0x01 or 0x05, 28 nibbles (high) @ 0x10,14,18,1c,20 ... 7c
|
||||
* subframe 2: header @ 0x02 or 0x06, 28 nibbles (low) @ 0x11,15,19,1d,21 ... 7d
|
||||
* ...
|
||||
* subframe 7: header @ 0x0b or 0x0f, 28 nibbles (high) @ 0x13,17,1b,1f,23 ... 7f
|
||||
*
|
||||
* 8-bit layout is similar but only has 4 subframes + subframe bytes, so half the samples:
|
||||
* subframe 0: header @ 0x00 or 0x04/08/0c, 28 bytes @ 0x10,14,18,1c,20 ... 7c
|
||||
* subframe 1: header @ 0x01 or 0x05/09/0d, 28 bytes @ 0x11,16,19,1d,21 ... 7d
|
||||
* ...
|
||||
* subframe 3: header @ 0x03 or 0x07/0b/0f, 28 bytes @ 0x13,17,1b,1f,23 ... 7f
|
||||
*
|
||||
* XA-EA found in EA SAT games set subframes header like: 0..3 null + 0..3 ok + 4..7 ok + 4..7 null
|
||||
* so decoder only reads those.
|
||||
*/
|
||||
|
|
|
@ -12,6 +12,10 @@ static const int scale_step_adpcmb[16] = {
|
|||
57, 57, 57, 57, 77, 102, 128, 153,
|
||||
};
|
||||
|
||||
static const int scale_step_capcom[8] = {
|
||||
58982, 58982, 58982, 58982, 78643, 104858, 131072, 157286,
|
||||
};
|
||||
|
||||
/* look-up for 'mul' IMA's sign*((code&7) * 2 + 1) for every code */
|
||||
static const int scale_delta[16] = {
|
||||
1, 3, 5, 7, 9, 11, 13, 15,
|
||||
|
@ -59,6 +63,31 @@ static void yamaha_aica_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offs
|
|||
*hist1 = sample;
|
||||
}
|
||||
|
||||
/* Capcom's version of Yamaha expand */
|
||||
static void yamaha_capcom_expand_nibble(uint8_t byte, int shift, int32_t* hist1, int32_t* step_size, int16_t *out_sample) {
|
||||
int code, ucode, delta, sample;
|
||||
const int scale = 0x200; /* ADPCM state var, but seemingly fixed */
|
||||
|
||||
code = (byte >> shift) & 0xf;
|
||||
ucode = code & 0x7;
|
||||
delta = (ucode * (*step_size)) >> 2; /* custom (SH2 CPU can't do odd shifts in one op, it seems) */
|
||||
if (code & 8)
|
||||
delta = -delta;
|
||||
sample = *hist1 + delta;
|
||||
|
||||
sample = (short)sample; /* clamp not done, but output is always low-ish */
|
||||
|
||||
*step_size = ((*step_size) * scale_step_capcom[ucode]) >> 16;
|
||||
if (*step_size < 0x80) *step_size = 0x80; /* unlike usual 0x7f */
|
||||
else if (*step_size > 0x6000) *step_size = 0x6000;
|
||||
|
||||
*hist1 = sample;
|
||||
|
||||
/* OG code adds out sample, but seems to be always 0 (used for mono downmix?) */
|
||||
sample = ((scale * sample) >> 8) /*+ *out_sample */;
|
||||
*out_sample = sample;
|
||||
}
|
||||
|
||||
|
||||
/* info about Yamaha ADPCM as created by official yadpcm.acm (in 'Yamaha-ADPCM-ACM-Driver-100-j')
|
||||
* - possibly RIFF codec 0x20
|
||||
|
@ -107,6 +136,37 @@ void decode_aica(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacin
|
|||
stream->adpcm_step_index = step_size;
|
||||
}
|
||||
|
||||
/* Capcom/Saturn Yamaha ADPCM, reverse engineered from the exe (codec has no apparent name so CP_YM = Capcom Yamaha) */
|
||||
void decode_cp_ym(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo) {
|
||||
int i, sample_count = 0;
|
||||
int16_t out_sample;
|
||||
int32_t hist1 = stream->adpcm_history1_16;
|
||||
int step_size = stream->adpcm_step_index;
|
||||
|
||||
/* no header (external setup), pre-clamp for wrong values */
|
||||
if (step_size < 0x80) step_size = 0x80;
|
||||
if (step_size > 0x6000) step_size = 0x6000;
|
||||
|
||||
for (i = first_sample; i < first_sample + samples_to_do; i++) {
|
||||
uint8_t byte;
|
||||
uint32_t offset = is_stereo ?
|
||||
stream->offset + i : /* stereo: one nibble per channel */
|
||||
stream->offset + i/2; /* mono: consecutive nibbles */
|
||||
int shift = is_stereo ?
|
||||
(!(channel&1) ? 0:4) : /* even = low/L, odd = high/R */
|
||||
(!(i&1) ? 0:4); /* low nibble first */
|
||||
|
||||
byte = read_u8(offset, stream->streamfile);
|
||||
yamaha_capcom_expand_nibble(byte, shift, &hist1, &step_size, &out_sample);
|
||||
outbuf[sample_count] = out_sample;
|
||||
sample_count += channelspacing;
|
||||
}
|
||||
|
||||
stream->adpcm_history1_16 = hist1;
|
||||
stream->adpcm_step_index = step_size;
|
||||
}
|
||||
|
||||
|
||||
/* tri-Ace Aska ADPCM, Yamaha ADPCM-B with headered frames (reversed from Android SO's .so)
|
||||
* implements table with if-else/switchs too but that's too goofy */
|
||||
void decode_aska(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, size_t frame_size) {
|
||||
|
|
|
@ -442,6 +442,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM* vgmstream) {
|
|||
return 0; /* variable (block-controlled) */
|
||||
|
||||
case coding_XA:
|
||||
case coding_XA_EA:
|
||||
return 28*8 / vgmstream->channels; /* 8 subframes per frame, mono/stereo */
|
||||
case coding_XA8:
|
||||
return 28*4 / vgmstream->channels; /* 4 subframes per frame, mono/stereo */
|
||||
|
@ -471,6 +472,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM* vgmstream) {
|
|||
case coding_WS: /* only works if output sample size is 8 bit, which always is for WS ADPCM */
|
||||
return vgmstream->ws_output_size;
|
||||
case coding_AICA:
|
||||
case coding_CP_YM:
|
||||
return 1;
|
||||
case coding_AICA_int:
|
||||
return 2;
|
||||
|
@ -659,6 +661,7 @@ int get_vgmstream_frame_size(VGMSTREAM* vgmstream) {
|
|||
return 0x00; /* variable (block-controlled) */
|
||||
|
||||
case coding_XA:
|
||||
case coding_XA_EA:
|
||||
case coding_XA8:
|
||||
return 0x80;
|
||||
case coding_PSX:
|
||||
|
@ -690,6 +693,7 @@ int get_vgmstream_frame_size(VGMSTREAM* vgmstream) {
|
|||
return vgmstream->current_block_size;
|
||||
case coding_AICA:
|
||||
case coding_AICA_int:
|
||||
case coding_CP_YM:
|
||||
return 0x01;
|
||||
case coding_ASKA:
|
||||
return vgmstream->frame_size;
|
||||
|
@ -1005,12 +1009,9 @@ void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_
|
|||
}
|
||||
break;
|
||||
case coding_XA:
|
||||
case coding_XA_EA:
|
||||
case coding_XA8: {
|
||||
int is_xa8 = (vgmstream->coding_type == coding_XA8);
|
||||
for (ch = 0; ch < vgmstream->channels; ch++) {
|
||||
decode_xa(&vgmstream->ch[ch], buffer+ch,
|
||||
vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch, is_xa8);
|
||||
}
|
||||
decode_xa(vgmstream, buffer, samples_to_do);
|
||||
break;
|
||||
}
|
||||
case coding_EA_XA:
|
||||
|
@ -1331,6 +1332,15 @@ void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_
|
|||
}
|
||||
break;
|
||||
}
|
||||
case coding_CP_YM: {
|
||||
int is_stereo = (vgmstream->channels > 1);
|
||||
for (ch = 0; ch < vgmstream->channels; ch++) {
|
||||
decode_cp_ym(&vgmstream->ch[ch], buffer+ch,
|
||||
vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch,
|
||||
is_stereo);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case coding_ASKA:
|
||||
for (ch = 0; ch < vgmstream->channels; ch++) {
|
||||
decode_aska(&vgmstream->ch[ch], buffer+ch,
|
||||
|
|
|
@ -58,9 +58,7 @@ static const char* extension_list[] = {
|
|||
//"aif", //common
|
||||
"aif-Loop",
|
||||
"aifc", //common?
|
||||
"aifcl", //fake extension for .aif???
|
||||
//"aiff", //common
|
||||
"aiffl", //fake extension for .aif???
|
||||
"aix",
|
||||
"akb",
|
||||
"al",
|
||||
|
@ -85,6 +83,7 @@ static const char* extension_list[] = {
|
|||
"aud",
|
||||
"audio", //txth/reserved [Grimm Echoes (Android)]
|
||||
"aus",
|
||||
"awa", //txth/reserved [Missing Parts Side A (PS2)]
|
||||
"awb",
|
||||
"awc",
|
||||
|
||||
|
@ -256,6 +255,7 @@ static const char* extension_list[] = {
|
|||
"kovs", //fake extension/header id for .kvs
|
||||
"kno",
|
||||
"kns",
|
||||
"koe",
|
||||
"kraw",
|
||||
"ktac",
|
||||
"ktsl2asbin",
|
||||
|
@ -335,6 +335,7 @@ static const char* extension_list[] = {
|
|||
"msa",
|
||||
"msb",
|
||||
"msd",
|
||||
"mse",
|
||||
"msf",
|
||||
"mss",
|
||||
"msv",
|
||||
|
@ -360,6 +361,7 @@ static const char* extension_list[] = {
|
|||
"nop",
|
||||
"nps",
|
||||
"npsf", //fake extension/header id for .nps (in bigfiles)
|
||||
"nsa",
|
||||
"nsopus",
|
||||
"nub",
|
||||
"nub2",
|
||||
|
@ -370,6 +372,7 @@ static const char* extension_list[] = {
|
|||
"nxa",
|
||||
|
||||
//"ogg", //common
|
||||
"ogg_",
|
||||
"ogl",
|
||||
"ogv",
|
||||
"oma", //FFmpeg/not parsed (ATRAC3/ATRAC3PLUS/MP3/LPCM/WMA)
|
||||
|
@ -431,6 +434,7 @@ static const char* extension_list[] = {
|
|||
"sab",
|
||||
"sad",
|
||||
"saf",
|
||||
"sam", //txth/reserved [Lost Kingdoms 2 (GC)]
|
||||
"sap",
|
||||
"sb0",
|
||||
"sb1",
|
||||
|
@ -532,6 +536,7 @@ static const char* extension_list[] = {
|
|||
"txtp",
|
||||
"tydsp",
|
||||
|
||||
"u0",
|
||||
"ue4opus",
|
||||
"ulw",
|
||||
"um3",
|
||||
|
@ -551,6 +556,7 @@ static const char* extension_list[] = {
|
|||
"vbx", //txth/reserved [THE Taxi 2 (PS2)]
|
||||
"vds",
|
||||
"vdm",
|
||||
"vgi", //txth/reserved [Time Crisis II (PS2)]
|
||||
"vgm", //txth/reserved [Maximo (PS2)]
|
||||
"vgs",
|
||||
"vgv",
|
||||
|
@ -567,6 +573,7 @@ static const char* extension_list[] = {
|
|||
"vsv",
|
||||
"vxn",
|
||||
|
||||
"w",
|
||||
"waa",
|
||||
"wac",
|
||||
"wad",
|
||||
|
@ -586,6 +593,7 @@ static const char* extension_list[] = {
|
|||
"wd",
|
||||
"wem",
|
||||
"wii",
|
||||
"wic", //txth/reserved [Road Rash (SAT)-videos]
|
||||
"wip", //txth/reserved [Colin McRae DiRT (PC)]
|
||||
"wlv", //txth/reserved [ToeJam & Earl III: Mission to Earth (DC)]
|
||||
"wmus",
|
||||
|
@ -593,6 +601,7 @@ static const char* extension_list[] = {
|
|||
"wpd",
|
||||
"wsd",
|
||||
"wsi",
|
||||
"wst", //txth/reserved [3jigen Shoujo o Hogo Shimashita (PC)]
|
||||
"wua",
|
||||
"wv2",
|
||||
"wv6",
|
||||
|
@ -735,6 +744,7 @@ static const coding_info coding_info_list[] = {
|
|||
|
||||
{coding_XA, "CD-ROM XA 4-bit ADPCM"},
|
||||
{coding_XA8, "CD-ROM XA 8-bit ADPCM"},
|
||||
{coding_XA_EA, "Electronic Arts XA 4-bit ADPCM"},
|
||||
{coding_PSX, "Playstation 4-bit ADPCM"},
|
||||
{coding_PSX_badflags, "Playstation 4-bit ADPCM (bad flags)"},
|
||||
{coding_PSX_cfg, "Playstation 4-bit ADPCM (configurable)"},
|
||||
|
@ -785,6 +795,7 @@ static const coding_info coding_info_list[] = {
|
|||
{coding_WS, "Westwood Studios VBR ADPCM"},
|
||||
{coding_AICA, "Yamaha AICA 4-bit ADPCM"},
|
||||
{coding_AICA_int, "Yamaha AICA 4-bit ADPCM (mono/interleave)"},
|
||||
{coding_CP_YM, "Capcom Yamaha 4-bit ADPCM"},
|
||||
{coding_ASKA, "tri-Ace Aska 4-bit ADPCM"},
|
||||
{coding_NXAP, "Nex NXAP 4-bit ADPCM"},
|
||||
{coding_TGC, "Tiger Game.com 4-bit ADPCM"},
|
||||
|
@ -1118,7 +1129,8 @@ static const meta_info meta_info_list[] = {
|
|||
{meta_XBOX_HLWAV, "Half-Life 2 .WAV header"},
|
||||
{meta_MYSPD, "U-Sing .MYSPD header"},
|
||||
{meta_HIS, "Her Interactive HIS header"},
|
||||
{meta_PS2_AST, "KOEI AST header"},
|
||||
{meta_AST_MV, "MicroVision AST header"},
|
||||
{meta_AST_MMV, "Marvelous AST header"},
|
||||
{meta_CAPDSP, "Capcom DSP header"},
|
||||
{meta_DMSG, "Microsoft RIFF DMSG header"},
|
||||
{meta_PONA_3DO, "Policenauts BGM header"},
|
||||
|
@ -1146,7 +1158,7 @@ static const meta_info meta_info_list[] = {
|
|||
{meta_DSP_XIII, "XIII dsp header"},
|
||||
{meta_DSP_CABELAS, "Cabelas games .DSP header"},
|
||||
{meta_PS2_ADM, "Dragon Quest V .ADM raw header"},
|
||||
{meta_PS2_LPCM, "LPCM header"},
|
||||
{meta_LPCM_SHADE, "Shade LPCM header"},
|
||||
{meta_PS2_VMS, "VMS Header"},
|
||||
{meta_XAU, "XPEC XAU header"},
|
||||
{meta_GH3_BAR, "Guitar Hero III Mobile .bar"},
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "layout.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "../vgmstream.h"
|
||||
#include "../util/endianness.h"
|
||||
|
||||
/* set up for the block at the given offset */
|
||||
void block_update_ea_1snh(off_t block_offset, VGMSTREAM* vgmstream) {
|
||||
|
@ -8,7 +9,7 @@ void block_update_ea_1snh(off_t block_offset, VGMSTREAM* vgmstream) {
|
|||
int i;
|
||||
uint32_t block_id;
|
||||
size_t block_size = 0, block_header = 0, audio_size = 0;
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_32bitBE : read_32bitLE;
|
||||
read_s32_t read_s32 = vgmstream->codec_endian ? read_s32be : read_s32le;
|
||||
|
||||
|
||||
/* EOF reads: signal we have nothing and let the layout fail */
|
||||
|
@ -19,27 +20,29 @@ void block_update_ea_1snh(off_t block_offset, VGMSTREAM* vgmstream) {
|
|||
return;
|
||||
}
|
||||
|
||||
block_id = read_32bitBE(block_offset + 0x00, sf);
|
||||
block_id = read_u32be(block_offset + 0x00, sf);
|
||||
|
||||
/* BE in SAT, but one file may have both BE and LE chunks [FIFA 98 (SAT): movie LE, audio BE] */
|
||||
if (guess_endianness32bit(block_offset + 0x04, sf))
|
||||
block_size = read_32bitBE(block_offset + 0x04, sf);
|
||||
if (guess_endian32(block_offset + 0x04, sf))
|
||||
block_size = read_u32be(block_offset + 0x04, sf);
|
||||
else
|
||||
block_size = read_32bitLE(block_offset + 0x04, sf);
|
||||
block_size = read_u32le(block_offset + 0x04, sf);
|
||||
|
||||
block_header = 0;
|
||||
|
||||
if (block_id == 0x31534E68 || block_id == 0x53454144) { /* "1SNh" "SEAD" audio header */
|
||||
int is_sead = (block_id == 0x53454144);
|
||||
int is_eacs = read_32bitBE(block_offset + 0x08, sf) == 0x45414353;
|
||||
int is_zero = read_32bitBE(block_offset + 0x08, sf) == 0x00;
|
||||
if (block_id == get_id32be("1SNh") || block_id == get_id32be("SEAD")) { /* audio header */
|
||||
int is_sead = (block_id == get_id32be("SEAD"));
|
||||
int is_eacs = is_id32be(block_offset + 0x08, sf, "EACS");
|
||||
int is_zero = read_u32be(block_offset + 0x08, sf) == 0x00;
|
||||
|
||||
block_header = (is_eacs || is_zero) ? 0x28 : (is_sead ? 0x14 : 0x2c);
|
||||
if (block_header >= block_size) /* sometimes has audio data after header */
|
||||
block_header = 0;
|
||||
} else if (block_id == 0x31534E64 || block_id == 0x534E4443) { /* "1SNd" "SNDC" audio data */
|
||||
}
|
||||
else if (block_id == get_id32be("1SNd") || block_id == get_id32be("SNDC")) {
|
||||
block_header = 0x08;
|
||||
} else if (block_id == 0x00000000 || block_id == 0xFFFFFFFF || block_id == 0x31534E65) { /* EOF or "1SNe" */
|
||||
}
|
||||
else if (block_id == 0x00000000 || block_id == 0xFFFFFFFF || block_id == get_id32be("1SNe")) { /* EOF */
|
||||
vgmstream->current_block_samples = -1;
|
||||
return;
|
||||
}
|
||||
|
@ -72,20 +75,25 @@ void block_update_ea_1snh(off_t block_offset, VGMSTREAM* vgmstream) {
|
|||
break;
|
||||
|
||||
case coding_PSX:
|
||||
if (vgmstream->codec_config == 1) {/* extra field */
|
||||
block_header += 0x04;
|
||||
audio_size -= 0x04;
|
||||
}
|
||||
|
||||
vgmstream->current_block_samples = ps_bytes_to_samples(audio_size, vgmstream->channels);
|
||||
for (i=0;i<vgmstream->channels;i++) {
|
||||
for (i = 0; i < vgmstream->channels; i++) {
|
||||
vgmstream->ch[i].offset = block_offset + block_header + i*(audio_size/vgmstream->channels);
|
||||
}
|
||||
break;
|
||||
|
||||
case coding_DVI_IMA:
|
||||
if (vgmstream->codec_config == 1) { /* ADPCM hist */
|
||||
vgmstream->current_block_samples = read_32bit(block_offset + block_header, sf);
|
||||
vgmstream->current_block_samples = read_s32(block_offset + block_header, sf);
|
||||
|
||||
for(i = 0; i < vgmstream->channels; i++) {
|
||||
off_t adpcm_offset = block_offset + block_header + 0x04;
|
||||
vgmstream->ch[i].adpcm_step_index = read_32bit(adpcm_offset + i*0x04 + 0x00*vgmstream->channels, sf);
|
||||
vgmstream->ch[i].adpcm_history1_32 = read_32bit(adpcm_offset + i*0x04 + 0x04*vgmstream->channels, sf);
|
||||
vgmstream->ch[i].adpcm_step_index = read_s32(adpcm_offset + i*0x04 + 0x00*vgmstream->channels, sf);
|
||||
vgmstream->ch[i].adpcm_history1_32 = read_s32(adpcm_offset + i*0x04 + 0x04*vgmstream->channels, sf);
|
||||
vgmstream->ch[i].offset = adpcm_offset + 0x08*vgmstream->channels;
|
||||
}
|
||||
|
||||
|
|
|
@ -44,36 +44,9 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) {
|
|||
block_samples = 0; /* layout ignores this */
|
||||
}
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
/* "SCHl" start block, when decoding multi files pasted together */
|
||||
if (block_id == 0x5343486C) {
|
||||
switch(vgmstream->coding_type) {
|
||||
case coding_MPEG_custom:
|
||||
case coding_MPEG_layer1:
|
||||
case coding_MPEG_layer2:
|
||||
case coding_MPEG_layer3:
|
||||
case coding_MPEG_ealayer3:
|
||||
/* need to reset MPEG decoder to reset discards and trailing samples in the buffers */
|
||||
flush_mpeg(vgmstream->codec_data);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
/* padding between "SCEl" and next "SCHl" (when subfiles exist) */
|
||||
if (block_id == 0x00000000)
|
||||
block_size = 0x04;
|
||||
|
||||
/* guard against errors (happens in bad rips/endianness, observed max is vid ~0x20000) */
|
||||
if (block_size == 0x00 || block_size > 0xFFFFF || block_samples > 0xFFFF) {
|
||||
block_size = 0x04;
|
||||
block_samples = 0;
|
||||
}
|
||||
|
||||
/* "SCEl" end chunk should be 32b-aligned, fixes some multi-SCHl [ex. Need for Speed 2 (PC) .eam] */
|
||||
if (((block_offset + block_size) % 0x04) && block_id == 0x5343456C) {
|
||||
block_size += 0x04 - ((block_offset + block_size) % 0x04);
|
||||
if (block_id == 0x00000000 || block_id == 0xFFFFFFFF || block_id == 0x5343456C) { /* EOF */
|
||||
vgmstream->current_block_samples = -1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,9 @@ VGMSTREAM* init_vgmstream_ads(STREAMFILE* sf) {
|
|||
|
||||
|
||||
/* checks */
|
||||
if (!is_id32be(0x00,sf,"SShd"))
|
||||
goto fail;
|
||||
|
||||
/* .ads: actual extension
|
||||
* .ss2: demuxed videos (fake?)
|
||||
* .pcm: Taisho Mononoke Ibunroku (PS2)
|
||||
|
@ -23,20 +26,20 @@ VGMSTREAM* init_vgmstream_ads(STREAMFILE* sf) {
|
|||
if (!check_extensions(sf, "ads,ss2,pcm,adx,,800"))
|
||||
goto fail;
|
||||
|
||||
if (!is_id32be(0x00,sf,"SShd") &&
|
||||
!is_id32be(0x20,sf,"SSbd"))
|
||||
goto fail;
|
||||
if (read_32bitLE(0x04,sf) != 0x18 && /* standard header size */
|
||||
read_32bitLE(0x04,sf) != 0x20) /* True Fortune (PS2) */
|
||||
if (read_u32le(0x04,sf) != 0x18 && /* standard header size */
|
||||
read_u32le(0x04,sf) != 0x20 && /* True Fortune (PS2) */
|
||||
read_u32le(0x04,sf) != get_streamfile_size(sf) - 0x08) /* Katamari Damacy videos */
|
||||
goto fail;
|
||||
|
||||
if (!is_id32be(0x20,sf,"SSbd"))
|
||||
goto fail;
|
||||
|
||||
/* base values (a bit unorderly since devs hack ADS too much and detection is messy) */
|
||||
{
|
||||
codec = read_32bitLE(0x08,sf);
|
||||
sample_rate = read_32bitLE(0x0C,sf);
|
||||
channels = read_32bitLE(0x10,sf); /* up to 4 [Eve of Extinction (PS2)] */
|
||||
interleave = read_32bitLE(0x14,sf); /* set even when mono */
|
||||
codec = read_u32le(0x08,sf);
|
||||
sample_rate = read_s32le(0x0C,sf);
|
||||
channels = read_s32le(0x10,sf); /* up to 4 [Eve of Extinction (PS2)] */
|
||||
interleave = read_s32le(0x14,sf); /* set even when mono */
|
||||
|
||||
|
||||
switch(codec) {
|
||||
|
@ -61,7 +64,7 @@ VGMSTREAM* init_vgmstream_ads(STREAMFILE* sf) {
|
|||
|
||||
case 0x00: /* PCM16BE from official docs, probably never used */
|
||||
default:
|
||||
VGM_LOG("ADS: unknown codec\n");
|
||||
vgm_logi("ADS: unknown codec\n");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
@ -127,8 +130,8 @@ VGMSTREAM* init_vgmstream_ads(STREAMFILE* sf) {
|
|||
{
|
||||
uint32_t loop_start, loop_end;
|
||||
|
||||
loop_start = read_32bitLE(0x18,sf);
|
||||
loop_end = read_32bitLE(0x1C,sf);
|
||||
loop_start = read_u32le(0x18,sf);
|
||||
loop_end = read_u32le(0x1C,sf);
|
||||
|
||||
loop_flag = 0;
|
||||
|
||||
|
@ -144,7 +147,7 @@ VGMSTREAM* init_vgmstream_ads(STREAMFILE* sf) {
|
|||
loop_start_offset = loop_start * 0x10;
|
||||
ignore_silent_frame_capcom = 1;
|
||||
}
|
||||
else if (read_32bitBE(0x28,sf) == 0x50414421) { /* "PAD!" padding until 0x800 */
|
||||
else if (is_id32be(0x28,sf, "PAD!")) { /* padding until 0x800 */
|
||||
/* Super Galdelic Hour: loop_start is PCM bytes */
|
||||
loop_flag = 1;
|
||||
loop_start_sample = loop_start / 2 / channels;
|
||||
|
|
|
@ -80,14 +80,14 @@ VGMSTREAM* init_vgmstream_adx_subkey(STREAMFILE* sf, uint16_t subkey) {
|
|||
/* encryption */
|
||||
if (version == 0x0408) {
|
||||
|
||||
if (find_adx_key(sf, 8, &xor_start, &xor_mult, &xor_add, 0)) {
|
||||
if (!find_adx_key(sf, 8, &xor_start, &xor_mult, &xor_add, 0)) {
|
||||
vgm_logi("ADX: decryption keystring not found\n");
|
||||
}
|
||||
coding_type = coding_CRI_ADX_enc_8;
|
||||
version = 0x0400;
|
||||
}
|
||||
else if (version == 0x0409) {
|
||||
if (find_adx_key(sf, 9, &xor_start, &xor_mult, &xor_add, subkey)) {
|
||||
if (!find_adx_key(sf, 9, &xor_start, &xor_mult, &xor_add, subkey)) {
|
||||
vgm_logi("ADX: decryption keycode not found\n");
|
||||
}
|
||||
coding_type = coding_CRI_ADX_enc_9;
|
||||
|
|
|
@ -6,23 +6,24 @@ VGMSTREAM* init_vgmstream_aif_asobo(STREAMFILE* sf) {
|
|||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
size_t data_size;
|
||||
int loop_flag, channel_count;
|
||||
int loop_flag, channels, sample_rate;
|
||||
|
||||
|
||||
/* checks */
|
||||
/* aif: standard, .laif/aiffl: for plugins */
|
||||
if ( !check_extensions(sf,"aif,laif,aiffl") )
|
||||
if (read_u16le(0x00,sf) != 0x69) /* fmt chunk with Xbox codec */
|
||||
goto fail;
|
||||
if ((uint16_t)read_16bitLE(0x00,sf) != 0x69) /* Xbox codec */
|
||||
/* aif: standard, .laif: for plugins */
|
||||
if ( !check_extensions(sf,"aif,laif") )
|
||||
goto fail;
|
||||
|
||||
channel_count = read_16bitLE(0x02,sf); /* assumed, only stereo is known */
|
||||
if (channel_count != 2) goto fail;
|
||||
channels = read_u16le(0x02,sf); /* assumed, only stereo is known */
|
||||
if (channels != 2) goto fail;
|
||||
|
||||
/* 0x08: ? */
|
||||
if ((uint16_t)read_16bitLE(0x0c,sf) != 0x24*channel_count) /* Xbox block */
|
||||
sample_rate = read_u32le(0x04,sf);
|
||||
/* 0x08: bitrate */
|
||||
if (read_u16le(0x0c,sf) != 0x24 * channels) /* Xbox block */
|
||||
goto fail;
|
||||
if ((uint16_t)read_16bitLE(0x0e,sf) != 0x04) /* Xbox bps */
|
||||
if (read_u16le(0x0e,sf) != 0x04) /* Xbox bps */
|
||||
goto fail;
|
||||
loop_flag = 0;
|
||||
|
||||
|
@ -31,17 +32,17 @@ VGMSTREAM* init_vgmstream_aif_asobo(STREAMFILE* sf) {
|
|||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_AIF_ASOBO;
|
||||
vgmstream->sample_rate = read_32bitLE(0x04,sf);
|
||||
vgmstream->num_samples = xbox_ima_bytes_to_samples(data_size,channel_count);
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = xbox_ima_bytes_to_samples(data_size, channels);
|
||||
|
||||
vgmstream->coding_type = coding_XBOX_IMA;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
if ( !vgmstream_open_stream(vgmstream, sf, start_offset) )
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
|
|
|
@ -81,9 +81,11 @@ VGMSTREAM* init_vgmstream_aifc(STREAMFILE* sf) {
|
|||
|
||||
|
||||
/* checks */
|
||||
if (!is_id32be(0x00,sf, "FORM"))
|
||||
goto fail;
|
||||
|
||||
/* .aif: common (AIFF or AIFC), .aiff: common AIFF, .aifc: common AIFC
|
||||
* .laif/laifc/laiff: for plugins
|
||||
* .aifcl/aiffl: for plugins?
|
||||
* .laif/laiff/laifc: for plugins
|
||||
* .cbd2: M2 games
|
||||
* .bgm: Super Street Fighter II Turbo (3DO)
|
||||
* .acm: Crusader - No Remorse (SAT)
|
||||
|
@ -91,15 +93,17 @@ VGMSTREAM* init_vgmstream_aifc(STREAMFILE* sf) {
|
|||
* .ai: Dragon Force (SAT)
|
||||
* (extensionless: Doom (3DO)
|
||||
* .fda: Homeworld 2 (PC)
|
||||
* .n64: Turok (N64) src */
|
||||
* .n64: Turok (N64) src
|
||||
* .pcm: Road Rash (SAT)
|
||||
*/
|
||||
if (check_extensions(sf, "aif,laif,")) {
|
||||
is_aifc_ext = 1;
|
||||
is_aiff_ext = 1;
|
||||
}
|
||||
else if (check_extensions(sf, "aifc,laifc,aifcl,afc,cbd2,bgm,fda,n64")) {
|
||||
else if (check_extensions(sf, "aifc,laifc,afc,cbd2,bgm,fda,n64")) {
|
||||
is_aifc_ext = 1;
|
||||
}
|
||||
else if (check_extensions(sf, "aiff,laiff,acm,adp,ai,aiffl")) {
|
||||
else if (check_extensions(sf, "aiff,laiff,acm,adp,ai,pcm")) {
|
||||
is_aiff_ext = 1;
|
||||
}
|
||||
else {
|
||||
|
@ -107,17 +111,16 @@ VGMSTREAM* init_vgmstream_aifc(STREAMFILE* sf) {
|
|||
}
|
||||
|
||||
file_size = get_streamfile_size(sf);
|
||||
if (read_u32be(0x00,sf) != 0x464F524D && /* "FORM" */
|
||||
read_u32be(0x04,sf)+0x08 != file_size)
|
||||
if (read_u32be(0x04,sf) + 0x08 != file_size)
|
||||
goto fail;
|
||||
|
||||
/* AIFF originally allowed only PCM (non-compressed) audio, so newer AIFC was added,
|
||||
* though some AIFF with other codecs exist */
|
||||
if (read_u32be(0x08,sf) == 0x41494643) { /* "AIFC" */
|
||||
if (is_id32be(0x08,sf, "AIFC")) {
|
||||
if (!is_aifc_ext) goto fail;
|
||||
is_aifc = 1;
|
||||
}
|
||||
else if (read_u32be(0x08,sf) == 0x41494646) { /* "AIFF" */
|
||||
else if (is_id32be(0x08,sf, "AIFF")) {
|
||||
if (!is_aiff_ext) goto fail;
|
||||
is_aiff = 1;
|
||||
}
|
||||
|
@ -160,9 +163,7 @@ VGMSTREAM* init_vgmstream_aifc(STREAMFILE* sf) {
|
|||
if (comm_found) goto fail;
|
||||
comm_found = 1;
|
||||
|
||||
channels = read_u16be(offset + 0x00,sf);
|
||||
if (channels <= 0) goto fail;
|
||||
|
||||
channels = read_u16be(offset + 0x00,sf);
|
||||
sample_count = read_u32be(offset + 0x02,sf); /* sample_frames in theory, depends on codec */
|
||||
sample_size = read_u16be(offset + 0x06,sf);
|
||||
sample_rate = read_f80be(offset + 0x08,sf);
|
||||
|
@ -233,7 +234,7 @@ VGMSTREAM* init_vgmstream_aifc(STREAMFILE* sf) {
|
|||
coding_type = coding_PCM16BE;
|
||||
interleave = 2;
|
||||
break;
|
||||
case 4: /* Crusader: No Remorse (SAT), Road Rash (3DO) */
|
||||
case 4: /* Crusader: No Remorse (SAT), Road Rash (3DO/SAT) */
|
||||
coding_type = coding_XA;
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* AST - from Marvelous(?) games [Katekyou Hitman Reborn! Dream Hyper Battle! (PS2), Binchou-tan: Shiawasegoyomi (PS2)] */
|
||||
VGMSTREAM* init_vgmstream_ast_mmv(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
uint32_t start_offset, data_size;
|
||||
int loop_flag, channels, sample_rate, interleave;
|
||||
|
||||
/* checks */
|
||||
if (!is_id32be(0x00,sf, "AST\0"))
|
||||
goto fail;
|
||||
|
||||
/* .ast: from executables (usually found in bigfiles) */
|
||||
if (!check_extensions(sf,"ast"))
|
||||
goto fail;
|
||||
|
||||
data_size = read_u32le(0x04, sf);
|
||||
if (data_size != get_streamfile_size(sf))
|
||||
goto fail;
|
||||
|
||||
sample_rate = read_s32le(0x08,sf);
|
||||
channels = read_32bitLE(0x0C,sf);
|
||||
interleave = read_u32le(0x10,sf);
|
||||
/* 0x14: number of blocks */
|
||||
/* 0x18: ? (not fully related to size/time) */
|
||||
/* 0x1c: f32 time */
|
||||
loop_flag = 0;
|
||||
start_offset = 0x100;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_AST_MMV;
|
||||
|
||||
vgmstream->num_samples = ps_bytes_to_samples(data_size - start_offset, channels);
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->interleave_block_size = interleave;
|
||||
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
|
||||
read_string(vgmstream->stream_name,0x20, 0x20,sf);
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,sf,start_offset))
|
||||
goto fail;
|
||||
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* AST - from MicroVision lib games [P.T.O. IV (PS2), Naval Ops: Warship Gunner (PS2)] */
|
||||
VGMSTREAM* init_vgmstream_ast_mv(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
uint32_t start_offset, data_size, check;
|
||||
int loop_flag, channels, interleave, sample_rate;
|
||||
|
||||
/* checks */
|
||||
if (!is_id32be(0x00,sf, "AST\0"))
|
||||
goto fail;
|
||||
|
||||
if (!check_extensions(sf,"ast"))
|
||||
goto fail;
|
||||
|
||||
channels = 2;
|
||||
sample_rate = read_s32le(0x04, sf);
|
||||
interleave = read_u32le(0x08,sf);
|
||||
data_size = read_u32le(0x0c,sf); /* may have garbage */
|
||||
check = read_u32be(0x10, sf);
|
||||
/* rest: null/garbage */
|
||||
|
||||
loop_flag = 0;
|
||||
start_offset = 0x800;
|
||||
|
||||
/* there is a variation in .ikm (Zwei), with loops and different start offset */
|
||||
if (check != 0x20002000 && /* NO:WG (garbage up to start) */
|
||||
check != 0x00000000) /* PTO4 */
|
||||
goto fail;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = ps_bytes_to_samples(data_size - start_offset, channels);
|
||||
vgmstream->interleave_block_size = interleave;
|
||||
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->meta_type = meta_AST_MV;
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
|
@ -1,35 +1,34 @@
|
|||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
/* .BAF - Bizarre Creations bank file [Blur (PS3), Project Gotham Racing 4 (X360), Geometry Wars (PC)] */
|
||||
VGMSTREAM * init_vgmstream_baf(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * init_vgmstream_baf(STREAMFILE *sf) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset, header_offset, name_offset;
|
||||
size_t stream_size;
|
||||
int loop_flag, channel_count, sample_rate, version, codec, tracks;
|
||||
int total_subsongs, target_subsong = streamFile->stream_index;
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*);
|
||||
|
||||
uint32_t channel_count, sample_rate, num_samples, version, codec, tracks;
|
||||
int loop_flag, total_subsongs, target_subsong = sf->stream_index;
|
||||
uint32_t (*read_u32)(off_t,STREAMFILE*);
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile, "baf"))
|
||||
if (!is_id32be(0x00, sf, "BANK"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00,streamFile) != 0x42414E4B) /* "BANK" */
|
||||
|
||||
if (!check_extensions(sf, "baf"))
|
||||
goto fail;
|
||||
|
||||
/* use BANK size to check endianness */
|
||||
if (guess_endianness32bit(0x04,streamFile)) {
|
||||
read_32bit = read_32bitBE;
|
||||
if (guess_endianness32bit(0x04,sf)) {
|
||||
read_u32 = read_u32be;
|
||||
} else {
|
||||
read_32bit = read_32bitLE;
|
||||
read_u32 = read_u32le;
|
||||
}
|
||||
|
||||
/* 0x04: bank size */
|
||||
version = read_32bit(0x08,streamFile);
|
||||
version = read_u32(0x08,sf);
|
||||
if (version != 0x03 && version != 0x04 && version != 0x05)
|
||||
goto fail;
|
||||
total_subsongs = read_32bit(0x0c,streamFile);
|
||||
total_subsongs = read_u32(0x0c,sf);
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
|
||||
/* - in v3 */
|
||||
|
@ -43,83 +42,96 @@ VGMSTREAM * init_vgmstream_baf(STREAMFILE *streamFile) {
|
|||
/* find target WAVE chunk */
|
||||
{
|
||||
int i;
|
||||
off_t offset = read_32bit(0x04, streamFile);
|
||||
off_t offset = read_u32(0x04, sf);
|
||||
|
||||
for (i = 0; i < total_subsongs; i++) {
|
||||
if (i+1 == target_subsong)
|
||||
break;
|
||||
offset += read_32bit(offset+0x04, streamFile); /* WAVE size, variable per codec */
|
||||
offset += read_u32(offset+0x04, sf); /* WAVE size, variable per codec */
|
||||
|
||||
/* skip companion "CUE " (found in 007: Blood Stone, contains segment cues) */
|
||||
if (read_32bitBE(offset+0x00, streamFile) == 0x43554520) {
|
||||
offset += read_32bit(offset+0x04, streamFile); /* CUE size */
|
||||
if (is_id32be(offset+0x00, sf, "CUE ")) {
|
||||
offset += read_u32(offset+0x04, sf); /* CUE size */
|
||||
}
|
||||
}
|
||||
header_offset = offset;
|
||||
}
|
||||
|
||||
/* parse header */
|
||||
if (read_32bitBE(header_offset+0x00, streamFile) != 0x57415645) /* "WAVE" */
|
||||
if (!is_id32be(header_offset+0x00, sf, "WAVE"))
|
||||
goto fail;
|
||||
codec = read_32bit(header_offset+0x08, streamFile);
|
||||
codec = read_u32(header_offset+0x08, sf);
|
||||
name_offset = header_offset + 0x0c;
|
||||
start_offset = read_32bit(header_offset+0x2c, streamFile);
|
||||
stream_size = read_32bit(header_offset+0x30, streamFile);
|
||||
start_offset = read_u32(header_offset+0x2c, sf);
|
||||
stream_size = read_u32(header_offset+0x30, sf);
|
||||
tracks = 0;
|
||||
|
||||
switch(codec) {
|
||||
case 0x03:
|
||||
case 0x03: /* PCM16LE */
|
||||
switch(version) {
|
||||
case 0x03: /* Geometry Wars (PC) */
|
||||
sample_rate = read_32bit(header_offset+0x38, streamFile);
|
||||
channel_count = read_32bit(header_offset+0x40, streamFile);
|
||||
sample_rate = read_u32(header_offset+0x38, sf);
|
||||
channel_count = read_u32(header_offset+0x40, sf);
|
||||
/* no actual flag, just loop +15sec songs */
|
||||
loop_flag = (pcm_bytes_to_samples(stream_size, channel_count, 16) > 15*sample_rate);
|
||||
break;
|
||||
|
||||
case 0x04: /* Project Gotham Racing 4 (X360) */
|
||||
sample_rate = read_32bit(header_offset+0x3c, streamFile);
|
||||
channel_count = read_32bit(header_offset+0x44, streamFile);
|
||||
loop_flag = read_8bit(header_offset+0x4b, streamFile);
|
||||
sample_rate = read_u32(header_offset+0x3c, sf);
|
||||
channel_count = read_u32(header_offset+0x44, sf);
|
||||
loop_flag = read_u8(header_offset+0x4b, sf);
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
num_samples = pcm_bytes_to_samples(stream_size, channel_count, 16);
|
||||
break;
|
||||
|
||||
case 0x07:
|
||||
switch(version) {
|
||||
case 0x07: /* PSX ADPCM (0x21 frame size) */
|
||||
if (version == 0x04 && read_u32(header_offset + 0x3c, sf) != 0) {
|
||||
/* Blur (Prototype) (PS3) */
|
||||
sample_rate = read_u32(header_offset+0x3c, sf);
|
||||
channel_count = read_u32(header_offset+0x44, sf);
|
||||
loop_flag = read_u8(header_offset+0x4b, sf);
|
||||
|
||||
/* mini-header at the start of the stream */
|
||||
num_samples = read_u32le(start_offset+0x04, sf) / 0x02; /* PCM size? */
|
||||
start_offset += read_u32le(start_offset+0x00, sf);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (version) {
|
||||
case 0x04: /* Blur (PS3) */
|
||||
case 0x05: /* James Bond 007: Blood Stone (X360) */
|
||||
sample_rate = read_32bit(header_offset+0x40, streamFile);
|
||||
loop_flag = read_8bit(header_offset+0x48, streamFile);
|
||||
tracks = read_8bit(header_offset+0x49, streamFile);
|
||||
channel_count = read_8bit(header_offset+0x4b, streamFile);
|
||||
sample_rate = read_u32(header_offset+0x40, sf);
|
||||
num_samples = read_u32(header_offset+0x44, sf);
|
||||
loop_flag = read_u8(header_offset+0x48, sf);
|
||||
tracks = read_u8(header_offset+0x49, sf);
|
||||
channel_count = read_u8(header_offset+0x4b, sf);
|
||||
|
||||
if (tracks) {
|
||||
channel_count = channel_count * tracks;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case 0x08:
|
||||
case 0x08: /* XMA1 */
|
||||
switch(version) {
|
||||
case 0x04: /* Project Gotham Racing (X360) */
|
||||
sample_rate = read_32bit(header_offset+0x3c, streamFile);
|
||||
channel_count = read_32bit(header_offset+0x44, streamFile);
|
||||
loop_flag = read_8bit(header_offset+0x54, streamFile) != 0;
|
||||
sample_rate = read_u32(header_offset+0x3c, sf);
|
||||
channel_count = read_u32(header_offset+0x44, sf);
|
||||
loop_flag = read_u8(header_offset+0x54, sf) != 0;
|
||||
break;
|
||||
|
||||
case 0x05: /* James Bond 007: Blood Stone (X360) */
|
||||
sample_rate = read_32bit(header_offset+0x40, streamFile);
|
||||
channel_count = read_32bit(header_offset+0x48, streamFile);
|
||||
loop_flag = read_8bit(header_offset+0x58, streamFile) != 0;
|
||||
sample_rate = read_u32(header_offset+0x40, sf);
|
||||
channel_count = read_u32(header_offset+0x48, sf);
|
||||
loop_flag = read_u8(header_offset+0x58, sf) != 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -150,9 +162,9 @@ VGMSTREAM * init_vgmstream_baf(STREAMFILE *streamFile) {
|
|||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x02;
|
||||
|
||||
vgmstream->num_samples = pcm_bytes_to_samples(stream_size, channel_count, 16);
|
||||
vgmstream->num_samples = num_samples;
|
||||
vgmstream->loop_start_sample = 0;
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||
vgmstream->loop_end_sample = num_samples;
|
||||
break;
|
||||
|
||||
case 0x07:
|
||||
|
@ -160,9 +172,9 @@ VGMSTREAM * init_vgmstream_baf(STREAMFILE *streamFile) {
|
|||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x21;
|
||||
|
||||
vgmstream->num_samples = read_32bit(header_offset+0x44, streamFile);
|
||||
vgmstream->num_samples = num_samples;
|
||||
vgmstream->loop_start_sample = 0;
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||
vgmstream->loop_end_sample = num_samples;
|
||||
break;
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
|
@ -170,8 +182,8 @@ VGMSTREAM * init_vgmstream_baf(STREAMFILE *streamFile) {
|
|||
uint8_t buf[0x100];
|
||||
int bytes;
|
||||
|
||||
bytes = ffmpeg_make_riff_xma1(buf,0x100, vgmstream->num_samples, stream_size, vgmstream->channels, vgmstream->sample_rate, 0);
|
||||
vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,stream_size);
|
||||
bytes = ffmpeg_make_riff_xma1(buf,0x100, 0, stream_size, vgmstream->channels, vgmstream->sample_rate, 0);
|
||||
vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, start_offset,stream_size);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
@ -185,18 +197,18 @@ VGMSTREAM * init_vgmstream_baf(STREAMFILE *streamFile) {
|
|||
msd.data_offset = start_offset;
|
||||
msd.data_size = stream_size;
|
||||
msd.loop_flag = loop_flag;
|
||||
msd.loop_start_b = read_32bit(header_offset+0x4c, streamFile);
|
||||
msd.loop_end_b = read_32bit(header_offset+0x50, streamFile);
|
||||
msd.loop_start_subframe = (read_8bit(header_offset+0x55, streamFile) >> 0) & 0x0f;
|
||||
msd.loop_end_subframe = (read_8bit(header_offset+0x55, streamFile) >> 4) & 0x0f;
|
||||
xma_get_samples(&msd, streamFile);
|
||||
msd.loop_start_b = read_u32(header_offset+0x4c, sf);
|
||||
msd.loop_end_b = read_u32(header_offset+0x50, sf);
|
||||
msd.loop_start_subframe = (read_u8(header_offset+0x55, sf) >> 0) & 0x0f;
|
||||
msd.loop_end_subframe = (read_u8(header_offset+0x55, sf) >> 4) & 0x0f;
|
||||
xma_get_samples(&msd, sf);
|
||||
|
||||
vgmstream->num_samples = msd.num_samples; /* also at 0x58, but unreliable? */
|
||||
vgmstream->loop_start_sample = msd.loop_start_sample;
|
||||
vgmstream->loop_end_sample = msd.loop_end_sample;
|
||||
}
|
||||
|
||||
xma_fix_raw_samples_ch(vgmstream, streamFile, start_offset, stream_size, channel_count, 1,1);
|
||||
xma_fix_raw_samples_ch(vgmstream, sf, start_offset, stream_size, channel_count, 1,1);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
@ -206,10 +218,10 @@ VGMSTREAM * init_vgmstream_baf(STREAMFILE *streamFile) {
|
|||
goto fail;
|
||||
}
|
||||
|
||||
read_string(vgmstream->stream_name,0x20+1, name_offset,streamFile);
|
||||
read_string(vgmstream->stream_name,0x20+1, name_offset,sf);
|
||||
|
||||
|
||||
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
|
@ -264,7 +276,7 @@ VGMSTREAM * init_vgmstream_baf_badrip(STREAMFILE *streamFile) {
|
|||
vgmstream->interleave_block_size = frame_size;
|
||||
vgmstream->meta_type = meta_BAF;
|
||||
|
||||
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
|
||||
if (!vgmstream_open_stream(vgmstream, streamFile, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ typedef struct _BARSTREAMFILE {
|
|||
/*static*/ STREAMFILE *wrap_bar_STREAMFILE(STREAMFILE *file);
|
||||
|
||||
|
||||
static size_t read_bar(BARSTREAMFILE *streamFile, uint8_t *dest, off_t offset, size_t length) {
|
||||
static size_t read_bar(BARSTREAMFILE *streamFile, uint8_t *dest, offv_t offset, size_t length) {
|
||||
off_t i;
|
||||
size_t read_length = streamFile->real_file->read(streamFile->real_file, dest, offset, length);
|
||||
|
||||
|
@ -36,7 +36,7 @@ static size_t get_size_bar(BARSTREAMFILE *streamFile) {
|
|||
return streamFile->real_file->get_size(streamFile->real_file);
|
||||
}
|
||||
|
||||
static size_t get_offset_bar(BARSTREAMFILE *streamFile) {
|
||||
static offv_t get_offset_bar(BARSTREAMFILE *streamFile) {
|
||||
return streamFile->real_file->get_offset(streamFile->real_file);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,41 +1,38 @@
|
|||
#include <math.h>
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "../util/endianness.h"
|
||||
|
||||
typedef enum { PSX, PCM16, ATRAC9, HEVAG } bnk_codec;
|
||||
|
||||
/* .BNK - Sony's Scream Tool bank format [Puyo Puyo Tetris (PS4), NekoBuro: Cats Block (Vita)] */
|
||||
VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset, stream_offset, name_offset = 0;
|
||||
size_t stream_size, interleave = 0;
|
||||
off_t sblk_offset, data_offset;
|
||||
size_t data_size;
|
||||
int channel_count = 0, loop_flag, sample_rate, parts, sblk_version, big_endian;
|
||||
uint32_t start_offset, stream_offset, name_offset = 0;
|
||||
uint32_t sblk_offset, data_offset;
|
||||
uint32_t stream_size, data_size, interleave = 0;
|
||||
int channels = 0, loop_flag, sample_rate, parts, sblk_version, big_endian;
|
||||
int loop_start = 0, loop_end = 0;
|
||||
uint32_t pitch, flags;
|
||||
uint32_t atrac9_info = 0;
|
||||
|
||||
int total_subsongs, target_subsong = sf->stream_index;
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
|
||||
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
|
||||
float (*read_f32)(off_t,STREAMFILE*) = NULL;
|
||||
read_u16_t read_u16;
|
||||
read_u32_t read_u32;
|
||||
read_f32_t read_f32;
|
||||
bnk_codec codec;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf, "bnk"))
|
||||
goto fail;
|
||||
|
||||
/* bnk version */
|
||||
if (read_32bitBE(0x00,sf) == 0x00000003) { /* PS3 */
|
||||
read_32bit = read_32bitBE;
|
||||
read_16bit = read_16bitBE;
|
||||
if (read_u32be(0x00,sf) == 0x03) { /* PS3 */
|
||||
read_u32 = read_u32be;
|
||||
read_u16 = read_u16be;
|
||||
read_f32 = read_f32be;
|
||||
big_endian = 1;
|
||||
}
|
||||
else if (read_32bitBE(0x00,sf) == 0x03000000) { /* PS2/Vita/PS4 */
|
||||
read_32bit = read_32bitLE;
|
||||
read_16bit = read_16bitLE;
|
||||
else if (read_u32le(0x00,sf) == 0x03) { /* PS2/Vita/PS4 */
|
||||
read_u32 = read_u32le;
|
||||
read_u16 = read_u16le;
|
||||
read_f32 = read_f32le;
|
||||
big_endian = 0;
|
||||
}
|
||||
|
@ -43,13 +40,18 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
|
|||
goto fail;
|
||||
}
|
||||
|
||||
parts = read_32bit(0x04,sf);
|
||||
/* checks */
|
||||
if (!check_extensions(sf, "bnk"))
|
||||
goto fail;
|
||||
|
||||
|
||||
parts = read_u32(0x04,sf);
|
||||
if (parts < 2 || parts > 3) goto fail;
|
||||
|
||||
sblk_offset = read_32bit(0x08,sf);
|
||||
sblk_offset = read_u32(0x08,sf);
|
||||
/* 0x0c: sklb size */
|
||||
data_offset = read_32bit(0x10,sf);
|
||||
data_size = read_32bit(0x14,sf);
|
||||
data_offset = read_u32(0x10,sf);
|
||||
data_size = read_u32(0x14,sf);
|
||||
/* when sblk_offset >= 0x20: */
|
||||
/* 0x18: ZLSD small footer, rare [Yakuza 6's Puyo Puyo (PS4)] */
|
||||
/* 0x1c: ZLSD size */
|
||||
|
@ -60,30 +62,30 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
|
|||
|
||||
|
||||
/* SBlk part: parse header */
|
||||
if (read_32bit(sblk_offset+0x00,sf) != 0x6B6C4253) /* "klBS" (SBlk = sample block?) */
|
||||
if (read_u32(sblk_offset+0x00,sf) != get_id32be("klBS")) /* SBlk = sample block? */
|
||||
goto fail;
|
||||
sblk_version = read_32bit(sblk_offset+0x04,sf);
|
||||
sblk_version = read_u32(sblk_offset+0x04,sf);
|
||||
/* 0x08: possibly when sblk_version=0x0d, 0x03=Vita, 0x06=PS4 */
|
||||
//;VGM_LOG("BNK: sblk_offset=%lx, data_offset=%lx, sblk_version %x\n", sblk_offset, data_offset, sblk_version);
|
||||
|
||||
{
|
||||
int i;
|
||||
off_t table1_offset, table2_offset, table3_offset, table4_offset;
|
||||
size_t section_entries, material_entries, stream_entries;
|
||||
size_t table1_entry_size;
|
||||
off_t table1_suboffset, table2_suboffset;
|
||||
off_t table2_entry_offset = 0, table3_entry_offset = 0;
|
||||
uint32_t table1_offset, table2_offset, table3_offset, table4_offset;
|
||||
uint32_t section_entries, material_entries, stream_entries;
|
||||
uint32_t table1_entry_size;
|
||||
uint32_t table1_suboffset, table2_suboffset;
|
||||
uint32_t table2_entry_offset = 0, table3_entry_offset = 0;
|
||||
int table4_entry_id = -1;
|
||||
off_t table4_entries_offset, table4_names_offset;
|
||||
uint32_t table4_entries_offset, table4_names_offset;
|
||||
|
||||
|
||||
switch(sblk_version) {
|
||||
case 0x01: /* Ratchet & Clank (PS2) */
|
||||
section_entries = (uint16_t)read_16bit(sblk_offset+0x16,sf); /* entry size: ~0x0c */
|
||||
material_entries = (uint16_t)read_16bit(sblk_offset+0x18,sf); /* entry size: ~0x28 */
|
||||
stream_entries = (uint16_t)read_16bit(sblk_offset+0x1a,sf); /* entry size: none (count) */
|
||||
table1_offset = sblk_offset + read_32bit(sblk_offset+0x1c,sf);
|
||||
table2_offset = sblk_offset + read_32bit(sblk_offset+0x20,sf);
|
||||
section_entries = read_u16(sblk_offset+0x16,sf); /* entry size: ~0x0c */
|
||||
material_entries = read_u16(sblk_offset+0x18,sf); /* entry size: ~0x28 */
|
||||
stream_entries = read_u16(sblk_offset+0x1a,sf); /* entry size: none (count) */
|
||||
table1_offset = sblk_offset + read_u32(sblk_offset+0x1c,sf);
|
||||
table2_offset = sblk_offset + read_u32(sblk_offset+0x20,sf);
|
||||
table3_offset = table2_offset; /* mixed table in this version */
|
||||
table4_offset = 0; /* not included */
|
||||
|
||||
|
@ -95,14 +97,15 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
|
|||
case 0x03: /* Yu-Gi-Oh! GX - The Beginning of Destiny (PS2) */
|
||||
case 0x04: /* Test banks */
|
||||
case 0x05: /* Ratchet & Clank (PS3) */
|
||||
case 0x08: /* Playstation Home Arcade (Vita) */
|
||||
case 0x09: /* Puyo Puyo Tetris (PS4) */
|
||||
section_entries = (uint16_t)read_16bit(sblk_offset+0x16,sf); /* entry size: ~0x0c */
|
||||
material_entries = (uint16_t)read_16bit(sblk_offset+0x18,sf); /* entry size: ~0x08 */
|
||||
stream_entries = (uint16_t)read_16bit(sblk_offset+0x1a,sf); /* entry size: ~0x18 + variable */
|
||||
table1_offset = sblk_offset + read_32bit(sblk_offset+0x1c,sf);
|
||||
table2_offset = sblk_offset + read_32bit(sblk_offset+0x20,sf);
|
||||
table3_offset = sblk_offset + read_32bit(sblk_offset+0x34,sf);
|
||||
table4_offset = sblk_offset + read_32bit(sblk_offset+0x38,sf);
|
||||
section_entries = read_u16(sblk_offset+0x16,sf); /* entry size: ~0x0c */
|
||||
material_entries = read_u16(sblk_offset+0x18,sf); /* entry size: ~0x08 */
|
||||
stream_entries = read_u16(sblk_offset+0x1a,sf); /* entry size: ~0x18 + variable */
|
||||
table1_offset = sblk_offset + read_u32(sblk_offset+0x1c,sf);
|
||||
table2_offset = sblk_offset + read_u32(sblk_offset+0x20,sf);
|
||||
table3_offset = sblk_offset + read_u32(sblk_offset+0x34,sf);
|
||||
table4_offset = sblk_offset + read_u32(sblk_offset+0x38,sf);
|
||||
|
||||
table1_entry_size = 0x0c;
|
||||
table1_suboffset = 0x08;
|
||||
|
@ -111,13 +114,13 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
|
|||
|
||||
case 0x0d: /* Polara (Vita), Crypt of the Necrodancer (Vita) */
|
||||
case 0x0e: /* Yakuza 6's Puyo Puyo (PS4) */
|
||||
table1_offset = sblk_offset + read_32bit(sblk_offset+0x18,sf);
|
||||
table2_offset = sblk_offset + read_32bit(sblk_offset+0x1c,sf);
|
||||
table3_offset = sblk_offset + read_32bit(sblk_offset+0x2c,sf);
|
||||
table4_offset = sblk_offset + read_32bit(sblk_offset+0x30,sf);
|
||||
section_entries = (uint16_t)read_16bit(sblk_offset+0x38,sf); /* entry size: ~0x24 */
|
||||
material_entries = (uint16_t)read_16bit(sblk_offset+0x3a,sf); /* entry size: ~0x08 */
|
||||
stream_entries = (uint16_t)read_16bit(sblk_offset+0x3c,sf); /* entry size: ~0x5c + variable */
|
||||
table1_offset = sblk_offset + read_u32(sblk_offset+0x18,sf);
|
||||
table2_offset = sblk_offset + read_u32(sblk_offset+0x1c,sf);
|
||||
table3_offset = sblk_offset + read_u32(sblk_offset+0x2c,sf);
|
||||
table4_offset = sblk_offset + read_u32(sblk_offset+0x30,sf);
|
||||
section_entries = read_u16(sblk_offset+0x38,sf); /* entry size: ~0x24 */
|
||||
material_entries = read_u16(sblk_offset+0x3a,sf); /* entry size: ~0x08 */
|
||||
stream_entries = read_u16(sblk_offset+0x3c,sf); /* entry size: ~0x5c + variable */
|
||||
|
||||
table1_entry_size = 0x24;
|
||||
table1_suboffset = 0x0c;
|
||||
|
@ -125,11 +128,11 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
|
|||
break;
|
||||
|
||||
default:
|
||||
VGM_LOG("BNK: unknown version %x\n", sblk_version);
|
||||
vgm_logi("BNK: unknown version %x (report)\n", sblk_version);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
//;VGM_LOG("BNK: table offsets=%lx, %lx, %lx, %lx\n", table1_offset,table2_offset,table3_offset,table4_offset);
|
||||
//;VGM_LOG("BNK: table offsets=%x, %x, %x, %x\n", table1_offset,table2_offset,table3_offset,table4_offset);
|
||||
//;VGM_LOG("BNK: table entries=%i, %i, %i\n", section_entries,material_entries,stream_entries);
|
||||
|
||||
|
||||
|
@ -159,7 +162,7 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
|
|||
*/
|
||||
|
||||
for (i = 0; i < material_entries; i++) {
|
||||
uint8_t table2_type = read_32bit(table2_offset + (i*0x28) + 0x00, sf);
|
||||
uint32_t table2_type = read_u32(table2_offset + (i*0x28) + 0x00, sf);
|
||||
|
||||
if (table2_type != 0x01)
|
||||
continue;
|
||||
|
@ -179,7 +182,7 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
|
|||
for (i = 0; i < material_entries; i++) {
|
||||
uint32_t table2_value, table2_subinfo, table2_subtype;
|
||||
|
||||
table2_value = (uint32_t)read_32bit(table2_offset+(i*0x08)+table2_suboffset+0x00,sf);
|
||||
table2_value = read_u32(table2_offset+(i*0x08)+table2_suboffset+0x00,sf);
|
||||
table2_subinfo = (table2_value >> 0) & 0xFFFF;
|
||||
table2_subtype = (table2_value >> 16) & 0xFFFF;
|
||||
if (table2_subtype != 0x100)
|
||||
|
@ -214,54 +217,31 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
|
|||
case 0x03:
|
||||
case 0x04:
|
||||
case 0x05:
|
||||
case 0x08:
|
||||
case 0x09:
|
||||
pitch = (uint8_t)read_8bit(table3_offset+table3_entry_offset+0x02,sf);
|
||||
flags = (uint8_t)read_8bit(table3_offset+table3_entry_offset+0x0f,sf);
|
||||
stream_offset = read_32bit(table3_offset+table3_entry_offset+0x10,sf);
|
||||
stream_size = read_32bit(table3_offset+table3_entry_offset+0x14,sf);
|
||||
pitch = read_u8(table3_offset+table3_entry_offset+0x02,sf);
|
||||
flags = read_u8(table3_offset+table3_entry_offset+0x0f,sf);
|
||||
stream_offset = read_u32(table3_offset+table3_entry_offset+0x10,sf);
|
||||
stream_size = read_u32(table3_offset+table3_entry_offset+0x14,sf);
|
||||
|
||||
/* must use some log/formula but whatevs */
|
||||
switch(pitch) {
|
||||
case 0xC6: sample_rate = 50000; break; //?
|
||||
case 0xC4: sample_rate = 48000; break;
|
||||
case 0xC3: sample_rate = 46000; break; //?
|
||||
case 0xC2: sample_rate = 44100; break;
|
||||
case 0xBC: sample_rate = 36000; break; //?
|
||||
case 0xBA: sample_rate = 32000; break; //?
|
||||
case 0xB9: sample_rate = 30000; break; //?
|
||||
case 0xB8: sample_rate = 28000; break; //?
|
||||
case 0xB6: sample_rate = 22050; break;
|
||||
case 0xB4: sample_rate = 18000; break; //?
|
||||
case 0xB2: sample_rate = 16000; break; //?
|
||||
case 0xB0: sample_rate = 15000; break; //?
|
||||
case 0xAF: sample_rate = 14000; break; //?
|
||||
case 0xAE: sample_rate = 13000; break; //?
|
||||
case 0xAD: sample_rate = 12500; break; //?
|
||||
case 0xAC: sample_rate = 12000; break; //?
|
||||
case 0xAB: sample_rate = 11050; break; //?
|
||||
case 0xAA: sample_rate = 11025; break;
|
||||
case 0xA9: sample_rate = 10000; break; //?
|
||||
case 0xA8: sample_rate = 9000; break; //?
|
||||
case 0xA7: sample_rate = 8000; break; //?
|
||||
case 0xA6: sample_rate = 7000; break; //?
|
||||
case 0xA5: sample_rate = 6500; break; //?
|
||||
case 0xA4: sample_rate = 6000; break; //?
|
||||
case 0xA3: sample_rate = 5800; break; //?
|
||||
case 0xA2: sample_rate = 5400; break; //?
|
||||
case 0xA1: sample_rate = 5000; break; //?
|
||||
case 0x9d: sample_rate = 4000; break; //?
|
||||
case 0x9c: sample_rate = 3500; break; //?
|
||||
default:
|
||||
VGM_LOG("BNK: unknown pitch %x\n", pitch);
|
||||
goto fail;
|
||||
/* approximate note-to-hz, not sure about real formula (observed from 0x9a to 0xc6)
|
||||
* using (rate) * 2 ^ ((pitch - note) / 12) but not correct? (may be rounded) */
|
||||
sample_rate = 614.0 * pow(2.0, (float)(pitch - 0x70) / 12.0);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x0d:
|
||||
case 0x0e:
|
||||
flags = (uint8_t)read_8bit(table3_offset+table3_entry_offset+0x12,sf);
|
||||
stream_offset = read_32bit(table3_offset+table3_entry_offset+0x44,sf);
|
||||
stream_size = read_32bit(table3_offset+table3_entry_offset+0x48,sf);
|
||||
flags = read_u8 (table3_offset+table3_entry_offset+0x12,sf);
|
||||
stream_offset = read_u32(table3_offset+table3_entry_offset+0x44,sf);
|
||||
stream_size = read_u32(table3_offset+table3_entry_offset+0x48,sf);
|
||||
sample_rate = (int)read_f32(table3_offset+table3_entry_offset+0x4c,sf);
|
||||
break;
|
||||
|
||||
|
@ -275,12 +255,13 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
|
|||
switch(sblk_version) {
|
||||
//case 0x03: /* different format? */
|
||||
//case 0x04: /* different format? */
|
||||
case 0x08:
|
||||
case 0x09:
|
||||
case 0x0d:
|
||||
case 0x0e:
|
||||
/* find if this sound has an assigned name in table1 */
|
||||
for (i = 0; i < section_entries; i++) {
|
||||
off_t entry_offset = (uint16_t)read_16bit(table1_offset+(i*table1_entry_size)+table1_suboffset+0x00,sf);
|
||||
uint32_t entry_offset = read_u16(table1_offset+(i*table1_entry_size)+table1_suboffset+0x00,sf);
|
||||
|
||||
/* rarely (ex. Polara sfx) one name applies to multiple materials,
|
||||
* from current entry_offset to next entry_offset (section offsets should be in order) */
|
||||
|
@ -296,15 +277,15 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
|
|||
/* 0x0c: table4 size */
|
||||
/* variable: entries */
|
||||
/* variable: names (null terminated) */
|
||||
table4_entries_offset = table4_offset + read_32bit(table4_offset+0x08, sf);
|
||||
table4_entries_offset = table4_offset + read_u32(table4_offset+0x08, sf);
|
||||
table4_names_offset = table4_entries_offset + (0x10*section_entries);
|
||||
//;VGM_LOG("BNK: t4_entries=%lx, t4_names=%lx\n", table4_entries_offset, table4_names_offset);
|
||||
|
||||
/* get assigned name from table4 names */
|
||||
for (i = 0; i < section_entries; i++) {
|
||||
int entry_id = read_32bit(table4_entries_offset+(i*0x10)+0x0c, sf);
|
||||
int entry_id = read_u32(table4_entries_offset+(i*0x10)+0x0c, sf);
|
||||
if (entry_id == table4_entry_id) {
|
||||
name_offset = table4_names_offset + read_32bit(table4_entries_offset+(i*0x10)+0x00, sf);
|
||||
name_offset = table4_names_offset + read_u32(table4_entries_offset+(i*0x10)+0x00, sf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -329,24 +310,24 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
|
|||
case 0x03:
|
||||
case 0x04:
|
||||
case 0x05:
|
||||
channel_count = 1;
|
||||
channels = 1;
|
||||
|
||||
/* early versions don't have PS-ADPCM size, could check next offset but it's all kind of loopy */
|
||||
if (sblk_version <= 0x03 && stream_size == 0 && (flags & 0x80) == 0) {
|
||||
off_t offset;
|
||||
off_t max_offset = get_streamfile_size(sf);
|
||||
uint32_t offset;
|
||||
uint32_t max_offset = get_streamfile_size(sf);
|
||||
|
||||
stream_size += 0x10;
|
||||
for (offset = data_offset + stream_offset + 0x10; offset < max_offset; offset += 0x10) {
|
||||
|
||||
/* beginning frame (if file loops won't have end frame) */
|
||||
if (read_32bitBE(offset + 0x00, sf) == 0x00000000 && read_32bitBE(offset + 0x04, sf) == 0x00000000)
|
||||
if (read_u32be(offset + 0x00, sf) == 0x00000000 && read_u32be(offset + 0x04, sf) == 0x00000000)
|
||||
break;
|
||||
|
||||
stream_size += 0x10;
|
||||
|
||||
/* end frame */
|
||||
if (read_32bitBE(offset + 0x00, sf) == 0x00077777 && read_32bitBE(offset + 0x04, sf) == 0x77777777)
|
||||
if (read_u32be(offset + 0x00, sf) == 0x00077777 && read_u32be(offset + 0x04, sf) == 0x77777777)
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -356,17 +337,17 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
|
|||
|
||||
/* hack for PS3 files that use dual subsongs as stereo */
|
||||
if (total_subsongs == 2 && stream_size * 2 == data_size) {
|
||||
channel_count = 2;
|
||||
stream_size = stream_size*channel_count;
|
||||
channels = 2;
|
||||
stream_size = stream_size*channels;
|
||||
total_subsongs = 1;
|
||||
}
|
||||
interleave = stream_size / channel_count;
|
||||
interleave = stream_size / channels;
|
||||
|
||||
if (flags & 0x80) {
|
||||
codec = PCM16; /* rare [Wipeout HD (PS3)] */
|
||||
if ((flags & 0x80) && sblk_version <= 3) { /* PS Home Arcade has other flags? */
|
||||
codec = PCM16; /* rare [Wipeout HD (PS3)]-v3 */
|
||||
}
|
||||
else {
|
||||
loop_flag = ps_find_loop_offsets(sf, start_offset, stream_size, channel_count, interleave, &loop_start, &loop_end);
|
||||
loop_flag = ps_find_loop_offsets(sf, start_offset, stream_size, channels, interleave, &loop_start, &loop_end);
|
||||
loop_flag = (flags & 0x40); /* no loops values in sight so may only apply to PS-ADPCM flags */
|
||||
|
||||
codec = PSX;
|
||||
|
@ -375,54 +356,68 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
|
|||
//postdata_size = 0x10; /* last frame may be garbage */
|
||||
break;
|
||||
|
||||
case 0x08:
|
||||
case 0x09:
|
||||
type = read_16bit(start_offset+0x00,sf);
|
||||
extradata_size = 0x08 + read_32bit(start_offset+0x04,sf); /* 0x14 for AT9 */
|
||||
type = read_u16(start_offset+0x00,sf);
|
||||
extradata_size = 0x08 + read_u32(start_offset+0x04,sf); /* 0x14 for AT9 */
|
||||
|
||||
switch(type) {
|
||||
case 0x00:
|
||||
channels = 1;
|
||||
codec = PSX;
|
||||
interleave = 0x10;
|
||||
break;
|
||||
|
||||
case 0x01:
|
||||
channels = 1;
|
||||
codec = PCM16;
|
||||
interleave = 0x01;
|
||||
break;
|
||||
|
||||
|
||||
case 0x02: /* ATRAC9 mono */
|
||||
case 0x05: /* ATRAC9 stereo */
|
||||
if (read_32bit(start_offset+0x08,sf) + 0x08 != extradata_size) /* repeat? */
|
||||
if (read_u32(start_offset+0x08,sf) + 0x08 != extradata_size) /* repeat? */
|
||||
goto fail;
|
||||
channel_count = (type == 0x02) ? 1 : 2;
|
||||
channels = (type == 0x02) ? 1 : 2;
|
||||
|
||||
atrac9_info = (uint32_t)read_32bitBE(start_offset+0x0c,sf);
|
||||
atrac9_info = read_u32be(start_offset+0x0c,sf);
|
||||
/* 0x10: null? */
|
||||
loop_length = read_32bit(start_offset+0x14,sf);
|
||||
loop_start = read_32bit(start_offset+0x18,sf);
|
||||
loop_length = read_u32(start_offset+0x14,sf);
|
||||
loop_start = read_u32(start_offset+0x18,sf);
|
||||
loop_end = loop_start + loop_length; /* loop_start is -1 if not set */
|
||||
|
||||
codec = ATRAC9;
|
||||
break;
|
||||
|
||||
default:
|
||||
VGM_LOG("BNK: unknown type %x\n", type);
|
||||
vgm_logi("BNK: unknown type %x (report)\n", type);
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x0d:
|
||||
case 0x0e:
|
||||
type = read_16bit(start_offset+0x00,sf);
|
||||
if (read_32bit(start_offset+0x04,sf) != 0x01) /* type? */
|
||||
type = read_u16(start_offset+0x00,sf);
|
||||
if (read_u32(start_offset+0x04,sf) != 0x01) /* type? */
|
||||
goto fail;
|
||||
extradata_size = 0x10 + read_32bit(start_offset+0x08,sf); /* 0x80 for AT9, 0x10 for PCM/PS-ADPCM */
|
||||
extradata_size = 0x10 + read_u32(start_offset+0x08,sf); /* 0x80 for AT9, 0x10 for PCM/PS-ADPCM */
|
||||
/* 0x0c: null? */
|
||||
|
||||
switch(type) {
|
||||
case 0x02: /* ATRAC9 mono */
|
||||
case 0x05: /* ATRAC9 stereo */
|
||||
if (read_32bit(start_offset+0x10,sf) + 0x10 != extradata_size) /* repeat? */
|
||||
if (read_u32(start_offset+0x10,sf) + 0x10 != extradata_size) /* repeat? */
|
||||
goto fail;
|
||||
channel_count = (type == 0x02) ? 1 : 2;
|
||||
channels = (type == 0x02) ? 1 : 2;
|
||||
|
||||
atrac9_info = (uint32_t)read_32bitBE(start_offset+0x14,sf);
|
||||
atrac9_info = read_u32be(start_offset+0x14,sf);
|
||||
/* 0x18: null? */
|
||||
/* 0x1c: channels? */
|
||||
/* 0x20: null? */
|
||||
|
||||
loop_length = read_32bit(start_offset+0x24,sf);
|
||||
loop_start = read_32bit(start_offset+0x28,sf);
|
||||
loop_length = read_u32(start_offset+0x24,sf);
|
||||
loop_start = read_u32(start_offset+0x28,sf);
|
||||
loop_end = loop_start + loop_length; /* loop_start is -1 if not set */
|
||||
|
||||
codec = ATRAC9;
|
||||
|
@ -431,11 +426,11 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
|
|||
case 0x01: /* PCM16LE mono? (NekoBuro/Polara sfx) */
|
||||
case 0x04: /* PCM16LE stereo? (NekoBuro/Polara sfx) */
|
||||
/* 0x10: null? */
|
||||
channel_count = read_32bit(start_offset+0x14,sf);
|
||||
channels = read_u32(start_offset+0x14,sf);
|
||||
interleave = 0x02;
|
||||
|
||||
loop_start = read_32bit(start_offset+0x18,sf);
|
||||
loop_length = read_32bit(start_offset+0x1c,sf);
|
||||
loop_start = read_u32(start_offset+0x18,sf);
|
||||
loop_length = read_u32(start_offset+0x1c,sf);
|
||||
loop_end = loop_start + loop_length; /* loop_start is -1 if not set */
|
||||
|
||||
codec = PCM16;
|
||||
|
@ -443,24 +438,24 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
|
|||
|
||||
case 0x00: /* PS-ADPCM (test banks) */
|
||||
/* 0x10: null? */
|
||||
channel_count = read_32bit(start_offset+0x14,sf);
|
||||
channels = read_u32(start_offset+0x14,sf);
|
||||
interleave = 0x02;
|
||||
|
||||
loop_start = read_32bit(start_offset+0x18,sf);
|
||||
loop_length = read_32bit(start_offset+0x1c,sf);
|
||||
loop_start = read_u32(start_offset+0x18,sf);
|
||||
loop_length = read_u32(start_offset+0x1c,sf);
|
||||
loop_end = loop_start + loop_length; /* loop_start is -1 if not set */
|
||||
|
||||
codec = HEVAG;
|
||||
break;
|
||||
|
||||
default:
|
||||
VGM_LOG("BNK: unknown type %x\n", type);
|
||||
vgm_logi("BNK: unknown type %x (report)\n", type);
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
VGM_LOG("BNK: unknown data version %x\n", sblk_version);
|
||||
vgm_logi("BNK: unknown data version %x (report)\n", sblk_version);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
@ -474,7 +469,7 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
|
|||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
vgmstream = allocate_vgmstream(channels,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
|
@ -518,7 +513,7 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
|
|||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = interleave;
|
||||
|
||||
vgmstream->num_samples = ps_bytes_to_samples(stream_size,channel_count);
|
||||
vgmstream->num_samples = ps_bytes_to_samples(stream_size,channels);
|
||||
vgmstream->loop_start_sample = loop_start;
|
||||
vgmstream->loop_end_sample = loop_end;
|
||||
break;
|
||||
|
@ -528,7 +523,7 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
|
|||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = interleave;
|
||||
|
||||
vgmstream->num_samples = ps_bytes_to_samples(stream_size,channel_count);
|
||||
vgmstream->num_samples = ps_bytes_to_samples(stream_size,channels);
|
||||
vgmstream->loop_start_sample = loop_start;
|
||||
vgmstream->loop_end_sample = loop_end;
|
||||
break;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "../util.h"
|
||||
#include "../util/chunks.h"
|
||||
#include "bnsf_keys.h"
|
||||
|
||||
|
||||
|
@ -13,55 +13,76 @@ static void find_bnsf_key(STREAMFILE *sf, off_t start, g7221_codec_data *data, u
|
|||
#endif
|
||||
|
||||
/* BNSF - Bandai Namco Sound Format/File [Tales of Graces (Wii), Tales of Berseria (PS4)] */
|
||||
VGMSTREAM * init_vgmstream_bnsf(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset = 0, first_offset = 0x0C;
|
||||
int loop_flag = 0, channel_count = 0, sample_rate;
|
||||
int num_samples, loop_start = 0, loop_end = 0, loop_adjust, block_samples;
|
||||
VGMSTREAM* init_vgmstream_bnsf(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset = 0;
|
||||
int loop_flag = 0, channels = 0, sample_rate = 0;
|
||||
uint32_t num_samples = 0, loop_start = 0, loop_end = 0, loop_adjust = 0, block_samples = 0;
|
||||
uint32_t codec, flags = 0;
|
||||
size_t bnsf_size, sdat_size, block_size;
|
||||
off_t loop_chunk = 0, sfmt_chunk, sdat_chunk;
|
||||
size_t bnsf_size, block_size = 0;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile,"bnsf"))
|
||||
if (!is_id32be(0x00,sf, "BNSF"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0,streamFile) != 0x424E5346) /* "BNSF" */
|
||||
if (!check_extensions(sf,"bnsf"))
|
||||
goto fail;
|
||||
|
||||
bnsf_size = read_32bitBE(0x04,streamFile);
|
||||
codec = read_32bitBE(0x08,streamFile);
|
||||
bnsf_size = read_u32be(0x04,sf);
|
||||
codec = read_u32be(0x08,sf);
|
||||
|
||||
if (bnsf_size + (codec == 0x49533232 ? 0x00 : 0x08) != get_streamfile_size(streamFile)) /* IS22 uses full size */
|
||||
if (codec != get_id32be("IS22")) /* uses full size */
|
||||
bnsf_size += 0x08;
|
||||
if (bnsf_size != get_streamfile_size(sf))
|
||||
goto fail;
|
||||
|
||||
if (!find_chunk_be(streamFile, 0x73666d74,first_offset,0, &sfmt_chunk,NULL)) /* "sfmt" */
|
||||
goto fail;
|
||||
if (!find_chunk_be(streamFile, 0x73646174,first_offset,0, &sdat_chunk,&sdat_size)) /* "sdat" */
|
||||
goto fail;
|
||||
if ( find_chunk_be(streamFile, 0x6C6F6F70,first_offset,0, &loop_chunk,NULL)) { /* "loop" */
|
||||
loop_flag = 1;
|
||||
loop_start = read_32bitBE(loop_chunk+0x00,streamFile); /* block-aligned */
|
||||
loop_end = read_32bitBE(loop_chunk+0x04,streamFile) + 1;
|
||||
{
|
||||
enum {
|
||||
CHUNK_sfmt = 0x73666d74,
|
||||
CHUNK_sdat = 0x73646174,
|
||||
CHUNK_loop = 0x6C6F6F70,
|
||||
};
|
||||
chunk_t rc = {0};
|
||||
|
||||
rc.be_size = 1;
|
||||
rc.current = 0x0C;
|
||||
while (next_chunk(&rc, sf)) {
|
||||
switch(rc.type) {
|
||||
case CHUNK_sfmt:
|
||||
flags = read_u16be(rc.offset+0x00,sf);
|
||||
channels = read_u16be(rc.offset+0x02,sf);
|
||||
sample_rate = read_s32be(rc.offset+0x04,sf);
|
||||
num_samples = read_s32be(rc.offset+0x08,sf);
|
||||
loop_adjust = read_s32be(rc.offset+0x0c,sf); /* 0 when no loop */
|
||||
block_size = read_u16be(rc.offset+0x10,sf);
|
||||
block_samples = read_u16be(rc.offset+0x12,sf);
|
||||
//max_samples = sdat_size / block_size * block_samples;
|
||||
break;
|
||||
|
||||
case CHUNK_loop:
|
||||
loop_flag = 1;
|
||||
loop_start = read_s32be(rc.offset+0x00,sf); /* block-aligned */
|
||||
loop_end = read_s32be(rc.offset+0x04,sf) + 1;
|
||||
break;
|
||||
|
||||
case CHUNK_sdat:
|
||||
start_offset = rc.offset;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flags = read_16bitBE(sfmt_chunk+0x00,streamFile);
|
||||
channel_count = read_16bitBE(sfmt_chunk+0x02,streamFile);
|
||||
sample_rate = read_32bitBE(sfmt_chunk+0x04,streamFile);
|
||||
num_samples = read_32bitBE(sfmt_chunk+0x08,streamFile);
|
||||
loop_adjust = read_32bitBE(sfmt_chunk+0x0c,streamFile); /* 0 when no loop */
|
||||
block_size = read_16bitBE(sfmt_chunk+0x10,streamFile);
|
||||
block_samples = read_16bitBE(sfmt_chunk+0x12,streamFile);
|
||||
//max_samples = sdat_size / block_size * block_samples;
|
||||
|
||||
start_offset = sdat_chunk;
|
||||
|
||||
if (loop_adjust >= block_samples) /* decoder can't handle this */
|
||||
goto fail;
|
||||
if (!start_offset)
|
||||
goto fail;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count, loop_flag);
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
|
@ -71,7 +92,7 @@ VGMSTREAM * init_vgmstream_bnsf(STREAMFILE *streamFile) {
|
|||
|
||||
vgmstream->meta_type = meta_BNSF;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = block_size / channel_count;
|
||||
vgmstream->interleave_block_size = block_size / channels;
|
||||
|
||||
switch (codec) {
|
||||
#ifdef VGM_USE_G7221
|
||||
|
@ -80,20 +101,20 @@ VGMSTREAM * init_vgmstream_bnsf(STREAMFILE *streamFile) {
|
|||
vgmstream->codec_data = init_g7221(vgmstream->channels, vgmstream->interleave_block_size);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
|
||||
/* get decryption key in .bnsfkey file or list, for later games' voices
|
||||
* [The Idolm@ster 2 (PS3/X360), Tales of Zestiria (PS3/PC)] */
|
||||
/* get decryption key in .bnsfkey file or list, for later games' voices and some odd BGM
|
||||
* [THE iDOL@STER 2 (PS3/X360), Tales of Zestiria (PS3/PC)] */
|
||||
if (flags != 0) { /* only known value is 0x02 though */
|
||||
size_t keysize;
|
||||
uint8_t key[24] = {0}; /* keystring 0-padded to 192-bit */
|
||||
|
||||
keysize = read_key_file(key, sizeof(key), streamFile);
|
||||
keysize = read_key_file(key, sizeof(key), sf);
|
||||
#ifdef BNSF_BRUTEFORCE
|
||||
if (1) {
|
||||
bruteforce_bnsf_key(streamFile, start_offset, vgmstream->codec_data, key);
|
||||
bruteforce_bnsf_key(sf, start_offset, vgmstream->codec_data, key);
|
||||
} else
|
||||
#endif
|
||||
if (keysize <= 0 || keysize > sizeof(key)) {
|
||||
find_bnsf_key(streamFile, start_offset, vgmstream->codec_data, key);
|
||||
find_bnsf_key(sf, start_offset, vgmstream->codec_data, key);
|
||||
}
|
||||
set_key_g7221(vgmstream->codec_data, key);
|
||||
}
|
||||
|
@ -118,10 +139,9 @@ VGMSTREAM * init_vgmstream_bnsf(STREAMFILE *streamFile) {
|
|||
}
|
||||
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
|
@ -187,9 +207,9 @@ static void bruteforce_bnsf_key(STREAMFILE* sf, off_t start, g7221_codec_data* d
|
|||
|
||||
sf_keys = open_streamfile_by_filename(sf, "keys.txt");
|
||||
if (!sf_keys) goto done;
|
||||
|
||||
|
||||
keys_size = get_streamfile_size(sf_keys);
|
||||
|
||||
|
||||
offset = 0x00;
|
||||
while (offset < keys_size) {
|
||||
int line_len;
|
||||
|
@ -204,7 +224,7 @@ static void bruteforce_bnsf_key(STREAMFILE* sf, off_t start, g7221_codec_data* d
|
|||
for (j = i + BNSF_MIN_KEY_LEN; j <= line_len; j++) {
|
||||
int keylen = j - i;
|
||||
const char* key = &line[i];
|
||||
|
||||
|
||||
test_key(sf, start, data, key, keylen, &best_score, best_key);
|
||||
if (best_score == 1) {
|
||||
VGM_ASSERT(best_score > 0, "BNSF: good key=%.24s (score=%i)\n", best_key, best_score);
|
||||
|
|
|
@ -1,177 +1,179 @@
|
|||
#include "meta.h"
|
||||
|
||||
|
||||
/* .cks - Cricket Audio stream [Part Time UFO (Android), Mega Man 1-6 (Android)] */
|
||||
VGMSTREAM * init_vgmstream_cks(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag, channel_count, codec, sample_rate;
|
||||
int32_t num_samples, loop_start, loop_end;
|
||||
size_t block_size;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile, "cks"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00,streamFile) != 0x636B6D6B) /* "ckmk" */
|
||||
goto fail;
|
||||
/* 0x04(4): platform bitflags (from LSB: iOS, Android, OS X, Windows, WP8, Linux, tvOS, undefined/ignored) */
|
||||
if (read_32bitLE(0x08,streamFile) != 0x00) /* expected file type (0x00: stream, 0x01: bank, 0x02+: unknown) */
|
||||
goto fail;
|
||||
if (read_32bitLE(0x0c,streamFile) != 0x02) /* file version (always 0x02) */
|
||||
goto fail;
|
||||
|
||||
codec = read_8bit(0x10,streamFile);
|
||||
channel_count = read_8bit(0x11,streamFile);
|
||||
sample_rate = (uint16_t)read_16bitLE(0x12,streamFile);
|
||||
num_samples = read_32bitLE(0x14,streamFile) * read_16bitLE(0x1a,streamFile); /* number_of_blocks * samples_per_frame */
|
||||
block_size = read_16bitLE(0x18,streamFile);
|
||||
/* 0x1c(2): volume */
|
||||
/* 0x1e(2): pan */
|
||||
loop_start = read_32bitLE(0x20,streamFile);
|
||||
loop_end = read_32bitLE(0x24,streamFile);
|
||||
loop_flag = read_16bitLE(0x28,streamFile) != 0; /* loop count (-1 = forever) */
|
||||
/* 0x2a(2): unused? */
|
||||
|
||||
start_offset = 0x2c;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = num_samples;
|
||||
vgmstream->loop_start_sample = loop_start;
|
||||
vgmstream->loop_end_sample = loop_end;
|
||||
|
||||
vgmstream->meta_type = meta_CKS;
|
||||
|
||||
switch(codec) {
|
||||
case 0x00: /* pcm16 [from tests] */
|
||||
vgmstream->coding_type = coding_PCM16LE;
|
||||
break;
|
||||
case 0x01: /* pcm8 [from tests] */
|
||||
vgmstream->coding_type = coding_PCM8;
|
||||
break;
|
||||
case 0x02: /* adpcm [Part Time UFO (Android), Mega Man 1-6 (Android)] */
|
||||
vgmstream->coding_type = coding_MSADPCM_ck;
|
||||
vgmstream->frame_size = block_size / channel_count; /* always 0x18 */
|
||||
break;
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = block_size / channel_count;
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* .ckb - Cricket Audio bank [Fire Emblem Heroes (Android), Mega Man 1-6 (Android)] */
|
||||
VGMSTREAM * init_vgmstream_ckb(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset, name_offset = 0;
|
||||
int loop_flag, channel_count, codec, sample_rate;
|
||||
int32_t num_samples, loop_start, loop_end;
|
||||
size_t block_size, stream_size;
|
||||
int total_subsongs, target_subsong = streamFile->stream_index;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile, "ckb"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00,streamFile) != 0x636B6D6B) /* "ckmk" */
|
||||
goto fail;
|
||||
/* 0x04(4): platform bitflags (from LSB: iOS, Android, OS X, Windows, WP8, Linux, tvOS, undefined/ignored) */
|
||||
if (read_32bitLE(0x08,streamFile) != 0x01) /* expected file type (0x00: stream, 0x01: bank, 0x02+: unknown) */
|
||||
goto fail;
|
||||
if (read_32bitLE(0x0c,streamFile) != 0x02) /* file version (always 0x02) */
|
||||
goto fail;
|
||||
|
||||
/* 0x10: bank name (size 0x1c+1) */
|
||||
/* 0x30/34: reserved? */
|
||||
total_subsongs = read_32bitLE(0x38,streamFile);
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
|
||||
/* 0x3c: total_subsongs again? (ignored) */
|
||||
/* 0x40/44: unknown (ignored) */
|
||||
|
||||
/* get subsong (stream offset isn't given so must calc manually) */
|
||||
{
|
||||
int i;
|
||||
off_t header_offset = 0x48;
|
||||
off_t stream_offset = 0x48 + total_subsongs*0x48;
|
||||
|
||||
for (i = 0; i < total_subsongs; i++) {
|
||||
name_offset = header_offset+0x00; /* stream name (size 0x1c+1) */
|
||||
codec = read_8bit(header_offset+0x20,streamFile);
|
||||
channel_count = read_8bit(header_offset+0x21,streamFile);
|
||||
sample_rate = (uint16_t)read_16bitLE(header_offset+0x22,streamFile);
|
||||
num_samples = read_32bitLE(header_offset+0x24,streamFile) * read_16bitLE(header_offset+0x2a,streamFile); /* number_of_blocks * samples_per_frame */
|
||||
block_size = read_16bitLE(header_offset+0x28,streamFile);
|
||||
/* 0x2c(2): volume */
|
||||
/* 0x2e(2): pan */
|
||||
loop_start = read_32bitLE(header_offset+0x30,streamFile);
|
||||
loop_end = read_32bitLE(header_offset+0x34,streamFile);
|
||||
loop_flag = read_16bitLE(header_offset+0x38,streamFile) != 0; /* loop count (-1 = forever) */
|
||||
/* 0x3a(2): unused? */
|
||||
stream_size = read_32bitLE(header_offset+0x3c,streamFile);
|
||||
/* 0x40/44(4): unused? */
|
||||
|
||||
if (target_subsong == (i+1))
|
||||
break;
|
||||
header_offset += 0x48;
|
||||
stream_offset += stream_size;
|
||||
}
|
||||
|
||||
start_offset = stream_offset;
|
||||
}
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = num_samples;
|
||||
vgmstream->loop_start_sample = loop_start;
|
||||
vgmstream->loop_end_sample = loop_end;
|
||||
|
||||
vgmstream->num_streams = total_subsongs;
|
||||
vgmstream->stream_size = stream_size;
|
||||
read_string(vgmstream->stream_name,0x1c+1, name_offset,streamFile);
|
||||
|
||||
vgmstream->meta_type = meta_CKB;
|
||||
|
||||
switch(codec) {
|
||||
case 0x00: /* pcm16 [Mega Man 1-6 (Android)] */
|
||||
vgmstream->coding_type = coding_PCM16LE;
|
||||
break;
|
||||
case 0x01: /* pcm8 */
|
||||
vgmstream->coding_type = coding_PCM8;
|
||||
break;
|
||||
case 0x02: /* adpcm [Fire Emblem Heroes (Android)] */
|
||||
vgmstream->coding_type = coding_MSADPCM_ck;
|
||||
/* frame_size is always 0x18 */
|
||||
break;
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = block_size / channel_count;
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
#include "meta.h"
|
||||
|
||||
|
||||
/* .cks - Cricket Audio stream [Part Time UFO (Android), Mega Man 1-6 (Android)] */
|
||||
VGMSTREAM* init_vgmstream_cks(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag, channels, codec, sample_rate;
|
||||
int32_t num_samples, loop_start, loop_end;
|
||||
size_t block_size;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!is_id32be(0x00,sf, "ckmk"))
|
||||
goto fail;
|
||||
if (!check_extensions(sf, "cks"))
|
||||
goto fail;
|
||||
|
||||
/* 0x04(4): platform bitflags (from LSB: iOS, Android, OS X, Windows, WP8, Linux, tvOS, undefined/ignored) */
|
||||
if (read_u32le(0x08,sf) != 0x00) /* expected file type (0x00: stream, 0x01: bank, 0x02+: unknown) */
|
||||
goto fail;
|
||||
if (read_u32le(0x0c,sf) != 0x02) /* file version (always 0x02) */
|
||||
goto fail;
|
||||
|
||||
codec = read_u8(0x10,sf);
|
||||
channels = read_u8(0x11,sf);
|
||||
sample_rate = read_u16le(0x12,sf);
|
||||
num_samples = read_s32le(0x14,sf) * read_u16le(0x1a,sf); /* number_of_blocks * samples_per_frame */
|
||||
block_size = read_u16le(0x18,sf);
|
||||
/* 0x1c(2): volume */
|
||||
/* 0x1e(2): pan */
|
||||
loop_start = read_s32le(0x20,sf);
|
||||
loop_end = read_s32le(0x24,sf);
|
||||
loop_flag = read_s16le(0x28,sf) != 0; /* loop count (-1 = forever) */
|
||||
/* 0x2a(2): unused? */
|
||||
|
||||
start_offset = 0x2c;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = num_samples;
|
||||
vgmstream->loop_start_sample = loop_start;
|
||||
vgmstream->loop_end_sample = loop_end;
|
||||
|
||||
vgmstream->meta_type = meta_CKS;
|
||||
|
||||
switch(codec) {
|
||||
case 0x00: /* pcm16 [from tests] */
|
||||
vgmstream->coding_type = coding_PCM16LE;
|
||||
break;
|
||||
case 0x01: /* pcm8 [from tests] */
|
||||
vgmstream->coding_type = coding_PCM8;
|
||||
break;
|
||||
case 0x02: /* adpcm [Part Time UFO (Android), Mega Man 1-6 (Android)] */
|
||||
vgmstream->coding_type = coding_MSADPCM_ck;
|
||||
vgmstream->frame_size = block_size / channels; /* always 0x18 */
|
||||
break;
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = block_size / channels;
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* .ckb - Cricket Audio bank [Fire Emblem Heroes (Android), Mega Man 1-6 (Android)] */
|
||||
VGMSTREAM* init_vgmstream_ckb(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset, name_offset = 0;
|
||||
int loop_flag = 0, channels = 0, codec = 0, sample_rate = 0;
|
||||
int32_t num_samples = 0, loop_start = 0, loop_end = 0;
|
||||
size_t block_size = 0, stream_size = 0;
|
||||
int total_subsongs, target_subsong = sf->stream_index;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!is_id32be(0x00,sf, "ckmk"))
|
||||
goto fail;
|
||||
if (!check_extensions(sf, "ckb"))
|
||||
goto fail;
|
||||
|
||||
/* 0x04(4): platform bitflags (from LSB: iOS, Android, OS X, Windows, WP8, Linux, tvOS, undefined/ignored) */
|
||||
if (read_u32le(0x08,sf) != 0x01) /* expected file type (0x00: stream, 0x01: bank, 0x02+: unknown) */
|
||||
goto fail;
|
||||
if (read_u32le(0x0c,sf) != 0x02) /* file version (always 0x02) */
|
||||
goto fail;
|
||||
|
||||
/* 0x10: bank name (size 0x1c+1) */
|
||||
/* 0x30/34: reserved? */
|
||||
total_subsongs = read_u32le(0x38,sf);
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
|
||||
/* 0x3c: total_subsongs again? (ignored) */
|
||||
/* 0x40/44: unknown (ignored) */
|
||||
|
||||
/* get subsong (stream offset isn't given so must calc manually) */
|
||||
{
|
||||
int i;
|
||||
off_t header_offset = 0x48;
|
||||
off_t stream_offset = 0x48 + total_subsongs*0x48;
|
||||
|
||||
for (i = 0; i < total_subsongs; i++) {
|
||||
name_offset = header_offset+0x00; /* stream name (size 0x1c+1) */
|
||||
codec = read_8bit(header_offset+0x20,sf);
|
||||
channels = read_8bit(header_offset+0x21,sf);
|
||||
sample_rate = (uint16_t)read_16bitLE(header_offset+0x22,sf);
|
||||
num_samples = read_32bitLE(header_offset+0x24,sf) * read_16bitLE(header_offset+0x2a,sf); /* number_of_blocks * samples_per_frame */
|
||||
block_size = read_16bitLE(header_offset+0x28,sf);
|
||||
/* 0x2c(2): volume */
|
||||
/* 0x2e(2): pan */
|
||||
loop_start = read_32bitLE(header_offset+0x30,sf);
|
||||
loop_end = read_32bitLE(header_offset+0x34,sf);
|
||||
loop_flag = read_16bitLE(header_offset+0x38,sf) != 0; /* loop count (-1 = forever) */
|
||||
/* 0x3a(2): unused? */
|
||||
stream_size = read_32bitLE(header_offset+0x3c,sf);
|
||||
/* 0x40/44(4): unused? */
|
||||
|
||||
if (target_subsong == (i+1))
|
||||
break;
|
||||
header_offset += 0x48;
|
||||
stream_offset += stream_size;
|
||||
}
|
||||
|
||||
start_offset = stream_offset;
|
||||
}
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = num_samples;
|
||||
vgmstream->loop_start_sample = loop_start;
|
||||
vgmstream->loop_end_sample = loop_end;
|
||||
|
||||
vgmstream->num_streams = total_subsongs;
|
||||
vgmstream->stream_size = stream_size;
|
||||
read_string(vgmstream->stream_name,0x1c+1, name_offset,sf);
|
||||
|
||||
vgmstream->meta_type = meta_CKB;
|
||||
|
||||
switch(codec) {
|
||||
case 0x00: /* pcm16 [Mega Man 1-6 (Android)] */
|
||||
vgmstream->coding_type = coding_PCM16LE;
|
||||
break;
|
||||
case 0x01: /* pcm8 */
|
||||
vgmstream->coding_type = coding_PCM8;
|
||||
break;
|
||||
case 0x02: /* adpcm [Fire Emblem Heroes (Android)] */
|
||||
vgmstream->coding_type = coding_MSADPCM_ck;
|
||||
/* frame_size is always 0x18 */
|
||||
break;
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = block_size / channels;
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,sf,start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "../layout/layout.h"
|
||||
#include "../util/endianness.h"
|
||||
|
||||
#define EA_CODEC_PCM 0x00
|
||||
#define EA_CODEC_ULAW 0x01
|
||||
|
@ -17,7 +18,9 @@ typedef struct {
|
|||
int32_t loop_start;
|
||||
int32_t loop_end;
|
||||
int32_t loop_start_offset;
|
||||
int32_t data_offset;
|
||||
uint32_t data_offset;
|
||||
|
||||
uint32_t base_size;
|
||||
|
||||
int big_endian;
|
||||
int loop_flag;
|
||||
|
@ -27,7 +30,7 @@ typedef struct {
|
|||
int total_subsongs;
|
||||
} eacs_header;
|
||||
|
||||
static int parse_header(STREAMFILE* sf, eacs_header* ea, off_t begin_offset);
|
||||
static int parse_header(STREAMFILE* sf, eacs_header* ea, uint32_t begin_offset);
|
||||
static VGMSTREAM* init_vgmstream_main(STREAMFILE* sf, eacs_header* ea);
|
||||
|
||||
static void set_ea_1snh_num_samples(VGMSTREAM* vgmstream, STREAMFILE* sf, eacs_header* ea, int find_loop);
|
||||
|
@ -35,24 +38,12 @@ static int get_ea_1snh_ima_version(STREAMFILE* sf, off_t start_offset, const eac
|
|||
|
||||
/* EA 1SNh - from early EA games, stream (~1996, ex. Need for Speed) */
|
||||
VGMSTREAM* init_vgmstream_ea_1snh(STREAMFILE* sf) {
|
||||
eacs_header ea = { 0 };
|
||||
off_t offset, eacs_offset;
|
||||
eacs_header ea = {0};
|
||||
off_t offset = 0x00, eacs_offset;
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
|
||||
|
||||
/* checks */
|
||||
/* .asf/as4: common,
|
||||
* .lasf: fake for plugins
|
||||
* .sng: fake for plugins (for .asf issues)
|
||||
* .cnk: some PS1 games
|
||||
* .uv/tgq: some SAT videos
|
||||
* .tgv: videos
|
||||
* (extensionless): Need for Speed (SAT) videos */
|
||||
if (!check_extensions(sf, "asf,lasf,sng,as4,cnk,uv,tgq,tgv,"))
|
||||
goto fail;
|
||||
|
||||
offset = 0x00;
|
||||
|
||||
/* in TGV videos, either TGVk or 1SNh block comes first */
|
||||
if (is_id32be(0x00, sf, "TGVk")) {
|
||||
offset = read_u32be(0x04, sf);
|
||||
|
@ -64,14 +55,28 @@ VGMSTREAM* init_vgmstream_ea_1snh(STREAMFILE* sf) {
|
|||
!is_id32be(offset + 0x00, sf, "SEAD"))
|
||||
goto fail;
|
||||
|
||||
/* .asf/as4: common,
|
||||
* .lasf: fake for plugins
|
||||
* .sng: fake for plugins (for .asf issues)
|
||||
* .cnk: some PS1 games [Triple Play 97 (PS1), FIFA 97 (PS1)]
|
||||
* .uv/tgq: some SAT videos
|
||||
* .tgv: videos
|
||||
* (extensionless): Need for Speed (SAT) videos */
|
||||
if (!check_extensions(sf, "asf,lasf,sng,as4,cnk,uv,tgq,tgv,"))
|
||||
goto fail;
|
||||
|
||||
/* stream is divided into blocks/chunks: 1SNh=audio header, 1SNd=data xN, 1SNl=loop end, 1SNe=end.
|
||||
* Video uses various blocks (TGVk/TGVf/MUVf/etc) and sometimes alt audio blocks (SEAD/SNDC/SEND). */
|
||||
ea.is_sead = is_id32be(offset + 0x00, sf, "SEAD");
|
||||
|
||||
if (!ea.is_sead)
|
||||
ea.base_size = read_u32le(offset + 0x04, sf);
|
||||
|
||||
eacs_offset = offset + 0x08; /* after 1SNh block id+size */
|
||||
|
||||
if (!parse_header(sf, &ea, eacs_offset))
|
||||
goto fail;
|
||||
|
||||
vgmstream = init_vgmstream_main(sf, &ea);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
|
@ -98,17 +103,19 @@ VGMSTREAM* init_vgmstream_ea_eacs(STREAMFILE* sf) {
|
|||
|
||||
|
||||
/* checks */
|
||||
if (!is_id32be(0x00,sf, "EACS") &&
|
||||
read_u32be(0x00,sf) != 0 && !is_id32be(0x228,sf, "EACS"))
|
||||
goto fail;
|
||||
|
||||
/* .eas: single bank [Need for Speed (PC)]
|
||||
* .bnk: multi bank [Need for Speed (PC)] */
|
||||
if (!check_extensions(sf,"eas,bnk"))
|
||||
* .bnk: multi bank [Need for Speed (PC)]
|
||||
* .as4: single [NBA Live 96 (PC)] */
|
||||
if (!check_extensions(sf,"eas,bnk,as4"))
|
||||
goto fail;
|
||||
|
||||
/* plain data without blocks, can contain N*(EACS header) + N*(data), or N (EACS header + data) */
|
||||
ea.is_bank = 1;
|
||||
|
||||
/* use ??? as endian marker (Saturn = BE) */
|
||||
//ea.big_endian = guess_endianness32bit(0x04,sf);
|
||||
|
||||
if (is_id32be(0x00,sf, "EACS")) {
|
||||
/* single bank variant */
|
||||
eacs_offset = 0x00;
|
||||
|
@ -121,8 +128,8 @@ VGMSTREAM* init_vgmstream_ea_eacs(STREAMFILE* sf) {
|
|||
if (target_subsong == 0) target_subsong = 1;
|
||||
if (target_subsong < 0) goto fail;
|
||||
|
||||
/* offsets to EACSs are scattered in the first 0x200
|
||||
* this looks dumb but seems like the only way */
|
||||
/* offsets to EACSs are scattered in the first 0x200, then 0x28 info + EACS per subsong.
|
||||
* This looks dumb but seems like the only way. */
|
||||
eacs_offset = 0;
|
||||
for (i = 0x00; i < 0x200; i += 0x04) {
|
||||
off_t bank_offset = read_u32le(i, sf);
|
||||
|
@ -134,7 +141,7 @@ VGMSTREAM* init_vgmstream_ea_eacs(STREAMFILE* sf) {
|
|||
/* parse mini bank header */
|
||||
if (ea.total_subsongs == target_subsong) {
|
||||
/* 0x00: some id or flags? */
|
||||
eacs_offset = read_u32le(bank_offset + 0x04, sf);
|
||||
eacs_offset = read_u32le(bank_offset + 0x04, sf); /* always after 0x28 from bank_offset */
|
||||
if (!is_id32be(eacs_offset, sf, "EACS"))
|
||||
goto fail;
|
||||
/* rest: not sure if part of this header */
|
||||
|
@ -193,10 +200,11 @@ static VGMSTREAM* init_vgmstream_main(STREAMFILE* sf, eacs_header* ea) {
|
|||
|
||||
case EA_CODEC_PSX: /* Need for Speed (PS1) */
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->codec_config = ea->codec_config;
|
||||
break;
|
||||
|
||||
default:
|
||||
VGM_LOG("EA EACS: unknown codec 0x%02x\n", ea->codec);
|
||||
vgm_logi("EA EACS: unknown codec 0x%02x\n", ea->codec);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
@ -210,13 +218,13 @@ fail:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static int parse_header(STREAMFILE* sf, eacs_header* ea, off_t offset) {
|
||||
static int parse_header(STREAMFILE* sf, eacs_header* ea, uint32_t offset) {
|
||||
/* audio header endianness doesn't always match block headers, use sample rate to detect */
|
||||
int32_t (*read_s32)(off_t,STREAMFILE*);
|
||||
read_s32_t read_s32;
|
||||
|
||||
if (is_id32be(offset+0x00,sf, "EACS")) {
|
||||
/* EACS subheader (PC, SAT) */
|
||||
ea->big_endian = guess_endianness32bit(offset + 0x04, sf);
|
||||
ea->big_endian = guess_endian32(offset + 0x04, sf);
|
||||
read_s32 = ea->big_endian ? read_s32be : read_s32le;
|
||||
|
||||
ea->sample_rate = read_s32(offset+0x04, sf);
|
||||
|
@ -236,9 +244,37 @@ static int parse_header(STREAMFILE* sf, eacs_header* ea, off_t offset) {
|
|||
ea->codec_config = get_ea_1snh_ima_version(sf, 0x00, ea);
|
||||
/* EACS banks with empty values exist but will be rejected later */
|
||||
}
|
||||
else if (read_u32be(offset + 0x00, sf) == 0x00) {
|
||||
else if (ea->is_sead) {
|
||||
/* alt subheader (found in some PC videos) */
|
||||
ea->big_endian = guess_endian32(offset + 0x00, sf);
|
||||
read_s32 = ea->big_endian ? read_s32be : read_s32le;
|
||||
|
||||
ea->sample_rate = read_s32(offset+0x00, sf);
|
||||
ea->channels = read_s32(offset+0x04, sf);
|
||||
ea->codec = read_s32(offset+0x08, sf);
|
||||
|
||||
if (ea->codec == EA_CODEC_IMA)
|
||||
ea->codec_config = get_ea_1snh_ima_version(sf, 0x00, ea);
|
||||
}
|
||||
else if (ea->base_size == 0x2c) {
|
||||
/* [NBA Live 96 (PS1), Need for Speed (PS1)] */
|
||||
ea->sample_rate = read_s32le(offset+0x00, sf);
|
||||
ea->channels = read_u8(offset+0x18, sf);
|
||||
ea->codec = EA_CODEC_PSX;
|
||||
ea->codec_config = 0;
|
||||
}
|
||||
else if (ea->base_size == 0x30) {
|
||||
/* [FIFA 97 (PS1), Triple Play 97 (PS1)] */
|
||||
/* 0x00: 0 or some id? (same for N files) */
|
||||
ea->sample_rate = read_s32le(offset+0x04, sf);
|
||||
ea->channels = read_u8(offset+0x1c, sf);
|
||||
ea->codec = EA_CODEC_PSX;
|
||||
ea->codec_config = 1;
|
||||
}
|
||||
else {
|
||||
//TODO: test
|
||||
/* found in early videos, similar to EACS */
|
||||
ea->big_endian = guess_endianness32bit(offset + 0x04, sf);
|
||||
ea->big_endian = guess_endian32(offset + 0x04, sf);
|
||||
read_s32 = ea->big_endian ? read_s32be : read_s32le;
|
||||
|
||||
ea->sample_rate = read_s32(offset + 0x04, sf);
|
||||
|
@ -250,27 +286,6 @@ static int parse_header(STREAMFILE* sf, eacs_header* ea, off_t offset) {
|
|||
if (ea->codec == EA_CODEC_IMA)
|
||||
ea->codec_config = get_ea_1snh_ima_version(sf, 0x00, ea);
|
||||
}
|
||||
else if (ea->is_sead) {
|
||||
/* alt subheader (found in some PC videos) */
|
||||
ea->big_endian = guess_endianness32bit(offset + 0x00, sf);
|
||||
read_s32 = ea->big_endian ? read_s32be : read_s32le;
|
||||
|
||||
ea->sample_rate = read_s32(offset+0x00, sf);
|
||||
ea->channels = read_s32(offset+0x04, sf);
|
||||
ea->codec = read_s32(offset+0x08, sf);
|
||||
|
||||
if (ea->codec == EA_CODEC_IMA)
|
||||
ea->codec_config = get_ea_1snh_ima_version(sf, 0x00, ea);
|
||||
}
|
||||
else {
|
||||
/* alt subheader (PS1) */
|
||||
ea->big_endian = guess_endianness32bit(offset + 0x00, sf);
|
||||
read_s32 = ea->big_endian ? read_s32be : read_s32le;
|
||||
|
||||
ea->sample_rate = read_s32(offset+0x00, sf);
|
||||
ea->channels = read_u8(offset+0x18, sf);
|
||||
ea->codec = EA_CODEC_PSX;
|
||||
}
|
||||
|
||||
ea->loop_flag = (ea->loop_end > 0);
|
||||
|
||||
|
@ -281,26 +296,28 @@ static int parse_header(STREAMFILE* sf, eacs_header* ea, off_t offset) {
|
|||
static void set_ea_1snh_num_samples(VGMSTREAM *vgmstream, STREAMFILE* sf, eacs_header* ea, int find_loop) {
|
||||
int32_t num_samples = 0, block_id;
|
||||
size_t file_size;
|
||||
int32_t(*read_s32)(off_t, STREAMFILE *) = ea->big_endian ? read_s32be : read_s32le;
|
||||
read_s32_t read_s32 = ea->big_endian ? read_s32be : read_s32le;
|
||||
int loop_end_found = 0;
|
||||
|
||||
file_size = get_streamfile_size(sf);
|
||||
vgmstream->next_block_offset = ea->data_offset;
|
||||
|
||||
while (vgmstream->next_block_offset < file_size) {
|
||||
block_update_ea_1snh(vgmstream->next_block_offset, vgmstream);
|
||||
block_update(vgmstream->next_block_offset, vgmstream);
|
||||
if (vgmstream->current_block_samples < 0)
|
||||
break;
|
||||
|
||||
block_id = read_u32be(vgmstream->current_block_offset, sf);
|
||||
|
||||
if (find_loop) {
|
||||
if (vgmstream->current_block_offset == ea->loop_start_offset) {
|
||||
ea->loop_start = num_samples;
|
||||
ea->loop_flag = 1;
|
||||
block_update_ea_1snh(ea->data_offset, vgmstream);
|
||||
block_update(ea->data_offset, vgmstream);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
if (block_id == get_id32be("1SNl") ) { /* loop point found */
|
||||
ea->loop_start_offset = read_s32(vgmstream->current_block_offset + 0x08, sf);
|
||||
ea->loop_end = num_samples;
|
||||
|
@ -314,7 +331,7 @@ static void set_ea_1snh_num_samples(VGMSTREAM *vgmstream, STREAMFILE* sf, eacs_h
|
|||
ea->num_samples = num_samples;
|
||||
|
||||
/* reset once we're done */
|
||||
block_update_ea_1snh(ea->data_offset, vgmstream);
|
||||
block_update(ea->data_offset, vgmstream);
|
||||
|
||||
if (loop_end_found) {
|
||||
/* recurse to find loop start sample */
|
||||
|
@ -326,21 +343,27 @@ static void set_ea_1snh_num_samples(VGMSTREAM *vgmstream, STREAMFILE* sf, eacs_h
|
|||
static int get_ea_1snh_ima_version(STREAMFILE* sf, off_t start_offset, const eacs_header* ea) {
|
||||
off_t block_offset = start_offset;
|
||||
size_t file_size = get_streamfile_size(sf);
|
||||
int32_t (*read_s32)(off_t,STREAMFILE*) = ea->big_endian ? read_s32be : read_s32le;
|
||||
read_s32_t read_s32 = ea->big_endian ? read_s32be : read_s32le;
|
||||
|
||||
if (ea->type == 0xFF) /* bnk */
|
||||
return 0;
|
||||
|
||||
while (block_offset < file_size) {
|
||||
uint32_t id = read_u32be(block_offset+0x00,sf);
|
||||
size_t block_size;
|
||||
|
||||
/* BE in SAT, but one file may have both BE and LE chunks */
|
||||
if (guess_endianness32bit(block_offset + 0x04, sf))
|
||||
if (guess_endian32(block_offset + 0x04, sf))
|
||||
block_size = read_u32be(block_offset + 0x04, sf);
|
||||
else
|
||||
block_size = read_u32le(block_offset + 0x04, sf);
|
||||
|
||||
if (id == 0x31534E64 || id == 0x534E4443) { /* "1SNd" "SNDC" audio data */
|
||||
size_t ima_samples = read_s32(block_offset + 0x08, sf);
|
||||
size_t expected_samples = (block_size - 0x08 - 0x04 - 0x08*ea->channels) * 2 / ea->channels;
|
||||
if (block_size == 0 || block_size == -1)
|
||||
break;
|
||||
|
||||
if (id == get_id32be("1SNd") || id == get_id32be("SNDC")) {
|
||||
int32_t ima_samples = read_s32(block_offset + 0x08, sf);
|
||||
int32_t expected_samples = (block_size - 0x08 - 0x04 - 0x08*ea->channels) * 2 / ea->channels;
|
||||
|
||||
if (ima_samples == expected_samples) {
|
||||
return 1; /* has ADPCM hist (hopefully) */
|
||||
|
|
|
@ -260,8 +260,10 @@ static VGMSTREAM* parse_s10a_header(STREAMFILE* sf, off_t offset, uint16_t targe
|
|||
} else {
|
||||
/* streamed asset */
|
||||
astFile = open_streamfile_by_ext(sf, "ast");
|
||||
if (!astFile)
|
||||
if (!astFile) {
|
||||
vgm_logi("EA ABK: .ast file not found (find and put together)\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (read_32bitBE(0x00, astFile) != 0x53313053) /* "S10S" */
|
||||
goto fail;
|
||||
|
@ -604,7 +606,7 @@ static STREAMFILE *open_mapfile_pair(STREAMFILE* sf, int track /*, int num_track
|
|||
}
|
||||
}
|
||||
|
||||
VGM_LOG("No MPF/MUS pair specified for %s.\n", file_name);
|
||||
vgm_logi("EA MPF: .mus file not found (find and put together)\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
@ -127,8 +127,8 @@ static VGMSTREAM * parse_bnk_header(STREAMFILE* sf, off_t offset, int target_str
|
|||
static int parse_variable_header(STREAMFILE* sf, ea_header* ea, off_t begin_offset, int max_length, int bnk_version);
|
||||
static uint32_t read_patch(STREAMFILE* sf, off_t* offset);
|
||||
static off_t get_ea_stream_mpeg_start_offset(STREAMFILE* sf, off_t start_offset, const ea_header* ea);
|
||||
static VGMSTREAM* init_vgmstream_ea_variable_header(STREAMFILE* sf, ea_header* ea, off_t start_offset, int is_bnk, int standalone);
|
||||
static void update_ea_stream_size_and_samples(STREAMFILE* sf, off_t start_offset, VGMSTREAM* vgmstream, int standalone);
|
||||
static VGMSTREAM* init_vgmstream_ea_variable_header(STREAMFILE* sf, ea_header* ea, off_t start_offset, int is_bnk);
|
||||
static void update_ea_stream_size(STREAMFILE* sf, off_t start_offset, VGMSTREAM* vgmstream);
|
||||
|
||||
/* EA SCHl with variable header - from EA games (roughly 1997~2010); generated by EA Canada's sx.exe/Sound eXchange */
|
||||
VGMSTREAM* init_vgmstream_ea_schl(STREAMFILE* sf) {
|
||||
|
@ -150,9 +150,8 @@ VGMSTREAM* init_vgmstream_ea_schl(STREAMFILE* sf) {
|
|||
* .hab: GoldenEye - Rogue Agent (inside .big)
|
||||
* .xsf: 007 - Agent Under Fire (Xbox)
|
||||
* .gsf: 007 - Everything or Nothing (GC)
|
||||
* .mus: map/mpf+mus only?
|
||||
* (extensionless): SSX (PS2) (inside .big) */
|
||||
if (!check_extensions(sf,"asf,lasf,str,chk,eam,exa,sng,aud,sx,xa,strm,stm,hab,xsf,gsf,mus,"))
|
||||
if (!check_extensions(sf,"asf,lasf,str,chk,eam,exa,sng,aud,sx,xa,strm,stm,hab,xsf,gsf,"))
|
||||
goto fail;
|
||||
|
||||
/* check header */
|
||||
|
@ -284,10 +283,9 @@ VGMSTREAM* init_vgmstream_ea_bnk(STREAMFILE* sf) {
|
|||
/* check extension */
|
||||
/* .bnk: common
|
||||
* .sdt: Harry Potter games
|
||||
* .mus: streams/jingles (rare)
|
||||
* .abk: GoldenEye - Rogue Agent
|
||||
* .ast: FIFA 2004 (inside .big) */
|
||||
if (!check_extensions(sf,"bnk,sdt,mus,abk,ast"))
|
||||
if (!check_extensions(sf,"bnk,sdt,abk,ast"))
|
||||
goto fail;
|
||||
|
||||
if (target_stream == 0) target_stream = 1;
|
||||
|
@ -428,8 +426,10 @@ VGMSTREAM* init_vgmstream_ea_abk(STREAMFILE* sf) {
|
|||
|
||||
case 0x02:
|
||||
astData = open_streamfile_by_ext(sf, "ast");
|
||||
if (!astData)
|
||||
if (!astData) {
|
||||
vgm_logi("EA ABK: .ast file not found (find and put together)\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* looped sounds basically consist of two independent segments
|
||||
* the first one is loop start, the second one is loop body */
|
||||
|
@ -737,7 +737,7 @@ static STREAMFILE* open_mapfile_pair(STREAMFILE* sf, int track /*, int num_track
|
|||
}
|
||||
}
|
||||
|
||||
VGM_LOG("No MPF/MUS pair specified for %s.\n", file_name);
|
||||
vgm_logi("EA MPF: .mus file not found (find and put together)\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -1130,7 +1130,7 @@ static VGMSTREAM* parse_schl_block(STREAMFILE* sf, off_t offset, int standalone)
|
|||
start_offset = offset + header_size; /* starts in "SCCl" (skipped in block layout) or very rarely "SCDl" and maybe movie blocks */
|
||||
|
||||
/* rest is common */
|
||||
return init_vgmstream_ea_variable_header(sf, &ea, start_offset, 0, standalone);
|
||||
return init_vgmstream_ea_variable_header(sf, &ea, start_offset, 0);
|
||||
|
||||
fail:
|
||||
return NULL;
|
||||
|
@ -1221,7 +1221,7 @@ static VGMSTREAM* parse_bnk_header(STREAMFILE* sf, off_t offset, int target_stre
|
|||
start_offset = ea.offsets[0]; /* first channel, presumably needed for MPEG */
|
||||
|
||||
/* rest is common */
|
||||
vgmstream = init_vgmstream_ea_variable_header(sf, &ea, start_offset, bnk_version, 0);
|
||||
vgmstream = init_vgmstream_ea_variable_header(sf, &ea, start_offset, bnk_version);
|
||||
if (!vgmstream) goto fail;
|
||||
if (!is_embedded) {
|
||||
vgmstream->num_streams = real_bnk_sounds;
|
||||
|
@ -1234,7 +1234,7 @@ fail:
|
|||
}
|
||||
|
||||
/* inits VGMSTREAM from a EA header */
|
||||
static VGMSTREAM* init_vgmstream_ea_variable_header(STREAMFILE* sf, ea_header* ea, off_t start_offset, int bnk_version, int standalone) {
|
||||
static VGMSTREAM* init_vgmstream_ea_variable_header(STREAMFILE* sf, ea_header* ea, off_t start_offset, int bnk_version) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
int i, ch;
|
||||
int is_bnk = bnk_version;
|
||||
|
@ -1490,7 +1490,7 @@ static VGMSTREAM* init_vgmstream_ea_variable_header(STREAMFILE* sf, ea_header* e
|
|||
|
||||
/* TODO: Figure out how to get stream size for BNK sounds */
|
||||
} else {
|
||||
update_ea_stream_size_and_samples(sf, start_offset, vgmstream, standalone);
|
||||
update_ea_stream_size(sf, start_offset, vgmstream);
|
||||
}
|
||||
|
||||
return vgmstream;
|
||||
|
@ -1901,18 +1901,18 @@ fail:
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void update_ea_stream_size_and_samples(STREAMFILE* sf, off_t start_offset, VGMSTREAM *vgmstream, int standalone) {
|
||||
static void update_ea_stream_size(STREAMFILE* sf, off_t start_offset, VGMSTREAM *vgmstream) {
|
||||
uint32_t block_id;
|
||||
int32_t num_samples = 0;
|
||||
size_t stream_size = 0, file_size;
|
||||
int multiple_schl = 0;
|
||||
|
||||
file_size = get_streamfile_size(sf);
|
||||
|
||||
/* formats with custom codecs */
|
||||
if (vgmstream->layout_type != layout_blocked_ea_schl) {
|
||||
if (vgmstream->layout_type != layout_blocked_ea_schl)
|
||||
return;
|
||||
}
|
||||
|
||||
if (vgmstream->stream_size != 0)
|
||||
return;
|
||||
|
||||
file_size = get_streamfile_size(sf);
|
||||
|
||||
/* manually read totals */
|
||||
vgmstream->next_block_offset = start_offset;
|
||||
|
@ -1923,20 +1923,10 @@ static void update_ea_stream_size_and_samples(STREAMFILE* sf, off_t start_offset
|
|||
|
||||
block_id = read_32bitBE(vgmstream->current_block_offset + 0x00, sf);
|
||||
if (block_id == EA_BLOCKID_END) { /* banks should never contain movie "SHxx" */
|
||||
if (!standalone)
|
||||
break;
|
||||
}
|
||||
else if (block_id == EA_BLOCKID_HEADER) { /* "SCHl" start block (movie "SHxx" shouldn't use multi files) */
|
||||
multiple_schl = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (vgmstream->current_block_samples > 0) {
|
||||
/* HACK: fix num_samples for streams with multiple SCHl. Need to eventually get rid of this.
|
||||
* Get total samples by parsing block headers, needed when multiple files are stitched together.
|
||||
* Some EA files (.mus/eam/sng/etc) concat many small subfiles, used for interactive/mapped
|
||||
* music (.map/lin). Subfiles always share header, except num_samples. */
|
||||
num_samples += vgmstream->current_block_samples;
|
||||
|
||||
/* stream size is almost never provided in bank files so we have to calc it manually */
|
||||
stream_size += vgmstream->next_block_offset - vgmstream->ch[0].offset;
|
||||
}
|
||||
|
@ -1944,17 +1934,7 @@ static void update_ea_stream_size_and_samples(STREAMFILE* sf, off_t start_offset
|
|||
|
||||
/* reset once we're done */
|
||||
block_update_ea_schl(start_offset, vgmstream);
|
||||
|
||||
/* only use calculated samples with multiple subfiles (rarely header samples may be less due to padding) */
|
||||
if (standalone && multiple_schl) {
|
||||
VGM_LOG("EA SCHl: multiple SCHl found\n");
|
||||
if (num_samples > vgmstream->num_samples) {
|
||||
vgmstream->num_samples = num_samples;
|
||||
}
|
||||
}
|
||||
|
||||
if (vgmstream->stream_size == 0)
|
||||
vgmstream->stream_size = stream_size;
|
||||
vgmstream->stream_size = stream_size;
|
||||
}
|
||||
|
||||
/* find data start offset inside the first SCDl; not very elegant but oh well */
|
||||
|
|
|
@ -1,118 +1,155 @@
|
|||
#include "meta.h"
|
||||
#include "../layout/layout.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* Possibly the same as EA_CODEC_x in variable SCHl */
|
||||
#define EA_CODEC_PCM 0x00
|
||||
#define EA_CODEC_IMA 0x02
|
||||
|
||||
typedef struct {
|
||||
int8_t version;
|
||||
int8_t bps;
|
||||
int8_t channels;
|
||||
int8_t codec;
|
||||
int16_t sample_rate;
|
||||
int32_t num_samples;
|
||||
|
||||
int big_endian;
|
||||
int loop_flag;
|
||||
} ea_fixed_header;
|
||||
|
||||
static int parse_fixed_header(STREAMFILE* streamFile, ea_fixed_header* ea, off_t begin_offset);
|
||||
|
||||
|
||||
/* EA SCHl with fixed header - from EA games (~1997? ex. NHL 97 PC) */
|
||||
VGMSTREAM * init_vgmstream_ea_schl_fixed(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
size_t header_size;
|
||||
ea_fixed_header ea = {0};
|
||||
|
||||
|
||||
/* checks */
|
||||
/* .asf: original
|
||||
* .lasf: fake for plugins */
|
||||
if (!check_extensions(streamFile,"asf,lasf"))
|
||||
goto fail;
|
||||
|
||||
/* check header (see ea_schl.c for more info about blocks) */
|
||||
if (read_32bitBE(0x00,streamFile) != 0x5343486C) /* "SCHl" */
|
||||
goto fail;
|
||||
|
||||
header_size = read_32bitLE(0x04,streamFile);
|
||||
|
||||
if (!parse_fixed_header(streamFile,&ea, 0x08))
|
||||
goto fail;
|
||||
|
||||
start_offset = header_size;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(ea.channels, ea.loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = ea.sample_rate;
|
||||
vgmstream->num_samples = ea.num_samples;
|
||||
//vgmstream->loop_start_sample = ea.loop_start;
|
||||
//vgmstream->loop_end_sample = ea.loop_end;
|
||||
|
||||
vgmstream->codec_endian = ea.big_endian;
|
||||
|
||||
vgmstream->meta_type = meta_EA_SCHL_fixed;
|
||||
|
||||
vgmstream->layout_type = layout_blocked_ea_schl;
|
||||
|
||||
switch (ea.codec) {
|
||||
case EA_CODEC_PCM:
|
||||
vgmstream->coding_type = ea.bps==8 ? coding_PCM8 : (ea.big_endian ? coding_PCM16BE : coding_PCM16LE);
|
||||
break;
|
||||
|
||||
case EA_CODEC_IMA:
|
||||
vgmstream->coding_type = coding_DVI_IMA; /* stereo/mono, high nibble first */
|
||||
break;
|
||||
|
||||
default:
|
||||
VGM_LOG("EA: unknown codec 0x%02x\n", ea.codec);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static int parse_fixed_header(STREAMFILE* streamFile, ea_fixed_header* ea, off_t begin_offset) {
|
||||
off_t offset = begin_offset;
|
||||
|
||||
if (read_32bitBE(offset+0x00, streamFile) != 0x5041546C && /* "PATl" */
|
||||
read_32bitBE(offset+0x38, streamFile) != 0x544D706C) /* "TMpl" */
|
||||
goto fail;
|
||||
|
||||
offset += 0x3c; /* after TMpl */
|
||||
ea->version = read_8bit(offset+0x00, streamFile);
|
||||
ea->bps = read_8bit(offset+0x01, streamFile);
|
||||
ea->channels = read_8bit(offset+0x02, streamFile);
|
||||
ea->codec = read_8bit(offset+0x03, streamFile);
|
||||
VGM_ASSERT(read_16bitLE(offset+0x04, streamFile) != 0, "EA SCHl fixed: unknown1 found\n");
|
||||
/* 0x04(16): unknown */
|
||||
ea->sample_rate = (uint16_t)read_16bitLE(offset+0x06, streamFile);
|
||||
ea->num_samples = read_32bitLE(offset+0x08, streamFile);
|
||||
VGM_ASSERT(read_32bitLE(offset+0x0c, streamFile) != -1, "EA SCHl fixed: unknown2 found\n"); /* loop start? */
|
||||
VGM_ASSERT(read_32bitLE(offset+0x10, streamFile) != -1, "EA SCHl fixed: unknown3 found\n"); /* loop end? */
|
||||
VGM_ASSERT(read_32bitLE(offset+0x14, streamFile) != 0, "EA SCHl fixed: unknown4 found\n"); /* data start? */
|
||||
VGM_ASSERT(read_32bitLE(offset+0x18, streamFile) != -1, "EA SCHl fixed: unknown5 found\n");
|
||||
VGM_ASSERT(read_32bitLE(offset+0x1c, streamFile) != 0x7F, "EA SCHl fixed: unknown6 found\n");
|
||||
|
||||
//ea->loop_flag = (ea->loop_end_sample);
|
||||
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
#include "meta.h"
|
||||
#include "../layout/layout.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* Possibly the same as EA_CODEC_x in variable SCHl */
|
||||
#define EA_CODEC_PCM 0x00
|
||||
#define EA_CODEC_IMA 0x02
|
||||
#define EA_CODEC_PSX 0x06
|
||||
|
||||
typedef struct {
|
||||
int8_t version;
|
||||
int8_t bps;
|
||||
int8_t channels;
|
||||
int8_t codec;
|
||||
int sample_rate;
|
||||
int32_t num_samples;
|
||||
|
||||
int big_endian;
|
||||
int loop_flag;
|
||||
} ea_fixed_header;
|
||||
|
||||
static int parse_fixed_header(STREAMFILE* sf, ea_fixed_header* ea);
|
||||
|
||||
|
||||
/* EA SCHl with fixed header - from EA games (~1997?) */
|
||||
VGMSTREAM* init_vgmstream_ea_schl_fixed(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
size_t header_size;
|
||||
ea_fixed_header ea = {0};
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!is_id32be(0x00,sf, "SCHl"))
|
||||
goto fail;
|
||||
|
||||
/* .asf: original [NHK 97 (PC)]
|
||||
* .lasf: fake for plugins
|
||||
* .cnk: ps1 [NBA Live 97 (PS1)] */
|
||||
if (!check_extensions(sf,"asf,lasf,cnk"))
|
||||
goto fail;
|
||||
|
||||
/* see ea_schl.c for more info about blocks */
|
||||
//TODO: handle SCCl? [NBA Live 97 (PS1)]
|
||||
|
||||
header_size = read_u32le(0x04,sf);
|
||||
|
||||
if (!parse_fixed_header(sf, &ea))
|
||||
goto fail;
|
||||
|
||||
start_offset = header_size;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(ea.channels, ea.loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = ea.sample_rate;
|
||||
vgmstream->num_samples = ea.num_samples;
|
||||
//vgmstream->loop_start_sample = ea.loop_start;
|
||||
//vgmstream->loop_end_sample = ea.loop_end;
|
||||
|
||||
vgmstream->codec_endian = ea.big_endian;
|
||||
|
||||
vgmstream->meta_type = meta_EA_SCHL_fixed;
|
||||
vgmstream->layout_type = layout_blocked_ea_schl;
|
||||
|
||||
switch (ea.codec) {
|
||||
case EA_CODEC_PCM:
|
||||
vgmstream->coding_type = ea.bps==8 ? coding_PCM8 : (ea.big_endian ? coding_PCM16BE : coding_PCM16LE);
|
||||
break;
|
||||
|
||||
case EA_CODEC_IMA:
|
||||
vgmstream->coding_type = coding_DVI_IMA; /* stereo/mono, high nibble first */
|
||||
break;
|
||||
|
||||
case EA_CODEC_PSX:
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
break;
|
||||
|
||||
default:
|
||||
VGM_LOG("EA: unknown codec 0x%02x\n", ea.codec);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static int parse_fixed_header(STREAMFILE* sf, ea_fixed_header* ea) {
|
||||
uint32_t offset = 0x00, size = 0;
|
||||
|
||||
if (is_id32be(offset+0x08, sf, "PATl"))
|
||||
offset = 0x08;
|
||||
else if (is_id32be(offset+0x0c, sf, "PATl"))
|
||||
offset = 0x0c; /* extra field in PS1 */
|
||||
else
|
||||
goto fail;
|
||||
|
||||
size = read_u32le(offset+0x34, sf);
|
||||
if (size == 0x20 && is_id32be(offset+0x38, sf, "TMpl")) { /* PC LE? */
|
||||
offset += 0x3c;
|
||||
|
||||
ea->version = read_u8 (offset+0x00, sf);
|
||||
ea->bps = read_u8 (offset+0x01, sf);
|
||||
ea->channels = read_u8 (offset+0x02, sf);
|
||||
ea->codec = read_u8 (offset+0x03, sf);
|
||||
/* 0x04: 0? */
|
||||
ea->sample_rate = read_u16le(offset+0x06, sf);
|
||||
ea->num_samples = read_s32le(offset+0x08, sf);
|
||||
/* 0x0c: -1? loop_start? */
|
||||
/* 0x10: -1? loop_end? */
|
||||
/* 0x14: 0? data start? */
|
||||
/* 0x18: -1? */
|
||||
/* 0x1c: volume? (always 128) */
|
||||
}
|
||||
else if (size == 0x38 && is_id32be(offset+0x38, sf, "TMxl")) { /* PSX LE? */
|
||||
offset += 0x3c;
|
||||
|
||||
ea->version = read_u8 (offset+0x00, sf);
|
||||
ea->bps = read_u8 (offset+0x01, sf);
|
||||
ea->channels = read_u8 (offset+0x02, sf);
|
||||
ea->codec = read_u8 (offset+0x03, sf);
|
||||
/* 0x04: 0? */
|
||||
ea->sample_rate = read_u16le(offset+0x06, sf);
|
||||
/* 0x08: 0x20C? */
|
||||
ea->num_samples = read_s32le(offset+0x0c, sf);
|
||||
/* 0x10: -1? loop_start? */
|
||||
/* 0x14: -1? loop_end? */
|
||||
/* 0x18: 0x20C? */
|
||||
/* 0x1c: 0? */
|
||||
/* 0x20: 0? */
|
||||
/* 0x24: 0? */
|
||||
/* 0x28: -1? */
|
||||
/* 0x2c: -1? */
|
||||
/* 0x30: -1? */
|
||||
/* 0x34: volume? (always 128) */
|
||||
}
|
||||
else {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
//ea->loop_flag = (ea->loop_end_sample);
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ VGMSTREAM* init_vgmstream_encrypted(STREAMFILE* sf) {
|
|||
goto fail;
|
||||
}
|
||||
|
||||
temp_sf = setup_ogg_vorbis_streamfile(sf, cfg);
|
||||
temp_sf = setup_ogg_vorbis_streamfile(sf, &cfg);
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
vgmstream = init_vgmstream_ogg_vorbis(temp_sf);
|
||||
|
@ -53,7 +53,7 @@ VGMSTREAM* init_vgmstream_encrypted(STREAMFILE* sf) {
|
|||
goto fail;
|
||||
}
|
||||
|
||||
temp_sf = setup_ogg_vorbis_streamfile(sf, cfg);
|
||||
temp_sf = setup_ogg_vorbis_streamfile(sf, &cfg);
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
#ifdef VGM_USE_FFMPEG //TODO: allow MP3 without FFmpeg
|
||||
|
@ -72,7 +72,7 @@ VGMSTREAM* init_vgmstream_encrypted(STREAMFILE* sf) {
|
|||
goto fail;
|
||||
}
|
||||
|
||||
temp_sf = setup_ogg_vorbis_streamfile(sf, cfg);
|
||||
temp_sf = setup_ogg_vorbis_streamfile(sf, &cfg);
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
vgmstream = init_vgmstream_riff(temp_sf);
|
||||
|
@ -80,12 +80,13 @@ VGMSTREAM* init_vgmstream_encrypted(STREAMFILE* sf) {
|
|||
return vgmstream;
|
||||
}
|
||||
|
||||
if (check_extensions(sf,"bgm")) {
|
||||
if (check_extensions(sf,"bgm,mse,koe")) {
|
||||
/* Studio Ring games [Nanami to Konomi no Oshiete ABC (PC), Oyatsu no Jikan (PC)]
|
||||
* .bgm: BGM, .mse: SE, .koe: Voice */
|
||||
uint8_t keybuf[0x100];
|
||||
size_t key_size;
|
||||
off_t start;
|
||||
|
||||
/* Studio Ring games [Nanami to Konomi no Oshiete ABC (PC), Oyatsu no Jikan (PC)] */
|
||||
if (id != get_id32be("RIFF"))
|
||||
goto fail;
|
||||
|
||||
|
|
|
@ -221,7 +221,7 @@ VGMSTREAM* init_vgmstream_fsb(STREAMFILE* sf) {
|
|||
}
|
||||
else {
|
||||
/* subsong header for normal files */
|
||||
stream_header_size = (uint16_t)read_16bitLE(header_offset+0x00,sf);
|
||||
stream_header_size = read_u16le(header_offset+0x00,sf);
|
||||
fsb.name_offset = header_offset+0x02;
|
||||
fsb.name_size = 0x20-0x02;
|
||||
fsb.num_samples = read_32bitLE(header_offset+0x20,sf);
|
||||
|
@ -231,7 +231,7 @@ VGMSTREAM* init_vgmstream_fsb(STREAMFILE* sf) {
|
|||
fsb.mode = read_32bitLE(header_offset+0x30,sf);
|
||||
fsb.sample_rate = read_32bitLE(header_offset+0x34,sf);
|
||||
/* 0x38: defvol, 0x3a: defpan, 0x3c: defpri */
|
||||
fsb.channels = read_16bitLE(header_offset+0x3e,sf);
|
||||
fsb.channels = read_u16le(header_offset+0x3e,sf);
|
||||
/* FSB3.1/4:
|
||||
* 0x40: mindistance, 0x44: maxdistance, 0x48: varfreq/size_32bits
|
||||
* 0x4c: varvol, 0x4e: fsb.varpan */
|
||||
|
@ -287,9 +287,9 @@ VGMSTREAM* init_vgmstream_fsb(STREAMFILE* sf) {
|
|||
//;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 ripping */
|
||||
VGM_ASSERT(fsb.base_header_size + fsb.sample_headers_size + fsb.sample_data_size != sf->get_size(sf),
|
||||
vgm_asserti(fsb.base_header_size + fsb.sample_headers_size + fsb.sample_data_size != get_streamfile_size(sf),
|
||||
"FSB wrong head/data_size found (expected 0x%x vs 0x%x)\n",
|
||||
fsb.base_header_size + fsb.sample_headers_size + fsb.sample_data_size, sf->get_size(sf));
|
||||
fsb.base_header_size + fsb.sample_headers_size + fsb.sample_data_size, get_streamfile_size(sf));
|
||||
|
||||
/* autodetect unwanted loops */
|
||||
{
|
||||
|
@ -319,7 +319,7 @@ VGMSTREAM* init_vgmstream_fsb(STREAMFILE* sf) {
|
|||
|
||||
fsb.loop_flag = !(fsb.mode & FSOUND_LOOP_OFF); /* disabled manually */
|
||||
if (fsb.loop_flag && !enable_loop && full_loop && is_small) {
|
||||
VGM_LOG("FSB: disable unwanted loop\n");
|
||||
VGM_LOG("FSB: disabled unwanted loop\n");
|
||||
fsb.loop_flag = 0;
|
||||
}
|
||||
}
|
||||
|
@ -426,8 +426,8 @@ VGMSTREAM* init_vgmstream_fsb(STREAMFILE* sf) {
|
|||
/* get libcelt version (set in the first subsong only, but try all extradata just in case) */
|
||||
if (fsb.first_extradata_offset || fsb.extradata_offset) {
|
||||
uint32_t lib = fsb.first_extradata_offset ?
|
||||
(uint32_t)read_32bitLE(fsb.first_extradata_offset, sf) :
|
||||
(uint32_t)read_32bitLE(fsb.extradata_offset, sf);;
|
||||
read_u32le(fsb.first_extradata_offset, sf) :
|
||||
read_u32le(fsb.extradata_offset, sf);
|
||||
switch(lib) {
|
||||
case 0x80000009: is_new_lib = 0; break; /* War Thunder (PC) */
|
||||
case 0x80000010: is_new_lib = 1; break; /* Vessel (PC) */
|
||||
|
@ -436,7 +436,7 @@ VGMSTREAM* init_vgmstream_fsb(STREAMFILE* sf) {
|
|||
}
|
||||
else {
|
||||
/* split FSBs? try to guess from observed bitstreams */
|
||||
uint16_t frame = (uint16_t)read_16bitBE(fsb.stream_offset+0x04+0x04,sf);
|
||||
uint16_t frame = read_u16be(fsb.stream_offset+0x04+0x04,sf);
|
||||
if ((frame & 0xF000) == 0x6000 || frame == 0xFFFE) {
|
||||
is_new_lib = 1;
|
||||
} else {
|
||||
|
|
|
@ -3,11 +3,14 @@
|
|||
#include "../coding/coding.h"
|
||||
#include "../coding/hca_decoder_clhca.h"
|
||||
|
||||
//#define HCA_BRUTEFORCE
|
||||
#ifdef HCA_BRUTEFORCE
|
||||
static void bruteforce_hca_key(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* p_keycode, uint16_t subkey);
|
||||
#ifdef VGM_DEBUG_OUTPUT
|
||||
//#define HCA_BRUTEFORCE
|
||||
#ifdef HCA_BRUTEFORCE
|
||||
#include "hca_bf.h"
|
||||
#endif
|
||||
#endif
|
||||
static void find_hca_key(hca_codec_data* hca_data, uint64_t* p_keycode, uint16_t subkey);
|
||||
|
||||
static int find_hca_key(hca_codec_data* hca_data, uint64_t* p_keycode, uint16_t subkey);
|
||||
|
||||
|
||||
/* CRI HCA - streamed audio from CRI ADX2/Atom middleware */
|
||||
|
@ -57,7 +60,9 @@ VGMSTREAM* init_vgmstream_hca_subkey(STREAMFILE* sf, uint16_t subkey) {
|
|||
}
|
||||
#ifdef HCA_BRUTEFORCE
|
||||
else if (1) {
|
||||
bruteforce_hca_key(sf, hca_data, &keycode, subkey);
|
||||
int ok = find_hca_key(hca_data, &keycode, subkey);
|
||||
if (!ok)
|
||||
bruteforce_hca_key(sf, hca_data, &keycode, subkey);
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
|
@ -123,60 +128,32 @@ fail:
|
|||
}
|
||||
|
||||
|
||||
static inline void test_key(hca_codec_data* hca_data, uint64_t key, uint16_t subkey, int* best_score, uint64_t* best_keycode) {
|
||||
int score;
|
||||
|
||||
//;VGM_LOG("HCA: test key=%08x%08x, subkey=%04x\n",
|
||||
// (uint32_t)((key >> 32) & 0xFFFFFFFF), (uint32_t)(key & 0xFFFFFFFF), subkey);
|
||||
|
||||
if (subkey) {
|
||||
key = key * ( ((uint64_t)subkey << 16u) | ((uint16_t)~subkey + 2u) );
|
||||
}
|
||||
|
||||
score = test_hca_key(hca_data, (unsigned long long)key);
|
||||
|
||||
//;VGM_LOG("HCA: test key=%08x%08x, subkey=%04x, score=%i\n",
|
||||
// (uint32_t)((key >> 32) & 0xFFFFFFFF), (uint32_t)(key & 0xFFFFFFFF), subkey, score);
|
||||
|
||||
/* wrong key */
|
||||
if (score < 0)
|
||||
return;
|
||||
|
||||
//;VGM_LOG("HCA: ok key=%08x%08x, subkey=%04x, score=%i\n",
|
||||
// (uint32_t)((key >> 32) & 0xFFFFFFFF), (uint32_t)(key & 0xFFFFFFFF), subkey, score);
|
||||
|
||||
/* update if something better is found */
|
||||
if (*best_score <= 0 || (score < *best_score && score > 0)) {
|
||||
*best_score = score;
|
||||
*best_keycode = key;
|
||||
}
|
||||
}
|
||||
|
||||
/* try to find the decryption key from a list. */
|
||||
static void find_hca_key(hca_codec_data* hca_data, uint64_t* p_keycode, uint16_t subkey) {
|
||||
const size_t keys_length = sizeof(hcakey_list) / sizeof(hcakey_info);
|
||||
int best_score = -1;
|
||||
/* try to find the decryption key from a list */
|
||||
static int find_hca_key(hca_codec_data* hca_data, uint64_t* p_keycode, uint16_t subkey) {
|
||||
const size_t keys_length = sizeof(hcakey_list) / sizeof(hcakey_list[0]);
|
||||
int i;
|
||||
hca_keytest_t hk = {0};
|
||||
|
||||
*p_keycode = 0xCC55463930DBE1AB; /* defaults to PSO2 key, most common */
|
||||
hk.best_key = 0xCC55463930DBE1AB; /* defaults to PSO2 key, most common */
|
||||
hk.subkey = subkey;
|
||||
|
||||
for (i = 0; i < keys_length; i++) {
|
||||
uint64_t key = hcakey_list[i].key;
|
||||
hk.key = hcakey_list[i].key;
|
||||
|
||||
test_key(hca_data, key, subkey, &best_score, p_keycode);
|
||||
if (best_score == 1)
|
||||
test_hca_key(hca_data, &hk);
|
||||
if (hk.best_score == 1)
|
||||
goto done;
|
||||
|
||||
#if 0
|
||||
{
|
||||
int j;
|
||||
|
||||
size_t subkeys_size = hcakey_list[i].subkeys_size;
|
||||
const uint16_t *subkeys = hcakey_list[i].subkeys;
|
||||
const uint16_t* subkeys = hcakey_list[i].subkeys;
|
||||
if (subkeys_size > 0 && subkey == 0) {
|
||||
for (j = 0; j < subkeys_size; j++) {
|
||||
test_key(hca_data, key, subkeys[j], &best_score, p_keycode);
|
||||
if (best_score == 1)
|
||||
hk.subkey = subkeys[j];
|
||||
test_hca_key(hca_data, &hk);
|
||||
if (hk.best_score == 1)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
@ -185,201 +162,9 @@ static void find_hca_key(hca_codec_data* hca_data, uint64_t* p_keycode, uint16_t
|
|||
}
|
||||
|
||||
done:
|
||||
VGM_ASSERT(best_score > 1, "HCA: best key=%08x%08x (score=%i)\n",
|
||||
(uint32_t)((*p_keycode >> 32) & 0xFFFFFFFF), (uint32_t)(*p_keycode & 0xFFFFFFFF), best_score);
|
||||
vgm_asserti(best_score < 0, "HCA: decryption key not found\n");
|
||||
*p_keycode = hk.best_key;
|
||||
VGM_ASSERT(hk.best_score > 1, "HCA: best key=%08x%08x (score=%i)\n",
|
||||
(uint32_t)((*p_keycode >> 32) & 0xFFFFFFFF), (uint32_t)(*p_keycode & 0xFFFFFFFF), hk.best_score);
|
||||
vgm_asserti(hk.best_score <= 0, "HCA: decryption key not found\n");
|
||||
return hk.best_score > 0;
|
||||
}
|
||||
|
||||
#ifdef HCA_BRUTEFORCE
|
||||
typedef enum {
|
||||
HBF_TYPE_64LE_1,
|
||||
HBF_TYPE_64BE_1,
|
||||
HBF_TYPE_32LE_1,
|
||||
HBF_TYPE_32BE_1,
|
||||
HBF_TYPE_64LE_4,
|
||||
HBF_TYPE_64BE_4,
|
||||
HBF_TYPE_32LE_4,
|
||||
HBF_TYPE_32BE_4,
|
||||
} HBF_type_t;
|
||||
|
||||
/* Bruteforce binary keys in executables and similar files, mainly for some mobile games.
|
||||
* Kinda slow but acceptable for ~20MB exes, not very optimized. Unity usually has keys
|
||||
* in plaintext (inside levelX or other base files) instead though. */
|
||||
static void bruteforce_hca_key_bin_type(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* p_keycode, uint16_t subkey, HBF_type_t type) {
|
||||
STREAMFILE* sf_keys = NULL;
|
||||
uint8_t* buf = NULL;
|
||||
int best_score = 0xFFFFFF, cur_score;
|
||||
off_t keys_size, bytes;
|
||||
int pos;
|
||||
uint64_t old_key = 0;
|
||||
uint64_t key = 0;
|
||||
|
||||
|
||||
/* load whole file in memory for performance (exes with keys shouldn't be too big) */
|
||||
sf_keys = open_streamfile_by_filename(sf, "keys.bin");
|
||||
if (!sf_keys) return;
|
||||
|
||||
VGM_LOG("HCA: test keys.bin (type %i)\n", type);
|
||||
*p_keycode = 0;
|
||||
|
||||
keys_size = get_streamfile_size(sf_keys);
|
||||
|
||||
buf = malloc(keys_size);
|
||||
if (!buf) goto done;
|
||||
|
||||
bytes = read_streamfile(buf, 0, keys_size, sf_keys);
|
||||
if (bytes != keys_size) goto done;
|
||||
|
||||
VGM_LOG("HCA: start\n");
|
||||
|
||||
pos = 0;
|
||||
while (pos < keys_size - 4) {
|
||||
VGM_ASSERT(pos % 0x1000000 == 0, "HCA: pos %x...\n", pos);
|
||||
|
||||
/* keys are usually u64le but other orders may exist */
|
||||
switch(type) {
|
||||
case HBF_TYPE_64LE_1: key = get_u64le(buf + pos); pos += 0x01; break;
|
||||
case HBF_TYPE_64BE_1: key = get_u64be(buf + pos); pos += 0x01; break;
|
||||
case HBF_TYPE_32LE_1: key = get_u32le(buf + pos); pos += 0x01; break;
|
||||
case HBF_TYPE_32BE_1: key = get_u32be(buf + pos); pos += 0x01; break;
|
||||
case HBF_TYPE_64LE_4: key = get_u64le(buf + pos); pos += 0x04; break;
|
||||
case HBF_TYPE_64BE_4: key = get_u64be(buf + pos); pos += 0x04; break;
|
||||
case HBF_TYPE_32LE_4: key = get_u32le(buf + pos); pos += 0x04; break;
|
||||
case HBF_TYPE_32BE_4: key = get_u32be(buf + pos); pos += 0x04; break;
|
||||
default: key = 0; pos = keys_size; break;
|
||||
}
|
||||
|
||||
if (key == 0 || key == old_key)
|
||||
continue;
|
||||
old_key = key;
|
||||
|
||||
cur_score = 0;
|
||||
test_key(hca_data, key, subkey, &cur_score, p_keycode);
|
||||
if (cur_score == 1) {
|
||||
best_score = cur_score;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (cur_score > 0 && cur_score <= 500) {
|
||||
VGM_LOG("HCA: possible key=%08x%08x (score=%i) at %x\n",
|
||||
(uint32_t)((key >> 32) & 0xFFFFFFFF), (uint32_t)(key & 0xFFFFFFFF), cur_score, pos-0x04);
|
||||
if (best_score > cur_score)
|
||||
best_score = cur_score;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
if (best_score < 0 || best_score > 10000) {
|
||||
*p_keycode = 0;
|
||||
VGM_LOG("HCA: good key not found\n");
|
||||
}
|
||||
else {
|
||||
/* print key as p_keycode includes subkey */
|
||||
VGM_LOG("HCA: best key=%08x%08x (score=%i)\n",
|
||||
(uint32_t)((key >> 32) & 0xFFFFFFFF), (uint32_t)(key & 0xFFFFFFFF), best_score);
|
||||
}
|
||||
|
||||
close_streamfile(sf_keys);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
static void bruteforce_hca_key_bin(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* p_keycode, uint16_t subkey) {
|
||||
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64LE_1);
|
||||
/*
|
||||
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32LE_1);
|
||||
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64BE_1);
|
||||
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32BE_1);
|
||||
|
||||
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64LE_4);
|
||||
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32LE_4);
|
||||
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64BE_4);
|
||||
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32BE_4);
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
#include <inttypes.h>
|
||||
//#include <stdio.h>
|
||||
|
||||
/* same as the above but for txt lines. */
|
||||
static void bruteforce_hca_key_txt(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* p_keycode, uint16_t subkey) {
|
||||
STREAMFILE* sf_keys = NULL;
|
||||
uint8_t* buf = NULL;
|
||||
int best_score = 0xFFFFFF, cur_score;
|
||||
off_t keys_size, bytes;
|
||||
int i = 0, pos;
|
||||
char line[1024];
|
||||
|
||||
|
||||
|
||||
/* load whole file in memory for performance (exes with keys shouldn't be too big) */
|
||||
sf_keys = open_streamfile_by_filename(sf, "keys.txt");
|
||||
if (!sf_keys) return;
|
||||
|
||||
VGM_LOG("HCA: test keys.txt\n");
|
||||
*p_keycode = 0;
|
||||
|
||||
keys_size = get_streamfile_size(sf_keys);
|
||||
|
||||
buf = malloc(keys_size);
|
||||
if (!buf) goto done;
|
||||
|
||||
bytes = read_streamfile(buf, 0, keys_size, sf_keys);
|
||||
if (bytes != keys_size) goto done;
|
||||
|
||||
VGM_LOG("HCA: start\n");
|
||||
|
||||
pos = 0;
|
||||
while (pos < keys_size) {
|
||||
int bytes_read, line_ok, count;
|
||||
uint64_t key = 0;
|
||||
|
||||
bytes_read = read_line(line, sizeof(line), pos, sf_keys, &line_ok);
|
||||
pos += bytes_read;
|
||||
if (!line_ok) continue; /* line too long */
|
||||
|
||||
count = sscanf(line, "%" SCNd64, &key);
|
||||
if (count != 1) continue;
|
||||
|
||||
VGM_ASSERT(pos % 10000 == 0, "HCA: count %i...\n", i);
|
||||
|
||||
if (key == 0)
|
||||
continue;
|
||||
i++;
|
||||
|
||||
cur_score = 0;
|
||||
test_key(hca_data, key, subkey, &cur_score, p_keycode);
|
||||
if (cur_score == 1)
|
||||
goto done;
|
||||
|
||||
if (cur_score > 0 && cur_score <= 500) {
|
||||
VGM_LOG("HCA: possible key=%08x%08x (score=%i) at %x\n",
|
||||
(uint32_t)((key >> 32) & 0xFFFFFFFF), (uint32_t)(key & 0xFFFFFFFF), cur_score, pos-0x04);
|
||||
if (best_score > cur_score)
|
||||
best_score = cur_score;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
VGM_LOG("HCA: done %i keys.txt\n", i);
|
||||
VGM_ASSERT(best_score > 0, "HCA: best key=%08x%08x (score=%i)\n",
|
||||
(uint32_t)((*p_keycode >> 32) & 0xFFFFFFFF), (uint32_t)(*p_keycode & 0xFFFFFFFF), best_score);
|
||||
VGM_ASSERT(best_score < 0, "HCA: key not found\n");
|
||||
if (best_score < 0 || best_score > 10000)
|
||||
*p_keycode = 0;
|
||||
|
||||
close_streamfile(sf_keys);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
static void bruteforce_hca_key(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* p_keycode, uint16_t subkey) {
|
||||
bruteforce_hca_key_bin(sf, hca_data, p_keycode, subkey);
|
||||
if (*p_keycode != 0)
|
||||
return;
|
||||
|
||||
bruteforce_hca_key_txt(sf, hca_data, p_keycode, subkey);
|
||||
if (*p_keycode != 0)
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,257 @@
|
|||
#ifndef _HCA_BF_
|
||||
#define _HCA_BF_
|
||||
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
#ifdef HCA_BRUTEFORCE
|
||||
|
||||
static void bruteforce_process_result(hca_keytest_t* hk, unsigned long long* p_keycode) {
|
||||
*p_keycode = hk->best_key;
|
||||
if (hk->best_score < 0 || hk->best_score > 10000) {
|
||||
VGM_LOG("HCA BF: no good key found\n");
|
||||
}
|
||||
else {
|
||||
VGM_LOG("HCA BF: best key=%08x%08x (score=%i)\n",
|
||||
(uint32_t)((*p_keycode >> 32) & 0xFFFFFFFF), (uint32_t)(*p_keycode & 0xFFFFFFFF), hk->best_score);
|
||||
}
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
HBF_TYPE_64LE_1,
|
||||
HBF_TYPE_64BE_1,
|
||||
HBF_TYPE_32LE_1,
|
||||
HBF_TYPE_32BE_1,
|
||||
HBF_TYPE_64LE_4,
|
||||
HBF_TYPE_64BE_4,
|
||||
HBF_TYPE_32LE_4,
|
||||
HBF_TYPE_32BE_4,
|
||||
} HBF_type_t;
|
||||
|
||||
/* Bruteforce binary keys in executables and similar files, mainly for some mobile games.
|
||||
* Kinda slow but acceptable for ~100MB exes, not very optimized. Unity usually has keys
|
||||
* in plaintext (inside levelX or other base files) instead though, use test below. */
|
||||
static void bruteforce_hca_key_bin_type(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* p_keycode, uint16_t subkey, HBF_type_t type) {
|
||||
STREAMFILE* sf_keys = NULL;
|
||||
uint8_t* buf = NULL;
|
||||
uint32_t keys_size, bytes;
|
||||
int pos, step;
|
||||
uint64_t key = 0, old_key = 0;
|
||||
hca_keytest_t hk = {0};
|
||||
|
||||
hk.subkey = subkey;
|
||||
|
||||
|
||||
/* load whole file in memory for performance (exes with keys shouldn't be too big) */
|
||||
sf_keys = open_streamfile_by_filename(sf, "keys.bin");
|
||||
if (!sf_keys) return;
|
||||
|
||||
VGM_LOG("HCA BF: test keys.bin (type %i)\n", type);
|
||||
*p_keycode = 0;
|
||||
|
||||
keys_size = get_streamfile_size(sf_keys);
|
||||
|
||||
buf = malloc(keys_size);
|
||||
if (!buf) {
|
||||
VGM_LOG("HCA BF: key file too big!\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
bytes = read_streamfile(buf, 0, keys_size, sf_keys);
|
||||
if (bytes != keys_size) goto done;
|
||||
|
||||
VGM_LOG("HCA BF: start .bin\n");
|
||||
|
||||
switch(type) {
|
||||
case HBF_TYPE_64LE_1:
|
||||
case HBF_TYPE_64BE_1:
|
||||
case HBF_TYPE_32LE_1:
|
||||
case HBF_TYPE_32BE_1: step = 0x01; break;
|
||||
case HBF_TYPE_64LE_4:
|
||||
case HBF_TYPE_64BE_4:
|
||||
case HBF_TYPE_32LE_4:
|
||||
case HBF_TYPE_32BE_4: step = 0x04; break;
|
||||
default: goto done;
|
||||
}
|
||||
|
||||
pos = 0;
|
||||
while (pos < keys_size - 8) {
|
||||
VGM_ASSERT(pos % 0x1000000 == 0, "HCA: pos %x...\n", pos);
|
||||
|
||||
/* keys are usually u64le but other orders may exist */
|
||||
switch(type) {
|
||||
case HBF_TYPE_64LE_1: key = get_u64le(buf + pos); break;
|
||||
case HBF_TYPE_64BE_1: key = get_u64be(buf + pos); break;
|
||||
case HBF_TYPE_32LE_1: key = get_u32le(buf + pos); break;
|
||||
case HBF_TYPE_32BE_1: key = get_u32be(buf + pos); break;
|
||||
case HBF_TYPE_64LE_4: key = get_u64le(buf + pos); break;
|
||||
case HBF_TYPE_64BE_4: key = get_u64be(buf + pos); break;
|
||||
case HBF_TYPE_32LE_4: key = get_u32le(buf + pos); break;
|
||||
case HBF_TYPE_32BE_4: key = get_u32be(buf + pos); break;
|
||||
default: goto done;
|
||||
}
|
||||
pos += step;
|
||||
|
||||
if (key == 0 || key == old_key)
|
||||
continue;
|
||||
old_key = key;
|
||||
|
||||
hk.key = key;
|
||||
test_hca_key(hca_data, &hk);
|
||||
if (hk.best_score == 1)
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
bruteforce_process_result(&hk, p_keycode);
|
||||
close_streamfile(sf_keys);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
static void bruteforce_hca_key_bin(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* p_keycode, uint16_t subkey) {
|
||||
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64LE_1);
|
||||
/*
|
||||
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32LE_1);
|
||||
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64BE_1);
|
||||
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32BE_1);
|
||||
|
||||
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64LE_4);
|
||||
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32LE_4);
|
||||
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64BE_4);
|
||||
bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32BE_4);
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
#include <inttypes.h>
|
||||
//#include <stdio.h>
|
||||
|
||||
/* same as the above but for txt lines. */
|
||||
static void bruteforce_hca_key_txt(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* p_keycode, uint16_t subkey) {
|
||||
STREAMFILE* sf_keys = NULL;
|
||||
uint8_t* buf = NULL;
|
||||
uint32_t keys_size, bytes;
|
||||
char line[1024];
|
||||
int i = 0, pos;
|
||||
uint64_t key = 0;
|
||||
hca_keytest_t hk = {0};
|
||||
|
||||
hk.subkey = subkey;
|
||||
|
||||
|
||||
/* load whole file in memory for performance (exes with keys shouldn't be too big) */
|
||||
sf_keys = open_streamfile_by_filename(sf, "keys.txt");
|
||||
if (!sf_keys) return;
|
||||
|
||||
VGM_LOG("HCA BF: test keys.txt\n");
|
||||
*p_keycode = 0;
|
||||
|
||||
keys_size = get_streamfile_size(sf_keys);
|
||||
|
||||
buf = malloc(keys_size);
|
||||
if (!buf) {
|
||||
VGM_LOG("HCA BF: key file too big!\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
bytes = read_streamfile(buf, 0, keys_size, sf_keys);
|
||||
if (bytes != keys_size) goto done;
|
||||
|
||||
VGM_LOG("HCA BF: start .txt\n");
|
||||
|
||||
pos = 0;
|
||||
while (pos < keys_size) {
|
||||
int bytes_read, line_ok, count;
|
||||
key = 0;
|
||||
|
||||
bytes_read = read_line(line, sizeof(line), pos, sf_keys, &line_ok);
|
||||
pos += bytes_read;
|
||||
if (!line_ok) continue; /* line too long */
|
||||
|
||||
count = sscanf(line, "%" SCNd64, &key);
|
||||
if (count != 1) continue;
|
||||
|
||||
VGM_ASSERT(pos % 10000 == 0, "HCA: count %i...\n", i);
|
||||
|
||||
if (key == 0)
|
||||
continue;
|
||||
i++;
|
||||
|
||||
hk.key = key;
|
||||
test_hca_key(hca_data, &hk);
|
||||
if (hk.best_score == 1)
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
bruteforce_process_result(&hk, p_keycode);
|
||||
close_streamfile(sf_keys);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
/* same as the above but good ol' bruteforce numbers (useful for games with keys that are dates) */
|
||||
static void bruteforce_hca_key_num(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* p_keycode, uint16_t subkey) {
|
||||
STREAMFILE* sf_keys = NULL;
|
||||
uint32_t keys_size;
|
||||
uint64_t min, max;
|
||||
uint64_t key = 0;
|
||||
hca_keytest_t hk = {0};
|
||||
|
||||
hk.subkey = subkey;
|
||||
|
||||
|
||||
/* load whole file in memory for performance (exes with keys shouldn't be too big) */
|
||||
sf_keys = open_streamfile_by_filename(sf, "keys.num");
|
||||
if (!sf_keys) return;
|
||||
|
||||
VGM_LOG("HCA BF: test keys.num\n");
|
||||
*p_keycode = 0;
|
||||
|
||||
keys_size = get_streamfile_size(sf_keys);
|
||||
|
||||
/* don't set too high as it does ~70000 keys per second, do the math */
|
||||
if (keys_size < 0x10) {
|
||||
min = 0;
|
||||
max = 0xFFFFFFFF;
|
||||
}
|
||||
else {
|
||||
min = read_u64be(0x00, sf_keys);
|
||||
max = read_u64be(0x08, sf_keys);
|
||||
}
|
||||
|
||||
VGM_LOG("HCA BF: start .num\n");
|
||||
|
||||
while (min < max) {
|
||||
key = min;
|
||||
|
||||
min++;
|
||||
VGM_ASSERT(min % 0x100000 == 0, "HCA: count %x...\n", (uint32_t)min);
|
||||
|
||||
hk.key = key;
|
||||
test_hca_key(hca_data, &hk);
|
||||
if (hk.best_score == 1)
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
bruteforce_process_result(&hk, p_keycode);
|
||||
close_streamfile(sf_keys);
|
||||
}
|
||||
|
||||
static void bruteforce_hca_key(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* p_keycode, uint16_t subkey) {
|
||||
bruteforce_hca_key_bin(sf, hca_data, p_keycode, subkey);
|
||||
if (*p_keycode != 0)
|
||||
return;
|
||||
|
||||
bruteforce_hca_key_txt(sf, hca_data, p_keycode, subkey);
|
||||
if (*p_keycode != 0)
|
||||
return;
|
||||
|
||||
bruteforce_hca_key_num(sf, hca_data, p_keycode, subkey);
|
||||
if (*p_keycode != 0)
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif /*_HCA_BF_*/
|
|
@ -48,7 +48,7 @@ static const hcakey_info hcakey_list[] = {
|
|||
// - Ro-Kyu-Bu! Naisho no Shutter Chance (PSVita)
|
||||
{1234253142}, // 0000000049913556
|
||||
|
||||
// Idolm@ster Cinderella Stage (iOS/Android)
|
||||
// THE iDOLM@STER Cinderella Girls: Starlight Stage (iOS/Android)
|
||||
// Shadowverse (iOS/Android)
|
||||
{59751358413602}, // 00003657F27E3B22
|
||||
|
||||
|
@ -67,16 +67,15 @@ static const hcakey_info hcakey_list[] = {
|
|||
// Sonic Runners (iOS/Android)
|
||||
{19910623}, // 00000000012FCFDF
|
||||
|
||||
// Fate/Grand Order (iOS/Android) base assets
|
||||
{12345}, // 0000000000003039
|
||||
|
||||
// Fate/Grand Order (iOS/Android) download assets *unconfirmed
|
||||
{9117927877783581796}, // 7E89631892EBF464
|
||||
// Fate/Grand Order (iOS/Android)
|
||||
{12345}, // 0000000000003039 - base assets
|
||||
{9117927877783581796}, // 7E89631892EBF464 - downloaded assets *unconfirmed
|
||||
|
||||
// Raramagi (iOS/Android)
|
||||
{45719322}, // 0000000002B99F1A
|
||||
|
||||
// Idolm@ster Million Live (iOS/Android)
|
||||
// THE iDOLM@STER Million Live! (iOS/Android)
|
||||
// THE iDOLM@STER SideM GROWING STARS (Android)
|
||||
{765765765765765}, // 0002B875BC731A85
|
||||
|
||||
// Kurokishi to Shiro no Maou (iOS/Android)
|
||||
|
@ -775,7 +774,48 @@ static const hcakey_info hcakey_list[] = {
|
|||
|
||||
// Nogizaka 46 Fractal (Android)
|
||||
{984635491346198130}, // 0DAA20C336EEAE72
|
||||
|
||||
|
||||
// NEO: The World Ends With You (PC)
|
||||
{53346933792338754}, // 00BD86C0EE8C7342
|
||||
|
||||
// THE iDOLM@STER Starlit Season (PS4/PC)
|
||||
{0x1e03b570b6145d1d}, // BGM
|
||||
{0x1da915aaa181a461}, // SE
|
||||
{0x1c82b6ab7487a5ec}, // Voice
|
||||
{0x6d275d3666c2f9c8}, // Sng001
|
||||
{0x0f53815df3044e6d}, // Sng002
|
||||
{0x158778e2e2fab347}, // Sng003
|
||||
{0x16b75e8b5247d46b}, // Sng004
|
||||
{0x157df8a6047048fc}, // Sng005
|
||||
{0x184d358b50b658d0}, // Sng006
|
||||
{0x157fb75af4ddd983}, // Sng007
|
||||
{0x404ba38c3e470827}, // Sng008
|
||||
{0x01d0b788a3b60d48}, // Sng009
|
||||
{0x021718d55d0960c9}, // Sng010
|
||||
{0x0021c5993d2b901c}, // Sng011
|
||||
{0x08237bcb9b711087}, // Sng012
|
||||
{0x01af60402e1228a5}, // Sng013
|
||||
{0x4eec18ab73a1a634}, // Sng014
|
||||
{0x1855099898b11ad9}, // Sng015
|
||||
{0x57ef8f2ea5d54db5}, // Sng016
|
||||
{0x17cc6975d67e2a1f}, // Sng017
|
||||
{0x0a5d0fc8cc5c4502}, // Sng018
|
||||
{0x198ea1a17416050b}, // Sng019
|
||||
{0x2aa3b8abad207a1e}, // Sng020
|
||||
{0x33d98a3a9f9bfdef}, // Sng026
|
||||
{0x2284fd5ca82c78f4}, // Sng027
|
||||
{0x178a76b6436d20f0}, // Sng028
|
||||
{0x3ff99f2fed65a1ed}, // Sng030
|
||||
|
||||
// Ulala: Idle Adventure (Android)
|
||||
{20191022}, // 000000000134172E
|
||||
|
||||
// Girls' Frontline: Project Neural Cloud (Android)
|
||||
{210222522032314}, // 0000BF323EBFE0BA
|
||||
|
||||
// Super Robot Wars 30 (PC)
|
||||
{6734488621090458}, // 0017ECFB5201069A
|
||||
|
||||
};
|
||||
|
||||
#endif/*_HCA_KEYS_H_*/
|
||||
|
|
|
@ -2,74 +2,50 @@
|
|||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
/* IKM - MiCROViSiON PS2 container [Zwei (PS2)] */
|
||||
VGMSTREAM* init_vgmstream_ikm_ps2(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
static VGMSTREAM* init_vgmstream_ikm_ps2(STREAMFILE* sf) {
|
||||
VGMSTREAM* v = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag, channel_count;
|
||||
int loop_flag, channels;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!is_id32be(0x00,sf, "IKM\0"))
|
||||
goto fail;
|
||||
if (!check_extensions(sf,"ikm"))
|
||||
goto fail;
|
||||
|
||||
/* 0x20: type 03? */
|
||||
if (!is_id32be(0x40,sf, "AST\0"))
|
||||
goto fail;
|
||||
|
||||
loop_flag = (read_s32le(0x14, sf) > 0);
|
||||
channel_count = read_s32le(0x50, sf);
|
||||
channels = read_s32le(0x50, sf);
|
||||
start_offset = 0x800;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
v = allocate_vgmstream(channels, loop_flag);
|
||||
if (!v) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_IKM;
|
||||
vgmstream->sample_rate = read_s32le(0x44, sf);
|
||||
vgmstream->num_samples = ps_bytes_to_samples(read_s32le(0x4c, sf), channel_count);
|
||||
vgmstream->loop_start_sample = read_s32le(0x14, sf);
|
||||
vgmstream->loop_end_sample = read_s32le(0x18, sf);
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x10; /* @0x40 / channels */
|
||||
v->meta_type = meta_IKM;
|
||||
v->sample_rate = read_s32le(0x44, sf);
|
||||
v->num_samples = ps_bytes_to_samples(read_s32le(0x4c, sf), channels);
|
||||
v->loop_start_sample = read_s32le(0x14, sf);
|
||||
v->loop_end_sample = read_s32le(0x18, sf);
|
||||
v->coding_type = coding_PSX;
|
||||
v->layout_type = layout_interleave;
|
||||
v->interleave_block_size = 0x10; /* @0x40 / channels */
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
if (!vgmstream_open_stream(v, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
return v;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
close_vgmstream(v);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* IKM - MiCROViSiON PC container [Chaos Legion (PC), Legend of Galactic Heroes (PC)] */
|
||||
VGMSTREAM* init_vgmstream_ikm_pc(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
static VGMSTREAM* init_vgmstream_ikm_pc(STREAMFILE* sf) {
|
||||
VGMSTREAM* v = NULL;
|
||||
off_t start_offset;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!is_id32be(0x00,sf, "IKM\0"))
|
||||
goto fail;
|
||||
if (!check_extensions(sf,"ikm"))
|
||||
goto fail;
|
||||
/* 0x20: type 01? */
|
||||
|
||||
/* find "OggS" start */
|
||||
if (is_id32be(0x30,sf, "OggS")) {
|
||||
if (is_id32be(0x30,sf, "OggS"))
|
||||
start_offset = 0x30; /* Chaos Legion (PC) */
|
||||
}
|
||||
else if (is_id32be(0x800,sf, "OggS")) {
|
||||
else
|
||||
start_offset = 0x800; /* Legend of Galactic Heroes (PC) */
|
||||
}
|
||||
else {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
ogg_vorbis_meta_info_t ovmi = {0};
|
||||
|
@ -81,31 +57,23 @@ VGMSTREAM* init_vgmstream_ikm_pc(STREAMFILE* sf) {
|
|||
ovmi.loop_flag = ovmi.loop_end > 0;
|
||||
ovmi.stream_size = read_s32le(0x24, sf);
|
||||
|
||||
vgmstream = init_vgmstream_ogg_vorbis_config(sf, start_offset, &ovmi);
|
||||
v = init_vgmstream_ogg_vorbis_config(sf, start_offset, &ovmi);
|
||||
if (!v) goto fail;
|
||||
}
|
||||
|
||||
return vgmstream;
|
||||
return v;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
close_vgmstream(v);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* IKM - MiCROViSiON PSP container [The Legend of Heroes: A Tear of Vermillion (PSP)] */
|
||||
VGMSTREAM* init_vgmstream_ikm_psp(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
static VGMSTREAM* init_vgmstream_ikm_psp(STREAMFILE* sf) {
|
||||
VGMSTREAM* v = NULL;
|
||||
STREAMFILE* temp_sf = NULL;
|
||||
off_t start_offset;
|
||||
size_t data_size;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!is_id32be(0x00,sf, "IKM\0"))
|
||||
goto fail;
|
||||
if (!check_extensions(sf,"ikm"))
|
||||
goto fail;
|
||||
|
||||
/* 0x20: type 00? */
|
||||
if (!is_id32be(0x800,sf, "RIFF"))
|
||||
goto fail;
|
||||
|
||||
|
@ -116,16 +84,43 @@ VGMSTREAM* init_vgmstream_ikm_psp(STREAMFILE* sf) {
|
|||
temp_sf = setup_subfile_streamfile(sf, start_offset, data_size, "at3");
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
vgmstream = init_vgmstream_riff(temp_sf);
|
||||
if (!vgmstream) goto fail;
|
||||
v = init_vgmstream_riff(temp_sf);
|
||||
if (!v) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_IKM;
|
||||
v->meta_type = meta_IKM;
|
||||
|
||||
close_streamfile(temp_sf);
|
||||
return vgmstream;
|
||||
return v;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_sf);
|
||||
close_vgmstream(vgmstream);
|
||||
close_vgmstream(v);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* IKM - MiCROViSiON container */
|
||||
VGMSTREAM* init_vgmstream_ikm(STREAMFILE* sf) {
|
||||
uint32_t type;
|
||||
|
||||
/* checks */
|
||||
if (!is_id32be(0x00,sf, "IKM\0"))
|
||||
goto fail;
|
||||
|
||||
if (!check_extensions(sf,"ikm"))
|
||||
goto fail;
|
||||
|
||||
type = read_u32le(0x20, sf);
|
||||
switch(type) {
|
||||
case 0x00: /* The Legend of Heroes: A Tear of Vermillion (PSP) */
|
||||
return init_vgmstream_ikm_psp(sf);
|
||||
case 0x01: /* Chaos Legion (PC), Legend of Galactic Heroes (PC) */
|
||||
return init_vgmstream_ikm_pc(sf);
|
||||
case 0x03: /* Zwei (PS2) */
|
||||
return init_vgmstream_ikm_ps2(sf);
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
fail:
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -30,7 +30,13 @@ VGMSTREAM* init_vgmstream_lopu_fb(STREAMFILE* sf) {
|
|||
/* rest: null */
|
||||
|
||||
loop_flag = (loop_end > 0); /* -1 if no loop */
|
||||
|
||||
/* Must remove skip or some files decode past limit. loop_end equals to PC (.ogg) version's max
|
||||
* samples, but in some case (stage_park) goes slightly past max but is still valid.
|
||||
* (loops shouldn't remove skip as they wouldn't match PC/bgm.txt loop times) */
|
||||
num_samples -= skip;
|
||||
if (num_samples < loop_end)
|
||||
num_samples = loop_end;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
|
@ -0,0 +1,51 @@
|
|||
#include "meta.h"
|
||||
|
||||
/* LPCM - from Shade's 'Shade game library' (ShdLib) [Ah! My Goddess (PS2), Warship Gunner (PS2)] */
|
||||
VGMSTREAM* init_vgmstream_lpcm_shade(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
uint32_t loop_flag, channels;
|
||||
|
||||
/* checks */
|
||||
if (!is_id32be(0x00, sf, "LPCM"))
|
||||
goto fail;
|
||||
|
||||
/* .w: real extension
|
||||
* .lpcm: fake (header id) */
|
||||
if (!check_extensions(sf, "w,lpcm"))
|
||||
goto fail;
|
||||
|
||||
/* extra checks since header is kind of simple */
|
||||
if (read_s32le(0x04,sf) * 0x02 * 2 > get_streamfile_size(sf)) /* data size is less than total samples */
|
||||
goto fail;
|
||||
if (read_u32le(0x10,sf) != 0) /* just in case */
|
||||
goto fail;
|
||||
|
||||
start_offset = 0x800; /* assumed, closer to num_samples */
|
||||
|
||||
loop_flag = read_s32le(0x8,sf) != 0;
|
||||
channels = 2;
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_LPCM_SHADE;
|
||||
vgmstream->sample_rate = 48000;
|
||||
|
||||
vgmstream->num_samples = read_s32le(0x4,sf);
|
||||
vgmstream->loop_start_sample = read_s32le(0x8,sf);
|
||||
vgmstream->loop_end_sample = read_s32le(0xc,sf);
|
||||
|
||||
vgmstream->coding_type = coding_PCM16LE;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x02;
|
||||
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
fail:
|
||||
if (vgmstream) close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
|
@ -222,9 +222,7 @@ VGMSTREAM * init_vgmstream_leg(STREAMFILE * streamFile);
|
|||
|
||||
VGMSTREAM * init_vgmstream_filp(STREAMFILE * streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ikm_ps2(STREAMFILE * streamFile);
|
||||
VGMSTREAM * init_vgmstream_ikm_pc(STREAMFILE * streamFile);
|
||||
VGMSTREAM * init_vgmstream_ikm_psp(STREAMFILE * streamFile);
|
||||
VGMSTREAM* init_vgmstream_ikm(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM * init_vgmstream_sfs(STREAMFILE * streamFile);
|
||||
|
||||
|
@ -444,7 +442,9 @@ VGMSTREAM * init_vgmstream_myspd(STREAMFILE* streamFile);
|
|||
|
||||
VGMSTREAM * init_vgmstream_his(STREAMFILE* streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ps2_ast(STREAMFILE* streamFile);
|
||||
VGMSTREAM* init_vgmstream_ast_mv(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM* init_vgmstream_ast_mmv(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM * init_vgmstream_dmsg(STREAMFILE* streamFile);
|
||||
|
||||
|
@ -480,7 +480,7 @@ VGMSTREAM * init_vgmstream_ps2_wad(STREAMFILE* streamFile);
|
|||
|
||||
VGMSTREAM * init_vgmstream_ps2_adm(STREAMFILE* streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ps2_lpcm(STREAMFILE* streamFile);
|
||||
VGMSTREAM* init_vgmstream_lpcm_shade(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM * init_vgmstream_dsp_bdsp(STREAMFILE* streamFile);
|
||||
|
||||
|
|
|
@ -131,13 +131,38 @@ static const uint32_t xiph_mappings[] = {
|
|||
};
|
||||
|
||||
|
||||
/* Ogg Vorbis - standard .ogg with (possibly) loop comments/metadata */
|
||||
static VGMSTREAM* _init_vgmstream_ogg_vorbis(STREAMFILE* sf) {
|
||||
static VGMSTREAM* _init_vgmstream_ogg_vorbis_cfg_ovmi(STREAMFILE* sf, ogg_vorbis_io_config_data* cfg, ogg_vorbis_meta_info_t* ovmi) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE* temp_sf = NULL;
|
||||
|
||||
|
||||
if (cfg->is_encrypted) {
|
||||
temp_sf = setup_ogg_vorbis_streamfile(sf, cfg);
|
||||
if (!temp_sf) goto fail;
|
||||
}
|
||||
|
||||
if (ovmi->meta_type == 0) {
|
||||
if (cfg->is_encrypted || ovmi->decryption_callback != NULL)
|
||||
ovmi->meta_type = meta_OGG_encrypted;
|
||||
else
|
||||
ovmi->meta_type = meta_OGG_VORBIS;
|
||||
}
|
||||
|
||||
vgmstream = _init_vgmstream_ogg_vorbis_config(temp_sf != NULL ? temp_sf : sf, cfg->start, ovmi);
|
||||
|
||||
close_streamfile(temp_sf);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_sf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* Ogg Vorbis - standard .ogg with (possibly) loop comments/metadata */
|
||||
static VGMSTREAM* _init_vgmstream_ogg_vorbis_common(STREAMFILE* sf) {
|
||||
ogg_vorbis_io_config_data cfg = {0};
|
||||
ogg_vorbis_meta_info_t ovmi = {0};
|
||||
off_t start_offset = 0;
|
||||
|
||||
int is_ogg = 0;
|
||||
int is_um3 = 0;
|
||||
|
@ -171,7 +196,8 @@ static VGMSTREAM* _init_vgmstream_ogg_vorbis(STREAMFILE* sf) {
|
|||
is_sngw = 1;
|
||||
} else if (check_extensions(sf,"isd")) { /* .isd: Inti Creates PC games */
|
||||
is_isd = 1;
|
||||
} else if (check_extensions(sf,"rpgmvo")) { /* .rpgmvo: RPG Maker MV games (PC) */
|
||||
} else if (check_extensions(sf,"rpgmvo,ogg_")) {
|
||||
/* .rpgmvo: RPG Maker MV games (PC), .ogg_: RPG Maker MZ games (PC) */
|
||||
is_rpgmvo = 1;
|
||||
} else if (check_extensions(sf,"eno")) { /* .eno: Metronomicon (PC) */
|
||||
is_eno = 1;
|
||||
|
@ -238,7 +264,7 @@ static VGMSTREAM* _init_vgmstream_ogg_vorbis(STREAMFILE* sf) {
|
|||
ovmi.decryption_callback = kovs_ogg_decryption_callback;
|
||||
ovmi.meta_type = meta_OGG_KOVS;
|
||||
|
||||
start_offset = 0x20;
|
||||
cfg.start = 0x20;
|
||||
}
|
||||
|
||||
if (is_sngw) { /* [Capcom's MT Framework PC games] */
|
||||
|
@ -321,7 +347,7 @@ static VGMSTREAM* _init_vgmstream_ogg_vorbis(STREAMFILE* sf) {
|
|||
if (sf_isl) {
|
||||
STREAMFILE* dec_sf = NULL;
|
||||
|
||||
dec_sf = setup_ogg_vorbis_streamfile(sf_isl, cfg);
|
||||
dec_sf = setup_ogg_vorbis_streamfile(sf_isl, &cfg);
|
||||
if (dec_sf) {
|
||||
off_t loop_offset;
|
||||
char basename[PATH_LIMIT];
|
||||
|
@ -353,22 +379,22 @@ static VGMSTREAM* _init_vgmstream_ogg_vorbis(STREAMFILE* sf) {
|
|||
}
|
||||
}
|
||||
|
||||
if (is_rpgmvo) { /* [RPG Maker MV (PC)] */
|
||||
if (is_rpgmvo) { /* [RPG Maker MV (PC), RPG Maker MZ (PC)] */
|
||||
if (read_32bitBE(0x00,sf) != 0x5250474D && /* "RPGM" */
|
||||
read_32bitBE(0x00,sf) != 0x56000000) { /* "V\0\0\0" */
|
||||
goto fail;
|
||||
}
|
||||
ovmi.decryption_callback = rpgmvo_ogg_decryption_callback;
|
||||
|
||||
start_offset = 0x10;
|
||||
cfg.start = 0x10;
|
||||
}
|
||||
|
||||
if (is_eno) { /* [Metronomicon (PC)] */
|
||||
/* first byte probably derives into key, but this works too */
|
||||
cfg.key[0] = (uint8_t)read_8bit(0x05,sf); /* regular ogg have a zero at this offset = easy key */
|
||||
cfg.key[0] = read_u8(0x05,sf); /* regular ogg have a zero at this offset = easy key */
|
||||
cfg.key_len = 1;
|
||||
cfg.is_encrypted = 1;
|
||||
start_offset = 0x01; /* "OggS" starts after key-thing */
|
||||
cfg.start = 0x01; /* "OggS" starts after key-thing */
|
||||
}
|
||||
|
||||
if (is_gwm) { /* [Adagio: Cloudburst (PC)] */
|
||||
|
@ -397,7 +423,7 @@ static VGMSTREAM* _init_vgmstream_ogg_vorbis(STREAMFILE* sf) {
|
|||
else { /* [Operation Babel: New Tokyo Legacy (PC), Labyrinth of Refrain: Coven of Dusk (PC)] */
|
||||
int i;
|
||||
/* found at file_size-1 but this works too (same key for most files but can vary) */
|
||||
uint8_t base_key = (uint8_t)read_8bit(0x04,sf) - 0x04;
|
||||
uint8_t base_key = read_u8(0x04,sf) - 0x04;
|
||||
|
||||
cfg.key_len = 256;
|
||||
for (i = 0; i < cfg.key_len; i++) {
|
||||
|
@ -408,13 +434,13 @@ static VGMSTREAM* _init_vgmstream_ogg_vorbis(STREAMFILE* sf) {
|
|||
}
|
||||
|
||||
if (is_bgm) { /* [Fortissimo (PC)] */
|
||||
size_t file_size = get_streamfile_size(sf);
|
||||
uint32_t file_size = get_streamfile_size(sf);
|
||||
uint8_t key[0x04];
|
||||
uint32_t xor_be;
|
||||
|
||||
put_32bitLE(key, (uint32_t)file_size);
|
||||
put_u32le(key, file_size);
|
||||
xor_be = get_u32be(key);
|
||||
if ((read_32bitBE(0x00,sf) ^ xor_be) == 0x4F676753) { /* "OggS" */
|
||||
if ((read_u32be(0x00,sf) ^ xor_be) == get_id32be("OggS")) {
|
||||
int i;
|
||||
cfg.key_len = 4;
|
||||
for (i = 0; i < cfg.key_len; i++) {
|
||||
|
@ -425,28 +451,80 @@ static VGMSTREAM* _init_vgmstream_ogg_vorbis(STREAMFILE* sf) {
|
|||
}
|
||||
|
||||
|
||||
if (cfg.is_encrypted) {
|
||||
temp_sf = setup_ogg_vorbis_streamfile(sf, cfg);
|
||||
if (!temp_sf) goto fail;
|
||||
}
|
||||
|
||||
if (ovmi.meta_type == 0) {
|
||||
if (cfg.is_encrypted || ovmi.decryption_callback != NULL)
|
||||
ovmi.meta_type = meta_OGG_encrypted;
|
||||
else
|
||||
ovmi.meta_type = meta_OGG_VORBIS;
|
||||
}
|
||||
|
||||
vgmstream = _init_vgmstream_ogg_vorbis_config(temp_sf != NULL ? temp_sf : sf, start_offset, &ovmi);
|
||||
|
||||
close_streamfile(temp_sf);
|
||||
return vgmstream;
|
||||
|
||||
return _init_vgmstream_ogg_vorbis_cfg_ovmi(sf, &cfg, &ovmi);
|
||||
fail:
|
||||
close_streamfile(temp_sf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Ogg Vorbis - encrypted .ogg [Yumekoi Tensei (PC)] */
|
||||
static VGMSTREAM* _init_vgmstream_ogg_vorbis_tink(STREAMFILE* sf) {
|
||||
ogg_vorbis_io_config_data cfg = {0};
|
||||
ogg_vorbis_meta_info_t ovmi = {0};
|
||||
uint32_t start;
|
||||
|
||||
/* checks */
|
||||
if (is_id32be(0x00, sf, "Tink")) {
|
||||
start = 0x00;
|
||||
}
|
||||
else if (is_id32be(0x0c, sf, "Tink")) {
|
||||
ovmi.loop_start = read_u32le(0x00, sf);
|
||||
ovmi.loop_end = read_u32le(0x04, sf);
|
||||
ovmi.loop_flag = read_u32le(0x0c, sf);
|
||||
ovmi.loop_end_found = 1;
|
||||
start = 0x0c;
|
||||
}
|
||||
else {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!check_extensions(sf,"u0"))
|
||||
goto fail;
|
||||
|
||||
cfg.is_encrypted = 1;
|
||||
cfg.is_header_swap = 1;
|
||||
cfg.start = start;
|
||||
cfg.max_offset = 0xE1F;
|
||||
cfg.key_len = 0xE1F;
|
||||
|
||||
if (sizeof(cfg.key) < cfg.key_len)
|
||||
goto fail;
|
||||
|
||||
/* copy key */
|
||||
{
|
||||
static const char* keystring = "BB3206F-F171-4885-A131-EC7FBA6FF491 Copyright 2004 Cyberworks \"TinkerBell\"., all rights reserved.";
|
||||
int i, keystring_len;
|
||||
start = 0;
|
||||
|
||||
memset(cfg.key, 0, 0x04);
|
||||
put_u8 (cfg.key + 0x04, 0x44);
|
||||
|
||||
keystring_len = strlen(keystring) + 1; /* including null */
|
||||
for (i = 0x05; i < cfg.key_len; i += keystring_len) {
|
||||
int copy = keystring_len;
|
||||
if (i + copy > cfg.key_len)
|
||||
copy = cfg.key_len - i;
|
||||
memcpy(cfg.key + i, keystring, copy);
|
||||
}
|
||||
}
|
||||
|
||||
return _init_vgmstream_ogg_vorbis_cfg_ovmi(sf, &cfg, &ovmi);
|
||||
fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static VGMSTREAM* _init_vgmstream_ogg_vorbis(STREAMFILE* sf) {
|
||||
VGMSTREAM* v;
|
||||
|
||||
v = _init_vgmstream_ogg_vorbis_common(sf);
|
||||
if (v) return v;
|
||||
|
||||
v = _init_vgmstream_ogg_vorbis_tink(sf);
|
||||
if (v) return v;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static VGMSTREAM* _init_vgmstream_ogg_vorbis_config(STREAMFILE* sf, off_t start, const ogg_vorbis_meta_info_t* ovmi) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
ogg_vorbis_codec_data* data = NULL;
|
||||
|
|
|
@ -5,10 +5,12 @@
|
|||
|
||||
typedef struct {
|
||||
int is_encrypted;
|
||||
uint8_t key[0x100];
|
||||
uint8_t key[0x1000];
|
||||
size_t key_len;
|
||||
int is_nibble_swap;
|
||||
int is_header_swap;
|
||||
uint32_t start;
|
||||
uint32_t max_offset;
|
||||
} ogg_vorbis_io_config_data;
|
||||
|
||||
typedef struct {
|
||||
|
@ -19,20 +21,35 @@ typedef struct {
|
|||
|
||||
static size_t ogg_vorbis_io_read(STREAMFILE *sf, uint8_t *dest, off_t offset, size_t length, ogg_vorbis_io_data* data) {
|
||||
static const uint8_t header_swap[4] = { 0x4F,0x67,0x67,0x53 }; /* "OggS" */
|
||||
static const size_t header_size = 0x04;
|
||||
int i;
|
||||
size_t bytes = read_streamfile(dest, offset, length, sf);
|
||||
|
||||
if (data->cfg.is_encrypted) {
|
||||
for (i = 0; i < bytes; i++) {
|
||||
if (data->cfg.is_header_swap && (offset + i) < header_size) {
|
||||
dest[i] = header_swap[(offset + i) % header_size];
|
||||
int max = bytes;
|
||||
int header_end = 0x04 + data->cfg.start;
|
||||
|
||||
if (data->cfg.max_offset + data->cfg.start) {
|
||||
if (offset > data->cfg.max_offset + data->cfg.start) {
|
||||
max = 0;
|
||||
}
|
||||
else {
|
||||
max = data->cfg.max_offset + data->cfg.start - offset;
|
||||
if (max > bytes)
|
||||
max = bytes;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < max; i++) {
|
||||
if (data->cfg.is_header_swap &&
|
||||
(offset + i) >= data->cfg.start &&
|
||||
(offset + i) < header_end) {
|
||||
dest[i] = header_swap[(offset + i) % 0x04];
|
||||
}
|
||||
else {
|
||||
if (!data->cfg.key_len && !data->cfg.is_nibble_swap)
|
||||
break;
|
||||
if (data->cfg.key_len)
|
||||
dest[i] ^= data->cfg.key[(offset + i) % data->cfg.key_len];
|
||||
if (data->cfg.key_len && (offset + i) >= data->cfg.start)
|
||||
dest[i] ^= data->cfg.key[(offset + i - data->cfg.start) % data->cfg.key_len];
|
||||
if (data->cfg.is_nibble_swap)
|
||||
dest[i] = ((dest[i] << 4) & 0xf0) | ((dest[i] >> 4) & 0x0f);
|
||||
}
|
||||
|
@ -44,11 +61,11 @@ static size_t ogg_vorbis_io_read(STREAMFILE *sf, uint8_t *dest, off_t offset, si
|
|||
|
||||
//todo maybe use generic decryption streamfile
|
||||
/* Decrypts Ogg Vorbis streams */
|
||||
static STREAMFILE* setup_ogg_vorbis_streamfile(STREAMFILE *sf, ogg_vorbis_io_config_data cfg) {
|
||||
static STREAMFILE* setup_ogg_vorbis_streamfile(STREAMFILE *sf, ogg_vorbis_io_config_data* cfg) {
|
||||
STREAMFILE *new_sf = NULL;
|
||||
ogg_vorbis_io_data io_data = {0};
|
||||
|
||||
io_data.cfg = cfg; /* memcpy */
|
||||
io_data.cfg = *cfg; /* memcpy */
|
||||
|
||||
new_sf = open_wrap_streamfile(sf);
|
||||
new_sf = open_io_streamfile_f(new_sf, &io_data, sizeof(ogg_vorbis_io_data), ogg_vorbis_io_read, NULL);
|
||||
|
|
|
@ -30,7 +30,7 @@ static VGMSTREAM* init_vgmstream_opus(STREAMFILE* sf, meta_t meta_type, off_t of
|
|||
|
||||
/* 0x80000002: 'offset info' chunk (seek table?), not seen */
|
||||
|
||||
/* 'context info' chunk, rare [Famicom Detective Club (Switch)] */
|
||||
/* 'context info' chunk, rare [Famicom Detective Club (Switch), SINce Memories (Switch)] */
|
||||
if (context_offset && read_u32le(offset + context_offset, sf) == 0x80000003) {
|
||||
/* maybe should give priority to external info? */
|
||||
context_offset += offset;
|
||||
|
@ -52,6 +52,12 @@ static VGMSTREAM* init_vgmstream_opus(STREAMFILE* sf, meta_t meta_type, off_t of
|
|||
multistream_offset = offset + 0x20;
|
||||
}
|
||||
|
||||
/* Opus can only do 48000 but some games store original rate [Grandia HD Collection, Lego Marvel] */
|
||||
if (sample_rate != 48000) {
|
||||
VGM_LOG("OPUS: ignored non-standard sample rate of %i\n", sample_rate);
|
||||
sample_rate = 48000;
|
||||
}
|
||||
|
||||
|
||||
/* 'data info' chunk */
|
||||
data_offset += offset;
|
||||
|
@ -69,8 +75,6 @@ static VGMSTREAM* init_vgmstream_opus(STREAMFILE* sf, meta_t meta_type, off_t of
|
|||
|
||||
vgmstream->meta_type = meta_type;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
if (vgmstream->sample_rate == 16000)
|
||||
vgmstream->sample_rate = 48000; // Grandia HD Collection contains a false sample_rate in header
|
||||
vgmstream->num_samples = num_samples;
|
||||
vgmstream->loop_start_sample = loop_start;
|
||||
vgmstream->loop_end_sample = loop_end;
|
||||
|
@ -124,6 +128,9 @@ VGMSTREAM* init_vgmstream_opus_std(STREAMFILE* sf) {
|
|||
int num_samples, loop_start, loop_end;
|
||||
|
||||
/* checks */
|
||||
if (read_u32le(0x00,sf) != 0x80000001) /* 'basic info' chunk */
|
||||
goto fail;
|
||||
|
||||
/* .opus: standard
|
||||
* .bgm: Cotton Reboot (Switch) */
|
||||
if (!check_extensions(sf,"opus,lopus,bgm"))
|
||||
|
@ -157,11 +164,11 @@ VGMSTREAM* init_vgmstream_opus_n1(STREAMFILE* sf) {
|
|||
int num_samples, loop_start, loop_end;
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf,"opus,lopus"))
|
||||
goto fail;
|
||||
if (!((read_u32be(0x04,sf) == 0x00000000 && read_u32be(0x0c,sf) == 0x00000000) ||
|
||||
(read_u32be(0x04,sf) == 0xFFFFFFFF && read_u32be(0x0c,sf) == 0xFFFFFFFF)))
|
||||
goto fail;
|
||||
if (!check_extensions(sf,"opus,lopus"))
|
||||
goto fail;
|
||||
|
||||
offset = 0x10;
|
||||
num_samples = 0;
|
||||
|
@ -181,7 +188,7 @@ VGMSTREAM* init_vgmstream_opus_capcom(STREAMFILE* sf) {
|
|||
int channels;
|
||||
|
||||
/* checks */
|
||||
if ( !check_extensions(sf,"opus,lopus"))
|
||||
if (!check_extensions(sf,"opus,lopus"))
|
||||
goto fail;
|
||||
|
||||
channels = read_32bitLE(0x04,sf);
|
||||
|
@ -259,10 +266,10 @@ VGMSTREAM* init_vgmstream_opus_nop(STREAMFILE* sf) {
|
|||
int num_samples, loop_start = 0, loop_end = 0, loop_flag;
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf,"nop"))
|
||||
if (!is_id32be(0x00, sf, "sadf") ||
|
||||
!is_id32be(0x08, sf, "opus"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00, sf) != 0x73616466 || /* "sadf" */
|
||||
read_32bitBE(0x08, sf) != 0x6f707573) /* "opus" */
|
||||
if (!check_extensions(sf,"nop"))
|
||||
goto fail;
|
||||
|
||||
offset = read_32bitLE(0x1c, sf);
|
||||
|
@ -284,9 +291,9 @@ VGMSTREAM* init_vgmstream_opus_shinen(STREAMFILE* sf) {
|
|||
int num_samples, loop_start, loop_end;
|
||||
|
||||
/* checks */
|
||||
if ( !check_extensions(sf,"opus,lopus"))
|
||||
if (read_u32be(0x08,sf) != 0x01000080)
|
||||
goto fail;
|
||||
if (read_32bitBE(0x08,sf) != 0x01000080)
|
||||
if ( !check_extensions(sf,"opus,lopus"))
|
||||
goto fail;
|
||||
|
||||
offset = 0x08;
|
||||
|
@ -308,11 +315,12 @@ VGMSTREAM* init_vgmstream_opus_nus3(STREAMFILE* sf) {
|
|||
int num_samples = 0, loop_start = 0, loop_end = 0, loop_flag;
|
||||
|
||||
/* checks */
|
||||
if (!is_id32be(0x00, sf, "OPUS"))
|
||||
goto fail;
|
||||
|
||||
/* .opus: header ID (they only exist inside .nus3bank) */
|
||||
if (!check_extensions(sf, "opus,lopus"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00, sf) != 0x4F505553) /* "OPUS" */
|
||||
goto fail;
|
||||
|
||||
/* Here's an interesting quirk, OPUS header contains big endian values
|
||||
while the Nintendo Opus header and data that follows remain little endian as usual */
|
||||
|
@ -337,13 +345,13 @@ VGMSTREAM* init_vgmstream_opus_sps_n1(STREAMFILE* sf) {
|
|||
int num_samples, loop_start = 0, loop_end = 0, loop_flag;
|
||||
|
||||
/* checks */
|
||||
if (read_u32be(0x00, sf) != 0x09000000) /* file type (see other N1 SPS) */
|
||||
goto fail;
|
||||
/* .sps: Labyrinth of Refrain: Coven of Dusk (Switch)
|
||||
* .nlsd: Disgaea Refine (Switch), Ys VIII (Switch)
|
||||
* .at9: void tRrLM(); //Void Terrarium (Switch) */
|
||||
if (!check_extensions(sf, "sps,nlsd,at9"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00, sf) != 0x09000000) /* file type (see other N1 SPS) */
|
||||
goto fail;
|
||||
|
||||
num_samples = read_32bitLE(0x0C, sf);
|
||||
|
||||
|
@ -382,9 +390,9 @@ VGMSTREAM* init_vgmstream_opus_opusx(STREAMFILE* sf) {
|
|||
float modifier;
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf, "opusx"))
|
||||
if (!is_id32be(0x00, sf, "OPUS"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00, sf) != 0x4F505553) /* "OPUS" */
|
||||
if (!check_extensions(sf, "opusx"))
|
||||
goto fail;
|
||||
|
||||
offset = 0x10;
|
||||
|
@ -414,10 +422,11 @@ VGMSTREAM* init_vgmstream_opus_prototype(STREAMFILE* sf) {
|
|||
int num_samples = 0, loop_start = 0, loop_end = 0, loop_flag;
|
||||
|
||||
/* checks */
|
||||
if (!is_id32be(0x00, sf, "OPUS"))
|
||||
goto fail;
|
||||
if (!check_extensions(sf, "opus,lopus"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00, sf) != 0x4F505553 || /* "OPUS" */
|
||||
read_32bitBE(0x18, sf) != 0x01000080)
|
||||
if (read_32bitBE(0x18, sf) != 0x01000080)
|
||||
goto fail;
|
||||
|
||||
offset = 0x18;
|
||||
|
@ -441,9 +450,9 @@ VGMSTREAM* init_vgmstream_opus_opusnx(STREAMFILE* sf) {
|
|||
int num_samples = 0, loop_start = 0, loop_end = 0;
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf, "opus,lopus"))
|
||||
if (!is_id64be(0x00, sf,"OPUSNX\0\0"))
|
||||
goto fail;
|
||||
if (read_64bitBE(0x00, sf) != 0x4F5055534E580000) /* "OPUSNX\0\0" */
|
||||
if (!check_extensions(sf, "opus,lopus"))
|
||||
goto fail;
|
||||
|
||||
offset = 0x10;
|
||||
|
@ -462,9 +471,9 @@ VGMSTREAM* init_vgmstream_opus_nsopus(STREAMFILE* sf) {
|
|||
int num_samples = 0, loop_start = 0, loop_end = 0;
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf, "nsopus"))
|
||||
if (!is_id32be(0x00, sf,"EWNO"))
|
||||
goto fail;
|
||||
if (read_u32be(0x00, sf) != 0x45574E4F) /* "EWNO" */
|
||||
if (!check_extensions(sf, "nsopus"))
|
||||
goto fail;
|
||||
|
||||
offset = 0x08;
|
||||
|
@ -481,12 +490,12 @@ VGMSTREAM* init_vgmstream_opus_sqex(STREAMFILE* sf) {
|
|||
int num_samples = 0, loop_start = 0, loop_end = 0, loop_flag;
|
||||
|
||||
/* checks */
|
||||
/* .wav: default
|
||||
* .opus: fake? */
|
||||
if (!check_extensions(sf, "wav,lwav,opus,lopus"))
|
||||
goto fail;
|
||||
if (read_u32be(0x00, sf) != 0x01000000)
|
||||
goto fail;
|
||||
|
||||
/* .wav: original */
|
||||
if (!check_extensions(sf, "wav,lwav"))
|
||||
goto fail;
|
||||
/* 0x04: channels */
|
||||
/* 0x08: data_size */
|
||||
offset = read_32bitLE(0x0C, sf);
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* AST - from Koei and Marvelous games (same internal dev?) */
|
||||
VGMSTREAM * init_vgmstream_ps2_ast(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag, channel_count, variant_type;
|
||||
|
||||
/* check extension */
|
||||
if (!check_extensions(streamFile,"ast")) goto fail;
|
||||
|
||||
/* check header */
|
||||
if (read_32bitBE(0x00,streamFile) != 0x41535400) /* "AST\0" */
|
||||
goto fail;
|
||||
|
||||
/* determine variant (after 0x10 is garbage/code data in type 1 until 0x800, but consistent in all songs) */
|
||||
if (read_32bitBE(0x10,streamFile) == 0x00000000 || read_32bitBE(0x10,streamFile) == 0x20002000) {
|
||||
variant_type = 1; /* Koei: P.T.O. IV (0x00000000), Naval Ops: Warship Gunner (0x20002000) */
|
||||
channel_count = 2;
|
||||
}
|
||||
else {
|
||||
variant_type = 2; /* Marvelous: Katekyoo Hitman Reborn! Dream Hyper Battle!, Binchou-tan: Shiawasegoyomi */
|
||||
channel_count = read_32bitLE(0x0C,streamFile);
|
||||
}
|
||||
|
||||
loop_flag = 0;
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
/* fill in the vital statistics */
|
||||
if (variant_type == 1) {
|
||||
start_offset = 0x800;
|
||||
vgmstream->sample_rate = read_32bitLE(0x04,streamFile);
|
||||
vgmstream->num_samples = ps_bytes_to_samples(read_32bitLE(0x0C,streamFile)-start_offset,channel_count);
|
||||
vgmstream->interleave_block_size = read_32bitLE(0x08,streamFile);
|
||||
}
|
||||
else if (variant_type == 2) {
|
||||
start_offset = 0x100;
|
||||
vgmstream->sample_rate = read_32bitLE(0x08,streamFile);
|
||||
vgmstream->num_samples = ps_bytes_to_samples(read_32bitLE(0x04,streamFile)-start_offset,channel_count);
|
||||
vgmstream->interleave_block_size = read_32bitLE(0x10,streamFile);
|
||||
}
|
||||
else {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->meta_type = meta_PS2_AST;
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
goto fail;
|
||||
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
|
||||
/* LPCM (from Ah! My Goddess (PS2)) */
|
||||
VGMSTREAM * init_vgmstream_ps2_lpcm(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
char filename[PATH_LIMIT];
|
||||
off_t start_offset;
|
||||
|
||||
int loop_flag;
|
||||
int channel_count;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
streamFile->get_name(streamFile,filename,sizeof(filename));
|
||||
if (strcasecmp("lpcm",filename_extension(filename))) goto fail;
|
||||
|
||||
/* check header */
|
||||
if (read_32bitBE(0,streamFile) != 0x4C50434D) /* LPCM */
|
||||
goto fail;
|
||||
|
||||
loop_flag = read_32bitLE(0x8,streamFile);
|
||||
channel_count = 2;
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
/* fill in the vital statistics */
|
||||
start_offset = 0x10;
|
||||
vgmstream->channels = channel_count;
|
||||
vgmstream->sample_rate = 48000;
|
||||
vgmstream->coding_type = coding_PCM16LE;
|
||||
vgmstream->num_samples = read_32bitLE(0x4,streamFile);
|
||||
if (loop_flag) {
|
||||
vgmstream->loop_start_sample = read_32bitLE(0x8,streamFile);
|
||||
vgmstream->loop_end_sample = read_32bitLE(0xc,streamFile);
|
||||
}
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 2;
|
||||
vgmstream->meta_type = meta_PS2_LPCM;
|
||||
|
||||
/* open the file for reading */
|
||||
{
|
||||
int i;
|
||||
STREAMFILE * file;
|
||||
file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
if (!file) goto fail;
|
||||
for (i=0;i<channel_count;i++) {
|
||||
vgmstream->ch[i].streamfile = file;
|
||||
|
||||
vgmstream->ch[i].channel_start_offset=
|
||||
vgmstream->ch[i].offset=start_offset+
|
||||
vgmstream->interleave_block_size*i;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
/* clean up anything we may have opened */
|
||||
if (vgmstream) close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
|
@ -353,8 +353,9 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) {
|
|||
* .saf: Whacked! (Xbox)
|
||||
* .mwv: Level-5 games [Dragon Quest VIII (PS2), Rogue Galaxy (PS2)]
|
||||
* .ima: Baja: Edge of Control (PS3/X360)
|
||||
* .nsa: Studio Ring games that uses NScripter [Hajimete no Otetsudai (PC)]
|
||||
*/
|
||||
if ( check_extensions(sf, "wav,lwav,xwav,da,dax,cd,med,snd,adx,adp,xss,xsew,adpcm,adw,wd,,sbv,wvx,str,at3,rws,aud,at9,saf,ima") ) {
|
||||
if ( check_extensions(sf, "wav,lwav,xwav,da,dax,cd,med,snd,adx,adp,xss,xsew,adpcm,adw,wd,,sbv,wvx,str,at3,rws,aud,at9,saf,ima,nsa") ) {
|
||||
;
|
||||
}
|
||||
else if ( check_extensions(sf, "mwv") ) {
|
||||
|
|
|
@ -272,6 +272,31 @@ static int parse_header(STREAMFILE* sf_h, STREAMFILE* sf_b, strwav_header* strwa
|
|||
return 1;
|
||||
}
|
||||
|
||||
/* Taz Wanted demo (PC)[2003] */
|
||||
if ( read_u32be(0x04,sf_h) == 0x00000900 &&
|
||||
read_u32le(0x24,sf_h) == read_u32le(0xfc,sf_h) && /* sample rate repeat */
|
||||
read_u32le(0x10c,sf_h) == header_size
|
||||
) {
|
||||
/* 0x08: null */
|
||||
/* 0x0c: hashname */
|
||||
strwav->num_samples = read_s32le(0x20,sf_h);
|
||||
strwav->sample_rate = read_s32le(0x24,sf_h);
|
||||
/* 0x28: 16 bps */
|
||||
strwav->flags = read_u32le(0x2c,sf_h);
|
||||
strwav->loop_end = read_s32le(0x30,sf_h);
|
||||
strwav->loop_start = read_s32le(0x38,sf_h);
|
||||
/* 0x58: number of chunks */
|
||||
strwav->tracks = read_s32le(0xD8,sf_h);
|
||||
/* 0xfc: sample rate 2 */
|
||||
/* 0x100: ? */
|
||||
/* 0x10c: header size */
|
||||
|
||||
strwav->codec = IMA;
|
||||
strwav->interleave = strwav->tracks > 1 ? 0x8000 : 0x10000;
|
||||
;VGM_LOG("STR+WAV: header TAZd (PC)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* The Fairly OddParents - Breakin' da Rules (Xbox)[2003] */
|
||||
if ( read_u32be(0x04,sf_h) == 0x00000900 &&
|
||||
read_u32le(0x24,sf_h) == read_u32le(0xb0,sf_h) && /* sample rate repeat */
|
||||
|
@ -479,12 +504,14 @@ static int parse_header(STREAMFILE* sf_h, STREAMFILE* sf_b, strwav_header* strwa
|
|||
return 1;
|
||||
}
|
||||
|
||||
/* Taz Wanted (PC)[2002] */
|
||||
/* Zapper: One Wicked Cricket! Beta (Xbox)[2002] */
|
||||
if ( read_u32be(0x04,sf_h) == 0x00000900 &&
|
||||
read_u32le(0x0c,sf_h) != header_size &&
|
||||
read_u32le(0x24,sf_h) != 0 &&
|
||||
read_u32le(0x24,sf_h) == read_u32le(0x90,sf_h) && /* sample rate repeat */
|
||||
read_u32le(0xa0,sf_h) == header_size /* ~0xC0 */
|
||||
(read_u32le(0xa0,sf_h) == header_size || /* Zapper */
|
||||
read_u32le(0xa0,sf_h) + 0x50 == header_size) /* Taz */
|
||||
) {
|
||||
/* 0x08: null */
|
||||
/* 0x0c: hashname */
|
||||
|
@ -612,7 +639,7 @@ static int parse_header(STREAMFILE* sf_h, STREAMFILE* sf_b, strwav_header* strwa
|
|||
|
||||
strwav->codec = DSP;
|
||||
strwav->dsps_table = 0xf0;
|
||||
strwav->interleave = strwav->tracks > 2 ? 0x8000 : 0x10000;
|
||||
strwav->interleave = strwav->tracks >= 2 ? 0x8000 : 0x10000;
|
||||
;VGM_LOG("STR+WAV: header SBCKK (GC)\n");
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -40,6 +40,9 @@ typedef enum {
|
|||
ASF = 30, /* Argonaut ASF 4-bit ADPCM */
|
||||
EAXA = 31, /* Electronic Arts EA-XA 4-bit ADPCM v1 */
|
||||
OKI4S = 32, /* OKI ADPCM with 16-bit output (unlike OKI/VOX/Dialogic ADPCM's 12-bit) */
|
||||
XA,
|
||||
XA_EA,
|
||||
CP_YM,
|
||||
|
||||
UNKNOWN = 99,
|
||||
} txth_codec_t;
|
||||
|
@ -117,11 +120,15 @@ typedef struct {
|
|||
uint32_t chunk_count;
|
||||
uint32_t chunk_header_size;
|
||||
uint32_t chunk_data_size;
|
||||
uint32_t chunk_value;
|
||||
uint32_t chunk_size_offset;
|
||||
uint32_t chunk_be;
|
||||
int chunk_start_set;
|
||||
int chunk_size_set;
|
||||
int chunk_count_set;
|
||||
|
||||
uint32_t base_offset;
|
||||
uint32_t is_offset_absolute;
|
||||
|
||||
uint32_t name_values[16];
|
||||
int name_values_count;
|
||||
|
@ -256,6 +263,9 @@ VGMSTREAM* init_vgmstream_txth(STREAMFILE* sf) {
|
|||
case TGC: coding = coding_TGC; break;
|
||||
case ASF: coding = coding_ASF; break;
|
||||
case EAXA: coding = coding_EA_XA; break;
|
||||
case XA: coding = coding_XA; break;
|
||||
case XA_EA: coding = coding_XA_EA; break;
|
||||
case CP_YM: coding = coding_CP_YM; break;
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
@ -367,6 +377,9 @@ VGMSTREAM* init_vgmstream_txth(STREAMFILE* sf) {
|
|||
|
||||
case coding_OKI16:
|
||||
case coding_OKI4S:
|
||||
case coding_XA:
|
||||
case coding_XA_EA:
|
||||
case coding_CP_YM:
|
||||
vgmstream->layout_type = layout_none;
|
||||
break;
|
||||
|
||||
|
@ -448,7 +461,7 @@ VGMSTREAM* init_vgmstream_txth(STREAMFILE* sf) {
|
|||
{
|
||||
int16_t (*read_16bit)(off_t, STREAMFILE*) = txth.coef_big_endian ? read_16bitBE : read_16bitLE;
|
||||
int16_t (*get_16bit)(const uint8_t* p) = txth.coef_big_endian ? get_16bitBE : get_16bitLE;
|
||||
|
||||
VGM_LOG("coef=%x\n",txth.coef_offset );
|
||||
for (i = 0; i < vgmstream->channels; i++) {
|
||||
if (txth.coef_mode == 0) { /* normal coefs */
|
||||
for (j = 0; j < 16; j++) {
|
||||
|
@ -648,7 +661,7 @@ static VGMSTREAM* init_subfile(txth_header* txth) {
|
|||
* - etc
|
||||
* to avoid it we set a particular fake extension and detect it when reading .txth
|
||||
*/
|
||||
strcpy(extension, "subfile_txth.");
|
||||
strcpy(extension, ".subfile_txth.");
|
||||
strcat(extension, txth->subfile_extension);
|
||||
|
||||
sf_sub = setup_subfile_streamfile(txth->sf_body, txth->subfile_offset, txth->subfile_size, extension);
|
||||
|
@ -722,50 +735,43 @@ fail:
|
|||
|
||||
|
||||
static STREAMFILE* open_txth(STREAMFILE* sf) {
|
||||
char basename[PATH_LIMIT];
|
||||
char filename[PATH_LIMIT];
|
||||
char fileext[PATH_LIMIT];
|
||||
const char *subext;
|
||||
const char* base_ext;
|
||||
const char* txth_ext;
|
||||
STREAMFILE* sf_text;
|
||||
|
||||
/* try "(path/)(name.ext).txth" */
|
||||
get_streamfile_name(sf,filename,PATH_LIMIT);
|
||||
if (strstr(filename, "subfile_txth") != NULL)
|
||||
|
||||
get_streamfile_name(sf, filename, sizeof(filename));
|
||||
if (strstr(filename, ".subfile_txth") != NULL)
|
||||
return NULL; /* detect special case of subfile-within-subfile */
|
||||
strcat(filename, ".txth");
|
||||
sf_text = open_streamfile(sf,filename);
|
||||
if (sf_text) return sf_text;
|
||||
|
||||
/* try "(path/)(.sub.ext).txth" */
|
||||
get_streamfile_basename(sf,basename,PATH_LIMIT);
|
||||
subext = filename_extension(basename);
|
||||
if (subext != NULL && subext[0] != '\0') {
|
||||
get_streamfile_path(sf,filename,PATH_LIMIT);
|
||||
get_streamfile_ext(sf,fileext,PATH_LIMIT);
|
||||
strcat(filename,".");
|
||||
strcat(filename, subext);
|
||||
strcat(filename,".");
|
||||
strcat(filename, fileext);
|
||||
strcat(filename, ".txth");
|
||||
base_ext = filename_extension(filename);
|
||||
concatn(sizeof(filename), filename, ".txth");
|
||||
txth_ext = filename_extension(filename);
|
||||
|
||||
sf_text = open_streamfile(sf,filename);
|
||||
/* try "(path/)(name.ext).txth" */
|
||||
{
|
||||
/* full filename, already prepared */
|
||||
|
||||
sf_text = open_streamfile(sf, filename);
|
||||
if (sf_text) return sf_text;
|
||||
}
|
||||
|
||||
/* try "(path/)(.ext).txth" */
|
||||
get_streamfile_path(sf,filename,PATH_LIMIT);
|
||||
get_streamfile_ext(sf,fileext,PATH_LIMIT);
|
||||
strcat(filename,".");
|
||||
strcat(filename, fileext);
|
||||
strcat(filename, ".txth");
|
||||
sf_text = open_streamfile(sf,filename);
|
||||
if (sf_text) return sf_text;
|
||||
if (base_ext) {
|
||||
base_ext--; //get_streamfile_path(sf, filename, sizeof(filename));
|
||||
|
||||
sf_text = open_streamfile_by_filename(sf, base_ext);
|
||||
if (sf_text) return sf_text;
|
||||
}
|
||||
|
||||
/* try "(path/).txth" */
|
||||
get_streamfile_path(sf,filename,PATH_LIMIT);
|
||||
strcat(filename, ".txth");
|
||||
sf_text = open_streamfile(sf,filename);
|
||||
if (sf_text) return sf_text;
|
||||
if (txth_ext) {
|
||||
txth_ext--; /* points to "txth" due to the concat */
|
||||
|
||||
sf_text = open_streamfile_by_filename(sf, txth_ext);
|
||||
if (sf_text) return sf_text;
|
||||
}
|
||||
|
||||
/* not found */
|
||||
return NULL;
|
||||
|
@ -788,7 +794,9 @@ static void set_body_chunk(txth_header* txth) {
|
|||
//todo maybe should only be done once, or have some count to retrigger to simplify?
|
||||
if (!txth->chunk_start_set || !txth->chunk_size_set || !txth->chunk_count_set)
|
||||
return;
|
||||
if (txth->chunk_size == 0 || txth->chunk_start > txth->data_size || txth->chunk_count == 0)
|
||||
if ((txth->chunk_size == 0 && ! txth->chunk_size_offset) ||
|
||||
txth->chunk_start > txth->data_size ||
|
||||
txth->chunk_count == 0)
|
||||
return;
|
||||
if (!txth->sf_body)
|
||||
return;
|
||||
|
@ -804,18 +812,22 @@ static void set_body_chunk(txth_header* txth) {
|
|||
{
|
||||
txth_io_config_data cfg = {0};
|
||||
|
||||
cfg.chunk_start = txth->chunk_start;
|
||||
cfg.chunk_number = txth->chunk_number - 1; /* 1-index to 0-index */
|
||||
cfg.chunk_header_size = txth->chunk_header_size;
|
||||
cfg.chunk_data_size = txth->chunk_data_size;
|
||||
|
||||
cfg.chunk_value = txth->chunk_value;
|
||||
cfg.chunk_size_offset = txth->chunk_size_offset;
|
||||
cfg.chunk_be = txth->chunk_be;
|
||||
|
||||
cfg.chunk_start = txth->chunk_start;
|
||||
cfg.chunk_size = txth->chunk_size;
|
||||
cfg.chunk_count = txth->chunk_count;
|
||||
cfg.chunk_number = txth->chunk_number - 1; /* 1-index to 0-index */
|
||||
|
||||
temp_sf = setup_txth_streamfile(txth->sf_body, cfg, txth->sf_body_opened);
|
||||
if (!temp_sf) return;
|
||||
}
|
||||
|
||||
|
||||
/* closing is handled by temp_sf */
|
||||
//if (txth->sf_body_opened) {
|
||||
// close_streamfile(txth->sf_body);
|
||||
|
@ -939,6 +951,9 @@ static txth_codec_t parse_codec(txth_header* txth, const char* val) {
|
|||
else if (is_string(val,"GCOM_ADPCM")) return TGC;
|
||||
else if (is_string(val,"ASF")) return ASF;
|
||||
else if (is_string(val,"EAXA")) return EAXA;
|
||||
else if (is_string(val,"XA")) return XA;
|
||||
else if (is_string(val,"XA_EA")) return XA_EA;
|
||||
else if (is_string(val,"CP_YM")) return CP_YM;
|
||||
/* special handling */
|
||||
else if (is_string(val,"name_value")) return txth->name_values[0];
|
||||
else if (is_string(val,"name_value1")) return txth->name_values[0];
|
||||
|
@ -949,6 +964,19 @@ static txth_codec_t parse_codec(txth_header* txth, const char* val) {
|
|||
return UNKNOWN;
|
||||
}
|
||||
|
||||
static int parse_be(txth_header* txth, const char* val, uint32_t* p_value) {
|
||||
if (is_string(val, "BE"))
|
||||
*p_value = 1;
|
||||
else if (is_string(val, "LE"))
|
||||
*p_value = 0;
|
||||
else
|
||||
if (!parse_num(txth->sf_head,txth,val, p_value))
|
||||
goto fail;
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char* key, char* val) {
|
||||
//;VGM_LOG("TXTH: key=%s, val=%s\n", key, val);
|
||||
|
||||
|
@ -1169,23 +1197,26 @@ static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char* key, cha
|
|||
goto fail;
|
||||
}
|
||||
|
||||
else if (is_string(key,"offset_absolute")) {
|
||||
if (!parse_num(txth->sf_head,txth,val, &txth->is_offset_absolute)) goto fail;
|
||||
}
|
||||
|
||||
/* COEFS */
|
||||
else if (is_string(key,"coef_offset")) {
|
||||
if (!parse_num(txth->sf_head,txth,val, &txth->coef_offset)) goto fail;
|
||||
/* special adjustments */
|
||||
VGM_LOG("coef norm=%x\n",txth->coef_offset );
|
||||
txth->coef_offset += txth->base_offset;
|
||||
if (txth->subsong_spacing)
|
||||
VGM_LOG("coef+base=%x\n",txth->coef_offset );
|
||||
if (txth->subsong_spacing && !txth->is_offset_absolute)
|
||||
txth->coef_offset += txth->subsong_spacing * (txth->target_subsong - 1);
|
||||
VGM_LOG("coef+spac=%x\n",txth->coef_offset );
|
||||
}
|
||||
else if (is_string(key,"coef_spacing")) {
|
||||
if (!parse_num(txth->sf_head,txth,val, &txth->coef_spacing)) goto fail;
|
||||
}
|
||||
else if (is_string(key,"coef_endianness")) {
|
||||
if (is_string(val, "BE"))
|
||||
txth->coef_big_endian = 1;
|
||||
else if (is_string(val, "LE"))
|
||||
txth->coef_big_endian = 0;
|
||||
else if (!parse_num(txth->sf_head,txth,val, &txth->coef_big_endian)) goto fail;
|
||||
if (!parse_be(txth, val, &txth->coef_big_endian)) goto fail;
|
||||
}
|
||||
else if (is_string(key,"coef_mode")) {
|
||||
if (!parse_num(txth->sf_head,txth,val, &txth->coef_mode)) goto fail;
|
||||
|
@ -1201,18 +1232,14 @@ static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char* key, cha
|
|||
txth->hist_set = 1;
|
||||
/* special adjustment */
|
||||
txth->hist_offset += txth->hist_offset;
|
||||
if (txth->subsong_spacing)
|
||||
if (txth->subsong_spacing && !txth->is_offset_absolute)
|
||||
txth->hist_offset += txth->subsong_spacing * (txth->target_subsong - 1);
|
||||
}
|
||||
else if (is_string(key,"hist_spacing")) {
|
||||
if (!parse_num(txth->sf_head,txth,val, &txth->hist_spacing)) goto fail;
|
||||
}
|
||||
else if (is_string(key,"hist_endianness")) {
|
||||
if (is_string(val, "BE"))
|
||||
txth->hist_big_endian = 1;
|
||||
else if (is_string(val, "LE"))
|
||||
txth->hist_big_endian = 0;
|
||||
else if (!parse_num(txth->sf_head,txth,val, &txth->hist_big_endian)) goto fail;
|
||||
if (!parse_be(txth, val, &txth->hist_big_endian)) goto fail;
|
||||
}
|
||||
|
||||
/* SUBSONGS */
|
||||
|
@ -1227,10 +1254,10 @@ static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char* key, cha
|
|||
txth->name_offset_set = 1;
|
||||
/* special adjustment */
|
||||
txth->name_offset += txth->base_offset;
|
||||
if (txth->subsong_spacing)
|
||||
if (txth->subsong_spacing && !txth->is_offset_absolute)
|
||||
txth->name_offset += txth->subsong_spacing * (txth->target_subsong - 1);
|
||||
}
|
||||
else if (is_string(key,"name_offset_absolute")) {
|
||||
else if (is_string(key,"name_offset_absolute")) { //TODO: remove
|
||||
if (!parse_num(txth->sf_head,txth,val, &txth->name_offset)) goto fail;
|
||||
txth->name_offset_set = 1;
|
||||
/* special adjustment */
|
||||
|
@ -1336,34 +1363,41 @@ static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char* key, cha
|
|||
}
|
||||
|
||||
/* CHUNKS */
|
||||
else if (is_string(key,"chunk_number")) {
|
||||
if (!parse_num(txth->sf_head,txth,val, &txth->chunk_number)) goto fail;
|
||||
else if (is_string(key,"chunk_count")) {
|
||||
if (!parse_num(txth->sf_head,txth,val, &txth->chunk_count)) goto fail;
|
||||
txth->chunk_count_set = 1;
|
||||
set_body_chunk(txth);
|
||||
}
|
||||
else if (is_string(key,"chunk_start")) {
|
||||
if (!parse_num(txth->sf_head,txth,val, &txth->chunk_start)) goto fail;
|
||||
txth->chunk_start_set = 1;
|
||||
set_body_chunk(txth);
|
||||
}
|
||||
else if (is_string(key,"chunk_header_size")) {
|
||||
if (!parse_num(txth->sf_head,txth,val, &txth->chunk_header_size)) goto fail;
|
||||
//txth->chunk_header_size_set = 1;
|
||||
//set_body_chunk(txth); /* optional and should go before chunk_size */
|
||||
}
|
||||
else if (is_string(key,"chunk_data_size")) {
|
||||
if (!parse_num(txth->sf_head,txth,val, &txth->chunk_data_size)) goto fail;
|
||||
//txth->chunk_data_size_set = 1;
|
||||
//set_body_chunk(txth); /* optional and should go before chunk_size */
|
||||
}
|
||||
else if (is_string(key,"chunk_size")) {
|
||||
if (!parse_num(txth->sf_head,txth,val, &txth->chunk_size)) goto fail;
|
||||
txth->chunk_size_set = 1;
|
||||
set_body_chunk(txth);
|
||||
}
|
||||
else if (is_string(key,"chunk_count")) {
|
||||
if (!parse_num(txth->sf_head,txth,val, &txth->chunk_count)) goto fail;
|
||||
txth->chunk_count_set = 1;
|
||||
set_body_chunk(txth);
|
||||
/* optional and should go before the above */
|
||||
else if (is_string(key,"chunk_number")) {
|
||||
if (!parse_num(txth->sf_head,txth,val, &txth->chunk_number)) goto fail;
|
||||
}
|
||||
else if (is_string(key,"chunk_header_size")) {
|
||||
if (!parse_num(txth->sf_head,txth,val, &txth->chunk_header_size)) goto fail;
|
||||
}
|
||||
else if (is_string(key,"chunk_data_size")) {
|
||||
if (!parse_num(txth->sf_head,txth,val, &txth->chunk_data_size)) goto fail;
|
||||
}
|
||||
else if (is_string(key,"chunk_value")) {
|
||||
if (!parse_num(txth->sf_head,txth,val, &txth->chunk_value)) goto fail;
|
||||
}
|
||||
else if (is_string(key,"chunk_size_offset")) {
|
||||
if (!parse_num(txth->sf_head,txth,val, &txth->chunk_size_offset)) goto fail;
|
||||
}
|
||||
else if (is_string(key,"chunk_endianness")) {
|
||||
if (!parse_be(txth, val, &txth->chunk_be)) goto fail;
|
||||
}
|
||||
|
||||
|
||||
/* BASE OFFSET */
|
||||
else if (is_string(key,"base_offset")) {
|
||||
|
@ -1564,6 +1598,16 @@ fail:
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void string_trim(char* str) {
|
||||
int str_len = strlen(str);
|
||||
int i;
|
||||
for (i = str_len - 1; i >= 0; i--) {
|
||||
if (str[i] != ' ')
|
||||
break;
|
||||
str[i] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
static int read_name_table_keyval(txth_header* txth, const char* line, char* key, char* val) {
|
||||
int ok;
|
||||
int subsong;
|
||||
|
@ -1576,8 +1620,10 @@ static int read_name_table_keyval(txth_header* txth, const char* line, char* key
|
|||
return 0;
|
||||
|
||||
/* try "(name): (val))" */
|
||||
|
||||
ok = sscanf(line, " %[^\t#:] : %[^\t#\r\n] ", key, val);
|
||||
if (ok == 2) {
|
||||
string_trim(key); /* otherwise includes end spaces before : */
|
||||
//;VGM_LOG("TXTH: name %s get\n", key);
|
||||
return 1;
|
||||
}
|
||||
|
@ -1628,32 +1674,27 @@ fail:
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int parse_name_table(txth_header* txth, char* name_list) {
|
||||
static int parse_name_table(txth_header* txth, char* set_name) {
|
||||
STREAMFILE* sf_names = NULL;
|
||||
off_t txt_offset, file_size;
|
||||
char fullname[PATH_LIMIT];
|
||||
char filename[PATH_LIMIT];
|
||||
char basename[PATH_LIMIT];
|
||||
const char* table_name;
|
||||
|
||||
/* just in case */
|
||||
if (!txth->sf_text || !txth->sf_body)
|
||||
goto fail;
|
||||
|
||||
/* trim name_list just in case */
|
||||
{
|
||||
int name_list_len = strlen(name_list);
|
||||
int i;
|
||||
for (i = name_list_len - 1; i >= 0; i--) {
|
||||
if (name_list[i] != ' ')
|
||||
break;
|
||||
name_list[i] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
//;VGM_LOG("TXTH: name_list='%s'\n", name_list);
|
||||
/* trim just in case */
|
||||
string_trim(set_name);
|
||||
if (is_string(set_name,"*"))
|
||||
table_name = ".names.txt";
|
||||
else
|
||||
table_name = set_name;
|
||||
|
||||
/* open companion file near .txth */
|
||||
sf_names = open_streamfile_by_filename(txth->sf_text, name_list);
|
||||
sf_names = open_streamfile_by_filename(txth->sf_text, table_name);
|
||||
if (!sf_names) goto fail;
|
||||
|
||||
get_streamfile_name(txth->sf_body, fullname, sizeof(filename));
|
||||
|
@ -1893,6 +1934,8 @@ static int parse_num(STREAMFILE* sf, txth_header* txth, const char* val, uint32_
|
|||
else if ((n = is_string_field(val,"subfile_offset"))) value = txth->subfile_offset;
|
||||
else if ((n = is_string_field(val,"subfile_size"))) value = txth->subfile_size;
|
||||
else if ((n = is_string_field(val,"base_offset"))) value = txth->base_offset;
|
||||
else if ((n = is_string_field(val,"coef_offset"))) value = txth->coef_offset;
|
||||
else if ((n = is_string_field(val,"hist_offset"))) value = txth->hist_offset;
|
||||
//todo whatever, improve
|
||||
else if ((n = is_string_field(val,"name_value"))) value = txth->name_values[0];
|
||||
else if ((n = is_string_field(val,"name_value1"))) value = txth->name_values[0];
|
||||
|
@ -2001,6 +2044,9 @@ static int get_bytes_to_samples(txth_header* txth, uint32_t bytes) {
|
|||
return asf_bytes_to_samples(bytes, txth->channels);
|
||||
case EAXA:
|
||||
return ea_xa_bytes_to_samples(bytes, txth->channels);
|
||||
case XA:
|
||||
case XA_EA:
|
||||
return xa_bytes_to_samples(bytes, txth->channels, 0, 0, 4);
|
||||
|
||||
/* XMA bytes-to-samples is done at the end as the value meanings are a bit different */
|
||||
case XMA1:
|
||||
|
@ -2011,6 +2057,7 @@ static int get_bytes_to_samples(txth_header* txth, uint32_t bytes) {
|
|||
case DVI_IMA:
|
||||
return ima_bytes_to_samples(bytes, txth->channels);
|
||||
case AICA:
|
||||
case CP_YM:
|
||||
return yamaha_bytes_to_samples(bytes, txth->channels);
|
||||
case PCFX:
|
||||
case OKI16:
|
||||
|
|
|
@ -1,30 +1,36 @@
|
|||
#ifndef _TXTH_STREAMFILE_H_
|
||||
#define _TXTH_STREAMFILE_H_
|
||||
#include "../streamfile.h"
|
||||
#include "../util/endianness.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
off_t chunk_start;
|
||||
size_t chunk_size;
|
||||
size_t chunk_header_size;
|
||||
size_t chunk_data_size;
|
||||
uint32_t chunk_start;
|
||||
uint32_t chunk_size;
|
||||
uint32_t chunk_header_size;
|
||||
uint32_t chunk_data_size;
|
||||
|
||||
int chunk_count;
|
||||
int chunk_number;
|
||||
|
||||
uint32_t chunk_value;
|
||||
uint32_t chunk_size_offset;
|
||||
int chunk_be;
|
||||
} txth_io_config_data;
|
||||
|
||||
typedef struct {
|
||||
/* config */
|
||||
txth_io_config_data cfg;
|
||||
size_t stream_size;
|
||||
uint32_t stream_size;
|
||||
|
||||
/* state */
|
||||
off_t logical_offset; /* fake offset */
|
||||
off_t physical_offset; /* actual offset */
|
||||
size_t block_size; /* current size */
|
||||
size_t skip_size; /* size from block start to reach data */
|
||||
size_t data_size; /* usable size in a block */
|
||||
uint32_t logical_offset; /* fake offset */
|
||||
uint32_t physical_offset; /* actual offset */
|
||||
uint32_t block_size; /* current size */
|
||||
uint32_t skip_size; /* size from block start to reach data */
|
||||
uint32_t data_size; /* usable size in a block */
|
||||
|
||||
size_t logical_size;
|
||||
uint32_t logical_size;
|
||||
} txth_io_data;
|
||||
|
||||
|
||||
|
@ -64,6 +70,22 @@ static size_t txth_io_read(STREAMFILE* sf, uint8_t* dest, off_t offset, size_t l
|
|||
data->data_size = data->cfg.chunk_data_size;
|
||||
}
|
||||
|
||||
/* chunk size reader (overwrites the above) */
|
||||
if (data->cfg.chunk_header_size && data->cfg.chunk_size_offset) {
|
||||
read_u32_t read_u32 = data->cfg.chunk_be ? read_u32be : read_u32le;
|
||||
|
||||
data->block_size = read_u32(data->physical_offset + data->cfg.chunk_size_offset, sf);
|
||||
data->data_size = data->block_size - data->cfg.chunk_header_size;
|
||||
|
||||
/* skip chunk if doesn't match expected header value */
|
||||
if (data->cfg.chunk_value) {
|
||||
uint32_t value = read_u32(data->physical_offset + 0x00, sf);
|
||||
if (value != data->cfg.chunk_value) {
|
||||
data->data_size = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* clamp for games where last block is smaller */ //todo not correct for all cases
|
||||
if (data->physical_offset + data->block_size > data->cfg.chunk_start + data->stream_size) {
|
||||
data->block_size = (data->cfg.chunk_start + data->stream_size) - data->physical_offset;
|
||||
|
|
|
@ -645,7 +645,7 @@ static VGMSTREAM *init_vgmstream_ubi_dat_main(ubi_sb_header *sb, STREAMFILE *sf_
|
|||
if (!sf_data) {
|
||||
/* play silence if external file is not found since Rayman 2 seems to rely on this behavior */
|
||||
vgm_logi("UBI DAT: external file '%s' not found (put together)\n", sb->resource_name);
|
||||
strncat(sb->readable_name, " (missing)", sizeof(sb->readable_name));
|
||||
concatn(sizeof(sb->readable_name), sb->readable_name, " (missing)");
|
||||
sb->duration = (float)pcm_bytes_to_samples(sb->stream_size, sb->channels, 16) / (float)sb->sample_rate;
|
||||
return init_vgmstream_ubi_sb_silence(sb);
|
||||
}
|
||||
|
@ -1301,8 +1301,11 @@ static VGMSTREAM* init_vgmstream_ubi_sb_audio(ubi_sb_header* sb, STREAMFILE* sf_
|
|||
if (sb->is_external) {
|
||||
sf_data = open_streamfile_by_filename(sf, sb->resource_name);
|
||||
if (sf_data == NULL) {
|
||||
/* play silence if external file is not found */
|
||||
vgm_logi("UBI SB: external file '%s' not found (put together)\n", sb->resource_name);
|
||||
goto fail;
|
||||
concatn(sizeof(sb->readable_name), sb->readable_name, " (missing)");
|
||||
sb->duration = 1.0f;
|
||||
return init_vgmstream_ubi_sb_silence(sb);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -1342,8 +1345,11 @@ static VGMSTREAM* init_vgmstream_ubi_sb_layer(ubi_sb_header* sb, STREAMFILE* sf_
|
|||
if (sb->is_external) {
|
||||
sf_data = open_streamfile_by_filename(sf,sb->resource_name);
|
||||
if (sf_data == NULL) {
|
||||
/* play silence if external file is not found */
|
||||
vgm_logi("UBI SB: external file '%s' not found (put together)\n", sb->resource_name);
|
||||
goto fail;
|
||||
concatn(sizeof(sb->readable_name), sb->readable_name, " (missing)");
|
||||
sb->duration = 1.0f;
|
||||
return init_vgmstream_ubi_sb_silence(sb);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -6,53 +6,45 @@
|
|||
static int xa_read_subsongs(STREAMFILE* sf, int target_subsong, off_t start, uint16_t* p_stream_config, off_t* p_stream_offset, size_t* p_stream_size, int* p_form2);
|
||||
static int xa_check_format(STREAMFILE* sf, off_t offset, int is_blocked);
|
||||
|
||||
/* XA - from Sony PS1 and Philips CD-i CD audio, also Saturn streams */
|
||||
/* XA - from Sony PS1 and Philips CD-i CD audio */
|
||||
VGMSTREAM* init_vgmstream_xa(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag = 0, channels, sample_rate, bps;
|
||||
int is_riff = 0, is_blocked = 0, is_form2 = 0;
|
||||
int is_riff = 0, is_form2 = 0, is_blocked;
|
||||
size_t stream_size = 0;
|
||||
int total_subsongs = 0, target_subsong = sf->stream_index;
|
||||
uint16_t target_config = 0;
|
||||
|
||||
|
||||
|
||||
/* checks */
|
||||
/* .xa: common
|
||||
* .str: often videos and sometimes speech/music
|
||||
* .adp: Phantasy Star Collection (SAT) raw XA
|
||||
* .pxa: Mortal Kombat 4 (PS1)
|
||||
* .grn: Micro Machines (CDi)
|
||||
* (extensionless): bigfiles [Castlevania: Symphony of the Night (PS1)] */
|
||||
if (!check_extensions(sf,"xa,str,adp,pxa,grn,"))
|
||||
goto fail;
|
||||
|
||||
/* Proper XA comes in raw (BIN 2352 mode2/form2) CD sectors, that contain XA subheaders.
|
||||
* Also has minimal support for headerless (ISO 2048 mode1/data) mode. */
|
||||
|
||||
/* check RIFF header = raw (optional, added when ripping and not part of the CD data) */
|
||||
if (read_u32be(0x00,sf) == 0x52494646 && /* "RIFF" */
|
||||
read_u32be(0x08,sf) == 0x43445841 && /* "CDXA" */
|
||||
read_u32be(0x0C,sf) == 0x666D7420) { /* "fmt " */
|
||||
if (read_u32be(0x00,sf) == 0x00FFFFFF && read_u32be(0x04,sf) == 0xFFFFFFFF && read_u32be(0x08,sf) == 0xFFFFFF00) {
|
||||
/* sector sync word = raw data */
|
||||
is_blocked = 1;
|
||||
start_offset = 0x00;
|
||||
}
|
||||
else if (is_id32be(0x00,sf, "RIFF") && is_id32be(0x08,sf, "CDXA") && is_id32be(0x0C,sf, "fmt ")) {
|
||||
/* RIFF header = raw with header (optional, added by CD drivers when copying and not part of the CD data) */
|
||||
is_blocked = 1;
|
||||
is_riff = 1;
|
||||
start_offset = 0x2c; /* after "data", ignore RIFF values as often are wrong */
|
||||
}
|
||||
else {
|
||||
/* sector sync word = raw */
|
||||
if (read_u32be(0x00,sf) == 0x00FFFFFF &&
|
||||
read_u32be(0x04,sf) == 0xFFFFFFFF &&
|
||||
read_u32be(0x08,sf) == 0xFFFFFF00) {
|
||||
is_blocked = 1;
|
||||
start_offset = 0x00;
|
||||
}
|
||||
else {
|
||||
/* headerless or possibly incorrectly ripped */
|
||||
start_offset = 0x00;
|
||||
}
|
||||
/* non-blocked (ISO 2048 mode1/data) or incorrectly ripped: use TXTH */
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* .xa: common
|
||||
* .str: often videos and sometimes speech/music
|
||||
* .pxa: Mortal Kombat 4 (PS1)
|
||||
* .grn: Micro Machines (CDi)
|
||||
* (extensionless): bigfiles [Castlevania: Symphony of the Night (PS1)] */
|
||||
if (!check_extensions(sf,"xa,str,pxa,grn,"))
|
||||
goto fail;
|
||||
|
||||
/* Proper XA comes in raw (BIN 2352 mode2/form2) CD sectors, that contain XA subheaders.
|
||||
* For headerless XA (ISO 2048 mode1/data) mode use TXTH. */
|
||||
|
||||
/* test for XA data, since format is raw-ish (with RIFF it's assumed to be ok) */
|
||||
if (!is_riff && !xa_check_format(sf, start_offset, is_blocked))
|
||||
goto fail;
|
||||
|
@ -89,42 +81,18 @@ VGMSTREAM* init_vgmstream_xa(STREAMFILE* sf) {
|
|||
switch((xa_header >> 6) & 1) { /* 6: emphasis (applies a filter) */
|
||||
case 0: break;
|
||||
default: /* shouldn't be used by games */
|
||||
VGM_LOG("XA: unknown emphasis found\n");
|
||||
break;
|
||||
vgm_logi("XA: unknown emphasis found\n");
|
||||
goto fail;
|
||||
}
|
||||
switch((xa_header >> 7) & 1) { /* 7: reserved */
|
||||
case 0: break;
|
||||
default:
|
||||
VGM_LOG("XA: unknown reserved bit found\n");
|
||||
break;
|
||||
vgm_logi("XA: unknown reserved bit found\n");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* headerless */
|
||||
if (check_extensions(sf,"adp")) {
|
||||
/* Phantasy Star Collection (SAT) raw files */
|
||||
/* most are stereo, though a few (mainly sfx banks, sometimes using .bin) are mono */
|
||||
|
||||
char filename[PATH_LIMIT] = {0};
|
||||
get_streamfile_filename(sf, filename,PATH_LIMIT);
|
||||
|
||||
/* detect PS1 mono files, very lame but whatevs, no way to detect XA mono/stereo */
|
||||
if (filename[0]=='P' && filename[1]=='S' && filename[2]=='1' && filename[3]=='S') {
|
||||
channels = 1;
|
||||
sample_rate = 22050;
|
||||
}
|
||||
else {
|
||||
channels = 2;
|
||||
sample_rate = 44100;
|
||||
}
|
||||
bps = 4;
|
||||
}
|
||||
else {
|
||||
/* incorrectly ripped standard XA */
|
||||
channels = 2;
|
||||
sample_rate = 37800;
|
||||
bps = 4;
|
||||
}
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* untested */
|
||||
|
@ -161,7 +129,9 @@ fail:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static int xa_check_format(STREAMFILE *sf, off_t offset, int is_blocked) {
|
||||
uint8_t frame_hdr[0x10];
|
||||
int i, j, sector = 0, skip = 0;
|
||||
off_t test_offset = offset;
|
||||
const size_t sector_size = (is_blocked ? 0x900 : 0x800);
|
||||
|
@ -172,23 +142,27 @@ static int xa_check_format(STREAMFILE *sf, off_t offset, int is_blocked) {
|
|||
|
||||
/* test frames inside CD sectors */
|
||||
while (sector < sector_max) {
|
||||
uint8_t xa_submode = read_u8(test_offset + 0x12, sf);
|
||||
int is_audio = !(xa_submode & 0x08) && (xa_submode & 0x04) && !(xa_submode & 0x02);
|
||||
if (is_blocked) {
|
||||
uint8_t xa_submode = read_u8(test_offset + 0x12, sf);
|
||||
int is_audio = !(xa_submode & 0x08) && (xa_submode & 0x04) && !(xa_submode & 0x02);
|
||||
|
||||
if (is_blocked && !is_audio) {
|
||||
skip++;
|
||||
if (sector == 0 && skip > skip_max) /* no a single audio sector found */
|
||||
goto fail;
|
||||
test_offset += sector_size + extra_size + extra_size;
|
||||
continue;
|
||||
if (is_blocked && !is_audio) {
|
||||
skip++;
|
||||
if (sector == 0 && skip > skip_max) /* no a single audio sector found */
|
||||
goto fail;
|
||||
test_offset += sector_size + extra_size + extra_size;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
test_offset += extra_size; /* header */
|
||||
|
||||
for (i = 0; i < (sector_size / frame_size); i++) {
|
||||
read_streamfile(frame_hdr, test_offset, sizeof(frame_hdr), sf);
|
||||
|
||||
/* XA frame checks: filter indexes should be 0..3, and shifts 0..C */
|
||||
for (j = 0; j < 16; j++) {
|
||||
uint8_t header = read_u8(test_offset + j, sf);
|
||||
uint8_t header = get_u8(frame_hdr + j);
|
||||
if (((header >> 4) & 0xF) > 0x03)
|
||||
goto fail;
|
||||
if (((header >> 0) & 0xF) > 0x0c)
|
||||
|
@ -196,14 +170,14 @@ static int xa_check_format(STREAMFILE *sf, off_t offset, int is_blocked) {
|
|||
}
|
||||
|
||||
/* XA headers pairs are repeated */
|
||||
if (read_u32be(test_offset+0x00, sf) != read_u32be(test_offset+0x04, sf) ||
|
||||
read_u32be(test_offset+0x08, sf) != read_u32be(test_offset+0x0c, sf))
|
||||
if (get_u32be(frame_hdr+0x00) != get_u32be(frame_hdr+0x04) ||
|
||||
get_u32be(frame_hdr+0x08) != get_u32be(frame_hdr+0x0c))
|
||||
goto fail;
|
||||
/* blank frames should always use 0x0c0c0c0c (due to how shift works) */
|
||||
if (read_u32be(test_offset+0x00, sf) == 0 &&
|
||||
read_u32be(test_offset+0x04, sf) == 0 &&
|
||||
read_u32be(test_offset+0x08, sf) == 0 &&
|
||||
read_u32be(test_offset+0x0c, sf) == 0)
|
||||
if (get_u32be(frame_hdr+0x00) == 0 &&
|
||||
get_u32be(frame_hdr+0x04) == 0 &&
|
||||
get_u32be(frame_hdr+0x08) == 0 &&
|
||||
get_u32be(frame_hdr+0x0c) == 0)
|
||||
goto fail;
|
||||
|
||||
test_offset += 0x80;
|
||||
|
|
|
@ -1,12 +1,22 @@
|
|||
#include "streamfile.h"
|
||||
#include "util.h"
|
||||
#include "vgmstream.h"
|
||||
#include <string.h>
|
||||
|
||||
/* for dup/fdopen in some systems */
|
||||
#ifndef _MSC_VER
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
//TODO: move
|
||||
#ifndef DIR_SEPARATOR
|
||||
#if defined (_WIN32) || defined (WIN32)
|
||||
#define DIR_SEPARATOR '\\'
|
||||
#else
|
||||
#define DIR_SEPARATOR '/'
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* For (rarely needed) +2GB file support we use fseek64/ftell64. Those are usually available
|
||||
* but may depend on compiler.
|
||||
* - MSVC: +VS2008 should work
|
||||
|
@ -108,7 +118,7 @@ static size_t stdio_read(STDIO_STREAMFILE* sf, uint8_t* dst, offv_t offset, size
|
|||
|
||||
#ifdef VGM_DEBUG_OUTPUT
|
||||
if (offset < sf->buf_offset && length > 0) {
|
||||
VGM_LOG("stdio: rebuffer, requested %x vs %x (sf %x)\n", (uint32_t)offset, (uint32_t)sf->buf_offset, (uint32_t)sf);
|
||||
//VGM_LOG("stdio: rebuffer, requested %x vs %x (sf %x)\n", (uint32_t)offset, (uint32_t)sf->buf_offset, (uint32_t)sf);
|
||||
//sf->rebuffer++;
|
||||
//if (rebuffer > N) ...
|
||||
}
|
||||
|
@ -341,7 +351,7 @@ static size_t buffer_read(BUFFER_STREAMFILE* sf, uint8_t* dst, offv_t offset, si
|
|||
|
||||
#ifdef VGM_DEBUG_OUTPUT
|
||||
if (offset < sf->buf_offset) {
|
||||
VGM_LOG("buffer: rebuffer, requested %x vs %x (sf %x)\n", (uint32_t)offset, (uint32_t)sf->buf_offset, (uint32_t)sf);
|
||||
//VGM_LOG("buffer: rebuffer, requested %x vs %x (sf %x)\n", (uint32_t)offset, (uint32_t)sf->buf_offset, (uint32_t)sf);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -1263,7 +1273,6 @@ STREAMFILE* read_filemap_file_pos(STREAMFILE* sf, int file_num, int* p_pos) {
|
|||
/* better way? */
|
||||
if (strcmp(line, "#@reset-pos") == 0) {
|
||||
file_pos = 0;
|
||||
VGM_LOG("pos =%i\n", file_pos);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
/*
|
||||
* streamfile.h - definitions for buffered file reading with STREAMFILE
|
||||
*/
|
||||
|
||||
#ifndef _STREAMFILE_H
|
||||
#define _STREAMFILE_H
|
||||
|
||||
|
@ -9,9 +8,14 @@
|
|||
#define _CRT_SECURE_NO_DEPRECATE
|
||||
#endif
|
||||
|
||||
//TODO cleanup
|
||||
//NULL, allocs
|
||||
#include <stdlib.h>
|
||||
//FILE
|
||||
#include <stdio.h>
|
||||
//string functions in meta and so on
|
||||
#include <string.h>
|
||||
//off_t
|
||||
#include <sys/types.h>
|
||||
#include "streamtypes.h"
|
||||
#include "util.h"
|
||||
|
@ -22,14 +26,6 @@
|
|||
#include <io.h>
|
||||
#endif
|
||||
|
||||
#ifndef DIR_SEPARATOR
|
||||
#if defined (_WIN32) || defined (WIN32)
|
||||
#define DIR_SEPARATOR '\\'
|
||||
#else
|
||||
#define DIR_SEPARATOR '/'
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* 64-bit offset is needed for banks that hit +2.5GB (like .fsb or .ktsl2stbin).
|
||||
* Leave as typedef to toggle since it's theoretically slower when compiled as 32-bit.
|
||||
* ATM it's only used in choice places until more performance tests are done.
|
||||
|
|
|
@ -12,19 +12,19 @@
|
|||
/* host endian independent multi-byte integer reading */
|
||||
|
||||
static inline int16_t get_16bitBE(const uint8_t* p) {
|
||||
return (p[0]<<8) | (p[1]);
|
||||
return ((uint16_t)p[0]<<8) | ((uint16_t)p[1]);
|
||||
}
|
||||
|
||||
static inline int16_t get_16bitLE(const uint8_t* p) {
|
||||
return (p[0]) | (p[1]<<8);
|
||||
return ((uint16_t)p[0]) | ((uint16_t)p[1]<<8);
|
||||
}
|
||||
|
||||
static inline int32_t get_32bitBE(const uint8_t* p) {
|
||||
return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | (p[3]);
|
||||
return ((uint32_t)p[0]<<24) | ((uint32_t)p[1]<<16) | ((uint32_t)p[2]<<8) | ((uint32_t)p[3]);
|
||||
}
|
||||
|
||||
static inline int32_t get_32bitLE(const uint8_t* p) {
|
||||
return (p[0]) | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
|
||||
return ((uint32_t)p[0]) | ((uint32_t)p[1]<<8) | ((uint32_t)p[2]<<16) | ((uint32_t)p[3]<<24);
|
||||
}
|
||||
|
||||
static inline int64_t get_64bitBE(const uint8_t* p) {
|
||||
|
|
|
@ -6,5 +6,10 @@
|
|||
typedef uint32_t (*read_u32_t)(off_t, STREAMFILE*);
|
||||
typedef int32_t (*read_s32_t)(off_t, STREAMFILE*);
|
||||
typedef uint16_t (*read_u16_t)(off_t, STREAMFILE*);
|
||||
typedef float (*read_f32_t)(off_t, STREAMFILE*);
|
||||
|
||||
//todo move here
|
||||
#define guess_endian32 guess_endianness32bit
|
||||
#define guess_endian16 guess_endianness16bit
|
||||
|
||||
#endif
|
||||
|
|
|
@ -92,9 +92,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
|
|||
init_vgmstream_musx,
|
||||
init_vgmstream_leg,
|
||||
init_vgmstream_filp,
|
||||
init_vgmstream_ikm_ps2,
|
||||
init_vgmstream_ikm_pc,
|
||||
init_vgmstream_ikm_psp,
|
||||
init_vgmstream_ikm,
|
||||
init_vgmstream_sfs,
|
||||
init_vgmstream_bg00,
|
||||
init_vgmstream_sat_dvi,
|
||||
|
@ -214,7 +212,8 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
|
|||
init_vgmstream_xbox_hlwav,
|
||||
init_vgmstream_myspd,
|
||||
init_vgmstream_his,
|
||||
init_vgmstream_ps2_ast,
|
||||
init_vgmstream_ast_mmv,
|
||||
init_vgmstream_ast_mv,
|
||||
init_vgmstream_dmsg,
|
||||
init_vgmstream_ngc_dsp_aaap,
|
||||
init_vgmstream_ngc_dsp_konami,
|
||||
|
@ -237,7 +236,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
|
|||
init_vgmstream_dsp_xiii,
|
||||
init_vgmstream_dsp_cabelas,
|
||||
init_vgmstream_ps2_adm,
|
||||
init_vgmstream_ps2_lpcm,
|
||||
init_vgmstream_lpcm_shade,
|
||||
init_vgmstream_dsp_bdsp,
|
||||
init_vgmstream_ps2_vms,
|
||||
init_vgmstream_xau,
|
||||
|
|
|
@ -96,6 +96,7 @@ typedef enum {
|
|||
|
||||
coding_XA, /* CD-ROM XA 4-bit */
|
||||
coding_XA8, /* CD-ROM XA 8-bit */
|
||||
coding_XA_EA, /* EA's Saturn XA (not to be confused with EA-XA) */
|
||||
coding_PSX, /* Sony PS ADPCM (VAG) */
|
||||
coding_PSX_badflags, /* Sony PS ADPCM with custom flag byte */
|
||||
coding_PSX_cfg, /* Sony PS ADPCM with configurable frame size (int math) */
|
||||
|
@ -147,6 +148,7 @@ typedef enum {
|
|||
|
||||
coding_AICA, /* Yamaha AICA ADPCM (stereo) */
|
||||
coding_AICA_int, /* Yamaha AICA ADPCM (mono/interleave) */
|
||||
coding_CP_YM, /* Capcom's Yamaha ADPCM (stereo/mono) */
|
||||
coding_ASKA, /* Aska ADPCM */
|
||||
coding_NXAP, /* NXAP ADPCM */
|
||||
|
||||
|
@ -516,7 +518,8 @@ typedef enum {
|
|||
meta_PONA_3DO, /* Policenauts (3DO) */
|
||||
meta_PONA_PSX, /* Policenauts (PSX) */
|
||||
meta_XBOX_HLWAV, /* Half Life 2 (XBOX) */
|
||||
meta_PS2_AST, /* Some KOEI game (PS2) */
|
||||
meta_AST_MV,
|
||||
meta_AST_MMV,
|
||||
meta_DMSG, /* Nightcaster II - Equinox (XBOX) */
|
||||
meta_NGC_DSP_AAAP, /* Turok: Evolution (NGC), Vexx (NGC) */
|
||||
meta_PS2_STER, /* Juuni Kokuki: Kakukaku Taru Ou Michi Beni Midori no Uka */
|
||||
|
@ -539,7 +542,7 @@ typedef enum {
|
|||
meta_DSP_XIII, /* XIII, possibly more (Ubisoft header???) */
|
||||
meta_DSP_CABELAS, /* Cabelas games */
|
||||
meta_PS2_ADM, /* Dragon Quest V (PS2) */
|
||||
meta_PS2_LPCM, /* Ah! My Goddess */
|
||||
meta_LPCM_SHADE,
|
||||
meta_DSP_BDSP, /* Ah! My Goddess */
|
||||
meta_PS2_VMS, /* Autobahn Raser - Police Madness */
|
||||
meta_XAU, /* XPEC Entertainment (Beat Down (PS2 Xbox), Spectral Force Chronicle (PS2)) */
|
||||
|
|
Loading…
Reference in New Issue