Updated VGMStream to r1050-3581-g8fd25a33
parent
1be15d73e3
commit
0b09d1567b
|
@ -133,6 +133,16 @@
|
|||
833A7A2E1ED11961003EC53E /* xau.c in Sources */ = {isa = PBXBuildFile; fileRef = 833A7A2D1ED11961003EC53E /* xau.c */; };
|
||||
8342469420C4D23000926E48 /* h4m.c in Sources */ = {isa = PBXBuildFile; fileRef = 8342469020C4D22F00926E48 /* h4m.c */; };
|
||||
8342469620C4D23D00926E48 /* blocked_h4m.c in Sources */ = {isa = PBXBuildFile; fileRef = 8342469520C4D23D00926E48 /* blocked_h4m.c */; };
|
||||
8346D97925BF838C00D1A8B0 /* idtech.c in Sources */ = {isa = PBXBuildFile; fileRef = 8346D97425BF838C00D1A8B0 /* idtech.c */; };
|
||||
8346D97A25BF838C00D1A8B0 /* idtech_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 8346D97525BF838C00D1A8B0 /* idtech_streamfile.h */; };
|
||||
8346D97B25BF838C00D1A8B0 /* ktac.c in Sources */ = {isa = PBXBuildFile; fileRef = 8346D97625BF838C00D1A8B0 /* ktac.c */; };
|
||||
8346D97C25BF838C00D1A8B0 /* mjb_mjh.c in Sources */ = {isa = PBXBuildFile; fileRef = 8346D97725BF838C00D1A8B0 /* mjb_mjh.c */; };
|
||||
8346D97D25BF838C00D1A8B0 /* compresswave.c in Sources */ = {isa = PBXBuildFile; fileRef = 8346D97825BF838C00D1A8B0 /* compresswave.c */; };
|
||||
8346D98325BF83B300D1A8B0 /* speex_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 8346D97E25BF83B200D1A8B0 /* speex_decoder.c */; };
|
||||
8346D98425BF83B300D1A8B0 /* coding_utils_samples.h in Headers */ = {isa = PBXBuildFile; fileRef = 8346D97F25BF83B200D1A8B0 /* coding_utils_samples.h */; };
|
||||
8346D98525BF83B300D1A8B0 /* compresswave_decoder_lib.c in Sources */ = {isa = PBXBuildFile; fileRef = 8346D98025BF83B300D1A8B0 /* compresswave_decoder_lib.c */; };
|
||||
8346D98625BF83B300D1A8B0 /* compresswave_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 8346D98125BF83B300D1A8B0 /* compresswave_decoder.c */; };
|
||||
8346D98725BF83B300D1A8B0 /* compresswave_decoder_lib.h in Headers */ = {isa = PBXBuildFile; fileRef = 8346D98225BF83B300D1A8B0 /* compresswave_decoder_lib.h */; };
|
||||
8349A8DF1FE6251F00E26435 /* vorbis_custom_utils_vid1.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8DC1FE6251E00E26435 /* vorbis_custom_utils_vid1.c */; };
|
||||
8349A8E11FE6251F00E26435 /* ea_mt_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8DE1FE6251F00E26435 /* ea_mt_decoder.c */; };
|
||||
8349A8E81FE6253900E26435 /* blocked_dec.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8E21FE6253800E26435 /* blocked_dec.c */; };
|
||||
|
@ -471,10 +481,8 @@
|
|||
837CEADB23487E8300E62A4A /* bgw_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 837CEAD723487E8300E62A4A /* bgw_streamfile.h */; };
|
||||
837CEAF123487F2C00E62A4A /* xvas.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEADC23487F2900E62A4A /* xvas.c */; };
|
||||
837CEAF223487F2C00E62A4A /* raw_pcm.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEADD23487F2A00E62A4A /* raw_pcm.c */; };
|
||||
837CEAF323487F2C00E62A4A /* mzrt_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 837CEADE23487F2A00E62A4A /* mzrt_streamfile.h */; };
|
||||
837CEAF423487F2C00E62A4A /* xa_xa30.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEADF23487F2A00E62A4A /* xa_xa30.c */; };
|
||||
837CEAF523487F2C00E62A4A /* ubi_hx.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEAE023487F2A00E62A4A /* ubi_hx.c */; };
|
||||
837CEAF623487F2C00E62A4A /* mzrt.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEAE123487F2A00E62A4A /* mzrt.c */; };
|
||||
837CEAF723487F2C00E62A4A /* nub.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEAE223487F2A00E62A4A /* nub.c */; };
|
||||
837CEAF823487F2C00E62A4A /* xmv_valve.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEAE323487F2A00E62A4A /* xmv_valve.c */; };
|
||||
837CEAF923487F2C00E62A4A /* xavs.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEAE423487F2A00E62A4A /* xavs.c */; };
|
||||
|
@ -877,6 +885,16 @@
|
|||
833A7A2D1ED11961003EC53E /* xau.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xau.c; sourceTree = "<group>"; };
|
||||
8342469020C4D22F00926E48 /* h4m.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = h4m.c; sourceTree = "<group>"; };
|
||||
8342469520C4D23D00926E48 /* blocked_h4m.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_h4m.c; sourceTree = "<group>"; };
|
||||
8346D97425BF838C00D1A8B0 /* idtech.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = idtech.c; sourceTree = "<group>"; };
|
||||
8346D97525BF838C00D1A8B0 /* idtech_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = idtech_streamfile.h; sourceTree = "<group>"; };
|
||||
8346D97625BF838C00D1A8B0 /* ktac.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ktac.c; sourceTree = "<group>"; };
|
||||
8346D97725BF838C00D1A8B0 /* mjb_mjh.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mjb_mjh.c; sourceTree = "<group>"; };
|
||||
8346D97825BF838C00D1A8B0 /* compresswave.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = compresswave.c; sourceTree = "<group>"; };
|
||||
8346D97E25BF83B200D1A8B0 /* speex_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = speex_decoder.c; sourceTree = "<group>"; };
|
||||
8346D97F25BF83B200D1A8B0 /* coding_utils_samples.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = coding_utils_samples.h; sourceTree = "<group>"; };
|
||||
8346D98025BF83B300D1A8B0 /* compresswave_decoder_lib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = compresswave_decoder_lib.c; sourceTree = "<group>"; };
|
||||
8346D98125BF83B300D1A8B0 /* compresswave_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = compresswave_decoder.c; sourceTree = "<group>"; };
|
||||
8346D98225BF83B300D1A8B0 /* compresswave_decoder_lib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = compresswave_decoder_lib.h; sourceTree = "<group>"; };
|
||||
8349A8DC1FE6251E00E26435 /* vorbis_custom_utils_vid1.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vorbis_custom_utils_vid1.c; sourceTree = "<group>"; };
|
||||
8349A8DE1FE6251F00E26435 /* ea_mt_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ea_mt_decoder.c; sourceTree = "<group>"; };
|
||||
8349A8E21FE6253800E26435 /* blocked_dec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_dec.c; sourceTree = "<group>"; };
|
||||
|
@ -1215,10 +1233,8 @@
|
|||
837CEAD723487E8300E62A4A /* bgw_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bgw_streamfile.h; sourceTree = "<group>"; };
|
||||
837CEADC23487F2900E62A4A /* xvas.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xvas.c; sourceTree = "<group>"; };
|
||||
837CEADD23487F2A00E62A4A /* raw_pcm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = raw_pcm.c; sourceTree = "<group>"; };
|
||||
837CEADE23487F2A00E62A4A /* mzrt_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mzrt_streamfile.h; sourceTree = "<group>"; };
|
||||
837CEADF23487F2A00E62A4A /* xa_xa30.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xa_xa30.c; sourceTree = "<group>"; };
|
||||
837CEAE023487F2A00E62A4A /* ubi_hx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ubi_hx.c; sourceTree = "<group>"; };
|
||||
837CEAE123487F2A00E62A4A /* mzrt.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mzrt.c; sourceTree = "<group>"; };
|
||||
837CEAE223487F2A00E62A4A /* nub.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nub.c; sourceTree = "<group>"; };
|
||||
837CEAE323487F2A00E62A4A /* xmv_valve.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xmv_valve.c; sourceTree = "<group>"; };
|
||||
837CEAE423487F2A00E62A4A /* xavs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xavs.c; sourceTree = "<group>"; };
|
||||
|
@ -1545,8 +1561,12 @@
|
|||
83031EC0243C50A800C3F3E0 /* circus_decoder_miniz.c */,
|
||||
83031EBE243C50A700C3F3E0 /* circus_decoder_miniz.h */,
|
||||
832BF7FC21E050B6006F50F1 /* circus_decoder.c */,
|
||||
8346D97F25BF83B200D1A8B0 /* coding_utils_samples.h */,
|
||||
831BA6221EAC61CB00CF89B0 /* coding_utils.c */,
|
||||
836F6DE518BDC2180095E648 /* coding.h */,
|
||||
8346D98025BF83B300D1A8B0 /* compresswave_decoder_lib.c */,
|
||||
8346D98225BF83B300D1A8B0 /* compresswave_decoder_lib.h */,
|
||||
8346D98125BF83B300D1A8B0 /* compresswave_decoder.c */,
|
||||
834FE0AD215C798B000A5D3D /* derf_decoder.c */,
|
||||
8351F3252212B53300A606E4 /* dsa_decoder.c */,
|
||||
834FE0B1215C798C000A5D3D /* ea_mt_decoder_utk.h */,
|
||||
|
@ -1600,6 +1620,7 @@
|
|||
8373341423F60C7B00DE14DC /* relic_decoder.c */,
|
||||
836F6DFB18BDC2180095E648 /* SASSC_decoder.c */,
|
||||
836F6DFC18BDC2180095E648 /* sdx2_decoder.c */,
|
||||
8346D97E25BF83B200D1A8B0 /* speex_decoder.c */,
|
||||
8373341023F60C7A00DE14DC /* tgcadpcm_decoder.c */,
|
||||
837CEA7623487E2400E62A4A /* ubi_adpcm_decoder.c */,
|
||||
83F1EE2C245D4FB20076E182 /* vadpcm_decoder.c */,
|
||||
|
@ -1734,6 +1755,7 @@
|
|||
8306B0CF2098458F000302D4 /* caf.c */,
|
||||
836F6E3B18BDC2180095E648 /* capdsp.c */,
|
||||
834FE0E8215C79EC000A5D3D /* ck.c */,
|
||||
8346D97825BF838C00D1A8B0 /* compresswave.c */,
|
||||
83A8BAE425667AA7000F5F3F /* cpk.c */,
|
||||
83FC177023AC59A800E1025F /* cri_utf.c */,
|
||||
83FC176C23AC58D100E1025F /* cri_utf.h */,
|
||||
|
@ -1799,6 +1821,8 @@
|
|||
834FE0DF215C79EB000A5D3D /* hd3_bd3.c */,
|
||||
836F6E5318BDC2180095E648 /* his.c */,
|
||||
834FE0E0215C79EB000A5D3D /* idsp_ie.c */,
|
||||
8346D97525BF838C00D1A8B0 /* idtech_streamfile.h */,
|
||||
8346D97425BF838C00D1A8B0 /* idtech.c */,
|
||||
8399335C2591E8C0001855AF /* ifs.c */,
|
||||
83C7280922BC893C00678B4A /* ikm.c */,
|
||||
837CEAE623487F2B00E62A4A /* ima.c */,
|
||||
|
@ -1816,6 +1840,7 @@
|
|||
834FE0C3215C79E6000A5D3D /* kma9_streamfile.h */,
|
||||
83A21F83201D8981000F04B9 /* kma9.c */,
|
||||
836F6E5918BDC2180095E648 /* kraw.c */,
|
||||
8346D97625BF838C00D1A8B0 /* ktac.c */,
|
||||
83AA7F792519C042004C5298 /* ktsc.c */,
|
||||
83D20074248DDB760048BD24 /* ktsr.c */,
|
||||
830EBE122004656E0023AA10 /* ktss.c */,
|
||||
|
@ -1829,6 +1854,7 @@
|
|||
83EDE5D61A70951A005F5D84 /* mca.c */,
|
||||
836F6E5E18BDC2180095E648 /* meta.h */,
|
||||
834FE0DE215C79EB000A5D3D /* mib_mih.c */,
|
||||
8346D97725BF838C00D1A8B0 /* mjb_mjh.c */,
|
||||
836F6E5F18BDC2180095E648 /* mn_str.c */,
|
||||
8349A9031FE6258100E26435 /* mogg.c */,
|
||||
836F6E6018BDC2180095E648 /* mp4.c */,
|
||||
|
@ -1852,8 +1878,6 @@
|
|||
836F6E6318BDC2180095E648 /* musc.c */,
|
||||
836F6E6418BDC2180095E648 /* musx.c */,
|
||||
836F6E6518BDC2180095E648 /* myspd.c */,
|
||||
837CEADE23487F2A00E62A4A /* mzrt_streamfile.h */,
|
||||
837CEAE123487F2A00E62A4A /* mzrt.c */,
|
||||
8349A9061FE6258100E26435 /* naac.c */,
|
||||
836F6E6618BDC2180095E648 /* naomi_adpcm.c */,
|
||||
836F6E6718BDC2180095E648 /* naomi_spsd.c */,
|
||||
|
@ -2156,6 +2180,8 @@
|
|||
836F705518BDC2190095E648 /* streamtypes.h in Headers */,
|
||||
836F6F3518BDC2190095E648 /* nwa_decoder.h in Headers */,
|
||||
83031EC9243C50A800C3F3E0 /* circus_decoder_lzxpcm.h in Headers */,
|
||||
8346D97A25BF838C00D1A8B0 /* idtech_streamfile.h in Headers */,
|
||||
8346D98425BF83B300D1A8B0 /* coding_utils_samples.h in Headers */,
|
||||
83C7282222BC893D00678B4A /* mta2_streamfile.h in Headers */,
|
||||
83AA7F802519C042004C5298 /* sab_streamfile.h in Headers */,
|
||||
83A21F87201D8981000F04B9 /* fsb_keys.h in Headers */,
|
||||
|
@ -2201,6 +2227,7 @@
|
|||
834FE0B5215C798C000A5D3D /* acm_decoder_libacm.h in Headers */,
|
||||
839E21E61F2EDAF100EE54D7 /* vorbis_custom_data_wwise.h in Headers */,
|
||||
83AA7F8A2519C076004C5298 /* decode.h in Headers */,
|
||||
8346D98725BF83B300D1A8B0 /* compresswave_decoder_lib.h in Headers */,
|
||||
8373342D23F60CDC00DE14DC /* bnsf_keys.h in Headers */,
|
||||
834FE103215C79ED000A5D3D /* ea_schl_streamfile.h in Headers */,
|
||||
83F0AA6021E2028C004BBC04 /* vsv_streamfile.h in Headers */,
|
||||
|
@ -2223,7 +2250,6 @@
|
|||
83C7281822BC893D00678B4A /* sfh_streamfile.h in Headers */,
|
||||
834FE0EF215C79ED000A5D3D /* xvag_streamfile.h in Headers */,
|
||||
8349A90C1FE6258200E26435 /* sqex_scd_streamfile.h in Headers */,
|
||||
837CEAF323487F2C00E62A4A /* mzrt_streamfile.h in Headers */,
|
||||
83031EC4243C50A800C3F3E0 /* circus_decoder_miniz.h in Headers */,
|
||||
8349A91B1FE6258200E26435 /* adx_keys.h in Headers */,
|
||||
836F6F4D18BDC2190095E648 /* layout.h in Headers */,
|
||||
|
@ -2419,6 +2445,7 @@
|
|||
836F705318BDC2190095E648 /* streamfile.c in Sources */,
|
||||
836F6F7418BDC2190095E648 /* bgw.c in Sources */,
|
||||
83F1EE2D245D4FB20076E182 /* imuse_decoder.c in Sources */,
|
||||
8346D98325BF83B300D1A8B0 /* speex_decoder.c in Sources */,
|
||||
836F6F7218BDC2190095E648 /* baf.c in Sources */,
|
||||
83F5F8831908D0A400C8E65F /* fsb5.c in Sources */,
|
||||
836F6FD418BDC2190095E648 /* ps2_dxh.c in Sources */,
|
||||
|
@ -2447,6 +2474,7 @@
|
|||
836F6FA118BDC2190095E648 /* myspd.c in Sources */,
|
||||
837CEB0123487F2C00E62A4A /* xmu.c in Sources */,
|
||||
836F6FD718BDC2190095E648 /* ps2_filp.c in Sources */,
|
||||
8346D97925BF838C00D1A8B0 /* idtech.c in Sources */,
|
||||
83FF0EBC1E93282100C58054 /* wwise.c in Sources */,
|
||||
836F6F7018BDC2190095E648 /* apple_caff.c in Sources */,
|
||||
836F6FB618BDC2190095E648 /* ngc_sck_dsp.c in Sources */,
|
||||
|
@ -2459,6 +2487,7 @@
|
|||
836F702318BDC2190095E648 /* rsf.c in Sources */,
|
||||
834FE109215C79ED000A5D3D /* idsp_ie.c in Sources */,
|
||||
83299FD01E7660C7003A3242 /* bik.c in Sources */,
|
||||
8346D97C25BF838C00D1A8B0 /* mjb_mjh.c in Sources */,
|
||||
8373341823F60C7B00DE14DC /* tgcadpcm_decoder.c in Sources */,
|
||||
836F6F3318BDC2190095E648 /* ngc_dtk_decoder.c in Sources */,
|
||||
83C7281322BC893D00678B4A /* mta2.c in Sources */,
|
||||
|
@ -2518,6 +2547,7 @@
|
|||
836F703618BDC2190095E648 /* thp.c in Sources */,
|
||||
836F6F7818BDC2190095E648 /* Cstr.c in Sources */,
|
||||
83F1EE30245D4FC10076E182 /* imuse.c in Sources */,
|
||||
8346D98625BF83B300D1A8B0 /* compresswave_decoder.c in Sources */,
|
||||
836F6F1E18BDC2190095E648 /* acm_decoder.c in Sources */,
|
||||
836F6FD818BDC2190095E648 /* ps2_gbts.c in Sources */,
|
||||
831BA61A1EAC61A500CF89B0 /* ps2_vds_vdm.c in Sources */,
|
||||
|
@ -2564,6 +2594,7 @@
|
|||
8306B0A720984552000302D4 /* blocked_xvas.c in Sources */,
|
||||
8349A9101FE6258200E26435 /* ea_eaac.c in Sources */,
|
||||
836F700F18BDC2190095E648 /* ps2_xa30.c in Sources */,
|
||||
8346D97B25BF838C00D1A8B0 /* ktac.c in Sources */,
|
||||
8349A8ED1FE6253900E26435 /* blocked_ea_sns.c in Sources */,
|
||||
836F6F6F18BDC2190095E648 /* akb.c in Sources */,
|
||||
83EED5D6203A8BD7008BEB45 /* blocked_ea_swvr.c in Sources */,
|
||||
|
@ -2821,7 +2852,6 @@
|
|||
836F6FC918BDC2190095E648 /* ps2_2pfs.c in Sources */,
|
||||
83AFABBC23795202002F3947 /* xssb.c in Sources */,
|
||||
836F704818BDC2190095E648 /* xbox_ims.c in Sources */,
|
||||
837CEAF623487F2C00E62A4A /* mzrt.c in Sources */,
|
||||
83AA7F832519C042004C5298 /* adp_konami.c in Sources */,
|
||||
836F6F7518BDC2190095E648 /* bnsf.c in Sources */,
|
||||
836F704318BDC2190095E648 /* wpd.c in Sources */,
|
||||
|
@ -2847,6 +2877,8 @@
|
|||
836F6F7618BDC2190095E648 /* brstm.c in Sources */,
|
||||
836F700718BDC2190095E648 /* ps2_vgv.c in Sources */,
|
||||
836F704F18BDC2190095E648 /* xwb.c in Sources */,
|
||||
8346D98525BF83B300D1A8B0 /* compresswave_decoder_lib.c in Sources */,
|
||||
8346D97D25BF838C00D1A8B0 /* compresswave.c in Sources */,
|
||||
8306B0AD20984552000302D4 /* blocked_caf.c in Sources */,
|
||||
8306B0AA20984552000302D4 /* blocked_sthd.c in Sources */,
|
||||
836F6FE918BDC2190095E648 /* ps2_mihb.c in Sources */,
|
||||
|
|
|
@ -270,6 +270,17 @@ void seek_imuse(imuse_codec_data* data, int32_t num_sample);
|
|||
void free_imuse(imuse_codec_data* data);
|
||||
|
||||
|
||||
/* compresswave_decoder */
|
||||
typedef struct compresswave_codec_data compresswave_codec_data;
|
||||
|
||||
compresswave_codec_data* init_compresswave(STREAMFILE* sf);
|
||||
void decode_compresswave(compresswave_codec_data* data, sample_t* outbuf, int32_t samples_to_do);
|
||||
void reset_compresswave(compresswave_codec_data* data);
|
||||
void seek_compresswave(compresswave_codec_data* data, int32_t num_sample);
|
||||
void free_compresswave(compresswave_codec_data* data);
|
||||
STREAMFILE* compresswave_get_streamfile(compresswave_codec_data* data);
|
||||
|
||||
|
||||
/* ea_mt_decoder*/
|
||||
typedef struct ea_mt_codec_data ea_mt_codec_data;
|
||||
|
||||
|
@ -512,6 +523,18 @@ void free_celt_fsb(celt_codec_data* data);
|
|||
#endif
|
||||
|
||||
|
||||
#ifdef VGM_USE_SPEEX
|
||||
/* speex_decoder */
|
||||
typedef struct speex_codec_data speex_codec_data;
|
||||
|
||||
speex_codec_data* init_speex_ea(int channels);
|
||||
void decode_speex(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do);
|
||||
void reset_speex(speex_codec_data* data);
|
||||
void seek_speex(VGMSTREAM* vgmstream, int32_t num_sample);
|
||||
void free_speex(speex_codec_data* data);
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
/* ffmpeg_decoder */
|
||||
ffmpeg_codec_data* init_ffmpeg_offset(STREAMFILE* sf, uint64_t start, uint64_t size);
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
#ifndef _CODING_UTILS_SAMPLES_
|
||||
#define _CODING_UTILS_SAMPLES_
|
||||
|
||||
/* sample helpers */
|
||||
//TODO maybe move to .c
|
||||
// (as .h can be inlined but these probably aren't called enough times that there is a notable boost)
|
||||
|
||||
typedef struct {
|
||||
int16_t* samples; /* current samples (pointer is moved once consumed) */
|
||||
int filled; /* samples left */
|
||||
int channels; /* max channels sample buf handles */
|
||||
//TODO may be more useful with filled+consumed and not moving *samples?
|
||||
} s16buf_t;
|
||||
|
||||
static void s16buf_silence(sample_t** p_outbuf, int32_t* p_samples_silence, int channels) {
|
||||
int samples_silence;
|
||||
|
||||
samples_silence = *p_samples_silence;
|
||||
|
||||
memset(*p_outbuf, 0, samples_silence * channels * sizeof(int16_t));
|
||||
|
||||
*p_outbuf += samples_silence * channels;
|
||||
*p_samples_silence -= samples_silence;
|
||||
}
|
||||
|
||||
static void s16buf_discard(sample_t** p_outbuf, s16buf_t* sbuf, int32_t* p_samples_discard) {
|
||||
int samples_discard;
|
||||
|
||||
samples_discard = *p_samples_discard;
|
||||
if (samples_discard > sbuf->filled)
|
||||
samples_discard = sbuf->filled;
|
||||
|
||||
/* just ignore part of samples */
|
||||
|
||||
sbuf->samples += samples_discard * sbuf->channels;
|
||||
sbuf->filled -= samples_discard;
|
||||
|
||||
*p_samples_discard -= samples_discard;
|
||||
}
|
||||
|
||||
/* copy, move and mark consumed samples */
|
||||
static void s16buf_consume(sample_t** p_outbuf, s16buf_t* sbuf, int32_t* p_samples_consume) {
|
||||
int samples_consume;
|
||||
|
||||
samples_consume = *p_samples_consume;
|
||||
if (samples_consume > sbuf->filled)
|
||||
samples_consume = sbuf->filled;
|
||||
|
||||
/* memcpy is safe when filled/samples_copy is 0 (but must pass non-NULL bufs) */
|
||||
memcpy(*p_outbuf, sbuf->samples, samples_consume * sbuf->channels * sizeof(int16_t));
|
||||
|
||||
sbuf->samples += samples_consume * sbuf->channels;
|
||||
sbuf->filled -= samples_consume;
|
||||
|
||||
*p_outbuf += samples_consume * sbuf->channels;
|
||||
*p_samples_consume -= samples_consume;
|
||||
}
|
||||
|
||||
|
||||
#endif /* _CODING_UTILS_SAMPLES_ */
|
|
@ -0,0 +1,138 @@
|
|||
#include "coding.h"
|
||||
#include "coding_utils_samples.h"
|
||||
#include "compresswave_decoder_lib.h"
|
||||
|
||||
|
||||
#define COMPRESSWAVE_MAX_FRAME_SAMPLES 0x1000 /* arbitrary but should be multiple of 2 for 22050 mode */
|
||||
|
||||
/* opaque struct */
|
||||
struct compresswave_codec_data {
|
||||
/* config */
|
||||
STREAMFILE* sf;
|
||||
TCompressWaveData* cw;
|
||||
|
||||
/* frame state */
|
||||
int16_t* samples;
|
||||
int frame_samples;
|
||||
|
||||
/* frame state */
|
||||
s16buf_t sbuf;
|
||||
int samples_discard;
|
||||
};
|
||||
|
||||
|
||||
compresswave_codec_data* init_compresswave(STREAMFILE* sf) {
|
||||
compresswave_codec_data* data = NULL;
|
||||
|
||||
data = calloc(1, sizeof(compresswave_codec_data));
|
||||
if (!data) goto fail;
|
||||
|
||||
data->sf = reopen_streamfile(sf, 0);
|
||||
if (!data->sf) goto fail;
|
||||
|
||||
data->frame_samples = COMPRESSWAVE_MAX_FRAME_SAMPLES;
|
||||
data->samples = malloc(2 * data->frame_samples * sizeof(int16_t)); /* always stereo */
|
||||
if (!data->samples) goto fail;
|
||||
|
||||
|
||||
data->cw = TCompressWaveData_Create();
|
||||
if (!data->cw) goto fail;
|
||||
|
||||
TCompressWaveData_LoadFromStream(data->cw, data->sf);
|
||||
|
||||
reset_compresswave(data);
|
||||
|
||||
return data;
|
||||
fail:
|
||||
free_compresswave(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static int decode_frame(compresswave_codec_data* data, int32_t samples_to_do) {
|
||||
uint32_t Len;
|
||||
int ok;
|
||||
|
||||
data->sbuf.samples = data->samples;
|
||||
data->sbuf.channels = 2;
|
||||
data->sbuf.filled = 0;
|
||||
|
||||
if (samples_to_do > data->frame_samples)
|
||||
samples_to_do = data->frame_samples;
|
||||
if (samples_to_do % 2 && samples_to_do > 1)
|
||||
samples_to_do -= 1; /* 22khz does 2 samples at once */
|
||||
|
||||
Len = samples_to_do * sizeof(int16_t) * 2; /* forced stereo */
|
||||
|
||||
ok = TCompressWaveData_Rendering(data->cw, data->sbuf.samples, Len);
|
||||
if (!ok) goto fail;
|
||||
|
||||
data->sbuf.filled = samples_to_do;
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void decode_compresswave(compresswave_codec_data* data, sample_t* outbuf, int32_t samples_to_do) {
|
||||
int ok;
|
||||
|
||||
|
||||
while (samples_to_do > 0) {
|
||||
s16buf_t* sbuf = &data->sbuf;
|
||||
|
||||
if (sbuf->filled <= 0) {
|
||||
ok = decode_frame(data, samples_to_do);
|
||||
if (!ok) goto fail;
|
||||
}
|
||||
|
||||
if (data->samples_discard)
|
||||
s16buf_discard(&outbuf, sbuf, &data->samples_discard);
|
||||
else
|
||||
s16buf_consume(&outbuf, sbuf, &samples_to_do);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
fail:
|
||||
VGM_LOG("COMPRESSWAVE: decode fail, missing %i samples\n", samples_to_do);
|
||||
s16buf_silence(&outbuf, &samples_to_do, 2);
|
||||
}
|
||||
|
||||
|
||||
void reset_compresswave(compresswave_codec_data* data) {
|
||||
if (!data) return;
|
||||
|
||||
/* actual way to reset internal flags */
|
||||
TCompressWaveData_Stop(data->cw);
|
||||
TCompressWaveData_Play(data->cw, 0);
|
||||
|
||||
data->sbuf.filled = 0;
|
||||
data->samples_discard = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void seek_compresswave(compresswave_codec_data* data, int32_t num_sample) {
|
||||
if (!data) return;
|
||||
|
||||
reset_compresswave(data);
|
||||
data->samples_discard += num_sample;
|
||||
}
|
||||
|
||||
void free_compresswave(compresswave_codec_data* data) {
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
TCompressWaveData_Free(data->cw);
|
||||
|
||||
close_streamfile(data->sf);
|
||||
free(data->samples);
|
||||
free(data);
|
||||
}
|
||||
|
||||
STREAMFILE* compresswave_get_streamfile(compresswave_codec_data* data) {
|
||||
if (!data) return NULL;
|
||||
return data->sf;
|
||||
}
|
|
@ -0,0 +1,998 @@
|
|||
#include "compresswave_decoder_lib.h"
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
/* Decodes CWav (CompressWave) audio codec, based on original delphi/pascal source code by Ko-Ta:
|
||||
* - http://kota.dokkoisho.com/
|
||||
* - http://kota.dokkoisho.com/library/CompressWave.zip
|
||||
* - https://web.archive.org/web/20180819144937/http://d.hatena.ne.jp/Ko-Ta/20070318/p1
|
||||
* (no license given)
|
||||
* Apparently found in few Japanese (doujin?) games around 1995-2002, most notably RADIO ZONDE.
|
||||
*
|
||||
* This is mostly a simple re-implementation following original code, basically Pascal-classes-to-plain-C
|
||||
* because why not. Only decoder part is replicated (some writting/loading/etc stuff removed or cleaned up).
|
||||
* Results should be byte-exact (all is int math).
|
||||
* **some parts like internal looping weren't tested (no valid files)
|
||||
*
|
||||
* Codec is basically huffman-coded ADPCM, that includes diff table and huffman tree setup in
|
||||
* CWav header. Described by the author as being "big, heavy and with bad sound quality".
|
||||
* An oddity is that mono files are output as fake stereo (repeats L/R), this is correct and agrees
|
||||
* with PCM totals in header. Output sample rate is always 44100 and files marked as 22050 or mono just
|
||||
* decode slightly differently. Curiously PCM output size in header may be not be multiple of 4, meaning
|
||||
* files that end with garbage half-a-sample (L sample = 0, nothing for R).
|
||||
*/
|
||||
|
||||
|
||||
/* ************************************************************************* */
|
||||
/* common */
|
||||
/* ************************************************************************* */
|
||||
// pascal reader simulated in C
|
||||
typedef struct {
|
||||
STREAMFILE* File;
|
||||
int64_t Position;
|
||||
int64_t Size;
|
||||
} TStream;
|
||||
|
||||
static void TStream_Read_Uint32(TStream* this, uint32_t* value) {
|
||||
uint8_t buf[0x4] = {0};
|
||||
|
||||
read_streamfile(buf, this->Position, sizeof(buf), this->File);
|
||||
this->Position += 0x4;
|
||||
|
||||
*value = get_u32le(buf);
|
||||
}
|
||||
|
||||
|
||||
/* ************************************************************************* */
|
||||
/* HuffLib.pas */
|
||||
/* ************************************************************************* */
|
||||
|
||||
#define CW_TRUE 1
|
||||
#define CW_FALSE 0
|
||||
|
||||
typedef enum { nsEmpty, nsBranch, nsLeaf, nsRoot } TNodeState;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//structure declaration
|
||||
|
||||
//node structure for huffman tree
|
||||
typedef struct {
|
||||
uint8_t Value; //value (0..255)
|
||||
int32_t Weight; //weight value used during huffman tree creation
|
||||
TNodeState State; //state
|
||||
int32_t Link[2]; //bidimensional tree L/R path (-1: unused, >=0: index)
|
||||
} THuffTreeNode;
|
||||
|
||||
//header info for file writting
|
||||
typedef struct {
|
||||
char HedChar[4]; //head info
|
||||
int32_t Version; //Version
|
||||
uint32_t HistGraph[256]; //appearance rate
|
||||
int64_t FileSize; //file size
|
||||
} THuffHedState;
|
||||
|
||||
//for jumping to arbitrary places (^^), various usages
|
||||
typedef struct {
|
||||
uint32_t BitBuf;
|
||||
int32_t BitCount;
|
||||
int64_t StreamPos;
|
||||
uint32_t CipherBuf;
|
||||
} THuffPositionData;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//huffman encoding class
|
||||
//handled values are 0~255 of 1 byte.
|
||||
//takes appearance rate and makes a huffman tree for encoding
|
||||
|
||||
//EXTRA: external lib part, but not really needed so all is static
|
||||
|
||||
typedef struct {
|
||||
//control
|
||||
TStream Buff; // target stream
|
||||
int64_t BeginPos; // encoded area
|
||||
int Mode; // (0=initial, 1=read, 2=write)
|
||||
|
||||
//bit IO
|
||||
int IoCount; // 0..initial state, 1..read, 2..write
|
||||
uint32_t BitBuf; // held buffer
|
||||
int BitCount; // processed bit count
|
||||
#if 0
|
||||
int64_t BitWriteLen; // written size
|
||||
#endif
|
||||
uint32_t CipherBuf;
|
||||
|
||||
//huffman
|
||||
THuffTreeNode Node[512]; //tree structure
|
||||
uint8_t Code[256][256]; //fork support
|
||||
int32_t Root; //root
|
||||
|
||||
//huffman cipher bits
|
||||
uint32_t CipherList[16];
|
||||
|
||||
//header info
|
||||
THuffHedState Hed;
|
||||
} THuff;
|
||||
|
||||
|
||||
//related to huffman encoding
|
||||
static void THuff_InitHuffTree(THuff* this); //initializes tree
|
||||
static int THuff_InsertHuffNode(THuff* this, int v, int w, TNodeState s, int b1, int b2); //add node to tree
|
||||
static void THuff_MakeHuffTree(THuff* this);
|
||||
|
||||
//related to single bit IO
|
||||
static void THuff_BeginBitIO(THuff* this);
|
||||
static void THuff_EndBitIO(THuff* this);
|
||||
static int THuff_ReadBit(THuff* this);
|
||||
static uint32_t THuff__ROR(uint32_t src, uint32_t shift);
|
||||
|
||||
static THuff* THuff_Create(TStream* buf); // creation
|
||||
static void THuff_Free(THuff* this); // release the power
|
||||
static void THuff_SetCipherCode(THuff* this, uint32_t msk); // encryption mask bits
|
||||
//functions for reading
|
||||
static void THuff_BeginRead(THuff* this);
|
||||
static int THuff_Read(THuff* this);
|
||||
|
||||
#if 0
|
||||
static int64_t THuff_GetFileSize(THuff* this); // get file size before encoding
|
||||
static int THuff_GetEOF(THuff* this); // EOF detection
|
||||
#endif
|
||||
static void THuff_MoveBeginPosition(THuff* this); // return to initial state
|
||||
static void THuff_GetPositionData(THuff* this, THuffPositionData* s); // secret
|
||||
static void THuff_SetPositionData(THuff* this, THuffPositionData* s);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//create
|
||||
static THuff* THuff_Create(TStream* buf) {
|
||||
THuff* this = malloc(sizeof(THuff));
|
||||
if (!this) return NULL;
|
||||
|
||||
//define stream
|
||||
this->Buff = *buf;
|
||||
|
||||
//initialization
|
||||
THuff_InitHuffTree(this);
|
||||
memcpy(this->Hed.HedChar, "HUF\0", 0x4);
|
||||
this->Hed.Version = 1;
|
||||
this->Hed.FileSize = 0;
|
||||
|
||||
//set cipher bits
|
||||
this->CipherBuf = 0;
|
||||
THuff_SetCipherCode(this, 0x0);
|
||||
|
||||
//mode
|
||||
this->Mode = 0;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//free
|
||||
static void THuff_Free(THuff* this) {
|
||||
if (this == NULL) return;
|
||||
if (this->Mode == 2)
|
||||
THuff_EndBitIO(this);
|
||||
free(this);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//init tree structure (unused state)
|
||||
static void THuff_InitHuffTree(THuff* this) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 512; i++) {
|
||||
this->Node[i].State = nsEmpty;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//add node to huffman tree
|
||||
static int THuff_InsertHuffNode(THuff* this, int v, int w, TNodeState s, int b1, int b2) {
|
||||
int result = 0;
|
||||
int i;
|
||||
|
||||
i = 0;
|
||||
while ((this->Node[i].State != nsEmpty) && (i < 512)) {
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i == 512) {
|
||||
result = -1;
|
||||
return result; //exit;
|
||||
}
|
||||
|
||||
this->Node[i].Value = v & 0xFF; //BYTE(v);
|
||||
this->Node[i].Weight = w;
|
||||
this->Node[i].State = s;
|
||||
this->Node[i].Link[0] = b1;
|
||||
if (this->Node[i].Link[0] > 511) {
|
||||
return -1;//? //halt;
|
||||
}
|
||||
this->Node[i].Link[1] = b2;
|
||||
if (this->Node[i].Link[1] > 511) {
|
||||
return -1;//? //halt;
|
||||
}
|
||||
//return entry number
|
||||
result = i;
|
||||
return result;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//reads and expands huffman-encoded data
|
||||
static int THuff_Read(THuff* this) {
|
||||
int i;
|
||||
|
||||
i = this->Root;
|
||||
while (this->Node[i].State != nsLeaf) {
|
||||
i = this->Node[i].Link[THuff_ReadBit(this)];
|
||||
}
|
||||
|
||||
return this->Node[i].Value;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//creates fork code from tree
|
||||
|
||||
//finds node of lowest weight
|
||||
static int THuff_MakeHuffTree_SerchMinNode(THuff* this, int* tNode) {
|
||||
int ii, aaa1, aaa2;
|
||||
|
||||
aaa1 = 0xFFFFFFF;
|
||||
aaa2 = 0;
|
||||
for (ii = 0 ; ii < 256; ii++) {
|
||||
if (tNode[ii] != -1) {
|
||||
if (this->Node[tNode[ii]].Weight < aaa1) {
|
||||
aaa2 = ii;
|
||||
aaa1 = this->Node[tNode[ii]].Weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
return aaa2;
|
||||
}
|
||||
|
||||
//finds closest node
|
||||
static int THuff_MakeHuffTree_SerchNearNode(THuff* this, int* tNode, int pos) {
|
||||
int ii, aaa1, aaa2;
|
||||
|
||||
aaa1 = 0xFFFFFFF;
|
||||
aaa2 = 0;
|
||||
for (ii = 0 ; ii < 256; ii++) {
|
||||
if (tNode[ii] != -1) {
|
||||
if ((abs(this->Node[tNode[ii]].Weight - this->Node[tNode[pos]].Weight) < aaa1) && (pos != ii)) {
|
||||
aaa2 = ii;
|
||||
aaa1 = this->Node[tNode[ii]].Weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
return aaa2;
|
||||
}
|
||||
|
||||
static void THuff_MakeHuffTree_MakeHuffCodeFromTree(THuff* this, uint8_t* tCode1, int* tCodePos, int pos) {
|
||||
int ii, aaa1;
|
||||
|
||||
if (this->Node[pos].State == nsLeaf) { //found
|
||||
tCode1[*tCodePos] = 0xFF;
|
||||
aaa1 = this->Node[pos].Value;
|
||||
for (ii = 0; ii < 256; ii++) {
|
||||
this->Code[aaa1][ii] = tCode1[ii];
|
||||
}
|
||||
}
|
||||
else { //not
|
||||
if (this->Node[pos].Link[0] != -1) {
|
||||
tCode1[*tCodePos] = 0;
|
||||
(*tCodePos)++;
|
||||
THuff_MakeHuffTree_MakeHuffCodeFromTree(this, tCode1, tCodePos, this->Node[pos].Link[0]);
|
||||
}
|
||||
|
||||
if (this->Node[pos].Link[1] != -1) {
|
||||
tCode1[*tCodePos] = 1;
|
||||
(*tCodePos)++;
|
||||
THuff_MakeHuffTree_MakeHuffCodeFromTree(this, tCode1, tCodePos, this->Node[pos].Link[1]);
|
||||
}
|
||||
}
|
||||
|
||||
(*tCodePos)--;
|
||||
}
|
||||
|
||||
// creates huffman tree/codes from apparance rate (0..255)
|
||||
static void THuff_MakeHuffTree(THuff* this) {
|
||||
int i, aa1, aa2, aa3;
|
||||
int tCodePos;
|
||||
uint8_t tCode1[257];
|
||||
#if 0
|
||||
uint8_t tCode2[257];
|
||||
#endif
|
||||
int tNode[257];
|
||||
|
||||
//initializes huffman tree
|
||||
THuff_InitHuffTree(this);
|
||||
for (i = 0; i < 256; i++) {
|
||||
tNode[i] = -1;
|
||||
tCode1[i] = 0;
|
||||
#if 0
|
||||
tCode2[i] = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
//adds child nodes + comparison target nodes
|
||||
for (i = 0; i < 256; i++) {
|
||||
tNode[i] = THuff_InsertHuffNode(this, i, this->Hed.HistGraph[i], nsLeaf, -1, -1);
|
||||
}
|
||||
|
||||
//creates optimal tree
|
||||
for (i = 0; i < 256 - 1; i++) {
|
||||
//find smallest node
|
||||
aa1 = THuff_MakeHuffTree_SerchMinNode(this, tNode);
|
||||
//find value closest to smallest node
|
||||
aa2 = THuff_MakeHuffTree_SerchNearNode(this, tNode, aa1);
|
||||
//make new node joining both together
|
||||
aa3 = THuff_InsertHuffNode(this, -1, this->Node[tNode[aa1]].Weight + this->Node[tNode[aa2]].Weight, nsBranch, tNode[aa1], tNode[aa2]);
|
||||
//remove aa1/2 from comparison target nodes.
|
||||
tNode[aa1] = -1;
|
||||
tNode[aa2] = -1;
|
||||
//add created node to comparison target nodes
|
||||
tNode[aa1] = aa3;
|
||||
}
|
||||
|
||||
//finally make added node top of the tree
|
||||
this->Root = aa3;
|
||||
|
||||
//create stack for data expansion from tree info
|
||||
tCodePos = 0;
|
||||
THuff_MakeHuffTree_MakeHuffCodeFromTree(this, tCode1, &tCodePos, this->Root);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//bit IO start process
|
||||
static void THuff_BeginBitIO(THuff* this) {
|
||||
this->IoCount = 0;
|
||||
this->BitBuf = 0;
|
||||
this->BitCount = 32;
|
||||
#if 0
|
||||
this->BitWriteLen = 0;
|
||||
#endif
|
||||
this->CipherBuf = 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//bit IO end process
|
||||
static void THuff_EndBitIO(THuff* this) {
|
||||
#if 0
|
||||
if (this->IoCount == 2 && this->BitCount > 0) {
|
||||
this->BitBuf = this->BitBuf ^ this->CipherBuf;
|
||||
TStream_Write(this->Buff, BitBuf,4);
|
||||
}
|
||||
#endif
|
||||
THuff_BeginBitIO(this);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//read 1 bit from file
|
||||
static int THuff_ReadBit(THuff* this) {
|
||||
int result;
|
||||
uint32_t aaa;
|
||||
|
||||
if (this->BitCount == 32) {
|
||||
this->IoCount = 1; //ReadMode
|
||||
if (this->Buff.Position < this->Buff.Size) {
|
||||
//read
|
||||
TStream_Read_Uint32(&this->Buff, &aaa); //Buff.Read(aaa,sizeof(DWORD));
|
||||
this->BitBuf = aaa ^ this->CipherBuf;
|
||||
|
||||
//decryption phase
|
||||
this->CipherBuf = THuff__ROR(this->CipherBuf, aaa & 7);
|
||||
this->CipherBuf = this->CipherBuf ^ this->CipherList[aaa & 7];
|
||||
}
|
||||
this->BitCount = 0;
|
||||
}
|
||||
|
||||
//return 1 bit
|
||||
result = this->BitBuf & 1;
|
||||
this->BitBuf = this->BitBuf >> 1;
|
||||
|
||||
//advance BitCount
|
||||
this->BitCount++;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//starts reading encoded data from stream
|
||||
|
||||
static void TStream_Read_THuffHedState(TStream* this, THuffHedState* Hed) {
|
||||
uint8_t buf[0x410];
|
||||
int i;
|
||||
|
||||
read_streamfile(buf, this->Position, sizeof(buf), this->File);
|
||||
this->Position += sizeof(buf);
|
||||
|
||||
/* 0x00: string size (always 3) */
|
||||
memcpy(Hed->HedChar, buf+0x01, 0x03);
|
||||
Hed->Version = get_u32le(buf+0x04);
|
||||
for (i = 0; i < 256; i++) {
|
||||
Hed->HistGraph[i] = get_u32le(buf+0x08 + i*0x04);
|
||||
}
|
||||
Hed->FileSize = get_u64le(buf+0x408); /* seems always 0 */
|
||||
}
|
||||
|
||||
static void THuff_BeginRead(THuff* this) {
|
||||
TStream_Read_THuffHedState(&this->Buff, &this->Hed); //Buff.Read(Hed,sizeof(THuffHedState));
|
||||
THuff_MakeHuffTree(this);
|
||||
this->BeginPos = this->Buff.Position;
|
||||
THuff_BeginBitIO(this);
|
||||
this->Mode = 1;
|
||||
}
|
||||
|
||||
#if 0
|
||||
//------------------------------------------------------------------------------
|
||||
//get file size before encoding
|
||||
static int64_t THuff_GetFileSize(THuff* this) {
|
||||
return this->Hed.FileSize;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//EOF detection
|
||||
static int THuff_GetEOF(THuff* this) {
|
||||
if (this->Buff.Position < this->Buff.Size)
|
||||
return CW_FALSE;
|
||||
else
|
||||
return CW_TRUE;
|
||||
}
|
||||
#endif
|
||||
//------------------------------------------------------------------------------
|
||||
//return to initial positon
|
||||
static void THuff_MoveBeginPosition(THuff* this) {
|
||||
THuff_EndBitIO(this);
|
||||
this->Buff.Position = this->BeginPos;
|
||||
THuff_BeginBitIO(this);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static void THuff_GetPositionData(THuff* this, THuffPositionData* s) {
|
||||
s->BitBuf = this->BitBuf;
|
||||
s->BitCount = this->BitCount;
|
||||
s->StreamPos = this->Buff.Position;
|
||||
s->CipherBuf = this->CipherBuf;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static void THuff_SetPositionData(THuff* this, THuffPositionData* s) {
|
||||
this->BitBuf = s->BitBuf;
|
||||
this->BitCount = s->BitCount;
|
||||
this->Buff.Position = s->StreamPos;
|
||||
this->CipherBuf = s->CipherBuf;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static void THuff_SetCipherCode(THuff* this, uint32_t msk) {
|
||||
//creates mask list
|
||||
this->CipherList[0] = msk / 3;
|
||||
this->CipherList[1] = msk / 17;
|
||||
this->CipherList[2] = msk / 7;
|
||||
this->CipherList[3] = msk / 5;
|
||||
this->CipherList[4] = msk / 3;
|
||||
this->CipherList[5] = msk / 11;
|
||||
this->CipherList[6] = msk / 13;
|
||||
this->CipherList[7] = msk / 19;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static uint32_t THuff__ROR(uint32_t src, uint32_t shift) {
|
||||
uint8_t num = shift % 0xFF;
|
||||
return ((uint32_t)src >> num) | ((uint32_t)src << (32 - num));
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// CompressWaveLib.pas
|
||||
//-------------------------------------------------------------------
|
||||
|
||||
#define PW_MAXVOLUME 0xFFFFFFF //don't change
|
||||
|
||||
|
||||
//proprietary compression file header
|
||||
typedef struct {
|
||||
//RIFF chunk
|
||||
char HedChar[8]; // 'CmpWave'
|
||||
uint32_t Channel; // 2(STEREO) / 1(MONO)
|
||||
uint32_t Sample; // 44100Hz / 22050Hz
|
||||
uint32_t Bit; // 16bit
|
||||
int32_t Tbl[256]; // conversion table value
|
||||
int64_t UnPressSize; // decompressed data size
|
||||
int64_t LoopStart; // loop start/end position
|
||||
int64_t LoopEnd;
|
||||
uint8_t LoopCount; // loop times
|
||||
char MusicTitle[128*2]; // song name
|
||||
char MusicArtist[128*2]; // composer
|
||||
} PRESSWAVEDATAHED;
|
||||
|
||||
//for writting
|
||||
typedef struct {
|
||||
short RBuf;
|
||||
short LBuf;
|
||||
} TLRWRITEBUFFER;
|
||||
|
||||
|
||||
//compression data class
|
||||
struct TCompressWaveData {
|
||||
//rendering flag (sets during rendering)
|
||||
int NowRendering;
|
||||
//flag for playback
|
||||
int32_t Faa1;
|
||||
int32_t Faa2;
|
||||
int32_t Fvv1;
|
||||
int32_t Fvv2;
|
||||
|
||||
int32_t FVolume;
|
||||
int32_t Ffade;
|
||||
int32_t FSetVolume;
|
||||
|
||||
int FEndLoop;
|
||||
int32_t FLoop;
|
||||
int FPlay;
|
||||
int64_t FWavePosition;
|
||||
int64_t FWaveLength;
|
||||
//flag for 22050kHz
|
||||
int32_t LBackBuf;
|
||||
int32_t RBackBuf;
|
||||
//for restoration
|
||||
THuffPositionData PosData;
|
||||
int32_t LPFaa1;
|
||||
int32_t LPFaa2;
|
||||
int32_t LPFvv1;
|
||||
int32_t LPFvv2;
|
||||
//cipher code
|
||||
uint32_t CipherCode;
|
||||
//hafu-hafu-hafuman
|
||||
THuff* RH;
|
||||
|
||||
#if 0
|
||||
TMemoryStream Data;
|
||||
#endif
|
||||
PRESSWAVEDATAHED Hed;
|
||||
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//create
|
||||
TCompressWaveData* TCompressWaveData_Create() {
|
||||
TCompressWaveData* this = malloc(sizeof(TCompressWaveData));
|
||||
if (!this) return NULL;
|
||||
#if 0
|
||||
this->Data = NULL;
|
||||
#endif
|
||||
this->RH = NULL;
|
||||
this->FWavePosition = 0;
|
||||
this->FWaveLength = 0;
|
||||
this->FVolume = PW_MAXVOLUME;
|
||||
this->FSetVolume = PW_MAXVOLUME;
|
||||
this->Ffade = 0;
|
||||
this->FEndLoop = CW_FALSE;
|
||||
this->FPlay = CW_FALSE;
|
||||
this->NowRendering = CW_FALSE;
|
||||
TCompressWaveData_SetCipherCode(this, 0);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//free
|
||||
void TCompressWaveData_Free(TCompressWaveData* this) {
|
||||
if (!this)
|
||||
return;
|
||||
|
||||
//EXTRA: presumably for threading but OG lib doesn't properly set this to false on all errors
|
||||
#if 0
|
||||
//sync
|
||||
while (this->NowRendering) {
|
||||
;
|
||||
}
|
||||
#endif
|
||||
//free
|
||||
if (this->RH != NULL)
|
||||
THuff_Free(this->RH);
|
||||
#if 0
|
||||
if (this->Data != NULL)
|
||||
TMemoryStream_Free(this->Data);
|
||||
#endif
|
||||
free(this);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//outpus 44100/16bit/stereo waveform to designed buffer
|
||||
|
||||
void TCompressWaveData_Rendering_ReadPress(TCompressWaveData* this, int32_t* RFlg, int32_t* LFlg) {
|
||||
if (this->Hed.Channel == 2) {
|
||||
*RFlg = THuff_Read(this->RH); //STEREO
|
||||
*LFlg = THuff_Read(this->RH);
|
||||
}
|
||||
else {
|
||||
*RFlg = THuff_Read(this->RH); //MONO
|
||||
*LFlg = *RFlg;
|
||||
}
|
||||
}
|
||||
|
||||
void TCompressWaveData_Rendering_WriteWave(TCompressWaveData* this, int16_t** buf1, int32_t RVol, int32_t LVol) {
|
||||
TLRWRITEBUFFER bbb = {0};
|
||||
|
||||
if (this->Hed.Sample == 44100) { //44100 STEREO/MONO
|
||||
bbb.RBuf = RVol;
|
||||
bbb.LBuf = LVol;
|
||||
(*buf1)[0] = bbb.RBuf;
|
||||
(*buf1)[1] = bbb.LBuf;
|
||||
(*buf1) += 2;
|
||||
}
|
||||
if (this->Hed.Sample == 22050) { //22050 STEREO/MONO
|
||||
bbb.RBuf = (this->RBackBuf + RVol) / 2;
|
||||
bbb.LBuf = (this->LBackBuf + LVol) / 2;
|
||||
(*buf1)[0] = bbb.RBuf;
|
||||
(*buf1)[1] = bbb.LBuf;
|
||||
(*buf1) += 2;
|
||||
|
||||
bbb.RBuf = RVol;
|
||||
bbb.LBuf = LVol;
|
||||
(*buf1)[0] = bbb.RBuf;
|
||||
(*buf1)[1] = bbb.LBuf;
|
||||
(*buf1) += 2;
|
||||
|
||||
this->RBackBuf = RVol;
|
||||
this->LBackBuf = LVol;
|
||||
}
|
||||
}
|
||||
|
||||
int TCompressWaveData_Rendering(TCompressWaveData* this, int16_t* buf, uint32_t Len) {
|
||||
int result;
|
||||
int32_t RFlg, LFlg, RVol, LVol;
|
||||
int i, aaa;
|
||||
int16_t* buf1;
|
||||
int32_t PressLength, WaveStep;
|
||||
|
||||
|
||||
this->NowRendering = CW_TRUE;
|
||||
result = CW_FALSE;
|
||||
#if 0
|
||||
if (this->Data == NULL) {
|
||||
this->NowRendering = CW_FALSE;
|
||||
return result; //exit;
|
||||
}
|
||||
#endif
|
||||
|
||||
//fadeout song stop
|
||||
if ((this->FVolume < 1) && (this->FSetVolume < 1)) {
|
||||
this->FPlay = CW_FALSE;
|
||||
}
|
||||
//if (abs(this->FSetVolume - this->FVolume) < this->Ffade) {
|
||||
// this->FPlay = CW_FALSE;
|
||||
//}
|
||||
|
||||
//stop if FPlay (play flag) wasn't set
|
||||
if (this->FPlay == CW_FALSE) {
|
||||
this->NowRendering = CW_FALSE;
|
||||
return result; //exit;
|
||||
}
|
||||
|
||||
//pre processing
|
||||
RVol = this->Fvv1;
|
||||
LVol = this->Fvv2;
|
||||
if (this->Hed.Sample == 44100)
|
||||
WaveStep = 4;
|
||||
else
|
||||
WaveStep = 8;
|
||||
|
||||
PressLength = (int32_t)Len / WaveStep;
|
||||
|
||||
//expansion processing
|
||||
buf1 = buf;
|
||||
for (i = 0; i < PressLength; i++) {
|
||||
|
||||
//crossed over?
|
||||
if (this->FWavePosition > this->FWaveLength) {
|
||||
if (this->FEndLoop == CW_TRUE) { //playback with loop?
|
||||
TCompressWaveData_Previous(this);
|
||||
}
|
||||
else { //in case of playback without loop
|
||||
this->FPlay = CW_FALSE;
|
||||
return result; //exit
|
||||
}
|
||||
}
|
||||
|
||||
//loop related
|
||||
if (this->Hed.LoopCount > this->FLoop) {
|
||||
//if position is loop start, hold current flag/state
|
||||
//shr 3 matches 8 bit aligment
|
||||
if ((this->Hed.LoopStart >> 3) == (this->FWavePosition >> 3)) {
|
||||
TCompressWaveData_GetLoopState(this);
|
||||
}
|
||||
//if reached loop end do loop.
|
||||
if ((this->Hed.LoopEnd >> 3) == (this->FWavePosition >> 3)) {
|
||||
if (this->Hed.LoopCount != 255)
|
||||
this->FLoop++;
|
||||
TCompressWaveData_SetLoopState(this);
|
||||
}
|
||||
}
|
||||
|
||||
//read
|
||||
TCompressWaveData_Rendering_ReadPress(this, &RFlg, &LFlg);
|
||||
this->Faa1 = this->Faa1 + this->Hed.Tbl[RFlg];
|
||||
this->Faa2 = this->Faa2 + this->Hed.Tbl[LFlg];
|
||||
this->Fvv1 = this->Fvv1 + this->Faa1;
|
||||
this->Fvv2 = this->Fvv2 + this->Faa2;
|
||||
|
||||
//volume adjustment
|
||||
aaa = this->FSetVolume - this->FVolume;
|
||||
if (abs(aaa) < this->Ffade) {
|
||||
this->FVolume = this->FSetVolume;
|
||||
}
|
||||
else {
|
||||
if (aaa > 0)
|
||||
this->FVolume = this->FVolume + this->Ffade;
|
||||
else
|
||||
this->FVolume = this->FVolume - this->Ffade;
|
||||
}
|
||||
|
||||
//threshold calcs (due to overflow)
|
||||
if (this->Fvv1 > +32760) {
|
||||
this->Fvv1 = +32760;
|
||||
this->Faa1 = 0;
|
||||
}
|
||||
if (this->Fvv1 < -32760) {
|
||||
this->Fvv1 = -32760;
|
||||
this->Faa1 = 0;
|
||||
}
|
||||
if (this->Fvv2 > +32760) {
|
||||
this->Fvv2 = +32760;
|
||||
this->Faa2 = 0;
|
||||
}
|
||||
if (this->Fvv2 < -32760) {
|
||||
this->Fvv2 = -32760;
|
||||
this->Faa2 = 0;
|
||||
}
|
||||
|
||||
aaa = (this->FVolume >> 20);
|
||||
RVol = this->Fvv1 * aaa / 256;
|
||||
LVol = this->Fvv2 * aaa / 256;
|
||||
|
||||
//expand to buffer
|
||||
TCompressWaveData_Rendering_WriteWave(this, &buf1, RVol, LVol);
|
||||
//advance playback position
|
||||
this->FWavePosition += WaveStep;
|
||||
}
|
||||
|
||||
//remainder calcs
|
||||
//depending on buffer lenght remainder may happen
|
||||
//example: 44100 / 4 = 11025...OK 44100 / 8 = 5512.5...NG
|
||||
// in that case appear as noise
|
||||
if (Len % 8 == 4) {
|
||||
TCompressWaveData_Rendering_WriteWave(this, &buf1, RVol, LVol);
|
||||
}
|
||||
|
||||
this->NowRendering = CW_FALSE;
|
||||
result = CW_TRUE;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//read compressed file from stream
|
||||
|
||||
static void TStream_Read_PRESSWAVEDATAHED(TStream* this, PRESSWAVEDATAHED* Hed) {
|
||||
uint8_t buf[0x538];
|
||||
int i, len;
|
||||
|
||||
read_streamfile(buf, this->Position, sizeof(buf), this->File);
|
||||
this->Position += sizeof(buf);
|
||||
|
||||
memcpy(Hed->HedChar, buf + 0x00, 8);
|
||||
Hed->Channel = get_u32le(buf + 0x08);
|
||||
Hed->Sample = get_u32le(buf + 0x0c);
|
||||
Hed->Bit = get_u32le(buf + 0x10);
|
||||
for (i = 0; i < 256; i++) {
|
||||
Hed->Tbl[i] = get_s32le(buf + 0x14 + i * 0x04);
|
||||
}
|
||||
Hed->UnPressSize = get_u64le(buf + 0x418);
|
||||
Hed->LoopStart = get_u64le(buf + 0x420);
|
||||
Hed->LoopEnd = get_u64le(buf + 0x428);
|
||||
Hed->LoopCount = get_u8 (buf + 0x430);
|
||||
|
||||
len = get_u8 (buf + 0x431);
|
||||
memcpy(Hed->MusicTitle, buf + 0x432, len);
|
||||
len = get_u8 (buf + 0x4B1);
|
||||
memcpy(Hed->MusicArtist, buf + 0x4B2, len);
|
||||
|
||||
/* 0x538: huffman table */
|
||||
/* 0x948: data start */
|
||||
}
|
||||
|
||||
int TCompressWaveData_LoadFromStream(TCompressWaveData* this, STREAMFILE* ss) {
|
||||
int result = CW_FALSE;
|
||||
TStream data = {0};
|
||||
|
||||
if (ss == NULL)
|
||||
return result;
|
||||
#if 0
|
||||
if (this->Data != NULL)
|
||||
TMemoryStream_Free(this->Data);
|
||||
#endif
|
||||
|
||||
data.File = ss; //data = TMemoryStream.Create;
|
||||
data.Size = get_streamfile_size(ss); //data.SetSize(ss.Size);
|
||||
//data.CopyFrom(ss,0);
|
||||
|
||||
//get header info
|
||||
data.Position = 0;
|
||||
|
||||
TStream_Read_PRESSWAVEDATAHED(&data, &this->Hed); //data.Read(Hed,sizeof(PRESSWAVEDATAHED));
|
||||
this->FWaveLength = this->Hed.UnPressSize;
|
||||
if (this->RH != NULL)
|
||||
THuff_Free(this->RH);
|
||||
this->RH = THuff_Create(&data);
|
||||
if (!this->RH) return result;
|
||||
|
||||
THuff_SetCipherCode(this->RH, 0x00);
|
||||
THuff_BeginRead(this->RH);
|
||||
|
||||
//initialize playback flag
|
||||
TCompressWaveData_Stop(this);
|
||||
TCompressWaveData_SetVolume(this, 1.0, 0.0);
|
||||
result = CW_TRUE;
|
||||
return result;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//temp pause
|
||||
void TCompressWaveData_Pause(TCompressWaveData* this) {
|
||||
this->FPlay = CW_FALSE;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//sets volume
|
||||
void TCompressWaveData_SetVolume(TCompressWaveData* this, float vol, float fade) {
|
||||
float aaa;
|
||||
|
||||
//EXTRA: C float seemingly can't store PW_MAXVOLUME (268435455 becomes 268435456.0), so must cast to double
|
||||
// to get proper results. Otherwise volume gets slightly different vs original (no casting needed there).
|
||||
// vol=1.0, fade=0.0 is the same as default params.
|
||||
|
||||
aaa = vol;
|
||||
//set volume threshold
|
||||
if (aaa > 1.0) aaa = 1.0;
|
||||
if (aaa < 0.0) aaa = 0.0;
|
||||
//calc volume increse
|
||||
if (fade < 0.01) { //with fade value
|
||||
this->Ffade = 0;
|
||||
this->FVolume = round(aaa * (double)PW_MAXVOLUME);
|
||||
this->FSetVolume = this->FVolume;
|
||||
}
|
||||
else { //without fade value
|
||||
this->Ffade = round(PW_MAXVOLUME / fade / 44100);
|
||||
this->FSetVolume = round(aaa * PW_MAXVOLUME);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//returns fade value
|
||||
float TCompressWaveData_GetFade(TCompressWaveData* this) {
|
||||
if ((this->Ffade == 0) || (abs(this->FVolume - this->FSetVolume) == 0)) {
|
||||
return 0; //exit;
|
||||
}
|
||||
return (abs(this->FVolume - this->FSetVolume)/44100) / this->Ffade;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//returns volume value
|
||||
float TCompressWaveData_GetVolume(TCompressWaveData* this) {
|
||||
return this->FVolume / PW_MAXVOLUME;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//returns volume after fade
|
||||
float TCompressWaveData_GetSetVolume(TCompressWaveData* this) {
|
||||
return this->FSetVolume / PW_MAXVOLUME;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//returns play time (current position). unit is secs
|
||||
float TCompressWaveData_GetPlayTime(TCompressWaveData* this) {
|
||||
return this->FWavePosition / (44100*4);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//returns song length. unit is secs
|
||||
float TCompressWaveData_GetTotalTime(TCompressWaveData* this) {
|
||||
return this->FWaveLength / (44100*4);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//play stop command. returns song to beginning
|
||||
void TCompressWaveData_Stop(TCompressWaveData* this) {
|
||||
//play flags to initial state
|
||||
this->FWavePosition = 0;
|
||||
this->Fvv1 = 0;
|
||||
this->Faa1 = 0;
|
||||
this->Fvv2 = 0;
|
||||
this->Faa2 = 0;
|
||||
this->LBackBuf = 0;
|
||||
this->RBackBuf = 0;
|
||||
TCompressWaveData_SetVolume(this, 1.0, 0);
|
||||
this->FPlay = CW_FALSE;
|
||||
this->FLoop = 0;
|
||||
#if 0
|
||||
if (this->Data == NULL)
|
||||
return;
|
||||
#endif
|
||||
|
||||
//EXTRA: presumably for threading but OG lib doesn't properly set this to false on all errors
|
||||
#if 0
|
||||
//sync
|
||||
while (this->NowRendering) {
|
||||
;
|
||||
}
|
||||
#endif
|
||||
|
||||
//return to initial location
|
||||
THuff_MoveBeginPosition(this->RH);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//returns song to beginning. difference vs STOP is that fade isn't initialized
|
||||
void TCompressWaveData_Previous(TCompressWaveData* this) {
|
||||
//play flags to initial state
|
||||
this->FWavePosition = 0;
|
||||
this->Fvv1 = 0;
|
||||
this->Faa1 = 0;
|
||||
this->Fvv2 = 0;
|
||||
this->Faa2 = 0;
|
||||
this->LBackBuf = 0;
|
||||
this->RBackBuf = 0;
|
||||
this->FLoop = 0;
|
||||
|
||||
#if 0
|
||||
if (this->Data == NULL)
|
||||
return;
|
||||
#endif
|
||||
//return to initial location
|
||||
THuff_MoveBeginPosition(this->RH);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------
|
||||
//starts song playback
|
||||
void TCompressWaveData_Play(TCompressWaveData* this, int loop) {
|
||||
this->FPlay = CW_TRUE;
|
||||
this->FEndLoop = loop;
|
||||
if ((this->FVolume == 0) && (this->FSetVolume == 0))
|
||||
TCompressWaveData_SetVolume(this, 1.0,0);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------
|
||||
//set parameters for looping
|
||||
//--------------------------------------------------------------
|
||||
//record encoded file position
|
||||
//since it uses huffman needs to held those flags too
|
||||
void TCompressWaveData_GetLoopState(TCompressWaveData* this) {
|
||||
this->LPFaa1 = this->Faa1;
|
||||
this->LPFaa2 = this->Faa2;
|
||||
this->LPFvv1 = this->Fvv1;
|
||||
this->LPFvv2 = this->Fvv2;
|
||||
THuff_GetPositionData(this->RH, &this->PosData);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------
|
||||
//return to recorded encoded file position
|
||||
void TCompressWaveData_SetLoopState(TCompressWaveData* this) {
|
||||
this->Faa1 = this->LPFaa1;
|
||||
this->Faa2 = this->LPFaa2;
|
||||
this->Fvv1 = this->LPFvv1;
|
||||
this->Fvv2 = this->LPFvv2;
|
||||
THuff_SetPositionData(this->RH, &this->PosData);
|
||||
this->FWavePosition = this->Hed.LoopStart;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//sets cipher code
|
||||
void TCompressWaveData_SetCipherCode(TCompressWaveData* this, uint32_t Num) {
|
||||
this->CipherCode = Num;
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
#ifndef _COMPRESSWAVE_DECODER_LIB_H
|
||||
#define _COMPRESSWAVE_DECODER_LIB_H
|
||||
#include "../streamfile.h"
|
||||
|
||||
typedef struct TCompressWaveData TCompressWaveData;
|
||||
|
||||
void TCompressWaveData_GetLoopState(TCompressWaveData* this);
|
||||
void TCompressWaveData_SetLoopState(TCompressWaveData* this);
|
||||
|
||||
TCompressWaveData* TCompressWaveData_Create();
|
||||
void TCompressWaveData_Free(TCompressWaveData* this);
|
||||
int TCompressWaveData_Rendering(TCompressWaveData* this, int16_t* buf, uint32_t Len);
|
||||
int TCompressWaveData_LoadFromStream(TCompressWaveData* this, STREAMFILE* ss);
|
||||
void TCompressWaveData_SetCipherCode(TCompressWaveData* this, uint32_t Num);
|
||||
|
||||
void TCompressWaveData_Play(TCompressWaveData* this, int loop);
|
||||
void TCompressWaveData_Stop(TCompressWaveData* this);
|
||||
void TCompressWaveData_Previous(TCompressWaveData* this);
|
||||
void TCompressWaveData_Pause(TCompressWaveData* this);
|
||||
void TCompressWaveData_SetVolume(TCompressWaveData* this, float vol, float fade);
|
||||
float TCompressWaveData_GetVolume(TCompressWaveData* this);
|
||||
float TCompressWaveData_GetSetVolume(TCompressWaveData* this);
|
||||
float TCompressWaveData_GetFade(TCompressWaveData* this);
|
||||
float TCompressWaveData_GetPlayTime(TCompressWaveData* this);
|
||||
float TCompressWaveData_GetTotalTime(TCompressWaveData* this);
|
||||
|
||||
#endif /*_COMPRESSWAVE_DECODER_LIB_H */
|
|
@ -67,7 +67,7 @@ fail:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
void decode_ea_mt(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t samples_to_do, int channel) {
|
||||
void decode_ea_mt(VGMSTREAM* vgmstream, sample_t* outbuf, int channelspacing, int32_t samples_to_do, int channel) {
|
||||
int i;
|
||||
ea_mt_codec_data* data = vgmstream->codec_data;
|
||||
ea_mt_codec_data* ch_data = &data[channel];
|
||||
|
@ -202,5 +202,4 @@ static size_t ea_mt_read_callback(void *dest, int size, void *arg) {
|
|||
ch_data->offset += bytes_read;
|
||||
|
||||
return bytes_read;
|
||||
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ static const int EA_XA_TABLE[20] = {
|
|||
};
|
||||
|
||||
/* EA XA v2 (always mono); like v1 but with "PCM samples" flag and doesn't add 128 on expand or clamp (pre-adjusted by the encoder?) */
|
||||
void decode_ea_xa_v2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
||||
void decode_ea_xa_v2(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
||||
uint8_t frame_info;
|
||||
int32_t coef1, coef2;
|
||||
int i, sample_count, shift;
|
||||
|
@ -87,7 +87,7 @@ void decode_ea_xa_v2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspac
|
|||
/* later PC games use float math, though in the end sounds basically the same (decompiled from various exes) */
|
||||
static const double XA_K0[16] = { 0.0, 0.9375, 1.796875, 1.53125 };
|
||||
static const double XA_K1[16] = { 0.0, 0.0, -0.8125, -0.859375 };
|
||||
/* code uses look-up table but it's be equivalent to:
|
||||
/* code uses look-up table but it's equivalent to:
|
||||
* (double)((nibble << 28) >> (shift + 8) >> 8) or (double)(signed_nibble << (12 - shift)) */
|
||||
static const uint32_t FLOAT_TABLE_INT[256] = {
|
||||
0x00000000,0x45800000,0x46000000,0x46400000,0x46800000,0x46A00000,0x46C00000,0x46E00000,
|
||||
|
@ -125,7 +125,7 @@ static const uint32_t FLOAT_TABLE_INT[256] = {
|
|||
};
|
||||
static const float* FLOAT_TABLE = (const float *)FLOAT_TABLE_INT;
|
||||
|
||||
void decode_ea_xa_v2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
||||
void decode_ea_xa_v2(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
||||
uint8_t frame_info;
|
||||
int i, sample_count, shift;
|
||||
|
||||
|
@ -184,7 +184,7 @@ void decode_ea_xa_v2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspac
|
|||
#endif
|
||||
|
||||
/* EA XA v1 (mono/stereo) */
|
||||
void decode_ea_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo) {
|
||||
void decode_ea_xa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo) {
|
||||
uint8_t frame_info;
|
||||
int32_t coef1, coef2;
|
||||
int i, sample_count, shift;
|
||||
|
@ -235,7 +235,7 @@ void decode_ea_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing
|
|||
}
|
||||
|
||||
/* Maxis EA-XA v1 (mono/stereo) with byte-interleave layout in stereo mode */
|
||||
void decode_maxis_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
||||
void decode_maxis_xa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
||||
uint8_t frame_info;
|
||||
int32_t coef1, coef2;
|
||||
int i, sample_count, shift;
|
||||
|
|
|
@ -122,7 +122,7 @@ static const int mc3_table[4][4][64] = {
|
|||
*
|
||||
* Tables and original algorithm by daemon1
|
||||
*/
|
||||
void decode_mc3(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
||||
void decode_mc3(VGMSTREAM* vgmstream, VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
||||
int i, sample_count = 0;
|
||||
|
||||
int32_t hist = stream->adpcm_history1_32;
|
||||
|
|
|
@ -0,0 +1,188 @@
|
|||
#include "coding.h"
|
||||
#include "coding_utils_samples.h"
|
||||
|
||||
#ifdef VGM_USE_SPEEX
|
||||
#include "speex/speex.h"
|
||||
|
||||
#define SPEEX_MAX_FRAME_SIZE 0x100 /* frame sizes are stored in a byte */
|
||||
#define SPEEX_MAX_FRAME_SAMPLES 640 /* nb=160, wb/uwb=320*2 */
|
||||
#define SPEEX_CTL_OK 0 /* -1=request unknown, -2=invalid param */
|
||||
#define SPEEX_DECODE_OK 0 /* -1 for end of stream, -2 corrupt stream */
|
||||
|
||||
|
||||
/* opaque struct */
|
||||
struct speex_codec_data {
|
||||
/* config */
|
||||
int channels;
|
||||
int samples_discard;
|
||||
int encoder_delay;
|
||||
|
||||
uint8_t buf[SPEEX_MAX_FRAME_SIZE];
|
||||
uint8_t frame_size;
|
||||
|
||||
int16_t* samples;
|
||||
int frame_samples;
|
||||
|
||||
/* frame state */
|
||||
s16buf_t sbuf;
|
||||
|
||||
void* state;
|
||||
SpeexBits bits;
|
||||
};
|
||||
|
||||
|
||||
/* raw SPEEX */
|
||||
speex_codec_data* init_speex_ea(int channels) {
|
||||
int res, sample_rate;
|
||||
speex_codec_data* data = NULL;
|
||||
|
||||
|
||||
data = calloc(1, sizeof(speex_codec_data));
|
||||
if (!data) goto fail;
|
||||
|
||||
//TODO: EA uses N decoders, unknown layout (known samples are mono)
|
||||
data->channels = channels;
|
||||
if (channels != 1)
|
||||
goto fail;
|
||||
|
||||
/* Modes: narrowband=nb, wideband=wb, ultrawideband=uwb modes.
|
||||
* EASpeex seem to always use uwb so use that for now until config is needed.
|
||||
* Examples normally use &speex_*_mode but export seem problematic? */
|
||||
data->state = speex_decoder_init(speex_lib_get_mode(SPEEX_MODEID_UWB));
|
||||
if (!data->state) goto fail;
|
||||
|
||||
speex_bits_init(&data->bits);
|
||||
|
||||
res = speex_decoder_ctl(data->state, SPEEX_GET_FRAME_SIZE, &data->frame_samples);
|
||||
if (res != SPEEX_CTL_OK) goto fail;
|
||||
|
||||
if (data->frame_samples > SPEEX_MAX_FRAME_SAMPLES)
|
||||
goto fail;
|
||||
|
||||
/* forced in EA's code, doesn't seem to affect decoding (all EAAC headers use this rate too) */
|
||||
sample_rate = 32000;
|
||||
res = speex_decoder_ctl(data->state, SPEEX_SET_SAMPLING_RATE, &sample_rate);
|
||||
if (res != SPEEX_CTL_OK) goto fail;
|
||||
|
||||
/* default "latency" for EASpeex */
|
||||
data->encoder_delay = 509;
|
||||
data->samples_discard = data->encoder_delay;
|
||||
|
||||
data->samples = malloc(channels * data->frame_samples * sizeof(int16_t));
|
||||
if (!data->samples) goto fail;
|
||||
|
||||
return data;
|
||||
|
||||
fail:
|
||||
free_speex(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static int decode_frame(speex_codec_data* data) {
|
||||
int res;
|
||||
|
||||
data->sbuf.samples = data->samples;
|
||||
data->sbuf.channels = 1;
|
||||
data->sbuf.filled = 0;
|
||||
|
||||
speex_bits_read_from(&data->bits, (const char*)data->buf, data->frame_size);
|
||||
|
||||
res = speex_decode_int(data->state, &data->bits, data->sbuf.samples);
|
||||
if (res != SPEEX_DECODE_OK) goto fail;
|
||||
|
||||
data->sbuf.filled = data->frame_samples;
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* for simple style speex (seen in EA-Speex and libspeex's sampledec.c) */
|
||||
static int read_frame(speex_codec_data* data, VGMSTREAMCHANNEL* stream) {
|
||||
uint8_t bytes;
|
||||
|
||||
data->frame_size = read_u8(stream->offset, stream->streamfile);
|
||||
stream->offset += 0x01;
|
||||
if (data->frame_size == 0) goto fail;
|
||||
|
||||
bytes = read_streamfile(data->buf, stream->offset, data->frame_size, stream->streamfile);
|
||||
stream->offset += data->frame_size;
|
||||
if (bytes != data->frame_size) goto fail;
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
void decode_speex(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do) {
|
||||
VGMSTREAMCHANNEL* stream = &vgmstream->ch[0];
|
||||
speex_codec_data* data = vgmstream->codec_data;
|
||||
int ok;
|
||||
|
||||
|
||||
while (samples_to_do > 0) {
|
||||
s16buf_t* sbuf = &data->sbuf;
|
||||
|
||||
if (sbuf->filled <= 0) {
|
||||
ok = read_frame(data, stream);
|
||||
if (!ok) goto fail;
|
||||
|
||||
ok = decode_frame(data);
|
||||
if (!ok) goto fail;
|
||||
}
|
||||
|
||||
if (data->samples_discard)
|
||||
s16buf_discard(&outbuf, sbuf, &data->samples_discard);
|
||||
else
|
||||
s16buf_consume(&outbuf, sbuf, &samples_to_do);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
fail:
|
||||
/* on error just put some 0 samples */
|
||||
VGM_LOG("SPEEX: decode fail at %x, missing %i samples\n", (uint32_t)stream->offset, samples_to_do);
|
||||
s16buf_silence(&outbuf, &samples_to_do, data->channels);
|
||||
}
|
||||
|
||||
|
||||
void reset_speex(speex_codec_data* data) {
|
||||
int res;
|
||||
|
||||
if (!data) return;
|
||||
|
||||
res = speex_decoder_ctl(data->state, SPEEX_RESET_STATE, NULL);
|
||||
if (res != SPEEX_CTL_OK) goto fail;
|
||||
|
||||
data->sbuf.filled = 0;
|
||||
data->samples_discard = data->encoder_delay;
|
||||
|
||||
return;
|
||||
fail:
|
||||
return; /* ? */
|
||||
}
|
||||
|
||||
void seek_speex(VGMSTREAM* vgmstream, int32_t num_sample) {
|
||||
speex_codec_data* data = vgmstream->codec_data;
|
||||
if (!data) return;
|
||||
|
||||
reset_speex(data);
|
||||
data->samples_discard += num_sample;
|
||||
|
||||
/* loop offsets are set during decode; force them to stream start so discard works */
|
||||
if (vgmstream->loop_ch)
|
||||
vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset;
|
||||
}
|
||||
|
||||
void free_speex(speex_codec_data* data) {
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
speex_decoder_destroy(data->state);
|
||||
speex_bits_destroy(&data->bits);
|
||||
|
||||
free(data->samples);
|
||||
free(data);
|
||||
}
|
||||
#endif
|
|
@ -5,7 +5,7 @@
|
|||
* mono or stereo modes, using multiple tables and temp step/delta values.
|
||||
*
|
||||
* Base reverse engineering by Zench: https://bitbucket.org/Zenchreal/decubisnd
|
||||
* Original ASM MMX/intrinsics to C++ by sigsegv; adapted by bnnm; special thanks to Nicknine.
|
||||
* Original 4-bit mode ASM MMX/intrinsics to C++ by sigsegv; adapted + 6-bit mode by bnnm; special thanks to Nicknine.
|
||||
*
|
||||
* Data always starts with a 0x30 main header (some games have extra data before too), then frames of
|
||||
* fixed size: 0x34 ADPCM setup per channel, 1 subframe + 1 padding byte, then another subframe and 1 byte.
|
||||
|
@ -221,7 +221,7 @@ fail:
|
|||
|
||||
/* *********************************************************************** */
|
||||
|
||||
int32_t adpcm6_table1[64] = {
|
||||
static const int32_t adpcm6_table1[64] = {
|
||||
-100000000, -369, -245, -133, -33, 56, 135, 207,
|
||||
275, 338, 395, 448, 499, 548, 593, 635,
|
||||
676, 717, 755, 791, 825, 858, 889, 919,
|
||||
|
@ -233,7 +233,7 @@ int32_t adpcm6_table1[64] = {
|
|||
51200, 56320, 63488, 67704, 75776, 89088, 102400, 0,
|
||||
};
|
||||
|
||||
int32_t adpcm6_table2[64] = {
|
||||
static const int32_t adpcm6_table2[64] = {
|
||||
1800, 1800, 1800, 2048, 3072, 4096, 5000, 5056,
|
||||
5184, 5240, 6144, 6880, 9624, 12880, 14952, 18040,
|
||||
20480, 22920, 25600, 28040, 32560, 35840, 40960, 45832,
|
||||
|
@ -245,19 +245,19 @@ int32_t adpcm6_table2[64] = {
|
|||
4, 5, 5, 5, 6, 6, 6, 7,
|
||||
};
|
||||
|
||||
int32_t adpcm4_table1[16] = {
|
||||
static const int32_t adpcm4_table1[16] = {
|
||||
-100000000, 8, 269, 425, 545, 645, 745, 850,
|
||||
/* probably unused */
|
||||
-1082465976, 1058977874, 1068540887, 1072986849, 1075167887, 1076761723, 1078439444, 1203982336,
|
||||
};
|
||||
|
||||
int32_t adpcm4_table2[16] = {
|
||||
static const int32_t adpcm4_table2[16] = {
|
||||
-1536, 2314, 5243, 8192, 14336, 25354, 45445, 143626,
|
||||
/* probably unused */
|
||||
0, 0, 0, 1, 1, 1, 3, 7,
|
||||
};
|
||||
|
||||
int32_t delta_table[33+33] = {
|
||||
static const int32_t delta_table[33+33] = {
|
||||
1024, 1031, 1053, 1076, 1099, 1123, 1148, 1172,
|
||||
1198, 1224, 1251, 1278, 1306, 1334, 1363, 1393,
|
||||
1423, 1454, 1485, 1518, 1551, 1584, 1619, 1654,
|
||||
|
|
|
@ -40,6 +40,10 @@ void free_codec(VGMSTREAM* vgmstream) {
|
|||
free_imuse(vgmstream->codec_data);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_COMPRESSWAVE) {
|
||||
free_compresswave(vgmstream->codec_data);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_EA_MT) {
|
||||
free_ea_mt(vgmstream->codec_data, vgmstream->channels);
|
||||
}
|
||||
|
@ -96,6 +100,12 @@ void free_codec(VGMSTREAM* vgmstream) {
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_SPEEX
|
||||
if (vgmstream->coding_type == coding_SPEEX) {
|
||||
free_speex(vgmstream->codec_data);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (vgmstream->coding_type == coding_ACM) {
|
||||
free_acm(vgmstream->codec_data);
|
||||
}
|
||||
|
@ -127,6 +137,10 @@ void seek_codec(VGMSTREAM* vgmstream) {
|
|||
seek_imuse(vgmstream->codec_data, vgmstream->loop_current_sample);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_COMPRESSWAVE) {
|
||||
seek_compresswave(vgmstream->codec_data, vgmstream->loop_current_sample);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_EA_MT) {
|
||||
seek_ea_mt(vgmstream, vgmstream->loop_current_sample);
|
||||
}
|
||||
|
@ -171,6 +185,12 @@ void seek_codec(VGMSTREAM* vgmstream) {
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_SPEEX
|
||||
if (vgmstream->coding_type == coding_SPEEX) {
|
||||
seek_speex(vgmstream, vgmstream->loop_current_sample);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
if (vgmstream->coding_type == coding_MPEG_custom ||
|
||||
vgmstream->coding_type == coding_MPEG_ealayer3 ||
|
||||
|
@ -219,6 +239,10 @@ void reset_codec(VGMSTREAM* vgmstream) {
|
|||
reset_imuse(vgmstream->codec_data);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_COMPRESSWAVE) {
|
||||
reset_compresswave(vgmstream->codec_data);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_EA_MT) {
|
||||
reset_ea_mt(vgmstream);
|
||||
}
|
||||
|
@ -269,6 +293,12 @@ void reset_codec(VGMSTREAM* vgmstream) {
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_SPEEX
|
||||
if (vgmstream->coding_type == coding_SPEEX) {
|
||||
reset_speex(vgmstream->codec_data);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
if (vgmstream->coding_type == coding_FFmpeg) {
|
||||
reset_ffmpeg(vgmstream->codec_data);
|
||||
|
@ -469,6 +499,8 @@ int get_vgmstream_samples_per_frame(VGMSTREAM* vgmstream) {
|
|||
return 0; /* varies per mode */
|
||||
case coding_IMUSE:
|
||||
return 0; /* varies per frame */
|
||||
case coding_COMPRESSWAVE:
|
||||
return 0; /* multiple of 2 */
|
||||
case coding_EA_MT:
|
||||
return 0; /* 432, but variable in looped files */
|
||||
case coding_CIRCUS_VQ:
|
||||
|
@ -492,6 +524,10 @@ int get_vgmstream_samples_per_frame(VGMSTREAM* vgmstream) {
|
|||
#ifdef VGM_USE_CELT
|
||||
case coding_CELT_FSB:
|
||||
return 0; /* 512? */
|
||||
#endif
|
||||
#ifdef VGM_USE_SPEEX
|
||||
case coding_SPEEX:
|
||||
return 0;
|
||||
#endif
|
||||
default:
|
||||
return 0;
|
||||
|
@ -673,6 +709,8 @@ int get_vgmstream_frame_size(VGMSTREAM* vgmstream) {
|
|||
return 0; /* varies per mode? */
|
||||
case coding_IMUSE:
|
||||
return 0; /* varies per frame */
|
||||
case coding_COMPRESSWAVE:
|
||||
return 0; /* huffman bits */
|
||||
case coding_EA_MT:
|
||||
return 0; /* variable (frames of bit counts or PCM frames) */
|
||||
#ifdef VGM_USE_ATRAC9
|
||||
|
@ -682,6 +720,10 @@ int get_vgmstream_frame_size(VGMSTREAM* vgmstream) {
|
|||
#ifdef VGM_USE_CELT
|
||||
case coding_CELT_FSB:
|
||||
return 0; /* varies, usually 0x80-100 */
|
||||
#endif
|
||||
#ifdef VGM_USE_SPEEX
|
||||
case coding_SPEEX:
|
||||
return 0; /* varies, usually 0x40-60 */
|
||||
#endif
|
||||
default: /* Vorbis, MPEG, ACM, etc */
|
||||
return 0;
|
||||
|
@ -1219,6 +1261,11 @@ void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_
|
|||
case coding_CELT_FSB:
|
||||
decode_celt_fsb(vgmstream, buffer, samples_to_do, vgmstream->channels);
|
||||
break;
|
||||
#endif
|
||||
#ifdef VGM_USE_SPEEX
|
||||
case coding_SPEEX:
|
||||
decode_speex(vgmstream, buffer, samples_to_do);
|
||||
break;
|
||||
#endif
|
||||
case coding_ACM:
|
||||
decode_acm(vgmstream->codec_data, buffer, samples_to_do, vgmstream->channels);
|
||||
|
@ -1375,6 +1422,10 @@ void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_
|
|||
decode_imuse(vgmstream, buffer, samples_to_do);
|
||||
break;
|
||||
|
||||
case coding_COMPRESSWAVE:
|
||||
decode_compresswave(vgmstream->codec_data, buffer, samples_to_do);
|
||||
break;
|
||||
|
||||
case coding_EA_MT:
|
||||
for (ch = 0; ch < vgmstream->channels; ch++) {
|
||||
decode_ea_mt(vgmstream, buffer+ch, vgmstream->channels, samples_to_do, ch);
|
||||
|
|
|
@ -118,6 +118,7 @@ static const char* extension_list[] = {
|
|||
"bo2",
|
||||
"brstm",
|
||||
"brstmspm",
|
||||
"bsnd",
|
||||
"btsnd",
|
||||
"bvg",
|
||||
"bwav",
|
||||
|
@ -138,6 +139,7 @@ static const char* extension_list[] = {
|
|||
"csa", //txth/reserved [LEGO Racers 2 (PS2)]
|
||||
"csmp",
|
||||
"cvs",
|
||||
"cwav",
|
||||
"cxs",
|
||||
|
||||
"da",
|
||||
|
@ -250,6 +252,7 @@ static const char* extension_list[] = {
|
|||
"kovs", //fake extension/header id for .kvs
|
||||
"kns",
|
||||
"kraw",
|
||||
"ktac",
|
||||
"ktsl2asbin",
|
||||
"ktss", //fake extension/header id for .kns
|
||||
"kvs",
|
||||
|
@ -303,6 +306,7 @@ static const char* extension_list[] = {
|
|||
"mds",
|
||||
"mdsp",
|
||||
"med",
|
||||
"mjb",
|
||||
"mi4",
|
||||
"mib",
|
||||
"mic",
|
||||
|
@ -781,6 +785,7 @@ static const coding_info coding_info_list[] = {
|
|||
{coding_OKI4S, "OKI 4-bit ADPCM (4-shift)"},
|
||||
{coding_PTADPCM, "Platinum 4-bit ADPCM"},
|
||||
{coding_IMUSE, "LucasArts iMUSE VIMA ADPCM"},
|
||||
{coding_COMPRESSWAVE, "CompressWave Huffman ADPCM"},
|
||||
|
||||
{coding_SDX2, "Squareroot-delta-exact (SDX2) 8-bit DPCM"},
|
||||
{coding_SDX2_int, "Squareroot-delta-exact (SDX2) 8-bit DPCM with 1 byte interleave"},
|
||||
|
@ -825,6 +830,9 @@ static const coding_info coding_info_list[] = {
|
|||
#ifdef VGM_USE_CELT
|
||||
{coding_CELT_FSB, "Custom CELT"},
|
||||
#endif
|
||||
#ifdef VGM_USE_SPEEX
|
||||
{coding_SPEEX, "Custom Speex"},
|
||||
#endif
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
{coding_FFmpeg, "FFmpeg"},
|
||||
#endif
|
||||
|
@ -919,7 +927,7 @@ static const meta_info meta_info_list[] = {
|
|||
{meta_PS2_EXST, "Sony EXST header"},
|
||||
{meta_SVAG_KCET, "Konami SVAG header"},
|
||||
{meta_PS_HEADERLESS, "Headerless PS-ADPCM raw header"},
|
||||
{meta_PS2_MIB_MIH, "Sony MultiStream MIH+MIB header"},
|
||||
{meta_MIB_MIH, "Sony MultiStream MIH+MIB header"},
|
||||
{meta_DSP_MPDSP, "Single DSP header stereo by .mpdsp extension"},
|
||||
{meta_PS2_MIC, "KOEI .MIC header"},
|
||||
{meta_DSP_JETTERS, "Double DSP header stereo by _lr.dsp extension"},
|
||||
|
@ -1326,6 +1334,10 @@ static const meta_info meta_info_list[] = {
|
|||
{meta_SBK, "Team17 SBK header"},
|
||||
{meta_DSP_WIIADPCM, "Exient WIIADPCM header"},
|
||||
{meta_DSP_CWAC, "CRI CWAC header"},
|
||||
{meta_COMPRESSWAVE, "CompressWave .cwav header"},
|
||||
{meta_KTAC, "Koei Tecmo KTAC header"},
|
||||
{meta_MJB_MJH, "Sony MultiStream MJH+MJB header"},
|
||||
{meta_BSNF, "id Software BSNF header"},
|
||||
};
|
||||
|
||||
void get_vgmstream_coding_description(VGMSTREAM* vgmstream, char* out, size_t out_size) {
|
||||
|
|
|
@ -243,6 +243,9 @@ static const adxkey_info adxkey9_list[] = {
|
|||
/* Persona 5 Royal (PS4) */
|
||||
{0x0000,0x1c85,0x7043, NULL,29915170}, // 0000000001C87822
|
||||
|
||||
/* Assault Lily Last Bullet (Android) */
|
||||
{0x0aca,0x0ef5,0x05c9, NULL,0}, // guessed with VGAudio (possible key: 5650EF42E5 / 370725044965)
|
||||
|
||||
};
|
||||
|
||||
static const int adxkey8_list_count = sizeof(adxkey8_list) / sizeof(adxkey8_list[0]);
|
||||
|
|
|
@ -1,35 +1,39 @@
|
|||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* .AO - from AlphaOgg lib [Cloudphobia (PC)] */
|
||||
VGMSTREAM * init_vgmstream_ao(STREAMFILE *streamFile) {
|
||||
/* .AO - from AlphaOgg lib [Cloudphobia (PC), GEO ~The Sword Millennia~ Kasumi no Tani no Kaibutsu (PC)] */
|
||||
VGMSTREAM* init_vgmstream_ao(STREAMFILE *sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
|
||||
|
||||
/* checks */
|
||||
if ( !check_extensions(streamFile,"ao") )
|
||||
if (!check_extensions(sf,"ao"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00,streamFile) != 0x414C5048) /* "ALPH" */
|
||||
goto fail;
|
||||
if (read_32bitBE(0x04,streamFile) != 0x414F4747) /* "AOGG" */
|
||||
if (!is_id64be(0x00,sf, "ALPHAOGG"))
|
||||
goto fail;
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
{
|
||||
ogg_vorbis_meta_info_t ovmi = {0};
|
||||
int sample_rate = read_u32le(0xF0, sf); /* Ogg header */
|
||||
|
||||
ovmi.meta_type = meta_AO;
|
||||
/* values at 0x08/0x0c/0x10 may be related to looping? */
|
||||
|
||||
ovmi.loop_start = read_f32le(0x08, sf) * sample_rate;
|
||||
ovmi.loop_end = read_f32le(0x0c, sf) * sample_rate; /* also num_samples in some versions */
|
||||
ovmi.loop_end_found = 1;
|
||||
ovmi.loop_flag = read_u8(0x10, sf) != 0; /* count or -1=infinite, u32 in some versions */
|
||||
/* AlphaOgg defines up to 16 loop points for some reason */
|
||||
|
||||
start_offset = 0xc8;
|
||||
vgmstream = init_vgmstream_ogg_vorbis_callbacks(streamFile, NULL, start_offset, &ovmi);
|
||||
vgmstream = init_vgmstream_ogg_vorbis_callbacks(sf, NULL, start_offset, &ovmi);
|
||||
}
|
||||
#else
|
||||
goto fail;
|
||||
#endif
|
||||
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
/* .CWAV - from CompressWave lib, found in few Japanese (doujin?) games around 1995-2002 [RADIO ZONDE (PC), GEO ~The Sword Millennia~ (PC)] */
|
||||
VGMSTREAM* init_vgmstream_compresswave(STREAMFILE *sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag, channels;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf, "cwav"))
|
||||
goto fail;
|
||||
|
||||
if (!is_id64be(0x00,sf, "CmpWave\0"))
|
||||
goto fail;
|
||||
|
||||
channels = 2; /* always, header channels is internal config */
|
||||
start_offset = 0x00;
|
||||
loop_flag = 1; //read_u8(0x430, sf) != 0; /* wrong count, see below */
|
||||
/* codec allows to use a cipher value, not seen */
|
||||
/* there is also title and artist, but default to "UnTitled" / "NoName" */
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_COMPRESSWAVE;
|
||||
vgmstream->sample_rate = 44100; /* always, header rate is internal config */
|
||||
/* in PCM bytes */
|
||||
vgmstream->num_samples = read_u64le(0x418, sf) / sizeof(int16_t) / channels;
|
||||
/* known files have wrong loop values and just repeat */
|
||||
vgmstream->loop_start_sample = 0; //read_u64le(0x420, sf) / sizeof(int16_t) / channels;
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples; //read_u64le(0x428, sf) / sizeof(int16_t) / channels;
|
||||
|
||||
vgmstream->codec_data = init_compresswave(sf);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_COMPRESSWAVE;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
|
@ -1332,7 +1332,6 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAM
|
|||
|
||||
break;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
|
@ -1345,7 +1344,23 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAM
|
|||
}
|
||||
#endif
|
||||
|
||||
case EAAC_CODEC_EASPEEX: /* "Esp0"?: EASpeex (libspeex variant, base versions vary: 1.0.5, 1.2beta3) */ //todo
|
||||
#ifdef VGM_USE_SPEEX
|
||||
case EAAC_CODEC_EASPEEX: { /* "Esp0"?: EASpeex (libspeex variant, base versions vary: 1.0.5, 1.2beta3) [FIFA 14 (PS4), FIFA 2020 (Switch)] */
|
||||
/* EASpeex looks normal but simplify with custom IO to avoid worrying about blocks.
|
||||
* First block samples count frames' samples subtracting encoder delay. */
|
||||
|
||||
vgmstream->codec_data = init_speex_ea(eaac.channels);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_SPEEX;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
temp_sf = setup_eaac_audio_streamfile(sf, eaac.version, eaac.codec, eaac.streamed,0,0, 0x00);
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
VGM_LOG("EA EAAC: unknown codec 0x%02x\n", eaac.codec);
|
||||
goto fail;
|
||||
|
|
|
@ -87,6 +87,7 @@ static size_t eaac_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset,
|
|||
case 0x05: /* EALayer3 v1 */
|
||||
case 0x06: /* EALayer3 v2 "PCM" */
|
||||
case 0x07: /* EALayer3 v2 "Spike" */
|
||||
case 0x09: /* EASpeex */
|
||||
case 0x0b: /* EAMP3 */
|
||||
case 0x0c: /* EAOpus */
|
||||
data->skip_size = 0x08;
|
||||
|
@ -208,6 +209,7 @@ static size_t eaac_io_size(STREAMFILE *streamfile, eaac_io_data* data) {
|
|||
case 0x05: /* EALayer3 v1 */
|
||||
case 0x06: /* EALayer3 v2 "PCM" */
|
||||
case 0x07: /* EALayer3 v2 "Spike" */
|
||||
case 0x09: /* EASpeex */
|
||||
case 0x0b: /* EAMP3 */
|
||||
case 0x0c: /* EAOpus */
|
||||
data_size = block_size - 0x08;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef _FSB_ENCRYPTED_STREAMFILE_H_
|
||||
#define _FSB_ENCRYPTED_H_
|
||||
|
||||
#define FSB_KEY_MAX 128 /* probably 32 */
|
||||
#define FSB_KEY_MAX 0x10000 //0x168
|
||||
|
||||
|
||||
typedef struct {
|
||||
|
|
|
@ -385,6 +385,9 @@ static const hcakey_info hcakey_list[] = {
|
|||
/* HoneyWorks Premium Live (Android) */
|
||||
{20200401000000}, // 0000125F45B9D640
|
||||
|
||||
/* Assault Lily Last Bullet (Android) */
|
||||
{6349046567469313}, // 00168E6C99510101
|
||||
|
||||
/* Dragalia Lost (iOS/Android) */
|
||||
{2967411924141, subkeys_dgl, sizeof(subkeys_dgl) / sizeof(subkeys_dgl[0]) }, // 000002B2E7889CAD
|
||||
|
||||
|
|
|
@ -0,0 +1,489 @@
|
|||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "idtech_streamfile.h"
|
||||
#include <string.h>
|
||||
|
||||
|
||||
/* mzrt - id Tech 4.5 audio found in .resource bigfiles (w/ internal filenames) [Doom 3 BFG edition (PC/PS3/X360)] */
|
||||
VGMSTREAM* init_vgmstream_mzrt_v0(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag = 0, channels, codec, sample_rate, block_size = 0, bps = 0, num_samples;
|
||||
STREAMFILE* temp_sf = NULL;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf, "idwav,idmsf,idxma"))
|
||||
goto fail;
|
||||
|
||||
if (!is_id32be(0x00,sf, "mzrt"))
|
||||
goto fail;
|
||||
|
||||
if (read_u32be(0x04, sf) != 0) /* version */
|
||||
goto fail;
|
||||
|
||||
/* this format is bizarrely mis-aligned (and mis-designed too) */
|
||||
|
||||
num_samples = read_s32be(0x11,sf);
|
||||
codec = read_u16le(0x15,sf);
|
||||
switch(codec) {
|
||||
case 0x0001:
|
||||
case 0x0002:
|
||||
case 0x0166:
|
||||
channels = read_u16le(0x17,sf);
|
||||
sample_rate = read_u32le(0x19, sf);
|
||||
block_size = read_u16le(0x21, sf);
|
||||
bps = read_u16le(0x23,sf);
|
||||
|
||||
start_offset = 0x25;
|
||||
break;
|
||||
|
||||
case 0x0000:
|
||||
sample_rate = read_u32be(0x1D, sf);
|
||||
channels = read_u32be(0x21, sf);
|
||||
|
||||
start_offset = 0x29;
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* skip MSADPCM data */
|
||||
if (codec == 0x0002) {
|
||||
if (!msadpcm_check_coefs(sf, start_offset + 0x02 + 0x02))
|
||||
goto fail;
|
||||
|
||||
start_offset += 0x02 + read_u16le(start_offset, sf);
|
||||
}
|
||||
|
||||
/* skip extra data */
|
||||
if (codec == 0x0166) {
|
||||
start_offset += 0x02 + read_u16le(start_offset, sf);
|
||||
}
|
||||
|
||||
/* skip unknown table */
|
||||
if (codec == 0x0000) {
|
||||
start_offset += 0x04 + read_u32be(start_offset, sf) * 0x04;
|
||||
}
|
||||
|
||||
/* skip unknown table */
|
||||
start_offset += 0x04 + read_u32be(start_offset, sf);
|
||||
|
||||
/* skip block info */
|
||||
if (codec != 0x0000) {
|
||||
/* 0x00: de-blocked size
|
||||
* 0x04: block count*/
|
||||
start_offset += 0x08;
|
||||
|
||||
/* idwav only uses 1 super-block though */
|
||||
temp_sf = setup_mzrt_streamfile(sf, start_offset);
|
||||
if (!temp_sf) goto fail;
|
||||
}
|
||||
else {
|
||||
/* 0x00: de-blocked size */
|
||||
start_offset += 0x04;
|
||||
}
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_MZRT;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = num_samples;
|
||||
|
||||
switch(codec) {
|
||||
case 0x0001:
|
||||
if (bps != 16) goto fail;
|
||||
vgmstream->coding_type = coding_PCM16LE;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = block_size / channels;
|
||||
break;
|
||||
|
||||
case 0x0002:
|
||||
if (bps != 4) goto fail;
|
||||
vgmstream->coding_type = coding_MSADPCM;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->frame_size = block_size;
|
||||
break;
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case 0x0166: {
|
||||
uint8_t buf[0x100];
|
||||
int bytes;
|
||||
size_t stream_size = get_streamfile_size(temp_sf);
|
||||
|
||||
bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,sizeof(buf), 0x15,0x34, stream_size, sf, 0);
|
||||
vgmstream->codec_data = init_ffmpeg_header_offset(temp_sf, buf,bytes, 0x00,stream_size);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
xma_fix_raw_samples_hb(vgmstream, sf, temp_sf, 0x00, stream_size, 0x15, 0,0);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
case 0x0000: {
|
||||
mpeg_custom_config cfg = {0};
|
||||
|
||||
cfg.skip_samples = 576; /* assumed */
|
||||
|
||||
vgmstream->codec_data = init_mpeg_custom(sf, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->layout_type = layout_none;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, temp_sf == NULL ? sf : temp_sf, temp_sf == NULL ? start_offset : 0x00))
|
||||
goto fail;
|
||||
close_streamfile(temp_sf);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_sf);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* mzrt - id Tech 5 audio [Rage (PS3), The Evil Within (PS3)] */
|
||||
VGMSTREAM* init_vgmstream_mzrt_v1(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset, offset;
|
||||
size_t stream_size;
|
||||
int loop_flag, channels, codec, type, sample_rate, block_size = 0, bps = 0;
|
||||
int32_t num_samples, loop_start = 0;
|
||||
STREAMFILE* sb = NULL;
|
||||
const char* extension = NULL;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf, "idmsf")) //idmsa: untested
|
||||
goto fail;
|
||||
|
||||
if (!is_id32be(0x00,sf, "mzrt"))
|
||||
goto fail;
|
||||
if (read_u32be(0x04, sf) != 1) /* version */
|
||||
goto fail;
|
||||
|
||||
type = read_s32be(0x09,sf);
|
||||
if (type == 0) { /* Rage */
|
||||
/* 0x0d: null */
|
||||
/* 0x11: crc? */
|
||||
/* 0x15: flag? */
|
||||
offset = 0x19;
|
||||
}
|
||||
else { /* TEW */
|
||||
offset = 0x0D;
|
||||
}
|
||||
|
||||
stream_size = read_u32be(offset + 0x00,sf);
|
||||
offset = read_u32be(offset + 0x04,sf); /* absolute but typically right after this */
|
||||
|
||||
/* 0x00: crc? */
|
||||
codec = read_u8(offset + 0x04,sf); /* assumed */
|
||||
switch(codec) {
|
||||
case 0x00: {
|
||||
/* 0x05: null? */
|
||||
num_samples = read_s32be(offset + 0x09,sf);
|
||||
/* 0x0D: null? */
|
||||
/* 0x11: loop related? */
|
||||
/* 0x1d: stream size? */
|
||||
/* others: ? */
|
||||
|
||||
/* fmt at 0x31 (codec, avg bitrate, etc) */
|
||||
channels = read_u16le(offset + 0x33, sf);
|
||||
sample_rate = read_u32le(offset + 0x35, sf);
|
||||
block_size = read_u16le(offset + 0x3d, sf);
|
||||
bps = read_u16le(offset + 0x3f, sf);
|
||||
|
||||
/* 0x41: MSADPCM fmt extra */
|
||||
if (!msadpcm_check_coefs(sf, offset + 0x41 + 0x02 + 0x02))
|
||||
goto fail;
|
||||
|
||||
extension = "msadpcm";
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x01: {
|
||||
uint32_t table_entries;
|
||||
|
||||
/* 0x05: stream size? */
|
||||
num_samples = read_s32be(offset + 0x09,sf);
|
||||
/* 0x0D: 0x40? */
|
||||
/* 0x11: loop related? */
|
||||
loop_start = read_s32be(offset + 0x15,sf);
|
||||
/* 0x19: null */
|
||||
table_entries = read_u32be(offset + 0x1d,sf);
|
||||
|
||||
/* skip seek table, format: frame size (16b) + frame samples (16b)
|
||||
* (first entry may be 0 then next entry x2 samples to mimic encoder latency?) */
|
||||
offset += 0x21 + table_entries * 0x04;
|
||||
|
||||
sample_rate = read_u32be(offset + 0x00, sf);
|
||||
channels = read_u32be(offset + 0x04, sf);
|
||||
/* 0x0c: MSF codec */
|
||||
|
||||
extension = type == 0 ? "msadpcm" : "msf";
|
||||
break;
|
||||
}
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
sb = open_streamfile_by_ext(sf, extension);
|
||||
if (!sb) goto fail;
|
||||
|
||||
if (stream_size != get_streamfile_size(sb))
|
||||
goto fail;
|
||||
|
||||
|
||||
loop_flag = (loop_start > 0);
|
||||
start_offset = 0x00;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_MZRT;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = num_samples;
|
||||
vgmstream->loop_start_sample = loop_start;
|
||||
vgmstream->loop_end_sample = num_samples;
|
||||
|
||||
switch(codec) {
|
||||
case 0x0002:
|
||||
if (bps != 4) goto fail;
|
||||
vgmstream->coding_type = coding_MSADPCM;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->frame_size = block_size;
|
||||
break;
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
case 0x01: {
|
||||
mpeg_custom_config cfg = {0};
|
||||
|
||||
cfg.skip_samples = 1152; /* seems ok */
|
||||
|
||||
vgmstream->codec_data = init_mpeg_custom(sb, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->num_samples -= cfg.skip_samples;
|
||||
vgmstream->loop_start_sample -= cfg.skip_samples;
|
||||
vgmstream->loop_end_sample -= cfg.skip_samples;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sb, start_offset))
|
||||
goto fail;
|
||||
close_streamfile(sb);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(sb);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* bsnf - id Tech 5 audio [Wolfenstein: The New Order (multi), Wolfenstein: The Old Blood (PS4)] */
|
||||
VGMSTREAM* init_vgmstream_bsnf(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset, offset, extra_offset;
|
||||
size_t stream_size;
|
||||
int loop_flag, channels, codec, sample_rate; //, block_size = 0, bps = 0;
|
||||
int32_t num_samples, loop_start = 0;
|
||||
STREAMFILE* sb = NULL;
|
||||
const char* suffix = NULL;
|
||||
const char* extension = NULL;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf, "bsnd"))
|
||||
goto fail;
|
||||
|
||||
if (!is_id32be(0x00,sf, "bsnf")) /* null-terminated string */
|
||||
goto fail;
|
||||
if (read_u32be(0x05, sf) != 0x00000100) /* version */
|
||||
goto fail;
|
||||
|
||||
offset = 0x18;
|
||||
|
||||
stream_size = read_u32be(offset + 0x00,sf);
|
||||
offset = read_u32be(offset + 0x04,sf); /* absolute but typically right after this */
|
||||
|
||||
/* 0x00: crc? */
|
||||
/* 0x04: CBR samples or 0 if VBR */
|
||||
num_samples = read_s32be(offset + 0x08,sf);
|
||||
loop_start = read_s32be(offset + 0x0c,sf);
|
||||
/* 0x10: stream size? */
|
||||
|
||||
codec = read_u16le(offset + 0x14,sf);
|
||||
channels = read_u16le(offset + 0x16, sf);
|
||||
sample_rate = read_u32le(offset + 0x18, sf);
|
||||
//block_size = read_u16le(offset + 0x20, sf);
|
||||
//bps = read_u16le(offset + 0x22, sf);
|
||||
|
||||
extra_offset = offset + 0x24;
|
||||
extension = "ogg"; /* same for all codecs */
|
||||
switch(codec) {
|
||||
case 0x0055: /* msf */
|
||||
/* 0x00: table entries */
|
||||
/* 0x04: seek table, format: frame size (16b) + frame samples (16b) */
|
||||
suffix = "_msf.bsnd";
|
||||
break;
|
||||
|
||||
case 0x0166: /* xma */
|
||||
/* 0x00: extra size */
|
||||
/* 0x02: xma config and block table */
|
||||
suffix = "_xma.bsnd";
|
||||
break;
|
||||
|
||||
case 0x674F: /* vorbis */
|
||||
/* 0x00: extra size */
|
||||
/* 0x02: num samples */
|
||||
suffix = "_vorbis.bsnd";
|
||||
goto fail; //untested
|
||||
//break;
|
||||
|
||||
case 0x42D2: /* at9 */
|
||||
/* 0x00: extra size */
|
||||
/* 0x02: encoder delay */
|
||||
/* 0x04: channel config */
|
||||
/* 0x08: ATRAC9 GUID */
|
||||
/* 0x1c: ATRAC9 config */
|
||||
suffix = "_at9.bsnd";
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
{
|
||||
int suffix_len = strlen(suffix);
|
||||
int filename_len;
|
||||
char filename[PATH_LIMIT];
|
||||
|
||||
get_streamfile_filename(sf, filename, sizeof(filename));
|
||||
filename_len = strlen(filename);
|
||||
|
||||
if (filename_len < suffix_len)
|
||||
goto fail;
|
||||
filename[filename_len - suffix_len + 0] = '.';
|
||||
filename[filename_len - suffix_len + 1] = '\0';
|
||||
strcat(filename, extension);
|
||||
|
||||
sb = open_streamfile_by_filename(sf, filename);
|
||||
if (!sb) goto fail;
|
||||
}
|
||||
|
||||
if (stream_size != get_streamfile_size(sb))
|
||||
goto fail;
|
||||
|
||||
loop_flag = (loop_start > 0);
|
||||
start_offset = 0x00;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_BSNF;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = num_samples;
|
||||
vgmstream->loop_start_sample = loop_start;
|
||||
vgmstream->loop_end_sample = num_samples;
|
||||
|
||||
switch(codec) {
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
case 0x0055: {
|
||||
mpeg_custom_config cfg = {0};
|
||||
|
||||
cfg.skip_samples = 1152; /* seems ok */
|
||||
|
||||
vgmstream->codec_data = init_mpeg_custom(sb, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->num_samples -= cfg.skip_samples;
|
||||
vgmstream->loop_start_sample -= cfg.skip_samples;
|
||||
vgmstream->loop_end_sample -= cfg.skip_samples;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case 0x0166: {
|
||||
uint8_t buf[0x100];
|
||||
size_t bytes, block_size, block_count;
|
||||
|
||||
block_size = 0x800;
|
||||
block_count = stream_size / block_size;
|
||||
|
||||
bytes = ffmpeg_make_riff_xma2(buf, sizeof(buf), num_samples, stream_size, channels, sample_rate, block_count, block_size);
|
||||
vgmstream->codec_data = init_ffmpeg_header_offset(sb, buf, bytes, start_offset, stream_size);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
xma_fix_raw_samples(vgmstream, sb, start_offset, stream_size, 0x00, 1,1);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
case 0x674F: {
|
||||
vgmstream->codec_data = init_ogg_vorbis(sb, start_offset, stream_size, NULL);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_OGG_VORBIS;
|
||||
vgmstream->layout_type = layout_none;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_ATRAC9
|
||||
case 0x42D2: {
|
||||
atrac9_config cfg = {0};
|
||||
|
||||
cfg.channels = vgmstream->channels;
|
||||
cfg.encoder_delay = read_u16le(extra_offset + 0x02,sf);
|
||||
cfg.config_data = read_u32be(extra_offset + 0x1c,sf);
|
||||
|
||||
vgmstream->codec_data = init_atrac9(&cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_ATRAC9;
|
||||
vgmstream->layout_type = layout_none;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sb, start_offset))
|
||||
goto fail;
|
||||
close_streamfile(sb);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(sb);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
|
@ -0,0 +1,514 @@
|
|||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
int channels;
|
||||
int sample_rate;
|
||||
int loop_flag;
|
||||
int32_t num_samples;
|
||||
int32_t loop_start;
|
||||
int32_t loop_end;
|
||||
uint32_t file_size;
|
||||
uint32_t stream_offset;
|
||||
uint32_t stream_size;
|
||||
uint32_t table_offset;
|
||||
uint32_t table_entries;
|
||||
int type;
|
||||
int encoder_delay;
|
||||
int end_padding;
|
||||
int frame_samples;
|
||||
} ktac_header_t;
|
||||
|
||||
|
||||
static int make_m4a_header(uint8_t* buf, int buf_len, ktac_header_t* ktac, STREAMFILE* sf);
|
||||
|
||||
/* KTAC - Koei Tecmo custom AAC [Kin'iro no Corda 3 (Vita), Shingeki no Kyojin: Shichi kara no Dasshutsu (3DS), Dynasty Warriors (PS4)] */
|
||||
VGMSTREAM* init_vgmstream_ktac(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
ktac_header_t ktac = {0};
|
||||
|
||||
|
||||
/* checks */
|
||||
/* .ktac: header id */
|
||||
if (!check_extensions(sf,"ktac"))
|
||||
goto fail;
|
||||
if (!is_id32be(0x00,sf, "KTAC"))
|
||||
goto fail;
|
||||
|
||||
/* 0x04: version? (always 1) */
|
||||
ktac.file_size = read_u32le(0x08,sf);
|
||||
if (ktac.file_size != get_streamfile_size(sf))
|
||||
goto fail;
|
||||
ktac.stream_offset = read_u32le(0x0c,sf);
|
||||
ktac.stream_size = read_u32le(0x10,sf);
|
||||
ktac.type = read_u32le(0x14,sf);
|
||||
ktac.sample_rate = read_u32le(0x18,sf);
|
||||
ktac.num_samples = read_u32le(0x1c,sf); /* full samples */
|
||||
ktac.channels = read_u16le(0x20,sf);
|
||||
ktac.frame_samples = read_u16le(0x22,sf);
|
||||
ktac.encoder_delay = read_u16le(0x24,sf);
|
||||
ktac.end_padding = read_u16le(0x26,sf);
|
||||
ktac.loop_start = read_u32le(0x28,sf);
|
||||
ktac.loop_end = read_u32le(0x2c,sf);
|
||||
/* 0x30: ? (big, related to loops) */
|
||||
/* 0x34: ? (always null) */
|
||||
ktac.table_offset = read_u32le(0x38,sf);
|
||||
ktac.table_entries= read_u32le(0x3c,sf);
|
||||
|
||||
ktac.loop_flag = (ktac.loop_end > 0);
|
||||
|
||||
/* type 1 files crash during sample_copy, wrong fake header/esds?
|
||||
* (0=AoT, KnC3 bgm, 1=KnC3 1ch voices, 2=DW4, Atelier Ryza) */
|
||||
if (ktac.type == 1)
|
||||
goto fail;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(ktac.channels, ktac.loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_KTAC;
|
||||
vgmstream->sample_rate = ktac.sample_rate;
|
||||
vgmstream->num_samples = ktac.num_samples - ktac.encoder_delay - ktac.end_padding;
|
||||
vgmstream->loop_start_sample = ktac.loop_start * ktac.frame_samples - ktac.encoder_delay;
|
||||
vgmstream->loop_end_sample = ktac.loop_end * ktac.frame_samples - ktac.encoder_delay;
|
||||
|
||||
/* KTAC uses AAC, but not type found in .aac (that has headered frames, like mp3) but raw
|
||||
* packets + frame size table (similar to .mp4/m4a). We make a fake M4A header to feed FFmpeg */
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
{
|
||||
ffmpeg_codec_data* ffmpeg_data = NULL;
|
||||
int bytes;
|
||||
uint8_t* buf = NULL;
|
||||
int buf_len = 0x400 + ktac.table_entries * 0x4;
|
||||
|
||||
if (buf_len > 0x100000) /* ??? */
|
||||
goto fail;
|
||||
|
||||
buf = malloc(buf_len);
|
||||
if (!buf) goto fail;
|
||||
|
||||
bytes = make_m4a_header(buf, buf_len, &ktac, sf);
|
||||
ffmpeg_data = init_ffmpeg_header_offset(sf, buf, bytes, ktac.stream_offset, ktac.stream_size);
|
||||
free(buf);
|
||||
|
||||
if (!ffmpeg_data) goto fail;
|
||||
vgmstream->codec_data = ffmpeg_data;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
/* not part of fake header since it's kinda complex to add (iTunes string comment) */
|
||||
ffmpeg_set_skip_samples(ffmpeg_data, ktac.encoder_delay);
|
||||
}
|
||||
#else
|
||||
goto fail;
|
||||
#endif
|
||||
|
||||
return vgmstream;
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* *********************************************************** */
|
||||
|
||||
/* Helpers for M4A headers, an insane soup of chunks (AKA "atoms").
|
||||
* Needs *A LOT* of atoms and fields so this is more elaborate than usual.
|
||||
* - https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFPreface/qtffPreface.html
|
||||
*/
|
||||
|
||||
/* generic additions */
|
||||
typedef struct {
|
||||
uint8_t* out;
|
||||
int bytes;
|
||||
} m4a_state_t;
|
||||
|
||||
typedef struct {
|
||||
STREAMFILE* sf;
|
||||
ktac_header_t* ktac; /* config */
|
||||
uint8_t* out; /* current position */
|
||||
int bytes; /* written bytes */
|
||||
m4a_state_t chunks; /* chunks offsets are absolute, save position until we know header size */
|
||||
} m4a_header_t;
|
||||
|
||||
static void add_u32b(m4a_header_t* h, uint32_t value) {
|
||||
put_u32be(h->out, value);
|
||||
h->out += 0x04;
|
||||
h->bytes += 0x04;
|
||||
}
|
||||
|
||||
static void add_u24b(m4a_header_t* h, uint32_t value) {
|
||||
put_u16be(h->out + 0x00, (value >> 8u) & 0xFFFF);
|
||||
put_u8 (h->out + 0x02, (value >> 0u) & 0xFF);
|
||||
h->out += 0x03;
|
||||
h->bytes += 0x03;
|
||||
}
|
||||
|
||||
static void add_u16b(m4a_header_t* h, uint16_t value) {
|
||||
put_u16be(h->out, value);
|
||||
h->out += 0x02;
|
||||
h->bytes += 0x02;
|
||||
}
|
||||
|
||||
static void add_u8(m4a_header_t* h, uint32_t value) {
|
||||
put_u8(h->out, value);
|
||||
h->out += 0x01;
|
||||
h->bytes += 0x01;
|
||||
}
|
||||
|
||||
static void add_name(m4a_header_t* h, const char* name) {
|
||||
memcpy(h->out, name, 0x4);
|
||||
h->out += 0x04;
|
||||
h->bytes += 0x04;
|
||||
}
|
||||
|
||||
static void add_atom(m4a_header_t* h, const char* name, uint32_t size) {
|
||||
add_u32b(h, size);
|
||||
add_name(h, name);
|
||||
}
|
||||
|
||||
/* register + write final size for atoms of variable/complex size */
|
||||
static void save_atom(m4a_header_t* h, m4a_state_t* s) {
|
||||
s->out = h->out;
|
||||
s->bytes = h->bytes;
|
||||
}
|
||||
|
||||
static void load_atom(m4a_header_t* h, m4a_state_t* s) {
|
||||
put_u32be(s->out, h->bytes - s->bytes);
|
||||
}
|
||||
|
||||
/* common atoms */
|
||||
|
||||
static void add_ftyp(m4a_header_t* h) {
|
||||
add_atom(h, "ftyp", 0x18);
|
||||
add_name(h, "M4A "); /* major brand */
|
||||
add_u32b(h, 512); /* minor version */
|
||||
add_name(h, "isom"); /* compatible brands */
|
||||
add_name(h, "iso2"); /* compatible brands */
|
||||
}
|
||||
|
||||
static void add_free(m4a_header_t* h) {
|
||||
add_atom(h, "free", 0x08);
|
||||
}
|
||||
|
||||
static void add_mdat(m4a_header_t* h) {
|
||||
add_atom(h, "mdat", 0x08 + h->ktac->stream_size);
|
||||
}
|
||||
|
||||
/* variable atoms */
|
||||
|
||||
|
||||
static void add_stco(m4a_header_t* h) {
|
||||
add_atom(h, "stco", 0x10 + 1 * 0x04);
|
||||
add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */
|
||||
add_u32b(h, 1); /* Number of entries */
|
||||
/* there may be an entry per frame, but only first seems needed */
|
||||
save_atom(h, &h->chunks);
|
||||
add_u32b(h, 0); /* Absolute offset N */
|
||||
}
|
||||
static void add_stsz(m4a_header_t* h) {
|
||||
int i;
|
||||
|
||||
add_atom(h, "stsz", 0x14 + h->ktac->table_entries * 0x04);
|
||||
add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */
|
||||
add_u32b(h, 0); /* Sample size (CBR) */
|
||||
add_u32b(h, h->ktac->table_entries); /* Number of entries (VBR) */
|
||||
for (i = 0; i < h->ktac->table_entries; i++) {
|
||||
uint32_t size = read_u32le(h->ktac->table_offset + i*0x04, h->sf);
|
||||
add_u32b(h, size); /* Sample N */
|
||||
}
|
||||
}
|
||||
|
||||
static void add_stsc(m4a_header_t* h) {
|
||||
add_atom(h, "stsc", 0x1c);
|
||||
add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */
|
||||
add_u32b(h, 1); /* Number of entries */
|
||||
add_u32b(h, 1); /* First chunk */
|
||||
add_u32b(h, h->ktac->table_entries); /* Samples per chunk */
|
||||
add_u32b(h, 1); /* Sample description ID */
|
||||
}
|
||||
|
||||
static void add_stts(m4a_header_t* h) {
|
||||
add_atom(h, "stts", 0x18);
|
||||
add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */
|
||||
add_u32b(h, 1); /* Number of entries */
|
||||
add_u32b(h, h->ktac->table_entries); /* Sample count */
|
||||
add_u32b(h, h->ktac->frame_samples); /* Sample duration */
|
||||
}
|
||||
|
||||
/* from mpeg4audio.c (also see ff_mp4_read_dec_config_descr) */
|
||||
static const int m4a_sample_rates[16] = {
|
||||
96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350
|
||||
};
|
||||
static const uint8_t m4a_channels[14] = {
|
||||
0,
|
||||
1, // mono (1/0)
|
||||
2, // stereo (2/0)
|
||||
3, // 3/0
|
||||
4, // 3/1
|
||||
5, // 3/2
|
||||
6, // 3/2.1
|
||||
8, // 5/2.1
|
||||
//0,
|
||||
//0,
|
||||
//0,
|
||||
//7, // 3/3.1
|
||||
//8, // 3/2/2.1
|
||||
//24 // 3/3/3 - 5/2/3 - 3/0/0.2
|
||||
};
|
||||
|
||||
static void add_esds(m4a_header_t* h) {
|
||||
uint16_t config = 0;
|
||||
|
||||
/* ES_descriptor (TLV format see ISO 14496-1) and DecSpecificInfoTag define actual decoding
|
||||
- config (channels/rate/etc), other atoms with the same stuff is just info
|
||||
* - http://ecee.colorado.edu/~ecen5653/ecen5653/papers/ISO%2014496-1%202004.PDF */
|
||||
|
||||
{
|
||||
uint8_t object_type = 0x02; /* 0x00=none, 0x01=AAC main, 0x02=AAC LC */
|
||||
uint8_t sr_index = 0;
|
||||
uint8_t ch_index = 0;
|
||||
uint8_t unknown = 0;
|
||||
int i;
|
||||
for (i = 0; i < 16; i++) {
|
||||
if (m4a_sample_rates[i] == h->ktac->sample_rate) {
|
||||
sr_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (m4a_channels[i] == h->ktac->channels) {
|
||||
ch_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
config |= (object_type & 0x1F) << 11; /* 5b */
|
||||
config |= (sr_index & 0x0F) << 7; /* 4b */
|
||||
config |= (ch_index & 0x0F) << 3; /* 4b */
|
||||
config |= (unknown & 0x07) << 0; /* 3b */
|
||||
}
|
||||
|
||||
add_atom(h, "esds", 0x33);
|
||||
add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */
|
||||
|
||||
add_u8 (h, 0x03); /* ES_DescrTag */
|
||||
add_u32b(h, 0x80808022); /* size 0x22 */
|
||||
add_u16b(h, 0x0000); /* stream Id */
|
||||
add_u8 (h, 0x00); /* flags */
|
||||
|
||||
add_u8 (h, 0x04); /* DecoderConfigDescrTag */
|
||||
add_u32b(h, 0x80808014); /* size 0x14 */
|
||||
add_u8 (h, 0x40); /* object type (0x40=audio) */
|
||||
add_u8 (h, 0x15); /* stream type (6b: 0x5=audio) + upstream (1b) + reserved (1b: const 1) */
|
||||
add_u24b(h, 0x000000); /* buffer size */
|
||||
add_u32b(h, 0); /* max bitrate (256000?)*/
|
||||
add_u32b(h, 0); /* average bitrate (256000?) */
|
||||
|
||||
add_u8 (h, 0x05); /* DecSpecificInfoTag */
|
||||
add_u32b(h, 0x80808002); /* size 0x02 */
|
||||
add_u16b(h, config); /* actual decoder info */
|
||||
|
||||
add_u8 (h, 0x06); /* SLConfigDescrTag */
|
||||
add_u32b(h, 0x80808001); /* size 0x01 */
|
||||
add_u8 (h, 0x02); /* predefined (2=default) */
|
||||
}
|
||||
|
||||
static void add_mp4a(m4a_header_t* h) {
|
||||
add_atom(h, "mp4a", 0x57);
|
||||
add_u32b(h, 0); /* ? */
|
||||
add_u32b(h, 1); /* Data reference index */
|
||||
add_u32b(h, 0); /* Reserved */
|
||||
add_u32b(h, 0); /* Reserved 2 */
|
||||
add_u16b(h, h->ktac->channels); /* Channel count */
|
||||
add_u16b(h, 16); /* Sample size */
|
||||
add_u32b(h, 0); /* Pre-defined */
|
||||
add_u16b(h, h->ktac->sample_rate); /* Sample rate */
|
||||
add_u16b(h, 0); /* ? */
|
||||
add_esds(h); /* elementary stream descriptor */
|
||||
}
|
||||
|
||||
static void add_stsd(m4a_header_t* h) {
|
||||
add_atom(h, "stsd", 0x67);
|
||||
add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */
|
||||
add_u32b(h, 1); /* Number of entries */
|
||||
add_mp4a(h);
|
||||
}
|
||||
|
||||
static void add_stbl(m4a_header_t* h) {
|
||||
m4a_state_t s;
|
||||
|
||||
save_atom(h, &s);
|
||||
add_atom(h, "stbl", 0x00);
|
||||
add_stsd(h); /* Sample description */
|
||||
add_stts(h); /* Time-to-sample */
|
||||
add_stsc(h); /* Sample-to-chunk */
|
||||
add_stsz(h); /* Sample size */
|
||||
add_stco(h); /* Chunk offset */
|
||||
load_atom(h, &s);
|
||||
}
|
||||
|
||||
static void add_dinf(m4a_header_t* h) {
|
||||
add_atom(h, "dinf", 0x24);
|
||||
add_atom(h, "dref", 0x1c);
|
||||
add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */
|
||||
add_u32b(h, 1); /* Number of entries */
|
||||
add_atom(h, "url ", 0x0c);
|
||||
add_u32b(h, 1); /* Version (1 byte) + Flags (3 byte) */
|
||||
}
|
||||
|
||||
static void add_smhd(m4a_header_t* h) {
|
||||
add_atom(h, "smhd", 0x10);
|
||||
add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */
|
||||
add_u16b(h, 0); /* Balance */
|
||||
add_u16b(h, 0); /* Reserved */
|
||||
}
|
||||
|
||||
static void add_minf(m4a_header_t* h) {
|
||||
m4a_state_t s;
|
||||
|
||||
save_atom(h, &s);
|
||||
add_atom(h, "minf", 0x00);
|
||||
add_smhd(h);
|
||||
add_dinf(h);
|
||||
add_stbl(h);
|
||||
load_atom(h, &s);
|
||||
}
|
||||
|
||||
static void add_hdlr(m4a_header_t* h) {
|
||||
add_atom(h, "hdlr", 0x22);
|
||||
add_u32b(h, 0); /* version (1 byte) + flags (3 byte) */
|
||||
add_u32b(h, 0); /* Component type */
|
||||
add_name(h, "soun"); /* Component subtype */
|
||||
add_u32b(h, 0); /* Component manufacturer */
|
||||
add_u32b(h, 0); /* Component flags */
|
||||
add_u32b(h, 0); /* Component flags mask */
|
||||
add_u16b(h, 0); /* Component name */
|
||||
}
|
||||
|
||||
static void add_mdhd(m4a_header_t* h) {
|
||||
add_atom(h, "mdhd", 0x20);
|
||||
add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */
|
||||
add_u32b(h, 0); /* Creation time */
|
||||
add_u32b(h, 0); /* Modification time */
|
||||
add_u32b(h, h->ktac->sample_rate); /* Time scale */
|
||||
add_u32b(h, h->ktac->num_samples); /* Duration */
|
||||
add_u16b(h, 0); /* Language (0xC455=eng?) */
|
||||
add_u16b(h, 0); /* Quality */
|
||||
}
|
||||
|
||||
static void add_mdia(m4a_header_t* h) {
|
||||
m4a_state_t s;
|
||||
|
||||
save_atom(h, &s);
|
||||
add_atom(h, "mdia", 0x00);
|
||||
add_mdhd(h);
|
||||
add_hdlr(h);
|
||||
add_minf(h);
|
||||
load_atom(h, &s);
|
||||
}
|
||||
|
||||
static void add_tkhd(m4a_header_t* h) {
|
||||
add_atom(h, "tkhd", 0x5C);
|
||||
add_u32b(h, 0x00000001); /* Version (1 byte) + Flags (3 byte), 1=track enabled */
|
||||
add_u32b(h, 0); /* Creation time */
|
||||
add_u32b(h, 0); /* Modification time */
|
||||
add_u32b(h, 1); /* Track ID */
|
||||
add_u32b(h, 0); /* Reserved 1 */
|
||||
add_u32b(h, h->ktac->num_samples); /* Duration */
|
||||
add_u32b(h, 0); /* Reserved 1 */
|
||||
add_u32b(h, 0); /* Reserved 2 */
|
||||
add_u16b(h, 0); /* Layer */
|
||||
add_u16b(h, 0); /* Alternate group (1?) */
|
||||
add_u16b(h, 0x0100); /* Volume */
|
||||
add_u16b(h, 0); /* Reserved */
|
||||
add_u32b(h, 0x00010000); /* matrix_A */
|
||||
add_u32b(h, 0); /* matrix_B */
|
||||
add_u32b(h, 0); /* matrix_U */
|
||||
add_u32b(h, 0); /* matrix_C */
|
||||
add_u32b(h, 0x00010000); /* matrix_D */
|
||||
add_u32b(h, 0); /* matrix_V */
|
||||
add_u32b(h, 0); /* matrix_X */
|
||||
add_u32b(h, 0); /* matrix_Y */
|
||||
add_u32b(h, 0x40000000); /* matrix_W */
|
||||
add_u32b(h, 0); /* Width */
|
||||
add_u32b(h, 0); /* Height */
|
||||
}
|
||||
|
||||
static void add_trak(m4a_header_t* h) {
|
||||
m4a_state_t s;
|
||||
|
||||
save_atom(h, &s);
|
||||
add_atom(h, "trak", 0x00);
|
||||
add_tkhd(h);
|
||||
add_mdia(h);
|
||||
load_atom(h, &s);
|
||||
}
|
||||
|
||||
static void add_mvhd(m4a_header_t* h) {
|
||||
add_atom(h, "mvhd", 0x6c);
|
||||
add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */
|
||||
add_u32b(h, 0); /* Creation time */
|
||||
add_u32b(h, 0); /* Modification time */
|
||||
add_u32b(h, h->ktac->sample_rate); /* Time scale */
|
||||
add_u32b(h, h->ktac->num_samples); /* Duration */
|
||||
add_u32b(h, 0x00010000); /* Preferred rate */
|
||||
add_u16b(h, 0x0100); /* Preferred volume */
|
||||
add_u32b(h, 0); /* Reserved 1 */
|
||||
add_u32b(h, 0); /* Reserved 2 */
|
||||
add_u16b(h, 0); /* Reserved 3 */
|
||||
add_u32b(h, 0x00010000); /* matrix_A */
|
||||
add_u32b(h, 0); /* matrix_B */
|
||||
add_u32b(h, 0); /* matrix_U */
|
||||
add_u32b(h, 0); /* matrix_C */
|
||||
add_u32b(h, 0x00010000); /* matrix_D */
|
||||
add_u32b(h, 0); /* matrix_V */
|
||||
add_u32b(h, 0); /* matrix_X */
|
||||
add_u32b(h, 0); /* matrix_Y */
|
||||
add_u32b(h, 0x40000000); /* matrix_W */
|
||||
add_u32b(h, 0); /* Preview time */
|
||||
add_u32b(h, 0); /* Preview duration */
|
||||
add_u32b(h, 0); /* Poster time */
|
||||
add_u32b(h, 0); /* Selection time */
|
||||
add_u32b(h, 0); /* Selection duration */
|
||||
add_u32b(h, 0); /* Current time */
|
||||
add_u32b(h, 2); /* Next track ID */
|
||||
}
|
||||
|
||||
static void add_moov(m4a_header_t* h) {
|
||||
m4a_state_t s;
|
||||
|
||||
save_atom(h, &s);
|
||||
add_atom(h, "moov", 0x00);
|
||||
add_mvhd(h);
|
||||
add_trak(h);
|
||||
//add_udta(h);
|
||||
load_atom(h, &s);
|
||||
}
|
||||
|
||||
/* *** */
|
||||
|
||||
static int make_m4a_header(uint8_t* buf, int buf_len, ktac_header_t* ktac, STREAMFILE* sf) {
|
||||
m4a_header_t h = {0};
|
||||
|
||||
if (buf_len < 0x300 + ktac->table_entries * 0x4) /* approx */
|
||||
goto fail;
|
||||
|
||||
h.sf = sf;
|
||||
h.ktac = ktac;
|
||||
h.out = buf;
|
||||
|
||||
add_ftyp(&h);
|
||||
add_free(&h);
|
||||
add_moov(&h);
|
||||
add_mdat(&h);
|
||||
|
||||
|
||||
/* define absolute chunk offset after all calcs */
|
||||
put_u32be(h.chunks.out, h.bytes);
|
||||
|
||||
return h.bytes;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
|
@ -851,7 +851,9 @@ VGMSTREAM * init_vgmstream_rad(STREAMFILE * streamFile);
|
|||
|
||||
VGMSTREAM * init_vgmstream_smk(STREAMFILE * streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_mzrt(STREAMFILE * streamFile);
|
||||
VGMSTREAM* init_vgmstream_mzrt_v0(STREAMFILE* sf);
|
||||
VGMSTREAM* init_vgmstream_mzrt_v1(STREAMFILE* sf);
|
||||
VGMSTREAM* init_vgmstream_bsnf(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM * init_vgmstream_xavs(STREAMFILE * streamFile);
|
||||
|
||||
|
@ -936,4 +938,10 @@ VGMSTREAM* init_vgmstream_ifs(STREAMFILE* sf);
|
|||
|
||||
VGMSTREAM* init_vgmstream_acx(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM* init_vgmstream_compresswave(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM* init_vgmstream_ktac(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM* init_vgmstream_mjb_mjh(STREAMFILE* sf);
|
||||
|
||||
#endif /*_META_H*/
|
||||
|
|
|
@ -2,27 +2,27 @@
|
|||
#include "../coding/coding.h"
|
||||
|
||||
/* MIB+MIH - SCEE MultiStream interleaved bank (header+data) [namCollection: Ace Combat 2 (PS2), Rampage: Total Destruction (PS2)] */
|
||||
VGMSTREAM * init_vgmstream_mib_mih(STREAMFILE *streamFile) {
|
||||
VGMSTREAM* init_vgmstream_mib_mih(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE * streamHeader = NULL;
|
||||
STREAMFILE* sh = NULL;
|
||||
off_t header_offset, start_offset;
|
||||
size_t data_size, frame_size, frame_last, frame_count, name_size;
|
||||
int channel_count, loop_flag, sample_rate;
|
||||
size_t data_size, frame_size, frame_last, frame_count;
|
||||
int channels, loop_flag, sample_rate;
|
||||
|
||||
/* check extension */
|
||||
if (!check_extensions(streamFile, "mib"))
|
||||
if (!check_extensions(sf, "mib"))
|
||||
goto fail;
|
||||
|
||||
streamHeader = open_streamfile_by_ext(streamFile,"mih");
|
||||
if (!streamHeader) goto fail;
|
||||
sh = open_streamfile_by_ext(sf,"mih");
|
||||
if (!sh) goto fail;
|
||||
|
||||
header_offset = 0x00;
|
||||
|
||||
if (read_32bitLE(0x00,streamHeader) != 0x40) { /* header size */
|
||||
name_size = read_32bitLE(0x00, streamHeader);
|
||||
if (read_32bitLE(0x04 + name_size, streamHeader) == 0x40 &&
|
||||
read_32bitLE(0x04 + name_size + 0x04, streamHeader) == 0x40) {
|
||||
/* Marc Ecko's Getting Up (PS2) has a name at the start */
|
||||
if (read_u32le(0x00,sh) != 0x40) { /* header size */
|
||||
/* Marc Ecko's Getting Up (PS2) has a name at the start (hack, not standard .mib+mih) */
|
||||
size_t name_size = read_u32le(0x00, sh);
|
||||
if (read_u32le(0x04 + name_size, sh) == 0x40 &&
|
||||
read_u32le(0x04 + name_size + 0x04, sh) == 0x40) {
|
||||
header_offset = 0x04 + name_size + 0x04;
|
||||
} else {
|
||||
goto fail;
|
||||
|
@ -33,40 +33,40 @@ VGMSTREAM * init_vgmstream_mib_mih(STREAMFILE *streamFile) {
|
|||
start_offset = 0x00;
|
||||
|
||||
/* 0x04: padding size (always 0x20, MIH header must be multiple of 0x40) */
|
||||
frame_last = (uint32_t)read_32bitLE(header_offset + 0x05,streamHeader) & 0x00FFFFFF; /* 24b */
|
||||
channel_count = read_32bitLE(header_offset + 0x08,streamHeader);
|
||||
sample_rate = read_32bitLE(header_offset + 0x0c,streamHeader);
|
||||
frame_size = read_32bitLE(header_offset + 0x10,streamHeader);
|
||||
frame_count = read_32bitLE(header_offset + 0x14,streamHeader);
|
||||
frame_last = read_u32le(header_offset + 0x05,sh) & 0x00FFFFFF; /* 24b */
|
||||
channels = read_u32le(header_offset + 0x08,sh);
|
||||
sample_rate = read_u32le(header_offset + 0x0c,sh);
|
||||
frame_size = read_u32le(header_offset + 0x10,sh);
|
||||
frame_count = read_u32le(header_offset + 0x14,sh);
|
||||
if (frame_count == 0) { /* rarely [Gladius (PS2)] */
|
||||
frame_count = get_streamfile_size(streamFile) / (frame_size * channel_count);
|
||||
frame_count = get_streamfile_size(sf) / (frame_size * channels);
|
||||
}
|
||||
|
||||
data_size = frame_count * frame_size;
|
||||
if (frame_last)
|
||||
data_size -= frame_size - frame_last; /* last frame has less usable data */
|
||||
data_size *= channel_count;
|
||||
data_size *= channels;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_MIB_MIH;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count);
|
||||
vgmstream->num_samples = ps_bytes_to_samples(data_size, channels);
|
||||
|
||||
vgmstream->meta_type = meta_PS2_MIB_MIH;
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = frame_size;
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
close_streamfile(streamHeader);
|
||||
close_streamfile(sh);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(streamHeader);
|
||||
close_streamfile(sh);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* MJB+MJH - SCEE MultiStream? bank of MIB+MIH [Star Wars: Bounty Hunter (PS2)] */
|
||||
VGMSTREAM* init_vgmstream_mjb_mjh(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE* sh = NULL;
|
||||
off_t start_offset = 0, header_offset = 0;
|
||||
size_t data_size, frame_size, frame_count;
|
||||
int channels, loop_flag, sample_rate;
|
||||
int total_subsongs, target_subsong = sf->stream_index;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf, "mjb"))
|
||||
goto fail;
|
||||
|
||||
sh = open_streamfile_by_ext(sf, "mjh");
|
||||
if (!sh) goto fail;
|
||||
|
||||
/* parse entries */
|
||||
{
|
||||
int i;
|
||||
off_t subsong_offset = 0;
|
||||
|
||||
total_subsongs = read_s32le(0x00,sh);
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
|
||||
|
||||
for (i = 0; i < total_subsongs; i++) {
|
||||
off_t offset = 0x40 + 0x40 * i;
|
||||
size_t subsong_size = read_u32le(offset + 0x08,sh) * read_u32le(offset + 0x10,sh) * read_u32le(offset + 0x14,sh);
|
||||
|
||||
if (i + 1== target_subsong) {
|
||||
header_offset = offset;
|
||||
start_offset = subsong_offset;
|
||||
}
|
||||
subsong_offset += subsong_size;
|
||||
}
|
||||
|
||||
if (!header_offset)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
VGM_LOG("3: %lx\n", start_offset);
|
||||
|
||||
/* also see MIB+MIH (same thing but this excludes padding stuff) */
|
||||
if (read_u32le(header_offset + 0x00,sh) != 0x40)
|
||||
goto fail;
|
||||
channels = read_u32le(header_offset + 0x08,sh);
|
||||
sample_rate = read_u32le(header_offset + 0x0c,sh);
|
||||
frame_size = read_u32le(header_offset + 0x10,sh);
|
||||
frame_count = read_u32le(header_offset + 0x14,sh);
|
||||
|
||||
data_size = frame_count * frame_size * channels;
|
||||
loop_flag = 0;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_MJB_MJH;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = ps_bytes_to_samples(data_size, channels);
|
||||
|
||||
vgmstream->num_streams = total_subsongs;
|
||||
vgmstream->stream_size = data_size;
|
||||
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = frame_size;
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
close_streamfile(sh);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(sh);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
|
@ -2,36 +2,36 @@
|
|||
#include "../coding/coding.h"
|
||||
|
||||
/* MSB+MSH - SCEE MultiStream flat bank [namCollection: Ace Combat 2 (PS2) sfx, EyeToy Play (PS2)] */
|
||||
VGMSTREAM * init_vgmstream_msb_msh(STREAMFILE *streamFile) {
|
||||
VGMSTREAM* init_vgmstream_msb_msh(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE * streamHeader = NULL;
|
||||
STREAMFILE* sh = NULL;
|
||||
off_t start_offset, header_offset = 0;
|
||||
size_t stream_size;
|
||||
int loop_flag = 0, channel_count, sample_rate;
|
||||
int total_subsongs, target_subsong = streamFile->stream_index;
|
||||
int loop_flag, channels, sample_rate;
|
||||
int total_subsongs, target_subsong = sf->stream_index;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile, "msb"))
|
||||
if (!check_extensions(sf, "msb"))
|
||||
goto fail;
|
||||
|
||||
streamHeader = open_streamfile_by_ext(streamFile, "msh");
|
||||
if (!streamHeader) goto fail;
|
||||
sh = open_streamfile_by_ext(sf, "msh");
|
||||
if (!sh) goto fail;
|
||||
|
||||
if (read_32bitLE(0x00,streamHeader) != get_streamfile_size(streamHeader))
|
||||
if (read_u32le(0x00,sh) != get_streamfile_size(sh))
|
||||
goto fail;
|
||||
/* 0x04: unknown */
|
||||
|
||||
/* parse entries */
|
||||
{
|
||||
int i;
|
||||
int entries = read_32bitLE(0x08,streamHeader);
|
||||
int entries = read_s32le(0x08,sh);
|
||||
|
||||
total_subsongs = 0;
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
|
||||
for (i = 0; i < entries; i++) {
|
||||
if (read_32bitLE(0x0c + 0x10*i, streamHeader) == 0) /* size 0 = empty entry */
|
||||
if (read_u32le(0x0c + 0x10*i, sh) == 0) /* size 0 = empty entry */
|
||||
continue;
|
||||
|
||||
total_subsongs++;
|
||||
|
@ -45,41 +45,38 @@ VGMSTREAM * init_vgmstream_msb_msh(STREAMFILE *streamFile) {
|
|||
}
|
||||
|
||||
|
||||
|
||||
loop_flag = 0;
|
||||
channel_count = 1;
|
||||
channels = 1;
|
||||
|
||||
stream_size = read_32bitLE(header_offset+0x00, streamHeader);
|
||||
if (read_32bitLE(header_offset+0x04, streamHeader) != 0) /* stereo flag? */
|
||||
stream_size = read_u32le(header_offset+0x00, sh);
|
||||
if (read_u32le(header_offset+0x04, sh) != 0) /* stereo flag? */
|
||||
goto fail;
|
||||
start_offset = read_32bitLE(header_offset+0x08, streamHeader);
|
||||
sample_rate = read_32bitLE(header_offset+0x0c, streamHeader); /* Ace Combat 2 seems to set wrong values but probably their bug */
|
||||
start_offset = read_u32le(header_offset+0x08, sh);
|
||||
sample_rate = read_u32le(header_offset+0x0c, sh); /* Ace Combat 2 seems to set wrong values but probably their bug */
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_MSB_MSH;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = ps_bytes_to_samples(stream_size,channel_count);
|
||||
vgmstream->num_samples = ps_bytes_to_samples(stream_size, channels);
|
||||
|
||||
vgmstream->num_streams = total_subsongs;
|
||||
vgmstream->stream_size = stream_size;
|
||||
vgmstream->meta_type = meta_MSB_MSH;
|
||||
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x10;
|
||||
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
|
||||
close_streamfile(streamHeader);
|
||||
close_streamfile(sh);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(streamHeader);
|
||||
close_streamfile(sh);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#include "../coding/coding.h"
|
||||
|
||||
typedef enum { MFX, MFX_BANK, SFX_BANK, SBNK, FORM } musx_form;
|
||||
typedef enum { PSX, DSP, XBOX, IMA, DAT } musx_codec;
|
||||
typedef enum { PSX, DSP, XBOX, IMA, DAT, NGCA, PCM } musx_codec;
|
||||
typedef struct {
|
||||
int big_endian;
|
||||
int version;
|
||||
|
@ -31,10 +31,10 @@ typedef struct {
|
|||
int32_t loop_end_sample;
|
||||
} musx_header;
|
||||
|
||||
static int parse_musx(STREAMFILE *streamFile, musx_header *musx);
|
||||
static int parse_musx(STREAMFILE* sf, musx_header* musx);
|
||||
|
||||
/* MUSX - from Eurocom's games */
|
||||
VGMSTREAM * init_vgmstream_musx(STREAMFILE *streamFile) {
|
||||
VGMSTREAM* init_vgmstream_musx(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
musx_header musx = {0};
|
||||
|
@ -43,12 +43,12 @@ VGMSTREAM * init_vgmstream_musx(STREAMFILE *streamFile) {
|
|||
/* checks */
|
||||
/* .sfx: actual extension (extracted from bigfiles with sometimes encrypted names)
|
||||
* .musx: header id */
|
||||
if (!check_extensions(streamFile, "sfx,musx"))
|
||||
if (!check_extensions(sf, "sfx,musx"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00,streamFile) != 0x4D555358) /* "MUSX" */
|
||||
if (!is_id32be(0x00,sf, "MUSX"))
|
||||
goto fail;
|
||||
|
||||
if (!parse_musx(streamFile, &musx))
|
||||
if (!parse_musx(sf, &musx))
|
||||
goto fail;
|
||||
|
||||
|
||||
|
@ -112,8 +112,41 @@ VGMSTREAM * init_vgmstream_musx(STREAMFILE *streamFile) {
|
|||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x08;
|
||||
|
||||
dsp_read_coefs(vgmstream,streamFile,musx.coefs_offset+0x1c, 0x60, musx.big_endian);
|
||||
dsp_read_hist(vgmstream,streamFile,musx.coefs_offset+0x40, 0x60, musx.big_endian);
|
||||
dsp_read_coefs(vgmstream,sf,musx.coefs_offset+0x1c, 0x60, musx.big_endian);
|
||||
dsp_read_hist(vgmstream,sf,musx.coefs_offset+0x40, 0x60, musx.big_endian);
|
||||
break;
|
||||
|
||||
case PCM:
|
||||
//vgmstream->num_samples = pcm_bytes_to_samples(musx.stream_size, musx.channels, 16);
|
||||
//vgmstream->loop_start_sample = pcm_bytes_to_samples(musx.loop_start, musx.channels, 16);
|
||||
//vgmstream->loop_end_sample = pcm_bytes_to_samples(musx.loop_end, musx.channels, 16);
|
||||
|
||||
vgmstream->coding_type = musx.big_endian ? coding_PCM16BE : coding_PCM16LE; /* only seen on PC but just in case */
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x02;
|
||||
break;
|
||||
|
||||
case NGCA:
|
||||
if (!is_id32be(start_offset,sf, "NGCA"))
|
||||
goto fail;
|
||||
/* 0x04: size (stereo full-interleaves 2 NGCA headers but sizes count all) */
|
||||
/* 0x08: channels */
|
||||
/* 0x0c: coefs */
|
||||
musx.coefs_offset = start_offset;
|
||||
start_offset += 0x40;
|
||||
//musx.stream_size -= 0x40 * musx.channels; /* needed for interleave */
|
||||
|
||||
//vgmstream->num_samples = dsp_bytes_to_samples(musx.stream_size - 0x40 * musx.channels, musx.channels);
|
||||
//vgmstream->loop_start_sample = dsp_bytes_to_samples(musx.loop_start + 0x40, musx.channels);
|
||||
//vgmstream->loop_end_sample = dsp_bytes_to_samples(musx.loop_end - 0x40 * musx.channels, musx.channels);
|
||||
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = musx.stream_size / musx.channels;
|
||||
|
||||
dsp_read_coefs(vgmstream, sf, musx.coefs_offset+0x0c, vgmstream->interleave_block_size, musx.big_endian);
|
||||
//dsp_read_hist(vgmstream, sf, musx.coefs_offset+0x30, 0x00, musx.big_endian); /* used? */
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -127,7 +160,7 @@ VGMSTREAM * init_vgmstream_musx(STREAMFILE *streamFile) {
|
|||
if (musx.loop_flag && musx.loop_end_sample)
|
||||
vgmstream->loop_end_sample = musx.loop_end_sample;
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
if (!vgmstream_open_stream(vgmstream,sf,start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
|
@ -137,21 +170,21 @@ fail:
|
|||
}
|
||||
|
||||
|
||||
static int parse_musx_stream(STREAMFILE *streamFile, musx_header *musx) {
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
|
||||
static int parse_musx_stream(STREAMFILE* sf, musx_header* musx) {
|
||||
uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL;
|
||||
int default_channels, default_sample_rate;
|
||||
|
||||
if (musx->big_endian) {
|
||||
read_32bit = read_32bitBE;
|
||||
read_u32 = read_u32be;
|
||||
} else {
|
||||
read_32bit = read_32bitLE;
|
||||
read_u32 = read_u32le;
|
||||
}
|
||||
|
||||
|
||||
/* autodetect for older versions that have no info */
|
||||
if (musx->platform == 0) {
|
||||
if (musx->big_endian) {
|
||||
musx->platform = 0x47433032; /* "GC02" (fake) */
|
||||
musx->platform = get_id32be("GC02"); /* (fake) */
|
||||
}
|
||||
else {
|
||||
off_t offset = musx->stream_offset;
|
||||
|
@ -159,10 +192,10 @@ static int parse_musx_stream(STREAMFILE *streamFile, musx_header *musx) {
|
|||
if (max > musx->stream_size)
|
||||
max = musx->stream_size;
|
||||
|
||||
if (ps_check_format(streamFile, offset, max)) {
|
||||
musx->platform = 0x5053325F; /* "PS2_" */
|
||||
if (ps_check_format(sf, offset, max)) {
|
||||
musx->platform = get_id32be("PS2_");
|
||||
} else {
|
||||
musx->platform = 0x58423032; /* "XB02" (fake) */
|
||||
musx->platform = get_id32be("XB02"); /* (fake) */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -258,19 +291,19 @@ static int parse_musx_stream(STREAMFILE *streamFile, musx_header *musx) {
|
|||
* 0x10: volume? (usually <= 100) */
|
||||
|
||||
/* find loops (cues1 also seems to have this info but this looks ok) */
|
||||
cues2_count = read_32bit(musx->loops_offset+0x04, streamFile);
|
||||
cues2_offset = musx->loops_offset + read_32bit(musx->loops_offset+0x0c, streamFile);
|
||||
cues2_count = read_u32(musx->loops_offset+0x04, sf);
|
||||
cues2_offset = musx->loops_offset + read_u32(musx->loops_offset+0x0c, sf);
|
||||
for (i = 0; i < cues2_count; i++) {
|
||||
uint32_t type, offset1, offset2;
|
||||
|
||||
if (musx->is_old) {
|
||||
offset1 = read_32bit(cues2_offset + i*0x20 + 0x04, streamFile);
|
||||
type = read_32bit(cues2_offset + i*0x20 + 0x08, streamFile);
|
||||
offset2 = read_32bit(cues2_offset + i*0x20 + 0x14, streamFile);
|
||||
offset1 = read_u32(cues2_offset + i*0x20 + 0x04, sf);
|
||||
type = read_u32(cues2_offset + i*0x20 + 0x08, sf);
|
||||
offset2 = read_u32(cues2_offset + i*0x20 + 0x14, sf);
|
||||
} else {
|
||||
offset1 = read_32bit(cues2_offset + i*0x14 + 0x04, streamFile);
|
||||
type = read_32bit(cues2_offset + i*0x14 + 0x08, streamFile);
|
||||
offset2 = read_32bit(cues2_offset + i*0x14 + 0x0c, streamFile);
|
||||
offset1 = read_u32(cues2_offset + i*0x14 + 0x04, sf);
|
||||
type = read_u32(cues2_offset + i*0x14 + 0x08, sf);
|
||||
offset2 = read_u32(cues2_offset + i*0x14 + 0x0c, sf);
|
||||
}
|
||||
|
||||
/* other types (0x0a, 0x09) look like section/end markers, 0x06/07 only seems to exist once */
|
||||
|
@ -282,7 +315,7 @@ static int parse_musx_stream(STREAMFILE *streamFile, musx_header *musx) {
|
|||
}
|
||||
}
|
||||
}
|
||||
else if (musx->loops_offset && read_32bitBE(musx->loops_offset, streamFile) != 0xABABABAB) {
|
||||
else if (musx->loops_offset && read_u32be(musx->loops_offset, sf) != 0xABABABAB) {
|
||||
/* parse loop table (loop starts are -1 if non-looping)
|
||||
* 0x00: version?
|
||||
* 0x04: flags? (&1=loops)
|
||||
|
@ -292,10 +325,10 @@ static int parse_musx_stream(STREAMFILE *streamFile, musx_header *musx) {
|
|||
* 0x14: loop start sample
|
||||
* 0x18: loop end offset
|
||||
* 0x1c: loop start offset */
|
||||
musx->loop_end_sample = read_32bitLE(musx->loops_offset+0x10, streamFile);
|
||||
musx->loop_start_sample = read_32bitLE(musx->loops_offset+0x14, streamFile);
|
||||
musx->loop_end = read_32bitLE(musx->loops_offset+0x18, streamFile);
|
||||
musx->loop_start = read_32bitLE(musx->loops_offset+0x1c, streamFile);
|
||||
musx->loop_end_sample = read_s32le(musx->loops_offset+0x10, sf);
|
||||
musx->loop_start_sample = read_s32le(musx->loops_offset+0x14, sf);
|
||||
musx->loop_end = read_s32le(musx->loops_offset+0x18, sf);
|
||||
musx->loop_start = read_s32le(musx->loops_offset+0x1c, sf);
|
||||
musx->num_samples = musx->loop_end_sample; /* preferable even if not looping as some files have padding */
|
||||
musx->loop_flag = (musx->loop_start_sample >= 0);
|
||||
}
|
||||
|
@ -310,7 +343,7 @@ static int parse_musx_stream(STREAMFILE *streamFile, musx_header *musx) {
|
|||
int pos;
|
||||
off_t offset = musx->stream_offset + musx->stream_size - 0x800;
|
||||
|
||||
if (read_streamfile(buf, offset, sizeof(buf), streamFile) != 0x800)
|
||||
if (read_streamfile(buf, offset, sizeof(buf), sf) != 0x800)
|
||||
goto fail;
|
||||
|
||||
pos = 0x800 - 0x04;
|
||||
|
@ -328,58 +361,66 @@ fail:
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int parse_musx(STREAMFILE *streamFile, musx_header *musx) {
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
|
||||
int target_subsong = streamFile->stream_index;
|
||||
static int parse_musx(STREAMFILE* sf, musx_header* musx) {
|
||||
int32_t (*read_s32)(off_t,STREAMFILE*) = NULL;
|
||||
uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL;
|
||||
uint16_t (*read_u16)(off_t,STREAMFILE*) = NULL;
|
||||
int target_subsong = sf->stream_index;
|
||||
|
||||
|
||||
/* base header is LE */
|
||||
/* 0x04: file ID (referenced in external .h, sometimes files are named HC(id).sfx too) */
|
||||
musx->version = read_32bitLE(0x08,streamFile);
|
||||
musx->file_size = read_32bitLE(0x0c,streamFile);
|
||||
|
||||
/* 0x04: file ID (referenced in external .h, sometimes files are named [prefix](id)[suffix].sfx too) */
|
||||
musx->version = read_u32le(0x08,sf);
|
||||
musx->file_size = read_u32le(0x0c,sf);
|
||||
|
||||
switch(musx->version) {
|
||||
case 201: /* Sphinx and the Cursed Mummy (PS2), Buffy the Vampire Slayer: Chaos Bleeds (PS2/GC/Xbox) */
|
||||
case 1: /* Athens 2004 (PS2) */
|
||||
musx->platform = 0; /* guess later */
|
||||
musx->tables_offset = 0x10;
|
||||
musx->big_endian = guess_endianness32bit(0x10, streamFile);
|
||||
musx->big_endian = guess_endianness32bit(0x10, sf);
|
||||
musx->is_old = 1;
|
||||
break;
|
||||
|
||||
case 4: /* Spyro: A Hero's Tail (PS2/Xbox/GC), Athens 2004 (PC) */
|
||||
case 5: /* Predator: Concrete Jungle (PS2/Xbox), Robots (GC) */
|
||||
case 6: /* Batman Begins (GC), Ice Age 2 (PS2/PC) */
|
||||
musx->platform = read_32bitBE(0x10,streamFile);
|
||||
musx->platform = read_u32be(0x10,sf);
|
||||
/* 0x14: some id/hash? */
|
||||
/* 0x18: platform number? */
|
||||
/* 0x1c: null */
|
||||
musx->tables_offset = 0x20;
|
||||
musx->big_endian = (musx->platform == 0x47435F5F); /* "GC__" */
|
||||
musx->big_endian = (musx->platform == get_id32be("GC__"));
|
||||
break;
|
||||
|
||||
case 10: /* 007: Quantum of Solace (PS2), Pirates of the Caribbean: At World's End (PSP), GoldenEye 007 (Wii), Rio (PS3) */
|
||||
musx->platform = read_32bitBE(0x10,streamFile);
|
||||
musx->platform = read_u32be(0x10,sf);
|
||||
/* 0x14: null */
|
||||
/* 0x18: platform number? */
|
||||
/* 0x1c: null */
|
||||
/* 0x20: hash */
|
||||
musx->tables_offset = 0; /* no tables */
|
||||
musx->big_endian = (musx->platform == 0x47435F5F || /* "GC__" */
|
||||
musx->platform == 0x58455F5F || /* "XE__" */
|
||||
musx->platform == 0x5053335F || /* "PS3_" */
|
||||
musx->platform == 0x5749495F); /* "WII_" */
|
||||
musx->big_endian = (
|
||||
musx->platform == get_id32be("GC__") ||
|
||||
musx->platform == get_id32be("XE__") ||
|
||||
musx->platform == get_id32be("PS3_") ||
|
||||
musx->platform == get_id32be("WII_")
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
VGM_LOG("MUSX: unknown version\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (musx->big_endian) {
|
||||
read_32bit = read_32bitBE;
|
||||
read_s32 = read_s32be;
|
||||
read_u32 = read_u32be;
|
||||
read_u16 = read_u16be;
|
||||
} else {
|
||||
read_32bit = read_32bitLE;
|
||||
read_s32 = read_s32le;
|
||||
read_u32 = read_u32le;
|
||||
read_u16 = read_u16le;
|
||||
}
|
||||
|
||||
|
||||
|
@ -388,53 +429,56 @@ static int parse_musx(STREAMFILE *streamFile, musx_header *musx) {
|
|||
off_t table1_offset, table2_offset /*, table3_offset, table4_offset*/;
|
||||
size_t /*table1_size, table2_size, table3_size,*/ table4_size;
|
||||
|
||||
table1_offset = read_32bit(musx->tables_offset+0x00, streamFile);
|
||||
//table1_size = read_32bit(musx->tables_offset+0x04, streamFile);
|
||||
table2_offset = read_32bit(musx->tables_offset+0x08, streamFile);
|
||||
//table2_size = read_32bit(musx->tables_offset+0x0c, streamFile);
|
||||
//table3_offset = read_32bit(musx->tables_offset+0x10, streamFile);
|
||||
//table3_size = read_32bit(musx->tables_offset+0x14, streamFile);
|
||||
//table4_offset = read_32bit(musx->tables_offset+0x18, streamFile);
|
||||
table4_size = read_32bit(musx->tables_offset+0x1c, streamFile);
|
||||
table1_offset = read_u32(musx->tables_offset+0x00, sf);
|
||||
//table1_size = read_u32(musx->tables_offset+0x04, sf);
|
||||
table2_offset = read_u32(musx->tables_offset+0x08, sf);
|
||||
//table2_size = read_u32(musx->tables_offset+0x0c, sf);
|
||||
//table3_offset = read_u32(musx->tables_offset+0x10, sf);
|
||||
//table3_size = read_u32(musx->tables_offset+0x14, sf);
|
||||
//table4_offset = read_u32(musx->tables_offset+0x18, sf);
|
||||
table4_size = read_u32(musx->tables_offset+0x1c, sf);
|
||||
|
||||
if (table2_offset == 0 || table2_offset == 0xABABABAB) {
|
||||
/* possibly a collection of IDs */
|
||||
VGM_LOG("MUSX: unsupported ID type\n");
|
||||
goto fail;
|
||||
}
|
||||
else if (table4_size != 0 && table4_size != 0xABABABAB) {
|
||||
/* sfx banks have table1=id table? table2=header table, table=DSP coefs table (optional), table4=data */
|
||||
musx->form = SFX_BANK;
|
||||
}
|
||||
else if (read_32bit(table1_offset+0x00, streamFile) < 0x9 &&
|
||||
read_32bit(table1_offset+0x08, streamFile) == 0x14 &&
|
||||
read_32bit(table1_offset+0x10, streamFile) <= 100) {
|
||||
else if (read_u32(table1_offset+0x00, sf) < 0x9 &&
|
||||
read_u32(table1_offset+0x08, sf) == 0x14 &&
|
||||
read_u32(table1_offset+0x10, sf) <= 100) {
|
||||
/* streams have a info table with a certain format */
|
||||
musx->form = MFX;
|
||||
}
|
||||
else if (read_32bit(table1_offset+0x00, streamFile) == 0 &&
|
||||
read_32bit(table1_offset+0x04, streamFile) > read_32bit(table1_offset+0x00, streamFile) &&
|
||||
read_32bit(table1_offset+0x08, streamFile) > read_32bit(table1_offset+0x04, streamFile)) {
|
||||
else if (read_u32(table1_offset+0x00, sf) == 0 &&
|
||||
read_u32(table1_offset+0x04, sf) > read_u32(table1_offset+0x00, sf) &&
|
||||
read_u32(table1_offset+0x08, sf) > read_u32(table1_offset+0x04, sf)) {
|
||||
/* a list of offsets starting from 0, each stream then has info table at offset */
|
||||
musx->form = MFX_BANK;
|
||||
}
|
||||
else {
|
||||
VGM_LOG("MUSX: unsupported unknown type\n");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (read_32bitBE(0x800,streamFile) == 0x53424E4B) { /* "SBNK" */
|
||||
/* SFX_BANK-like sound bank found on v10 Wii games */
|
||||
if (is_id32be(0x800,sf, "SBNK")) {
|
||||
/* SFX_BANK-like sound bank found on v10 Wii/PC games [Dead Space Extraction (Wii), G-Force (PC)] */
|
||||
musx->form = SBNK;
|
||||
}
|
||||
else if (read_32bitBE(0x800,streamFile) == 0x464F524D) { /* "FORM" */
|
||||
else if (is_id32be(0x800,sf, "FORM")) {
|
||||
/* RIFF-like sound bank found on v10 PSP games */
|
||||
musx->form = FORM;
|
||||
}
|
||||
else if (read_32bitBE(0x800,streamFile) == 0x45535044) { /* "ESPD" */
|
||||
else if (is_id32be(0x800,sf, "ESPD")) {
|
||||
/* projectdetails.sfx */
|
||||
goto fail;
|
||||
}
|
||||
else {
|
||||
/* simplest music type*/
|
||||
musx->form = MFX;
|
||||
}
|
||||
}
|
||||
|
@ -445,27 +489,27 @@ static int parse_musx(STREAMFILE *streamFile, musx_header *musx) {
|
|||
case MFX:
|
||||
|
||||
if (musx->tables_offset) {
|
||||
musx->loops_offset = read_32bit(musx->tables_offset+0x00, streamFile);
|
||||
musx->loops_offset = read_u32(musx->tables_offset+0x00, sf);
|
||||
|
||||
musx->stream_offset = read_32bit(musx->tables_offset+0x08, streamFile);
|
||||
musx->stream_size = read_32bit(musx->tables_offset+0x0c, streamFile);
|
||||
musx->stream_offset = read_u32(musx->tables_offset+0x08, sf);
|
||||
musx->stream_size = read_u32(musx->tables_offset+0x0c, sf);
|
||||
}
|
||||
else {
|
||||
if (read_32bitBE(0x30, streamFile) != 0xABABABAB) {
|
||||
uint32_t miniheader = read_32bitBE(0x40, streamFile);
|
||||
if (read_u32be(0x30, sf) != 0xABABABAB) {
|
||||
uint32_t miniheader = read_u32be(0x40, sf);
|
||||
switch(miniheader) {
|
||||
case 0x44415434: /* "DAT4" */
|
||||
case 0x44415435: /* "DAT5" */
|
||||
case 0x44415438: /* "DAT8" */
|
||||
/* found on PS3/Wii (but not always?) */
|
||||
musx->stream_size = read_32bitLE(0x44, streamFile);
|
||||
musx->channels = read_32bitLE(0x48, streamFile);
|
||||
musx->sample_rate = read_32bitLE(0x4c, streamFile);
|
||||
musx->stream_size = read_u32le(0x44, sf);
|
||||
musx->channels = read_u32le(0x48, sf);
|
||||
musx->sample_rate = read_u32le(0x4c, sf);
|
||||
musx->loops_offset = 0x50;
|
||||
break;
|
||||
default:
|
||||
if (read_32bitBE(0x30, streamFile) == 0x00 &&
|
||||
read_32bitBE(0x34, streamFile) == 0x00) {
|
||||
if (read_u32be(0x30, sf) == 0x00 &&
|
||||
read_u32be(0x34, sf) == 0x00) {
|
||||
/* no subheader [Spider-Man 4 (Wii)] */
|
||||
musx->loops_offset = 0x00;
|
||||
} else {
|
||||
|
@ -480,32 +524,32 @@ static int parse_musx(STREAMFILE *streamFile, musx_header *musx) {
|
|||
musx->stream_size = 0; /* read later */
|
||||
}
|
||||
|
||||
if (!parse_musx_stream(streamFile, musx))
|
||||
if (!parse_musx_stream(sf, musx))
|
||||
goto fail;
|
||||
break;
|
||||
|
||||
case MFX_BANK: {
|
||||
off_t target_offset, base_offset, data_offset;
|
||||
|
||||
musx->total_subsongs = read_32bit(musx->tables_offset+0x04, streamFile) / 0x04; /* size */
|
||||
musx->total_subsongs = read_u32(musx->tables_offset+0x04, sf) / 0x04; /* size */
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
if (target_subsong > musx->total_subsongs || musx->total_subsongs <= 0) goto fail;
|
||||
|
||||
base_offset = read_32bit(musx->tables_offset+0x00, streamFile);
|
||||
data_offset = read_32bit(musx->tables_offset+0x08, streamFile);
|
||||
base_offset = read_u32(musx->tables_offset+0x00, sf);
|
||||
data_offset = read_u32(musx->tables_offset+0x08, sf);
|
||||
|
||||
target_offset = read_32bit(base_offset + (target_subsong - 1)*0x04, streamFile) + data_offset;
|
||||
target_offset = read_u32(base_offset + (target_subsong - 1)*0x04, sf) + data_offset;
|
||||
|
||||
/* 0x00: id? */
|
||||
musx->stream_offset = read_32bit(target_offset + 0x04, streamFile) + data_offset;
|
||||
musx->stream_size = read_32bit(target_offset + 0x08, streamFile);
|
||||
musx->stream_offset = read_u32(target_offset + 0x04, sf) + data_offset;
|
||||
musx->stream_size = read_u32(target_offset + 0x08, sf);
|
||||
musx->loops_offset = target_offset + 0x0c;
|
||||
|
||||
/* this looks correct for PS2 and Xbox, not sure about GC */
|
||||
musx->channels = 1;
|
||||
musx->sample_rate = 22050;
|
||||
|
||||
if (!parse_musx_stream(streamFile, musx))
|
||||
if (!parse_musx_stream(sf, musx))
|
||||
goto fail;
|
||||
break;
|
||||
}
|
||||
|
@ -514,13 +558,13 @@ static int parse_musx(STREAMFILE *streamFile, musx_header *musx) {
|
|||
off_t target_offset, head_offset, coef_offset, data_offset;
|
||||
size_t coef_size;
|
||||
|
||||
//unk_offset = read_32bit(musx->tables_offset+0x00, streamFile); /* ids and cue-like config? */
|
||||
head_offset = read_32bit(musx->tables_offset+0x08, streamFile);
|
||||
coef_offset = read_32bit(musx->tables_offset+0x10, streamFile);
|
||||
coef_size = read_32bit(musx->tables_offset+0x14, streamFile);
|
||||
data_offset = read_32bit(musx->tables_offset+0x18, streamFile);
|
||||
//unk_offset = read_u32(musx->tables_offset+0x00, sf); /* ids and cue-like config? */
|
||||
head_offset = read_u32(musx->tables_offset+0x08, sf);
|
||||
coef_offset = read_u32(musx->tables_offset+0x10, sf);
|
||||
coef_size = read_u32(musx->tables_offset+0x14, sf);
|
||||
data_offset = read_u32(musx->tables_offset+0x18, sf);
|
||||
|
||||
musx->total_subsongs = read_32bit(head_offset+0x00, streamFile);
|
||||
musx->total_subsongs = read_u32(head_offset+0x00, sf);
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
if (target_subsong > musx->total_subsongs || musx->total_subsongs <= 0) goto fail;
|
||||
|
||||
|
@ -529,13 +573,13 @@ static int parse_musx(STREAMFILE *streamFile, musx_header *musx) {
|
|||
target_offset = head_offset + 0x04 + (target_subsong - 1)*0x28;
|
||||
|
||||
/* 0x00: flag? */
|
||||
musx->stream_offset = read_32bit(target_offset + 0x04, streamFile) + data_offset;
|
||||
musx->stream_size = read_32bit(target_offset + 0x08, streamFile);
|
||||
musx->sample_rate = read_32bit(target_offset + 0x0c, streamFile);
|
||||
musx->stream_offset = read_u32(target_offset + 0x04, sf) + data_offset;
|
||||
musx->stream_size = read_u32(target_offset + 0x08, sf);
|
||||
musx->sample_rate = read_u32(target_offset + 0x0c, sf);
|
||||
/* 0x10: size? */
|
||||
/* 0x14: channels? */
|
||||
/* 0x18: always 4? */
|
||||
musx->coefs_offset = read_32bit(target_offset + 0x1c, streamFile) + coef_offset; /* may be set even for non-GC */
|
||||
musx->coefs_offset = read_u32(target_offset + 0x1c, sf) + coef_offset; /* may be set even for non-GC */
|
||||
/* 0x20: null? */
|
||||
/* 0x24: sub-id? */
|
||||
musx->channels = 1;
|
||||
|
@ -544,11 +588,11 @@ static int parse_musx(STREAMFILE *streamFile, musx_header *musx) {
|
|||
target_offset = head_offset + 0x04 + (target_subsong - 1)*0x20;
|
||||
|
||||
/* 0x00: flag? */
|
||||
musx->stream_offset = read_32bit(target_offset + 0x04, streamFile) + data_offset;
|
||||
musx->stream_size = read_32bit(target_offset + 0x08, streamFile);
|
||||
musx->sample_rate = read_32bit(target_offset + 0x0c, streamFile);
|
||||
musx->stream_offset = read_u32(target_offset + 0x04, sf) + data_offset;
|
||||
musx->stream_size = read_u32(target_offset + 0x08, sf);
|
||||
musx->sample_rate = read_u32(target_offset + 0x0c, sf);
|
||||
/* 0x10: size? */
|
||||
musx->coefs_offset = read_32bit(target_offset + 0x14, streamFile) + coef_offset; /* may be set even for non-GC */
|
||||
musx->coefs_offset = read_u32(target_offset + 0x14, sf) + coef_offset; /* may be set even for non-GC */
|
||||
/* 0x18: null? */
|
||||
/* 0x1c: sub-id? */
|
||||
musx->channels = 1;
|
||||
|
@ -557,13 +601,81 @@ static int parse_musx(STREAMFILE *streamFile, musx_header *musx) {
|
|||
if (coef_size == 0)
|
||||
musx->coefs_offset = 0; /* disable for later detection */
|
||||
|
||||
if (!parse_musx_stream(streamFile, musx))
|
||||
if (!parse_musx_stream(sf, musx))
|
||||
goto fail;
|
||||
break;
|
||||
}
|
||||
|
||||
case SBNK: {
|
||||
off_t target_offset, head_offset, data_offset;
|
||||
uint8_t codec;
|
||||
|
||||
/* 0x00: id */
|
||||
/* 0x04: always 0x12? */
|
||||
/* 0x08: file ID (same as base file) */
|
||||
/* 0x0c: some ID? */
|
||||
|
||||
/* 0x10: table1 count */
|
||||
/* 0x14: table1 offset (from here) */
|
||||
/* 0x18: table2 count */
|
||||
/* 0x1c: table2 offset (from here) */
|
||||
|
||||
/* 0x20: table3 count */
|
||||
/* 0x24: table3 offset (from here) */
|
||||
/* 0x28: table4 count */
|
||||
/* 0x2c: table4 offset (from here) */
|
||||
|
||||
/* 0x30: table5 count (waves) */
|
||||
/* 0x34: table5 offset (from here) */
|
||||
/* 0x38: table6 count */
|
||||
/* 0x3c: table6 offset (from here) */
|
||||
|
||||
/* 0x40: table7 count */
|
||||
/* 0x44: table7 offset (from here) */
|
||||
/* 0x48: data size */
|
||||
/* 0x4c: data offset (absolute) */
|
||||
|
||||
musx->tables_offset = 0x800;
|
||||
if (!is_id32be(musx->tables_offset+0x00, sf, "SBNK"))
|
||||
goto fail;
|
||||
|
||||
musx->total_subsongs = read_u32(musx->tables_offset+0x30, sf);
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
if (target_subsong > musx->total_subsongs || musx->total_subsongs <= 0) goto fail;
|
||||
|
||||
head_offset = read_u32(musx->tables_offset+0x34, sf) + musx->tables_offset + 0x34;
|
||||
data_offset = read_u32(musx->tables_offset+0x4c, sf);
|
||||
|
||||
target_offset = head_offset + (target_subsong - 1)*0x1c;
|
||||
;VGM_LOG("MUSX: ho=%lx, do=%lx, to=%lx\n", head_offset, data_offset, target_offset);
|
||||
|
||||
/* 0x00: subfile ID */
|
||||
musx->num_samples = read_s32(target_offset + 0x04, sf);
|
||||
musx->loop_start_sample = read_s32(target_offset + 0x08, sf); /* -1 if no loop */
|
||||
musx->sample_rate = read_u16(target_offset + 0x0c, sf);
|
||||
codec = read_u8 (target_offset + 0x0e, sf);
|
||||
musx->channels = read_u8 (target_offset + 0x0f, sf);
|
||||
musx->stream_offset = read_u32(target_offset + 0x10, sf) + data_offset;
|
||||
musx->stream_size = read_u32(target_offset + 0x14, sf);
|
||||
musx->loop_start = read_s32(target_offset + 0x18, sf);
|
||||
|
||||
musx->loop_end_sample = musx->num_samples;
|
||||
musx->loop_flag = (musx->loop_start_sample >= 0);
|
||||
|
||||
switch(codec) {
|
||||
case 0x11: musx->codec = DAT; break;
|
||||
case 0x13: musx->codec = NGCA; break;
|
||||
case 0x14: musx->codec = PCM; break;
|
||||
default:
|
||||
VGM_LOG("MUSX: unknown form\n");
|
||||
VGM_LOG("MUSX: unknown SBNK codec %x\n", codec);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
VGM_LOG("MUSX: unsupported form\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,151 +0,0 @@
|
|||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "mzrt_streamfile.h"
|
||||
|
||||
|
||||
/* mzrt - idTech "4.5" audio found in .resource bigfiles (w/ internal filenames) [Doom 3 BFG edition (PC/PS3/X360)] */
|
||||
VGMSTREAM * init_vgmstream_mzrt(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag = 0, channel_count, codec, sample_rate, block_size = 0, bps = 0, num_samples;
|
||||
STREAMFILE *temp_streamFile = NULL;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile, "idwav,idmsf,idxma"))
|
||||
goto fail;
|
||||
|
||||
if (read_32bitBE(0x00,streamFile) != 0x6D7A7274) /* "mzrt" */
|
||||
goto fail;
|
||||
|
||||
/* this format is bizarrely mis-aligned (and mis-designed too) */
|
||||
|
||||
num_samples = read_32bitBE(0x11,streamFile);
|
||||
codec = read_16bitLE(0x15,streamFile);
|
||||
switch(codec) {
|
||||
case 0x0001:
|
||||
case 0x0002:
|
||||
case 0x0166:
|
||||
channel_count = read_16bitLE(0x17,streamFile);
|
||||
sample_rate = read_32bitLE(0x19, streamFile);
|
||||
block_size = read_16bitLE(0x21, streamFile);
|
||||
bps = read_16bitLE(0x23,streamFile);
|
||||
|
||||
start_offset = 0x25;
|
||||
break;
|
||||
|
||||
case 0x0000:
|
||||
sample_rate = read_32bitBE(0x1D, streamFile);
|
||||
channel_count = read_32bitBE(0x21, streamFile);
|
||||
|
||||
start_offset = 0x29;
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* skip MSADPCM data */
|
||||
if (codec == 0x0002) {
|
||||
if (!msadpcm_check_coefs(streamFile, start_offset + 0x02 + 0x02))
|
||||
goto fail;
|
||||
|
||||
start_offset += 0x02 + read_16bitLE(start_offset, streamFile);
|
||||
}
|
||||
|
||||
/* skip extra data */
|
||||
if (codec == 0x0166) {
|
||||
start_offset += 0x02 + read_16bitLE(start_offset, streamFile);
|
||||
}
|
||||
|
||||
/* skip unknown table */
|
||||
if (codec == 0x0000) {
|
||||
start_offset += 0x04 + read_32bitBE(start_offset, streamFile) * 0x04;
|
||||
}
|
||||
|
||||
/* skip unknown table */
|
||||
start_offset += 0x04 + read_32bitBE(start_offset, streamFile);
|
||||
|
||||
/* skip block info */
|
||||
if (codec != 0x0000) {
|
||||
/* 0x00: de-blocked size
|
||||
* 0x04: block count*/
|
||||
start_offset += 0x08;
|
||||
|
||||
/* idwav only uses 1 super-block though */
|
||||
temp_streamFile = setup_mzrt_streamfile(streamFile, start_offset);
|
||||
if (!temp_streamFile) goto fail;
|
||||
}
|
||||
else {
|
||||
/* 0x00: de-blocked size */
|
||||
start_offset += 0x04;
|
||||
}
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_MZRT;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = num_samples;
|
||||
|
||||
switch(codec) {
|
||||
case 0x0001:
|
||||
if (bps != 16) goto fail;
|
||||
vgmstream->coding_type = coding_PCM16LE;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = block_size / channel_count;
|
||||
break;
|
||||
|
||||
case 0x0002:
|
||||
if (bps != 4) goto fail;
|
||||
vgmstream->coding_type = coding_MSADPCM;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->frame_size = block_size;
|
||||
break;
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case 0x0166: {
|
||||
uint8_t buf[0x100];
|
||||
int bytes;
|
||||
size_t stream_size = get_streamfile_size(temp_streamFile);
|
||||
|
||||
bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,sizeof(buf), 0x15,0x34, stream_size, streamFile, 0);
|
||||
vgmstream->codec_data = init_ffmpeg_header_offset(temp_streamFile, buf,bytes, 0x00,stream_size);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
xma_fix_raw_samples_hb(vgmstream, streamFile, temp_streamFile, 0x00,stream_size, 0x15, 0,0);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
case 0x0000: {
|
||||
mpeg_custom_config cfg = {0};
|
||||
|
||||
cfg.skip_samples = 576; /* assumed */
|
||||
|
||||
vgmstream->codec_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->layout_type = layout_none;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,temp_streamFile == NULL ? streamFile : temp_streamFile,temp_streamFile == NULL ? start_offset : 0x00))
|
||||
goto fail;
|
||||
close_streamfile(temp_streamFile);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_streamFile);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
|
@ -178,13 +178,18 @@ static VGMSTREAM* init_vgmstream_dsp_common(STREAMFILE* sf, dsp_meta *dspm) {
|
|||
for (i = 0; i < channels; i++) {
|
||||
off_t loop_offset = ch_header[i].loop_start_offset;
|
||||
|
||||
loop_offset = loop_offset / 0x8 * 0x8; /* loop points to a nibble, but we need closest frame header */
|
||||
if (dspm->interleave) {
|
||||
/* Loop offset points to a nibble, but we need closest frame header.
|
||||
* Stereo doesn't always point to a proper offset unless de-adjusted with interleave first. */
|
||||
if (!dspm->interleave) {
|
||||
loop_offset = loop_offset / 0x08 * 0x8;
|
||||
}
|
||||
else {
|
||||
loop_offset = loop_offset / 0x10 * 0x8;
|
||||
loop_offset = (loop_offset / dspm->interleave * dspm->interleave * channels) + (loop_offset % dspm->interleave);
|
||||
}
|
||||
|
||||
if (ch_header[i].loop_ps != read_u8(dspm->start_offset + i*dspm->interleave + loop_offset,sf)) {
|
||||
//;VGM_LOG("DSP: bad loop ps: %x vs at %lx\n", ch_header[i].loop_ps, dspm->start_offset + i*dspm->interleave + loop_offset);
|
||||
//;VGM_LOG("DSP: ch%i bad loop ps: %x vs at %lx\n", i, ch_header[i].loop_ps, dspm->start_offset + i*dspm->interleave + loop_offset);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
@ -660,8 +665,6 @@ VGMSTREAM* init_vgmstream_sadb(STREAMFILE* sf) {
|
|||
dspm.start_offset = read_32bitBE(0x48,sf);
|
||||
dspm.interleave = 0x10;
|
||||
|
||||
dspm.ignore_loop_ps = 1; /* does something strange with offsets/etc, ignore */
|
||||
|
||||
dspm.meta_type = meta_DSP_SADB;
|
||||
return init_vgmstream_dsp_common(sf, &dspm);
|
||||
fail:
|
||||
|
@ -1370,7 +1373,6 @@ VGMSTREAM* init_vgmstream_dsp_cwac(STREAMFILE* sf) {
|
|||
dspm.max_channels = 2;
|
||||
dspm.header_spacing = dspm.interleave;
|
||||
dspm.start_offset = dspm.header_offset + 0x60;
|
||||
dspm.ignore_loop_ps = 1; /* loop offset seems relative to CWAC? also interleave affects it */
|
||||
|
||||
dspm.meta_type = meta_DSP_CWAC;
|
||||
return init_vgmstream_dsp_common(sf, &dspm);
|
||||
|
|
|
@ -20,19 +20,19 @@ typedef struct {
|
|||
strwav_codec codec;
|
||||
} strwav_header;
|
||||
|
||||
static int parse_header(STREAMFILE* streamHeader, strwav_header* strwav);
|
||||
static int parse_header(STREAMFILE* sf_h, strwav_header* strwav);
|
||||
|
||||
|
||||
/* STR+WAV - Blitz Games streams+header */
|
||||
VGMSTREAM * init_vgmstream_str_wav(STREAMFILE *streamFile) {
|
||||
VGMSTREAM* init_vgmstream_str_wav(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE * streamHeader = NULL;
|
||||
STREAMFILE* sf_h = NULL;
|
||||
strwav_header strwav = {0};
|
||||
off_t start_offset;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile, "str,data"))
|
||||
if (!check_extensions(sf, "str,data"))
|
||||
goto fail;
|
||||
|
||||
/* get external header (extracted with filenames from bigfiles) */
|
||||
|
@ -42,26 +42,26 @@ VGMSTREAM * init_vgmstream_str_wav(STREAMFILE *streamFile) {
|
|||
* - file.wma.str + file.wma [Fuzion Frenzy (Xbox)]
|
||||
* - file.data + file (extensionless) [SpongeBob's Surf & Skate Roadtrip (X360)] */
|
||||
char basename[PATH_LIMIT];
|
||||
get_streamfile_basename(streamFile,basename,PATH_LIMIT);
|
||||
streamHeader = open_streamfile_by_filename(streamFile, basename);
|
||||
if (!streamHeader) {
|
||||
get_streamfile_basename(sf,basename,PATH_LIMIT);
|
||||
sf_h = open_streamfile_by_filename(sf, basename);
|
||||
if (!sf_h) {
|
||||
/* try again with file.str=body + file.wav=header [Bad Boys II (PS2), Zapper (PS2)] */
|
||||
streamHeader = open_streamfile_by_ext(streamFile, "wav");
|
||||
if (!streamHeader) {
|
||||
sf_h = open_streamfile_by_ext(sf, "wav");
|
||||
if (!sf_h) {
|
||||
/* try again with file.str=body + file.sth=header (renamed/fake extension) */
|
||||
streamHeader = open_streamfile_by_ext(streamFile, "sth");
|
||||
if (!streamHeader) goto fail;
|
||||
sf_h = open_streamfile_by_ext(sf, "sth");
|
||||
if (!sf_h) goto fail;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* header must have known extensions */
|
||||
if (!check_extensions(streamHeader, "wav,wma,"))
|
||||
if (!check_extensions(sf_h, "wav,wma,"))
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* detect version */
|
||||
if (!parse_header(streamHeader, &strwav))
|
||||
if (!parse_header(sf_h, &strwav))
|
||||
goto fail;
|
||||
|
||||
if (strwav.flags == 0)
|
||||
|
@ -111,12 +111,12 @@ VGMSTREAM * init_vgmstream_str_wav(STREAMFILE *streamFile) {
|
|||
for (i = 0; i < 16; i++) {
|
||||
off_t coef_offset;
|
||||
if (strwav.dsps_table) /* mini table with offsets to DSP headers */
|
||||
coef_offset = read_32bitBE(strwav.dsps_table+0x04*ch,streamHeader) + 0x1c;
|
||||
coef_offset = read_32bitBE(strwav.dsps_table+0x04*ch,sf_h) + 0x1c;
|
||||
else if (strwav.coefs_table) /* mini table with offsets to coefs then header */
|
||||
coef_offset = read_32bitBE(strwav.coefs_table+0x04*ch,streamHeader);
|
||||
coef_offset = read_32bitBE(strwav.coefs_table+0x04*ch,sf_h);
|
||||
else
|
||||
coef_offset = strwav.coefs_offset + 0x60*ch;
|
||||
vgmstream->ch[ch].adpcm_coef[i] = read_16bitBE(coef_offset+i*0x02,streamHeader);
|
||||
vgmstream->ch[ch].adpcm_coef[i] = read_16bitBE(coef_offset+i*0x02,sf_h);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -140,7 +140,7 @@ VGMSTREAM * init_vgmstream_str_wav(STREAMFILE *streamFile) {
|
|||
case WMA: {
|
||||
ffmpeg_codec_data *ffmpeg_data = NULL;
|
||||
|
||||
ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset,get_streamfile_size(streamFile));
|
||||
ffmpeg_data = init_ffmpeg_offset(sf, start_offset,get_streamfile_size(sf));
|
||||
if (!ffmpeg_data) goto fail;
|
||||
vgmstream->codec_data = ffmpeg_data;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
|
@ -160,17 +160,17 @@ VGMSTREAM * init_vgmstream_str_wav(STREAMFILE *streamFile) {
|
|||
size_t stream_size;
|
||||
size_t bytes, block_size, block_count;
|
||||
|
||||
stream_size = get_streamfile_size(streamFile);
|
||||
stream_size = get_streamfile_size(sf);
|
||||
block_size = 0x10000;
|
||||
block_count = stream_size / block_size; /* not accurate? */
|
||||
|
||||
bytes = ffmpeg_make_riff_xma2(buf,0x100, strwav.num_samples, stream_size, strwav.channels, strwav.sample_rate, block_count, block_size);
|
||||
vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, 0x00,stream_size);
|
||||
vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, 0x00,stream_size);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
xma_fix_raw_samples(vgmstream, streamFile, 0x00,stream_size, 0, 0,0);
|
||||
xma_fix_raw_samples(vgmstream, sf, 0x00,stream_size, 0, 0,0);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
@ -180,13 +180,13 @@ VGMSTREAM * init_vgmstream_str_wav(STREAMFILE *streamFile) {
|
|||
}
|
||||
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
if (!vgmstream_open_stream(vgmstream,sf,start_offset))
|
||||
goto fail;
|
||||
close_streamfile(streamHeader);
|
||||
close_streamfile(sf_h);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(streamHeader);
|
||||
close_streamfile(sf_h);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -195,30 +195,30 @@ fail:
|
|||
|
||||
/* Parse header versions. Almost every game uses its own variation (struct serialization?),
|
||||
* so detection could be improved once enough versions are known. */
|
||||
static int parse_header(STREAMFILE* streamHeader, strwav_header* strwav) {
|
||||
static int parse_header(STREAMFILE* sf_h, strwav_header* strwav) {
|
||||
size_t header_size;
|
||||
|
||||
if (read_32bitBE(0x00,streamHeader) != 0x00000000)
|
||||
if (read_32bitBE(0x00,sf_h) != 0x00000000)
|
||||
goto fail;
|
||||
|
||||
header_size = get_streamfile_size(streamHeader);
|
||||
header_size = get_streamfile_size(sf_h);
|
||||
|
||||
//todo loop start/end values may be off for some headers
|
||||
|
||||
/* Fuzion Frenzy (Xbox)[2001] wma */
|
||||
if ( read_32bitBE(0x04,streamHeader) == 0x00000900 &&
|
||||
read_32bitLE(0x20,streamHeader) == 0 && /* no num_samples */
|
||||
read_32bitLE(0x24,streamHeader) == read_32bitLE(0x80,streamHeader) && /* sample rate repeat */
|
||||
read_32bitLE(0x28,streamHeader) == 0x10 &&
|
||||
if ( read_32bitBE(0x04,sf_h) == 0x00000900 &&
|
||||
read_32bitLE(0x20,sf_h) == 0 && /* no num_samples */
|
||||
read_32bitLE(0x24,sf_h) == read_32bitLE(0x80,sf_h) && /* sample rate repeat */
|
||||
read_32bitLE(0x28,sf_h) == 0x10 &&
|
||||
header_size == 0x110 /* no value in header */
|
||||
) {
|
||||
strwav->num_samples = read_32bitLE(0x20,streamHeader); /* 0 but will be rectified later */
|
||||
strwav->sample_rate = read_32bitLE(0x24,streamHeader);
|
||||
strwav->flags = read_32bitLE(0x2c,streamHeader);
|
||||
strwav->num_samples = read_32bitLE(0x20,sf_h); /* 0 but will be rectified later */
|
||||
strwav->sample_rate = read_32bitLE(0x24,sf_h);
|
||||
strwav->flags = read_32bitLE(0x2c,sf_h);
|
||||
strwav->loop_start = 0;
|
||||
strwav->loop_end = 0;
|
||||
|
||||
strwav->channels = read_32bitLE(0x60,streamHeader) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */
|
||||
strwav->channels = read_32bitLE(0x60,sf_h) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */
|
||||
strwav->loop_flag = strwav->flags & 0x01;
|
||||
strwav->interleave = 0;
|
||||
|
||||
|
@ -229,18 +229,18 @@ static int parse_header(STREAMFILE* streamHeader, strwav_header* strwav) {
|
|||
|
||||
/* Taz: Wanted (GC)[2002] */
|
||||
/* Cubix Robots for Everyone: Showdown (GC)[2003] */
|
||||
if ( read_32bitBE(0x04,streamHeader) == 0x00000900 &&
|
||||
read_32bitBE(0x24,streamHeader) == read_32bitBE(0x90,streamHeader) && /* sample rate repeat */
|
||||
read_32bitBE(0x28,streamHeader) == 0x10 &&
|
||||
read_32bitBE(0xa0,streamHeader) == header_size /* ~0x3C0 */
|
||||
if ( read_32bitBE(0x04,sf_h) == 0x00000900 &&
|
||||
read_32bitBE(0x24,sf_h) == read_32bitBE(0x90,sf_h) && /* sample rate repeat */
|
||||
read_32bitBE(0x28,sf_h) == 0x10 &&
|
||||
read_32bitBE(0xa0,sf_h) == header_size /* ~0x3C0 */
|
||||
) {
|
||||
strwav->num_samples = read_32bitBE(0x20,streamHeader);
|
||||
strwav->sample_rate = read_32bitBE(0x24,streamHeader);
|
||||
strwav->flags = read_32bitBE(0x2c,streamHeader);
|
||||
strwav->loop_start = read_32bitBE(0xb8,streamHeader);
|
||||
strwav->loop_end = read_32bitBE(0xbc,streamHeader);
|
||||
strwav->num_samples = read_32bitBE(0x20,sf_h);
|
||||
strwav->sample_rate = read_32bitBE(0x24,sf_h);
|
||||
strwav->flags = read_32bitBE(0x2c,sf_h);
|
||||
strwav->loop_start = read_32bitBE(0xb8,sf_h);
|
||||
strwav->loop_end = read_32bitBE(0xbc,sf_h);
|
||||
|
||||
strwav->channels = read_32bitBE(0x50,streamHeader) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */
|
||||
strwav->channels = read_32bitBE(0x50,sf_h) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */
|
||||
strwav->loop_flag = strwav->flags & 0x01;
|
||||
strwav->interleave = strwav->channels > 2 ? 0x8000 : 0x10000;
|
||||
|
||||
|
@ -251,18 +251,18 @@ static int parse_header(STREAMFILE* streamHeader, strwav_header* strwav) {
|
|||
}
|
||||
|
||||
/* The Fairly OddParents - Breakin' da Rules (Xbox)[2003] */
|
||||
if ( read_32bitBE(0x04,streamHeader) == 0x00000900 &&
|
||||
read_32bitLE(0x24,streamHeader) == read_32bitLE(0xb0,streamHeader) && /* sample rate repeat */
|
||||
read_32bitLE(0x28,streamHeader) == 0x10 &&
|
||||
read_32bitLE(0xc0,streamHeader)*0x04 + read_32bitLE(0xc4,streamHeader) == header_size /* ~0xe0 + variable */
|
||||
if ( read_32bitBE(0x04,sf_h) == 0x00000900 &&
|
||||
read_32bitLE(0x24,sf_h) == read_32bitLE(0xb0,sf_h) && /* sample rate repeat */
|
||||
read_32bitLE(0x28,sf_h) == 0x10 &&
|
||||
read_32bitLE(0xc0,sf_h)*0x04 + read_32bitLE(0xc4,sf_h) == header_size /* ~0xe0 + variable */
|
||||
) {
|
||||
strwav->num_samples = read_32bitLE(0x20,streamHeader);
|
||||
strwav->sample_rate = read_32bitLE(0x24,streamHeader);
|
||||
strwav->flags = read_32bitLE(0x2c,streamHeader);
|
||||
strwav->loop_start = read_32bitLE(0x38,streamHeader);
|
||||
strwav->num_samples = read_32bitLE(0x20,sf_h);
|
||||
strwav->sample_rate = read_32bitLE(0x24,sf_h);
|
||||
strwav->flags = read_32bitLE(0x2c,sf_h);
|
||||
strwav->loop_start = read_32bitLE(0x38,sf_h);
|
||||
strwav->loop_end = strwav->num_samples;
|
||||
|
||||
strwav->channels = read_32bitLE(0x70,streamHeader) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */
|
||||
strwav->channels = read_32bitLE(0x70,sf_h) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */
|
||||
strwav->loop_flag = strwav->flags & 0x01;
|
||||
strwav->interleave = 0xD800/2;
|
||||
|
||||
|
@ -272,19 +272,19 @@ static int parse_header(STREAMFILE* streamHeader, strwav_header* strwav) {
|
|||
}
|
||||
|
||||
/* Bad Boys II (GC)[2004] */
|
||||
if ( read_32bitBE(0x04,streamHeader) == 0x00000800 &&
|
||||
read_32bitBE(0x24,streamHeader) == read_32bitBE(0xb0,streamHeader) && /* sample rate repeat */
|
||||
read_32bitBE(0x24,streamHeader) == read_32bitBE(read_32bitBE(0xe0,streamHeader)+0x08,streamHeader) && /* sample rate vs 1st DSP header */
|
||||
read_32bitBE(0x28,streamHeader) == 0x10 &&
|
||||
read_32bitBE(0xc0,streamHeader)*0x04 + read_32bitBE(0xc4,streamHeader) == header_size /* variable + variable */
|
||||
if ( read_32bitBE(0x04,sf_h) == 0x00000800 &&
|
||||
read_32bitBE(0x24,sf_h) == read_32bitBE(0xb0,sf_h) && /* sample rate repeat */
|
||||
read_32bitBE(0x24,sf_h) == read_32bitBE(read_32bitBE(0xe0,sf_h)+0x08,sf_h) && /* sample rate vs 1st DSP header */
|
||||
read_32bitBE(0x28,sf_h) == 0x10 &&
|
||||
read_32bitBE(0xc0,sf_h)*0x04 + read_32bitBE(0xc4,sf_h) == header_size /* variable + variable */
|
||||
) {
|
||||
strwav->num_samples = read_32bitBE(0x20,streamHeader);
|
||||
strwav->sample_rate = read_32bitBE(0x24,streamHeader);
|
||||
strwav->flags = read_32bitBE(0x2c,streamHeader);
|
||||
strwav->loop_start = read_32bitBE(0xd8,streamHeader);
|
||||
strwav->loop_end = read_32bitBE(0xdc,streamHeader);
|
||||
strwav->num_samples = read_32bitBE(0x20,sf_h);
|
||||
strwav->sample_rate = read_32bitBE(0x24,sf_h);
|
||||
strwav->flags = read_32bitBE(0x2c,sf_h);
|
||||
strwav->loop_start = read_32bitBE(0xd8,sf_h);
|
||||
strwav->loop_end = read_32bitBE(0xdc,sf_h);
|
||||
|
||||
strwav->channels = read_32bitBE(0x70,streamHeader) * read_32bitBE(0x88,streamHeader); /* tracks of Nch */
|
||||
strwav->channels = read_32bitBE(0x70,sf_h) * read_32bitBE(0x88,sf_h); /* tracks of Nch */
|
||||
strwav->loop_flag = strwav->flags & 0x01;
|
||||
strwav->interleave = strwav->channels > 2 ? 0x8000 : 0x10000;
|
||||
|
||||
|
@ -296,20 +296,20 @@ static int parse_header(STREAMFILE* streamHeader, strwav_header* strwav) {
|
|||
|
||||
/* Bad Boys II (PS2)[2004] */
|
||||
/* Pac-Man World 3 (PS2)[2005] */
|
||||
if ((read_32bitBE(0x04,streamHeader) == 0x00000800 ||
|
||||
read_32bitBE(0x04,streamHeader) == 0x01000800) && /* rare (PW3 mu_spectral1_explore_2) */
|
||||
read_32bitLE(0x24,streamHeader) == read_32bitLE(0x70,streamHeader) && /* sample rate repeat */
|
||||
read_32bitLE(0x28,streamHeader) == 0x10 &&
|
||||
read_32bitLE(0x78,streamHeader)*0x04 + read_32bitLE(0x7c,streamHeader) == header_size /* ~0xe0 + variable */
|
||||
if ((read_32bitBE(0x04,sf_h) == 0x00000800 ||
|
||||
read_32bitBE(0x04,sf_h) == 0x01000800) && /* rare (PW3 mu_spectral1_explore_2) */
|
||||
read_32bitLE(0x24,sf_h) == read_32bitLE(0x70,sf_h) && /* sample rate repeat */
|
||||
read_32bitLE(0x28,sf_h) == 0x10 &&
|
||||
read_32bitLE(0x78,sf_h)*0x04 + read_32bitLE(0x7c,sf_h) == header_size /* ~0xe0 + variable */
|
||||
) {
|
||||
strwav->num_samples = read_32bitLE(0x20,streamHeader);
|
||||
strwav->sample_rate = read_32bitLE(0x24,streamHeader);
|
||||
strwav->flags = read_32bitLE(0x2c,streamHeader);
|
||||
strwav->loop_start = read_32bitLE(0x38,streamHeader);
|
||||
strwav->interleave = read_32bitLE(0x40,streamHeader) == 1 ? 0x8000 : 0x4000;
|
||||
strwav->loop_end = read_32bitLE(0x54,streamHeader);
|
||||
strwav->num_samples = read_32bitLE(0x20,sf_h);
|
||||
strwav->sample_rate = read_32bitLE(0x24,sf_h);
|
||||
strwav->flags = read_32bitLE(0x2c,sf_h);
|
||||
strwav->loop_start = read_32bitLE(0x38,sf_h);
|
||||
strwav->interleave = read_32bitLE(0x40,sf_h) == 1 ? 0x8000 : 0x4000;
|
||||
strwav->loop_end = read_32bitLE(0x54,sf_h);
|
||||
|
||||
strwav->channels = read_32bitLE(0x40,streamHeader) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */
|
||||
strwav->channels = read_32bitLE(0x40,sf_h) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */
|
||||
strwav->loop_flag = strwav->flags & 0x01;
|
||||
strwav->interleave = strwav->channels > 2 ? 0x4000 : 0x8000;
|
||||
|
||||
|
@ -318,19 +318,63 @@ static int parse_header(STREAMFILE* streamHeader, strwav_header* strwav) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
/* Zapper: One Wicked Cricket! (PS2)[2005] */
|
||||
if ( read_32bitBE(0x04,streamHeader) == 0x00000900 &&
|
||||
read_32bitLE(0x24,streamHeader) == read_32bitLE(0x70,streamHeader) && /* sample rate repeat */
|
||||
read_32bitLE(0x28,streamHeader) == 0x10 &&
|
||||
read_32bitLE(0x7c,streamHeader) == header_size /* ~0xD0 */
|
||||
/* Pac-Man World 3 (Xbox)[2005] */
|
||||
if ((read_u32be(0x04,sf_h) == 0x00000800 ||
|
||||
read_u32be(0x04,sf_h) == 0x01000800) && /* rare (PW3 mu_spectral1_explore_2) */
|
||||
read_u32le(0x24,sf_h) == read_u32le(0xB0,sf_h) && /* sample rate repeat */
|
||||
read_u32le(0x28,sf_h) == 0x10 &&
|
||||
read_u32le(0xE0,sf_h) + read_u32le(0xE4,sf_h) * 0x40 == header_size /* ~0x100 + cues */
|
||||
) {
|
||||
strwav->num_samples = read_32bitLE(0x20,streamHeader);
|
||||
strwav->sample_rate = read_32bitLE(0x24,streamHeader);
|
||||
strwav->flags = read_32bitLE(0x2c,streamHeader);
|
||||
strwav->loop_start = read_32bitLE(0x38,streamHeader);
|
||||
strwav->loop_end = read_32bitLE(0x54,streamHeader);
|
||||
strwav->num_samples = read_u32le(0x20,sf_h);
|
||||
strwav->sample_rate = read_u32le(0x24,sf_h);
|
||||
strwav->flags = read_u32le(0x2c,sf_h);
|
||||
strwav->loop_start = read_u32le(0x38,sf_h);
|
||||
strwav->loop_end = strwav->num_samples;
|
||||
|
||||
strwav->channels = read_32bitLE(0x40,streamHeader) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */
|
||||
strwav->channels = read_u32le(0x70,sf_h) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */
|
||||
strwav->loop_flag = strwav->flags & 0x01;
|
||||
strwav->interleave = strwav->channels > 2 ? 0xD800/2 : 0xD800;
|
||||
|
||||
strwav->codec = XBOX;
|
||||
//;VGM_LOG("STR+WAV: Pac-Man World 3 (Xbox)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Pac-Man World 3 (PC)[2005] */
|
||||
if ((read_u32be(0x04,sf_h) == 0x00000800 ||
|
||||
read_u32be(0x04,sf_h) == 0x01000800) && /* rare (PW3 mu_spectral1_explore_2) */
|
||||
read_u32le(0x24,sf_h) == read_u32le(0x114,sf_h) && /* sample rate repeat */
|
||||
read_u32le(0x28,sf_h) == 0x10 &&
|
||||
read_u32le(0x130,sf_h) + read_u32le(0x134,sf_h) * 0x40 == header_size /* ~0x140 + cues */
|
||||
) {
|
||||
strwav->num_samples = read_u32le(0x20,sf_h);
|
||||
strwav->sample_rate = read_u32le(0x24,sf_h);
|
||||
strwav->flags = read_u32le(0x2c,sf_h);
|
||||
strwav->loop_start = read_u32le(0x38,sf_h);
|
||||
strwav->loop_end = read_u32le(0x30,sf_h);
|
||||
|
||||
strwav->channels = read_u32le(0xF8,sf_h) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */
|
||||
strwav->loop_flag = strwav->flags & 0x01;
|
||||
strwav->interleave = strwav->channels > 2 ? 0x8000 : 0x10000;
|
||||
|
||||
strwav->codec = IMA;
|
||||
//;VGM_LOG("STR+WAV: Pac-Man World 3 (PC)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Zapper: One Wicked Cricket! (PS2)[2005] */
|
||||
if ( read_32bitBE(0x04,sf_h) == 0x00000900 &&
|
||||
read_32bitLE(0x24,sf_h) == read_32bitLE(0x70,sf_h) && /* sample rate repeat */
|
||||
read_32bitLE(0x28,sf_h) == 0x10 &&
|
||||
read_32bitLE(0x7c,sf_h) == header_size /* ~0xD0 */
|
||||
) {
|
||||
strwav->num_samples = read_32bitLE(0x20,sf_h);
|
||||
strwav->sample_rate = read_32bitLE(0x24,sf_h);
|
||||
strwav->flags = read_32bitLE(0x2c,sf_h);
|
||||
strwav->loop_start = read_32bitLE(0x38,sf_h);
|
||||
strwav->loop_end = read_32bitLE(0x54,sf_h);
|
||||
|
||||
strwav->channels = read_32bitLE(0x40,sf_h) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */
|
||||
strwav->loop_flag = strwav->flags & 0x01;
|
||||
strwav->interleave = strwav->channels > 2 ? 0x4000 : 0x8000;
|
||||
|
||||
|
@ -340,18 +384,18 @@ static int parse_header(STREAMFILE* streamHeader, strwav_header* strwav) {
|
|||
}
|
||||
|
||||
/* Zapper: One Wicked Cricket! (GC)[2005] */
|
||||
if ( read_32bitBE(0x04,streamHeader) == 0x00000900 &&
|
||||
read_32bitBE(0x24,streamHeader) == read_32bitBE(0xB0,streamHeader) && /* sample rate repeat */
|
||||
read_32bitBE(0x28,streamHeader) == 0x10 &&
|
||||
read_32bitLE(0xc0,streamHeader) == header_size /* variable LE size */
|
||||
if ( read_32bitBE(0x04,sf_h) == 0x00000900 &&
|
||||
read_32bitBE(0x24,sf_h) == read_32bitBE(0xB0,sf_h) && /* sample rate repeat */
|
||||
read_32bitBE(0x28,sf_h) == 0x10 &&
|
||||
read_32bitLE(0xc0,sf_h) == header_size /* variable LE size */
|
||||
) {
|
||||
strwav->num_samples = read_32bitBE(0x20,streamHeader);
|
||||
strwav->sample_rate = read_32bitBE(0x24,streamHeader);
|
||||
strwav->flags = read_32bitBE(0x2c,streamHeader);
|
||||
strwav->loop_start = read_32bitBE(0xd8,streamHeader);
|
||||
strwav->loop_end = read_32bitBE(0xdc,streamHeader);
|
||||
strwav->num_samples = read_32bitBE(0x20,sf_h);
|
||||
strwav->sample_rate = read_32bitBE(0x24,sf_h);
|
||||
strwav->flags = read_32bitBE(0x2c,sf_h);
|
||||
strwav->loop_start = read_32bitBE(0xd8,sf_h);
|
||||
strwav->loop_end = read_32bitBE(0xdc,sf_h);
|
||||
|
||||
strwav->channels = read_32bitBE(0x70,streamHeader) * read_32bitBE(0x88,streamHeader); /* tracks of Nch */
|
||||
strwav->channels = read_32bitBE(0x70,sf_h) * read_32bitBE(0x88,sf_h); /* tracks of Nch */
|
||||
strwav->loop_flag = strwav->flags & 0x01;
|
||||
strwav->interleave = strwav->channels > 2 ? 0x8000 : 0x10000;
|
||||
|
||||
|
@ -362,18 +406,18 @@ static int parse_header(STREAMFILE* streamHeader, strwav_header* strwav) {
|
|||
}
|
||||
|
||||
/* Zapper: One Wicked Cricket! (PC)[2005] */
|
||||
if ( read_32bitBE(0x04,streamHeader) == 0x00000900 &&
|
||||
read_32bitLE(0x24,streamHeader) == read_32bitLE(0x114,streamHeader) && /* sample rate repeat */
|
||||
read_32bitLE(0x28,streamHeader) == 0x10 &&
|
||||
read_32bitLE(0x12c,streamHeader) == header_size /* ~0x130 */
|
||||
if ( read_32bitBE(0x04,sf_h) == 0x00000900 &&
|
||||
read_32bitLE(0x24,sf_h) == read_32bitLE(0x114,sf_h) && /* sample rate repeat */
|
||||
read_32bitLE(0x28,sf_h) == 0x10 &&
|
||||
read_32bitLE(0x12c,sf_h) == header_size /* ~0x130 */
|
||||
) {
|
||||
strwav->num_samples = read_32bitLE(0x20,streamHeader);
|
||||
strwav->sample_rate = read_32bitLE(0x24,streamHeader);
|
||||
strwav->flags = read_32bitLE(0x2c,streamHeader);
|
||||
strwav->loop_start = read_32bitLE(0x54,streamHeader);
|
||||
strwav->loop_end = read_32bitLE(0x30,streamHeader);
|
||||
strwav->num_samples = read_32bitLE(0x20,sf_h);
|
||||
strwav->sample_rate = read_32bitLE(0x24,sf_h);
|
||||
strwav->flags = read_32bitLE(0x2c,sf_h);
|
||||
strwav->loop_start = read_32bitLE(0x54,sf_h);
|
||||
strwav->loop_end = read_32bitLE(0x30,sf_h);
|
||||
|
||||
strwav->channels = read_32bitLE(0xF8,streamHeader) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */
|
||||
strwav->channels = read_32bitLE(0xF8,sf_h) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */
|
||||
strwav->loop_flag = strwav->flags & 0x01;
|
||||
strwav->interleave = strwav->channels > 2 ? 0x8000 : 0x10000;
|
||||
|
||||
|
@ -384,21 +428,22 @@ static int parse_header(STREAMFILE* streamHeader, strwav_header* strwav) {
|
|||
|
||||
/* Pac-Man World 3 (GC)[2005] */
|
||||
/* SpongeBob SquarePants: Creature from the Krusty Krab (GC)[2006] */
|
||||
if ( read_32bitBE(0x04,streamHeader) == 0x00000800 &&
|
||||
read_32bitBE(0x24,streamHeader) == read_32bitBE(0xb0,streamHeader) && /* sample rate repeat */
|
||||
read_32bitBE(0x24,streamHeader) == read_32bitBE(read_32bitBE(0xf0,streamHeader)+0x08,streamHeader) && /* sample rate vs 1st DSP header */
|
||||
read_32bitBE(0x28,streamHeader) == 0x10 &&
|
||||
read_32bitBE(0xc0,streamHeader)*0x04 + read_32bitBE(0xc4,streamHeader) == read_32bitBE(0xe0,streamHeader) && /* main size */
|
||||
(read_32bitBE(0xe0,streamHeader) + read_32bitBE(0xe4,streamHeader)*0x40 == header_size || /* main size + extradata 1 (config? PMW3 cs2.wav) */
|
||||
read_32bitBE(0xe0,streamHeader) + read_32bitBE(0xe4,streamHeader)*0x08 == header_size) /* main size + extradata 2 (ids? SBSP 0_0_mu_hr.wav) */
|
||||
/* SpongeBob SquarePants: Creature from the Krusty Krab (Wii)[2006] */
|
||||
if ( read_32bitBE(0x04,sf_h) == 0x00000800 &&
|
||||
read_32bitBE(0x24,sf_h) == read_32bitBE(0xb0,sf_h) && /* sample rate repeat */
|
||||
read_32bitBE(0x24,sf_h) == read_32bitBE(read_32bitBE(0xf0,sf_h)+0x08,sf_h) && /* sample rate vs 1st DSP header */
|
||||
read_32bitBE(0x28,sf_h) == 0x10 &&
|
||||
read_32bitBE(0xc0,sf_h)*0x04 + read_32bitBE(0xc4,sf_h) == read_32bitBE(0xe0,sf_h) && /* main size */
|
||||
(read_32bitBE(0xe0,sf_h) + read_32bitBE(0xe4,sf_h)*0x40 == header_size || /* main size + extradata 1 (config? PMW3 cs2.wav) */
|
||||
read_32bitBE(0xe0,sf_h) + read_32bitBE(0xe4,sf_h)*0x08 == header_size) /* main size + extradata 2 (ids? SBSP 0_0_mu_hr.wav) */
|
||||
) {
|
||||
strwav->num_samples = read_32bitBE(0x20,streamHeader);
|
||||
strwav->sample_rate = read_32bitBE(0x24,streamHeader);
|
||||
strwav->flags = read_32bitBE(0x2c,streamHeader);
|
||||
strwav->loop_start = read_32bitBE(0xd8,streamHeader);
|
||||
strwav->loop_end = read_32bitBE(0xdc,streamHeader);
|
||||
strwav->num_samples = read_32bitBE(0x20,sf_h);
|
||||
strwav->sample_rate = read_32bitBE(0x24,sf_h);
|
||||
strwav->flags = read_32bitBE(0x2c,sf_h);
|
||||
strwav->loop_start = read_32bitBE(0xd8,sf_h);
|
||||
strwav->loop_end = read_32bitBE(0xdc,sf_h);
|
||||
|
||||
strwav->channels = read_32bitBE(0x70,streamHeader) * read_32bitBE(0x88,streamHeader); /* tracks of Nch */
|
||||
strwav->channels = read_32bitBE(0x70,sf_h) * read_32bitBE(0x88,sf_h); /* tracks of Nch */
|
||||
strwav->loop_flag = strwav->flags & 0x01;
|
||||
strwav->interleave = strwav->channels > 2 ? 0x8000 : 0x10000;
|
||||
|
||||
|
@ -409,19 +454,19 @@ static int parse_header(STREAMFILE* streamHeader, strwav_header* strwav) {
|
|||
}
|
||||
|
||||
/* SpongeBob SquarePants: Creature from the Krusty Krab (PS2)[2006] */
|
||||
if ( read_32bitBE(0x04,streamHeader) == 0x00000800 &&
|
||||
read_32bitLE(0x08,streamHeader) == 0x00000000 &&
|
||||
read_32bitLE(0x0c,streamHeader) != header_size && /* some ID */
|
||||
(header_size == 0x74 + read_32bitLE(0x64,streamHeader)*0x04 ||
|
||||
header_size == 0x78 + read_32bitLE(0x64,streamHeader)*0x04)
|
||||
if ( read_32bitBE(0x04,sf_h) == 0x00000800 &&
|
||||
read_32bitLE(0x08,sf_h) == 0x00000000 &&
|
||||
read_32bitLE(0x0c,sf_h) != header_size && /* some ID */
|
||||
(header_size == 0x74 + read_32bitLE(0x64,sf_h)*0x04 ||
|
||||
header_size == 0x78 + read_32bitLE(0x64,sf_h)*0x04)
|
||||
) {
|
||||
strwav->loop_start = read_32bitLE(0x24,streamHeader); //not ok?
|
||||
strwav->num_samples = read_32bitLE(0x30,streamHeader);
|
||||
strwav->loop_end = read_32bitLE(0x34,streamHeader);
|
||||
strwav->sample_rate = read_32bitLE(0x38,streamHeader);
|
||||
strwav->flags = read_32bitLE(0x3c,streamHeader);
|
||||
strwav->loop_start = read_32bitLE(0x24,sf_h); //not ok?
|
||||
strwav->num_samples = read_32bitLE(0x30,sf_h);
|
||||
strwav->loop_end = read_32bitLE(0x34,sf_h);
|
||||
strwav->sample_rate = read_32bitLE(0x38,sf_h);
|
||||
strwav->flags = read_32bitLE(0x3c,sf_h);
|
||||
|
||||
strwav->channels = read_32bitLE(0x64,streamHeader); /* tracks of 1ch */
|
||||
strwav->channels = read_32bitLE(0x64,sf_h); /* tracks of 1ch */
|
||||
strwav->loop_flag = strwav->flags & 0x01;
|
||||
strwav->interleave = strwav->channels > 4 ? 0x4000 : 0x8000;
|
||||
|
||||
|
@ -431,18 +476,18 @@ static int parse_header(STREAMFILE* streamHeader, strwav_header* strwav) {
|
|||
}
|
||||
|
||||
/* Tak and the Guardians of Gross (PS2)[2008] */
|
||||
if ( read_32bitBE(0x04,streamHeader) == 0x00000800 &&
|
||||
read_32bitLE(0x08,streamHeader) != 0x00000000 &&
|
||||
read_32bitLE(0x0c,streamHeader) == header_size && /* ~0x80+0x04*ch */
|
||||
read_32bitLE(0x0c,streamHeader) == 0x80 + read_32bitLE(0x70,streamHeader)*0x04
|
||||
if ( read_32bitBE(0x04,sf_h) == 0x00000800 &&
|
||||
read_32bitLE(0x08,sf_h) != 0x00000000 &&
|
||||
read_32bitLE(0x0c,sf_h) == header_size && /* ~0x80+0x04*ch */
|
||||
read_32bitLE(0x0c,sf_h) == 0x80 + read_32bitLE(0x70,sf_h)*0x04
|
||||
) {
|
||||
strwav->loop_start = read_32bitLE(0x24,streamHeader); //not ok?
|
||||
strwav->num_samples = read_32bitLE(0x30,streamHeader);
|
||||
strwav->loop_end = read_32bitLE(0x34,streamHeader);
|
||||
strwav->sample_rate = read_32bitLE(0x38,streamHeader);
|
||||
strwav->flags = read_32bitLE(0x3c,streamHeader);
|
||||
strwav->loop_start = read_32bitLE(0x24,sf_h); //not ok?
|
||||
strwav->num_samples = read_32bitLE(0x30,sf_h);
|
||||
strwav->loop_end = read_32bitLE(0x34,sf_h);
|
||||
strwav->sample_rate = read_32bitLE(0x38,sf_h);
|
||||
strwav->flags = read_32bitLE(0x3c,sf_h);
|
||||
|
||||
strwav->channels = read_32bitLE(0x70,streamHeader); /* tracks of 1ch */
|
||||
strwav->channels = read_32bitLE(0x70,sf_h); /* tracks of 1ch */
|
||||
strwav->loop_flag = strwav->flags & 0x01;
|
||||
strwav->interleave = strwav->channels > 4 ? 0x4000 : 0x8000;
|
||||
|
||||
|
@ -453,20 +498,21 @@ static int parse_header(STREAMFILE* streamHeader, strwav_header* strwav) {
|
|||
|
||||
/* Tak and the Guardians of Gross (Wii)[2008] */
|
||||
/* The House of the Dead: Overkill (Wii)[2009] (not Blitz but still the same format) */
|
||||
if ((read_32bitBE(0x04,streamHeader) == 0x00000800 ||
|
||||
read_32bitBE(0x04,streamHeader) == 0x00000700) && /* rare? */
|
||||
read_32bitLE(0x08,streamHeader) != 0x00000000 &&
|
||||
read_32bitBE(0x0c,streamHeader) == header_size && /* variable per header */
|
||||
read_32bitBE(0x7c,streamHeader) != 0 && /* has DSP header */
|
||||
read_32bitBE(0x38,streamHeader) == read_32bitBE(read_32bitBE(0x7c,streamHeader)+0x38,streamHeader) /* sample rate vs 1st DSP header */
|
||||
/* All Star Karate (Wii)[2010] */
|
||||
if ((read_32bitBE(0x04,sf_h) == 0x00000800 ||
|
||||
read_32bitBE(0x04,sf_h) == 0x00000700) && /* rare? */
|
||||
read_32bitLE(0x08,sf_h) != 0x00000000 &&
|
||||
read_32bitBE(0x0c,sf_h) == header_size && /* variable per header */
|
||||
read_32bitBE(0x7c,sf_h) != 0 && /* has DSP header */
|
||||
read_32bitBE(0x38,sf_h) == read_32bitBE(read_32bitBE(0x7c,sf_h)+0x38,sf_h) /* sample rate vs 1st DSP header */
|
||||
) {
|
||||
strwav->loop_start = 0; //read_32bitLE(0x24,streamHeader); //not ok?
|
||||
strwav->num_samples = read_32bitBE(0x30,streamHeader);
|
||||
strwav->loop_end = read_32bitBE(0x34,streamHeader);
|
||||
strwav->sample_rate = read_32bitBE(0x38,streamHeader);
|
||||
strwav->flags = read_32bitBE(0x3c,streamHeader);
|
||||
strwav->loop_start = 0; //read_32bitLE(0x24,sf_h); //not ok?
|
||||
strwav->num_samples = read_32bitBE(0x30,sf_h);
|
||||
strwav->loop_end = read_32bitBE(0x34,sf_h);
|
||||
strwav->sample_rate = read_32bitBE(0x38,sf_h);
|
||||
strwav->flags = read_32bitBE(0x3c,sf_h);
|
||||
|
||||
strwav->channels = read_32bitBE(0x70,streamHeader); /* tracks of 1ch */
|
||||
strwav->channels = read_32bitBE(0x70,sf_h); /* tracks of 1ch */
|
||||
strwav->loop_flag = strwav->flags & 0x01;
|
||||
strwav->interleave = strwav->channels > 4 ? 0x4000 : 0x8000;
|
||||
|
||||
|
@ -477,19 +523,19 @@ static int parse_header(STREAMFILE* streamHeader, strwav_header* strwav) {
|
|||
}
|
||||
|
||||
/* The House of the Dead: Overkill (PS3)[2009] (not Blitz but still the same format) */
|
||||
if ((read_32bitBE(0x04,streamHeader) == 0x00000800 ||
|
||||
read_32bitBE(0x04,streamHeader) == 0x00000700) && /* rare? */
|
||||
read_32bitLE(0x08,streamHeader) != 0x00000000 &&
|
||||
read_32bitBE(0x0c,streamHeader) == header_size && /* variable per header */
|
||||
read_32bitBE(0x7c,streamHeader) == 0 /* not DSP header */
|
||||
if ((read_32bitBE(0x04,sf_h) == 0x00000800 ||
|
||||
read_32bitBE(0x04,sf_h) == 0x00000700) && /* rare? */
|
||||
read_32bitLE(0x08,sf_h) != 0x00000000 &&
|
||||
read_32bitBE(0x0c,sf_h) == header_size && /* variable per header */
|
||||
read_32bitBE(0x7c,sf_h) == 0 /* not DSP header */
|
||||
) {
|
||||
strwav->loop_start = 0; //read_32bitLE(0x24,streamHeader); //not ok?
|
||||
strwav->num_samples = read_32bitBE(0x30,streamHeader);
|
||||
strwav->loop_end = read_32bitBE(0x34,streamHeader);
|
||||
strwav->sample_rate = read_32bitBE(0x38,streamHeader);
|
||||
strwav->flags = read_32bitBE(0x3c,streamHeader);
|
||||
strwav->loop_start = 0; //read_32bitLE(0x24,sf_h); //not ok?
|
||||
strwav->num_samples = read_32bitBE(0x30,sf_h);
|
||||
strwav->loop_end = read_32bitBE(0x34,sf_h);
|
||||
strwav->sample_rate = read_32bitBE(0x38,sf_h);
|
||||
strwav->flags = read_32bitBE(0x3c,sf_h);
|
||||
|
||||
strwav->channels = read_32bitBE(0x70,streamHeader); /* tracks of 1ch */
|
||||
strwav->channels = read_32bitBE(0x70,sf_h); /* tracks of 1ch */
|
||||
strwav->loop_flag = strwav->flags & 0x01;
|
||||
strwav->interleave = strwav->channels > 4 ? 0x4000 : 0x8000;
|
||||
|
||||
|
@ -499,20 +545,20 @@ static int parse_header(STREAMFILE* streamHeader, strwav_header* strwav) {
|
|||
}
|
||||
|
||||
/* SpongeBob's Surf & Skate Roadtrip (X360)[2011] */
|
||||
if ((read_32bitBE(0x04,streamHeader) == 0x00000800 || /* used? */
|
||||
read_32bitBE(0x04,streamHeader) == 0x00000700) &&
|
||||
read_32bitLE(0x08,streamHeader) != 0x00000000 &&
|
||||
read_32bitBE(0x0c,streamHeader) == 0x124 && /* variable, not sure about final calc */
|
||||
read_32bitBE(0x8c,streamHeader) == 0x180 /* encoder delay actually */
|
||||
if ((read_32bitBE(0x04,sf_h) == 0x00000800 || /* used? */
|
||||
read_32bitBE(0x04,sf_h) == 0x00000700) &&
|
||||
read_32bitLE(0x08,sf_h) != 0x00000000 &&
|
||||
read_32bitBE(0x0c,sf_h) == 0x124 && /* variable, not sure about final calc */
|
||||
read_32bitBE(0x8c,sf_h) == 0x180 /* encoder delay actually */
|
||||
//0x4c is data_size + 0x210
|
||||
) {
|
||||
strwav->loop_start = 0; //read_32bitLE(0x24,streamHeader); //not ok?
|
||||
strwav->num_samples = read_32bitBE(0x30,streamHeader);//todo sometimes wrong?
|
||||
strwav->loop_end = read_32bitBE(0x34,streamHeader);
|
||||
strwav->sample_rate = read_32bitBE(0x38,streamHeader);
|
||||
strwav->flags = read_32bitBE(0x3c,streamHeader);
|
||||
strwav->loop_start = 0; //read_32bitLE(0x24,sf_h); //not ok?
|
||||
strwav->num_samples = read_32bitBE(0x30,sf_h);//todo sometimes wrong?
|
||||
strwav->loop_end = read_32bitBE(0x34,sf_h);
|
||||
strwav->sample_rate = read_32bitBE(0x38,sf_h);
|
||||
strwav->flags = read_32bitBE(0x3c,sf_h);
|
||||
|
||||
strwav->channels = read_32bitBE(0x70,streamHeader); /* multichannel XMA */
|
||||
strwav->channels = read_32bitBE(0x70,sf_h); /* multichannel XMA */
|
||||
strwav->loop_flag = strwav->flags & 0x01;
|
||||
|
||||
strwav->codec = XMA2;
|
||||
|
|
|
@ -35,8 +35,7 @@ VGMSTREAM* init_vgmstream_ta_aac(STREAMFILE* sf) {
|
|||
/* .aac: actual extension, .laac: for players to avoid hijacking MP4/AAC */
|
||||
if (!check_extensions(sf, "aac,laac"))
|
||||
goto fail;
|
||||
if (read_u32be(0x00, sf) != 0x41414320 &&
|
||||
read_u32le(0x00, sf) != 0x41414320) /* "AAC " */
|
||||
if (!is_id32be(0x00, sf, "AAC ") && !is_id32le(0x00, sf, "AAC "))
|
||||
goto fail;
|
||||
|
||||
if (!parse_aac(sf, &aac))
|
||||
|
@ -92,12 +91,28 @@ VGMSTREAM* init_vgmstream_ta_aac(STREAMFILE* sf) {
|
|||
}
|
||||
#endif
|
||||
#ifdef VGM_USE_ATRAC9
|
||||
case 0x08: { /* Judas Code (Vita) */
|
||||
case 0x08: /* Judas Code (Vita) */
|
||||
case 0x18: { /* Resonance of Fate (PS4) */
|
||||
atrac9_config cfg = {0};
|
||||
|
||||
cfg.channels = vgmstream->channels;
|
||||
|
||||
if (aac.codec == 0x08) {
|
||||
/* 0x00: ? (related to bitrate/channels?) */
|
||||
cfg.encoder_delay = read_s32le(aac.extra_offset + 0x04,sf);
|
||||
cfg.config_data = read_u32be(aac.extra_offset + 0x08,sf);
|
||||
}
|
||||
else {
|
||||
/* 0x00: ? (related to bitrate/channels?) */
|
||||
cfg.encoder_delay = read_s16le(aac.extra_offset + 0x04,sf);
|
||||
/* 0x06: samples per frame */
|
||||
/* 0x08: num samples (without encoder delay) */
|
||||
cfg.config_data = read_u32le(aac.extra_offset + 0x0c,sf);
|
||||
/* 0x10: loop start sample (without encoder delay) */
|
||||
/* 0x14: loop end sample (without encoder delay) */
|
||||
/* 0x18: related to loop start (adjustment? same as loop start when less than a sample) */
|
||||
/* using loop samples causes clicks in some tracks, so maybe it's info only,
|
||||
* or it's meant to be adjusted with value at 0x18 */
|
||||
}
|
||||
|
||||
vgmstream->codec_data = init_atrac9(&cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
|
@ -158,6 +173,7 @@ VGMSTREAM* init_vgmstream_ta_aac(STREAMFILE* sf) {
|
|||
}
|
||||
#endif
|
||||
default:
|
||||
VGM_LOG("AAC: unknown codec %x\n", aac.codec);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
@ -189,7 +205,7 @@ static int parse_aac_v1(STREAMFILE* sf, aac_header* aac) {
|
|||
/* 0x20: config? (0x00010003) */
|
||||
/* 0x30+ DIR + dirn subsongs */
|
||||
|
||||
if (read_u32be(0x30, sf) != 0x44495220) /* "DIR " */
|
||||
if (!is_id32be(0x30, sf, "DIR "))
|
||||
goto fail;
|
||||
aac->total_subsongs = read_u32be(0x40, sf);
|
||||
|
||||
|
@ -220,7 +236,7 @@ static int parse_aac_v1(STREAMFILE* sf, aac_header* aac) {
|
|||
}
|
||||
}
|
||||
|
||||
if (read_u32be(offset + 0x00, sf) != 0x57415645) /* "WAVE" */
|
||||
if (!is_id32be(offset + 0x00, sf, "WAVE"))
|
||||
goto fail;
|
||||
wave_offset = offset;
|
||||
offset += 0x10;
|
||||
|
@ -291,9 +307,9 @@ static int parse_aac_v2(STREAMFILE* sf, aac_header* aac) {
|
|||
if (target_subsong == 0) target_subsong = 1;
|
||||
aac->total_subsongs = 0;
|
||||
|
||||
if (read_u32be(start + 0x00, sf) == 0x414D4620) { /* "AMF " */
|
||||
if (is_id32be(start + 0x00, sf, "AMF ")) {
|
||||
/* GUID subsongs */
|
||||
if (read_u32be(start + 0x10, sf) != 0x68656164) /* "head" */
|
||||
if (!is_id32be(start + 0x10, sf, "head"))
|
||||
goto fail;
|
||||
size = read_u32be(start + 0x10 + 0x10, sf);
|
||||
|
||||
|
@ -321,7 +337,7 @@ static int parse_aac_v2(STREAMFILE* sf, aac_header* aac) {
|
|||
test_offset += entry_size;
|
||||
}
|
||||
}
|
||||
else if (read_u32be(start + 0x00, sf) == 0x41534320) { /* "ASC " */
|
||||
else if (is_id32be(start + 0x00, sf, "ASC ")) {
|
||||
/* regular subsongs */
|
||||
offset = 0;
|
||||
for (test_offset = 0x30; test_offset < start; test_offset += 0x10) {
|
||||
|
@ -342,14 +358,14 @@ static int parse_aac_v2(STREAMFILE* sf, aac_header* aac) {
|
|||
|
||||
if (target_subsong < 0 || target_subsong > aac->total_subsongs || aac->total_subsongs < 1) goto fail;
|
||||
|
||||
if (read_u32be(offset + 0x00, sf) != 0x41534320) /* "ASC " */
|
||||
if (!is_id32be(offset + 0x00, sf, "ASC "))
|
||||
goto fail;
|
||||
asc_offset = offset;
|
||||
|
||||
/* ASC section has offsets to "PLBK" chunk (?) and "WAVE" (header), may be followed by "VRC " (?) */
|
||||
/* 0x50: PLBK offset */
|
||||
offset += read_u32be(offset + 0x54, sf); /* WAVE offset */
|
||||
if (read_u32be(offset + 0x00, sf) != 0x57415645) /* "WAVE" */
|
||||
if (!is_id32be(offset + 0x00, sf, "WAVE"))
|
||||
goto fail;
|
||||
offset += 0x10;
|
||||
|
||||
|
@ -415,14 +431,14 @@ static int parse_aac_v3(STREAMFILE* sf, aac_header* aac) {
|
|||
/* 0x00: id */
|
||||
/* 0x04: size */
|
||||
/* 0x10: config? (0x00020100/0x00020002/0x00020301/etc) */
|
||||
/* 0x14: platform ("VITA"=Vita, "DRD "=Android, "MSPC"=PC) */
|
||||
/* 0x14: platform ("VITA"=Vita, "DRD "=Android, "MSPC"=PC, "PS4 "=PS4) */
|
||||
|
||||
/* offsets table: offset + flag? + size + align? */
|
||||
offset = read_u32le(0x20, sf); /* "AAOB" table (audio object?) */
|
||||
/* 0x30: "VRCB" table (some cue/config? related to subsongs? may be empty) */
|
||||
/* 0x40: "WAVB" table (wave body, has offset + size per stream then data, not needed since offsets are elsewhere too) */
|
||||
|
||||
if (read_u32le(offset + 0x00, sf) != 0x41414F42) /* "AAOB" */
|
||||
if (!is_id32le(offset + 0x00, sf, "AAOB"))
|
||||
goto fail;
|
||||
size = read_u32le(offset + 0x04, sf);
|
||||
|
||||
|
@ -436,7 +452,7 @@ static int parse_aac_v3(STREAMFILE* sf, aac_header* aac) {
|
|||
uint32_t entry_offset = read_u32le(test_offset + 0x00, sf);
|
||||
/* 0x04: entry size */
|
||||
|
||||
if (entry_offset == 0x41414F20) /* reached first "AAO " */
|
||||
if (entry_offset == get_id32be("AAO ")) /* reached end */
|
||||
break;
|
||||
|
||||
if (entry_offset) { /* often 0 */
|
||||
|
@ -450,14 +466,14 @@ static int parse_aac_v3(STREAMFILE* sf, aac_header* aac) {
|
|||
|
||||
if (target_subsong < 0 || target_subsong > aac->total_subsongs || aac->total_subsongs < 1) goto fail;
|
||||
|
||||
if (read_u32le(offset + 0x00, sf) != 0x41414F20) /* "AAO " */
|
||||
if (!is_id32le(offset + 0x00, sf, "AAO "))
|
||||
goto fail;
|
||||
|
||||
|
||||
/* AAO section has offsets to "PLBK" chunk (?) and "WAVE" (header) */
|
||||
/* 0x14: PLBK offset */
|
||||
offset += read_u32le(offset + 0x18, sf); /* WAVE offset */
|
||||
if (read_u32le(offset + 0x00, sf) != 0x57415645) /* "WAVE" */
|
||||
if (!is_id32le(offset + 0x00, sf, "WAVE"))
|
||||
goto fail;
|
||||
offset += 0x10;
|
||||
|
||||
|
|
|
@ -307,10 +307,20 @@ static inline int min_s32(int32_t a, int32_t b) { return a < b ? a : b; }
|
|||
//align32, align16, clamp16, etc
|
||||
#endif
|
||||
|
||||
static inline const int is_id32be(off_t offset, STREAMFILE* sf, const char* s) {
|
||||
/* fastest to compare would be read_u32x == (uint32), but should be pre-optimized (see get_id32x) */
|
||||
static inline /*const*/ int is_id32be(off_t offset, STREAMFILE* sf, const char* s) {
|
||||
return read_u32be(offset, sf) == get_id32be(s);
|
||||
}
|
||||
|
||||
static inline /*const*/ int is_id32le(off_t offset, STREAMFILE* sf, const char* s) {
|
||||
return read_u32le(offset, sf) == get_id32be(s);
|
||||
}
|
||||
|
||||
static inline /*const*/ int is_id64be(off_t offset, STREAMFILE* sf, const char* s) {
|
||||
return read_u64be(offset, sf) == get_id64be(s);
|
||||
}
|
||||
|
||||
|
||||
//TODO: maybe move to streamfile.c
|
||||
/* guess byte endianness from a given value, return true if big endian and false if little endian */
|
||||
static inline int guess_endianness16bit(off_t offset, STREAMFILE* sf) {
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
#ifndef _UTIL_H
|
||||
#define _UTIL_H
|
||||
|
||||
/* very common functions, so static inline in .h is useful to avoid some call overhead */
|
||||
/* very common functions, so static (inline) in .h as compiler can optimize to avoid some call overhead */
|
||||
|
||||
/* host endian independent multi-byte integer reading */
|
||||
|
||||
|
@ -98,10 +98,31 @@ static inline int clamp16(int32_t val) {
|
|||
else return val;
|
||||
}
|
||||
|
||||
static inline const uint32_t get_id32be(const char* s) {
|
||||
|
||||
/* transforms a string to uint32 (for comparison), but if this is static + all goes well
|
||||
* compiler should pre-calculate and use uint32 directly */
|
||||
static inline /*const*/ uint32_t get_id32be(const char* s) {
|
||||
return (uint32_t)(s[0] << 24) | (s[1] << 16) | (s[2] << 8) | (s[3] << 0);
|
||||
}
|
||||
|
||||
//static inline /*const*/ uint32_t get_id32le(const char* s) {
|
||||
// return (uint32_t)(s[0] << 0) | (s[1] << 8) | (s[2] << 16) | (s[3] << 24);
|
||||
//}
|
||||
|
||||
static inline /*const*/ uint64_t get_id64be(const char* s) {
|
||||
return (uint64_t)(
|
||||
((uint64_t)s[0] << 56) |
|
||||
((uint64_t)s[1] << 48) |
|
||||
((uint64_t)s[2] << 40) |
|
||||
((uint64_t)s[3] << 32) |
|
||||
((uint64_t)s[4] << 24) |
|
||||
((uint64_t)s[5] << 16) |
|
||||
((uint64_t)s[6] << 8) |
|
||||
((uint64_t)s[7] << 0)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/* less common functions, no need to inline */
|
||||
|
||||
int round10(int val);
|
||||
|
|
|
@ -463,7 +463,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
|
|||
init_vgmstream_acb,
|
||||
init_vgmstream_rad,
|
||||
init_vgmstream_smk,
|
||||
init_vgmstream_mzrt,
|
||||
init_vgmstream_mzrt_v0,
|
||||
init_vgmstream_xavs,
|
||||
init_vgmstream_psf_single,
|
||||
init_vgmstream_psf_segmented,
|
||||
|
@ -517,6 +517,11 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
|
|||
init_vgmstream_dsp_cwac,
|
||||
init_vgmstream_ifs,
|
||||
init_vgmstream_acx,
|
||||
init_vgmstream_compresswave,
|
||||
init_vgmstream_ktac,
|
||||
init_vgmstream_mjb_mjh,
|
||||
init_vgmstream_mzrt_v1,
|
||||
init_vgmstream_bsnf,
|
||||
|
||||
/* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */
|
||||
init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */
|
||||
|
@ -1333,6 +1338,10 @@ static STREAMFILE* get_vgmstream_average_bitrate_channel_streamfile(VGMSTREAM* v
|
|||
return acm_get_streamfile(vgmstream->codec_data);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_COMPRESSWAVE) {
|
||||
return compresswave_get_streamfile(vgmstream->codec_data);
|
||||
}
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
if (vgmstream->coding_type == coding_OGG_VORBIS) {
|
||||
return ogg_vorbis_get_streamfile(vgmstream->codec_data);
|
||||
|
|
|
@ -28,6 +28,7 @@ enum { VGMSTREAM_MAX_NUM_SAMPLES = 1000000000 }; /* no ~5h vgm hopefully */
|
|||
//#define VGM_USE_FFMPEG
|
||||
//#define VGM_USE_ATRAC9
|
||||
//#define VGM_USE_CELT
|
||||
//#define VGM_USE_SPEEX
|
||||
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
|
@ -178,6 +179,7 @@ typedef enum {
|
|||
coding_OKI4S, /* OKI 4-bit ADPCM with 16-bit output and cuadruple step */
|
||||
coding_PTADPCM, /* Platinum 4-bit ADPCM */
|
||||
coding_IMUSE, /* LucasArts iMUSE Variable ADPCM */
|
||||
coding_COMPRESSWAVE, /* CompressWave Huffman ADPCM */
|
||||
|
||||
/* others */
|
||||
coding_SDX2, /* SDX2 2:1 Squareroot-Delta-Exact compression DPCM */
|
||||
|
@ -234,6 +236,10 @@ typedef enum {
|
|||
coding_CELT_FSB, /* Custom Xiph CELT (MDCT-based) */
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_SPEEX
|
||||
coding_SPEEX, /* Custom Speex (CELP-based) */
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
coding_FFmpeg, /* Formats handled by FFmpeg (ATRAC3, XMA, AC3, etc) */
|
||||
#endif
|
||||
|
@ -361,7 +367,7 @@ typedef enum {
|
|||
meta_PS2_EXST, /* Shadow of Colossus EXST */
|
||||
meta_SVAG_KCET,
|
||||
meta_PS_HEADERLESS, /* headerless PS-ADPCM */
|
||||
meta_PS2_MIB_MIH, /* MIB File + MIH Header*/
|
||||
meta_MIB_MIH,
|
||||
meta_PS2_MIC, /* KOEI MIC File */
|
||||
meta_PS2_VAGi, /* VAGi Interleaved File */
|
||||
meta_PS2_VAGp, /* VAGp Mono File */
|
||||
|
@ -749,6 +755,10 @@ typedef enum {
|
|||
meta_SBK,
|
||||
meta_DSP_WIIADPCM,
|
||||
meta_DSP_CWAC,
|
||||
meta_COMPRESSWAVE,
|
||||
meta_KTAC,
|
||||
meta_MJB_MJH,
|
||||
meta_BSNF,
|
||||
} meta_t;
|
||||
|
||||
/* standard WAVEFORMATEXTENSIBLE speaker positions */
|
||||
|
|
Loading…
Reference in New Issue