Updated VGMStream to r1050-2895-g772fe03e
parent
5fbf722f29
commit
38b5dd6e3f
|
@ -11,6 +11,22 @@
|
|||
8301659A1F256BD000CA0941 /* txth.c in Sources */ = {isa = PBXBuildFile; fileRef = 830165971F256BD000CA0941 /* txth.c */; };
|
||||
8301659B1F256BD000CA0941 /* ea_schl_fixed.c in Sources */ = {isa = PBXBuildFile; fileRef = 830165981F256BD000CA0941 /* ea_schl_fixed.c */; };
|
||||
8301659C1F256BD000CA0941 /* nds_strm_ffta2.c in Sources */ = {isa = PBXBuildFile; fileRef = 830165991F256BD000CA0941 /* nds_strm_ffta2.c */; };
|
||||
83031EC4243C50A800C3F3E0 /* circus_decoder_miniz.h in Headers */ = {isa = PBXBuildFile; fileRef = 83031EBE243C50A700C3F3E0 /* circus_decoder_miniz.h */; };
|
||||
83031EC5243C50A800C3F3E0 /* circus_decoder_lib_data.h in Headers */ = {isa = PBXBuildFile; fileRef = 83031EBF243C50A800C3F3E0 /* circus_decoder_lib_data.h */; };
|
||||
83031EC6243C50A800C3F3E0 /* circus_decoder_miniz.c in Sources */ = {isa = PBXBuildFile; fileRef = 83031EC0243C50A800C3F3E0 /* circus_decoder_miniz.c */; };
|
||||
83031EC7243C50A800C3F3E0 /* circus_decoder_lib.h in Headers */ = {isa = PBXBuildFile; fileRef = 83031EC1243C50A800C3F3E0 /* circus_decoder_lib.h */; };
|
||||
83031EC8243C50A800C3F3E0 /* circus_decoder_lib.c in Sources */ = {isa = PBXBuildFile; fileRef = 83031EC2243C50A800C3F3E0 /* circus_decoder_lib.c */; };
|
||||
83031EC9243C50A800C3F3E0 /* circus_decoder_lzxpcm.h in Headers */ = {isa = PBXBuildFile; fileRef = 83031EC3243C50A800C3F3E0 /* circus_decoder_lzxpcm.h */; };
|
||||
83031ECC243C50CC00C3F3E0 /* blocked_ubi_sce.c in Sources */ = {isa = PBXBuildFile; fileRef = 83031ECA243C50CB00C3F3E0 /* blocked_ubi_sce.c */; };
|
||||
83031ECD243C50CC00C3F3E0 /* blocked_vid1.c in Sources */ = {isa = PBXBuildFile; fileRef = 83031ECB243C50CB00C3F3E0 /* blocked_vid1.c */; };
|
||||
83031ED1243C50DF00C3F3E0 /* encrypted.c in Sources */ = {isa = PBXBuildFile; fileRef = 83031ECE243C50DE00C3F3E0 /* encrypted.c */; };
|
||||
83031ED2243C50DF00C3F3E0 /* bkhd.c in Sources */ = {isa = PBXBuildFile; fileRef = 83031ECF243C50DE00C3F3E0 /* bkhd.c */; };
|
||||
83031ED3243C50DF00C3F3E0 /* fsb5_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 83031ED0243C50DF00C3F3E0 /* fsb5_streamfile.h */; };
|
||||
83031ED9243C510500C3F3E0 /* ubi_lyn_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 83031ED4243C510400C3F3E0 /* ubi_lyn_streamfile.h */; };
|
||||
83031EDA243C510500C3F3E0 /* mul_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 83031ED5243C510400C3F3E0 /* mul_streamfile.h */; };
|
||||
83031EDB243C510500C3F3E0 /* xnb_lz4mg.h in Headers */ = {isa = PBXBuildFile; fileRef = 83031ED6243C510400C3F3E0 /* xnb_lz4mg.h */; };
|
||||
83031EDC243C510500C3F3E0 /* vid1.c in Sources */ = {isa = PBXBuildFile; fileRef = 83031ED7243C510400C3F3E0 /* vid1.c */; };
|
||||
83031EDD243C510500C3F3E0 /* xnb_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 83031ED8243C510500C3F3E0 /* xnb_streamfile.h */; };
|
||||
8306B08420984518000302D4 /* at3plus_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B08120984517000302D4 /* at3plus_decoder.c */; };
|
||||
8306B08520984518000302D4 /* yamaha_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B08220984517000302D4 /* yamaha_decoder.c */; };
|
||||
8306B08620984518000302D4 /* fadpcm_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B08320984517000302D4 /* fadpcm_decoder.c */; };
|
||||
|
@ -48,7 +64,6 @@
|
|||
8306B0DE20984590000302D4 /* awc_xma_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 8306B0C32098458C000302D4 /* awc_xma_streamfile.h */; };
|
||||
8306B0DF20984590000302D4 /* ea_wve_ad10.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0C42098458D000302D4 /* ea_wve_ad10.c */; };
|
||||
8306B0E020984590000302D4 /* ppst_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 8306B0C52098458D000302D4 /* ppst_streamfile.h */; };
|
||||
8306B0E120984590000302D4 /* ubi_lyn_ogg_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 8306B0C62098458D000302D4 /* ubi_lyn_ogg_streamfile.h */; };
|
||||
8306B0E220984590000302D4 /* smv.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0C72098458D000302D4 /* smv.c */; };
|
||||
8306B0E420984590000302D4 /* wave.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0C92098458E000302D4 /* wave.c */; };
|
||||
8306B0E520984590000302D4 /* ubi_lyn.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0CA2098458E000302D4 /* ubi_lyn.c */; };
|
||||
|
@ -140,7 +155,6 @@
|
|||
8349A9101FE6258200E26435 /* ea_eaac.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8F71FE6257E00E26435 /* ea_eaac.c */; };
|
||||
8349A9111FE6258200E26435 /* bar_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 8349A8F81FE6257E00E26435 /* bar_streamfile.h */; };
|
||||
8349A9121FE6258200E26435 /* vsf_tta.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8F91FE6257E00E26435 /* vsf_tta.c */; };
|
||||
8349A9131FE6258200E26435 /* ngc_vid1.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8FA1FE6257E00E26435 /* ngc_vid1.c */; };
|
||||
8349A9141FE6258200E26435 /* omu.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8FB1FE6257F00E26435 /* omu.c */; };
|
||||
8349A9151FE6258200E26435 /* ps2_xa2_rrp.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8FC1FE6257F00E26435 /* ps2_xa2_rrp.c */; };
|
||||
8349A9161FE6258200E26435 /* flx.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8FD1FE6257F00E26435 /* flx.c */; };
|
||||
|
@ -197,7 +211,6 @@
|
|||
834FE107215C79ED000A5D3D /* mib_mih.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0DE215C79EB000A5D3D /* mib_mih.c */; };
|
||||
834FE108215C79ED000A5D3D /* hd3_bd3.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0DF215C79EB000A5D3D /* hd3_bd3.c */; };
|
||||
834FE109215C79ED000A5D3D /* idsp_ie.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0E0215C79EB000A5D3D /* idsp_ie.c */; };
|
||||
834FE10C215C79ED000A5D3D /* fsb5_interleave_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 834FE0E3215C79EC000A5D3D /* fsb5_interleave_streamfile.h */; };
|
||||
834FE10D215C79ED000A5D3D /* vag.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0E4215C79EC000A5D3D /* vag.c */; };
|
||||
834FE10E215C79ED000A5D3D /* ahv.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0E5215C79EC000A5D3D /* ahv.c */; };
|
||||
834FE10F215C79ED000A5D3D /* sdf.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0E6215C79EC000A5D3D /* sdf.c */; };
|
||||
|
@ -702,6 +715,22 @@
|
|||
830165971F256BD000CA0941 /* txth.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = txth.c; sourceTree = "<group>"; };
|
||||
830165981F256BD000CA0941 /* ea_schl_fixed.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ea_schl_fixed.c; sourceTree = "<group>"; };
|
||||
830165991F256BD000CA0941 /* nds_strm_ffta2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nds_strm_ffta2.c; sourceTree = "<group>"; };
|
||||
83031EBE243C50A700C3F3E0 /* circus_decoder_miniz.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = circus_decoder_miniz.h; sourceTree = "<group>"; };
|
||||
83031EBF243C50A800C3F3E0 /* circus_decoder_lib_data.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = circus_decoder_lib_data.h; sourceTree = "<group>"; };
|
||||
83031EC0243C50A800C3F3E0 /* circus_decoder_miniz.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = circus_decoder_miniz.c; sourceTree = "<group>"; };
|
||||
83031EC1243C50A800C3F3E0 /* circus_decoder_lib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = circus_decoder_lib.h; sourceTree = "<group>"; };
|
||||
83031EC2243C50A800C3F3E0 /* circus_decoder_lib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = circus_decoder_lib.c; sourceTree = "<group>"; };
|
||||
83031EC3243C50A800C3F3E0 /* circus_decoder_lzxpcm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = circus_decoder_lzxpcm.h; sourceTree = "<group>"; };
|
||||
83031ECA243C50CB00C3F3E0 /* blocked_ubi_sce.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_ubi_sce.c; sourceTree = "<group>"; };
|
||||
83031ECB243C50CB00C3F3E0 /* blocked_vid1.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_vid1.c; sourceTree = "<group>"; };
|
||||
83031ECE243C50DE00C3F3E0 /* encrypted.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = encrypted.c; sourceTree = "<group>"; };
|
||||
83031ECF243C50DE00C3F3E0 /* bkhd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bkhd.c; sourceTree = "<group>"; };
|
||||
83031ED0243C50DF00C3F3E0 /* fsb5_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fsb5_streamfile.h; sourceTree = "<group>"; };
|
||||
83031ED4243C510400C3F3E0 /* ubi_lyn_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ubi_lyn_streamfile.h; sourceTree = "<group>"; };
|
||||
83031ED5243C510400C3F3E0 /* mul_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mul_streamfile.h; sourceTree = "<group>"; };
|
||||
83031ED6243C510400C3F3E0 /* xnb_lz4mg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xnb_lz4mg.h; sourceTree = "<group>"; };
|
||||
83031ED7243C510400C3F3E0 /* vid1.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vid1.c; sourceTree = "<group>"; };
|
||||
83031ED8243C510500C3F3E0 /* xnb_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xnb_streamfile.h; sourceTree = "<group>"; };
|
||||
8306B08120984517000302D4 /* at3plus_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = at3plus_decoder.c; sourceTree = "<group>"; };
|
||||
8306B08220984517000302D4 /* yamaha_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = yamaha_decoder.c; sourceTree = "<group>"; };
|
||||
8306B08320984517000302D4 /* fadpcm_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fadpcm_decoder.c; sourceTree = "<group>"; };
|
||||
|
@ -739,7 +768,6 @@
|
|||
8306B0C32098458C000302D4 /* awc_xma_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = awc_xma_streamfile.h; sourceTree = "<group>"; };
|
||||
8306B0C42098458D000302D4 /* ea_wve_ad10.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ea_wve_ad10.c; sourceTree = "<group>"; };
|
||||
8306B0C52098458D000302D4 /* ppst_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ppst_streamfile.h; sourceTree = "<group>"; };
|
||||
8306B0C62098458D000302D4 /* ubi_lyn_ogg_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ubi_lyn_ogg_streamfile.h; sourceTree = "<group>"; };
|
||||
8306B0C72098458D000302D4 /* smv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = smv.c; sourceTree = "<group>"; };
|
||||
8306B0C92098458E000302D4 /* wave.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wave.c; sourceTree = "<group>"; };
|
||||
8306B0CA2098458E000302D4 /* ubi_lyn.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ubi_lyn.c; sourceTree = "<group>"; };
|
||||
|
@ -830,7 +858,6 @@
|
|||
8349A8F71FE6257E00E26435 /* ea_eaac.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ea_eaac.c; sourceTree = "<group>"; };
|
||||
8349A8F81FE6257E00E26435 /* bar_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bar_streamfile.h; sourceTree = "<group>"; };
|
||||
8349A8F91FE6257E00E26435 /* vsf_tta.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vsf_tta.c; sourceTree = "<group>"; };
|
||||
8349A8FA1FE6257E00E26435 /* ngc_vid1.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ngc_vid1.c; sourceTree = "<group>"; };
|
||||
8349A8FB1FE6257F00E26435 /* omu.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = omu.c; sourceTree = "<group>"; };
|
||||
8349A8FC1FE6257F00E26435 /* ps2_xa2_rrp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_xa2_rrp.c; sourceTree = "<group>"; };
|
||||
8349A8FD1FE6257F00E26435 /* flx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = flx.c; sourceTree = "<group>"; };
|
||||
|
@ -886,7 +913,6 @@
|
|||
834FE0DE215C79EB000A5D3D /* mib_mih.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mib_mih.c; sourceTree = "<group>"; };
|
||||
834FE0DF215C79EB000A5D3D /* hd3_bd3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hd3_bd3.c; sourceTree = "<group>"; };
|
||||
834FE0E0215C79EB000A5D3D /* idsp_ie.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = idsp_ie.c; sourceTree = "<group>"; };
|
||||
834FE0E3215C79EC000A5D3D /* fsb5_interleave_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fsb5_interleave_streamfile.h; sourceTree = "<group>"; };
|
||||
834FE0E4215C79EC000A5D3D /* vag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vag.c; sourceTree = "<group>"; };
|
||||
834FE0E5215C79EC000A5D3D /* ahv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ahv.c; sourceTree = "<group>"; };
|
||||
834FE0E6215C79EC000A5D3D /* sdf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sdf.c; sourceTree = "<group>"; };
|
||||
|
@ -1437,6 +1463,12 @@
|
|||
8306B08120984517000302D4 /* at3plus_decoder.c */,
|
||||
830EBE0F2004655D0023AA10 /* atrac9_decoder.c */,
|
||||
834FE0B2215C798C000A5D3D /* celt_fsb_decoder.c */,
|
||||
83031EBF243C50A800C3F3E0 /* circus_decoder_lib_data.h */,
|
||||
83031EC2243C50A800C3F3E0 /* circus_decoder_lib.c */,
|
||||
83031EC1243C50A800C3F3E0 /* circus_decoder_lib.h */,
|
||||
83031EC3243C50A800C3F3E0 /* circus_decoder_lzxpcm.h */,
|
||||
83031EC0243C50A800C3F3E0 /* circus_decoder_miniz.c */,
|
||||
83031EBE243C50A700C3F3E0 /* circus_decoder_miniz.h */,
|
||||
832BF7FC21E050B6006F50F1 /* circus_decoder.c */,
|
||||
831BA6221EAC61CB00CF89B0 /* coding_utils.c */,
|
||||
836F6DE518BDC2180095E648 /* coding.h */,
|
||||
|
@ -1540,8 +1572,10 @@
|
|||
8306B09C20984550000302D4 /* blocked_str_snds.c */,
|
||||
8306B09820984550000302D4 /* blocked_thp.c */,
|
||||
8306B09920984550000302D4 /* blocked_tra.c */,
|
||||
83031ECA243C50CB00C3F3E0 /* blocked_ubi_sce.c */,
|
||||
8349A8E61FE6253900E26435 /* blocked_vawx.c */,
|
||||
83AA5D1A1F6E2F7F0020821C /* blocked_vgs.c */,
|
||||
83031ECB243C50CB00C3F3E0 /* blocked_vid1.c */,
|
||||
832BF80321E050DC006F50F1 /* blocked_vs_square.c */,
|
||||
832BF80421E050DC006F50F1 /* blocked_vs_str.c */,
|
||||
8306B0A120984551000302D4 /* blocked_vs.c */,
|
||||
|
@ -1605,6 +1639,7 @@
|
|||
837CEAD723487E8300E62A4A /* bgw_streamfile.h */,
|
||||
836F6E3818BDC2180095E648 /* bgw.c */,
|
||||
83299FCE1E7660C7003A3242 /* bik.c */,
|
||||
83031ECF243C50DE00C3F3E0 /* bkhd.c */,
|
||||
837CEAD423487E8300E62A4A /* bmp_konami.c */,
|
||||
834FE0CA215C79E7000A5D3D /* bnk_sony.c */,
|
||||
8373342523F60CDC00DE14DC /* bnsf_keys.h */,
|
||||
|
@ -1643,6 +1678,7 @@
|
|||
83EED5D1203A8BC7008BEB45 /* ea_swvr.c */,
|
||||
8306B0C42098458D000302D4 /* ea_wve_ad10.c */,
|
||||
8306B0BF2098458C000302D4 /* ea_wve_au00.c */,
|
||||
83031ECE243C50DE00C3F3E0 /* encrypted.c */,
|
||||
836F6E4918BDC2180095E648 /* exakt_sc.c */,
|
||||
836F6E4A18BDC2180095E648 /* excitebots.c */,
|
||||
8349A8EF1FE6257C00E26435 /* ezw.c */,
|
||||
|
@ -1657,7 +1693,7 @@
|
|||
83A21F7E201D8980000F04B9 /* fsb_keys.h */,
|
||||
836F6E4C18BDC2180095E648 /* fsb.c */,
|
||||
83C7280522BC893B00678B4A /* fsb5_fev.c */,
|
||||
834FE0E3215C79EC000A5D3D /* fsb5_interleave_streamfile.h */,
|
||||
83031ED0243C50DF00C3F3E0 /* fsb5_streamfile.h */,
|
||||
83F5F8821908D0A400C8E65F /* fsb5.c */,
|
||||
8373342323F60CDB00DE14DC /* fwse.c */,
|
||||
834D3A6D19F47C98001C54F6 /* g1l.c */,
|
||||
|
@ -1714,6 +1750,7 @@
|
|||
83C7280E22BC893D00678B4A /* mta2_streamfile.h */,
|
||||
83C727FF22BC893900678B4A /* mta2.c */,
|
||||
83C7280322BC893A00678B4A /* mtaf.c */,
|
||||
83031ED5243C510400C3F3E0 /* mul_streamfile.h */,
|
||||
832BF81221E05149006F50F1 /* mul.c */,
|
||||
836F6E6218BDC2180095E648 /* mus_acm.c */,
|
||||
83C7280622BC893B00678B4A /* mus_vc.c */,
|
||||
|
@ -1747,7 +1784,6 @@
|
|||
8306B0BE2098458C000302D4 /* ngc_str_cauldron.c */,
|
||||
836F6E7C18BDC2180095E648 /* ngc_tydsp.c */,
|
||||
831BD11F1EEE1CF200198540 /* ngc_ulw.c */,
|
||||
8349A8FA1FE6257E00E26435 /* ngc_vid1.c */,
|
||||
836F6E7E18BDC2180095E648 /* ngc_ymf.c */,
|
||||
836F6E7F18BDC2180095E648 /* ngca.c */,
|
||||
83C727FC22BC893900678B4A /* nps.c */,
|
||||
|
@ -1906,7 +1942,7 @@
|
|||
836F6EFC18BDC2190095E648 /* ubi_ckd.c */,
|
||||
837CEAE023487F2A00E62A4A /* ubi_hx.c */,
|
||||
8306B0D720984590000302D4 /* ubi_jade.c */,
|
||||
8306B0C62098458D000302D4 /* ubi_lyn_ogg_streamfile.h */,
|
||||
83031ED4243C510400C3F3E0 /* ubi_lyn_streamfile.h */,
|
||||
8306B0CA2098458E000302D4 /* ubi_lyn.c */,
|
||||
831BA6131EAC61A500CF89B0 /* ubi_raki.c */,
|
||||
8375737521F950EC00F01AF5 /* ubi_sb_streamfile.h */,
|
||||
|
@ -1917,6 +1953,7 @@
|
|||
834FE0D3215C79E9000A5D3D /* vai.c */,
|
||||
831BA6141EAC61A500CF89B0 /* vawx.c */,
|
||||
836F6EFD18BDC2190095E648 /* vgs.c */,
|
||||
83031ED7243C510400C3F3E0 /* vid1.c */,
|
||||
834FE0CE215C79E8000A5D3D /* vis.c */,
|
||||
834FE0D4215C79E9000A5D3D /* vpk.c */,
|
||||
832BF81821E0514A006F50F1 /* vs_square.c */,
|
||||
|
@ -1960,6 +1997,8 @@
|
|||
834FE0DC215C79EA000A5D3D /* xmd.c */,
|
||||
837CEAEC23487F2C00E62A4A /* xmu.c */,
|
||||
837CEAE323487F2A00E62A4A /* xmv_valve.c */,
|
||||
83031ED6243C510400C3F3E0 /* xnb_lz4mg.h */,
|
||||
83031ED8243C510500C3F3E0 /* xnb_streamfile.h */,
|
||||
830EBE112004656E0023AA10 /* xnb.c */,
|
||||
832BF81921E0514A006F50F1 /* xopus.c */,
|
||||
832BF80A21E05148006F50F1 /* xpcm.c */,
|
||||
|
@ -2012,6 +2051,7 @@
|
|||
files = (
|
||||
836F705518BDC2190095E648 /* streamtypes.h in Headers */,
|
||||
836F6F3518BDC2190095E648 /* nwa_decoder.h in Headers */,
|
||||
83031EC9243C50A800C3F3E0 /* circus_decoder_lzxpcm.h in Headers */,
|
||||
83C7282222BC893D00678B4A /* mta2_streamfile.h in Headers */,
|
||||
83A21F87201D8981000F04B9 /* fsb_keys.h in Headers */,
|
||||
834FE0EC215C79ED000A5D3D /* kma9_streamfile.h in Headers */,
|
||||
|
@ -2031,6 +2071,7 @@
|
|||
832BF82021E0514B006F50F1 /* zsnd_streamfile.h in Headers */,
|
||||
8375737721F950ED00F01AF5 /* ubi_sb_streamfile.h in Headers */,
|
||||
839E21E01F2EDAF100EE54D7 /* vorbis_custom_data_fsb.h in Headers */,
|
||||
83031EDB243C510500C3F3E0 /* xnb_lz4mg.h in Headers */,
|
||||
836F705418BDC2190095E648 /* streamfile.h in Headers */,
|
||||
83A3F0761E3AD8B900D6A794 /* stack_alloc.h in Headers */,
|
||||
8373342723F60CDC00DE14DC /* lrmd_streamfile.h in Headers */,
|
||||
|
@ -2039,15 +2080,14 @@
|
|||
83C7280F22BC893D00678B4A /* xwb_xsb.h in Headers */,
|
||||
83C7281622BC893D00678B4A /* xwma_konami_streamfile.h in Headers */,
|
||||
839E21E81F2EDAF100EE54D7 /* mpeg_decoder.h in Headers */,
|
||||
834FE10C215C79ED000A5D3D /* fsb5_interleave_streamfile.h in Headers */,
|
||||
839E21E51F2EDAF100EE54D7 /* vorbis_custom_decoder.h in Headers */,
|
||||
837CEADB23487E8300E62A4A /* bgw_streamfile.h in Headers */,
|
||||
837CEB0323487F2C00E62A4A /* xavs_streamfile.h in Headers */,
|
||||
8306B0E120984590000302D4 /* ubi_lyn_ogg_streamfile.h in Headers */,
|
||||
836F705918BDC2190095E648 /* vgmstream.h in Headers */,
|
||||
8373342623F60CDC00DE14DC /* deblock_streamfile.h in Headers */,
|
||||
83C7281522BC893D00678B4A /* txth_streamfile.h in Headers */,
|
||||
8306B0DE20984590000302D4 /* awc_xma_streamfile.h in Headers */,
|
||||
83031EC5243C50A800C3F3E0 /* circus_decoder_lib_data.h in Headers */,
|
||||
834FE0B5215C798C000A5D3D /* acm_decoder_libacm.h in Headers */,
|
||||
839E21E61F2EDAF100EE54D7 /* vorbis_custom_data_wwise.h in Headers */,
|
||||
8373342D23F60CDC00DE14DC /* bnsf_keys.h in Headers */,
|
||||
|
@ -2059,19 +2099,25 @@
|
|||
836F6F9A18BDC2190095E648 /* meta.h in Headers */,
|
||||
8306B0D820984590000302D4 /* ea_eaac_streamfile.h in Headers */,
|
||||
837CEB0523487F2C00E62A4A /* sqex_sead_streamfile.h in Headers */,
|
||||
83031ED9243C510500C3F3E0 /* ubi_lyn_streamfile.h in Headers */,
|
||||
8373341D23F60C7B00DE14DC /* g7221_decoder_lib.h in Headers */,
|
||||
8306B0E820984590000302D4 /* opus_interleave_streamfile.h in Headers */,
|
||||
83031EDD243C510500C3F3E0 /* xnb_streamfile.h in Headers */,
|
||||
8373341623F60C7B00DE14DC /* g7221_decoder_aes.h in Headers */,
|
||||
8373341923F60C7B00DE14DC /* g7221_decoder_lib_data.h in Headers */,
|
||||
83AFABBD23795202002F3947 /* ea_eaac_opus_streamfile.h in Headers */,
|
||||
83031EDA243C510500C3F3E0 /* mul_streamfile.h in Headers */,
|
||||
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 */,
|
||||
83269DD22399F5DE00F49FE3 /* nus3bank_streamfile.h in Headers */,
|
||||
83AA5D251F6E2F9C0020821C /* hca_keys.h in Headers */,
|
||||
83031EC7243C50A800C3F3E0 /* circus_decoder_lib.h in Headers */,
|
||||
83031ED3243C50DF00C3F3E0 /* fsb5_streamfile.h in Headers */,
|
||||
836F6F2318BDC2190095E648 /* coding.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -2435,7 +2481,6 @@
|
|||
83299FD11E7660C7003A3242 /* dsp_adx.c in Sources */,
|
||||
836F704018BDC2190095E648 /* wii_sng.c in Sources */,
|
||||
834FE10D215C79ED000A5D3D /* vag.c in Sources */,
|
||||
8349A9131FE6258200E26435 /* ngc_vid1.c in Sources */,
|
||||
8350C0551E071881009E0A93 /* xma.c in Sources */,
|
||||
837CEAF523487F2C00E62A4A /* ubi_hx.c in Sources */,
|
||||
836F6F3218BDC2190095E648 /* ngc_dsp_decoder.c in Sources */,
|
||||
|
@ -2479,6 +2524,7 @@
|
|||
8306B0BA20984552000302D4 /* blocked_wsi.c in Sources */,
|
||||
83BAFB6C19F45EB3005DAB60 /* bfstm.c in Sources */,
|
||||
836F704418BDC2190095E648 /* ws_aud.c in Sources */,
|
||||
83031ED2243C50DF00C3F3E0 /* bkhd.c in Sources */,
|
||||
8373342F23F60D4100DE14DC /* tgc.c in Sources */,
|
||||
83709E081ECBC1A4005C03D3 /* ps2_rxws.c in Sources */,
|
||||
8373342823F60CDC00DE14DC /* fda.c in Sources */,
|
||||
|
@ -2513,6 +2559,7 @@
|
|||
836F6FE618BDC2190095E648 /* ps2_mcg.c in Sources */,
|
||||
83AA5D241F6E2F9C0020821C /* awc.c in Sources */,
|
||||
8349A8E91FE6253900E26435 /* blocked_ea_1snh.c in Sources */,
|
||||
83031EC6243C50A800C3F3E0 /* circus_decoder_miniz.c in Sources */,
|
||||
8306B0E620984590000302D4 /* msb_msh.c in Sources */,
|
||||
836F702918BDC2190095E648 /* sat_sap.c in Sources */,
|
||||
8351F3292212B53400A606E4 /* dsa_decoder.c in Sources */,
|
||||
|
@ -2521,6 +2568,7 @@
|
|||
831BA6281EAC61CB00CF89B0 /* coding_utils.c in Sources */,
|
||||
8373342B23F60CDC00DE14DC /* fwse.c in Sources */,
|
||||
837CEAFF23487F2C00E62A4A /* raw_snds.c in Sources */,
|
||||
83031EDC243C510500C3F3E0 /* vid1.c in Sources */,
|
||||
836F700218BDC2190095E648 /* ps2_tk5.c in Sources */,
|
||||
83AA5D271F6E2F9C0020821C /* stm.c in Sources */,
|
||||
831BA61D1EAC61A500CF89B0 /* ubi_raki.c in Sources */,
|
||||
|
@ -2563,6 +2611,7 @@
|
|||
834FE0F5215C79ED000A5D3D /* wv2.c in Sources */,
|
||||
836F703D18BDC2190095E648 /* wii_mus.c in Sources */,
|
||||
836F700D18BDC2190095E648 /* ps2_wmus.c in Sources */,
|
||||
83031EC8243C50A800C3F3E0 /* circus_decoder_lib.c in Sources */,
|
||||
831BA61C1EAC61A500CF89B0 /* sxd.c in Sources */,
|
||||
836F6F8018BDC2190095E648 /* dsp_bdsp.c in Sources */,
|
||||
836F6F9618BDC2190095E648 /* lsf.c in Sources */,
|
||||
|
@ -2576,14 +2625,17 @@
|
|||
8306B0B120984552000302D4 /* blocked_halpst.c in Sources */,
|
||||
836F6FD018BDC2190095E648 /* ps2_b1s.c in Sources */,
|
||||
83AA5D181F6E2F600020821C /* mpeg_custom_utils_awc.c in Sources */,
|
||||
83031ED1243C50DF00C3F3E0 /* encrypted.c in Sources */,
|
||||
834FE0FE215C79ED000A5D3D /* ps_headerless.c in Sources */,
|
||||
8306B0BB20984552000302D4 /* blocked_gsb.c in Sources */,
|
||||
83031ECC243C50CC00C3F3E0 /* blocked_ubi_sce.c in Sources */,
|
||||
836F6F7718BDC2190095E648 /* capdsp.c in Sources */,
|
||||
836F6FB018BDC2190095E648 /* ngc_dsp_ygo.c in Sources */,
|
||||
836F703318BDC2190095E648 /* str_snds.c in Sources */,
|
||||
832BF82221E0514B006F50F1 /* vs_str.c in Sources */,
|
||||
8349A9191FE6258200E26435 /* afc.c in Sources */,
|
||||
836F703718BDC2190095E648 /* tun.c in Sources */,
|
||||
83031ECD243C50CC00C3F3E0 /* blocked_vid1.c in Sources */,
|
||||
836F700B18BDC2190095E648 /* ps2_wad.c in Sources */,
|
||||
8349A9161FE6258200E26435 /* flx.c in Sources */,
|
||||
832BF82921E0514B006F50F1 /* msf_banpresto.c in Sources */,
|
||||
|
|
|
@ -54,3 +54,8 @@ void decode_asf(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing,
|
|||
stream->adpcm_history1_32 = hist1;
|
||||
stream->adpcm_history2_32 = hist2;
|
||||
}
|
||||
|
||||
int32_t asf_bytes_to_samples(size_t bytes, int channels) {
|
||||
if (channels <= 0) return 0;
|
||||
return bytes / channels / 0x11 * 32;
|
||||
}
|
||||
|
|
|
@ -1,29 +1,109 @@
|
|||
#include "coding.h"
|
||||
|
||||
|
||||
/* Circus XPCM mode 2 decoding, verified vs EF.exe (info from foo_adpcm/libpcm and https://github.com/lioncash/ExtractData) */
|
||||
void decode_circus_adpcm(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
|
||||
int i, sample_pos = 0;
|
||||
int32_t hist = stream->adpcm_history1_32;
|
||||
int scale = stream->adpcm_scale;
|
||||
off_t frame_offset = stream->offset; /* frame size is 1 */
|
||||
|
||||
|
||||
for (i = first_sample; i < first_sample + samples_to_do; i++) {
|
||||
int8_t code = read_8bit(frame_offset+i,stream->streamfile);
|
||||
|
||||
hist += code << scale;
|
||||
if (code == 0) {
|
||||
if (scale > 0)
|
||||
scale--;
|
||||
}
|
||||
else if (code == 127 || code == -128) {
|
||||
if (scale < 8)
|
||||
scale++;
|
||||
}
|
||||
outbuf[sample_pos] = hist;
|
||||
}
|
||||
|
||||
stream->adpcm_history1_32 = hist;
|
||||
stream->adpcm_scale = scale;
|
||||
}
|
||||
#include "coding.h"
|
||||
#include "circus_decoder_lib.h"
|
||||
|
||||
|
||||
|
||||
struct circus_codec_data {
|
||||
STREAMFILE* sf;
|
||||
int16_t* buf;
|
||||
int buf_samples_all;
|
||||
circus_handle_t* handle;
|
||||
};
|
||||
|
||||
|
||||
circus_codec_data* init_circus_vq(STREAMFILE* sf, off_t start, uint8_t codec, uint8_t flags) {
|
||||
circus_codec_data* data = NULL;
|
||||
|
||||
data = calloc(1, sizeof(circus_codec_data));
|
||||
if (!data) goto fail;
|
||||
|
||||
data->sf = reopen_streamfile(sf, 0);
|
||||
data->handle = circus_init(start, codec, flags);
|
||||
if (!data->handle) goto fail;
|
||||
|
||||
return data;
|
||||
fail:
|
||||
free_circus_vq(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void decode_circus_vq(circus_codec_data* data, sample_t* outbuf, int32_t samples_to_do, int channels) {
|
||||
int ok, i, samples_to_get;
|
||||
|
||||
while (samples_to_do > 0) {
|
||||
if (data->buf_samples_all == 0) {
|
||||
ok = circus_decode_frame(data->handle, data->sf, &data->buf, &data->buf_samples_all);
|
||||
if (!ok) goto decode_fail;
|
||||
}
|
||||
|
||||
samples_to_get = data->buf_samples_all / channels;
|
||||
if (samples_to_get > samples_to_do)
|
||||
samples_to_get = samples_to_do;
|
||||
|
||||
for (i = 0; i < samples_to_get * channels; i++) {
|
||||
outbuf[i] = data->buf[i];
|
||||
}
|
||||
|
||||
data->buf += samples_to_get * channels;
|
||||
data->buf_samples_all -= samples_to_get * channels;
|
||||
outbuf += samples_to_get * channels;
|
||||
samples_to_do -= samples_to_get;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
decode_fail:
|
||||
VGM_LOG("CIRCUS: decode error\n");
|
||||
memset(outbuf, 0, samples_to_do * channels * sizeof(sample_t));
|
||||
}
|
||||
|
||||
void reset_circus_vq(circus_codec_data* data) {
|
||||
if (!data) return;
|
||||
|
||||
circus_reset(data->handle);
|
||||
data->buf_samples_all = 0;
|
||||
}
|
||||
|
||||
void seek_circus_vq(circus_codec_data* data, int32_t num_sample) {
|
||||
if (!data) return;
|
||||
|
||||
reset_circus_vq(data);
|
||||
//data->samples_discard = num_sample; //todo (xpcm don't have loop points tho)
|
||||
}
|
||||
|
||||
void free_circus_vq(circus_codec_data* data) {
|
||||
if (!data) return;
|
||||
|
||||
close_streamfile(data->sf);
|
||||
circus_free(data->handle);
|
||||
free(data);
|
||||
}
|
||||
|
||||
/* ************************************************************************* */
|
||||
|
||||
/* Circus XPCM mode 2 decoding, verified vs EF.exe (info from foo_adpcm/libpcm and https://github.com/lioncash/ExtractData) */
|
||||
void decode_circus_adpcm(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
|
||||
int i, sample_pos = 0;
|
||||
int32_t hist = stream->adpcm_history1_32;
|
||||
int scale = stream->adpcm_scale;
|
||||
off_t frame_offset = stream->offset; /* frame size is 1 */
|
||||
|
||||
|
||||
for (i = first_sample; i < first_sample + samples_to_do; i++) {
|
||||
int8_t code = read_8bit(frame_offset+i,stream->streamfile);
|
||||
|
||||
hist += code << scale;
|
||||
if (code == 0) {
|
||||
if (scale > 0)
|
||||
scale--;
|
||||
}
|
||||
else if (code == 127 || code == -128) {
|
||||
if (scale < 8)
|
||||
scale++;
|
||||
}
|
||||
outbuf[sample_pos] = hist;
|
||||
}
|
||||
|
||||
stream->adpcm_history1_32 = hist;
|
||||
stream->adpcm_scale = scale;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,492 @@
|
|||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* Decodes Circus's audio codec, reverse engineered from various .exe.
|
||||
*
|
||||
* Some sources identify this codec as VQ (vector quantization), though vector(?)
|
||||
* data isn't actually bitpacked and just compressed using custom LZ or standard zlib.
|
||||
* Channels aren't divided either so decoding results in N-ch interleaved PCM.
|
||||
* It does seem to be using LPC/speech stuff from VQ codecs though.
|
||||
*
|
||||
* Some info from Japanese libpcm.c found in foo_adpcm
|
||||
* https://bitbucket.org/losnoco/foo_adpcm/src/master/foo_oki/source/libpcm/libpcm.cpp
|
||||
*/
|
||||
|
||||
#include "circus_decoder_lib.h"
|
||||
#include "circus_decoder_lib_data.h"
|
||||
|
||||
#include "circus_decoder_lzxpcm.h"
|
||||
|
||||
/* use miniz (API-compatible) to avoid adding external zlib just for this codec
|
||||
* - https://github.com/richgel999/miniz */
|
||||
#include "circus_decoder_miniz.h"
|
||||
//#include "zlib.h"
|
||||
|
||||
|
||||
//#define XPCM_CODEC_PCM 0
|
||||
#define XPCM_CODEC_VQ_LZXPCM 1
|
||||
//#define XPCM_CODEC_ADPCM 2
|
||||
#define XPCM_CODEC_VQ_DEFLATE 3
|
||||
|
||||
/* frame encodes 4096 PCM samples (all channels) = 4096*2 = 0x2000 bytes, re-interleaved then compressed */
|
||||
#define XPCM_FRAME_SIZE (4096 * 2)
|
||||
#define XPCM_FRAME_CODES 4096
|
||||
#define XPCM_FRAME_SAMPLES_ALL 4064
|
||||
#define XPCM_FRAME_OVERLAP_ALL 32
|
||||
#define XPCM_INPUT_SIZE 0x8000
|
||||
|
||||
/* ************************************************************************* */
|
||||
/* DECODE */
|
||||
/* ************************************************************************* */
|
||||
|
||||
struct circus_handle_t {
|
||||
/* config */
|
||||
off_t start;
|
||||
uint8_t codec;
|
||||
uint8_t flags;
|
||||
const int* scales;
|
||||
|
||||
/* temp buffers */
|
||||
uint8_t srcbuf[XPCM_INPUT_SIZE]; /* compressed input data (arbitrary size) */
|
||||
uint8_t decbuf[XPCM_FRAME_SIZE]; /* single decompressed frame */
|
||||
uint8_t intbuf[XPCM_FRAME_SIZE]; /* re-interleaved frame */
|
||||
int32_t invbuf[XPCM_FRAME_CODES]; /* main LPC data (may need less) */
|
||||
int32_t tmpbuf[XPCM_FRAME_CODES]; /* temp LPC data (may need less) */
|
||||
|
||||
/* output samples (original code reuses decbuf though) */
|
||||
int16_t pcmbuf[XPCM_FRAME_SAMPLES_ALL + XPCM_FRAME_OVERLAP_ALL]; /* final output samples and extra overlap samples */
|
||||
|
||||
/* sample filter state */
|
||||
int hist1;
|
||||
int hist2;
|
||||
int frame;
|
||||
|
||||
/* lz/deflate decompression state */
|
||||
lzxpcm_stream_t lstrm;
|
||||
z_stream dstrm;
|
||||
off_t offset;
|
||||
};
|
||||
|
||||
|
||||
static void convert(uint8_t flags, int32_t* invbuf, int16_t* pcmbuf, int* p_hist1, int* p_hist2, int frame) {
|
||||
int i;
|
||||
int sample, hist1, hist2;
|
||||
|
||||
hist1 = *p_hist1;
|
||||
hist2 = *p_hist2;
|
||||
|
||||
/* some ops below would use SHRs (>>), but there is some rounding in the original
|
||||
* ASM that decompiles and I think corresponds do DIVs (right shift and divs of
|
||||
* negative values isn't equivalent). Similarly the filters seem to use CDQ tricks
|
||||
* to simulate s64 ops, but I'm not sure casting is 100% equivalent (sounds ok tho). */
|
||||
|
||||
/* do final filtering and conversion to PCM */
|
||||
for (i = 0; i < XPCM_FRAME_SAMPLES_ALL + XPCM_FRAME_OVERLAP_ALL; i++) {
|
||||
sample = *invbuf++;
|
||||
if (flags & 0x10)
|
||||
sample = (3 * (int64_t)sample / 2) / 1024; //>> 10;
|
||||
else
|
||||
sample = sample / 1024; //>> 10;
|
||||
|
||||
sample = ((27 * (int64_t)sample + 4 * hist1 + hist2) << 11) / 65536; //>> 16
|
||||
|
||||
hist2 = hist1;
|
||||
hist1 = sample;
|
||||
|
||||
/* last 32 decoded samples aren't output, but are used next frame to overlap
|
||||
* with beginning samples (filters(?) windowing, not too noticeable though) */
|
||||
if (i < XPCM_FRAME_OVERLAP_ALL && frame > 0) {
|
||||
sample = ((i * (int64_t)sample) + ((XPCM_FRAME_OVERLAP_ALL - i) * pcmbuf[XPCM_FRAME_SAMPLES_ALL + i])) / 32; //>> 5
|
||||
}
|
||||
|
||||
if (sample > 32767)
|
||||
sample = 32767;
|
||||
else if (sample < -32768)
|
||||
sample = -32768;
|
||||
|
||||
pcmbuf[i] = sample;
|
||||
}
|
||||
|
||||
*p_hist1 = hist1;
|
||||
*p_hist2 = hist2;
|
||||
}
|
||||
|
||||
static void transform(int32_t* invbuf, int32_t* tmpbuf) {
|
||||
int lpc1, lpc2, lpc3, lpc4;
|
||||
int step1, step2, step3;
|
||||
int sc1, sc2;
|
||||
|
||||
/* bits were originally configurable (passed arg), but actually called with const 12,
|
||||
* and removed in later games along with superfluous ifs (coefs > 0, bits >= 3, etc) */
|
||||
//const int frame_bits = 12;
|
||||
|
||||
step1 = 4096; /* 1 << 12 */
|
||||
step2 = step1 >> 1;
|
||||
step3 = step2 >> 1;
|
||||
sc1 = 1;
|
||||
|
||||
/* inverse transform of LPC(?) coefs */
|
||||
for (lpc1 = 0; lpc1 < 12 - 2; lpc1++) {
|
||||
int sub1, sub2;
|
||||
int i1, i2, i3, i4;
|
||||
int64_t cos1, sin1, cos2, sin2; /* needs i64 to force 64b ops (avoid overflows) */
|
||||
|
||||
cos1 = (int64_t)sincos_table[sc1 + 1024];
|
||||
sin1 = (int64_t)sincos_table[sc1 + 0];
|
||||
|
||||
i1 = 0;
|
||||
i2 = step2;
|
||||
i3 = step3;
|
||||
i4 = step2 + step3;
|
||||
|
||||
for (lpc2 = 0; lpc2 < 4096; lpc2 += step1) {
|
||||
sub1 = invbuf[i1 + 0] - invbuf[i2 + 0];
|
||||
sub2 = tmpbuf[i1 + 0] - tmpbuf[i2 + 0];
|
||||
invbuf[i1 + 0] += invbuf[i2 + 0];
|
||||
tmpbuf[i1 + 0] += tmpbuf[i2 + 0];
|
||||
invbuf[i2 + 0] = sub1;
|
||||
tmpbuf[i2 + 0] = sub2;
|
||||
|
||||
sub1 = invbuf[i1 + 1] - invbuf[i2 + 1];
|
||||
sub2 = tmpbuf[i1 + 1] - tmpbuf[i2 + 1];
|
||||
invbuf[i1 + 1] += invbuf[i2 + 1];
|
||||
tmpbuf[i1 + 1] += tmpbuf[i2 + 1];
|
||||
invbuf[i2 + 1] = ((sub1 * cos1) >> 12) + ((sub2 * sin1) >> 12);
|
||||
tmpbuf[i2 + 1] = ((sub2 * cos1) >> 12) - ((sub1 * sin1) >> 12);
|
||||
|
||||
sub1 = invbuf[i3 + 0] - invbuf[i4 + 0];
|
||||
sub2 = tmpbuf[i3 + 0] - tmpbuf[i4 + 0];
|
||||
invbuf[i3 + 0] += invbuf[i4 + 0];
|
||||
tmpbuf[i3 + 0] += tmpbuf[i4 + 0];
|
||||
invbuf[i4 + 0] = sub2;
|
||||
tmpbuf[i4 + 0] = -sub1;
|
||||
|
||||
sub1 = invbuf[i3 + 1] - invbuf[i4 + 1];
|
||||
sub2 = tmpbuf[i3 + 1] - tmpbuf[i4 + 1];
|
||||
invbuf[i3 + 1] += invbuf[i4 + 1];
|
||||
tmpbuf[i3 + 1] += tmpbuf[i4 + 1];
|
||||
invbuf[i4 + 1] = ((sub2 * cos1) >> 12) - ((sub1 * sin1) >> 12);
|
||||
tmpbuf[i4 + 1] = -(((sub1 * cos1) >> 12) + ((sub2 * sin1) >> 12));
|
||||
|
||||
i1 += step1;
|
||||
i2 += step1;
|
||||
i3 += step1;
|
||||
i4 += step1;
|
||||
}
|
||||
|
||||
if (step3 > 2) {
|
||||
sc2 = sc1 * 2;
|
||||
|
||||
for (lpc3 = 2; lpc3 < step3; lpc3++) {
|
||||
cos2 = (int64_t)sincos_table[sc2 + 1024];
|
||||
sin2 = (int64_t)sincos_table[sc2 + 0];
|
||||
sc2 += sc1;
|
||||
|
||||
i1 = 0 + lpc3;
|
||||
i2 = step2 + lpc3;
|
||||
i3 = step3 + lpc3;
|
||||
i4 = step2 + step3 + lpc3;
|
||||
|
||||
for (lpc4 = 0; lpc4 < 4096; lpc4 += step1) {
|
||||
sub1 = invbuf[i1] - invbuf[i2];
|
||||
sub2 = tmpbuf[i1] - tmpbuf[i2];
|
||||
invbuf[i1] += invbuf[i2];
|
||||
tmpbuf[i1] += tmpbuf[i2];
|
||||
invbuf[i2] = ((sub1 * cos2) >> 12) + ((sub2 * sin2) >> 12);
|
||||
tmpbuf[i2] = ((sub2 * cos2) >> 12) - ((sub1 * sin2) >> 12);
|
||||
|
||||
sub1 = invbuf[i3] - invbuf[i4];
|
||||
sub2 = tmpbuf[i3] - tmpbuf[i4];
|
||||
invbuf[i3] += invbuf[i4];
|
||||
tmpbuf[i3] += tmpbuf[i4];
|
||||
invbuf[i4] = ((sub2 * cos2) >> 12) - ((sub1 * sin2) >> 12);
|
||||
tmpbuf[i4] = -(((sub1 * cos2) >> 12) + ((sub2 * sin2) >> 12));
|
||||
|
||||
i1 += step1;
|
||||
i2 += step1;
|
||||
i3 += step1;
|
||||
i4 += step1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
step1 = step2; // step1 >>= 1;
|
||||
step2 = step3; // step2 >>= 1;
|
||||
step3 >>= 1;
|
||||
sc1 *= 2;
|
||||
}
|
||||
|
||||
{
|
||||
int i, j;
|
||||
int sub1, sub2, pow;
|
||||
|
||||
for (i = 0; i < 4096; i += 4) {
|
||||
sub1 = invbuf[i + 0] - invbuf[i + 2];
|
||||
invbuf[i + 0] += invbuf[i + 2];
|
||||
invbuf[i + 2] = sub1;
|
||||
|
||||
sub2 = tmpbuf[i + 0] - tmpbuf[i + 2];
|
||||
tmpbuf[i + 0] += tmpbuf[i + 2];
|
||||
tmpbuf[i + 2] = sub2;
|
||||
|
||||
sub1 = invbuf[i + 3] - invbuf[i + 1];
|
||||
sub2 = tmpbuf[i + 1] - tmpbuf[i + 3];
|
||||
invbuf[i + 1] += invbuf[i + 3];
|
||||
invbuf[i + 3] = sub2;
|
||||
tmpbuf[i + 1] += tmpbuf[i + 3];
|
||||
tmpbuf[i + 3] = sub1;
|
||||
}
|
||||
|
||||
for (i = 0; i < 4096; i += 2) {
|
||||
sub1 = invbuf[i + 0] - invbuf[i + 1];
|
||||
invbuf[i + 0] += invbuf[i + 1];
|
||||
invbuf[i + 1] = sub1;
|
||||
|
||||
sub2 = tmpbuf[i + 0] - tmpbuf[i + 1];
|
||||
tmpbuf[i + 0] += tmpbuf[i + 1];
|
||||
tmpbuf[i + 1] = sub2;
|
||||
}
|
||||
|
||||
for (i = 1, j = 0; i < 4096 - 1; i++) {
|
||||
for (pow = 4096 / 2; pow <= j; pow /= 2) {
|
||||
j -= pow;
|
||||
}
|
||||
j += pow;
|
||||
|
||||
if (i < j) {
|
||||
sub1 = invbuf[j];
|
||||
invbuf[j] = invbuf[i];
|
||||
invbuf[i] = sub1;
|
||||
|
||||
sub2 = tmpbuf[j];
|
||||
tmpbuf[j] = tmpbuf[i];
|
||||
tmpbuf[i] = sub2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void scale(const uint8_t* intbuf, const int* scales, int32_t* invbuf, int32_t* tmpbuf) {
|
||||
int i, j;
|
||||
|
||||
/* reinterleave and scale intbuf into invbuf and tmpbuf */
|
||||
for (i = 0, j = 0; i < 4096 / 2; i++, j += 16) {
|
||||
int scale, qv1, qv2;
|
||||
|
||||
scale = scales[j / 4096];
|
||||
|
||||
qv1 = (intbuf[i*4 + 0] << 0) | (intbuf[i*4 + 1] << 8); /* get_u16le */
|
||||
qv2 = (intbuf[i*4 + 2] << 0) | (intbuf[i*4 + 3] << 8); /* get_u16le */
|
||||
|
||||
/* lowest bit is short of "positive" flag, or rather: even=0..-32767, odd=1..32768
|
||||
* (originally done through a LUT init at runtime with all 65536 indexes) */
|
||||
qv1 = (qv1 & 1) ? (qv1 >> 1) + 1 : -(qv1 >> 1);
|
||||
qv2 = (qv2 & 1) ? (qv2 >> 1) + 1 : -(qv2 >> 1);
|
||||
|
||||
invbuf[i] = scale * qv1;
|
||||
tmpbuf[i] = scale * qv2;
|
||||
}
|
||||
|
||||
/* reset rest of invbuf/tmpbuf */
|
||||
for (i = 4096 / 2; i < 4096; i++) {
|
||||
invbuf[i] = 0;
|
||||
tmpbuf[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void interleave(const uint8_t* decbuf, uint8_t* intbuf) {
|
||||
int i, j;
|
||||
|
||||
/* reorder odd decbuf bytes into intbuf */
|
||||
for (i = 0, j = 1; i < 0x1000; i++, j += 2) {
|
||||
intbuf[j] = decbuf[i];
|
||||
}
|
||||
|
||||
/* reorder even decbuf bytes into intbuf */
|
||||
for (i = 0x1000, j = 0; i < 0x1800; i++, j += 4) {
|
||||
uint8_t lo = decbuf[i + 0x800];
|
||||
uint8_t hi = decbuf[i];
|
||||
|
||||
intbuf[j + 0] = (hi & 0xF0) | (lo >> 4);
|
||||
intbuf[j + 2] = (hi << 4) | (lo & 0x0F);
|
||||
}
|
||||
}
|
||||
|
||||
/* ************************************************************ */
|
||||
/* API */
|
||||
/* ************************************************************ */
|
||||
|
||||
circus_handle_t* circus_init(off_t start, uint8_t codec, uint8_t flags) {
|
||||
circus_handle_t* handle = NULL;
|
||||
int scale_index, err;
|
||||
|
||||
handle = malloc(sizeof(circus_handle_t));
|
||||
if (!handle) goto fail;
|
||||
|
||||
handle->start = start;
|
||||
handle->codec = codec; //(config >> 0) & 0xFF;
|
||||
handle->flags = flags; //(config >> 8) & 0xFF;
|
||||
|
||||
scale_index = (handle->flags & 0xF);
|
||||
if (scale_index > 5) goto fail;
|
||||
handle->scales = scale_table[scale_index];
|
||||
|
||||
if (handle->codec == XPCM_CODEC_VQ_DEFLATE) {
|
||||
memset(&handle->dstrm, 0, sizeof(z_stream));
|
||||
err = inflateInit(&handle->dstrm);
|
||||
if (err < 0) goto fail;
|
||||
}
|
||||
|
||||
circus_reset(handle);
|
||||
|
||||
return handle;
|
||||
fail:
|
||||
circus_free(handle);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void circus_free(circus_handle_t* handle) {
|
||||
if (!handle)
|
||||
return;
|
||||
|
||||
if (handle->codec == XPCM_CODEC_VQ_DEFLATE) {
|
||||
inflateEnd(&handle->dstrm);
|
||||
}
|
||||
|
||||
free(handle);
|
||||
}
|
||||
|
||||
void circus_reset(circus_handle_t* handle) {
|
||||
if (!handle)
|
||||
return;
|
||||
handle->hist1 = 0;
|
||||
handle->hist2 = 0;
|
||||
handle->frame = 0;
|
||||
|
||||
if (handle->codec == XPCM_CODEC_VQ_LZXPCM) {
|
||||
lzxpcm_reset(&handle->lstrm);
|
||||
} else if (handle->codec == XPCM_CODEC_VQ_DEFLATE) {
|
||||
inflateReset(&handle->dstrm);
|
||||
}
|
||||
handle->offset = handle->start;
|
||||
}
|
||||
|
||||
static int decompress_frame_lzxpcm(circus_handle_t* handle, STREAMFILE* sf) {
|
||||
int res;
|
||||
|
||||
handle->lstrm.next_out = handle->decbuf;
|
||||
handle->lstrm.avail_out = sizeof(handle->decbuf);
|
||||
handle->lstrm.total_out = 0;
|
||||
do {
|
||||
if (handle->lstrm.avail_in == 0) {
|
||||
handle->lstrm.next_in = handle->srcbuf;
|
||||
handle->lstrm.avail_in = read_streamfile(handle->srcbuf, handle->offset, sizeof(handle->srcbuf), sf);
|
||||
handle->offset += handle->lstrm.avail_in;
|
||||
|
||||
/* EOF (game reserves some extra buf so memset'ing is probably equivalent) */
|
||||
if (handle->lstrm.avail_in == 0) {
|
||||
memset(handle->decbuf + handle->lstrm.total_out, 0, sizeof(handle->decbuf) - handle->dstrm.total_out);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
res = lzxpcm_decompress(&handle->lstrm);
|
||||
if (res != LZXPCM_OK)
|
||||
goto fail;
|
||||
}
|
||||
while(handle->lstrm.avail_out != 0);
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int decompress_frame_deflate(circus_handle_t* handle, STREAMFILE* sf) {
|
||||
int res;
|
||||
|
||||
handle->dstrm.next_out = handle->decbuf;
|
||||
handle->dstrm.avail_out = sizeof(handle->decbuf);
|
||||
handle->dstrm.total_out = 0;
|
||||
do {
|
||||
if (handle->dstrm.avail_in == 0) {
|
||||
handle->dstrm.next_in = handle->srcbuf;
|
||||
handle->dstrm.avail_in = read_streamfile(handle->srcbuf, handle->offset, sizeof(handle->srcbuf), sf);
|
||||
handle->offset += handle->dstrm.avail_in;
|
||||
|
||||
/* EOF (game reserves some extra buf so memset'ing is probably equivalent) */
|
||||
if (handle->dstrm.avail_in == 0) {
|
||||
memset(handle->decbuf + handle->dstrm.total_out, 0, sizeof(handle->decbuf) - handle->dstrm.total_out);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
res = inflate(&handle->dstrm, Z_NO_FLUSH);
|
||||
if (res != Z_OK && res != Z_STREAM_END)
|
||||
goto fail;
|
||||
}
|
||||
while(handle->dstrm.avail_out != 0);
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef XPCM_ALT
|
||||
/* original code uses zlib 1.2.1 to decompress the full stream into memory */
|
||||
static int deflate_decompress_full(uint8_t* dst, size_t dst_size, const uint8_t* src, size_t src_size) {
|
||||
int err;
|
||||
z_stream strm = {0};
|
||||
strm.next_in = src;
|
||||
strm.avail_in = src_size;
|
||||
strm.next_out = dst;
|
||||
strm.avail_out = dst_size;
|
||||
|
||||
err = inflateInit(&strm);
|
||||
if (err < 0) {
|
||||
//printf("inflateInit error: %i\n", err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = inflate(&strm, Z_FINISH);
|
||||
if (err < 0) {
|
||||
//printf("inflate error: %i\n", err);
|
||||
//return 0;
|
||||
}
|
||||
|
||||
err = inflateEnd(&strm);
|
||||
if (err < 0) {
|
||||
//printf("inflateEnd error: %i\n", err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int circus_decode_frame(circus_handle_t* handle, STREAMFILE* sf, int16_t** p_buf, int* p_buf_samples_all) {
|
||||
int ok;
|
||||
|
||||
if (handle->codec == XPCM_CODEC_VQ_LZXPCM) {
|
||||
ok = decompress_frame_lzxpcm(handle, sf);
|
||||
} else if (handle->codec == XPCM_CODEC_VQ_DEFLATE) {
|
||||
ok = decompress_frame_deflate(handle, sf);
|
||||
} else {
|
||||
ok = 0;
|
||||
}
|
||||
if (!ok)
|
||||
goto fail;
|
||||
|
||||
interleave(handle->decbuf, handle->intbuf);
|
||||
scale(handle->intbuf, handle->scales, handle->invbuf, handle->tmpbuf);
|
||||
transform(handle->invbuf, handle->tmpbuf);
|
||||
convert(handle->flags, handle->invbuf, handle->pcmbuf, &handle->hist1, &handle->hist2, handle->frame);
|
||||
handle->frame++;
|
||||
|
||||
*p_buf = handle->pcmbuf;
|
||||
*p_buf_samples_all = XPCM_FRAME_SAMPLES_ALL;
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
#ifndef _CIRCUS_DECODER_LIB_H_
|
||||
#define _CIRCUS_DECODER_LIB_H_
|
||||
|
||||
#include "../streamfile.h"
|
||||
|
||||
typedef struct circus_handle_t circus_handle_t;
|
||||
|
||||
circus_handle_t* circus_init(off_t start, uint8_t codec, uint8_t flags);
|
||||
|
||||
void circus_free(circus_handle_t* handle);
|
||||
|
||||
void circus_reset(circus_handle_t* handle);
|
||||
|
||||
int circus_decode_frame(circus_handle_t* handle, STREAMFILE* sf, int16_t** p_buf, int* p_buf_samples_all);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,347 @@
|
|||
#ifndef _CIRCUS_DECODER_LIB_DATA_H_
|
||||
#define _CIRCUS_DECODER_LIB_DATA_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
/* sin-cosine LUT, seems generated with: */
|
||||
/* table[i] = round( sin(2.0 * PI * i / 1024.0) * 1000.0 ) */
|
||||
static const int sincos_table[1024*5 + 1] = {
|
||||
0, 6, 12, 18, 25, 31, 37, 43, 50, 56, 62, 69, 75, 81, 87, 94,
|
||||
100, 106, 113, 119, 125, 131, 138, 144, 150, 157, 163, 169, 175, 182, 188, 194,
|
||||
200, 207, 213, 219, 226, 232, 238, 244, 251, 257, 263, 269, 276, 282, 288, 295,
|
||||
301, 307, 313, 320, 326, 332, 338, 345, 351, 357, 363, 370, 376, 382, 388, 395,
|
||||
401, 407, 413, 420, 426, 432, 438, 445, 451, 457, 463, 470, 476, 482, 488, 495,
|
||||
501, 507, 513, 520, 526, 532, 538, 545, 551, 557, 563, 569, 576, 582, 588, 594,
|
||||
601, 607, 613, 619, 625, 632, 638, 644, 650, 656, 663, 669, 675, 681, 687, 694,
|
||||
700, 706, 712, 718, 725, 731, 737, 743, 749, 755, 762, 768, 774, 780, 786, 792,
|
||||
799, 805, 811, 817, 823, 829, 836, 842, 848, 854, 860, 866, 872, 879, 885, 891,
|
||||
897, 903, 909, 915, 921, 928, 934, 940, 946, 952, 958, 964, 970, 976, 983, 989,
|
||||
995, 1001, 1007, 1013, 1019, 1025, 1031, 1037, 1043, 1050, 1056, 1062, 1068, 1074, 1080, 1086,
|
||||
1092, 1098, 1104, 1110, 1116, 1122, 1128, 1134, 1140, 1146, 1152, 1158, 1164, 1170, 1176, 1182,
|
||||
1189, 1195, 1201, 1207, 1213, 1219, 1225, 1231, 1237, 1243, 1248, 1254, 1260, 1266, 1272, 1278,
|
||||
1284, 1290, 1296, 1302, 1308, 1314, 1320, 1326, 1332, 1338, 1344, 1350, 1356, 1362, 1368, 1373,
|
||||
1379, 1385, 1391, 1397, 1403, 1409, 1415, 1421, 1427, 1433, 1438, 1444, 1450, 1456, 1462, 1468,
|
||||
1474, 1479, 1485, 1491, 1497, 1503, 1509, 1515, 1520, 1526, 1532, 1538, 1544, 1550, 1555, 1561,
|
||||
1567, 1573, 1579, 1584, 1590, 1596, 1602, 1608, 1613, 1619, 1625, 1631, 1636, 1642, 1648, 1654,
|
||||
1659, 1665, 1671, 1677, 1682, 1688, 1694, 1699, 1705, 1711, 1717, 1722, 1728, 1734, 1739, 1745,
|
||||
1751, 1756, 1762, 1768, 1773, 1779, 1785, 1790, 1796, 1802, 1807, 1813, 1819, 1824, 1830, 1835,
|
||||
1841, 1847, 1852, 1858, 1864, 1869, 1875, 1880, 1886, 1891, 1897, 1903, 1908, 1914, 1919, 1925,
|
||||
1930, 1936, 1941, 1947, 1952, 1958, 1964, 1969, 1975, 1980, 1986, 1991, 1997, 2002, 2007, 2013,
|
||||
2018, 2024, 2029, 2035, 2040, 2046, 2051, 2057, 2062, 2067, 2073, 2078, 2084, 2089, 2094, 2100,
|
||||
2105, 2111, 2116, 2121, 2127, 2132, 2138, 2143, 2148, 2154, 2159, 2164, 2170, 2175, 2180, 2186,
|
||||
2191, 2196, 2201, 2207, 2212, 2217, 2223, 2228, 2233, 2238, 2244, 2249, 2254, 2259, 2265, 2270,
|
||||
2275, 2280, 2286, 2291, 2296, 2301, 2306, 2312, 2317, 2322, 2327, 2332, 2337, 2343, 2348, 2353,
|
||||
2358, 2363, 2368, 2373, 2379, 2384, 2389, 2394, 2399, 2404, 2409, 2414, 2419, 2424, 2429, 2434,
|
||||
2439, 2445, 2450, 2455, 2460, 2465, 2470, 2475, 2480, 2485, 2490, 2495, 2500, 2505, 2510, 2515,
|
||||
2519, 2524, 2529, 2534, 2539, 2544, 2549, 2554, 2559, 2564, 2569, 2574, 2578, 2583, 2588, 2593,
|
||||
2598, 2603, 2608, 2613, 2617, 2622, 2627, 2632, 2637, 2641, 2646, 2651, 2656, 2661, 2665, 2670,
|
||||
2675, 2680, 2684, 2689, 2694, 2699, 2703, 2708, 2713, 2717, 2722, 2727, 2732, 2736, 2741, 2746,
|
||||
2750, 2755, 2760, 2764, 2769, 2773, 2778, 2783, 2787, 2792, 2796, 2801, 2806, 2810, 2815, 2819,
|
||||
2824, 2828, 2833, 2837, 2842, 2847, 2851, 2856, 2860, 2865, 2869, 2874, 2878, 2882, 2887, 2891,
|
||||
2896, 2900, 2905, 2909, 2914, 2918, 2922, 2927, 2931, 2936, 2940, 2944, 2949, 2953, 2957, 2962,
|
||||
2966, 2970, 2975, 2979, 2983, 2988, 2992, 2996, 3000, 3005, 3009, 3013, 3018, 3022, 3026, 3030,
|
||||
3034, 3039, 3043, 3047, 3051, 3055, 3060, 3064, 3068, 3072, 3076, 3080, 3085, 3089, 3093, 3097,
|
||||
3101, 3105, 3109, 3113, 3117, 3121, 3126, 3130, 3134, 3138, 3142, 3146, 3150, 3154, 3158, 3162,
|
||||
3166, 3170, 3174, 3178, 3182, 3186, 3190, 3193, 3197, 3201, 3205, 3209, 3213, 3217, 3221, 3225,
|
||||
3229, 3232, 3236, 3240, 3244, 3248, 3252, 3255, 3259, 3263, 3267, 3271, 3274, 3278, 3282, 3286,
|
||||
3289, 3293, 3297, 3301, 3304, 3308, 3312, 3315, 3319, 3323, 3326, 3330, 3334, 3337, 3341, 3345,
|
||||
3348, 3352, 3356, 3359, 3363, 3366, 3370, 3373, 3377, 3381, 3384, 3388, 3391, 3395, 3398, 3402,
|
||||
3405, 3409, 3412, 3416, 3419, 3423, 3426, 3429, 3433, 3436, 3440, 3443, 3447, 3450, 3453, 3457,
|
||||
3460, 3463, 3467, 3470, 3473, 3477, 3480, 3483, 3487, 3490, 3493, 3497, 3500, 3503, 3506, 3510,
|
||||
3513, 3516, 3519, 3522, 3526, 3529, 3532, 3535, 3538, 3541, 3545, 3548, 3551, 3554, 3557, 3560,
|
||||
3563, 3566, 3570, 3573, 3576, 3579, 3582, 3585, 3588, 3591, 3594, 3597, 3600, 3603, 3606, 3609,
|
||||
3612, 3615, 3618, 3621, 3624, 3627, 3629, 3632, 3635, 3638, 3641, 3644, 3647, 3650, 3652, 3655,
|
||||
3658, 3661, 3664, 3667, 3669, 3672, 3675, 3678, 3680, 3683, 3686, 3689, 3691, 3694, 3697, 3700,
|
||||
3702, 3705, 3708, 3710, 3713, 3716, 3718, 3721, 3723, 3726, 3729, 3731, 3734, 3736, 3739, 3742,
|
||||
3744, 3747, 3749, 3752, 3754, 3757, 3759, 3762, 3764, 3767, 3769, 3772, 3774, 3776, 3779, 3781,
|
||||
3784, 3786, 3789, 3791, 3793, 3796, 3798, 3800, 3803, 3805, 3807, 3810, 3812, 3814, 3816, 3819,
|
||||
3821, 3823, 3826, 3828, 3830, 3832, 3834, 3837, 3839, 3841, 3843, 3845, 3848, 3850, 3852, 3854,
|
||||
3856, 3858, 3860, 3862, 3864, 3867, 3869, 3871, 3873, 3875, 3877, 3879, 3881, 3883, 3885, 3887,
|
||||
3889, 3891, 3893, 3895, 3897, 3899, 3900, 3902, 3904, 3906, 3908, 3910, 3912, 3914, 3915, 3917,
|
||||
3919, 3921, 3923, 3925, 3926, 3928, 3930, 3932, 3933, 3935, 3937, 3939, 3940, 3942, 3944, 3945,
|
||||
3947, 3949, 3950, 3952, 3954, 3955, 3957, 3959, 3960, 3962, 3963, 3965, 3967, 3968, 3970, 3971,
|
||||
3973, 3974, 3976, 3977, 3979, 3980, 3982, 3983, 3985, 3986, 3988, 3989, 3990, 3992, 3993, 3995,
|
||||
3996, 3997, 3999, 4000, 4001, 4003, 4004, 4005, 4007, 4008, 4009, 4011, 4012, 4013, 4014, 4016,
|
||||
4017, 4018, 4019, 4020, 4022, 4023, 4024, 4025, 4026, 4027, 4029, 4030, 4031, 4032, 4033, 4034,
|
||||
4035, 4036, 4037, 4038, 4039, 4040, 4041, 4042, 4043, 4044, 4045, 4046, 4047, 4048, 4049, 4050,
|
||||
4051, 4052, 4053, 4054, 4055, 4056, 4057, 4057, 4058, 4059, 4060, 4061, 4062, 4062, 4063, 4064,
|
||||
4065, 4065, 4066, 4067, 4068, 4068, 4069, 4070, 4071, 4071, 4072, 4073, 4073, 4074, 4075, 4075,
|
||||
4076, 4076, 4077, 4078, 4078, 4079, 4079, 4080, 4080, 4081, 4081, 4082, 4082, 4083, 4083, 4084,
|
||||
4084, 4085, 4085, 4086, 4086, 4087, 4087, 4087, 4088, 4088, 4089, 4089, 4089, 4090, 4090, 4090,
|
||||
4091, 4091, 4091, 4091, 4092, 4092, 4092, 4092, 4093, 4093, 4093, 4093, 4094, 4094, 4094, 4094,
|
||||
4094, 4094, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095,
|
||||
|
||||
4096, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4094,
|
||||
4094, 4094, 4094, 4094, 4094, 4093, 4093, 4093, 4093, 4092, 4092, 4092, 4092, 4091, 4091, 4091,
|
||||
4091, 4090, 4090, 4090, 4089, 4089, 4089, 4088, 4088, 4087, 4087, 4087, 4086, 4086, 4085, 4085,
|
||||
4084, 4084, 4083, 4083, 4082, 4082, 4081, 4081, 4080, 4080, 4079, 4079, 4078, 4078, 4077, 4076,
|
||||
4076, 4075, 4075, 4074, 4073, 4073, 4072, 4071, 4071, 4070, 4069, 4068, 4068, 4067, 4066, 4065,
|
||||
4065, 4064, 4063, 4062, 4062, 4061, 4060, 4059, 4058, 4057, 4057, 4056, 4055, 4054, 4053, 4052,
|
||||
4051, 4050, 4049, 4048, 4047, 4046, 4045, 4044, 4043, 4042, 4041, 4040, 4039, 4038, 4037, 4036,
|
||||
4035, 4034, 4033, 4032, 4031, 4030, 4029, 4027, 4026, 4025, 4024, 4023, 4022, 4020, 4019, 4018,
|
||||
4017, 4016, 4014, 4013, 4012, 4011, 4009, 4008, 4007, 4005, 4004, 4003, 4001, 4000, 3999, 3997,
|
||||
3996, 3995, 3993, 3992, 3990, 3989, 3988, 3986, 3985, 3983, 3982, 3980, 3979, 3977, 3976, 3974,
|
||||
3973, 3971, 3970, 3968, 3967, 3965, 3963, 3962, 3960, 3959, 3957, 3955, 3954, 3952, 3950, 3949,
|
||||
3947, 3945, 3944, 3942, 3940, 3939, 3937, 3935, 3933, 3932, 3930, 3928, 3926, 3925, 3923, 3921,
|
||||
3919, 3917, 3915, 3914, 3912, 3910, 3908, 3906, 3904, 3902, 3900, 3899, 3897, 3895, 3893, 3891,
|
||||
3889, 3887, 3885, 3883, 3881, 3879, 3877, 3875, 3873, 3871, 3869, 3867, 3864, 3862, 3860, 3858,
|
||||
3856, 3854, 3852, 3850, 3848, 3845, 3843, 3841, 3839, 3837, 3834, 3832, 3830, 3828, 3826, 3823,
|
||||
3821, 3819, 3816, 3814, 3812, 3810, 3807, 3805, 3803, 3800, 3798, 3796, 3793, 3791, 3789, 3786,
|
||||
3784, 3781, 3779, 3776, 3774, 3772, 3769, 3767, 3764, 3762, 3759, 3757, 3754, 3752, 3749, 3747,
|
||||
3744, 3742, 3739, 3736, 3734, 3731, 3729, 3726, 3723, 3721, 3718, 3716, 3713, 3710, 3708, 3705,
|
||||
3702, 3700, 3697, 3694, 3691, 3689, 3686, 3683, 3680, 3678, 3675, 3672, 3669, 3667, 3664, 3661,
|
||||
3658, 3655, 3652, 3650, 3647, 3644, 3641, 3638, 3635, 3632, 3629, 3627, 3624, 3621, 3618, 3615,
|
||||
3612, 3609, 3606, 3603, 3600, 3597, 3594, 3591, 3588, 3585, 3582, 3579, 3576, 3573, 3570, 3566,
|
||||
3563, 3560, 3557, 3554, 3551, 3548, 3545, 3541, 3538, 3535, 3532, 3529, 3526, 3522, 3519, 3516,
|
||||
3513, 3510, 3506, 3503, 3500, 3497, 3493, 3490, 3487, 3483, 3480, 3477, 3473, 3470, 3467, 3463,
|
||||
3460, 3457, 3453, 3450, 3447, 3443, 3440, 3436, 3433, 3429, 3426, 3423, 3419, 3416, 3412, 3409,
|
||||
3405, 3402, 3398, 3395, 3391, 3388, 3384, 3381, 3377, 3373, 3370, 3366, 3363, 3359, 3356, 3352,
|
||||
3348, 3345, 3341, 3337, 3334, 3330, 3326, 3323, 3319, 3315, 3312, 3308, 3304, 3301, 3297, 3293,
|
||||
3289, 3286, 3282, 3278, 3274, 3271, 3267, 3263, 3259, 3255, 3252, 3248, 3244, 3240, 3236, 3232,
|
||||
3229, 3225, 3221, 3217, 3213, 3209, 3205, 3201, 3197, 3193, 3190, 3186, 3182, 3178, 3174, 3170,
|
||||
3166, 3162, 3158, 3154, 3150, 3146, 3142, 3138, 3134, 3130, 3126, 3121, 3117, 3113, 3109, 3105,
|
||||
3101, 3097, 3093, 3089, 3085, 3080, 3076, 3072, 3068, 3064, 3060, 3055, 3051, 3047, 3043, 3039,
|
||||
3034, 3030, 3026, 3022, 3018, 3013, 3009, 3005, 3000, 2996, 2992, 2988, 2983, 2979, 2975, 2970,
|
||||
2966, 2962, 2957, 2953, 2949, 2944, 2940, 2936, 2931, 2927, 2922, 2918, 2914, 2909, 2905, 2900,
|
||||
2896, 2891, 2887, 2882, 2878, 2874, 2869, 2865, 2860, 2856, 2851, 2847, 2842, 2837, 2833, 2828,
|
||||
2824, 2819, 2815, 2810, 2806, 2801, 2796, 2792, 2787, 2783, 2778, 2773, 2769, 2764, 2760, 2755,
|
||||
2750, 2746, 2741, 2736, 2732, 2727, 2722, 2717, 2713, 2708, 2703, 2699, 2694, 2689, 2684, 2680,
|
||||
2675, 2670, 2665, 2661, 2656, 2651, 2646, 2641, 2637, 2632, 2627, 2622, 2617, 2613, 2608, 2603,
|
||||
2598, 2593, 2588, 2583, 2578, 2574, 2569, 2564, 2559, 2554, 2549, 2544, 2539, 2534, 2529, 2524,
|
||||
2519, 2515, 2510, 2505, 2500, 2495, 2490, 2485, 2480, 2475, 2470, 2465, 2460, 2455, 2450, 2445,
|
||||
2439, 2434, 2429, 2424, 2419, 2414, 2409, 2404, 2399, 2394, 2389, 2384, 2379, 2373, 2368, 2363,
|
||||
2358, 2353, 2348, 2343, 2337, 2332, 2327, 2322, 2317, 2312, 2306, 2301, 2296, 2291, 2286, 2280,
|
||||
2275, 2270, 2265, 2259, 2254, 2249, 2244, 2238, 2233, 2228, 2223, 2217, 2212, 2207, 2201, 2196,
|
||||
2191, 2186, 2180, 2175, 2170, 2164, 2159, 2154, 2148, 2143, 2138, 2132, 2127, 2121, 2116, 2111,
|
||||
2105, 2100, 2094, 2089, 2084, 2078, 2073, 2067, 2062, 2057, 2051, 2046, 2040, 2035, 2029, 2024,
|
||||
2018, 2013, 2007, 2002, 1997, 1991, 1986, 1980, 1975, 1969, 1964, 1958, 1952, 1947, 1941, 1936,
|
||||
1930, 1925, 1919, 1914, 1908, 1903, 1897, 1891, 1886, 1880, 1875, 1869, 1864, 1858, 1852, 1847,
|
||||
1841, 1835, 1830, 1824, 1819, 1813, 1807, 1802, 1796, 1790, 1785, 1779, 1773, 1768, 1762, 1756,
|
||||
1751, 1745, 1739, 1734, 1728, 1722, 1717, 1711, 1705, 1699, 1694, 1688, 1682, 1677, 1671, 1665,
|
||||
1659, 1654, 1648, 1642, 1636, 1631, 1625, 1619, 1613, 1608, 1602, 1596, 1590, 1584, 1579, 1573,
|
||||
1567, 1561, 1555, 1550, 1544, 1538, 1532, 1526, 1520, 1515, 1509, 1503, 1497, 1491, 1485, 1479,
|
||||
1474, 1468, 1462, 1456, 1450, 1444, 1438, 1433, 1427, 1421, 1415, 1409, 1403, 1397, 1391, 1385,
|
||||
1379, 1373, 1368, 1362, 1356, 1350, 1344, 1338, 1332, 1326, 1320, 1314, 1308, 1302, 1296, 1290,
|
||||
1284, 1278, 1272, 1266, 1260, 1254, 1248, 1243, 1237, 1231, 1225, 1219, 1213, 1207, 1201, 1195,
|
||||
1189, 1182, 1176, 1170, 1164, 1158, 1152, 1146, 1140, 1134, 1128, 1122, 1116, 1110, 1104, 1098,
|
||||
1092, 1086, 1080, 1074, 1068, 1062, 1056, 1050, 1043, 1037, 1031, 1025, 1019, 1013, 1007, 1001,
|
||||
995, 989, 983, 976, 970, 964, 958, 952, 946, 940, 934, 928, 921, 915, 909, 903,
|
||||
897, 891, 885, 879, 872, 866, 860, 854, 848, 842, 836, 829, 823, 817, 811, 805,
|
||||
799, 792, 786, 780, 774, 768, 762, 755, 749, 743, 737, 731, 725, 718, 712, 706,
|
||||
700, 694, 687, 681, 675, 669, 663, 656, 650, 644, 638, 632, 625, 619, 613, 607,
|
||||
601, 594, 588, 582, 576, 569, 563, 557, 551, 545, 538, 532, 526, 520, 513, 507,
|
||||
501, 495, 488, 482, 476, 470, 463, 457, 451, 445, 438, 432, 426, 420, 413, 407,
|
||||
401, 395, 388, 382, 376, 370, 363, 357, 351, 345, 338, 332, 326, 320, 313, 307,
|
||||
301, 295, 288, 282, 276, 269, 263, 257, 251, 244, 238, 232, 226, 219, 213, 207,
|
||||
200, 194, 188, 182, 175, 169, 163, 157, 150, 144, 138, 131, 125, 119, 113, 106,
|
||||
100, 94, 87, 81, 75, 69, 62, 56, 50, 43, 37, 31, 25, 18, 12, 6,
|
||||
|
||||
0, -6, -12, -18, -25, -31, -37, -43, -50, -56, -62, -69, -75, -81, -87, -94,
|
||||
-100, -106, -113, -119, -125, -131, -138, -144, -150, -157, -163, -169, -175, -182, -188, -194,
|
||||
-200, -207, -213, -219, -226, -232, -238, -244, -251, -257, -263, -269, -276, -282, -288, -295,
|
||||
-301, -307, -313, -320, -326, -332, -338, -345, -351, -357, -363, -370, -376, -382, -388, -395,
|
||||
-401, -407, -413, -420, -426, -432, -438, -445, -451, -457, -463, -470, -476, -482, -488, -495,
|
||||
-501, -507, -513, -520, -526, -532, -538, -545, -551, -557, -563, -569, -576, -582, -588, -594,
|
||||
-601, -607, -613, -619, -625, -632, -638, -644, -650, -656, -663, -669, -675, -681, -687, -694,
|
||||
-700, -706, -712, -718, -725, -731, -737, -743, -749, -755, -762, -768, -774, -780, -786, -792,
|
||||
-799, -805, -811, -817, -823, -829, -836, -842, -848, -854, -860, -866, -872, -879, -885, -891,
|
||||
-897, -903, -909, -915, -921, -928, -934, -940, -946, -952, -958, -964, -970, -976, -983, -989,
|
||||
-995,-1001,-1007,-1013,-1019,-1025,-1031,-1037,-1043,-1050,-1056,-1062,-1068,-1074,-1080,-1086,
|
||||
-1092,-1098,-1104,-1110,-1116,-1122,-1128,-1134,-1140,-1146,-1152,-1158,-1164,-1170,-1176,-1182,
|
||||
-1189,-1195,-1201,-1207,-1213,-1219,-1225,-1231,-1237,-1243,-1248,-1254,-1260,-1266,-1272,-1278,
|
||||
-1284,-1290,-1296,-1302,-1308,-1314,-1320,-1326,-1332,-1338,-1344,-1350,-1356,-1362,-1368,-1373,
|
||||
-1379,-1385,-1391,-1397,-1403,-1409,-1415,-1421,-1427,-1433,-1438,-1444,-1450,-1456,-1462,-1468,
|
||||
-1474,-1479,-1485,-1491,-1497,-1503,-1509,-1515,-1520,-1526,-1532,-1538,-1544,-1550,-1555,-1561,
|
||||
-1567,-1573,-1579,-1584,-1590,-1596,-1602,-1608,-1613,-1619,-1625,-1631,-1636,-1642,-1648,-1654,
|
||||
-1659,-1665,-1671,-1677,-1682,-1688,-1694,-1699,-1705,-1711,-1717,-1722,-1728,-1734,-1739,-1745,
|
||||
-1751,-1756,-1762,-1768,-1773,-1779,-1785,-1790,-1796,-1802,-1807,-1813,-1819,-1824,-1830,-1835,
|
||||
-1841,-1847,-1852,-1858,-1864,-1869,-1875,-1880,-1886,-1891,-1897,-1903,-1908,-1914,-1919,-1925,
|
||||
-1930,-1936,-1941,-1947,-1952,-1958,-1964,-1969,-1975,-1980,-1986,-1991,-1997,-2002,-2007,-2013,
|
||||
-2018,-2024,-2029,-2035,-2040,-2046,-2051,-2057,-2062,-2067,-2073,-2078,-2084,-2089,-2094,-2100,
|
||||
-2105,-2111,-2116,-2121,-2127,-2132,-2138,-2143,-2148,-2154,-2159,-2164,-2170,-2175,-2180,-2186,
|
||||
-2191,-2196,-2201,-2207,-2212,-2217,-2223,-2228,-2233,-2238,-2244,-2249,-2254,-2259,-2265,-2270,
|
||||
-2275,-2280,-2286,-2291,-2296,-2301,-2306,-2312,-2317,-2322,-2327,-2332,-2337,-2343,-2348,-2353,
|
||||
-2358,-2363,-2368,-2373,-2379,-2384,-2389,-2394,-2399,-2404,-2409,-2414,-2419,-2424,-2429,-2434,
|
||||
-2439,-2445,-2450,-2455,-2460,-2465,-2470,-2475,-2480,-2485,-2490,-2495,-2500,-2505,-2510,-2515,
|
||||
-2519,-2524,-2529,-2534,-2539,-2544,-2549,-2554,-2559,-2564,-2569,-2574,-2578,-2583,-2588,-2593,
|
||||
-2598,-2603,-2608,-2613,-2617,-2622,-2627,-2632,-2637,-2641,-2646,-2651,-2656,-2661,-2665,-2670,
|
||||
-2675,-2680,-2684,-2689,-2694,-2699,-2703,-2708,-2713,-2717,-2722,-2727,-2732,-2736,-2741,-2746,
|
||||
-2750,-2755,-2760,-2764,-2769,-2773,-2778,-2783,-2787,-2792,-2796,-2801,-2806,-2810,-2815,-2819,
|
||||
-2824,-2828,-2833,-2837,-2842,-2847,-2851,-2856,-2860,-2865,-2869,-2874,-2878,-2882,-2887,-2891,
|
||||
-2896,-2900,-2905,-2909,-2914,-2918,-2922,-2927,-2931,-2936,-2940,-2944,-2949,-2953,-2957,-2962,
|
||||
-2966,-2970,-2975,-2979,-2983,-2988,-2992,-2996,-3000,-3005,-3009,-3013,-3018,-3022,-3026,-3030,
|
||||
-3034,-3039,-3043,-3047,-3051,-3055,-3060,-3064,-3068,-3072,-3076,-3080,-3085,-3089,-3093,-3097,
|
||||
-3101,-3105,-3109,-3113,-3117,-3121,-3126,-3130,-3134,-3138,-3142,-3146,-3150,-3154,-3158,-3162,
|
||||
-3166,-3170,-3174,-3178,-3182,-3186,-3190,-3193,-3197,-3201,-3205,-3209,-3213,-3217,-3221,-3225,
|
||||
-3229,-3232,-3236,-3240,-3244,-3248,-3252,-3255,-3259,-3263,-3267,-3271,-3274,-3278,-3282,-3286,
|
||||
-3289,-3293,-3297,-3301,-3304,-3308,-3312,-3315,-3319,-3323,-3326,-3330,-3334,-3337,-3341,-3345,
|
||||
-3348,-3352,-3356,-3359,-3363,-3366,-3370,-3373,-3377,-3381,-3384,-3388,-3391,-3395,-3398,-3402,
|
||||
-3405,-3409,-3412,-3416,-3419,-3423,-3426,-3429,-3433,-3436,-3440,-3443,-3447,-3450,-3453,-3457,
|
||||
-3460,-3463,-3467,-3470,-3473,-3477,-3480,-3483,-3487,-3490,-3493,-3497,-3500,-3503,-3506,-3510,
|
||||
-3513,-3516,-3519,-3522,-3526,-3529,-3532,-3535,-3538,-3541,-3545,-3548,-3551,-3554,-3557,-3560,
|
||||
-3563,-3566,-3570,-3573,-3576,-3579,-3582,-3585,-3588,-3591,-3594,-3597,-3600,-3603,-3606,-3609,
|
||||
-3612,-3615,-3618,-3621,-3624,-3627,-3629,-3632,-3635,-3638,-3641,-3644,-3647,-3650,-3652,-3655,
|
||||
-3658,-3661,-3664,-3667,-3669,-3672,-3675,-3678,-3680,-3683,-3686,-3689,-3691,-3694,-3697,-3700,
|
||||
-3702,-3705,-3708,-3710,-3713,-3716,-3718,-3721,-3723,-3726,-3729,-3731,-3734,-3736,-3739,-3742,
|
||||
-3744,-3747,-3749,-3752,-3754,-3757,-3759,-3762,-3764,-3767,-3769,-3772,-3774,-3776,-3779,-3781,
|
||||
-3784,-3786,-3789,-3791,-3793,-3796,-3798,-3800,-3803,-3805,-3807,-3810,-3812,-3814,-3816,-3819,
|
||||
-3821,-3823,-3826,-3828,-3830,-3832,-3834,-3837,-3839,-3841,-3843,-3845,-3848,-3850,-3852,-3854,
|
||||
-3856,-3858,-3860,-3862,-3864,-3867,-3869,-3871,-3873,-3875,-3877,-3879,-3881,-3883,-3885,-3887,
|
||||
-3889,-3891,-3893,-3895,-3897,-3899,-3900,-3902,-3904,-3906,-3908,-3910,-3912,-3914,-3915,-3917,
|
||||
-3919,-3921,-3923,-3925,-3926,-3928,-3930,-3932,-3933,-3935,-3937,-3939,-3940,-3942,-3944,-3945,
|
||||
-3947,-3949,-3950,-3952,-3954,-3955,-3957,-3959,-3960,-3962,-3963,-3965,-3967,-3968,-3970,-3971,
|
||||
-3973,-3974,-3976,-3977,-3979,-3980,-3982,-3983,-3985,-3986,-3988,-3989,-3990,-3992,-3993,-3995,
|
||||
-3996,-3997,-3999,-4000,-4001,-4003,-4004,-4005,-4007,-4008,-4009,-4011,-4012,-4013,-4014,-4016,
|
||||
-4017,-4018,-4019,-4020,-4022,-4023,-4024,-4025,-4026,-4027,-4029,-4030,-4031,-4032,-4033,-4034,
|
||||
-4035,-4036,-4037,-4038,-4039,-4040,-4041,-4042,-4043,-4044,-4045,-4046,-4047,-4048,-4049,-4050,
|
||||
-4051,-4052,-4053,-4054,-4055,-4056,-4057,-4057,-4058,-4059,-4060,-4061,-4062,-4062,-4063,-4064,
|
||||
-4065,-4065,-4066,-4067,-4068,-4068,-4069,-4070,-4071,-4071,-4072,-4073,-4073,-4074,-4075,-4075,
|
||||
-4076,-4076,-4077,-4078,-4078,-4079,-4079,-4080,-4080,-4081,-4081,-4082,-4082,-4083,-4083,-4084,
|
||||
-4084,-4085,-4085,-4086,-4086,-4087,-4087,-4087,-4088,-4088,-4089,-4089,-4089,-4090,-4090,-4090,
|
||||
-4091,-4091,-4091,-4091,-4092,-4092,-4092,-4092,-4093,-4093,-4093,-4093,-4094,-4094,-4094,-4094,
|
||||
-4094,-4094,-4095,-4095,-4095,-4095,-4095,-4095,-4095,-4095,-4095,-4095,-4095,-4095,-4095,-4095,
|
||||
|
||||
-4096,-4095,-4095,-4095,-4095,-4095,-4095,-4095,-4095,-4095,-4095,-4095,-4095,-4095,-4095,-4094,
|
||||
-4094,-4094,-4094,-4094,-4094,-4093,-4093,-4093,-4093,-4092,-4092,-4092,-4092,-4091,-4091,-4091,
|
||||
-4091,-4090,-4090,-4090,-4089,-4089,-4089,-4088,-4088,-4087,-4087,-4087,-4086,-4086,-4085,-4085,
|
||||
-4084,-4084,-4083,-4083,-4082,-4082,-4081,-4081,-4080,-4080,-4079,-4079,-4078,-4078,-4077,-4076,
|
||||
-4076,-4075,-4075,-4074,-4073,-4073,-4072,-4071,-4071,-4070,-4069,-4068,-4068,-4067,-4066,-4065,
|
||||
-4065,-4064,-4063,-4062,-4062,-4061,-4060,-4059,-4058,-4057,-4057,-4056,-4055,-4054,-4053,-4052,
|
||||
-4051,-4050,-4049,-4048,-4047,-4046,-4045,-4044,-4043,-4042,-4041,-4040,-4039,-4038,-4037,-4036,
|
||||
-4035,-4034,-4033,-4032,-4031,-4030,-4029,-4027,-4026,-4025,-4024,-4023,-4022,-4020,-4019,-4018,
|
||||
-4017,-4016,-4014,-4013,-4012,-4011,-4009,-4008,-4007,-4005,-4004,-4003,-4001,-4000,-3999,-3997,
|
||||
-3996,-3995,-3993,-3992,-3990,-3989,-3988,-3986,-3985,-3983,-3982,-3980,-3979,-3977,-3976,-3974,
|
||||
-3973,-3971,-3970,-3968,-3967,-3965,-3963,-3962,-3960,-3959,-3957,-3955,-3954,-3952,-3950,-3949,
|
||||
-3947,-3945,-3944,-3942,-3940,-3939,-3937,-3935,-3933,-3932,-3930,-3928,-3926,-3925,-3923,-3921,
|
||||
-3919,-3917,-3915,-3914,-3912,-3910,-3908,-3906,-3904,-3902,-3900,-3899,-3897,-3895,-3893,-3891,
|
||||
-3889,-3887,-3885,-3883,-3881,-3879,-3877,-3875,-3873,-3871,-3869,-3867,-3864,-3862,-3860,-3858,
|
||||
-3856,-3854,-3852,-3850,-3848,-3845,-3843,-3841,-3839,-3837,-3834,-3832,-3830,-3828,-3826,-3823,
|
||||
-3821,-3819,-3816,-3814,-3812,-3810,-3807,-3805,-3803,-3800,-3798,-3796,-3793,-3791,-3789,-3786,
|
||||
-3784,-3781,-3779,-3776,-3774,-3772,-3769,-3767,-3764,-3762,-3759,-3757,-3754,-3752,-3749,-3747,
|
||||
-3744,-3742,-3739,-3736,-3734,-3731,-3729,-3726,-3723,-3721,-3718,-3716,-3713,-3710,-3708,-3705,
|
||||
-3702,-3700,-3697,-3694,-3691,-3689,-3686,-3683,-3680,-3678,-3675,-3672,-3669,-3667,-3664,-3661,
|
||||
-3658,-3655,-3652,-3650,-3647,-3644,-3641,-3638,-3635,-3632,-3629,-3627,-3624,-3621,-3618,-3615,
|
||||
-3612,-3609,-3606,-3603,-3600,-3597,-3594,-3591,-3588,-3585,-3582,-3579,-3576,-3573,-3570,-3566,
|
||||
-3563,-3560,-3557,-3554,-3551,-3548,-3545,-3541,-3538,-3535,-3532,-3529,-3526,-3522,-3519,-3516,
|
||||
-3513,-3510,-3506,-3503,-3500,-3497,-3493,-3490,-3487,-3483,-3480,-3477,-3473,-3470,-3467,-3463,
|
||||
-3460,-3457,-3453,-3450,-3447,-3443,-3440,-3436,-3433,-3429,-3426,-3423,-3419,-3416,-3412,-3409,
|
||||
-3405,-3402,-3398,-3395,-3391,-3388,-3384,-3381,-3377,-3373,-3370,-3366,-3363,-3359,-3356,-3352,
|
||||
-3348,-3345,-3341,-3337,-3334,-3330,-3326,-3323,-3319,-3315,-3312,-3308,-3304,-3301,-3297,-3293,
|
||||
-3289,-3286,-3282,-3278,-3274,-3271,-3267,-3263,-3259,-3255,-3252,-3248,-3244,-3240,-3236,-3232,
|
||||
-3229,-3225,-3221,-3217,-3213,-3209,-3205,-3201,-3197,-3193,-3190,-3186,-3182,-3178,-3174,-3170,
|
||||
-3166,-3162,-3158,-3154,-3150,-3146,-3142,-3138,-3134,-3130,-3126,-3121,-3117,-3113,-3109,-3105,
|
||||
-3101,-3097,-3093,-3089,-3085,-3080,-3076,-3072,-3068,-3064,-3060,-3055,-3051,-3047,-3043,-3039,
|
||||
-3034,-3030,-3026,-3022,-3018,-3013,-3009,-3005,-3000,-2996,-2992,-2988,-2983,-2979,-2975,-2970,
|
||||
-2966,-2962,-2957,-2953,-2949,-2944,-2940,-2936,-2931,-2927,-2922,-2918,-2914,-2909,-2905,-2900,
|
||||
-2896,-2891,-2887,-2882,-2878,-2874,-2869,-2865,-2860,-2856,-2851,-2847,-2842,-2837,-2833,-2828,
|
||||
-2824,-2819,-2815,-2810,-2806,-2801,-2796,-2792,-2787,-2783,-2778,-2773,-2769,-2764,-2760,-2755,
|
||||
-2750,-2746,-2741,-2736,-2732,-2727,-2722,-2717,-2713,-2708,-2703,-2699,-2694,-2689,-2684,-2680,
|
||||
-2675,-2670,-2665,-2661,-2656,-2651,-2646,-2641,-2637,-2632,-2627,-2622,-2617,-2613,-2608,-2603,
|
||||
-2598,-2593,-2588,-2583,-2578,-2574,-2569,-2564,-2559,-2554,-2549,-2544,-2539,-2534,-2529,-2524,
|
||||
-2519,-2515,-2510,-2505,-2500,-2495,-2490,-2485,-2480,-2475,-2470,-2465,-2460,-2455,-2450,-2445,
|
||||
-2439,-2434,-2429,-2424,-2419,-2414,-2409,-2404,-2399,-2394,-2389,-2384,-2379,-2373,-2368,-2363,
|
||||
-2358,-2353,-2348,-2343,-2337,-2332,-2327,-2322,-2317,-2312,-2306,-2301,-2296,-2291,-2286,-2280,
|
||||
-2275,-2270,-2265,-2259,-2254,-2249,-2244,-2238,-2233,-2228,-2223,-2217,-2212,-2207,-2201,-2196,
|
||||
-2191,-2186,-2180,-2175,-2170,-2164,-2159,-2154,-2148,-2143,-2138,-2132,-2127,-2121,-2116,-2111,
|
||||
-2105,-2100,-2094,-2089,-2084,-2078,-2073,-2067,-2062,-2057,-2051,-2046,-2040,-2035,-2029,-2024,
|
||||
-2018,-2013,-2007,-2002,-1997,-1991,-1986,-1980,-1975,-1969,-1964,-1958,-1952,-1947,-1941,-1936,
|
||||
-1930,-1925,-1919,-1914,-1908,-1903,-1897,-1891,-1886,-1880,-1875,-1869,-1864,-1858,-1852,-1847,
|
||||
-1841,-1835,-1830,-1824,-1819,-1813,-1807,-1802,-1796,-1790,-1785,-1779,-1773,-1768,-1762,-1756,
|
||||
-1751,-1745,-1739,-1734,-1728,-1722,-1717,-1711,-1705,-1699,-1694,-1688,-1682,-1677,-1671,-1665,
|
||||
-1659,-1654,-1648,-1642,-1636,-1631,-1625,-1619,-1613,-1608,-1602,-1596,-1590,-1584,-1579,-1573,
|
||||
-1567,-1561,-1555,-1550,-1544,-1538,-1532,-1526,-1520,-1515,-1509,-1503,-1497,-1491,-1485,-1479,
|
||||
-1474,-1468,-1462,-1456,-1450,-1444,-1438,-1433,-1427,-1421,-1415,-1409,-1403,-1397,-1391,-1385,
|
||||
-1379,-1373,-1368,-1362,-1356,-1350,-1344,-1338,-1332,-1326,-1320,-1314,-1308,-1302,-1296,-1290,
|
||||
-1284,-1278,-1272,-1266,-1260,-1254,-1248,-1243,-1237,-1231,-1225,-1219,-1213,-1207,-1201,-1195,
|
||||
-1189,-1182,-1176,-1170,-1164,-1158,-1152,-1146,-1140,-1134,-1128,-1122,-1116,-1110,-1104,-1098,
|
||||
-1092,-1086,-1080,-1074,-1068,-1062,-1056,-1050,-1043,-1037,-1031,-1025,-1019,-1013,-1007,-1001,
|
||||
-995, -989, -983, -976, -970, -964, -958, -952, -946, -940, -934, -928, -921, -915, -909, -903,
|
||||
-897, -891, -885, -879, -872, -866, -860, -854, -848, -842, -836, -829, -823, -817, -811, -805,
|
||||
-799, -792, -786, -780, -774, -768, -762, -755, -749, -743, -737, -731, -725, -718, -712, -706,
|
||||
-700, -694, -687, -681, -675, -669, -663, -656, -650, -644, -638, -632, -625, -619, -613, -607,
|
||||
-601, -594, -588, -582, -576, -569, -563, -557, -551, -545, -538, -532, -526, -520, -513, -507,
|
||||
-501, -495, -488, -482, -476, -470, -463, -457, -451, -445, -438, -432, -426, -420, -413, -407,
|
||||
-401, -395, -388, -382, -376, -370, -363, -357, -351, -345, -338, -332, -326, -320, -313, -307,
|
||||
-301, -295, -288, -282, -276, -269, -263, -257, -251, -244, -238, -232, -226, -219, -213, -207,
|
||||
-200, -194, -188, -182, -175, -169, -163, -157, -150, -144, -138, -131, -125, -119, -113, -106,
|
||||
-100, -94, -87, -81, -75, -69, -62, -56, -50, -43, -37, -31, -25, -18, -12, -6,
|
||||
|
||||
0, 6, 12, 18, 25, 31, 37, 43, 50, 56, 62, 69, 75, 81, 87, 94,
|
||||
100, 106, 113, 119, 125, 131, 138, 144, 150, 157, 163, 169, 175, 182, 188, 194,
|
||||
200, 207, 213, 219, 226, 232, 238, 244, 251, 257, 263, 269, 276, 282, 288, 295,
|
||||
301, 307, 313, 320, 326, 332, 338, 345, 351, 357, 363, 370, 376, 382, 388, 395,
|
||||
401, 407, 413, 420, 426, 432, 438, 445, 451, 457, 463, 470, 476, 482, 488, 495,
|
||||
501, 507, 513, 520, 526, 532, 538, 545, 551, 557, 563, 569, 576, 582, 588, 594,
|
||||
601, 607, 613, 619, 625, 632, 638, 644, 650, 656, 663, 669, 675, 681, 687, 694,
|
||||
700, 706, 712, 718, 725, 731, 737, 743, 749, 755, 762, 768, 774, 780, 786, 792,
|
||||
799, 805, 811, 817, 823, 829, 836, 842, 848, 854, 860, 866, 872, 879, 885, 891,
|
||||
897, 903, 909, 915, 921, 928, 934, 940, 946, 952, 958, 964, 970, 976, 983, 989,
|
||||
995, 1001, 1007, 1013, 1019, 1025, 1031, 1037, 1043, 1050, 1056, 1062, 1068, 1074, 1080, 1086,
|
||||
1092, 1098, 1104, 1110, 1116, 1122, 1128, 1134, 1140, 1146, 1152, 1158, 1164, 1170, 1176, 1182,
|
||||
1189, 1195, 1201, 1207, 1213, 1219, 1225, 1231, 1237, 1243, 1248, 1254, 1260, 1266, 1272, 1278,
|
||||
1284, 1290, 1296, 1302, 1308, 1314, 1320, 1326, 1332, 1338, 1344, 1350, 1356, 1362, 1368, 1373,
|
||||
1379, 1385, 1391, 1397, 1403, 1409, 1415, 1421, 1427, 1433, 1438, 1444, 1450, 1456, 1462, 1468,
|
||||
1474, 1479, 1485, 1491, 1497, 1503, 1509, 1515, 1520, 1526, 1532, 1538, 1544, 1550, 1555, 1561,
|
||||
1567, 1573, 1579, 1584, 1590, 1596, 1602, 1608, 1613, 1619, 1625, 1631, 1636, 1642, 1648, 1654,
|
||||
1659, 1665, 1671, 1677, 1682, 1688, 1694, 1699, 1705, 1711, 1717, 1722, 1728, 1734, 1739, 1745,
|
||||
1751, 1756, 1762, 1768, 1773, 1779, 1785, 1790, 1796, 1802, 1807, 1813, 1819, 1824, 1830, 1835,
|
||||
1841, 1847, 1852, 1858, 1864, 1869, 1875, 1880, 1886, 1891, 1897, 1903, 1908, 1914, 1919, 1925,
|
||||
1930, 1936, 1941, 1947, 1952, 1958, 1964, 1969, 1975, 1980, 1986, 1991, 1997, 2002, 2007, 2013,
|
||||
2018, 2024, 2029, 2035, 2040, 2046, 2051, 2057, 2062, 2067, 2073, 2078, 2084, 2089, 2094, 2100,
|
||||
2105, 2111, 2116, 2121, 2127, 2132, 2138, 2143, 2148, 2154, 2159, 2164, 2170, 2175, 2180, 2186,
|
||||
2191, 2196, 2201, 2207, 2212, 2217, 2223, 2228, 2233, 2238, 2244, 2249, 2254, 2259, 2265, 2270,
|
||||
2275, 2280, 2286, 2291, 2296, 2301, 2306, 2312, 2317, 2322, 2327, 2332, 2337, 2343, 2348, 2353,
|
||||
2358, 2363, 2368, 2373, 2379, 2384, 2389, 2394, 2399, 2404, 2409, 2414, 2419, 2424, 2429, 2434,
|
||||
2439, 2445, 2450, 2455, 2460, 2465, 2470, 2475, 2480, 2485, 2490, 2495, 2500, 2505, 2510, 2515,
|
||||
2519, 2524, 2529, 2534, 2539, 2544, 2549, 2554, 2559, 2564, 2569, 2574, 2578, 2583, 2588, 2593,
|
||||
2598, 2603, 2608, 2613, 2617, 2622, 2627, 2632, 2637, 2641, 2646, 2651, 2656, 2661, 2665, 2670,
|
||||
2675, 2680, 2684, 2689, 2694, 2699, 2703, 2708, 2713, 2717, 2722, 2727, 2732, 2736, 2741, 2746,
|
||||
2750, 2755, 2760, 2764, 2769, 2773, 2778, 2783, 2787, 2792, 2796, 2801, 2806, 2810, 2815, 2819,
|
||||
2824, 2828, 2833, 2837, 2842, 2847, 2851, 2856, 2860, 2865, 2869, 2874, 2878, 2882, 2887, 2891,
|
||||
2896, 2900, 2905, 2909, 2914, 2918, 2922, 2927, 2931, 2936, 2940, 2944, 2949, 2953, 2957, 2962,
|
||||
2966, 2970, 2975, 2979, 2983, 2988, 2992, 2996, 3000, 3005, 3009, 3013, 3018, 3022, 3026, 3030,
|
||||
3034, 3039, 3043, 3047, 3051, 3055, 3060, 3064, 3068, 3072, 3076, 3080, 3085, 3089, 3093, 3097,
|
||||
3101, 3105, 3109, 3113, 3117, 3121, 3126, 3130, 3134, 3138, 3142, 3146, 3150, 3154, 3158, 3162,
|
||||
3166, 3170, 3174, 3178, 3182, 3186, 3190, 3193, 3197, 3201, 3205, 3209, 3213, 3217, 3221, 3225,
|
||||
3229, 3232, 3236, 3240, 3244, 3248, 3252, 3255, 3259, 3263, 3267, 3271, 3274, 3278, 3282, 3286,
|
||||
3289, 3293, 3297, 3301, 3304, 3308, 3312, 3315, 3319, 3323, 3326, 3330, 3334, 3337, 3341, 3345,
|
||||
3348, 3352, 3356, 3359, 3363, 3366, 3370, 3373, 3377, 3381, 3384, 3388, 3391, 3395, 3398, 3402,
|
||||
3405, 3409, 3412, 3416, 3419, 3423, 3426, 3429, 3433, 3436, 3440, 3443, 3447, 3450, 3453, 3457,
|
||||
3460, 3463, 3467, 3470, 3473, 3477, 3480, 3483, 3487, 3490, 3493, 3497, 3500, 3503, 3506, 3510,
|
||||
3513, 3516, 3519, 3522, 3526, 3529, 3532, 3535, 3538, 3541, 3545, 3548, 3551, 3554, 3557, 3560,
|
||||
3563, 3566, 3570, 3573, 3576, 3579, 3582, 3585, 3588, 3591, 3594, 3597, 3600, 3603, 3606, 3609,
|
||||
3612, 3615, 3618, 3621, 3624, 3627, 3629, 3632, 3635, 3638, 3641, 3644, 3647, 3650, 3652, 3655,
|
||||
3658, 3661, 3664, 3667, 3669, 3672, 3675, 3678, 3680, 3683, 3686, 3689, 3691, 3694, 3697, 3700,
|
||||
3702, 3705, 3708, 3710, 3713, 3716, 3718, 3721, 3723, 3726, 3729, 3731, 3734, 3736, 3739, 3742,
|
||||
3744, 3747, 3749, 3752, 3754, 3757, 3759, 3762, 3764, 3767, 3769, 3772, 3774, 3776, 3779, 3781,
|
||||
3784, 3786, 3789, 3791, 3793, 3796, 3798, 3800, 3803, 3805, 3807, 3810, 3812, 3814, 3816, 3819,
|
||||
3821, 3823, 3826, 3828, 3830, 3832, 3834, 3837, 3839, 3841, 3843, 3845, 3848, 3850, 3852, 3854,
|
||||
3856, 3858, 3860, 3862, 3864, 3867, 3869, 3871, 3873, 3875, 3877, 3879, 3881, 3883, 3885, 3887,
|
||||
3889, 3891, 3893, 3895, 3897, 3899, 3900, 3902, 3904, 3906, 3908, 3910, 3912, 3914, 3915, 3917,
|
||||
3919, 3921, 3923, 3925, 3926, 3928, 3930, 3932, 3933, 3935, 3937, 3939, 3940, 3942, 3944, 3945,
|
||||
3947, 3949, 3950, 3952, 3954, 3955, 3957, 3959, 3960, 3962, 3963, 3965, 3967, 3968, 3970, 3971,
|
||||
3973, 3974, 3976, 3977, 3979, 3980, 3982, 3983, 3985, 3986, 3988, 3989, 3990, 3992, 3993, 3995,
|
||||
3996, 3997, 3999, 4000, 4001, 4003, 4004, 4005, 4007, 4008, 4009, 4011, 4012, 4013, 4014, 4016,
|
||||
4017, 4018, 4019, 4020, 4022, 4023, 4024, 4025, 4026, 4027, 4029, 4030, 4031, 4032, 4033, 4034,
|
||||
4035, 4036, 4037, 4038, 4039, 4040, 4041, 4042, 4043, 4044, 4045, 4046, 4047, 4048, 4049, 4050,
|
||||
4051, 4052, 4053, 4054, 4055, 4056, 4057, 4057, 4058, 4059, 4060, 4061, 4062, 4062, 4063, 4064,
|
||||
4065, 4065, 4066, 4067, 4068, 4068, 4069, 4070, 4071, 4071, 4072, 4073, 4073, 4074, 4075, 4075,
|
||||
4076, 4076, 4077, 4078, 4078, 4079, 4079, 4080, 4080, 4081, 4081, 4082, 4082, 4083, 4083, 4084,
|
||||
4084, 4085, 4085, 4086, 4086, 4087, 4087, 4087, 4088, 4088, 4089, 4089, 4089, 4090, 4090, 4090,
|
||||
4091, 4091, 4091, 4091, 4092, 4092, 4092, 4092, 4093, 4093, 4093, 4093, 4094, 4094, 4094, 4094,
|
||||
4094, 4094, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095,
|
||||
|
||||
4096,
|
||||
};
|
||||
|
||||
/* code can't actually access 2nd index >8 though */
|
||||
static const int scale_table[5][8*2] = {
|
||||
{ 2048, 2048, 4096, 4096, 8192, 8192,16384,16384, 1, 1, 1, 1, 1, 1, 1, 1, },
|
||||
{ 2048, 2048, 2048, 4096, 4096, 4096, 8192,16384, 1, 1, 1, 1, 1, 1, 1, 1, },
|
||||
{ 2048, 2048, 2048, 2048, 2048, 4096, 4096, 8192, 1, 1, 1, 1, 1, 1, 1, 1, },
|
||||
{ 1024, 1024, 1024, 2048, 2048, 2048, 2048, 2048, 1, 1, 1, 1, 1, 1, 1, 1, },
|
||||
{ 1365, 1365, 2048, 2730, 2730, 4096, 4096, 4096, 1, 1, 1, 1, 1, 1, 1, 1, },
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,297 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Decompresses Circus's custom LZ used in XPCM as a machine state for streaming,
|
||||
* that may break during any step. Original code decompress at once the full thing
|
||||
* into memory so it's simpler. */
|
||||
|
||||
#define LZXPCM_OK 0
|
||||
#define LZXPCM_ERROR -1
|
||||
#define LZXPCM_WINDOW_SIZE (1 << 16)
|
||||
|
||||
|
||||
typedef enum {
|
||||
READ_FLAGS,
|
||||
COPY_LITERAL,
|
||||
READ_TOKEN,
|
||||
PARSE_TOKEN,
|
||||
SET_MATCH,
|
||||
COPY_MATCH
|
||||
} lzxpcm_state_t;
|
||||
|
||||
typedef struct {
|
||||
lzxpcm_state_t state;
|
||||
|
||||
uint32_t flags;
|
||||
uint8_t token;
|
||||
int values_pos;
|
||||
int offset_pos;
|
||||
int match_len;
|
||||
int match_pos;
|
||||
|
||||
int window_pos;
|
||||
uint8_t window[LZXPCM_WINDOW_SIZE];
|
||||
} lzxpcm_context_t;
|
||||
|
||||
typedef struct {
|
||||
lzxpcm_context_t ctx;
|
||||
|
||||
uint8_t *next_out; /* next bytes to write (reassign when avail is 0) */
|
||||
int avail_out; /* bytes available at next_out */
|
||||
int total_out; /* written bytes, for reference (set to 0 per call if needed) */
|
||||
|
||||
const uint8_t *next_in; /* next bytes to read (reassign when avail is 0) */
|
||||
int avail_in; /* bytes available at next_in */
|
||||
int total_in; /* read bytes, for reference (set to 0 per call if needed) */
|
||||
} lzxpcm_stream_t;
|
||||
|
||||
|
||||
static void lzxpcm_reset(lzxpcm_stream_t* strm) {
|
||||
memset(strm, 0, sizeof(lzxpcm_stream_t));
|
||||
}
|
||||
|
||||
/* Decompress src into dst, returning a code and number of bytes used. Caller must handle
|
||||
* stop (when no more input data or all data has been decompressed) as LZXPCM has no end marker. */
|
||||
static int lzxpcm_decompress(lzxpcm_stream_t* strm) {
|
||||
lzxpcm_context_t* ctx = &strm->ctx;
|
||||
uint8_t* dst = strm->next_out;
|
||||
const uint8_t* src = strm->next_in;
|
||||
int dst_size = strm->avail_out;
|
||||
int src_size = strm->avail_in;
|
||||
int dst_pos = 0;
|
||||
int src_pos = 0;
|
||||
uint8_t next_val;
|
||||
|
||||
|
||||
while (1) {
|
||||
/* mostly linear state machine, but it may break anytime when reaching dst or src
|
||||
* end, and resume from same state in next call */
|
||||
switch(ctx->state) {
|
||||
|
||||
case READ_FLAGS:
|
||||
if (src_pos >= src_size)
|
||||
goto buffer_end;
|
||||
|
||||
ctx->flags >>= 1;
|
||||
|
||||
if ((ctx->flags & 0x0100) == 0) {
|
||||
ctx->flags = 0xFF00 | src[src_pos++];
|
||||
}
|
||||
|
||||
if (ctx->flags & 1)
|
||||
ctx->state = COPY_LITERAL;
|
||||
else
|
||||
ctx->state = READ_TOKEN;
|
||||
break;
|
||||
|
||||
case COPY_LITERAL:
|
||||
if (src_pos >= src_size || dst_pos >= dst_size)
|
||||
goto buffer_end;
|
||||
next_val = src[src_pos++];
|
||||
|
||||
dst[dst_pos++] = next_val;
|
||||
|
||||
ctx->window[ctx->window_pos++] = next_val;
|
||||
if (ctx->window_pos == LZXPCM_WINDOW_SIZE)
|
||||
ctx->window_pos = 0;
|
||||
|
||||
ctx->state = READ_FLAGS;
|
||||
break;
|
||||
|
||||
case READ_TOKEN:
|
||||
if (src_pos >= src_size)
|
||||
goto buffer_end;
|
||||
ctx->token = src[src_pos++];
|
||||
|
||||
ctx->values_pos = 0;
|
||||
|
||||
ctx->state = PARSE_TOKEN;
|
||||
break;
|
||||
|
||||
case PARSE_TOKEN:
|
||||
if (ctx->token >= 0xC0) {
|
||||
ctx->match_len = ((ctx->token >> 2) & 0x0F) + 4; /* 6b */
|
||||
|
||||
if (src_pos >= src_size)
|
||||
goto buffer_end;
|
||||
ctx->offset_pos = src[src_pos++]; /* upper 2b + lower 8b */
|
||||
ctx->offset_pos |= ((ctx->token & 3) << 8);
|
||||
|
||||
}
|
||||
else if (ctx->token >= 0x80) {
|
||||
ctx->match_len = ((ctx->token >> 5) & 3) + 2; /* 2b */
|
||||
|
||||
ctx->offset_pos = ctx->token & 0x1F; /* 5b */
|
||||
if (ctx->offset_pos == 0) {
|
||||
if (src_pos >= src_size)
|
||||
goto buffer_end;
|
||||
ctx->offset_pos = src[src_pos++];
|
||||
}
|
||||
}
|
||||
else if (ctx->token == 0x7F) {
|
||||
if (ctx->values_pos == 0) {
|
||||
if (src_pos >= src_size)
|
||||
goto buffer_end;
|
||||
ctx->match_len = (src[src_pos++] << 0u);
|
||||
ctx->values_pos++;
|
||||
}
|
||||
|
||||
if (ctx->values_pos == 1) {
|
||||
if (src_pos >= src_size)
|
||||
goto buffer_end;
|
||||
ctx->match_len |= (src[src_pos++] << 8u);
|
||||
ctx->match_len += 2;
|
||||
ctx->values_pos++;
|
||||
}
|
||||
|
||||
if (ctx->values_pos == 2) {
|
||||
if (src_pos >= src_size)
|
||||
goto buffer_end;
|
||||
ctx->offset_pos = (src[src_pos++] << 0u);
|
||||
ctx->values_pos++;
|
||||
}
|
||||
|
||||
if (ctx->values_pos == 3) {
|
||||
if (src_pos >= src_size)
|
||||
goto buffer_end;
|
||||
ctx->offset_pos |= (src[src_pos++] << 8u);
|
||||
ctx->values_pos++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
ctx->match_len = ctx->token + 4;
|
||||
|
||||
if (ctx->values_pos == 0) {
|
||||
if (src_pos >= src_size)
|
||||
goto buffer_end;
|
||||
ctx->offset_pos = (src[src_pos++] << 0u);
|
||||
ctx->values_pos++;
|
||||
}
|
||||
|
||||
if (ctx->values_pos == 1) {
|
||||
if (src_pos >= src_size)
|
||||
goto buffer_end;
|
||||
ctx->offset_pos |= (src[src_pos++] << 8u);
|
||||
ctx->values_pos++;
|
||||
}
|
||||
}
|
||||
|
||||
ctx->state = SET_MATCH;
|
||||
break;
|
||||
|
||||
case SET_MATCH:
|
||||
ctx->match_pos = ctx->window_pos - ctx->offset_pos;
|
||||
if (ctx->match_pos < 0) /* circular buffer so negative is from window end */
|
||||
ctx->match_pos = LZXPCM_WINDOW_SIZE + ctx->match_pos;
|
||||
|
||||
ctx->state = COPY_MATCH;
|
||||
break;
|
||||
|
||||
case COPY_MATCH:
|
||||
while (ctx->match_len > 0) {
|
||||
if (dst_pos >= dst_size)
|
||||
goto buffer_end;
|
||||
|
||||
next_val = ctx->window[ctx->match_pos++];
|
||||
if (ctx->match_pos == LZXPCM_WINDOW_SIZE)
|
||||
ctx->match_pos = 0;
|
||||
|
||||
dst[dst_pos++] = next_val;
|
||||
|
||||
ctx->window[ctx->window_pos++] = next_val;
|
||||
if (ctx->window_pos == LZXPCM_WINDOW_SIZE)
|
||||
ctx->window_pos = 0;
|
||||
|
||||
ctx->match_len--;
|
||||
};
|
||||
|
||||
ctx->state = READ_FLAGS;
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
buffer_end:
|
||||
strm->next_out += dst_pos;
|
||||
strm->next_in += src_pos;
|
||||
strm->avail_out -= dst_pos;
|
||||
strm->avail_in -= src_pos;
|
||||
strm->total_out += dst_pos;
|
||||
strm->total_in += src_pos;
|
||||
|
||||
return LZXPCM_OK;
|
||||
fail:
|
||||
return LZXPCM_ERROR;
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
/* non-streamed form that XPCM originally uses, assumes buffers are big enough */
|
||||
static int lzxpcm_decompress_full(uint8_t* dst, size_t dst_size, const uint8_t* src, size_t src_size) {
|
||||
int src_pos = 0;
|
||||
int dst_pos = 0;
|
||||
uint32_t flags = 0;
|
||||
|
||||
|
||||
while (src_pos < src_size && dst_pos < dst_size) {
|
||||
flags >>= 1;
|
||||
|
||||
if ((flags & 0x0100) == 0) {
|
||||
flags = 0xFF00 | src[src_pos++];
|
||||
}
|
||||
|
||||
if (flags & 1) {
|
||||
/* uncompressed byte per bit */
|
||||
dst[dst_pos++] = src[src_pos++];
|
||||
}
|
||||
else {
|
||||
/* compressed data */
|
||||
uint32_t length;
|
||||
uint32_t offset;
|
||||
const uint32_t token = src[src_pos++];
|
||||
|
||||
if (token >= 0xC0) {
|
||||
length = ((token >> 2) & 0x0F) + 4; /* 6b */
|
||||
|
||||
offset = ((token & 3) << 8) | src[src_pos++]; /* upper 2b + lower 8b */
|
||||
}
|
||||
else if (token >= 0x80) {
|
||||
length = ((token >> 5) & 3) + 2; /* 2b */
|
||||
|
||||
offset = token & 0x1F; /* 5b */
|
||||
if (offset == 0) {
|
||||
offset = src[src_pos++];
|
||||
}
|
||||
}
|
||||
else if (token == 0x7F) {
|
||||
length = (uint16_t)(src[src_pos] | src[src_pos+1] << 8u) + 2;
|
||||
src_pos += 2;
|
||||
|
||||
offset = (uint16_t)(src[src_pos] | src[src_pos+1] << 8u);
|
||||
src_pos += 2;
|
||||
}
|
||||
else {
|
||||
length = token + 4;
|
||||
|
||||
offset = (uint16_t)(src[src_pos] | src[src_pos+1] << 8u);
|
||||
src_pos += 2;
|
||||
}
|
||||
|
||||
if (dst_pos + length > dst_size) {
|
||||
length = dst_size - dst_pos;
|
||||
}
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
dst[dst_pos] = dst[dst_pos - offset];
|
||||
dst_pos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -35,7 +35,7 @@ void decode_apple_ima4(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channel
|
|||
void decode_fsb_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
|
||||
void decode_wwise_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
|
||||
void decode_awc_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
void decode_ubi_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
|
||||
void decode_ubi_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int codec_config);
|
||||
void decode_h4m_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, uint16_t frame_format);
|
||||
size_t ima_bytes_to_samples(size_t bytes, int channels);
|
||||
size_t ms_ima_bytes_to_samples(size_t bytes, int block_align, int channels);
|
||||
|
@ -167,6 +167,7 @@ void decode_fadpcm(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacin
|
|||
|
||||
/* asf_decoder */
|
||||
void decode_asf(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
int32_t asf_bytes_to_samples(size_t bytes, int channels);
|
||||
|
||||
/* dsa_decoder */
|
||||
void decode_dsa(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
|
@ -178,7 +179,13 @@ void decode_xmd(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing,
|
|||
void decode_derf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
|
||||
/* circus_decoder */
|
||||
void decode_circus_adpcm(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
typedef struct circus_codec_data circus_codec_data;
|
||||
circus_codec_data* init_circus_vq(STREAMFILE* sf, off_t start, uint8_t codec, uint8_t flags);
|
||||
void decode_circus_vq(circus_codec_data* data, sample_t* outbuf, int32_t samples_to_do, int channels);
|
||||
void reset_circus_vq(circus_codec_data* data);
|
||||
void seek_circus_vq(circus_codec_data* data, int32_t num_sample);
|
||||
void free_circus_vq(circus_codec_data* data);
|
||||
void decode_circus_adpcm(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
|
||||
/* oki_decoder */
|
||||
void decode_pcfx(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int mode);
|
||||
|
|
|
@ -1009,8 +1009,9 @@ void decode_awc_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspa
|
|||
|
||||
|
||||
/* DVI stereo/mono with some mini header and sample output */
|
||||
void decode_ubi_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
||||
void decode_ubi_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int codec_config) {
|
||||
int i, sample_count = 0;
|
||||
int has_header = (codec_config & 0x80) == 0;
|
||||
|
||||
int32_t hist1 = stream->adpcm_history1_32;
|
||||
int step_index = stream->adpcm_step_index;
|
||||
|
@ -1018,7 +1019,7 @@ void decode_ubi_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspa
|
|||
//internal interleave
|
||||
|
||||
//header in the beginning of the stream
|
||||
if (stream->channel_start_offset == stream->offset) {
|
||||
if (has_header && stream->channel_start_offset == stream->offset) {
|
||||
int version, big_endian, header_samples, max_samples_to_do;
|
||||
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
|
||||
off_t offset = stream->offset;
|
||||
|
@ -1051,8 +1052,12 @@ void decode_ubi_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspa
|
|||
}
|
||||
}
|
||||
|
||||
if (has_header) {
|
||||
first_sample -= 10; //todo fix hack (needed to adjust nibble offset below)
|
||||
}
|
||||
|
||||
first_sample -= 10; //todo fix hack (needed to adjust nibble offset below)
|
||||
if (step_index < 0) step_index=0;
|
||||
if (step_index > 88) step_index=88;
|
||||
|
||||
for (i = first_sample; i < first_sample + samples_to_do; i++, sample_count += channelspacing) {
|
||||
off_t byte_offset = channelspacing == 1 ?
|
||||
|
|
|
@ -173,25 +173,25 @@ void decode_hevag(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspaci
|
|||
if (shift_factor > 12)
|
||||
shift_factor = 9; /* ? */
|
||||
|
||||
shift_factor = 20 - shift_factor;
|
||||
/* decode nibbles */
|
||||
for (i = first_sample; i < first_sample + samples_to_do; i++) {
|
||||
int32_t sample = 0, scale = 0;
|
||||
int32_t sample = 0;
|
||||
|
||||
if (flag < 0x07) { /* with flag 0x07 decoded sample must be 0 */
|
||||
uint8_t nibbles = frame[0x02 + i/2];
|
||||
|
||||
scale = i&1 ? /* low nibble first */
|
||||
sample = (i&1 ? /* low nibble first */
|
||||
get_high_nibble_signed(nibbles):
|
||||
get_low_nibble_signed(nibbles);
|
||||
sample = (hist1 * hevag_coefs[coef_index][0] +
|
||||
hist2 * hevag_coefs[coef_index][1] +
|
||||
hist3 * hevag_coefs[coef_index][2] +
|
||||
hist4 * hevag_coefs[coef_index][3] ) / 32;
|
||||
sample = (sample + (scale << (20 - shift_factor)) + 128) >> 8;
|
||||
sample = clamp16(sample);
|
||||
get_low_nibble_signed(nibbles)) << shift_factor; /*scale*/
|
||||
sample = ((hist1 * hevag_coefs[coef_index][0] +
|
||||
hist2 * hevag_coefs[coef_index][1] +
|
||||
hist3 * hevag_coefs[coef_index][2] +
|
||||
hist4 * hevag_coefs[coef_index][3]) >> 5) + sample;
|
||||
sample >>= 8;
|
||||
}
|
||||
|
||||
outbuf[sample_count] = sample;
|
||||
outbuf[sample_count] = clamp16(sample); /*clamping*/
|
||||
sample_count += channelspacing;
|
||||
|
||||
hist4 = hist3;
|
||||
|
|
|
@ -93,22 +93,22 @@ void decode_psx(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing,
|
|||
VGM_ASSERT_ONCE(flag > 7,"PS-ADPCM: unknown flag at %x\n", (uint32_t)frame_offset); /* meta should use PSX-badflags */
|
||||
|
||||
|
||||
shift_factor = 20 - shift_factor;
|
||||
/* decode nibbles */
|
||||
for (i = first_sample; i < first_sample + samples_to_do; i++) {
|
||||
int32_t sample = 0;
|
||||
|
||||
if (flag < 0x07) { /* with flag 0x07 decoded sample must be 0 */
|
||||
uint8_t nibbles = frame[0x02 + i/2];
|
||||
|
||||
sample = i&1 ? /* low nibble first */
|
||||
(nibbles >> 4) & 0x0f :
|
||||
(nibbles >> 0) & 0x0f;
|
||||
sample = (int16_t)((sample << 12) & 0xf000) >> shift_factor; /* 16b sign extend + scale */
|
||||
sample = (int32_t)(sample + ps_adpcm_coefs_f[coef_index][0]*hist1 + ps_adpcm_coefs_f[coef_index][1]*hist2);
|
||||
sample = clamp16(sample);
|
||||
|
||||
sample = (i&1 ? /* low nibble first */
|
||||
get_high_nibble_signed(nibbles):
|
||||
get_low_nibble_signed(nibbles)) << shift_factor; /*scale*/
|
||||
sample = sample + (int32_t)((ps_adpcm_coefs_f[coef_index][0]*hist1 + ps_adpcm_coefs_f[coef_index][1]*hist2) * 256.0f);
|
||||
sample >>= 8;
|
||||
}
|
||||
|
||||
outbuf[sample_count] = sample;
|
||||
outbuf[sample_count] = clamp16(sample); /*clamping*/
|
||||
sample_count += channelspacing;
|
||||
|
||||
hist2 = hist1;
|
||||
|
@ -192,7 +192,6 @@ void decode_psx_pivotal(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channe
|
|||
uint8_t coef_index, shift_factor;
|
||||
int32_t hist1 = stream->adpcm_history1_32;
|
||||
int32_t hist2 = stream->adpcm_history2_32;
|
||||
float scale;
|
||||
|
||||
|
||||
/* external interleave (variable size), mono */
|
||||
|
@ -213,21 +212,19 @@ void decode_psx_pivotal(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channe
|
|||
if (shift_factor > 12) /* same */
|
||||
shift_factor = 12;
|
||||
|
||||
scale = (float)(1.0 / (double)(1 << shift_factor));
|
||||
|
||||
|
||||
shift_factor = 20 - shift_factor;
|
||||
/* decode nibbles */
|
||||
for (i = first_sample; i < first_sample + samples_to_do; i++) {
|
||||
int32_t sample = 0;
|
||||
uint8_t nibbles = frame[0x01 + i/2];
|
||||
|
||||
sample = !(i&1) ? /* low nibble first */
|
||||
(nibbles >> 0) & 0x0f :
|
||||
(nibbles >> 4) & 0x0f;
|
||||
sample = (int16_t)((sample << 12) & 0xf000); /* 16b sign extend + default scale */
|
||||
sample = sample*scale + ps_adpcm_coefs_f[coef_index][0]*hist1 + ps_adpcm_coefs_f[coef_index][1]*hist2; /* actually substracts negative coefs but whatevs */
|
||||
sample = (i&1 ? /* low nibble first */
|
||||
get_high_nibble_signed(nibbles):
|
||||
get_low_nibble_signed(nibbles)) << shift_factor; /*scale*/
|
||||
sample = sample + (int32_t)((ps_adpcm_coefs_f[coef_index][0]*hist1 + ps_adpcm_coefs_f[coef_index][1]*hist2) * 256.0f); /* actually substracts negative coefs but whatevs */
|
||||
sample >>= 8;
|
||||
|
||||
outbuf[sample_count] = clamp16(sample);
|
||||
outbuf[sample_count] = clamp16(sample); /*clamping*/
|
||||
sample_count += channelspacing;
|
||||
|
||||
hist2 = hist1;
|
||||
|
|
|
@ -1,145 +1,152 @@
|
|||
#include "vorbis_custom_decoder.h"
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
#include <vorbis/codec.h>
|
||||
|
||||
|
||||
/* **************************************************************************** */
|
||||
/* DEFS */
|
||||
/* **************************************************************************** */
|
||||
|
||||
static int get_packet_header(STREAMFILE *streamFile, off_t *offset, size_t *size);
|
||||
static int build_header_comment(uint8_t * buf, size_t bufsize);
|
||||
|
||||
|
||||
/* **************************************************************************** */
|
||||
/* EXTERNAL API */
|
||||
/* **************************************************************************** */
|
||||
|
||||
/**
|
||||
* VID1 removes the Ogg layer and uses a block layout with custom packet headers.
|
||||
*
|
||||
* Info from hcs's vid1_2ogg: https://github.com/hcs64/vgm_ripping/tree/master/demux/vid1_2ogg
|
||||
*/
|
||||
int vorbis_custom_setup_init_vid1(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data) {
|
||||
off_t offset = start_offset;
|
||||
size_t packet_size = 0;
|
||||
|
||||
/* read header packets (id/setup), each with an VID1 header */
|
||||
|
||||
/* normal identificacion packet */
|
||||
get_packet_header(streamFile, &offset, &packet_size);
|
||||
if (packet_size > data->buffer_size) goto fail;
|
||||
data->op.bytes = read_streamfile(data->buffer,offset,packet_size, streamFile);
|
||||
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse identification header */
|
||||
offset += packet_size;
|
||||
|
||||
/* generate comment packet */
|
||||
data->op.bytes = build_header_comment(data->buffer, data->buffer_size);
|
||||
if (!data->op.bytes) goto fail;
|
||||
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) !=0 ) goto fail; /* parse comment header */
|
||||
|
||||
/* normal setup packet */
|
||||
get_packet_header(streamFile, &offset, &packet_size);
|
||||
if (packet_size > data->buffer_size) goto fail;
|
||||
data->op.bytes = read_streamfile(data->buffer,offset,packet_size, streamFile);
|
||||
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse setup header */
|
||||
offset += packet_size;
|
||||
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int vorbis_custom_parse_packet_vid1(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data) {
|
||||
size_t bytes;
|
||||
|
||||
|
||||
/* test block start */
|
||||
if (read_32bitBE(stream->offset + 0x00,stream->streamfile) == 0x4652414D && /* "FRAM" */
|
||||
read_32bitBE(stream->offset + 0x20,stream->streamfile) == 0x41554444) { /* "AUDD" */
|
||||
data->block_offset = stream->offset;
|
||||
data->block_size = read_32bitBE(stream->offset + 0x2c,stream->streamfile);
|
||||
stream->offset += 0x34; /* actual start, rest is chunk sizes and maybe granule info */
|
||||
}
|
||||
|
||||
|
||||
/* get packet info the VID1 header */
|
||||
get_packet_header(stream->streamfile, &stream->offset, (uint32_t*)&data->op.bytes);
|
||||
if (data->op.bytes == 0 || data->op.bytes > data->buffer_size) goto fail; /* EOF or end padding */
|
||||
|
||||
/* read raw block */
|
||||
bytes = read_streamfile(data->buffer,stream->offset, data->op.bytes,stream->streamfile);
|
||||
stream->offset += data->op.bytes;
|
||||
if (bytes != data->op.bytes) goto fail; /* wrong packet? */
|
||||
|
||||
//todo: sometimes there are short packets like 01be590000 and Vorbis complains and skips, no idea
|
||||
|
||||
/* test block end (weird size calc but seems ok) */
|
||||
if ((stream->offset - (data->block_offset + 0x34)) >= (data->block_size - 0x06)) {
|
||||
stream->offset = data->block_offset + read_32bitBE(data->block_offset + 0x04,stream->streamfile);
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* **************************************************************************** */
|
||||
/* INTERNAL HELPERS */
|
||||
/* **************************************************************************** */
|
||||
|
||||
static int build_header_comment(uint8_t * buf, size_t bufsize) {
|
||||
int bytes = 0x19;
|
||||
|
||||
if (bytes > bufsize) return 0;
|
||||
|
||||
put_8bit (buf+0x00, 0x03); /* packet_type (comments) */
|
||||
memcpy (buf+0x01, "vorbis", 6); /* id */
|
||||
put_32bitLE(buf+0x07, 0x09); /* vendor_length */
|
||||
memcpy (buf+0x0b, "vgmstream", 9); /* vendor_string */
|
||||
put_32bitLE(buf+0x14, 0x00); /* user_comment_list_length */
|
||||
put_8bit (buf+0x18, 0x01); /* framing_flag (fixed) */
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/* read header in Vorbis bitpacking format */
|
||||
static int get_packet_header(STREAMFILE *streamFile, off_t *offset, size_t *size) {
|
||||
uint8_t ibuf[0x04]; /* header buffer */
|
||||
size_t ibufsize = 0x04; /* header ~max */
|
||||
vgm_bitstream ib = {0};
|
||||
uint32_t size_bits;
|
||||
|
||||
|
||||
if (read_streamfile(ibuf,(*offset),ibufsize, streamFile) != ibufsize)
|
||||
goto fail;
|
||||
ib.buf = ibuf;
|
||||
ib.bufsize = ibufsize;
|
||||
ib.b_off = 0;
|
||||
ib.mode = BITSTREAM_VORBIS;
|
||||
|
||||
/* read using Vorbis weird LSF */
|
||||
r_bits(&ib, 4,&size_bits);
|
||||
r_bits(&ib, (size_bits+1),(uint32_t*)size);
|
||||
|
||||
/* special meaning, seen in silent frames */
|
||||
if (size_bits == 0 && *size == 0 && (uint8_t)read_8bit(*offset, streamFile)==0x80) {
|
||||
*size = 0x01;
|
||||
}
|
||||
|
||||
/* pad and convert to byte offset */
|
||||
if (ib.b_off % 8)
|
||||
ib.b_off += 8 - (ib.b_off % 8);
|
||||
*offset += (ib.b_off/8);
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
#include "vorbis_custom_decoder.h"
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
#include <vorbis/codec.h>
|
||||
|
||||
|
||||
/* **************************************************************************** */
|
||||
/* DEFS */
|
||||
/* **************************************************************************** */
|
||||
|
||||
static int get_packet_header(STREAMFILE *streamFile, off_t *offset, size_t *size);
|
||||
static int build_header_comment(uint8_t * buf, size_t bufsize);
|
||||
|
||||
|
||||
/* **************************************************************************** */
|
||||
/* EXTERNAL API */
|
||||
/* **************************************************************************** */
|
||||
|
||||
/**
|
||||
* VID1 removes the Ogg layer and uses a block layout with custom packet headers.
|
||||
*
|
||||
* Info from hcs's vid1_2ogg: https://github.com/hcs64/vgm_ripping/tree/master/demux/vid1_2ogg
|
||||
*/
|
||||
int vorbis_custom_setup_init_vid1(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data) {
|
||||
off_t offset = start_offset;
|
||||
size_t packet_size = 0;
|
||||
|
||||
/* read header packets (id/setup), each with an VID1 header */
|
||||
|
||||
/* normal identificacion packet */
|
||||
get_packet_header(streamFile, &offset, &packet_size);
|
||||
if (packet_size > data->buffer_size) goto fail;
|
||||
data->op.bytes = read_streamfile(data->buffer,offset,packet_size, streamFile);
|
||||
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse identification header */
|
||||
offset += packet_size;
|
||||
|
||||
/* generate comment packet */
|
||||
data->op.bytes = build_header_comment(data->buffer, data->buffer_size);
|
||||
if (!data->op.bytes) goto fail;
|
||||
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) !=0 ) goto fail; /* parse comment header */
|
||||
|
||||
/* normal setup packet */
|
||||
get_packet_header(streamFile, &offset, &packet_size);
|
||||
if (packet_size > data->buffer_size) goto fail;
|
||||
data->op.bytes = read_streamfile(data->buffer,offset,packet_size, streamFile);
|
||||
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse setup header */
|
||||
offset += packet_size;
|
||||
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int vorbis_custom_parse_packet_vid1(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data) {
|
||||
size_t bytes;
|
||||
|
||||
|
||||
/* test block start */
|
||||
if (read_32bitBE(stream->offset + 0x00,stream->streamfile) == 0x4652414D) { /* "FRAM" */
|
||||
stream->offset += 0x20;
|
||||
|
||||
if (read_32bitBE(stream->offset + 0x00,stream->streamfile) == 0x56494444) { /* "VIDD"*/
|
||||
stream->offset += read_32bitBE(stream->offset + 0x04, stream->streamfile);
|
||||
}
|
||||
|
||||
if (read_32bitBE(stream->offset + 0x00,stream->streamfile) == 0x41554444) { /* "AUDD" */
|
||||
data->block_offset = stream->offset;
|
||||
data->block_size = read_32bitBE(stream->offset + 0x0c,stream->streamfile);
|
||||
stream->offset += 0x14; /* actual start, rest is chunk sizes and maybe granule info */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* get packet info the VID1 header */
|
||||
get_packet_header(stream->streamfile, &stream->offset, (uint32_t*)&data->op.bytes);
|
||||
if (data->op.bytes == 0 || data->op.bytes > data->buffer_size) goto fail; /* EOF or end padding */
|
||||
|
||||
/* read raw block */
|
||||
bytes = read_streamfile(data->buffer,stream->offset, data->op.bytes,stream->streamfile);
|
||||
stream->offset += data->op.bytes;
|
||||
if (bytes != data->op.bytes) goto fail; /* wrong packet? */
|
||||
|
||||
//todo: sometimes there are short packets like 01be590000 and Vorbis complains and skips, no idea
|
||||
|
||||
/* test block end (weird size calc but seems ok) */
|
||||
if ((stream->offset - (data->block_offset + 0x14)) >= (data->block_size - 0x06)) {
|
||||
stream->offset = data->block_offset + read_32bitBE(data->block_offset + 0x04,stream->streamfile);
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* **************************************************************************** */
|
||||
/* INTERNAL HELPERS */
|
||||
/* **************************************************************************** */
|
||||
|
||||
static int build_header_comment(uint8_t * buf, size_t bufsize) {
|
||||
int bytes = 0x19;
|
||||
|
||||
if (bytes > bufsize) return 0;
|
||||
|
||||
put_8bit (buf+0x00, 0x03); /* packet_type (comments) */
|
||||
memcpy (buf+0x01, "vorbis", 6); /* id */
|
||||
put_32bitLE(buf+0x07, 0x09); /* vendor_length */
|
||||
memcpy (buf+0x0b, "vgmstream", 9); /* vendor_string */
|
||||
put_32bitLE(buf+0x14, 0x00); /* user_comment_list_length */
|
||||
put_8bit (buf+0x18, 0x01); /* framing_flag (fixed) */
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/* read header in Vorbis bitpacking format */
|
||||
static int get_packet_header(STREAMFILE *streamFile, off_t *offset, size_t *size) {
|
||||
uint8_t ibuf[0x04]; /* header buffer */
|
||||
size_t ibufsize = 0x04; /* header ~max */
|
||||
vgm_bitstream ib = {0};
|
||||
uint32_t size_bits;
|
||||
|
||||
|
||||
if (read_streamfile(ibuf,(*offset),ibufsize, streamFile) != ibufsize)
|
||||
goto fail;
|
||||
ib.buf = ibuf;
|
||||
ib.bufsize = ibufsize;
|
||||
ib.b_off = 0;
|
||||
ib.mode = BITSTREAM_VORBIS;
|
||||
|
||||
/* read using Vorbis weird LSF */
|
||||
r_bits(&ib, 4,&size_bits);
|
||||
r_bits(&ib, (size_bits+1),(uint32_t*)size);
|
||||
|
||||
/* special meaning, seen in silent frames */
|
||||
if (size_bits == 0 && *size == 0 && (uint8_t)read_8bit(*offset, streamFile)==0x80) {
|
||||
*size = 0x01;
|
||||
}
|
||||
|
||||
/* pad and convert to byte offset */
|
||||
if (ib.b_off % 8)
|
||||
ib.b_off += 8 - (ib.b_off % 8);
|
||||
*offset += (ib.b_off/8);
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -64,6 +64,7 @@ static const char* extension_list[] = {
|
|||
"akb",
|
||||
"al",
|
||||
"al2",
|
||||
"ams", //txth/reserved [Super Dragon Ball Z (PS2) ELF names]
|
||||
"amts", //fake extension/header id for .stm (renamed? to be removed?)
|
||||
"ao",
|
||||
"apc",
|
||||
|
@ -297,6 +298,8 @@ static const char* extension_list[] = {
|
|||
"mihb",
|
||||
"mnstr",
|
||||
"mogg",
|
||||
//"m4a", //common
|
||||
//"m4v", //common
|
||||
//"mp+", //common [Moonshine Runners (PC)]
|
||||
//"mp2", //common
|
||||
//"mp3", //common
|
||||
|
@ -450,6 +453,7 @@ static const char* extension_list[] = {
|
|||
"snr",
|
||||
"sns",
|
||||
"snu",
|
||||
"snz", //txth/reserved [Killzone HD (PS3)]
|
||||
"sod",
|
||||
"son",
|
||||
"spd",
|
||||
|
@ -458,6 +462,7 @@ static const char* extension_list[] = {
|
|||
"spsd",
|
||||
"spw",
|
||||
"ss2",
|
||||
"ssd", //txth/reserved [Zack & Wiki (Wii)]
|
||||
"ssm",
|
||||
"sss",
|
||||
"ster",
|
||||
|
@ -514,6 +519,7 @@ static const char* extension_list[] = {
|
|||
"vgm", //txth/reserved [Maximo (PS2)]
|
||||
"vgs",
|
||||
"vgv",
|
||||
"vid",
|
||||
"vig",
|
||||
"vis",
|
||||
"vms",
|
||||
|
@ -608,6 +614,8 @@ static const char* common_extension_list[] = {
|
|||
"aiff", //common
|
||||
"bin", //common
|
||||
"flac", //common
|
||||
"m4a", //common
|
||||
"m4v", //common
|
||||
"mp+", //common [Moonshine Runners (PC)]
|
||||
"mp2", //common
|
||||
"mp3", //common
|
||||
|
@ -719,7 +727,6 @@ static const coding_info coding_info_list[] = {
|
|||
{coding_REF_IMA, "Reflections 4-bit IMA ADPCM"},
|
||||
{coding_AWC_IMA, "Rockstar AWC 4-bit IMA ADPCM"},
|
||||
{coding_UBI_IMA, "Ubisoft 4-bit IMA ADPCM"},
|
||||
|
||||
{coding_H4M_IMA, "Hudson HVQM4 4-bit IMA ADPCM"},
|
||||
|
||||
{coding_MSADPCM, "Microsoft 4-bit ADPCM"},
|
||||
|
@ -758,7 +765,7 @@ static const coding_info coding_info_list[] = {
|
|||
{coding_UBI_ADPCM, "Ubisoft 4/6-bit ADPCM"},
|
||||
|
||||
{coding_EA_MT, "Electronic Arts MicroTalk"},
|
||||
|
||||
{coding_CIRCUS_VQ, "Circus VQ"},
|
||||
{coding_RELIC, "Relic Codec"},
|
||||
{coding_CRI_HCA, "CRI HCA"},
|
||||
|
||||
|
@ -791,6 +798,9 @@ static const coding_info coding_info_list[] = {
|
|||
#ifdef VGM_USE_FFMPEG
|
||||
{coding_FFmpeg, "FFmpeg"},
|
||||
#endif
|
||||
#ifdef VGM_USE_FDKAAC
|
||||
{coding_MP4_AAC, "MPEG-4 AAC"},
|
||||
#endif
|
||||
};
|
||||
|
||||
static const layout_info layout_info_list[] = {
|
||||
|
@ -838,6 +848,8 @@ static const layout_info layout_info_list[] = {
|
|||
{layout_blocked_h4m, "blocked (H4M)"},
|
||||
{layout_blocked_xa_aiff, "blocked (XA AIFF)"},
|
||||
{layout_blocked_vs_square, "blocked (Square VS)"},
|
||||
{layout_blocked_vid1, "blocked (VID1)"},
|
||||
{layout_blocked_ubi_sce, "blocked (Ubi SCE)"},
|
||||
};
|
||||
|
||||
static const meta_info meta_info_list[] = {
|
||||
|
@ -1168,7 +1180,7 @@ static const meta_info meta_info_list[] = {
|
|||
{meta_VXN, "Gameloft VXN header"},
|
||||
{meta_EA_SNR_SNS, "Electronic Arts SNR+SNS header"},
|
||||
{meta_EA_SPS, "Electronic Arts SPS header"},
|
||||
{meta_NGC_VID1, "Neversoft VID1 header"},
|
||||
{meta_VID1, "Factor 5 VID1 header"},
|
||||
{meta_PC_FLX, "Ultima IX .FLX header"},
|
||||
{meta_MOGG, "Harmonix Music Systems MOGG Vorbis"},
|
||||
{meta_OGG_VORBIS, "Ogg Vorbis header"},
|
||||
|
@ -1272,6 +1284,7 @@ static const meta_info meta_info_list[] = {
|
|||
{meta_TGC, "Tiger Game.com .4 header"},
|
||||
{meta_KWB, "Koei Tecmo WaveBank header"},
|
||||
{meta_LRMD, "Sony LRMD header"},
|
||||
{meta_WWISE_FX, "Audiokinetic Wwise FX header"},
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -205,6 +205,12 @@ void block_update(off_t block_offset, VGMSTREAM * vgmstream) {
|
|||
case layout_blocked_vs_square:
|
||||
block_update_vs_square(block_offset,vgmstream);
|
||||
break;
|
||||
case layout_blocked_vid1:
|
||||
block_update_vid1(block_offset,vgmstream);
|
||||
break;
|
||||
case layout_blocked_ubi_sce:
|
||||
block_update_ubi_sce(block_offset,vgmstream);
|
||||
break;
|
||||
default: /* not a blocked layout */
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
#include "layout.h"
|
||||
#include "../vgmstream.h"
|
||||
|
||||
/* weird mix of Ubi ADPCM format with Ubi IMA, found in Splinter Cell Essentials (PSP) */
|
||||
void block_update_ubi_sce(off_t block_offset, VGMSTREAM* vgmstream) {
|
||||
STREAMFILE* sf = vgmstream->ch[0].streamfile;
|
||||
int i, channels;
|
||||
size_t header_size, frame_size, subframe_size, padding_size;
|
||||
|
||||
/* format mimics Ubi ADPCM's:
|
||||
* - 0x34/38 header with frame size (ex. 0x600), pre-read in meta
|
||||
* xN frames:
|
||||
* - 0x34 channel header per channel, with ADPCM config
|
||||
* - subframe (ex. 0x300) + padding byte
|
||||
* - subframe (ex. 0x300) + padding byte
|
||||
*
|
||||
* to skip the padding byte we'll detect subframes using codec_config as a counter
|
||||
* (higher bit has a special meaning)
|
||||
*/
|
||||
|
||||
vgmstream->codec_config |= 0x80; /* flag for decoder, ugly I know */
|
||||
|
||||
if ((vgmstream->codec_config & 1) == 0) {
|
||||
header_size = 0x34; /* read header in first subframe */
|
||||
}
|
||||
else {
|
||||
header_size = 0x00;
|
||||
}
|
||||
vgmstream->codec_config ^= 1; /* swap counter bit */
|
||||
|
||||
channels = vgmstream->channels;
|
||||
frame_size = vgmstream->full_block_size;
|
||||
subframe_size = frame_size / 2;
|
||||
padding_size = 0x01;
|
||||
|
||||
vgmstream->current_block_offset = block_offset;
|
||||
vgmstream->current_block_size = subframe_size;
|
||||
vgmstream->next_block_offset = block_offset + header_size * vgmstream->channels + subframe_size + padding_size;
|
||||
|
||||
for (i = 0; i < vgmstream->channels; i++) {
|
||||
vgmstream->ch[i].offset = block_offset + header_size * channels;
|
||||
|
||||
if (header_size > 0) {
|
||||
vgmstream->ch[i].adpcm_step_index = read_32bitLE(block_offset + header_size * i + 0x04, sf);
|
||||
vgmstream->ch[i].adpcm_history1_32 = read_32bitLE(block_offset + header_size * i + 0x08, sf);
|
||||
|
||||
/* TODO figure out
|
||||
* First step seems to always be a special value for the decoder, unsure of meaning.
|
||||
* 0 = too quiet and max = 88 = waveform starts a bit off and clicky. First hist is usually +-1,
|
||||
* other frames look, fine not sure what are they aiming for.
|
||||
*/
|
||||
if (vgmstream->ch[i].adpcm_step_index == 0x500) {
|
||||
vgmstream->ch[i].adpcm_step_index = 88;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
#include "layout.h"
|
||||
#include "../vgmstream.h"
|
||||
|
||||
/* blocks with video and audio */
|
||||
void block_update_vid1(off_t block_offset, VGMSTREAM* vgmstream) {
|
||||
STREAMFILE* sf = vgmstream->ch[0].streamfile;
|
||||
int ch;
|
||||
int channels = vgmstream->channels;
|
||||
uint32_t (*read_u32)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_u32be : read_u32le;
|
||||
size_t audd_size = 0, data_size = 0;
|
||||
|
||||
|
||||
if (read_u32(block_offset + 0x00, sf) != 0x4652414D) { /* "FRAM" */
|
||||
/* signal EOF, as files ends with padding */
|
||||
vgmstream->current_block_offset = block_offset;
|
||||
vgmstream->next_block_offset = block_offset;
|
||||
vgmstream->current_block_size = 0;
|
||||
vgmstream->current_block_samples = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
block_offset += 0x20;
|
||||
if (read_u32(block_offset + 0x00, sf) == 0x56494444) { /* "VIDD"*/
|
||||
block_offset += read_u32(block_offset + 0x04, sf);
|
||||
}
|
||||
|
||||
if (read_u32(block_offset + 0x00, sf) == 0x41554444) { /* "AUDD" */
|
||||
audd_size = read_u32(block_offset + 0x04, sf);
|
||||
data_size = read_u32(block_offset + 0x0c, sf);
|
||||
}
|
||||
|
||||
vgmstream->current_block_offset = block_offset;
|
||||
vgmstream->next_block_offset = block_offset + audd_size;
|
||||
vgmstream->current_block_size = (data_size / channels);
|
||||
vgmstream->current_block_samples = 0;
|
||||
|
||||
for (ch = 0; ch < vgmstream->channels; ch++) {
|
||||
off_t interleave, head_size;
|
||||
|
||||
switch(vgmstream->coding_type) {
|
||||
case coding_PCM16_int:
|
||||
interleave = 0x02 * ch;
|
||||
head_size = 0x10;
|
||||
break;
|
||||
case coding_XBOX_IMA:
|
||||
interleave = 0x00;
|
||||
head_size = 0x10;
|
||||
break;
|
||||
case coding_NGC_DSP:
|
||||
interleave = (data_size / channels) * ch;
|
||||
head_size = 0x20;
|
||||
break;
|
||||
default:
|
||||
interleave = 0;
|
||||
head_size = 0x10;
|
||||
break;
|
||||
}
|
||||
|
||||
vgmstream->ch[ch].offset = block_offset + head_size + interleave;
|
||||
}
|
||||
}
|
|
@ -46,6 +46,8 @@ void block_update_sthd(off_t block_offset, VGMSTREAM * vgmstream);
|
|||
void block_update_h4m(off_t block_offset, VGMSTREAM * vgmstream);
|
||||
void block_update_xa_aiff(off_t block_offset, VGMSTREAM * vgmstream);
|
||||
void block_update_vs_square(off_t block_offset, VGMSTREAM * vgmstream);
|
||||
void block_update_vid1(off_t block_offset, VGMSTREAM* vgmstream);
|
||||
void block_update_ubi_sce(off_t block_offset, VGMSTREAM* vgmstream);
|
||||
|
||||
/* other layouts */
|
||||
void render_vgmstream_interleave(sample_t * buffer, int32_t sample_count, VGMSTREAM * vgmstream);
|
||||
|
|
|
@ -1,299 +1,109 @@
|
|||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "../util.h"
|
||||
|
||||
/* .dsp w/ Cstr header, seen in Star Fox Assault and Donkey Konga */
|
||||
VGMSTREAM * init_vgmstream_cstr(STREAMFILE *streamFile) {
|
||||
|
||||
/* Cstr - from Namco NuSound v1 games [Mr. Driller (GC), Star Fox Assault (GC), Donkey Konga (GC)] */
|
||||
VGMSTREAM * init_vgmstream_cstr(STREAMFILE* sf) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
char filename[PATH_LIMIT];
|
||||
|
||||
int loop_flag;
|
||||
off_t start_offset;
|
||||
off_t first_data;
|
||||
off_t loop_offset;
|
||||
size_t interleave;
|
||||
int loop_adjust;
|
||||
int double_loop_end = 0;
|
||||
size_t interleave, first_skip;
|
||||
int loop_flag, channels, sample_rate;
|
||||
int loop_start, loop_end, num_samples, num_nibbles;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
streamFile->get_name(streamFile,filename,sizeof(filename));
|
||||
if (strcasecmp("dsp",filename_extension(filename))) goto fail;
|
||||
|
||||
/* check header */
|
||||
if ((uint32_t)read_32bitBE(0,streamFile)!=0x43737472) /* "Cstr" */
|
||||
goto fail;
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,"header ok\n");
|
||||
#endif
|
||||
|
||||
if (read_8bit(0x1b,streamFile)==1) {
|
||||
/* mono version, much simpler to handle */
|
||||
/* Only seen in R Racing Evolution radio sfx */
|
||||
|
||||
start_offset = 0x80;
|
||||
loop_flag = read_16bitBE(0x2c,streamFile);
|
||||
|
||||
/* check initial predictor/scale */
|
||||
if (read_16bitBE(0x5e,streamFile) != (uint8_t)read_8bit(start_offset,streamFile))
|
||||
goto fail;
|
||||
|
||||
/* check type==0 and gain==0 */
|
||||
if (read_16bitBE(0x2e,streamFile) || read_16bitBE(0x5c,streamFile))
|
||||
goto fail;
|
||||
|
||||
loop_offset = start_offset+read_32bitBE(0x10,streamFile);
|
||||
if (loop_flag) {
|
||||
if (read_16bitBE(0x64,streamFile) != (uint8_t)read_8bit(loop_offset,streamFile)) goto fail;
|
||||
}
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
|
||||
vgmstream = allocate_vgmstream(1,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
/* fill in the vital statistics */
|
||||
vgmstream->sample_rate = read_32bitBE(0x28,streamFile);
|
||||
vgmstream->num_samples = read_32bitBE(0x20,streamFile);
|
||||
|
||||
if (loop_flag) {
|
||||
vgmstream->loop_start_sample = dsp_nibbles_to_samples(
|
||||
read_32bitBE(0x30,streamFile));
|
||||
vgmstream->loop_end_sample = dsp_nibbles_to_samples(
|
||||
read_32bitBE(0x34,streamFile))+1;
|
||||
}
|
||||
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->meta_type = meta_DSP_CSTR;
|
||||
|
||||
{
|
||||
int i;
|
||||
for (i=0;i<16;i++)
|
||||
vgmstream->ch[0].adpcm_coef[i]=read_16bitBE(0x3c+i*2,streamFile);
|
||||
}
|
||||
|
||||
/* open the file for reading by each channel */
|
||||
vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
|
||||
if (!vgmstream->ch[0].streamfile) goto fail;
|
||||
|
||||
vgmstream->ch[0].channel_start_offset=
|
||||
vgmstream->ch[0].offset=
|
||||
start_offset;
|
||||
|
||||
return vgmstream;
|
||||
} /* end mono */
|
||||
|
||||
interleave = read_16bitBE(0x06,streamFile);
|
||||
start_offset = 0xe0;
|
||||
first_data = start_offset+read_32bitBE(0x0c,streamFile);
|
||||
loop_flag = read_16bitBE(0x2c,streamFile);
|
||||
|
||||
if (!loop_flag) {
|
||||
/* Nonlooped tracks seem to follow no discernable pattern
|
||||
* with where they actually start.
|
||||
* But! with the magic of initial p/s redundancy, we can guess.
|
||||
*/
|
||||
while (first_data<start_offset+0x800 &&
|
||||
(read_16bitBE(0x5e,streamFile) != (uint8_t)read_8bit(first_data,streamFile) ||
|
||||
read_16bitBE(0xbe,streamFile) != (uint8_t)read_8bit(first_data+interleave,streamFile)))
|
||||
first_data+=8;
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,"guessed first_data at %#x\n",first_data);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* check initial predictor/scale */
|
||||
if (read_16bitBE(0x5e,streamFile) != (uint8_t)read_8bit(first_data,streamFile))
|
||||
goto fail;
|
||||
if (read_16bitBE(0xbe,streamFile) != (uint8_t)read_8bit(first_data+interleave,streamFile))
|
||||
/* checks */
|
||||
if (!check_extensions(sf,"dsp"))
|
||||
goto fail;
|
||||
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,"p/s ok\n");
|
||||
#endif
|
||||
|
||||
/* check type==0 and gain==0 */
|
||||
if (read_16bitBE(0x2e,streamFile) || read_16bitBE(0x5c,streamFile))
|
||||
goto fail;
|
||||
if (read_16bitBE(0x8e,streamFile) || read_16bitBE(0xbc,streamFile))
|
||||
if (read_u32be(0x00,sf) != 0x43737472) /* "Cstr" */
|
||||
goto fail;
|
||||
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,"type & gain ok\n");
|
||||
#endif
|
||||
/* 0x04: version (0x0066 = 0.66 as seen in nus_config .txt) */
|
||||
interleave = read_u16be(0x06, sf);
|
||||
/* 0x08: config? (volume/pan/etc?) */
|
||||
first_skip = read_s32be(0x0c, sf); /* first interleaved block has normal size, but starts late */
|
||||
loop_start = read_s32be(0x10, sf);
|
||||
/* 0x14: num samples LE or null in mono files */
|
||||
/* 0x18: sample rate LE or null in mono files */
|
||||
/* 0x01a: always 0x40 */
|
||||
channels = read_u8(0x1b, sf); /* mono only seen in R:Racing Evolution radio sfx */
|
||||
/* 0x1c: always null */
|
||||
|
||||
/* check for loop flag agreement */
|
||||
if (read_16bitBE(0x2c,streamFile) != read_16bitBE(0x8c,streamFile))
|
||||
goto fail;
|
||||
/* next is DSP header, with some oddities:
|
||||
* - loop flag isn't always set vs Cstr's flag (won't have DSP loop_ps/etc)
|
||||
* - loop start nibbles can be wrong even with loop flag set
|
||||
* - wonky loop_ps as a result (other fields agree between channels) */
|
||||
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,"loop flags agree\n");
|
||||
#endif
|
||||
num_samples = read_s32be(0x20 + 0x00, sf);
|
||||
num_nibbles = read_s32be(0x20 + 0x04, sf);
|
||||
sample_rate = read_s32be(0x20 + 0x08, sf);
|
||||
//loop_flag = read_s16be(0x20 + 0x0c, sf);
|
||||
//loop_start = read_s32be(0x20 + 0x10, sf);
|
||||
loop_end = read_s32be(0x20 + 0x14, sf);
|
||||
|
||||
loop_offset = start_offset+read_32bitBE(0x10,streamFile)*2;
|
||||
if (loop_flag) {
|
||||
int loops_ok=0;
|
||||
/* check loop predictor/scale */
|
||||
/* some fuzz allowed */
|
||||
for (loop_adjust=0;loop_adjust>=-0x10;loop_adjust-=8) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,"looking for loop p/s at %#x,%#x\n",loop_offset-interleave+loop_adjust,loop_offset+loop_adjust);
|
||||
#endif
|
||||
if (read_16bitBE(0x64,streamFile) == (uint8_t)read_8bit(loop_offset-interleave+loop_adjust,streamFile) &&
|
||||
read_16bitBE(0xc4,streamFile) == (uint8_t)read_8bit(loop_offset+loop_adjust,streamFile)) {
|
||||
loops_ok=1;
|
||||
loop_flag = (loop_start >= 0);
|
||||
start_offset = 0x20 + 0x60 * channels + first_skip;
|
||||
|
||||
#if 1
|
||||
/* nonlooped tracks may not set first skip for no reason, but can be tested with initial p/s */
|
||||
if (!loop_flag && channels == 2 && first_skip == 0) {
|
||||
while (first_skip < 0x800) {
|
||||
if (read_u16be(0x20 + 0x3e, sf) == read_u8(start_offset + first_skip, sf) &&
|
||||
read_u16be(0x20 + 0x60 + 0x3e, sf) == read_u8(start_offset + first_skip + interleave, sf))
|
||||
break;
|
||||
}
|
||||
first_skip += 0x08;
|
||||
}
|
||||
if (!loops_ok)
|
||||
for (loop_adjust=interleave;loop_adjust<=interleave+0x10;loop_adjust+=8) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,"looking for loop p/s at %#x,%#x\n",loop_offset-interleave+loop_adjust,loop_offset+loop_adjust);
|
||||
/* not found */
|
||||
if (first_skip == 0x800)
|
||||
first_skip = 0;
|
||||
else
|
||||
start_offset += first_skip;
|
||||
}
|
||||
if (first_skip > 0 && loop_start >= (interleave - first_skip))
|
||||
loop_start = loop_start - (interleave - first_skip);
|
||||
#endif
|
||||
if (read_16bitBE(0x64,streamFile) == (uint8_t)read_8bit(loop_offset-interleave+loop_adjust,streamFile) &&
|
||||
read_16bitBE(0xc4,streamFile) == (uint8_t)read_8bit(loop_offset+loop_adjust,streamFile)) {
|
||||
loops_ok=1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
loop_start = loop_start * 2;
|
||||
|
||||
if (!loops_ok) goto fail;
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,"loop p/s ok (with %#4x adjust)\n",loop_adjust);
|
||||
#endif
|
||||
|
||||
/* check for agreement */
|
||||
/* loop end (channel 1 & 2 headers) */
|
||||
if (read_32bitBE(0x34,streamFile) != read_32bitBE(0x94,streamFile))
|
||||
goto fail;
|
||||
|
||||
/* Mr. Driller oddity */
|
||||
if (dsp_nibbles_to_samples(read_32bitBE(0x34,streamFile)*2)+1 <= read_32bitBE(0x20,streamFile)) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,"loop end <= half total samples, should be doubled\n");
|
||||
#endif
|
||||
double_loop_end = 1;
|
||||
}
|
||||
|
||||
/* loop start (Cstr header and channel 1 header) */
|
||||
if (read_32bitBE(0x30,streamFile) != read_32bitBE(0x10,streamFile)
|
||||
#if 0
|
||||
/* this particular glitch only true for SFA, though it
|
||||
* seems like something similar happens in Donkey Konga */
|
||||
/* loop start (Cstr, channel 1 & 2 headers) */
|
||||
|| (read_32bitBE(0x0c,streamFile)+read_32bitLE(0x30,streamFile)) !=
|
||||
read_32bitBE(0x90,streamFile)
|
||||
#endif
|
||||
)
|
||||
/* alternatively (Donkey Konga) the header loop is 0x0c+0x10 */
|
||||
if (
|
||||
/* loop start (Cstr header and channel 1 header) */
|
||||
read_32bitBE(0x30,streamFile) != read_32bitBE(0x10,streamFile)+
|
||||
read_32bitBE(0x0c,streamFile))
|
||||
/* further alternatively (Donkey Konga), if we loop back to
|
||||
* the very first frame 0x30 might be 0x00000002 (which
|
||||
* is a *valid* std dsp loop start, imagine that) while 0x10
|
||||
* is 0x00000000 */
|
||||
if (!(read_32bitBE(0x30,streamFile) == 2 &&
|
||||
read_32bitBE(0x10,streamFile) == 0))
|
||||
/* lest there be too few alternatives, in Mr. Driller we
|
||||
* find that [0x30] + [0x0c] + 8 = [0x10]*2 */
|
||||
if (!(double_loop_end &&
|
||||
read_32bitBE(0x30,streamFile) +
|
||||
read_32bitBE(0x0c,streamFile) + 8 ==
|
||||
read_32bitBE(0x10,streamFile)*2)) {
|
||||
/* for Mr.Driller Drill Land, no idea about the above but seems to play and loop ok */
|
||||
VGM_LOG("Cstr: bad loop check ignored\n");
|
||||
//goto fail;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,"loop points agree\n");
|
||||
#endif
|
||||
/* Mr. Driller oddity, unreliable loop flag */
|
||||
if (loop_end == num_nibbles) {
|
||||
loop_flag = 0;
|
||||
}
|
||||
|
||||
/* assure that sample counts, sample rates agree */
|
||||
if (
|
||||
/* sample count (channel 1 & 2 headers) */
|
||||
read_32bitBE(0x20,streamFile) != read_32bitBE(0x80,streamFile) ||
|
||||
/* sample rate (channel 1 & 2 headers) */
|
||||
read_32bitBE(0x28,streamFile) != read_32bitBE(0x88,streamFile) ||
|
||||
/* sample count (Cstr header and channel 1 header) */
|
||||
read_32bitLE(0x14,streamFile) != read_32bitBE(0x20,streamFile) ||
|
||||
/* sample rate (Cstr header and channel 1 header) */
|
||||
(uint16_t)read_16bitLE(0x18,streamFile) != read_32bitBE(0x28,streamFile))
|
||||
goto fail;
|
||||
/* Mr. Driller oddity, half nibbles */
|
||||
if (loop_end * 2 + 1 <= num_nibbles) {
|
||||
loop_end = loop_end * 2;
|
||||
}
|
||||
|
||||
|
||||
/* no loop_ps checks given how buggy the format is */
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
|
||||
vgmstream = allocate_vgmstream(2,loop_flag);
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
/* fill in the vital statistics */
|
||||
vgmstream->sample_rate = read_32bitBE(0x28,streamFile);
|
||||
/* This is a slight hack to counteract their hack.
|
||||
* All the data is ofset by first_data so that the loop
|
||||
* point occurs at a block boundary. However, I always begin decoding
|
||||
* right after the header, as that is the start of the first block and
|
||||
* my interleave code relies on starting at the beginning of a block.
|
||||
* So we decode a few silent samples at the beginning, and here we make up
|
||||
* for it by lengthening the track by that much.
|
||||
*/
|
||||
vgmstream->num_samples = read_32bitBE(0x20,streamFile) +
|
||||
(first_data-start_offset)/8*14;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = num_samples;
|
||||
vgmstream->loop_start_sample = dsp_bytes_to_samples(loop_start, channels);
|
||||
|
||||
if (loop_flag) {
|
||||
off_t loop_start_bytes = loop_offset-start_offset-interleave;
|
||||
vgmstream->loop_start_sample = dsp_nibbles_to_samples((loop_start_bytes/(2*interleave)*interleave+loop_start_bytes%(interleave*2))*2);
|
||||
/*dsp_nibbles_to_samples(loop_start_bytes);*/
|
||||
/*dsp_nibbles_to_samples(read_32bitBE(0x30,streamFile)*2-inter);*/
|
||||
vgmstream->loop_end_sample = dsp_nibbles_to_samples(
|
||||
read_32bitBE(0x34,streamFile))+1;
|
||||
|
||||
if (double_loop_end)
|
||||
vgmstream->loop_end_sample =
|
||||
dsp_nibbles_to_samples(read_32bitBE(0x34,streamFile)*2)+1;
|
||||
|
||||
if (vgmstream->loop_end_sample > vgmstream->num_samples) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,"loop_end_sample > num_samples, adjusting\n");
|
||||
#endif
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||
}
|
||||
}
|
||||
vgmstream->loop_end_sample = dsp_nibbles_to_samples(loop_end) + 1;
|
||||
/* Donkey Konga 3 oddity, loop/num nibbles not correct vs final samples */
|
||||
if (vgmstream->loop_end_sample > num_samples)
|
||||
vgmstream->loop_end_sample = num_samples;
|
||||
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = interleave;
|
||||
vgmstream->interleave_first_block_size = interleave - first_skip;
|
||||
vgmstream->interleave_first_skip = first_skip;
|
||||
vgmstream->meta_type = meta_DSP_CSTR;
|
||||
VGM_LOG("1=%x, =%x\n",vgmstream->interleave_first_block_size, vgmstream->interleave_first_skip);
|
||||
dsp_read_coefs_be(vgmstream, sf, 0x3c, 0x60);
|
||||
|
||||
{
|
||||
int i;
|
||||
for (i=0;i<16;i++)
|
||||
vgmstream->ch[0].adpcm_coef[i]=read_16bitBE(0x3c+i*2,streamFile);
|
||||
for (i=0;i<16;i++)
|
||||
vgmstream->ch[1].adpcm_coef[i]=read_16bitBE(0x9c+i*2,streamFile);
|
||||
}
|
||||
|
||||
/* open the file for reading by each channel */
|
||||
{
|
||||
int i;
|
||||
for (i=0;i<2;i++) {
|
||||
vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
|
||||
if (!vgmstream->ch[i].streamfile) goto fail;
|
||||
|
||||
vgmstream->ch[i].channel_start_offset=
|
||||
vgmstream->ch[i].offset=
|
||||
start_offset+interleave*i;
|
||||
}
|
||||
}
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
/* clean up anything we may have opened */
|
||||
fail:
|
||||
if (vgmstream) close_vgmstream(vgmstream);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -232,7 +232,10 @@ static const adxkey_info adxkey9_list[] = {
|
|||
{0x2378,0x5511,0x0201, NULL,5613126134333697}, // 0013F11BC5510101
|
||||
|
||||
/* Detective Conan Runner / Case Closed Runner (Android) */
|
||||
{0x0613,0x0e3d,0x6dff, NULL,1175268187653273344}, // 104f643098e3f700
|
||||
{0x0613,0x0e3d,0x6dff, NULL,1175268187653273344}, // 104f643098e3f700
|
||||
|
||||
/* Persona 5 Royal (PS4)*/
|
||||
{0x0000,0x1c85,0x1043, NULL,29902882}, // guessed with VGAudio
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -25,16 +25,15 @@ typedef struct {
|
|||
static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc);
|
||||
|
||||
|
||||
/* AWC - from RAGE (Rockstar Advanced Game Engine) audio (Red Dead Redemption, Max Payne 3, GTA5) */
|
||||
/* AWC - from RAGE (Rockstar Advanced Game Engine) audio [Red Dead Redemption, Max Payne 3, GTA5 (multi)] */
|
||||
VGMSTREAM * init_vgmstream_awc(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
awc_header awc = {0};
|
||||
|
||||
/* check extension */
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile,"awc"))
|
||||
goto fail;
|
||||
|
||||
/* check header */
|
||||
if (!parse_awc_header(streamFile, &awc))
|
||||
goto fail;
|
||||
|
||||
|
@ -51,7 +50,8 @@ VGMSTREAM * init_vgmstream_awc(STREAMFILE *streamFile) {
|
|||
|
||||
|
||||
switch(awc.codec) {
|
||||
case 0x01: /* PCM (PC/PS3) [sfx, rarely] */
|
||||
case 0x00: /* PCM (PC) sfx, very rare, lower sample rates? [Max Payne 3 (PC)] */
|
||||
case 0x01: /* PCM (PC/PS3) sfx, rarely */
|
||||
if (awc.is_music) goto fail; /* blocked_awc needs to be prepared */
|
||||
vgmstream->coding_type = awc.big_endian ? coding_PCM16BE : coding_PCM16LE;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
|
@ -294,17 +294,22 @@ static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc) {
|
|||
sample_rate = (uint16_t)read_16bit(offset + 0x0c + 0x10*ch + 0x0a,streamFile);
|
||||
codec = read_8bit(offset + 0x0c + 0x10*ch + 0x0c, streamFile);
|
||||
|
||||
/* validate as all channels should repeat this (when channels is even and > 2 seems
|
||||
* it's stereo pairs, and num_samples can vary slightly but no matter) */
|
||||
/* validate channels differences */
|
||||
if ((awc->num_samples && !(awc->num_samples >= num_samples - 10 && awc->num_samples <= num_samples + 10)) ||
|
||||
(awc->sample_rate && awc->sample_rate != sample_rate) ||
|
||||
(awc->codec && awc->codec != codec)) {
|
||||
VGM_LOG("AWC: found header diffs in channel %i, ns=%i vs %i, sr=%i vs %i, c=%i vs %i\n",
|
||||
ch, awc->num_samples, num_samples, awc->sample_rate, sample_rate, awc->codec, codec);
|
||||
//goto fail; //todo some Max Payne 3 cutscene channels have huge sample diffs
|
||||
(awc->sample_rate && awc->sample_rate != sample_rate)) {
|
||||
VGM_LOG("AWC: found header diffs in channel %i, ns=%i vs %i, sr=%i vs %i\n",
|
||||
ch, awc->num_samples, num_samples, awc->sample_rate, sample_rate);
|
||||
/* sometimes (often cutscenes in Max Payne 3 and RDR DLC) channels have bif sample diffs,
|
||||
* probably one stream is simply silent after its samples end */
|
||||
}
|
||||
|
||||
awc->num_samples = num_samples;
|
||||
if ((awc->codec && awc->codec != codec)) {
|
||||
VGM_LOG("AWC: found header diffs in channel %i, c=%i vs %i\n", ch, awc->codec, codec);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (awc->num_samples < num_samples) /* use biggest channel */
|
||||
awc->num_samples = num_samples;
|
||||
awc->sample_rate = sample_rate;
|
||||
awc->codec = codec;
|
||||
}
|
||||
|
|
|
@ -1,245 +1,114 @@
|
|||
#ifndef _AWC_XMA_STREAMFILE_H_
|
||||
#define _AWC_XMA_STREAMFILE_H_
|
||||
#include "../streamfile.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
/* config */
|
||||
int channel;
|
||||
int channel_count;
|
||||
size_t block_size;
|
||||
off_t stream_offset;
|
||||
size_t stream_size;
|
||||
|
||||
/* state */
|
||||
off_t logical_offset; /* offset that corresponds to physical_offset */
|
||||
off_t physical_offset; /* actual file offset */
|
||||
|
||||
size_t skip_size; /* size to skip from a block start to reach data start */
|
||||
size_t data_size; /* logical size of the block */
|
||||
|
||||
size_t logical_size;
|
||||
} awc_xma_io_data;
|
||||
|
||||
|
||||
static size_t get_block_header_size(STREAMFILE *streamFile, off_t offset, awc_xma_io_data *data);
|
||||
static size_t get_repeated_data_size(STREAMFILE *streamFile, off_t next_offset, size_t repeat_samples);
|
||||
static size_t get_block_skip_count(STREAMFILE *streamFile, off_t offset, int channel);
|
||||
|
||||
/* Reads plain XMA data of a single stream. Each block has a header and channels have different num_samples/frames.
|
||||
* Channel data is separate within the block (first all frames of ch0, then ch1, etc), padded, and sometimes
|
||||
* the last few frames of a channel are repeated in the new block (marked with the "discard samples" field). */
|
||||
static size_t awc_xma_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, awc_xma_io_data* data) {
|
||||
size_t total_read = 0;
|
||||
size_t frame_size = 0x800;
|
||||
|
||||
/* ignore bad reads */
|
||||
if (offset < 0 || offset > data->logical_size) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* previous offset: re-start as we can't map logical<>physical offsets
|
||||
* (kinda slow as it trashes buffers, but shouldn't happen often) */
|
||||
if (offset < data->logical_offset) {
|
||||
data->logical_offset = 0x00;
|
||||
data->physical_offset = data->stream_offset;
|
||||
data->data_size = 0;
|
||||
}
|
||||
|
||||
/* read blocks, one at a time */
|
||||
while (length > 0) {
|
||||
|
||||
/* ignore EOF */
|
||||
if (data->logical_offset >= data->logical_size) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* process new block */
|
||||
if (data->data_size == 0) {
|
||||
size_t header_size = get_block_header_size(streamfile, data->physical_offset, data);
|
||||
/* header table entries = frames... I hope */
|
||||
size_t others_size = get_block_skip_count(streamfile, data->physical_offset, data->channel) * frame_size;
|
||||
//size_t skip_size = read_32bitBE(data->physical_offset + 0x10*data->channel + 0x00, streamfile) * frame_size;
|
||||
size_t data_size = read_32bitBE(data->physical_offset + 0x10*data->channel + 0x04, streamfile) * frame_size;
|
||||
size_t repeat_samples = read_32bitBE(data->physical_offset + 0x10*data->channel + 0x08, streamfile);
|
||||
size_t repeat_size = 0;
|
||||
|
||||
|
||||
/* if there are repeat samples current block repeats some frames from last block, find out size */
|
||||
if (repeat_samples) {
|
||||
off_t data_offset = data->physical_offset + header_size + others_size;
|
||||
repeat_size = get_repeated_data_size(streamfile, data_offset, repeat_samples);
|
||||
}
|
||||
|
||||
data->skip_size = header_size + others_size + repeat_size;
|
||||
data->data_size = data_size - repeat_size;
|
||||
}
|
||||
|
||||
/* move to next block */
|
||||
if (offset >= data->logical_offset + data->data_size) {
|
||||
data->physical_offset += data->block_size;
|
||||
data->logical_offset += data->data_size;
|
||||
data->data_size = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* read data */
|
||||
{
|
||||
size_t bytes_consumed, bytes_done, to_read;
|
||||
|
||||
bytes_consumed = offset - data->logical_offset;
|
||||
|
||||
to_read = data->data_size - bytes_consumed;
|
||||
if (to_read > length)
|
||||
to_read = length;
|
||||
bytes_done = read_streamfile(dest, data->physical_offset + data->skip_size + bytes_consumed, to_read, streamfile);
|
||||
|
||||
offset += bytes_done;
|
||||
total_read += bytes_done;
|
||||
length -= bytes_done;
|
||||
dest += bytes_done;
|
||||
|
||||
if (bytes_done != to_read || bytes_done == 0) {
|
||||
break; /* error/EOF */
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return total_read;
|
||||
}
|
||||
|
||||
static size_t awc_xma_io_size(STREAMFILE *streamfile, awc_xma_io_data* data) {
|
||||
off_t physical_offset, max_physical_offset;
|
||||
size_t frame_size = 0x800;
|
||||
size_t logical_size = 0;
|
||||
|
||||
if (data->logical_size)
|
||||
return data->logical_size;
|
||||
|
||||
physical_offset = data->stream_offset;
|
||||
max_physical_offset = data->stream_offset + data->stream_size;
|
||||
|
||||
/* get size of the logical stream */
|
||||
while (physical_offset < max_physical_offset) {
|
||||
size_t header_size = get_block_header_size(streamfile, physical_offset, data);
|
||||
/* header table entries = frames... I hope */
|
||||
size_t skip_size = get_block_skip_count(streamfile, physical_offset, data->channel) * frame_size;
|
||||
//size_t skip_size = read_32bitBE(physical_offset + 0x10*data->channel + 0x00, streamfile) * frame_size;
|
||||
size_t data_size = read_32bitBE(physical_offset + 0x10*data->channel + 0x04, streamfile) * frame_size;
|
||||
size_t repeat_samples = read_32bitBE(physical_offset + 0x10*data->channel + 0x08, streamfile);
|
||||
size_t repeat_size = 0;
|
||||
|
||||
/* if there are repeat samples current block repeats some frames from last block, find out size */
|
||||
if (repeat_samples) {
|
||||
off_t data_offset = physical_offset + header_size + skip_size;
|
||||
repeat_size = get_repeated_data_size(streamfile, data_offset, repeat_samples);
|
||||
}
|
||||
|
||||
logical_size += data_size - repeat_size;
|
||||
physical_offset += data->block_size;
|
||||
}
|
||||
|
||||
data->logical_size = logical_size;
|
||||
return data->logical_size;
|
||||
}
|
||||
|
||||
|
||||
/* Prepares custom IO for AWC XMA, which is interleaved XMA in AWC blocks */
|
||||
static STREAMFILE* setup_awc_xma_streamfile(STREAMFILE *streamFile, off_t stream_offset, size_t stream_size, size_t block_size, int channel_count, int channel) {
|
||||
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
|
||||
awc_xma_io_data io_data = {0};
|
||||
size_t io_data_size = sizeof(awc_xma_io_data);
|
||||
|
||||
io_data.channel = channel;
|
||||
io_data.channel_count = channel_count;
|
||||
io_data.stream_offset = stream_offset;
|
||||
io_data.stream_size = stream_size;
|
||||
io_data.block_size = block_size;
|
||||
io_data.physical_offset = stream_offset;
|
||||
io_data.logical_size = awc_xma_io_size(streamFile, &io_data); /* force init */
|
||||
|
||||
if (io_data.logical_size > io_data.stream_size) {
|
||||
VGM_LOG("AWC XMA: wrong logical size\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* setup subfile */
|
||||
new_streamFile = open_wrap_streamfile(streamFile);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, awc_xma_io_read,awc_xma_io_size);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
new_streamFile = open_buffer_streamfile(new_streamFile,0);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
return temp_streamFile;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_streamFile);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* block header size, aligned/padded to 0x800 */
|
||||
static size_t get_block_header_size(STREAMFILE *streamFile, off_t offset, awc_xma_io_data *data) {
|
||||
size_t header_size = 0;
|
||||
int i;
|
||||
int entries = data->channel_count;
|
||||
|
||||
for (i = 0; i < entries; i++) {
|
||||
header_size += 0x10;
|
||||
header_size += read_32bitBE(offset + 0x10*i + 0x04, streamFile) * 0x04; /* entries in the table */
|
||||
}
|
||||
|
||||
if (header_size % 0x800) /* padded */
|
||||
header_size += 0x800 - (header_size % 0x800);
|
||||
|
||||
return header_size;
|
||||
}
|
||||
|
||||
|
||||
/* find data that repeats in the beginning of a new block at the end of last block */
|
||||
static size_t get_repeated_data_size(STREAMFILE *streamFile, off_t next_offset, size_t repeat_samples) {
|
||||
const size_t frame_size = 0x800;
|
||||
const size_t samples_per_subframe = 512;
|
||||
size_t samples_this_frame;
|
||||
uint8_t subframes;
|
||||
|
||||
//todo: fix this
|
||||
/* Repeat samples are the number of decoded samples to discard, but in this streamfile we can't do that.
|
||||
* Also XMA is VBR, and may encode silent frames with up to 63 subframes yet we may have few repeat samples.
|
||||
* We could find out how many subframes of 512 samples to skip, then adjust the XMA frame header, though
|
||||
* output will be slightly off since subframes are related.
|
||||
*
|
||||
* For now just skip a full frame depending on the number of subframes vs repeat samples.
|
||||
* Most files work ok-ish but channels may desync slightly. */
|
||||
|
||||
subframes = ((uint8_t)read_8bit(next_offset,streamFile) >> 2) & 0x3F; /* peek into frame header */
|
||||
samples_this_frame = subframes*samples_per_subframe;
|
||||
if (repeat_samples >= (int)(samples_this_frame*0.13)) { /* skip mosts */
|
||||
return frame_size;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* header has a skip value, but somehow it's sometimes bigger than expected (WHY!?!?) so just sum all */
|
||||
static size_t get_block_skip_count(STREAMFILE *streamFile, off_t offset, int channel) {
|
||||
size_t skip_count = 0;
|
||||
int i;
|
||||
|
||||
//skip_size = read_32bitBE(offset + 0x10*channel + 0x00, streamFile); /* wrong! */
|
||||
for (i = 0; i < channel; i++) {
|
||||
skip_count += read_32bitBE(offset + 0x10*i + 0x04, streamFile); /* number of frames of this channel */
|
||||
}
|
||||
|
||||
return skip_count;
|
||||
}
|
||||
|
||||
|
||||
#endif /* _AWC_XMA_STREAMFILE_H_ */
|
||||
#ifndef _AWC_XMA_STREAMFILE_H_
|
||||
#define _AWC_XMA_STREAMFILE_H_
|
||||
#include "deblock_streamfile.h"
|
||||
|
||||
|
||||
static size_t get_block_header_size(STREAMFILE* sf, off_t offset, int channels);
|
||||
static size_t get_repeated_data_size(STREAMFILE* sf, off_t next_offset, size_t repeat_samples);
|
||||
static size_t get_block_skip_count(STREAMFILE* sf, off_t offset, int channel);
|
||||
|
||||
static void block_callback(STREAMFILE *sf, deblock_io_data* data) {
|
||||
const size_t frame_size = 0x800;
|
||||
int channel = data->cfg.track_number;
|
||||
int channels = data->cfg.track_count;
|
||||
|
||||
/* Blocks have a header then data per channel, each with a different num_samples/frames,
|
||||
* separate (first all frames of ch0, then ch1, etc), padded, and sometimes the last few
|
||||
* frames of a channel are repeated in the new block (marked with "repeat samples"). */
|
||||
size_t header_size = get_block_header_size(sf, data->physical_offset, channels);
|
||||
/* header table entries = frames... I hope */
|
||||
size_t others_size = get_block_skip_count(sf, data->physical_offset, channel) * frame_size;
|
||||
//size_t skip_size = read_u32be(data->physical_offset + 0x10*channel + 0x00, sf) * frame_size;
|
||||
size_t data_size = read_u32be(data->physical_offset + 0x10*channel + 0x04, sf) * frame_size;
|
||||
size_t repeat_samples = read_u32be(data->physical_offset + 0x10*channel + 0x08, sf);
|
||||
size_t repeat_size = 0;
|
||||
|
||||
data->block_size = data->cfg.chunk_size;
|
||||
|
||||
/* if there are repeat samples current block repeats some frames from last block, find out size */
|
||||
if (repeat_samples) {
|
||||
off_t data_offset = data->physical_offset + header_size + others_size;
|
||||
repeat_size = get_repeated_data_size(sf, data_offset, repeat_samples);
|
||||
}
|
||||
|
||||
data->skip_size = header_size + others_size + repeat_size;
|
||||
data->data_size = data_size - repeat_size;
|
||||
}
|
||||
|
||||
/* block header size, aligned/padded to 0x800 */
|
||||
static size_t get_block_header_size(STREAMFILE* sf, off_t offset, int channels) {
|
||||
size_t header_size = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < channels; i++) {
|
||||
header_size += 0x10;
|
||||
header_size += read_u32be(offset + 0x10*i + 0x04, sf) * 0x04; /* entries in the table */
|
||||
}
|
||||
|
||||
if (header_size % 0x800) /* padded */
|
||||
header_size += 0x800 - (header_size % 0x800);
|
||||
|
||||
return header_size;
|
||||
}
|
||||
|
||||
/* find data that repeats in the beginning of a new block at the end of last block */
|
||||
static size_t get_repeated_data_size(STREAMFILE* sf, off_t next_offset, size_t repeat_samples) {
|
||||
const size_t frame_size = 0x800;
|
||||
const size_t samples_per_subframe = 512;
|
||||
size_t samples_this_frame;
|
||||
uint8_t subframes;
|
||||
|
||||
//todo: fix this
|
||||
/* Repeat samples are the number of decoded samples to discard, but in this streamfile we can't do that.
|
||||
* Also XMA is VBR, and may encode silent frames with up to 63 subframes yet we may have few repeat samples.
|
||||
* We could find out how many subframes of 512 samples to skip, then adjust the XMA frame header, though
|
||||
* output will be slightly off since subframes are related.
|
||||
*
|
||||
* For now just skip a full frame depending on the number of subframes vs repeat samples.
|
||||
* Most files work ok-ish but channels may desync slightly. */
|
||||
|
||||
subframes = ((uint8_t)read_8bit(next_offset,sf) >> 2) & 0x3F; /* peek into frame header */
|
||||
samples_this_frame = subframes*samples_per_subframe;
|
||||
if (repeat_samples >= (int)(samples_this_frame*0.13)) { /* skip mosts */
|
||||
return frame_size;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* header has a skip value, but somehow it's sometimes bigger than expected (WHY!?!?) so just sum all */
|
||||
static size_t get_block_skip_count(STREAMFILE* sf, off_t offset, int channel) {
|
||||
size_t skip_count = 0;
|
||||
int i;
|
||||
|
||||
//skip_size = read_u32be(offset + 0x10*channel + 0x00, sf); /* wrong! */
|
||||
for (i = 0; i < channel; i++) {
|
||||
skip_count += read_u32be(offset + 0x10*i + 0x04, sf); /* number of frames of this channel */
|
||||
}
|
||||
|
||||
return skip_count;
|
||||
}
|
||||
|
||||
|
||||
/* Deblocks interleaved XMA in AWC blocks */
|
||||
static STREAMFILE* setup_awc_xma_streamfile(STREAMFILE *sf, off_t stream_offset, size_t stream_size, size_t block_size, int channel_count, int channel) {
|
||||
STREAMFILE *new_sf = NULL;
|
||||
deblock_config_t cfg = {0};
|
||||
|
||||
cfg.track_number = channel;
|
||||
cfg.track_count = channel_count;
|
||||
cfg.stream_start = stream_offset;
|
||||
cfg.stream_size = stream_size;
|
||||
cfg.chunk_size = block_size;
|
||||
//cfg.physical_offset = stream_offset;
|
||||
//cfg.logical_size = awc_xma_io_size(sf, &cfg); /* force init */
|
||||
cfg.block_callback = block_callback;
|
||||
|
||||
new_sf = open_wrap_streamfile(sf);
|
||||
new_sf = open_io_deblock_streamfile_f(new_sf, &cfg);
|
||||
//new_sf = open_buffer_streamfile_f(new_sf, 0);
|
||||
return new_sf;
|
||||
}
|
||||
|
||||
#endif /* _AWC_XMA_STREAMFILE_H_ */
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
/* BKHD - Wwise soundbank container */
|
||||
VGMSTREAM* init_vgmstream_bkhd(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE* temp_sf = NULL;
|
||||
off_t subfile_offset, didx_offset, data_offset, offset;
|
||||
size_t subfile_size, didx_size;
|
||||
uint32_t subfile_id;
|
||||
int big_endian;
|
||||
uint32_t (*read_u32)(off_t,STREAMFILE*);
|
||||
int total_subsongs, target_subsong = sf->stream_index;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf,"bnk"))
|
||||
goto fail;
|
||||
if (read_u32be(0x00, sf) != 0x424B4844) /* "BKHD" */
|
||||
goto fail;
|
||||
big_endian = guess_endianness32bit(0x04, sf);
|
||||
read_u32 = big_endian ? read_u32be : read_u32le;
|
||||
|
||||
/* Wwise banks have event/track/sequence/etc info in the HIRC chunk, as well
|
||||
* as other chunks, and may have a DIDX index to memory .wem in DATA.
|
||||
* We support the internal .wem mainly for quick tests, as the HIRC is
|
||||
* complex and better handled with TXTP (some info from Nicknine's script) */
|
||||
|
||||
/* unlike RIFF first chunk follows chunk rules */
|
||||
if (!find_chunk(sf, 0x44494458, 0x00,0, &didx_offset, &didx_size, big_endian, 0)) /* "DIDX" */
|
||||
goto fail;
|
||||
if (!find_chunk(sf, 0x44415441, 0x00,0, &data_offset, NULL, big_endian, 0)) /* "DATA" */
|
||||
goto fail;
|
||||
|
||||
total_subsongs = didx_size / 0x0c;
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
|
||||
|
||||
offset = didx_offset + (target_subsong - 1) * 0x0c;
|
||||
subfile_id = read_u32(offset + 0x00, sf);
|
||||
subfile_offset = read_u32(offset + 0x04, sf) + data_offset;
|
||||
subfile_size = read_u32(offset + 0x08, sf);
|
||||
|
||||
//;VGM_LOG("BKHD: %lx, %x\n", subfile_offset, subfile_size);
|
||||
temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, "wem");
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
subfile_id = read_u32(0x00, temp_sf);
|
||||
if (subfile_id == 0x52494646 || subfile_id == 0x52494658) { /* "RIFF" / "RIFX" */
|
||||
vgmstream = init_vgmstream_wwise(temp_sf);
|
||||
if (!vgmstream) goto fail;
|
||||
}
|
||||
else {
|
||||
vgmstream = init_vgmstream_bkhd_fx(temp_sf);
|
||||
if (!vgmstream) goto fail;
|
||||
}
|
||||
|
||||
vgmstream->num_streams = total_subsongs;
|
||||
snprintf(vgmstream->stream_name, STREAM_NAME_SIZE, "%u", subfile_id);
|
||||
|
||||
close_streamfile(temp_sf);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_sf);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* BKHD mini format, probably from a generator plugin [Borderlands 2 (X360)] */
|
||||
VGMSTREAM* init_vgmstream_bkhd_fx(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset, data_size;
|
||||
int big_endian, loop_flag, channels, sample_rate, entries;
|
||||
uint32_t (*read_u32)(off_t,STREAMFILE*);
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf,"wem,bnk")) /* assumed */
|
||||
goto fail;
|
||||
big_endian = guess_endianness32bit(0x00, sf);
|
||||
read_u32 = big_endian ? read_u32be : read_u32le;
|
||||
|
||||
if (read_u32(0x00, sf) != 0x0400) /* codec? */
|
||||
goto fail;
|
||||
if (read_u32(0x04, sf) != 0x0800) /* codec? */
|
||||
goto fail;
|
||||
sample_rate = read_u32(0x08, sf);
|
||||
channels = read_u32(0x0c, sf);
|
||||
/* 0x10: some id or small size? */
|
||||
/* 0x14/18: some float? */
|
||||
entries = read_u32(0x1c, sf);
|
||||
/* 0x20 data size / 0x10 */
|
||||
if (read_u8(0x24, sf) != 4) /* bps */
|
||||
goto fail;
|
||||
/* 0x30: unknown table of 16b that goes up and down */
|
||||
|
||||
start_offset = 0x30 + align_size_to_block(entries * 0x02, 0x10);
|
||||
data_size = get_streamfile_size(sf) - start_offset;
|
||||
loop_flag = 0;
|
||||
|
||||
/* output sounds a bit funny, maybe not an actual stream but parts using the table */
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_WWISE_FX;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
|
||||
vgmstream->coding_type = coding_PCMFLOAT;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->codec_endian = big_endian;
|
||||
vgmstream->interleave_block_size = 0x4;
|
||||
|
||||
vgmstream->num_samples = pcm_bytes_to_samples(data_size, channels, 32);
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,sf,start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
|
@ -4,8 +4,8 @@
|
|||
typedef enum { PSX, PCM16, ATRAC9, HEVAG } bnk_codec;
|
||||
|
||||
/* .BNK - Sony's Scream Tool bank format [Puyo Puyo Tetris (PS4), NekoBuro: Cats Block (Vita)] */
|
||||
VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset, stream_offset, name_offset = 0;
|
||||
size_t stream_size, interleave = 0;
|
||||
off_t sblk_offset, data_offset;
|
||||
|
@ -15,7 +15,7 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
|
|||
uint32_t pitch, flags;
|
||||
uint32_t atrac9_info = 0;
|
||||
|
||||
int total_subsongs, target_subsong = streamFile->stream_index;
|
||||
int total_subsongs, target_subsong = sf->stream_index;
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
|
||||
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
|
||||
float (*read_f32)(off_t,STREAMFILE*) = NULL;
|
||||
|
@ -23,17 +23,17 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
|
|||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile, "bnk"))
|
||||
if (!check_extensions(sf, "bnk"))
|
||||
goto fail;
|
||||
|
||||
/* bnk version */
|
||||
if (read_32bitBE(0x00,streamFile) == 0x00000003) { /* PS3 */
|
||||
if (read_32bitBE(0x00,sf) == 0x00000003) { /* PS3 */
|
||||
read_32bit = read_32bitBE;
|
||||
read_16bit = read_16bitBE;
|
||||
read_f32 = read_f32be;
|
||||
big_endian = 1;
|
||||
}
|
||||
else if (read_32bitBE(0x00,streamFile) == 0x03000000) { /* PS2/Vita/PS4 */
|
||||
else if (read_32bitBE(0x00,sf) == 0x03000000) { /* PS2/Vita/PS4 */
|
||||
read_32bit = read_32bitLE;
|
||||
read_16bit = read_16bitLE;
|
||||
read_f32 = read_f32le;
|
||||
|
@ -43,13 +43,13 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
|
|||
goto fail;
|
||||
}
|
||||
|
||||
parts = read_32bit(0x04,streamFile);
|
||||
parts = read_32bit(0x04,sf);
|
||||
if (parts < 2 || parts > 3) goto fail;
|
||||
|
||||
sblk_offset = read_32bit(0x08,streamFile);
|
||||
sblk_offset = read_32bit(0x08,sf);
|
||||
/* 0x0c: sklb size */
|
||||
data_offset = read_32bit(0x10,streamFile);
|
||||
data_size = read_32bit(0x14,streamFile);
|
||||
data_offset = read_32bit(0x10,sf);
|
||||
data_size = read_32bit(0x14,sf);
|
||||
/* when sblk_offset >= 0x20: */
|
||||
/* 0x18: ZLSD small footer, rare [Yakuza 6's Puyo Puyo (PS4)] */
|
||||
/* 0x1c: ZLSD size */
|
||||
|
@ -60,9 +60,9 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
|
|||
|
||||
|
||||
/* SBlk part: parse header */
|
||||
if (read_32bit(sblk_offset+0x00,streamFile) != 0x6B6C4253) /* "klBS" (SBlk = sample block?) */
|
||||
if (read_32bit(sblk_offset+0x00,sf) != 0x6B6C4253) /* "klBS" (SBlk = sample block?) */
|
||||
goto fail;
|
||||
sblk_version = read_32bit(sblk_offset+0x04,streamFile);
|
||||
sblk_version = read_32bit(sblk_offset+0x04,sf);
|
||||
/* 0x08: possibly when sblk_version=0x0d, 0x03=Vita, 0x06=PS4 */
|
||||
//;VGM_LOG("BNK: sblk_offset=%lx, data_offset=%lx, sblk_version %x\n", sblk_offset, data_offset, sblk_version);
|
||||
|
||||
|
@ -79,11 +79,11 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
|
|||
|
||||
switch(sblk_version) {
|
||||
case 0x01: /* Ratchet & Clank (PS2) */
|
||||
section_entries = (uint16_t)read_16bit(sblk_offset+0x16,streamFile); /* entry size: ~0x0c */
|
||||
material_entries = (uint16_t)read_16bit(sblk_offset+0x18,streamFile); /* entry size: ~0x28 */
|
||||
stream_entries = (uint16_t)read_16bit(sblk_offset+0x1a,streamFile); /* entry size: none (count) */
|
||||
table1_offset = sblk_offset + read_32bit(sblk_offset+0x1c,streamFile);
|
||||
table2_offset = sblk_offset + read_32bit(sblk_offset+0x20,streamFile);
|
||||
section_entries = (uint16_t)read_16bit(sblk_offset+0x16,sf); /* entry size: ~0x0c */
|
||||
material_entries = (uint16_t)read_16bit(sblk_offset+0x18,sf); /* entry size: ~0x28 */
|
||||
stream_entries = (uint16_t)read_16bit(sblk_offset+0x1a,sf); /* entry size: none (count) */
|
||||
table1_offset = sblk_offset + read_32bit(sblk_offset+0x1c,sf);
|
||||
table2_offset = sblk_offset + read_32bit(sblk_offset+0x20,sf);
|
||||
table3_offset = table2_offset; /* mixed table in this version */
|
||||
table4_offset = 0; /* not included */
|
||||
|
||||
|
@ -92,16 +92,17 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
|
|||
table2_suboffset = 0;
|
||||
break;
|
||||
|
||||
case 0x03: /* Yu-Gi-Oh! GX - The Beginning of Destiny (PS2) */
|
||||
case 0x04: /* Test banks */
|
||||
case 0x05: /* Ratchet & Clank (PS3) */
|
||||
case 0x09: /* Puyo Puyo Tetris (PS4) */
|
||||
section_entries = (uint16_t)read_16bit(sblk_offset+0x16,streamFile); /* entry size: ~0x0c */
|
||||
material_entries = (uint16_t)read_16bit(sblk_offset+0x18,streamFile); /* entry size: ~0x08 */
|
||||
stream_entries = (uint16_t)read_16bit(sblk_offset+0x1a,streamFile); /* entry size: ~0x18 + variable */
|
||||
table1_offset = sblk_offset + read_32bit(sblk_offset+0x1c,streamFile);
|
||||
table2_offset = sblk_offset + read_32bit(sblk_offset+0x20,streamFile);
|
||||
table3_offset = sblk_offset + read_32bit(sblk_offset+0x34,streamFile);
|
||||
table4_offset = sblk_offset + read_32bit(sblk_offset+0x38,streamFile);
|
||||
section_entries = (uint16_t)read_16bit(sblk_offset+0x16,sf); /* entry size: ~0x0c */
|
||||
material_entries = (uint16_t)read_16bit(sblk_offset+0x18,sf); /* entry size: ~0x08 */
|
||||
stream_entries = (uint16_t)read_16bit(sblk_offset+0x1a,sf); /* entry size: ~0x18 + variable */
|
||||
table1_offset = sblk_offset + read_32bit(sblk_offset+0x1c,sf);
|
||||
table2_offset = sblk_offset + read_32bit(sblk_offset+0x20,sf);
|
||||
table3_offset = sblk_offset + read_32bit(sblk_offset+0x34,sf);
|
||||
table4_offset = sblk_offset + read_32bit(sblk_offset+0x38,sf);
|
||||
|
||||
table1_entry_size = 0x0c;
|
||||
table1_suboffset = 0x08;
|
||||
|
@ -110,13 +111,13 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
|
|||
|
||||
case 0x0d: /* Polara (Vita), Crypt of the Necrodancer (Vita) */
|
||||
case 0x0e: /* Yakuza 6's Puyo Puyo (PS4) */
|
||||
table1_offset = sblk_offset + read_32bit(sblk_offset+0x18,streamFile);
|
||||
table2_offset = sblk_offset + read_32bit(sblk_offset+0x1c,streamFile);
|
||||
table3_offset = sblk_offset + read_32bit(sblk_offset+0x2c,streamFile);
|
||||
table4_offset = sblk_offset + read_32bit(sblk_offset+0x30,streamFile);
|
||||
section_entries = (uint16_t)read_16bit(sblk_offset+0x38,streamFile); /* entry size: ~0x24 */
|
||||
material_entries = (uint16_t)read_16bit(sblk_offset+0x3a,streamFile); /* entry size: ~0x08 */
|
||||
stream_entries = (uint16_t)read_16bit(sblk_offset+0x3c,streamFile); /* entry size: ~0x5c + variable */
|
||||
table1_offset = sblk_offset + read_32bit(sblk_offset+0x18,sf);
|
||||
table2_offset = sblk_offset + read_32bit(sblk_offset+0x1c,sf);
|
||||
table3_offset = sblk_offset + read_32bit(sblk_offset+0x2c,sf);
|
||||
table4_offset = sblk_offset + read_32bit(sblk_offset+0x30,sf);
|
||||
section_entries = (uint16_t)read_16bit(sblk_offset+0x38,sf); /* entry size: ~0x24 */
|
||||
material_entries = (uint16_t)read_16bit(sblk_offset+0x3a,sf); /* entry size: ~0x08 */
|
||||
stream_entries = (uint16_t)read_16bit(sblk_offset+0x3c,sf); /* entry size: ~0x5c + variable */
|
||||
|
||||
table1_entry_size = 0x24;
|
||||
table1_suboffset = 0x0c;
|
||||
|
@ -151,19 +152,34 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
|
|||
case 0x01:
|
||||
/* table2/3 has size 0x28 entries, seemingly:
|
||||
* 0x00: subtype(01=sound)
|
||||
* 0x0A: pitch
|
||||
* 0x08: same as other versions (pitch, flags, offset...)
|
||||
* rest: padding
|
||||
* 0x18: stream offset
|
||||
* there is no stream size so we'd need some sample counting
|
||||
* many sounds repeat entries for no apparent reason
|
||||
* there is no stream size like in v0x03
|
||||
*/
|
||||
|
||||
goto fail;
|
||||
for (i = 0; i < material_entries; i++) {
|
||||
uint8_t table2_type = read_32bit(table2_offset + (i*0x28) + 0x00, sf);
|
||||
|
||||
if (table2_type != 0x01)
|
||||
continue;
|
||||
|
||||
total_subsongs++;
|
||||
if (total_subsongs == target_subsong) {
|
||||
table2_entry_offset = 0;
|
||||
table3_entry_offset = (i*0x28) + 0x08;
|
||||
/* continue to count all subsongs*/
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
for (i = 0; i < material_entries; i++) {
|
||||
uint32_t table2_value, table2_subinfo, table2_subtype;
|
||||
|
||||
table2_value = (uint32_t)read_32bit(table2_offset+(i*0x08)+table2_suboffset+0x00,streamFile);
|
||||
table2_value = (uint32_t)read_32bit(table2_offset+(i*0x08)+table2_suboffset+0x00,sf);
|
||||
table2_subinfo = (table2_value >> 0) & 0xFFFF;
|
||||
table2_subtype = (table2_value >> 16) & 0xFFFF;
|
||||
if (table2_subtype != 0x100)
|
||||
|
@ -194,14 +210,15 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
|
|||
|
||||
/* parse sounds */
|
||||
switch(sblk_version) {
|
||||
case 0x01:
|
||||
case 0x03:
|
||||
case 0x04:
|
||||
case 0x05:
|
||||
case 0x09:
|
||||
pitch = (uint8_t)read_8bit(table3_offset+table3_entry_offset+0x02,streamFile);
|
||||
flags = (uint8_t)read_8bit(table3_offset+table3_entry_offset+0x0f,streamFile);
|
||||
stream_offset = read_32bit(table3_offset+table3_entry_offset+0x10,streamFile);
|
||||
stream_size = read_32bit(table3_offset+table3_entry_offset+0x14,streamFile);
|
||||
pitch = (uint8_t)read_8bit(table3_offset+table3_entry_offset+0x02,sf);
|
||||
flags = (uint8_t)read_8bit(table3_offset+table3_entry_offset+0x0f,sf);
|
||||
stream_offset = read_32bit(table3_offset+table3_entry_offset+0x10,sf);
|
||||
stream_size = read_32bit(table3_offset+table3_entry_offset+0x14,sf);
|
||||
|
||||
/* must use some log/formula but whatevs */
|
||||
switch(pitch) {
|
||||
|
@ -218,6 +235,7 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
|
|||
case 0xAF: sample_rate = 14000; break; //?
|
||||
case 0xAE: sample_rate = 13000; break; //?
|
||||
case 0xAC: sample_rate = 12000; break; //?
|
||||
case 0xAB: sample_rate = 11050; break; //?
|
||||
case 0xAA: sample_rate = 11025; break;
|
||||
case 0xA9: sample_rate = 10000; break; //?
|
||||
case 0xA4: sample_rate = 6000; break; //?
|
||||
|
@ -230,10 +248,10 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
|
|||
|
||||
case 0x0d:
|
||||
case 0x0e:
|
||||
flags = (uint8_t)read_8bit(table3_offset+table3_entry_offset+0x12,streamFile);
|
||||
stream_offset = read_32bit(table3_offset+table3_entry_offset+0x44,streamFile);
|
||||
stream_size = read_32bit(table3_offset+table3_entry_offset+0x48,streamFile);
|
||||
sample_rate = (int)read_f32(table3_offset+table3_entry_offset+0x4c,streamFile);
|
||||
flags = (uint8_t)read_8bit(table3_offset+table3_entry_offset+0x12,sf);
|
||||
stream_offset = read_32bit(table3_offset+table3_entry_offset+0x44,sf);
|
||||
stream_size = read_32bit(table3_offset+table3_entry_offset+0x48,sf);
|
||||
sample_rate = (int)read_f32(table3_offset+table3_entry_offset+0x4c,sf);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -251,7 +269,7 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
|
|||
case 0x0e:
|
||||
/* find if this sound has an assigned name in table1 */
|
||||
for (i = 0; i < section_entries; i++) {
|
||||
off_t entry_offset = (uint16_t)read_16bit(table1_offset+(i*table1_entry_size)+table1_suboffset+0x00,streamFile);
|
||||
off_t entry_offset = (uint16_t)read_16bit(table1_offset+(i*table1_entry_size)+table1_suboffset+0x00,sf);
|
||||
|
||||
/* rarely (ex. Polara sfx) one name applies to multiple materials,
|
||||
* from current entry_offset to next entry_offset (section offsets should be in order) */
|
||||
|
@ -267,15 +285,15 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
|
|||
/* 0x0c: table4 size */
|
||||
/* variable: entries */
|
||||
/* variable: names (null terminated) */
|
||||
table4_entries_offset = table4_offset + read_32bit(table4_offset+0x08, streamFile);
|
||||
table4_entries_offset = table4_offset + read_32bit(table4_offset+0x08, sf);
|
||||
table4_names_offset = table4_entries_offset + (0x10*section_entries);
|
||||
//;VGM_LOG("BNK: t4_entries=%lx, t4_names=%lx\n", table4_entries_offset, table4_names_offset);
|
||||
|
||||
/* get assigned name from table4 names */
|
||||
for (i = 0; i < section_entries; i++) {
|
||||
int entry_id = read_32bit(table4_entries_offset+(i*0x10)+0x0c, streamFile);
|
||||
int entry_id = read_32bit(table4_entries_offset+(i*0x10)+0x0c, sf);
|
||||
if (entry_id == table4_entry_id) {
|
||||
name_offset = table4_names_offset + read_32bit(table4_entries_offset+(i*0x10)+0x00, streamFile);
|
||||
name_offset = table4_names_offset + read_32bit(table4_entries_offset+(i*0x10)+0x00, sf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -302,6 +320,29 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
|
|||
case 0x05:
|
||||
channel_count = 1;
|
||||
|
||||
/* early versions don't have PS-ADPCM size, could check next offset but it's all kind of loopy */
|
||||
if (sblk_version <= 0x03 && stream_size == 0 && (flags & 0x80) == 0) {
|
||||
off_t offset;
|
||||
off_t max_offset = get_streamfile_size(sf);
|
||||
|
||||
stream_size += 0x10;
|
||||
for (offset = data_offset + stream_offset + 0x10; offset < max_offset; offset += 0x10) {
|
||||
|
||||
/* beginning frame (if file loops won't have end frame) */
|
||||
if (read_32bitBE(offset + 0x00, sf) == 0x00000000 && read_32bitBE(offset + 0x04, sf) == 0x00000000)
|
||||
break;
|
||||
|
||||
stream_size += 0x10;
|
||||
|
||||
/* end frame */
|
||||
if (read_32bitBE(offset + 0x00, sf) == 0x00077777 && read_32bitBE(offset + 0x04, sf) == 0x77777777)
|
||||
break;
|
||||
}
|
||||
|
||||
//;VGM_LOG("BNK: stream offset=%lx + %lx, new size=%x\n", data_offset, stream_offset, stream_size);
|
||||
}
|
||||
|
||||
|
||||
/* hack for PS3 files that use dual subsongs as stereo */
|
||||
if (total_subsongs == 2 && stream_size * 2 == data_size) {
|
||||
channel_count = 2;
|
||||
|
@ -314,7 +355,7 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
|
|||
codec = PCM16; /* rare [Wipeout HD (PS3)] */
|
||||
}
|
||||
else {
|
||||
loop_flag = ps_find_loop_offsets(streamFile, start_offset, stream_size, channel_count, interleave, &loop_start, &loop_end);
|
||||
loop_flag = ps_find_loop_offsets(sf, start_offset, stream_size, channel_count, interleave, &loop_start, &loop_end);
|
||||
loop_flag = (flags & 0x40); /* no loops values in sight so may only apply to PS-ADPCM flags */
|
||||
|
||||
codec = PSX;
|
||||
|
@ -324,20 +365,20 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
|
|||
break;
|
||||
|
||||
case 0x09:
|
||||
type = read_16bit(start_offset+0x00,streamFile);
|
||||
extradata_size = 0x08 + read_32bit(start_offset+0x04,streamFile); /* 0x14 for AT9 */
|
||||
type = read_16bit(start_offset+0x00,sf);
|
||||
extradata_size = 0x08 + read_32bit(start_offset+0x04,sf); /* 0x14 for AT9 */
|
||||
|
||||
switch(type) {
|
||||
case 0x02: /* ATRAC9 mono */
|
||||
case 0x05: /* ATRAC9 stereo */
|
||||
if (read_32bit(start_offset+0x08,streamFile) + 0x08 != extradata_size) /* repeat? */
|
||||
if (read_32bit(start_offset+0x08,sf) + 0x08 != extradata_size) /* repeat? */
|
||||
goto fail;
|
||||
channel_count = (type == 0x02) ? 1 : 2;
|
||||
|
||||
atrac9_info = (uint32_t)read_32bitBE(start_offset+0x0c,streamFile);
|
||||
atrac9_info = (uint32_t)read_32bitBE(start_offset+0x0c,sf);
|
||||
/* 0x10: null? */
|
||||
loop_length = read_32bit(start_offset+0x14,streamFile);
|
||||
loop_start = read_32bit(start_offset+0x18,streamFile);
|
||||
loop_length = read_32bit(start_offset+0x14,sf);
|
||||
loop_start = read_32bit(start_offset+0x18,sf);
|
||||
loop_end = loop_start + loop_length; /* loop_start is -1 if not set */
|
||||
|
||||
codec = ATRAC9;
|
||||
|
@ -351,26 +392,26 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
|
|||
|
||||
case 0x0d:
|
||||
case 0x0e:
|
||||
type = read_16bit(start_offset+0x00,streamFile);
|
||||
if (read_32bit(start_offset+0x04,streamFile) != 0x01) /* type? */
|
||||
type = read_16bit(start_offset+0x00,sf);
|
||||
if (read_32bit(start_offset+0x04,sf) != 0x01) /* type? */
|
||||
goto fail;
|
||||
extradata_size = 0x10 + read_32bit(start_offset+0x08,streamFile); /* 0x80 for AT9, 0x10 for PCM/PS-ADPCM */
|
||||
extradata_size = 0x10 + read_32bit(start_offset+0x08,sf); /* 0x80 for AT9, 0x10 for PCM/PS-ADPCM */
|
||||
/* 0x0c: null? */
|
||||
|
||||
switch(type) {
|
||||
case 0x02: /* ATRAC9 mono */
|
||||
case 0x05: /* ATRAC9 stereo */
|
||||
if (read_32bit(start_offset+0x10,streamFile) + 0x10 != extradata_size) /* repeat? */
|
||||
if (read_32bit(start_offset+0x10,sf) + 0x10 != extradata_size) /* repeat? */
|
||||
goto fail;
|
||||
channel_count = (type == 0x02) ? 1 : 2;
|
||||
|
||||
atrac9_info = (uint32_t)read_32bitBE(start_offset+0x14,streamFile);
|
||||
atrac9_info = (uint32_t)read_32bitBE(start_offset+0x14,sf);
|
||||
/* 0x18: null? */
|
||||
/* 0x1c: channels? */
|
||||
/* 0x20: null? */
|
||||
|
||||
loop_length = read_32bit(start_offset+0x24,streamFile);
|
||||
loop_start = read_32bit(start_offset+0x28,streamFile);
|
||||
loop_length = read_32bit(start_offset+0x24,sf);
|
||||
loop_start = read_32bit(start_offset+0x28,sf);
|
||||
loop_end = loop_start + loop_length; /* loop_start is -1 if not set */
|
||||
|
||||
codec = ATRAC9;
|
||||
|
@ -379,11 +420,11 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
|
|||
case 0x01: /* PCM16LE mono? (NekoBuro/Polara sfx) */
|
||||
case 0x04: /* PCM16LE stereo? (NekoBuro/Polara sfx) */
|
||||
/* 0x10: null? */
|
||||
channel_count = read_32bit(start_offset+0x14,streamFile);
|
||||
channel_count = read_32bit(start_offset+0x14,sf);
|
||||
interleave = 0x02;
|
||||
|
||||
loop_start = read_32bit(start_offset+0x18,streamFile);
|
||||
loop_length = read_32bit(start_offset+0x1c,streamFile);
|
||||
loop_start = read_32bit(start_offset+0x18,sf);
|
||||
loop_length = read_32bit(start_offset+0x1c,sf);
|
||||
loop_end = loop_start + loop_length; /* loop_start is -1 if not set */
|
||||
|
||||
codec = PCM16;
|
||||
|
@ -391,11 +432,11 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
|
|||
|
||||
case 0x00: /* PS-ADPCM (test banks) */
|
||||
/* 0x10: null? */
|
||||
channel_count = read_32bit(start_offset+0x14,streamFile);
|
||||
channel_count = read_32bit(start_offset+0x14,sf);
|
||||
interleave = 0x02;
|
||||
|
||||
loop_start = read_32bit(start_offset+0x18,streamFile);
|
||||
loop_length = read_32bit(start_offset+0x1c,streamFile);
|
||||
loop_start = read_32bit(start_offset+0x18,sf);
|
||||
loop_length = read_32bit(start_offset+0x1c,sf);
|
||||
loop_end = loop_start + loop_length; /* loop_start is -1 if not set */
|
||||
|
||||
codec = HEVAG;
|
||||
|
@ -486,10 +527,10 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
|
|||
}
|
||||
|
||||
if (name_offset)
|
||||
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamFile);
|
||||
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,sf);
|
||||
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
fail:
|
||||
|
@ -500,12 +541,16 @@ fail:
|
|||
|
||||
#if 0
|
||||
/* .BNK - Sony's bank, earlier version [Jak and Daxter (PS2), NCAA Gamebreaker 2001 (PS2)] */
|
||||
VGMSTREAM * init_vgmstream_bnk_sony_v2(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * init_vgmstream_bnk_sony_v2(STREAMFILE *sf) {
|
||||
/* 0x00: 0x00000001
|
||||
* 0x04: sections (2 or 3)
|
||||
* 0x08+ similar to v3 but "SBv2"
|
||||
* table formats is a bit different
|
||||
* header is like v3 but stream size is in other table?
|
||||
* 0x08+: similar as above (start, size) but with "SBv2"
|
||||
*
|
||||
* 0x2C/2E=entries?
|
||||
* 0x34: table offsets (from sbv2 start)
|
||||
* - table1: 0x1c entries
|
||||
* - table2: 0x08 entries, possibly point to table3
|
||||
* - table3: 0x18 entries, same format as early SBlk (pitch/flags/offset, no size)
|
||||
*/
|
||||
|
||||
fail:
|
||||
|
|
|
@ -4,12 +4,13 @@
|
|||
#include "bnsf_keys.h"
|
||||
|
||||
|
||||
#ifdef VGM_USE_G7221
|
||||
//#define BNSF_BRUTEFORCE
|
||||
#ifdef BNSF_BRUTEFORCE
|
||||
static void bruteforce_bnsf_key(STREAMFILE* sf, off_t start, g7221_codec_data* data, uint8_t* best_key);
|
||||
#endif
|
||||
static void find_bnsf_key(STREAMFILE *sf, off_t start, g7221_codec_data *data, uint8_t *best_key);
|
||||
|
||||
#endif
|
||||
|
||||
/* BNSF - Bandai Namco Sound Format/File [Tales of Graces (Wii), Tales of Berseria (PS4)] */
|
||||
VGMSTREAM * init_vgmstream_bnsf(STREAMFILE *streamFile) {
|
||||
|
@ -126,6 +127,7 @@ fail:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef VGM_USE_G7221
|
||||
static inline void test_key(STREAMFILE* sf, off_t start, g7221_codec_data* data, const char* key, int keylen, int* p_best_score, uint8_t* p_best_key) {
|
||||
uint8_t tmpkey[24];
|
||||
int score;
|
||||
|
@ -219,3 +221,5 @@ static void bruteforce_bnsf_key(STREAMFILE* sf, off_t start, g7221_codec_data* d
|
|||
close_streamfile(sf_keys);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -13,6 +13,7 @@ static void block_callback_default(STREAMFILE *sf, deblock_io_data *data) {
|
|||
static size_t deblock_io_read(STREAMFILE *sf, uint8_t *dest, off_t offset, size_t length, deblock_io_data* data) {
|
||||
size_t total_read = 0;
|
||||
|
||||
//;VGM_LOG("DEBLOCK: of=%lx, sz=%x, po=%lx\n", offset, length, data->physical_offset);
|
||||
|
||||
/* re-start when previous offset (can't map logical<>physical offsets) */
|
||||
if (data->logical_offset < 0 || offset < data->logical_offset) {
|
||||
|
@ -81,11 +82,11 @@ static size_t deblock_io_read(STREAMFILE *sf, uint8_t *dest, off_t offset, size_
|
|||
data->data_size = 0;
|
||||
|
||||
data->step_count = data->cfg.step_count;
|
||||
//VGM_LOG("ignore at %lx + %lx, skips=%i, reads=%i\n", data->physical_offset, data->block_size, data->step_count, data->read_count);
|
||||
//VGM_LOG("ignore at %lx + %lx, skips=%i\n", data->physical_offset, data->block_size, data->step_count);
|
||||
continue;
|
||||
}
|
||||
|
||||
//VGM_LOG("accept at %lx + %lx, skips=%i, reads=%i\n", data->physical_offset, data->block_size, data->step_count, data->read_count);
|
||||
//;VGM_LOG("accept at %lx + %lx, skips=%i\n", data->physical_offset, data->block_size, data->step_count);
|
||||
|
||||
/* read block data */
|
||||
{
|
||||
|
|
|
@ -1618,11 +1618,13 @@ static layered_layout_data* build_layered_eaaudiocore(STREAMFILE *sf_data, eaac_
|
|||
if ( !vgmstream_open_stream(data->layers[i], temp_sf, 0x00) ) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
close_streamfile(temp_sf);
|
||||
temp_sf = NULL;
|
||||
}
|
||||
|
||||
if (!setup_layout_layered(data))
|
||||
goto fail;
|
||||
close_streamfile(temp_sf);
|
||||
return data;
|
||||
|
||||
fail:
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "ogg_vorbis_streamfile.h"
|
||||
|
||||
//todo fuse ogg encryptions and use generic names
|
||||
|
||||
|
||||
static const uint8_t tpf_key[] = {
|
||||
0x0a,0x2b,0x36,0x6f,0x0b,0x0a,0x2b,0x36,0x6f,0x0B
|
||||
};
|
||||
|
||||
static void load_key(ogg_vorbis_io_config_data* cfg, const uint8_t* key, size_t size) {
|
||||
cfg->is_encrypted = 1;
|
||||
cfg->key_len = size;
|
||||
memcpy(cfg->key, key, size);
|
||||
}
|
||||
|
||||
/* parser for various encrypted games */
|
||||
VGMSTREAM* init_vgmstream_encrypted(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE* temp_sf = NULL;
|
||||
ogg_vorbis_io_config_data cfg = {0};
|
||||
uint32_t id;
|
||||
|
||||
|
||||
/* checks */
|
||||
id = read_u32be(0x00, sf);
|
||||
|
||||
if (check_extensions(sf,"ogg,logg")) {
|
||||
/* The Pirate's Fate (PC) */
|
||||
if (id == 0x454C513C) { /* "OggS" xored */
|
||||
load_key(&cfg, tpf_key, sizeof(tpf_key));
|
||||
}
|
||||
else {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
temp_sf = setup_ogg_vorbis_streamfile(sf, cfg);
|
||||
if (!temp_sf) goto fail;
|
||||
#ifdef VGM_USE_VORBIS
|
||||
vgmstream = init_vgmstream_ogg_vorbis(temp_sf);
|
||||
#endif
|
||||
close_streamfile(temp_sf);
|
||||
return vgmstream;
|
||||
}
|
||||
|
||||
if (check_extensions(sf,"mp3")) {
|
||||
/* The Pirate's Fate (PC) */
|
||||
if ((id & 0xFFFFFF00) == 0x436F0500) { /* "ID3\0" xored */
|
||||
load_key(&cfg, tpf_key, sizeof(tpf_key));
|
||||
}
|
||||
else {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
temp_sf = setup_ogg_vorbis_streamfile(sf, cfg);
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
#ifdef VGM_USE_FFMPEG //TODO: allow MP3 without FFmpeg
|
||||
vgmstream = init_vgmstream_ffmpeg(temp_sf);
|
||||
#endif
|
||||
close_streamfile(temp_sf);
|
||||
return vgmstream;
|
||||
}
|
||||
|
||||
if (check_extensions(sf,"wav,lwav")) {
|
||||
/* The Pirate's Fate (PC) */
|
||||
if (id == 0x58627029) { /* "RIFF" xored */
|
||||
load_key(&cfg, tpf_key, sizeof(tpf_key));
|
||||
}
|
||||
else {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
temp_sf = setup_ogg_vorbis_streamfile(sf, cfg);
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
vgmstream = init_vgmstream_riff(temp_sf);
|
||||
close_streamfile(temp_sf);
|
||||
return vgmstream;
|
||||
}
|
||||
|
||||
|
||||
fail:
|
||||
return NULL;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,201 +0,0 @@
|
|||
#ifndef _FSB5_INTERLEAVE_STREAMFILE_H_
|
||||
#define _FSB5_INTERLEAVE_STREAMFILE_H_
|
||||
#include "../streamfile.h"
|
||||
|
||||
typedef enum { FSB5_INT_CELT, FSB5_INT_ATRAC9 } fsb_interleave_codec_t;
|
||||
typedef struct {
|
||||
/* state */
|
||||
off_t logical_offset; /* offset that corresponds to physical_offset */
|
||||
off_t physical_offset; /* actual file offset */
|
||||
int skip_frames; /* frames to skip from other streams at points */
|
||||
|
||||
/* config */
|
||||
fsb_interleave_codec_t codec;
|
||||
size_t interleave;
|
||||
int stream_count;
|
||||
int stream_number;
|
||||
size_t stream_size;
|
||||
off_t start_offset; /* pointing to the stream's beginning */
|
||||
size_t total_size; /* size of the resulting substream */
|
||||
} fsb_interleave_io_data;
|
||||
|
||||
|
||||
|
||||
/* Reads skipping other streams */
|
||||
static size_t fsb_interleave_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, fsb_interleave_io_data* data) {
|
||||
size_t total_read = 0;
|
||||
|
||||
/* ignore bad reads */
|
||||
if (offset < 0 || offset > data->total_size) {
|
||||
return total_read;
|
||||
}
|
||||
|
||||
/* previous offset: re-start as we can't map logical<>physical offsets
|
||||
* (kinda slow as it trashes buffers, but shouldn't happen often) */
|
||||
if (offset < data->logical_offset) {
|
||||
data->physical_offset = data->start_offset;
|
||||
data->logical_offset = 0x00;
|
||||
data->skip_frames = data->stream_number;
|
||||
}
|
||||
|
||||
/* read doing one frame at a time */
|
||||
while (length > 0) {
|
||||
size_t to_read, bytes_read;
|
||||
off_t intrablock_offset, intradata_offset;
|
||||
uint32_t data_size;
|
||||
|
||||
if (offset >= data->total_size)
|
||||
break;
|
||||
|
||||
/* get current data */
|
||||
switch (data->codec) {
|
||||
case FSB5_INT_CELT:
|
||||
case FSB5_INT_ATRAC9:
|
||||
data_size = data->interleave;
|
||||
break;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* skip frames from other streams */
|
||||
if (data->skip_frames) {
|
||||
data->physical_offset += data_size;
|
||||
data->skip_frames--;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* requested offset is outside current block, try next */
|
||||
if (offset >= data->logical_offset + data_size) {
|
||||
data->physical_offset += data_size;
|
||||
data->logical_offset += data_size;
|
||||
data->skip_frames = data->stream_count - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* reads could fall in the middle of the block */
|
||||
intradata_offset = offset - data->logical_offset;
|
||||
intrablock_offset = intradata_offset;
|
||||
|
||||
/* clamp reads up to this block's end */
|
||||
to_read = (data_size - intradata_offset);
|
||||
if (to_read > length)
|
||||
to_read = length;
|
||||
if (to_read == 0)
|
||||
break; /* should never happen... */
|
||||
|
||||
/* finally read and move buffer/offsets */
|
||||
bytes_read = read_streamfile(dest, data->physical_offset + intrablock_offset, to_read, streamfile);
|
||||
total_read += bytes_read;
|
||||
if (bytes_read != to_read)
|
||||
break; /* couldn't read fully */
|
||||
|
||||
dest += bytes_read;
|
||||
offset += bytes_read;
|
||||
length -= bytes_read;
|
||||
|
||||
/* block fully read, go next */
|
||||
if (intradata_offset + bytes_read == data_size) {
|
||||
data->physical_offset += data_size;
|
||||
data->logical_offset += data_size;
|
||||
data->skip_frames = data->stream_count - 1;
|
||||
}
|
||||
}
|
||||
|
||||
return total_read;
|
||||
}
|
||||
|
||||
static size_t fsb_interleave_io_size(STREAMFILE *streamfile, fsb_interleave_io_data* data) {
|
||||
off_t physical_offset, max_physical_offset;
|
||||
size_t total_size = 0;
|
||||
int skip_frames = 0;
|
||||
|
||||
if (data->total_size)
|
||||
return data->total_size;
|
||||
|
||||
physical_offset = data->start_offset;
|
||||
max_physical_offset = physical_offset + data->stream_size;
|
||||
skip_frames = data->stream_number;
|
||||
|
||||
/* get size of the underlying stream, skipping other streams
|
||||
* (all streams should have the same frame count) */
|
||||
while (physical_offset < max_physical_offset) {
|
||||
size_t data_size;
|
||||
|
||||
data_size = data->interleave;
|
||||
switch(data->codec) {
|
||||
case FSB5_INT_CELT:
|
||||
case FSB5_INT_ATRAC9:
|
||||
data_size = data->interleave;
|
||||
break;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* there may be padding at the end, so this doubles as EOF marker */
|
||||
if (data_size == 0)
|
||||
break;
|
||||
if (physical_offset+data_size > max_physical_offset)
|
||||
break;
|
||||
|
||||
/* skip frames from other streams */
|
||||
if (skip_frames) {
|
||||
physical_offset += data_size;
|
||||
skip_frames--;
|
||||
continue;
|
||||
}
|
||||
|
||||
physical_offset += data_size;
|
||||
total_size += data_size;
|
||||
skip_frames = data->stream_count - 1;
|
||||
}
|
||||
|
||||
data->total_size = total_size;
|
||||
return data->total_size;
|
||||
}
|
||||
|
||||
|
||||
/* Prepares custom IO for multistreams, interleaves 1 packet per stream */
|
||||
static STREAMFILE* setup_fsb5_interleave_streamfile(STREAMFILE *streamFile, off_t start_offset, size_t stream_size, int stream_count, int stream_number, fsb_interleave_codec_t codec, size_t interleave) {
|
||||
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
|
||||
fsb_interleave_io_data io_data = {0};
|
||||
size_t io_data_size = sizeof(fsb_interleave_io_data);
|
||||
|
||||
io_data.start_offset = start_offset;
|
||||
io_data.physical_offset = start_offset;
|
||||
io_data.skip_frames = stream_number; /* adjust since start_offset points to the first */
|
||||
io_data.codec = codec;
|
||||
io_data.interleave = interleave;
|
||||
io_data.stream_count = stream_count;
|
||||
io_data.stream_number = stream_number;
|
||||
io_data.stream_size = stream_size; /* full size for all streams */
|
||||
|
||||
io_data.total_size = fsb_interleave_io_size(streamFile, &io_data); /* force init, size of a single stream */
|
||||
|
||||
if (io_data.total_size == 0 || io_data.total_size > io_data.stream_size) {
|
||||
VGM_LOG("FSB5 INTERLEAVE: wrong total_size %x vs %x\n", io_data.total_size,io_data.stream_size);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* setup subfile */
|
||||
new_streamFile = open_wrap_streamfile(streamFile);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, fsb_interleave_io_read,fsb_interleave_io_size);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
new_streamFile = open_buffer_streamfile(new_streamFile,0);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
return temp_streamFile;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_streamFile);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* _FSB_INTERLEAVE_STREAMFILE_H_ */
|
|
@ -0,0 +1,21 @@
|
|||
#ifndef _FSB5_STREAMFILE_H_
|
||||
#define _FSB5_STREAMFILE_H_
|
||||
#include "deblock_streamfile.h"
|
||||
|
||||
static STREAMFILE* setup_fsb5_streamfile(STREAMFILE* sf, off_t stream_start, size_t stream_size, int stream_count, int stream_number, size_t interleave) {
|
||||
STREAMFILE* new_sf = NULL;
|
||||
deblock_config_t cfg = {0};
|
||||
|
||||
cfg.stream_start = stream_start;
|
||||
cfg.stream_size = stream_size;
|
||||
cfg.chunk_size = interleave;
|
||||
cfg.step_start = stream_number;
|
||||
cfg.step_count = stream_count;
|
||||
|
||||
/* setup sf */
|
||||
new_sf = open_wrap_streamfile(sf);
|
||||
new_sf = open_io_deblock_streamfile_f(new_sf, &cfg);
|
||||
return new_sf;
|
||||
}
|
||||
|
||||
#endif /* _FSB5_STREAMFILE_H_ */
|
|
@ -229,6 +229,7 @@ static const hcakey_info hcakey_list[] = {
|
|||
{5047159794308}, // 00000497222AAA84
|
||||
|
||||
// Shin Tennis no Ouji-sama: Rising Beat (iOS/Android) voices?
|
||||
// UNI'S ON AIR (iOS/Android)
|
||||
{4902201417679}, // 0000047561F95FCF
|
||||
|
||||
// Kai-ri-Sei Million Arthur (Vita)
|
||||
|
|
|
@ -2,32 +2,38 @@
|
|||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
/* .ISB - Creative ISACT (Interactive Spatial Audio Composition Tools) middleware [Psychonauts (PC)] */
|
||||
VGMSTREAM * init_vgmstream_isb(STREAMFILE *streamFile) {
|
||||
/* .ISB - Creative ISACT (Interactive Spatial Audio Composition Tools) middleware [Psychonauts (PC), Mass Effect (multi)] */
|
||||
VGMSTREAM * init_vgmstream_isb(STREAMFILE *sf) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset = 0, name_offset = 0;
|
||||
size_t stream_size = 0, name_size = 0;
|
||||
int loop_flag = 0, channel_count = 0, sample_rate = 0, codec = 0, pcm_bytes = 0, bps = 0;
|
||||
int total_subsongs, target_subsong = streamFile->stream_index;
|
||||
int total_subsongs, target_subsong = sf->stream_index;
|
||||
uint32_t (*read_u32me)(off_t,STREAMFILE*);
|
||||
uint32_t (*read_u32ce)(off_t,STREAMFILE*);
|
||||
int big_endian;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile, "isb"))
|
||||
if (!check_extensions(sf, "isb"))
|
||||
goto fail;
|
||||
|
||||
if (read_32bitBE(0x00,streamFile) != 0x52494646) /* "RIFF" */
|
||||
big_endian = read_u32be(0x00,sf) == 0x46464952; /* "FFIR" */
|
||||
read_u32me = big_endian ? read_u32be : read_u32le; /* machine endianness... */
|
||||
read_u32ce = big_endian ? read_u32le : read_u32be; /* chunks change with endianness but this just reads as BE */
|
||||
|
||||
if (read_u32ce(0x00,sf) != 0x52494646) /* "RIFF" */
|
||||
goto fail;
|
||||
if (read_32bitLE(0x04,streamFile) + 0x08 != get_streamfile_size(streamFile))
|
||||
if (read_u32me(0x04,sf) + 0x08 != get_streamfile_size(sf))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x08,streamFile) != 0x69736266) /* "isbf" */
|
||||
if (read_u32ce(0x08,sf) != 0x69736266) /* "isbf" */
|
||||
goto fail;
|
||||
|
||||
/* some files have a companion .icb, seems to be a cue file pointing here */
|
||||
|
||||
/* format is RIFF with many custom chunks, apparently for their DAW-like editor with
|
||||
* complex functions, but most seem always included by default and unused, and games
|
||||
* Psychonauts seems to use the format as a simple audio bank. Mass Effect (X360)
|
||||
* apparently uses ISACT, while Psychonauts Xbox/PS2 don't. */
|
||||
* seems to use the format as a simple audio bank. Psychonauts Xbox/PS2 doesn't use ISACT. */
|
||||
|
||||
{
|
||||
off_t offset, max_offset, header_offset = 0;
|
||||
|
@ -38,15 +44,15 @@ VGMSTREAM * init_vgmstream_isb(STREAMFILE *streamFile) {
|
|||
|
||||
/* parse base RIFF */
|
||||
offset = 0x0c;
|
||||
max_offset = get_streamfile_size(streamFile);
|
||||
max_offset = get_streamfile_size(sf);
|
||||
while (offset < max_offset) {
|
||||
uint32_t chunk_type = read_u32be(offset + 0x00,streamFile);
|
||||
uint32_t chunk_size = read_s32le(offset + 0x04,streamFile);
|
||||
uint32_t chunk_type = read_u32ce(offset + 0x00,sf);
|
||||
uint32_t chunk_size = read_u32me(offset + 0x04,sf);
|
||||
offset += 0x08;
|
||||
|
||||
switch(chunk_type) {
|
||||
case 0x4C495354: /* "LIST" */
|
||||
if (read_u32be(offset, streamFile) != 0x73616D70) /* "samp" */
|
||||
if (read_u32ce(offset, sf) != 0x73616D70) /* "samp" */
|
||||
break; /* there are "bfob" LIST without data */
|
||||
|
||||
total_subsongs++;
|
||||
|
@ -72,8 +78,8 @@ VGMSTREAM * init_vgmstream_isb(STREAMFILE *streamFile) {
|
|||
offset = header_offset + 0x04;
|
||||
max_offset = offset + header_size;
|
||||
while (offset < max_offset) {
|
||||
uint32_t chunk_type = read_u32be(offset + 0x00,streamFile);
|
||||
uint32_t chunk_size = read_s32le(offset + 0x04,streamFile);
|
||||
uint32_t chunk_type = read_u32ce(offset + 0x00,sf);
|
||||
uint32_t chunk_size = read_u32me(offset + 0x04,sf);
|
||||
offset += 0x08;
|
||||
|
||||
switch(chunk_type) {
|
||||
|
@ -83,21 +89,21 @@ VGMSTREAM * init_vgmstream_isb(STREAMFILE *streamFile) {
|
|||
break;
|
||||
|
||||
case 0x63686E6B: /* "chnk" */
|
||||
channel_count = read_u32le(offset + 0x00, streamFile);
|
||||
channel_count = read_u32me(offset + 0x00, sf);
|
||||
break;
|
||||
|
||||
case 0x73696E66: /* "sinf" */
|
||||
/* 0x00: null? */
|
||||
/* 0x04: some value? */
|
||||
sample_rate = read_u32le(offset + 0x08, streamFile);
|
||||
pcm_bytes = read_u32le(offset + 0x0c, streamFile);
|
||||
bps = read_u16le(offset + 0x10, streamFile);
|
||||
sample_rate = read_u32me(offset + 0x08, sf);
|
||||
pcm_bytes = read_u32me(offset + 0x0c, sf);
|
||||
bps = read_u16le(offset + 0x10, sf);
|
||||
/* 0x12: some value? */
|
||||
break;
|
||||
|
||||
case 0x636D7069: /* "cmpi" */
|
||||
codec = read_u32le(offset + 0x00, streamFile);
|
||||
if (read_u32le(offset + 0x04, streamFile) != codec) {
|
||||
codec = read_u32me(offset + 0x00, sf);
|
||||
if (read_u32me(offset + 0x04, sf) != codec) {
|
||||
VGM_LOG("ISB: unknown compression repeat\n");
|
||||
goto fail;
|
||||
}
|
||||
|
@ -134,11 +140,17 @@ VGMSTREAM * init_vgmstream_isb(STREAMFILE *streamFile) {
|
|||
vgmstream = allocate_vgmstream(channel_count, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_WAF; //todo
|
||||
vgmstream->meta_type = meta_ISB;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_streams = total_subsongs;
|
||||
vgmstream->stream_size = stream_size;
|
||||
|
||||
if (name_offset) { /* UTF16 but only uses lower bytes */
|
||||
if (name_size > STREAM_NAME_SIZE)
|
||||
name_size = STREAM_NAME_SIZE;
|
||||
read_string_utf16(vgmstream->stream_name,name_size, name_offset, sf, big_endian);
|
||||
}
|
||||
|
||||
switch(codec) {
|
||||
case 0x00:
|
||||
if (bps == 8) {
|
||||
|
@ -162,7 +174,7 @@ VGMSTREAM * init_vgmstream_isb(STREAMFILE *streamFile) {
|
|||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
case 0x02:
|
||||
vgmstream->codec_data = init_ogg_vorbis(streamFile, start_offset, stream_size, NULL);
|
||||
vgmstream->codec_data = init_ogg_vorbis(sf, start_offset, stream_size, NULL);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_OGG_VORBIS;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
@ -170,18 +182,64 @@ VGMSTREAM * init_vgmstream_isb(STREAMFILE *streamFile) {
|
|||
break;
|
||||
#endif
|
||||
|
||||
default: /* according to press releases ISACT may support WMA and XMA */
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case 0x04: {
|
||||
uint8_t buf[0x100];
|
||||
size_t bytes;
|
||||
off_t fmt_offset = start_offset;
|
||||
size_t fmt_size = 0x20;
|
||||
|
||||
start_offset += fmt_size;
|
||||
stream_size -= fmt_size;
|
||||
|
||||
/* XMA1 "fmt" chunk (BE, unlike the usual LE) */
|
||||
bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,sizeof(buf), fmt_offset,fmt_size, stream_size, sf, 1);
|
||||
vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, start_offset, stream_size);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->num_samples = pcm_bytes / channel_count / (bps/8);
|
||||
xma_fix_raw_samples(vgmstream, sf, start_offset, stream_size, fmt_offset, 1,1);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
case 0x05: {
|
||||
//TODO: improve
|
||||
VGMSTREAM *temp_vgmstream = NULL;
|
||||
STREAMFILE *temp_sf = NULL;
|
||||
|
||||
temp_sf = setup_subfile_streamfile(sf, start_offset, stream_size, "msf");
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
temp_vgmstream = init_vgmstream_msf(temp_sf);
|
||||
if (temp_vgmstream) {
|
||||
temp_vgmstream->num_streams = vgmstream->num_streams;
|
||||
temp_vgmstream->stream_size = vgmstream->stream_size;
|
||||
temp_vgmstream->meta_type = vgmstream->meta_type;
|
||||
strcpy(temp_vgmstream->stream_name, vgmstream->stream_name);
|
||||
|
||||
close_streamfile(temp_sf);
|
||||
close_vgmstream(vgmstream);
|
||||
return temp_vgmstream;
|
||||
}
|
||||
else {
|
||||
close_streamfile(temp_sf);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
default: /* according to press releases ISACT may support WMA */
|
||||
VGM_LOG("ISB: unknown codec %i\n", codec);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (name_offset) { /* UTF16 but only uses lower bytes */
|
||||
if (name_size > STREAM_NAME_SIZE)
|
||||
name_size = STREAM_NAME_SIZE;
|
||||
read_string_utf16le(vgmstream->stream_name,name_size, name_offset, streamFile);
|
||||
}
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
|
|
|
@ -101,7 +101,7 @@ VGMSTREAM * init_vgmstream_ngc_str(STREAMFILE *streamFile);
|
|||
|
||||
VGMSTREAM * init_vgmstream_caf(STREAMFILE *streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ps2_vpk(STREAMFILE *streamFile);
|
||||
VGMSTREAM * init_vgmstream_vpk(STREAMFILE *streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile);
|
||||
|
||||
|
@ -888,4 +888,9 @@ VGMSTREAM * init_vgmstream_kwb(STREAMFILE* sf);
|
|||
|
||||
VGMSTREAM * init_vgmstream_lrmd(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM* init_vgmstream_bkhd(STREAMFILE* sf);
|
||||
VGMSTREAM* init_vgmstream_bkhd_fx(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM* init_vgmstream_encrypted(STREAMFILE* sf);
|
||||
|
||||
#endif /*_META_H*/
|
||||
|
|
|
@ -98,8 +98,6 @@ VGMSTREAM * init_vgmstream_mp4_aac_offset(STREAMFILE *streamFile, uint64_t start
|
|||
aac_file->h_aacdecoder = aacDecoder_Open( TT_MP4_RAW, 1 );
|
||||
if ( !aac_file->h_aacdecoder ) goto fail;
|
||||
|
||||
aacDecoder_SetParam( aac_file->h_aacdecoder, AAC_PCM_OUTPUT_CHANNELS, 2 );
|
||||
|
||||
MP4GetTrackESConfiguration( aac_file->h_mp4file, aac_file->track_id, (uint8_t**)(&buffer), (uint32_t*)(&buffer_size));
|
||||
|
||||
ubuffer_size = buffer_size;
|
||||
|
|
|
@ -1,164 +1,269 @@
|
|||
#include "meta.h"
|
||||
#include "../layout/layout.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
typedef enum { NONE, PSX, DSP, XBOX } mul_codec;
|
||||
|
||||
/* .MUL - from Crystal Dynamics games [Legacy of Kain: Defiance (PS2), Tomb Raider Underworld (multi)] */
|
||||
VGMSTREAM * init_vgmstream_mul(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset, coefs_offset = 0;
|
||||
int loop_flag, channel_count, sample_rate, num_samples, loop_start;
|
||||
int big_endian;
|
||||
mul_codec codec = NONE;
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
|
||||
|
||||
|
||||
/* checks */
|
||||
/* .mul: found in the exe, used by the bigfile extractor (Gibbed.TombRaider)
|
||||
* (some files have companion .mus/sam files but seem to be sequences/control stuff)
|
||||
* .(extensionless): filenames as found in the bigfile
|
||||
* .emff: fake extension ('Eidos Music File Format') */
|
||||
if (!check_extensions(streamFile, "mul,,emff"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x10,streamFile) != 0 ||
|
||||
read_32bitBE(0x14,streamFile) != 0 ||
|
||||
read_32bitBE(0x18,streamFile) != 0 ||
|
||||
read_32bitBE(0x1c,streamFile) != 0)
|
||||
goto fail;
|
||||
|
||||
big_endian = guess_endianness32bit(0x00, streamFile);
|
||||
read_32bit = big_endian ? read_32bitBE : read_32bitLE;
|
||||
|
||||
sample_rate = read_32bit(0x00,streamFile);
|
||||
loop_start = read_32bit(0x04,streamFile);
|
||||
num_samples = read_32bit(0x08,streamFile);
|
||||
channel_count = read_32bit(0x0C,streamFile);
|
||||
if (sample_rate < 8000 || sample_rate > 48000 || channel_count > 8)
|
||||
goto fail;
|
||||
/* 0x20: flag when file has non-audio blocks (ignored by the layout) */
|
||||
/* 0x24: same? */
|
||||
/* 0x28: loop offset within audio data (not file offset) */
|
||||
/* 0x2c: some value related to loop? */
|
||||
/* 0x34: id? */
|
||||
/* 0x38+: channel config until ~0x100? (multiple 0x3F800000 depending on the number of channels) */
|
||||
|
||||
/* test known versions (later versions start from 0x24 instead of 0x20) */
|
||||
if (!(read_32bit(0x38,streamFile) == 0x3F800000 ||
|
||||
read_32bit(0x3c,streamFile) == 0x3F800000)) /* Tomb Raider Underworld */
|
||||
goto fail;
|
||||
|
||||
loop_flag = (loop_start >= 0); /* 0xFFFFFFFF when not looping */
|
||||
start_offset = 0x800;
|
||||
|
||||
/* format is pretty limited so we need to guess codec */
|
||||
if (big_endian) {
|
||||
/* test DSP (GC/Wii): check known coef locations */
|
||||
if (read_32bitBE(0xC8,streamFile) != 0) { /* Tomb Raider Legend (GC) */
|
||||
codec = DSP;
|
||||
coefs_offset = 0xC8;
|
||||
}
|
||||
else if (read_32bitBE(0xCC,streamFile) != 0) { /* Tomb Raider Anniversary (Wii) */
|
||||
codec = DSP;
|
||||
coefs_offset = 0xCC;
|
||||
}
|
||||
else if (read_32bitBE(0x2D0,streamFile) != 0) { /* Tomb Raider Underworld (Wii) */
|
||||
codec = DSP;
|
||||
coefs_offset = 0x2D0;
|
||||
}
|
||||
|
||||
// todo test XMA1 (X360): mono streams, each block has 1 sub-blocks of 0x800 packet per channel
|
||||
|
||||
// todo test ? (PS3)
|
||||
}
|
||||
else {
|
||||
int i;
|
||||
off_t offset = start_offset;
|
||||
size_t file_size = get_streamfile_size(streamFile);
|
||||
size_t frame_size;
|
||||
|
||||
/* check first audio frame */
|
||||
while (offset < file_size) {
|
||||
uint32_t block_type = read_32bit(offset+0x00, streamFile);
|
||||
uint32_t block_size = read_32bit(offset+0x04, streamFile);
|
||||
uint32_t data_size = read_32bit(offset+0x10, streamFile);
|
||||
|
||||
if (block_type != 0x00) {
|
||||
offset += 0x10 + block_size;
|
||||
continue; /* not audio */
|
||||
}
|
||||
|
||||
/* test PS-ADPCM (PS2/PSP): flag is always 2 in .mul */
|
||||
frame_size = 0x10;
|
||||
for (i = 0; i < data_size / frame_size; i++) {
|
||||
if (read_8bit(offset + 0x20 + frame_size*i + 0x01, streamFile) != 0x02)
|
||||
break;
|
||||
}
|
||||
if (i == data_size / frame_size) {
|
||||
codec = PSX;
|
||||
break;
|
||||
}
|
||||
|
||||
/* test XBOX-IMA (PC/Xbox): reserved frame header value is always 0 */
|
||||
frame_size = 0x24;
|
||||
for (i = 0; i < data_size / frame_size; i++) {
|
||||
if (read_8bit(offset + 0x20 + frame_size*i + 0x03, streamFile) != 0x00)
|
||||
break;
|
||||
}
|
||||
if (i == data_size / frame_size) {
|
||||
codec = XBOX;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (codec == NONE) {
|
||||
VGM_LOG("MUL: unknown codec\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_MUL;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = num_samples;
|
||||
vgmstream->loop_start_sample = loop_start;
|
||||
vgmstream->loop_end_sample = num_samples;
|
||||
vgmstream->codec_endian = big_endian;
|
||||
|
||||
switch(codec) {
|
||||
case PSX:
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->layout_type = layout_blocked_mul;
|
||||
break;
|
||||
|
||||
case DSP:
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_blocked_mul;
|
||||
|
||||
dsp_read_coefs_be(vgmstream,streamFile,coefs_offset+0x00,0x2e);
|
||||
dsp_read_hist_be (vgmstream,streamFile,coefs_offset+0x24,0x2e);
|
||||
break;
|
||||
|
||||
case XBOX:
|
||||
vgmstream->coding_type = coding_XBOX_IMA_int;
|
||||
vgmstream->layout_type = layout_blocked_mul;
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
#include "meta.h"
|
||||
#include "../layout/layout.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "mul_streamfile.h"
|
||||
|
||||
typedef enum { PSX, DSP, XBOX, XMA1 } mul_codec;
|
||||
|
||||
static int guess_codec(STREAMFILE* sf, int big_endian, int channels, mul_codec* p_codec, off_t* p_extra_offset);
|
||||
|
||||
static layered_layout_data* build_layered_mul(STREAMFILE *sf_data, int big_endian, VGMSTREAM* vgmstream);
|
||||
|
||||
/* .MUL - from Crystal Dynamics games [Legacy of Kain: Defiance (PS2), Tomb Raider Underworld (multi)] */
|
||||
VGMSTREAM * init_vgmstream_mul(STREAMFILE *sf) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset, coefs_offset = 0;
|
||||
int loop_flag, channel_count, sample_rate, num_samples, loop_start;
|
||||
int big_endian;
|
||||
mul_codec codec;
|
||||
uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL;
|
||||
|
||||
|
||||
/* checks */
|
||||
/* .mul: found in the exe, used by the bigfile extractor (Gibbed.TombRaider)
|
||||
* (some files have companion .mus/sam files but seem to be sequences/control stuff)
|
||||
* .(extensionless): filenames as found in the bigfile
|
||||
* .emff: fake extension ('Eidos Music File Format') */
|
||||
if (!check_extensions(sf, "mul,,emff"))
|
||||
goto fail;
|
||||
if (read_u32be(0x10,sf) != 0 ||
|
||||
read_u32be(0x14,sf) != 0 ||
|
||||
read_u32be(0x18,sf) != 0 ||
|
||||
read_u32be(0x1c,sf) != 0)
|
||||
goto fail;
|
||||
|
||||
big_endian = guess_endianness32bit(0x00, sf);
|
||||
read_u32 = big_endian ? read_u32be : read_u32le;
|
||||
|
||||
sample_rate = read_u32(0x00,sf);
|
||||
loop_start = read_u32(0x04,sf);
|
||||
num_samples = read_u32(0x08,sf);
|
||||
channel_count = read_u32(0x0C,sf);
|
||||
if (sample_rate < 8000 || sample_rate > 48000 || channel_count > 8)
|
||||
goto fail;
|
||||
/* 0x20: flag when file has non-audio blocks (ignored by the layout) */
|
||||
/* 0x24: same? */
|
||||
/* 0x28: loop offset within audio data (not file offset) */
|
||||
/* 0x2c: some value related to loop? */
|
||||
/* 0x34: id? */
|
||||
/* 0x38+: channel config until ~0x100? (multiple 0x3F800000 / 1.0f depending on the number of channels) */
|
||||
|
||||
/* test known versions (later versions start from 0x24 instead of 0x20) */
|
||||
if (!(read_u32(0x38,sf) == 0x3F800000 ||
|
||||
read_u32(0x3c,sf) == 0x3F800000)) /* Tomb Raider Underworld */
|
||||
goto fail;
|
||||
|
||||
loop_flag = (loop_start >= 0); /* 0xFFFFFFFF when not looping */
|
||||
start_offset = 0x800;
|
||||
|
||||
/* format is pretty limited so we need to guess codec */
|
||||
if (!guess_codec(sf, big_endian, channel_count, &codec, &coefs_offset))
|
||||
goto fail;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_MUL;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = num_samples;
|
||||
vgmstream->loop_start_sample = loop_start;
|
||||
vgmstream->loop_end_sample = num_samples;
|
||||
vgmstream->codec_endian = big_endian;
|
||||
|
||||
switch(codec) {
|
||||
case PSX:
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->layout_type = layout_blocked_mul;
|
||||
break;
|
||||
|
||||
case DSP:
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_blocked_mul;
|
||||
|
||||
dsp_read_coefs_be(vgmstream,sf,coefs_offset+0x00,0x2e);
|
||||
dsp_read_hist_be (vgmstream,sf,coefs_offset+0x24,0x2e);
|
||||
break;
|
||||
|
||||
case XBOX:
|
||||
vgmstream->coding_type = coding_XBOX_IMA_int;
|
||||
vgmstream->layout_type = layout_blocked_mul;
|
||||
break;
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case XMA1: {
|
||||
|
||||
vgmstream->layout_data = build_layered_mul(sf, big_endian, vgmstream);
|
||||
if (!vgmstream->layout_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_layered;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int guess_codec(STREAMFILE* sf, int big_endian, int channels, mul_codec* p_codec, off_t* p_extra_offset) {
|
||||
uint32_t (*read_u32)(off_t,STREAMFILE*);
|
||||
|
||||
read_u32 = big_endian ? read_u32be : read_u32le;
|
||||
*p_extra_offset = 0;
|
||||
|
||||
if (big_endian) {
|
||||
/* test DSP (GC/Wii): check known coef locations */
|
||||
if (read_u32be(0xC8,sf) != 0) { /* Tomb Raider Legend (GC) */
|
||||
*p_codec = DSP;
|
||||
*p_extra_offset = 0xC8;
|
||||
return 1;
|
||||
}
|
||||
else if (read_u32be(0xCC,sf) != 0) { /* Tomb Raider Anniversary (Wii) */
|
||||
*p_codec = DSP;
|
||||
*p_extra_offset = 0xCC;
|
||||
return 1;
|
||||
}
|
||||
else if (read_u32be(0x2D0,sf) != 0) { /* Tomb Raider Underworld (Wii) */
|
||||
*p_codec = DSP;
|
||||
*p_extra_offset = 0x2D0;
|
||||
return 1;
|
||||
}
|
||||
else { //if (ps_check_format(sf, 0x820, 0x100)) {
|
||||
/* may be PS3/X360, tested below [Tomb Raider 7 (PS3)] */
|
||||
}
|
||||
|
||||
// todo test XMA1 (X360): N mono streams (layered), each block has 1 sub-blocks of 0x800 packet per channel
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
int i;
|
||||
off_t offset = 0x800;
|
||||
size_t file_size = get_streamfile_size(sf);
|
||||
size_t frame_size;
|
||||
|
||||
/* check first audio frame */
|
||||
while (offset < file_size) {
|
||||
uint32_t block_type = read_u32(offset+0x00, sf);
|
||||
uint32_t block_size = read_u32(offset+0x04, sf);
|
||||
uint32_t data_size = read_u32(offset+0x10, sf);
|
||||
|
||||
if (block_type != 0x00) {
|
||||
offset += 0x10 + block_size;
|
||||
continue; /* not audio */
|
||||
}
|
||||
|
||||
/* test XMA1 (X360): has sub-blocks of 0x800 per channel */
|
||||
if (block_size == 0x810 * channels) {
|
||||
for (i = 0; i < channels; i++) {
|
||||
off_t test_offset = offset + 0x10 + 0x810*i;
|
||||
if (read_u32(test_offset + 0x00, sf) != 0x800 || /* XMA packet size */
|
||||
read_u32(test_offset + 0x10, sf) != 0x08000000) /* XMA packet first header */
|
||||
break;
|
||||
}
|
||||
if (i == channels) {
|
||||
*p_codec = XMA1;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* test PS-ADPCM (PS2/PSP): flag is always 2 in .mul */
|
||||
frame_size = 0x10;
|
||||
for (i = 0; i < data_size / frame_size; i++) {
|
||||
if (read_8bit(offset + 0x20 + frame_size*i + 0x01, sf) != 0x02)
|
||||
break;
|
||||
}
|
||||
if (i == data_size / frame_size) {
|
||||
*p_codec = PSX;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* test XBOX-IMA (PC/Xbox): reserved frame header value is always 0 */
|
||||
frame_size = 0x24;
|
||||
for (i = 0; i < data_size / frame_size; i++) {
|
||||
if (read_8bit(offset + 0x20 + frame_size*i + 0x03, sf) != 0x00)
|
||||
break;
|
||||
}
|
||||
if (i == data_size / frame_size) {
|
||||
*p_codec = XBOX;
|
||||
return 1;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* MUL contain one XMA1 streams per channel so we need the usual voodoo */
|
||||
static layered_layout_data* build_layered_mul(STREAMFILE *sf_data, int big_endian, VGMSTREAM* vgmstream) {
|
||||
layered_layout_data* data = NULL;
|
||||
STREAMFILE* temp_sf = NULL;
|
||||
int i, layers = vgmstream->channels;
|
||||
|
||||
|
||||
/* init layout */
|
||||
data = init_layout_layered(layers);
|
||||
if (!data) goto fail;
|
||||
|
||||
for (i = 0; i < layers; i++) {
|
||||
int layer_channels = 1;
|
||||
|
||||
|
||||
/* build the layer VGMSTREAM */
|
||||
data->layers[i] = allocate_vgmstream(layer_channels, 0);
|
||||
if (!data->layers[i]) goto fail;
|
||||
|
||||
data->layers[i]->sample_rate = vgmstream->sample_rate;
|
||||
data->layers[i]->num_samples = vgmstream->num_samples;
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
{
|
||||
uint8_t buf[0x100];
|
||||
int bytes;
|
||||
size_t stream_size;
|
||||
|
||||
temp_sf = setup_mul_streamfile(sf_data, big_endian, i, layers);
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
stream_size = get_streamfile_size(temp_sf);
|
||||
|
||||
bytes = ffmpeg_make_riff_xma1(buf, 0x100, data->layers[i]->num_samples, stream_size, data->layers[i]->channels, data->layers[i]->sample_rate, 0);
|
||||
data->layers[i]->codec_data = init_ffmpeg_header_offset(temp_sf, buf,bytes, 0x00, stream_size);
|
||||
if (!data->layers[i]->codec_data) goto fail;
|
||||
|
||||
data->layers[i]->coding_type = coding_FFmpeg;
|
||||
data->layers[i]->layout_type = layout_none;
|
||||
data->layers[i]->stream_size = stream_size;
|
||||
|
||||
xma_fix_raw_samples(data->layers[i], temp_sf, 0x00,stream_size, 0, 0,0); /* ? */
|
||||
|
||||
close_streamfile(temp_sf);
|
||||
temp_sf = NULL;
|
||||
}
|
||||
#else
|
||||
goto fail;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!setup_layout_layered(data))
|
||||
goto fail;
|
||||
return data;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_sf);
|
||||
free_layout_layered(data);
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
#ifndef _MUL_STREAMFILE_H_
|
||||
#define _MUL_STREAMFILE_H_
|
||||
#include "deblock_streamfile.h"
|
||||
|
||||
static void block_callback(STREAMFILE* sf, deblock_io_data* data) {
|
||||
uint32_t block_type;
|
||||
size_t block_size;
|
||||
uint32_t (*read_u32)(off_t,STREAMFILE*) = data->cfg.big_endian ? read_u32be : read_u32le;
|
||||
|
||||
|
||||
if (data->physical_offset == 0) {
|
||||
data->block_size = 0x800;
|
||||
data->data_size = 0;
|
||||
data->skip_size = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
block_type = read_u32(data->physical_offset + 0x00, sf);
|
||||
block_size = read_u32(data->physical_offset + 0x04, sf); /* not including main header */
|
||||
|
||||
/* some blocks only contain half of data (continues in next block) so use track numbers */
|
||||
|
||||
if (block_type == 0x00 && block_size != 0) {
|
||||
/* header block */
|
||||
data->block_size = 0x10;
|
||||
data->data_size = 0;
|
||||
data->skip_size = 0;
|
||||
}
|
||||
else if (block_type == 0x00000800) {
|
||||
data->block_size = 0x810;
|
||||
|
||||
/* actually sub-block with size + number, kinda half-assed but meh... */
|
||||
if (block_size == data->cfg.track_number) {
|
||||
data->data_size = 0x800;
|
||||
data->skip_size = 0x10;
|
||||
}
|
||||
else{
|
||||
data->data_size = 0;
|
||||
data->skip_size = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* non-audio block */
|
||||
data->block_size = block_size + 0x10;
|
||||
data->data_size = 0;
|
||||
data->skip_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Deinterleaves MUL streams */
|
||||
static STREAMFILE* setup_mul_streamfile(STREAMFILE* sf, int big_endian, int track_number, int track_count) {
|
||||
STREAMFILE *new_sf = NULL;
|
||||
deblock_config_t cfg = {0};
|
||||
|
||||
cfg.big_endian = big_endian;
|
||||
cfg.track_number = track_number;
|
||||
cfg.track_count = track_count;
|
||||
cfg.block_callback = block_callback;
|
||||
|
||||
new_sf = open_wrap_streamfile(sf);
|
||||
new_sf = open_io_deblock_streamfile_f(new_sf, &cfg);
|
||||
return new_sf;
|
||||
}
|
||||
|
||||
#endif /* _MUL_STREAMFILE_H_ */
|
|
@ -1,75 +0,0 @@
|
|||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
/* VID1 - from Neversoft games (Gun, Tony Hawk's American Wasteland GC) */
|
||||
VGMSTREAM * init_vgmstream_ngc_vid1(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset, header_offset, header_size;
|
||||
int loop_flag = 0, channel_count;
|
||||
|
||||
|
||||
/* check extension */
|
||||
if (!check_extensions(streamFile,"ogg,logg"))
|
||||
goto fail;
|
||||
|
||||
/* chunked/blocked format containing video or audio frames */
|
||||
if (read_32bitBE(0x00,streamFile) != 0x56494431) /* "VID1" */
|
||||
goto fail;
|
||||
|
||||
/* find actual header start/size in the chunks */
|
||||
{
|
||||
header_offset = read_32bitBE(0x04, streamFile);
|
||||
if (read_32bitBE(header_offset,streamFile) != 0x48454144) /* "HEAD" */
|
||||
goto fail;
|
||||
start_offset = header_offset + read_32bitBE(header_offset + 0x04, streamFile);
|
||||
header_offset += 0x0c;
|
||||
|
||||
/* videos have VIDH before AUDH, and VIDD in blocks, but aren't parsed ATM */
|
||||
|
||||
if (read_32bitBE(header_offset,streamFile) != 0x41554448) /* "AUDH" */
|
||||
goto fail;
|
||||
header_size = read_32bitBE(header_offset + 0x04, streamFile);
|
||||
header_offset += 0x0c;
|
||||
|
||||
if (read_32bitBE(header_offset,streamFile) != 0x56415544) /* "VAUD" (Vorbis audio?) */
|
||||
goto fail;
|
||||
header_offset += 0x04;
|
||||
header_size -= 0x10;
|
||||
|
||||
}
|
||||
channel_count = read_8bit(header_offset + 0x04,streamFile);
|
||||
/* other values unknown, maybe related to vorbis (ex. bitrate/encoding modes) */
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = read_32bitBE(header_offset + 0x00, streamFile);
|
||||
vgmstream->num_samples = read_32bitBE(header_offset + 0x1c, streamFile);
|
||||
vgmstream->meta_type = meta_NGC_VID1;
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
{
|
||||
vorbis_custom_config cfg = {0};
|
||||
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->coding_type = coding_VORBIS_custom;
|
||||
vgmstream->codec_data = init_vorbis_custom(streamFile, header_offset + 0x20, VORBIS_VID1, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
}
|
||||
#else
|
||||
goto fail;
|
||||
#endif
|
||||
|
||||
/* open the file for reading */
|
||||
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
|
@ -1,44 +1,56 @@
|
|||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* NPFS - found in Namco PS2/PSP games [Tekken 5 (PS2), Venus & Braves (PS2), Ridge Racer (PSP)] */
|
||||
VGMSTREAM * init_vgmstream_nps(STREAMFILE *streamFile) {
|
||||
/* NPFS - found in Namco NuSound v1 games [Tekken 5 (PS2), Venus & Braves (PS2), Ridge Racer (PSP)] */
|
||||
VGMSTREAM* init_vgmstream_nps(STREAMFILE* sf) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag, channel_count;
|
||||
uint32_t channel_size;
|
||||
int loop_flag, channel_count, loop_start, sample_rate;
|
||||
|
||||
|
||||
/* checks */
|
||||
/* .nps: referenced extension (ex. Venus & Braves data files)
|
||||
/* .nps: referenced extension (ex. Venus & Braves, Ridge Racer data files)
|
||||
* .npsf: header id (Namco Production Sound File?) */
|
||||
if ( !check_extensions(streamFile,"nps,npsf"))
|
||||
if ( !check_extensions(sf,"nps,npsf"))
|
||||
goto fail;
|
||||
|
||||
if (read_32bitBE(0x00,streamFile) != 0x4E505346) /* "NPSF" */
|
||||
if (read_u32be(0x00, sf) != 0x4E505346) /* "NPSF" */
|
||||
goto fail;
|
||||
|
||||
loop_flag = (read_32bitLE(0x14,streamFile) != 0xFFFFFFFF);
|
||||
channel_count = read_32bitLE(0x0C,streamFile);
|
||||
start_offset = (off_t)read_32bitLE(0x10,streamFile);
|
||||
/* 0x04: version? (0x00001000 = 1.00?) */
|
||||
channel_size = read_s32le(0x08, sf);
|
||||
channel_count = read_s32le(0x0C, sf);
|
||||
start_offset = read_s32le(0x10, sf); /* interleave? */
|
||||
loop_start = read_s32le(0x14, sf);
|
||||
sample_rate = read_s32le(0x18, sf);
|
||||
/* 0x1c: volume? (0x3e8 = 1000 = max) */
|
||||
/* 0x20: flags? (varies between sound types in a game, but no clear pattern vs other games) */
|
||||
/* 0x24: flag? (0/1) */
|
||||
/* 0x28: null */
|
||||
/* 0x2c: null */
|
||||
/* 0x30: always 0x40 */
|
||||
/* 0x34: name (usually null terminated but may contain garbage) */
|
||||
/* rest: null or 0xFF until start */
|
||||
loop_flag = loop_start != -1;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
vgmstream = allocate_vgmstream(channel_count, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->channels = read_32bitLE(0x0C,streamFile);
|
||||
vgmstream->sample_rate = read_32bitLE(0x18,streamFile);
|
||||
vgmstream->num_samples = ps_bytes_to_samples(read_32bitLE(0x08,streamFile), 1); /* single channel data */
|
||||
if(vgmstream->loop_flag) {
|
||||
vgmstream->loop_start_sample = read_32bitLE(0x14,streamFile);
|
||||
vgmstream->loop_end_sample = ps_bytes_to_samples(read_32bitLE(0x08,streamFile), 1);
|
||||
}
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = ps_bytes_to_samples(channel_size, 1);
|
||||
vgmstream->loop_start_sample = loop_start;
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = read_32bitLE(0x04,streamFile) / 2;
|
||||
vgmstream->interleave_block_size = 0x800;
|
||||
vgmstream->meta_type = meta_NPS;
|
||||
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, 0x34,streamFile);
|
||||
read_string(vgmstream->stream_name, STREAM_NAME_SIZE, 0x34, sf);
|
||||
|
||||
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
|
|
|
@ -86,7 +86,10 @@ VGMSTREAM * init_vgmstream_ogg_opus(STREAMFILE *streamFile) {
|
|||
else if (strstr(user_comment,"LoopEnd=")==user_comment) { /* LoopStart pair */
|
||||
loop_end = atol(strrchr(user_comment,'=')+1);
|
||||
}
|
||||
|
||||
else if (strstr(user_comment, "loops=") == user_comment) { /* The Legend of Heroes: Trails of Cold Steel III (Switch) */
|
||||
sscanf(strrchr(user_comment, '=') + 1, "%d-%d", &loop_start, &loop_end);
|
||||
loop_flag = 1;
|
||||
}
|
||||
|
||||
//;VGM_LOG("OggOpus: user_comment=%s\n", user_comment);
|
||||
offset += 0x04 + user_comment_size;
|
||||
|
|
|
@ -172,7 +172,7 @@ VGMSTREAM * init_vgmstream_opus_capcom(STREAMFILE *streamFile) {
|
|||
/* 0x30+: extra chunks (0x00: 0x7f, 0x04: num_sample), alt loop starts/regions? */
|
||||
|
||||
if (channel_count == 6) {
|
||||
/* 2ch multistream hacky-hacks, don't try this at home. We'll end up with:
|
||||
/* 2ch multistream hacky-hacks in RE:RE, don't try this at home. We'll end up with:
|
||||
* main vgmstream > N vgmstream layers > substream IO deinterleaver > opus meta > Opus IO transmogrifier (phew) */
|
||||
layered_layout_data* data = NULL;
|
||||
int layers = channel_count / 2;
|
||||
|
@ -193,11 +193,11 @@ VGMSTREAM * init_vgmstream_opus_capcom(STREAMFILE *streamFile) {
|
|||
|
||||
/* open each layer subfile */
|
||||
for (i = 0; i < layers; i++) {
|
||||
STREAMFILE* temp_streamFile = setup_opus_interleave_streamfile(streamFile, offset+0x28*i, layers);
|
||||
if (!temp_streamFile) goto fail;
|
||||
STREAMFILE* temp_sf = setup_opus_interleave_streamfile(streamFile, offset, i, layers);
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
data->layers[i] = init_vgmstream_opus(temp_streamFile, meta_OPUS, 0x00, num_samples,loop_start,loop_end);
|
||||
close_streamfile(temp_streamFile);
|
||||
data->layers[i] = init_vgmstream_opus(temp_sf, meta_OPUS, 0x00, num_samples,loop_start,loop_end);
|
||||
close_streamfile(temp_sf);
|
||||
if (!data->layers[i]) goto fail;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,136 +1,47 @@
|
|||
#ifndef _OPUS_INTERLEAVE_STREAMFILE_H_
|
||||
#define _OPUS_INTERLEAVE_STREAMFILE_H_
|
||||
#include "../streamfile.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
/* config */
|
||||
int streams;
|
||||
off_t stream_offset;
|
||||
|
||||
/* state */
|
||||
off_t logical_offset; /* offset that corresponds to physical_offset */
|
||||
off_t physical_offset; /* actual file offset */
|
||||
int skip_frames; /* frames to skip from other streams at points */
|
||||
|
||||
size_t logical_size;
|
||||
} opus_interleave_io_data;
|
||||
|
||||
|
||||
/* Reads skipping other streams */
|
||||
static size_t opus_interleave_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, opus_interleave_io_data* data) {
|
||||
size_t total_read = 0;
|
||||
|
||||
/* ignore bad reads */
|
||||
if (offset < 0 || offset > data->logical_size) {
|
||||
return total_read;
|
||||
}
|
||||
|
||||
/* previous offset: re-start as we can't map logical<>physical offsets (may be VBR) */
|
||||
if (offset < data->logical_offset) {
|
||||
data->physical_offset = data->stream_offset;
|
||||
data->logical_offset = 0x00;
|
||||
data->skip_frames = 0;
|
||||
}
|
||||
|
||||
/* read doing one frame at a time */
|
||||
while (length > 0) {
|
||||
size_t data_size;
|
||||
|
||||
/* ignore EOF */
|
||||
if (data->logical_offset >= data->logical_size) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* process block (must be read every time since skip frame sizes may vary) */
|
||||
{
|
||||
data_size = read_32bitBE(data->physical_offset,streamfile);
|
||||
if ((uint32_t)data_size == 0x01000080) //todo not ok if offset between 0 and header_size
|
||||
data_size = read_32bitLE(data->physical_offset+0x10,streamfile) + 0x08;
|
||||
else
|
||||
data_size += 0x08;
|
||||
}
|
||||
|
||||
/* skip frames from other streams */
|
||||
if (data->skip_frames) {
|
||||
data->physical_offset += data_size;
|
||||
data->skip_frames--;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* move to next block */
|
||||
if (offset >= data->logical_offset + data_size) {
|
||||
data->physical_offset += data_size;
|
||||
data->logical_offset += data_size;
|
||||
data->skip_frames = data->streams - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* read data */
|
||||
{
|
||||
size_t bytes_consumed, bytes_done, to_read;
|
||||
|
||||
bytes_consumed = offset - data->logical_offset;
|
||||
to_read = data_size - bytes_consumed;
|
||||
if (to_read > length)
|
||||
to_read = length;
|
||||
bytes_done = read_streamfile(dest, data->physical_offset + bytes_consumed, to_read, streamfile);
|
||||
|
||||
offset += bytes_done;
|
||||
total_read += bytes_done;
|
||||
length -= bytes_done;
|
||||
dest += bytes_done;
|
||||
|
||||
if (bytes_done != to_read || bytes_done == 0) {
|
||||
break; /* error/EOF */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return total_read;
|
||||
}
|
||||
|
||||
static size_t opus_interleave_io_size(STREAMFILE *streamfile, opus_interleave_io_data* data) {
|
||||
off_t info_offset;
|
||||
|
||||
if (data->logical_size)
|
||||
return data->logical_size;
|
||||
|
||||
info_offset = read_32bitLE(data->stream_offset+0x10,streamfile);
|
||||
data->logical_size = (0x08+info_offset) + read_32bitLE(data->stream_offset+info_offset+0x04,streamfile);
|
||||
return data->logical_size;
|
||||
}
|
||||
|
||||
|
||||
/* Prepares custom IO for multistream, interleaves 1 packet per stream */
|
||||
static STREAMFILE* setup_opus_interleave_streamfile(STREAMFILE *streamFile, off_t start_offset, int streams) {
|
||||
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
|
||||
opus_interleave_io_data io_data = {0};
|
||||
size_t io_data_size = sizeof(opus_interleave_io_data);
|
||||
|
||||
io_data.stream_offset = start_offset;
|
||||
io_data.streams = streams;
|
||||
io_data.physical_offset = start_offset;
|
||||
io_data.logical_size = opus_interleave_io_size(streamFile, &io_data); /* force init */
|
||||
|
||||
/* setup subfile */
|
||||
new_streamFile = open_wrap_streamfile(streamFile);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, opus_interleave_io_read,opus_interleave_io_size);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
new_streamFile = open_buffer_streamfile(new_streamFile,0);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
return temp_streamFile;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_streamFile);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* _OPUS_INTERLEAVE_STREAMFILE_H_ */
|
||||
#ifndef _OPUS_INTERLEAVE_STREAMFILE_H_
|
||||
#define _OPUS_INTERLEAVE_STREAMFILE_H_
|
||||
#include "deblock_streamfile.h"
|
||||
|
||||
|
||||
static void block_callback(STREAMFILE* sf, deblock_io_data* data) {
|
||||
uint32_t chunk_size = read_u32be(data->physical_offset, sf);
|
||||
|
||||
//todo not ok if offset between 0 and header_size
|
||||
if (chunk_size == 0x01000080) /* header */
|
||||
chunk_size = read_u32le(data->physical_offset + 0x10, sf) + 0x08;
|
||||
else
|
||||
chunk_size += 0x08;
|
||||
data->block_size = chunk_size;
|
||||
data->data_size = data->block_size;
|
||||
}
|
||||
|
||||
/* Deblocks NXOPUS streams that interleave 1 packet per stream */
|
||||
static STREAMFILE* setup_opus_interleave_streamfile(STREAMFILE* sf, off_t start_offset, int stream_number, int stream_count) {
|
||||
STREAMFILE* new_sf = NULL;
|
||||
deblock_config_t cfg = {0};
|
||||
|
||||
cfg.stream_start = start_offset;
|
||||
cfg.step_start = stream_number;
|
||||
cfg.step_count = stream_count;
|
||||
{
|
||||
int i;
|
||||
off_t offset = start_offset;
|
||||
/* read full size from NXOPUS header N */
|
||||
for (i = 0; i < stream_number + 1; i++) {
|
||||
off_t start = read_s32le(offset + 0x10, sf);
|
||||
off_t size = read_s32le(offset + start + 0x04, sf);
|
||||
|
||||
if (i == stream_number)
|
||||
cfg.logical_size = 0x08 + start + size;
|
||||
offset += 0x08 + start;
|
||||
}
|
||||
}
|
||||
cfg.block_callback = block_callback;
|
||||
|
||||
new_sf = open_wrap_streamfile(sf);
|
||||
new_sf = open_io_deblock_streamfile_f(new_sf, &cfg);
|
||||
//new_sf = open_buffer_streamfile_f(new_sf, 0);
|
||||
return new_sf;
|
||||
}
|
||||
|
||||
#endif /* _OPUS_INTERLEAVE_STREAMFILE_H_ */
|
||||
|
|
|
@ -509,20 +509,21 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
|
|||
}
|
||||
break;
|
||||
|
||||
case 0x4E584246: /* "NXBF" (Namco NUS v1) [R:Racing Evolution (Xbox)] */
|
||||
/* 0x00: "NXBF" again */
|
||||
/* 0x04: always 0x1000? */
|
||||
case 0x4E584246: /* "NXBF" (Namco NuSound v1) [R:Racing Evolution (Xbox)] */
|
||||
/* very similar to NUS's NPSF, but not quite like Cstr */
|
||||
/* 0x00: "NXBF" id */
|
||||
/* 0x04: version? (0x00001000 = 1.00?) */
|
||||
/* 0x08: data size */
|
||||
/* 0x0c: channels */
|
||||
/* 0x10: null */
|
||||
loop_start_nxbf = read_32bitLE(current_chunk + 0x08 + 0x14, streamFile);
|
||||
/* 0x18: sample rate */
|
||||
/* 0x1c: volume? */
|
||||
/* 0x1c: volume? (0x3e8 = 1000 = max) */
|
||||
/* 0x20: type/flags? */
|
||||
/* 0x24: codec? */
|
||||
/* 0x24: flag? */
|
||||
/* 0x28: null */
|
||||
/* 0x2c: null */
|
||||
/* 0x30: type/flags? */
|
||||
/* 0x30: always 0x40 */
|
||||
loop_flag = (loop_start_nxbf >= 0);
|
||||
break;
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,86 +1,67 @@
|
|||
#ifndef _SQEX_SEAD_STREAMFILE_H_
|
||||
#define _SQEX_SEAD_STREAMFILE_H_
|
||||
#include "../streamfile.h"
|
||||
|
||||
static STREAMFILE* setup_sqex_sead_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, int encryption, size_t header_size, size_t key_start);
|
||||
|
||||
|
||||
typedef struct {
|
||||
size_t header_size;
|
||||
size_t key_start;
|
||||
} sqex_sead_decryption_data;
|
||||
|
||||
|
||||
static size_t sqex_sead_decryption_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, sqex_sead_decryption_data* data) {
|
||||
/* Found in FFXII_TZA.exe (same key in SCD Ogg V3) */
|
||||
static const uint8_t encryption_key[0x100] = {
|
||||
0x3A,0x32,0x32,0x32,0x03,0x7E,0x12,0xF7,0xB2,0xE2,0xA2,0x67,0x32,0x32,0x22,0x32, // 00-0F
|
||||
0x32,0x52,0x16,0x1B,0x3C,0xA1,0x54,0x7B,0x1B,0x97,0xA6,0x93,0x1A,0x4B,0xAA,0xA6, // 10-1F
|
||||
0x7A,0x7B,0x1B,0x97,0xA6,0xF7,0x02,0xBB,0xAA,0xA6,0xBB,0xF7,0x2A,0x51,0xBE,0x03, // 20-2F
|
||||
0xF4,0x2A,0x51,0xBE,0x03,0xF4,0x2A,0x51,0xBE,0x12,0x06,0x56,0x27,0x32,0x32,0x36, // 30-3F
|
||||
0x32,0xB2,0x1A,0x3B,0xBC,0x91,0xD4,0x7B,0x58,0xFC,0x0B,0x55,0x2A,0x15,0xBC,0x40, // 40-4F
|
||||
0x92,0x0B,0x5B,0x7C,0x0A,0x95,0x12,0x35,0xB8,0x63,0xD2,0x0B,0x3B,0xF0,0xC7,0x14, // 50-5F
|
||||
0x51,0x5C,0x94,0x86,0x94,0x59,0x5C,0xFC,0x1B,0x17,0x3A,0x3F,0x6B,0x37,0x32,0x32, // 60-6F
|
||||
0x30,0x32,0x72,0x7A,0x13,0xB7,0x26,0x60,0x7A,0x13,0xB7,0x26,0x50,0xBA,0x13,0xB4, // 70-7F
|
||||
0x2A,0x50,0xBA,0x13,0xB5,0x2E,0x40,0xFA,0x13,0x95,0xAE,0x40,0x38,0x18,0x9A,0x92, // 80-8F
|
||||
0xB0,0x38,0x00,0xFA,0x12,0xB1,0x7E,0x00,0xDB,0x96,0xA1,0x7C,0x08,0xDB,0x9A,0x91, // 90-9F
|
||||
0xBC,0x08,0xD8,0x1A,0x86,0xE2,0x70,0x39,0x1F,0x86,0xE0,0x78,0x7E,0x03,0xE7,0x64, // A0-AF
|
||||
0x51,0x9C,0x8F,0x34,0x6F,0x4E,0x41,0xFC,0x0B,0xD5,0xAE,0x41,0xFC,0x0B,0xD5,0xAE, // B0-BF
|
||||
0x41,0xFC,0x3B,0x70,0x71,0x64,0x33,0x32,0x12,0x32,0x32,0x36,0x70,0x34,0x2B,0x56, // C0-CF
|
||||
0x22,0x70,0x3A,0x13,0xB7,0x26,0x60,0xBA,0x1B,0x94,0xAA,0x40,0x38,0x00,0xFA,0xB2, // D0-DF
|
||||
0xE2,0xA2,0x67,0x32,0x32,0x12,0x32,0xB2,0x32,0x32,0x32,0x32,0x75,0xA3,0x26,0x7B, // E0-EF
|
||||
0x83,0x26,0xF9,0x83,0x2E,0xFF,0xE3,0x16,0x7D,0xC0,0x1E,0x63,0x21,0x07,0xE3,0x01, // F0-FF
|
||||
};
|
||||
size_t bytes_read;
|
||||
off_t encrypted_offset = data->header_size;
|
||||
int i;
|
||||
|
||||
bytes_read = streamfile->read(streamfile, dest, offset, length);
|
||||
|
||||
/* decrypt data (xor) */
|
||||
if (offset >= encrypted_offset) {
|
||||
for (i = 0; i < bytes_read; i++) {
|
||||
dest[i] ^= encryption_key[(data->key_start + (offset - encrypted_offset) + i) % 0x100];
|
||||
}
|
||||
}
|
||||
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
/* decrypts subfile if neccessary */
|
||||
static STREAMFILE* setup_sqex_sead_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, int encryption, size_t header_size, size_t key_start) {
|
||||
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
|
||||
|
||||
/* setup subfile */
|
||||
new_streamFile = open_wrap_streamfile(streamFile);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
if (encryption) {
|
||||
sqex_sead_decryption_data io_data = {0};
|
||||
size_t io_data_size = sizeof(sqex_sead_decryption_data);
|
||||
|
||||
io_data.header_size = header_size;
|
||||
io_data.key_start = key_start;
|
||||
|
||||
new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, sqex_sead_decryption_read,NULL);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
}
|
||||
|
||||
new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,"hca");
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
return temp_streamFile;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_streamFile);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* _SQEX_SEAD_STREAMFILE_H_ */
|
||||
#ifndef _SQEX_SEAD_STREAMFILE_H_
|
||||
#define _SQEX_SEAD_STREAMFILE_H_
|
||||
#include "../streamfile.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
size_t start;
|
||||
size_t key_start;
|
||||
} sqex_sead_io_data;
|
||||
|
||||
|
||||
static size_t sqex_sead_io_read(STREAMFILE *sf, uint8_t *dest, off_t offset, size_t length, sqex_sead_io_data* data) {
|
||||
/* Found in FFXII_TZA.exe (same key in SCD Ogg V3) */
|
||||
static const uint8_t key[0x100] = {
|
||||
0x3A,0x32,0x32,0x32,0x03,0x7E,0x12,0xF7,0xB2,0xE2,0xA2,0x67,0x32,0x32,0x22,0x32, // 00-0F
|
||||
0x32,0x52,0x16,0x1B,0x3C,0xA1,0x54,0x7B,0x1B,0x97,0xA6,0x93,0x1A,0x4B,0xAA,0xA6, // 10-1F
|
||||
0x7A,0x7B,0x1B,0x97,0xA6,0xF7,0x02,0xBB,0xAA,0xA6,0xBB,0xF7,0x2A,0x51,0xBE,0x03, // 20-2F
|
||||
0xF4,0x2A,0x51,0xBE,0x03,0xF4,0x2A,0x51,0xBE,0x12,0x06,0x56,0x27,0x32,0x32,0x36, // 30-3F
|
||||
0x32,0xB2,0x1A,0x3B,0xBC,0x91,0xD4,0x7B,0x58,0xFC,0x0B,0x55,0x2A,0x15,0xBC,0x40, // 40-4F
|
||||
0x92,0x0B,0x5B,0x7C,0x0A,0x95,0x12,0x35,0xB8,0x63,0xD2,0x0B,0x3B,0xF0,0xC7,0x14, // 50-5F
|
||||
0x51,0x5C,0x94,0x86,0x94,0x59,0x5C,0xFC,0x1B,0x17,0x3A,0x3F,0x6B,0x37,0x32,0x32, // 60-6F
|
||||
0x30,0x32,0x72,0x7A,0x13,0xB7,0x26,0x60,0x7A,0x13,0xB7,0x26,0x50,0xBA,0x13,0xB4, // 70-7F
|
||||
0x2A,0x50,0xBA,0x13,0xB5,0x2E,0x40,0xFA,0x13,0x95,0xAE,0x40,0x38,0x18,0x9A,0x92, // 80-8F
|
||||
0xB0,0x38,0x00,0xFA,0x12,0xB1,0x7E,0x00,0xDB,0x96,0xA1,0x7C,0x08,0xDB,0x9A,0x91, // 90-9F
|
||||
0xBC,0x08,0xD8,0x1A,0x86,0xE2,0x70,0x39,0x1F,0x86,0xE0,0x78,0x7E,0x03,0xE7,0x64, // A0-AF
|
||||
0x51,0x9C,0x8F,0x34,0x6F,0x4E,0x41,0xFC,0x0B,0xD5,0xAE,0x41,0xFC,0x0B,0xD5,0xAE, // B0-BF
|
||||
0x41,0xFC,0x3B,0x70,0x71,0x64,0x33,0x32,0x12,0x32,0x32,0x36,0x70,0x34,0x2B,0x56, // C0-CF
|
||||
0x22,0x70,0x3A,0x13,0xB7,0x26,0x60,0xBA,0x1B,0x94,0xAA,0x40,0x38,0x00,0xFA,0xB2, // D0-DF
|
||||
0xE2,0xA2,0x67,0x32,0x32,0x12,0x32,0xB2,0x32,0x32,0x32,0x32,0x75,0xA3,0x26,0x7B, // E0-EF
|
||||
0x83,0x26,0xF9,0x83,0x2E,0xFF,0xE3,0x16,0x7D,0xC0,0x1E,0x63,0x21,0x07,0xE3,0x01, // F0-FF
|
||||
};
|
||||
int i;
|
||||
size_t bytes = read_streamfile(dest, offset, length, sf);
|
||||
|
||||
/* decrypt data (xor) */
|
||||
//if (offset >= data->start) {
|
||||
for (i = 0; i < bytes; i++) {
|
||||
if (offset + i >= data->start) { //todo recheck
|
||||
dest[i] ^= key[(data->key_start + (offset - data->start) + i) % sizeof(key)];
|
||||
}
|
||||
}
|
||||
//}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/* decrypts subfile if needed */
|
||||
static STREAMFILE* setup_sqex_sead_streamfile(STREAMFILE* sf, off_t subfile_offset, size_t subfile_size, int encryption, size_t header_size, size_t key_start) {
|
||||
STREAMFILE* new_sf = NULL;
|
||||
|
||||
/* setup sf */
|
||||
new_sf = open_wrap_streamfile(sf);
|
||||
new_sf = open_clamp_streamfile_f(new_sf, subfile_offset, subfile_size);
|
||||
if (encryption) {
|
||||
sqex_sead_io_data io_data = {0};
|
||||
|
||||
io_data.start = header_size;
|
||||
io_data.key_start = key_start;
|
||||
|
||||
new_sf = open_io_streamfile_f(new_sf, &io_data, sizeof(sqex_sead_io_data), sqex_sead_io_read, NULL);
|
||||
}
|
||||
|
||||
new_sf = open_fakename_streamfile_f(new_sf, NULL, "hca");
|
||||
return new_sf;
|
||||
}
|
||||
|
||||
#endif /* _SQEX_SEAD_STREAMFILE_H_ */
|
||||
|
|
|
@ -36,7 +36,8 @@ typedef enum {
|
|||
PCM4_U = 26, /* 4-bit unsigned PCM (3rd and 4th gen games) */
|
||||
OKI16 = 27, /* OKI ADPCM with 16-bit output (unlike OKI/VOX/Dialogic ADPCM's 12-bit) */
|
||||
AAC = 28, /* Advanced Audio Coding (raw without .mp4) */
|
||||
TGC = 29 /* Tiger Game.com 4-bit ADPCM */
|
||||
TGC = 29, /* Tiger Game.com 4-bit ADPCM */
|
||||
ASF = 30, /* Argonaut ASF 4-bit ADPCM */
|
||||
} txth_type;
|
||||
|
||||
typedef struct {
|
||||
|
@ -223,6 +224,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
|
|||
case PCM4_U: coding = coding_PCM4_U; break;
|
||||
case OKI16: coding = coding_OKI16; break;
|
||||
case TGC: coding = coding_TGC; break;
|
||||
case ASF: coding = coding_ASF; break;
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
@ -334,6 +336,11 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
|
|||
vgmstream->layout_type = layout_none;
|
||||
break;
|
||||
|
||||
case coding_ASF:
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x11;
|
||||
break;
|
||||
|
||||
case coding_MS_IMA:
|
||||
if (!txth.interleave) goto fail; /* creates garbage */
|
||||
|
||||
|
@ -450,7 +457,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
|
|||
}
|
||||
else {
|
||||
/* fake header FFmpeg */
|
||||
uint8_t buf[200];
|
||||
uint8_t buf[0x100];
|
||||
int32_t bytes;
|
||||
|
||||
if (txth.codec == ATRAC3) {
|
||||
|
@ -465,14 +472,14 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
|
|||
else if (txth.codec == ATRAC3PLUS) {
|
||||
int block_size = txth.interleave;
|
||||
|
||||
bytes = ffmpeg_make_riff_atrac3plus(buf, 200, vgmstream->num_samples, txth.data_size, vgmstream->channels, vgmstream->sample_rate, block_size, txth.skip_samples);
|
||||
bytes = ffmpeg_make_riff_atrac3plus(buf, sizeof(buf), vgmstream->num_samples, txth.data_size, vgmstream->channels, vgmstream->sample_rate, block_size, txth.skip_samples);
|
||||
ffmpeg_data = init_ffmpeg_header_offset(txth.streamBody, buf,bytes, txth.start_offset,txth.data_size);
|
||||
if ( !ffmpeg_data ) goto fail;
|
||||
}
|
||||
else if (txth.codec == XMA1) {
|
||||
int xma_stream_mode = txth.codec_mode == 1 ? 1 : 0;
|
||||
|
||||
bytes = ffmpeg_make_riff_xma1(buf, 100, vgmstream->num_samples, txth.data_size, vgmstream->channels, vgmstream->sample_rate, xma_stream_mode);
|
||||
bytes = ffmpeg_make_riff_xma1(buf, sizeof(buf), vgmstream->num_samples, txth.data_size, vgmstream->channels, vgmstream->sample_rate, xma_stream_mode);
|
||||
ffmpeg_data = init_ffmpeg_header_offset(txth.streamBody, buf,bytes, txth.start_offset,txth.data_size);
|
||||
if ( !ffmpeg_data ) goto fail;
|
||||
}
|
||||
|
@ -482,7 +489,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
|
|||
block_size = txth.interleave ? txth.interleave : 2048;
|
||||
block_count = txth.data_size / block_size;
|
||||
|
||||
bytes = ffmpeg_make_riff_xma2(buf, 200, vgmstream->num_samples, txth.data_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size);
|
||||
bytes = ffmpeg_make_riff_xma2(buf, sizeof(buf), vgmstream->num_samples, txth.data_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size);
|
||||
ffmpeg_data = init_ffmpeg_header_offset(txth.streamBody, buf,bytes, txth.start_offset,txth.data_size);
|
||||
if ( !ffmpeg_data ) goto fail;
|
||||
}
|
||||
|
@ -848,6 +855,7 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char
|
|||
else if (is_string(val,"AAC")) txth->codec = AAC;
|
||||
else if (is_string(val,"TGC")) txth->codec = TGC;
|
||||
else if (is_string(val,"GCOM_ADPCM")) txth->codec = TGC;
|
||||
else if (is_string(val,"ASF")) txth->codec = ASF;
|
||||
else goto fail;
|
||||
|
||||
/* set common interleaves to simplify usage
|
||||
|
@ -1063,9 +1071,10 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char
|
|||
/* COEFS */
|
||||
else if (is_string(key,"coef_offset")) {
|
||||
if (!parse_num(txth->streamHead,txth,val, &txth->coef_offset)) goto fail;
|
||||
/* special adjustment */
|
||||
/* special adjustments */
|
||||
txth->coef_offset += txth->base_offset;
|
||||
if (txth->subsong_offset)
|
||||
txth->coef_offset = txth->base_offset + txth->coef_offset + txth->subsong_offset * (txth->target_subsong - 1);
|
||||
txth->coef_offset += txth->subsong_offset * (txth->target_subsong - 1);
|
||||
}
|
||||
else if (is_string(key,"coef_spacing")) {
|
||||
if (!parse_num(txth->streamHead,txth,val, &txth->coef_spacing)) goto fail;
|
||||
|
@ -1090,8 +1099,9 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char
|
|||
if (!parse_num(txth->streamHead,txth,val, &txth->hist_offset)) goto fail;
|
||||
txth->hist_set = 1;
|
||||
/* special adjustment */
|
||||
txth->hist_offset += txth->hist_offset;
|
||||
if (txth->subsong_offset)
|
||||
txth->hist_offset = txth->base_offset + txth->hist_offset + txth->subsong_offset * (txth->target_subsong - 1);
|
||||
txth->hist_offset += txth->subsong_offset * (txth->target_subsong - 1);
|
||||
}
|
||||
else if (is_string(key,"hist_spacing")) {
|
||||
if (!parse_num(txth->streamHead,txth,val, &txth->hist_spacing)) goto fail;
|
||||
|
@ -1115,8 +1125,9 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char
|
|||
if (!parse_num(txth->streamHead,txth,val, &txth->name_offset)) goto fail;
|
||||
txth->name_offset_set = 1;
|
||||
/* special adjustment */
|
||||
txth->name_offset += txth->base_offset;
|
||||
if (txth->subsong_offset)
|
||||
txth->name_offset = txth->base_offset + txth->name_offset + txth->subsong_offset * (txth->target_subsong - 1);
|
||||
txth->name_offset += txth->subsong_offset * (txth->target_subsong - 1);
|
||||
}
|
||||
else if (is_string(key,"name_size")) {
|
||||
if (!parse_num(txth->streamHead,txth,val, &txth->name_size)) goto fail;
|
||||
|
@ -1314,6 +1325,8 @@ static uint16_t get_string_wchar(const char * val, int pos, int *csize) {
|
|||
|
||||
if (wchar >= 0x41 && wchar <= 0x5a)
|
||||
wchar += 0x20;
|
||||
if (wchar == '\\')
|
||||
wchar = '/'; /* normalize paths */
|
||||
}
|
||||
|
||||
return wchar;
|
||||
|
@ -1408,11 +1421,12 @@ fail:
|
|||
static int parse_name_table(txth_header * txth, char * name_list) {
|
||||
STREAMFILE *nameFile = NULL;
|
||||
off_t txt_offset, file_size;
|
||||
char fullname[PATH_LIMIT];
|
||||
char filename[PATH_LIMIT];
|
||||
char basename[PATH_LIMIT];
|
||||
|
||||
/* just in case */
|
||||
if (txth->streamfile_is_txth || !txth->streamText || !txth->streamFile)
|
||||
if (!txth->streamText || !txth->streamBody)
|
||||
goto fail;
|
||||
|
||||
/* trim name_list just in case */
|
||||
|
@ -1426,15 +1440,16 @@ static int parse_name_table(txth_header * txth, char * name_list) {
|
|||
}
|
||||
}
|
||||
|
||||
//;VGM_LOG("TXTH: name_list2='%s'\n", name_list);
|
||||
//;VGM_LOG("TXTH: name_list='%s'\n", name_list);
|
||||
|
||||
/* open companion file near .txth */
|
||||
nameFile = open_streamfile_by_filename(txth->streamText, name_list);
|
||||
if (!nameFile) goto fail;
|
||||
|
||||
get_streamfile_filename(txth->streamFile, filename, sizeof(filename));
|
||||
get_streamfile_basename(txth->streamFile, basename, sizeof(basename));
|
||||
//;VGM_LOG("TXTH: filename=%s, basename=%s\n", filename, basename);
|
||||
get_streamfile_name(txth->streamBody, fullname, sizeof(filename));
|
||||
get_streamfile_filename(txth->streamBody, filename, sizeof(filename));
|
||||
get_streamfile_basename(txth->streamBody, basename, sizeof(basename));
|
||||
//;VGM_LOG("TXTH: names full=%s, file=%s, base=%s\n", fullname, filename, basename);
|
||||
|
||||
txt_offset = 0x00;
|
||||
file_size = get_streamfile_size(nameFile);
|
||||
|
@ -1477,7 +1492,10 @@ static int parse_name_table(txth_header * txth, char * name_list) {
|
|||
|
||||
//;VGM_LOG("TXTH: compare name '%s'\n", key);
|
||||
/* parse values if key (name) matches default ("") or filename with/without extension */
|
||||
if (key[0]=='\0' || is_string_match(filename, key) || is_string_match(basename, key)) {
|
||||
if (key[0]=='\0'
|
||||
|| is_string_match(filename, key)
|
||||
|| is_string_match(basename, key)
|
||||
|| is_string_match(fullname, key)) {
|
||||
int n;
|
||||
char subval[TXT_LINE_MAX];
|
||||
const char *current = val;
|
||||
|
@ -1615,7 +1633,8 @@ static int parse_num(STREAMFILE * streamFile, txth_header * txth, const char * v
|
|||
else if ((n = is_string_field(val,"subsong_count"))) value = txth->subsong_count;
|
||||
else if ((n = is_string_field(val,"subsong_offset"))) value = txth->subsong_offset;
|
||||
else if ((n = is_string_field(val,"subfile_offset"))) value = txth->subfile_offset;
|
||||
else if ((n = is_string_field(val,"subfile_size"))) value = txth->subfile_size;
|
||||
else if ((n = is_string_field(val,"subfile_size"))) value = txth->subfile_size;
|
||||
else if ((n = is_string_field(val,"base_offset"))) value = txth->base_offset;
|
||||
//todo whatever, improve
|
||||
else if ((n = is_string_field(val,"name_value"))) value = txth->name_values[0];
|
||||
else if ((n = is_string_field(val,"name_value1"))) value = txth->name_values[0];
|
||||
|
@ -1719,6 +1738,8 @@ static int get_bytes_to_samples(txth_header * txth, uint32_t bytes) {
|
|||
#endif
|
||||
case AC3:
|
||||
return ac3_bytes_to_samples(bytes, txth->interleave, txth->channels);
|
||||
case ASF:
|
||||
return asf_bytes_to_samples(bytes, txth->channels);
|
||||
|
||||
/* XMA bytes-to-samples is done at the end as the value meanings are a bit different */
|
||||
case XMA1:
|
||||
|
|
|
@ -2,39 +2,107 @@
|
|||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
/* CKD RIFF - Ubisoft audio [Rayman Origins (Wii)] */
|
||||
VGMSTREAM * init_vgmstream_ubi_ckd(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset, first_offset = 0xc, chunk_offset;
|
||||
typedef enum { MSADPCM, DSP, MP3, XMA2 } ckd_codec;
|
||||
|
||||
/* CKD RIFF - UbiArt Framework (v1) audio [Rayman Origins (Wii/X360/PS3/PC)] */
|
||||
VGMSTREAM* init_vgmstream_ubi_ckd(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset, first_offset = 0x0c, chunk_offset;
|
||||
size_t chunk_size, data_size;
|
||||
int loop_flag, channel_count, interleave;
|
||||
int loop_flag, channel_count, interleave = 0, format;
|
||||
ckd_codec codec;
|
||||
int big_endian;
|
||||
uint32_t (*read_u32)(off_t,STREAMFILE*);
|
||||
uint16_t (*read_u16)(off_t,STREAMFILE*);
|
||||
|
||||
|
||||
/* check extension, case insensitive */
|
||||
if (!check_extensions(streamFile,"ckd")) goto fail;
|
||||
|
||||
/* check header */
|
||||
if (read_32bitBE(0x0,streamFile) != 0x52494646) /* RIFF */
|
||||
goto fail;
|
||||
if (read_32bitBE(0x26,streamFile) != 0x6473704C) /* dspL */
|
||||
/* checks */
|
||||
/* .wav.ckd: main (other files are called .xxx.ckd too) */
|
||||
if (!check_extensions(sf,"ckd"))
|
||||
goto fail;
|
||||
|
||||
/* another slighly funny RIFF */
|
||||
if (read_u32be(0x00,sf) != 0x52494646) /* "RIFF" */
|
||||
goto fail;
|
||||
if (read_u32be(0x08,sf) != 0x57415645) /* "WAVE" */
|
||||
goto fail;
|
||||
/* RIFF size is normal */
|
||||
|
||||
big_endian = guess_endianness32bit(0x04, sf);
|
||||
read_u32 = big_endian ? read_u32be : read_u32le;
|
||||
read_u16 = big_endian ? read_u16be : read_u16le;
|
||||
|
||||
loop_flag = 0;
|
||||
channel_count = read_16bitBE(0x16,streamFile);
|
||||
|
||||
/* find data chunk, in 3 variants */
|
||||
if (find_chunk_be(streamFile, 0x64617453,first_offset,0, &chunk_offset,&chunk_size)) { /*"datS"*/
|
||||
/* normal interleave */
|
||||
start_offset = chunk_offset;
|
||||
data_size = chunk_size;
|
||||
interleave = 8;
|
||||
} else if (find_chunk_be(streamFile, 0x6461744C,first_offset,0, &chunk_offset,&chunk_size)) { /*"datL"*/
|
||||
/* mono or full interleave (with a "datR" after the "datL", no check as we can just pretend it exists) */
|
||||
start_offset = chunk_offset;
|
||||
data_size = chunk_size * channel_count;
|
||||
interleave = (4+4) + chunk_size; /* don't forget to skip the "datR"+size chunk */
|
||||
} else {
|
||||
goto fail;
|
||||
format = read_u16(0x14,sf);
|
||||
channel_count = read_u16(0x16,sf);
|
||||
|
||||
switch(format) {
|
||||
case 0x0002:
|
||||
if (big_endian) {
|
||||
if (read_u32(0x26,sf) != 0x6473704C) /* "dspL" */
|
||||
goto fail;
|
||||
|
||||
/* find data chunk, in 2 variants */
|
||||
if (find_chunk_be(sf, 0x64617453,first_offset,0, &chunk_offset,&chunk_size)) { /* "datS" */
|
||||
/* normal interleave */
|
||||
start_offset = chunk_offset;
|
||||
data_size = chunk_size;
|
||||
interleave = 0x08;
|
||||
} else if (find_chunk_be(sf, 0x6461744C,first_offset,0, &chunk_offset,&chunk_size)) { /* "datL" */
|
||||
/* mono "datL" or full interleave with a "datR" after the "datL" (no check, pretend it exists) */
|
||||
start_offset = chunk_offset;
|
||||
data_size = chunk_size * channel_count;
|
||||
interleave = (0x4+0x4) + chunk_size; /* don't forget to skip the "datR"+size chunk */
|
||||
} else {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
codec = DSP;
|
||||
}
|
||||
else {
|
||||
/* PC has MS-ADPCM, same as wav's except without "fact" (recommended by MS), kinda useless
|
||||
* but might as well have it here */
|
||||
if (find_chunk_le(sf, 0x64617461,first_offset,0, &chunk_offset,&chunk_size)) { /* "data" */
|
||||
start_offset = chunk_offset;
|
||||
data_size = chunk_size;
|
||||
} else {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
interleave = read_u16(0x20, sf);
|
||||
if (!msadpcm_check_coefs(sf, 0x28))
|
||||
goto fail;
|
||||
|
||||
/* there is also a "smpl" chunk with full loops too, but other codecs don't have it for the same tracks... */
|
||||
|
||||
codec = MSADPCM;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x0055:
|
||||
if (read_u32(0x26,sf) != 0x6D736620) /* "msf " */
|
||||
goto fail;
|
||||
start_offset = 0x26;
|
||||
data_size = read_u32(0x2A,sf);
|
||||
codec = MP3;
|
||||
break;
|
||||
|
||||
case 0x0166:
|
||||
if (read_u32(0x48,sf) != 0x7365656B && /* "seek */
|
||||
read_u32(0x48,sf) != 0x7365656B) /* "data" */
|
||||
goto fail;
|
||||
|
||||
if (find_chunk_be(sf, 0x64617461,first_offset,0, &chunk_offset,&chunk_size)) { /* "data" */
|
||||
start_offset = chunk_offset;
|
||||
data_size = chunk_size;
|
||||
} else {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
codec = XMA2;
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
|
@ -42,18 +110,59 @@ VGMSTREAM * init_vgmstream_ubi_ckd(STREAMFILE *streamFile) {
|
|||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = read_32bitBE(0x18,streamFile);
|
||||
vgmstream->sample_rate = read_u32(0x18,sf);
|
||||
vgmstream->num_samples = dsp_bytes_to_samples(data_size, channel_count);
|
||||
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = channel_count==1 ? layout_none : layout_interleave;
|
||||
vgmstream->interleave_block_size = interleave;
|
||||
vgmstream->meta_type = meta_UBI_CKD;
|
||||
|
||||
dsp_read_coefs_be(vgmstream,streamFile, 0x4A, (4+4)+0x60);
|
||||
switch(codec) {
|
||||
case MSADPCM:
|
||||
vgmstream->coding_type = coding_MSADPCM;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->frame_size = interleave;
|
||||
break;
|
||||
|
||||
case DSP:
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = interleave;
|
||||
dsp_read_coefs_be(vgmstream,sf, 0x4A, (4+4)+0x60);
|
||||
break;
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
#ifdef VGM_USE_MPEG
|
||||
case MP3: {
|
||||
vgmstream->codec_data = init_mpeg(sf, start_offset, &vgmstream->coding_type, vgmstream->channels);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->num_samples = mpeg_bytes_to_samples(data_size, vgmstream->codec_data);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case XMA2: {
|
||||
uint8_t buf[0x100];
|
||||
int bytes;
|
||||
|
||||
bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf, sizeof(buf), 0x14, 0x34, data_size, sf, 1);
|
||||
vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, start_offset,data_size);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->num_samples = read_u32(0x14+0x18,sf);
|
||||
|
||||
xma_fix_raw_samples(vgmstream, sf, start_offset,data_size, 0, 0,0); /* should apply to num_samples? */
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,sf,start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
#include "meta.h"
|
||||
#include "../layout/layout.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "ubi_lyn_ogg_streamfile.h"
|
||||
#include "ubi_lyn_streamfile.h"
|
||||
|
||||
static STREAMFILE* setup_lyn_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size);
|
||||
|
||||
/* LyN RIFF - from Ubisoft LyN engine games [Red Steel 2 (Wii), Adventures of Tintin (Multi), From Dust (Multi), Just Dance 3/4 (multi)] */
|
||||
VGMSTREAM * init_vgmstream_ubi_lyn(STREAMFILE *streamFile) {
|
||||
|
@ -111,7 +110,7 @@ VGMSTREAM * init_vgmstream_ubi_lyn(STREAMFILE *streamFile) {
|
|||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
case 0x3157: { /* Ogg (PC), interleaved 1ch */
|
||||
size_t interleave_size, stride_size;
|
||||
size_t interleave_size;
|
||||
layered_layout_data* data = NULL;
|
||||
int i;
|
||||
|
||||
|
@ -119,7 +118,6 @@ VGMSTREAM * init_vgmstream_ubi_lyn(STREAMFILE *streamFile) {
|
|||
goto fail;
|
||||
|
||||
interleave_size = read_32bitLE(start_offset+0x04,streamFile);
|
||||
stride_size = interleave_size * channel_count;
|
||||
/* interleave is adjusted so there is no smaller last block, it seems */
|
||||
|
||||
vgmstream->coding_type = coding_OGG_VORBIS;
|
||||
|
@ -132,15 +130,15 @@ VGMSTREAM * init_vgmstream_ubi_lyn(STREAMFILE *streamFile) {
|
|||
|
||||
/* open each layer subfile */
|
||||
for (i = 0; i < channel_count; i++) {
|
||||
STREAMFILE* temp_streamFile = NULL;
|
||||
size_t total_size = read_32bitLE(start_offset+0x08 + 0x04*i,streamFile);
|
||||
off_t layer_offset = start_offset+0x08 + 0x04*channel_count + interleave_size*i;
|
||||
STREAMFILE* temp_sf = NULL;
|
||||
size_t logical_size = read_32bitLE(start_offset+0x08 + 0x04*i,streamFile);
|
||||
off_t layer_offset = start_offset + 0x08 + 0x04*channel_count; //+ interleave_size*i;
|
||||
|
||||
temp_streamFile = setup_lyn_ogg_streamfile(streamFile, layer_offset, interleave_size, stride_size, total_size);
|
||||
if (!temp_streamFile) goto fail;
|
||||
temp_sf = setup_ubi_lyn_streamfile(streamFile, layer_offset, interleave_size, i, channel_count, logical_size);
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
data->layers[i] = init_vgmstream_ogg_vorbis(temp_streamFile);
|
||||
close_streamfile(temp_streamFile);
|
||||
data->layers[i] = init_vgmstream_ogg_vorbis(temp_sf);
|
||||
close_streamfile(temp_sf);
|
||||
if (!data->layers[i]) goto fail;
|
||||
|
||||
/* could validate between layers, meh */
|
||||
|
@ -193,10 +191,8 @@ VGMSTREAM * init_vgmstream_ubi_lyn(STREAMFILE *streamFile) {
|
|||
off_t chunk_offset;
|
||||
size_t chunk_size, seek_size;
|
||||
|
||||
if (read_32bitLE(start_offset+0x00,streamFile) != 3) /* id? */
|
||||
goto fail;
|
||||
|
||||
/* skip standard XMA header + seek table */
|
||||
/* 0x00: version? no apparent differences (0x1=Just Dance 4, 0x3=others) */
|
||||
chunk_offset = start_offset + 0x04 + 0x04;
|
||||
chunk_size = read_32bitLE(start_offset + 0x04, streamFile);
|
||||
seek_size = read_32bitLE(chunk_offset+chunk_size, streamFile);
|
||||
|
@ -231,7 +227,7 @@ fail:
|
|||
/* LyN RIFF in containers */
|
||||
VGMSTREAM * init_vgmstream_ubi_lyn_container(STREAMFILE *streamFile) {
|
||||
VGMSTREAM *vgmstream = NULL;
|
||||
STREAMFILE *temp_streamFile = NULL;
|
||||
STREAMFILE *temp_sf = NULL;
|
||||
off_t subfile_offset;
|
||||
size_t subfile_size;
|
||||
|
||||
|
@ -261,35 +257,16 @@ VGMSTREAM * init_vgmstream_ubi_lyn_container(STREAMFILE *streamFile) {
|
|||
|
||||
subfile_size = read_32bitLE(subfile_offset+0x04,streamFile) + 0x04+0x04;
|
||||
|
||||
temp_streamFile = setup_lyn_streamfile(streamFile, subfile_offset,subfile_size);
|
||||
if (!temp_streamFile) goto fail;
|
||||
temp_sf = setup_subfile_streamfile(streamFile, subfile_offset, subfile_size, NULL);
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
vgmstream = init_vgmstream_ubi_lyn(temp_streamFile);
|
||||
vgmstream = init_vgmstream_ubi_lyn(temp_sf);
|
||||
|
||||
close_streamfile(temp_streamFile);
|
||||
close_streamfile(temp_sf);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_streamFile);
|
||||
close_streamfile(temp_sf);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static STREAMFILE* setup_lyn_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size) {
|
||||
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
|
||||
|
||||
/* setup subfile */
|
||||
new_streamFile = open_wrap_streamfile(streamFile);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
return temp_streamFile;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_streamFile);
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -1,92 +0,0 @@
|
|||
#ifndef _LYN_OGG_STREAMFILE_H_
|
||||
#define _LYN_OGG_STREAMFILE_H_
|
||||
#include "../streamfile.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
off_t start_physical_offset; /* interleaved data start, for this substream */
|
||||
size_t interleave_block_size; /* max size that can be read before encountering other substreams */
|
||||
size_t stride_size; /* step size between interleave blocks (interleave*channels) */
|
||||
size_t total_size; /* final size of the deinterleaved substream */
|
||||
} lyn_ogg_io_data;
|
||||
|
||||
|
||||
/* Handles deinterleaving of complete files, skipping portions or other substreams. */
|
||||
static size_t lyn_ogg_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, lyn_ogg_io_data* data) {
|
||||
size_t total_read = 0;
|
||||
|
||||
while (length > 0) {
|
||||
size_t to_read;
|
||||
size_t length_available;
|
||||
off_t block_num;
|
||||
off_t intrablock_offset;
|
||||
off_t physical_offset;
|
||||
|
||||
block_num = offset / data->interleave_block_size;
|
||||
intrablock_offset = offset % data->interleave_block_size;
|
||||
physical_offset = data->start_physical_offset + block_num*data->stride_size + intrablock_offset;
|
||||
length_available = data->interleave_block_size - intrablock_offset;
|
||||
|
||||
if (length < length_available) {
|
||||
to_read = length;
|
||||
}
|
||||
else {
|
||||
to_read = length_available;
|
||||
}
|
||||
|
||||
if (to_read > 0) {
|
||||
size_t bytes_read;
|
||||
|
||||
bytes_read = read_streamfile(dest, physical_offset, to_read, streamfile);
|
||||
total_read += bytes_read;
|
||||
|
||||
if (bytes_read != to_read) {
|
||||
return total_read;
|
||||
}
|
||||
|
||||
dest += bytes_read;
|
||||
offset += bytes_read;
|
||||
length -= bytes_read;
|
||||
}
|
||||
}
|
||||
|
||||
return total_read;
|
||||
}
|
||||
|
||||
static size_t lyn_ogg_io_size(STREAMFILE *streamfile, lyn_ogg_io_data* data) {
|
||||
return data->total_size;
|
||||
}
|
||||
|
||||
|
||||
static STREAMFILE* setup_lyn_ogg_streamfile(STREAMFILE *streamFile, off_t start_offset, size_t interleave_block_size, size_t stride_size, size_t total_size) {
|
||||
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
|
||||
lyn_ogg_io_data io_data = {0};
|
||||
size_t io_data_size = sizeof(lyn_ogg_io_data);
|
||||
|
||||
io_data.start_physical_offset = start_offset;
|
||||
io_data.interleave_block_size = interleave_block_size;
|
||||
io_data.stride_size = stride_size;
|
||||
io_data.total_size = total_size;
|
||||
|
||||
|
||||
/* setup subfile */
|
||||
new_streamFile = open_wrap_streamfile(streamFile);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, lyn_ogg_io_read,lyn_ogg_io_size);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,"ogg");
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
return temp_streamFile;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_streamFile);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* _LYN_OGG_STREAMFILE_H_ */
|
|
@ -0,0 +1,23 @@
|
|||
#ifndef _UBI_LYN_STREAMFILE_H_
|
||||
#define _UBI_LYN_STREAMFILE_H_
|
||||
#include "deblock_streamfile.h"
|
||||
|
||||
/* Deinterleaves LYN streams */
|
||||
static STREAMFILE* setup_ubi_lyn_streamfile(STREAMFILE* sf, off_t stream_offset, size_t interleave_size, int stream_number, int stream_count, size_t logical_size) {
|
||||
STREAMFILE *new_sf = NULL;
|
||||
deblock_config_t cfg = {0};
|
||||
VGM_LOG("so=%lx, chu=%x, n=%i, c=%i, lo=%x\n", stream_offset, interleave_size, stream_number, stream_count, logical_size);
|
||||
cfg.stream_start = stream_offset;
|
||||
cfg.chunk_size = interleave_size;
|
||||
cfg.step_start = stream_number;
|
||||
cfg.step_count = stream_count;
|
||||
cfg.logical_size = logical_size;
|
||||
|
||||
new_sf = open_wrap_streamfile(sf);
|
||||
new_sf = open_io_deblock_streamfile_f(new_sf, &cfg);
|
||||
//new_sf = open_buffer_streamfile_f(new_sf, 0);
|
||||
new_sf = open_fakename_streamfile_f(new_sf, NULL, "ogg");
|
||||
return new_sf;
|
||||
}
|
||||
|
||||
#endif /* _UBI_LYN_STREAMFILE_H_ */
|
|
@ -7,7 +7,7 @@
|
|||
#define SB_MAX_LAYER_COUNT 16 /* arbitrary max */
|
||||
#define SB_MAX_CHAIN_COUNT 256 /* +150 exist in Tonic Trouble */
|
||||
|
||||
typedef enum { UBI_IMA, UBI_ADPCM, RAW_PCM, RAW_PSX, RAW_DSP, RAW_XBOX, FMT_VAG, FMT_AT3, RAW_AT3, FMT_XMA1, RAW_XMA1, FMT_OGG, FMT_CWAV, FMT_APM, FMT_MPDX } ubi_sb_codec;
|
||||
typedef enum { UBI_IMA, UBI_ADPCM, RAW_PCM, RAW_PSX, RAW_DSP, RAW_XBOX, FMT_VAG, FMT_AT3, RAW_AT3, FMT_XMA1, RAW_XMA1, FMT_OGG, FMT_CWAV, FMT_APM, FMT_MPDX, UBI_IMA_SCE } ubi_sb_codec;
|
||||
typedef enum { UBI_PC, UBI_PS2, UBI_XBOX, UBI_GC, UBI_X360, UBI_PSP, UBI_PS3, UBI_WII, UBI_3DS } ubi_sb_platform;
|
||||
typedef enum { UBI_NONE = 0, UBI_AUDIO, UBI_LAYER, UBI_SEQUENCE, UBI_SILENCE } ubi_sb_type;
|
||||
|
||||
|
@ -496,6 +496,16 @@ static VGMSTREAM * init_vgmstream_ubi_sb_base(ubi_sb_header *sb, STREAMFILE *str
|
|||
vgmstream->layout_type = layout_none;
|
||||
break;
|
||||
|
||||
case UBI_IMA_SCE:
|
||||
vgmstream->coding_type = coding_UBI_IMA;
|
||||
vgmstream->layout_type = layout_blocked_ubi_sce;
|
||||
vgmstream->full_block_size = read_32bitLE(0x18, streamData);
|
||||
|
||||
/* this "codec" is an ugly hack of IMA w/ Ubi ADPCM's frame format, surely to
|
||||
* shoehorn a simpler codec into the existing code when porting the game */
|
||||
start_offset += 0x08 + 0x30; /* skip Ubi ADPCM header */
|
||||
break;
|
||||
|
||||
case UBI_ADPCM:
|
||||
/* custom Ubi 4/6-bit ADPCM used in early games:
|
||||
* - Splinter Cell (PC): 4-bit w/ 2ch (music), 6-bit w/ 1ch (sfx)
|
||||
|
@ -1553,10 +1563,9 @@ static int parse_stream_codec(ubi_sb_header * sb) {
|
|||
case UBI_PS3:
|
||||
sb->codec = RAW_PSX; /* PS3 */
|
||||
break;
|
||||
case UBI_PSP:
|
||||
/* TODO: IMA using Ubisoft ADPCM frame layout [Splinter Cell: Essentials (PSP)] */
|
||||
VGM_LOG("UBI SB: Unimplemented custom IMA codec.\n");
|
||||
goto fail;
|
||||
case UBI_PSP: /* Splinter Cell: Essentials (PSP) */
|
||||
sb->codec = UBI_IMA_SCE;
|
||||
break;
|
||||
default:
|
||||
sb->codec = UBI_ADPCM;
|
||||
break;
|
||||
|
@ -2704,6 +2713,19 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
/* Tom Clancy's Ghost Recon Advanced Warfighter (2006)(Xbox)-bank */
|
||||
if (sb->version == 0x00130004 && sb->platform == UBI_XBOX) {
|
||||
config_sb_entry(sb, 0x48, 0x50);
|
||||
|
||||
config_sb_audio_fb(sb, 0x1c, (1 << 3), (1 << 4), (1 << 10));
|
||||
config_sb_audio_he(sb, 0x3c, 0x34, 0x20, 0x28, 0x44, 0x40);
|
||||
|
||||
/* what */
|
||||
sb->cfg.audio_extra_offset = 0x10;
|
||||
sb->cfg.audio_stream_offset = 0x14;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Prince of Persia: The Two Thrones (2005)(PC)-bank */
|
||||
if (sb->version == 0x00150000 && sb->platform == UBI_PC) {
|
||||
config_sb_entry(sb, 0x68, 0x78);
|
||||
|
@ -2903,8 +2925,12 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
/* Red Steel (2006)(Wii)-bank */
|
||||
if (sb->version == 0x00180006 && sb->platform == UBI_WII) {
|
||||
/* Red Steel (2006)(Wii)-bank 0x00180006 */
|
||||
/* Splinter Cell: Double Agent (2006)(Wii)-map 0x00180007 */
|
||||
/* Open Season (2006)(Wii)-map 0x00180008 */
|
||||
if ((sb->version == 0x00180006 && sb->platform == UBI_WII) ||
|
||||
(sb->version == 0x00180007 && sb->platform == UBI_WII) ||
|
||||
(sb->version == 0x00180008 && sb->platform == UBI_WII)) {
|
||||
config_sb_entry(sb, 0x68, 0x6c);
|
||||
|
||||
config_sb_audio_fs(sb, 0x28, 0x2c, 0x30);
|
||||
|
@ -2969,12 +2995,14 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) {
|
|||
/* My Word Coach (2007)(Wii)-bank 0x00190002 */
|
||||
/* Prince of Persia: Rival Swords (2007)(Wii)-bank 0x00190003 */
|
||||
/* Rainbow Six Vegas (2007)(PS3)-bank 0x00190005 */
|
||||
/* Surf's Up (2007)(Wii)-bank 0x00190005 */
|
||||
/* Surf's Up (2007)(PS3)-bank 0x00190005 */
|
||||
/* Surf's Up (2007)(X360)-bank 0x00190005 */
|
||||
/* Splinter Cell: Double Agent (2007)(PS3)-map 0x00190005 */
|
||||
if ((sb->version == 0x00190002 && sb->platform == UBI_X360) ||
|
||||
(sb->version == 0x00190002 && sb->platform == UBI_WII) ||
|
||||
(sb->version == 0x00190003 && sb->platform == UBI_WII) ||
|
||||
(sb->version == 0x00190005 && sb->platform == UBI_WII) ||
|
||||
(sb->version == 0x00190005 && sb->platform == UBI_PS3) ||
|
||||
(sb->version == 0x00190005 && sb->platform == UBI_X360)) {
|
||||
config_sb_entry(sb, 0x68, 0x70);
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "../layout/layout.h"
|
||||
|
||||
|
||||
/* VID1 - Factor 5/DivX format GC/Xbox games [Gun (GC), Tony Hawk's American Wasteland (GC), Enter The Matrix (Xbox)]*/
|
||||
VGMSTREAM * init_vgmstream_ngc_vid1(STREAMFILE* sf) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset, header_offset;
|
||||
int loop_flag = 0, channels, sample_rate;
|
||||
uint32_t codec;
|
||||
int big_endian;
|
||||
uint32_t (*read_u32)(off_t,STREAMFILE*);
|
||||
|
||||
|
||||
/* checks */
|
||||
/* .vid: video + (often) audio
|
||||
* .ogg: audio only [Gun (GC)], .logg: for plugins */
|
||||
if (!check_extensions(sf,"vid,ogg,logg"))
|
||||
goto fail;
|
||||
|
||||
/* chunked/blocked format containing video or audio frames */
|
||||
if (read_u32be(0x00, sf) == 0x56494431) { /* "VID1" BE (GC) */
|
||||
big_endian = 1;
|
||||
}
|
||||
else if (read_u32le(0x00,sf) == 0x56494431) { /* "VID1" LE (Xbox) */
|
||||
big_endian = 0;
|
||||
}
|
||||
else {
|
||||
goto fail;
|
||||
}
|
||||
read_u32 = big_endian ? read_u32be : read_u32le;
|
||||
|
||||
|
||||
/* find actual header start/size in the chunks (id + size + null) */
|
||||
{
|
||||
off_t offset = read_u32(0x04, sf);
|
||||
|
||||
if (read_u32(offset + 0x00, sf) != 0x48454144) /* "HEAD" */
|
||||
goto fail;
|
||||
start_offset = offset + read_u32(offset + 0x04, sf);
|
||||
offset += 0x0c;
|
||||
|
||||
/* videos have VIDH before AUDH */
|
||||
if (read_u32(offset + 0x00, sf) == 0x56494448) { /* "VIDH" */
|
||||
offset += read_u32(offset + 0x04, sf);
|
||||
}
|
||||
|
||||
if (read_u32(offset, sf) != 0x41554448) /* "AUDH" */
|
||||
goto fail;
|
||||
offset += 0x0c;
|
||||
|
||||
header_offset = offset;
|
||||
codec = read_u32(offset + 0x00, sf);
|
||||
sample_rate = read_u32(offset + 0x04, sf);
|
||||
channels = read_u8 (offset + 0x08, sf);
|
||||
/* 0x09: flag? 0/1 */
|
||||
/* 0x0a+: varies per codec (PCM/X-IMA: nothing, DSP: coefs, Vorbis: bitrate/frame info?) + padding */
|
||||
}
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_VID1;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->codec_endian = big_endian;
|
||||
|
||||
switch(codec) {
|
||||
case 0x50433136: /* "PC16" [Enter the Matrix (Xbox)] */
|
||||
vgmstream->coding_type = coding_PCM16_int;
|
||||
vgmstream->layout_type = layout_blocked_vid1;
|
||||
break;
|
||||
|
||||
case 0x5841504D: /* "XAPM" [Enter the Matrix (Xbox)] */
|
||||
vgmstream->coding_type = coding_XBOX_IMA;
|
||||
vgmstream->layout_type = layout_blocked_vid1;
|
||||
break;
|
||||
|
||||
case 0x4150434D: /* "APCM" [Enter the Matrix (GC)] */
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_blocked_vid1;
|
||||
|
||||
dsp_read_coefs_be(vgmstream, sf, header_offset + 0x0a, 0x20);
|
||||
break;
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
case 0x56415544: { /* "VAUD" [Gun (GC)] */
|
||||
vorbis_custom_config cfg = {0};
|
||||
|
||||
vgmstream->num_samples = read_u32(header_offset + 0x20, sf);
|
||||
|
||||
vgmstream->codec_data = init_vorbis_custom(sf, header_offset + 0x24, VORBIS_VID1, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_VORBIS_custom;
|
||||
vgmstream->layout_type = layout_none;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
|
||||
/* calc num_samples as playable data size varies between files/blocks */
|
||||
if (vgmstream->layout_type == layout_blocked_vid1) {
|
||||
int block_samples;
|
||||
|
||||
vgmstream->next_block_offset = start_offset;
|
||||
do {
|
||||
block_update(vgmstream->next_block_offset, vgmstream);
|
||||
if (vgmstream->current_block_samples < 0)
|
||||
break;
|
||||
|
||||
switch(vgmstream->coding_type) {
|
||||
case coding_PCM16_int: block_samples = pcm_bytes_to_samples(vgmstream->current_block_size, 1, 16); break;
|
||||
case coding_XBOX_IMA: block_samples = xbox_ima_bytes_to_samples(vgmstream->current_block_size, 1); break;
|
||||
case coding_NGC_DSP: block_samples = dsp_bytes_to_samples(vgmstream->current_block_size, 1); break;
|
||||
default: goto fail;
|
||||
}
|
||||
vgmstream->num_samples += block_samples;
|
||||
}
|
||||
while (vgmstream->next_block_offset < get_streamfile_size(sf));
|
||||
block_update(start_offset, vgmstream);
|
||||
}
|
||||
|
||||
return vgmstream;
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
#include "../coding/coding.h"
|
||||
|
||||
/* VPK - from SCE America second party devs [God of War (PS2), NBA 08 (PS3)] */
|
||||
VGMSTREAM * init_vgmstream_ps2_vpk(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * init_vgmstream_vpk(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
int loop_flag, channel_count;
|
||||
off_t start_offset, loop_channel_offset;
|
||||
|
@ -16,12 +16,14 @@ VGMSTREAM * init_vgmstream_ps2_vpk(STREAMFILE *streamFile) {
|
|||
if (read_32bitBE(0x00,streamFile) != 0x204B5056) /* " KPV" */
|
||||
goto fail;
|
||||
|
||||
/* seems this consistently has 0x10-0x20 extra bytes, landing in garbage 0xC00000..00 frames at the end */
|
||||
channel_size = read_32bitLE(0x04,streamFile) - 0x20; /* remove for cleaner ends */
|
||||
/* files are padded with garbage/silent 0xC00000..00 frames, and channel_size sometimes
|
||||
* has extra size into the padding: +0x10 (NBA08), +0x20 (GoW), or none (Sly 2, loops ok).
|
||||
* Could detect and remove to slightly improve full loops, but maybe this is just how the game works */
|
||||
channel_size = read_32bitLE(0x04,streamFile);
|
||||
|
||||
start_offset = read_32bitLE(0x08,streamFile);
|
||||
channel_count = read_32bitLE(0x14,streamFile);
|
||||
/* 0x18+(per channel): channel config(?) */
|
||||
/* 0x18+: channel config(?), 0x04 per channel */
|
||||
loop_channel_offset = read_32bitLE(0x7FC,streamFile);
|
||||
loop_flag = (loop_channel_offset != 0); /* found in Sly 2/3 */
|
||||
|
||||
|
|
|
@ -213,10 +213,12 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
|||
goto fail;
|
||||
}
|
||||
|
||||
if (ww.codec == PCM || ww.codec == IMA || ww.codec == VORBIS || ww.codec == XMA2 || ww.codec == OPUSNX)
|
||||
if (ww.codec == PCM || ww.codec == IMA || ww.codec == VORBIS || ww.codec == XMA2 || ww.codec == OPUSNX || ww.codec == OPUS) {
|
||||
ww.truncated = 1; /* only seen those, probably all exist */
|
||||
else
|
||||
} else {
|
||||
VGM_LOG("WWISE: wrong size, maybe truncated\n");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -605,6 +607,13 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
|||
/* 0x1c: stream size without OggS? */
|
||||
/* 0x20: full samples (without encoder delay) */
|
||||
|
||||
/* OPUS is VBR so this is very approximate percent, meh */
|
||||
if (ww.truncated) {
|
||||
vgmstream->num_samples = (int32_t)(vgmstream->num_samples *
|
||||
(double)(ww.file_size - start_offset) / (double)ww.data_size);
|
||||
ww.data_size = ww.file_size - start_offset;
|
||||
}
|
||||
|
||||
vgmstream->codec_data = init_ffmpeg_offset(streamFile, ww.data_offset,ww.data_size);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
|
|
|
@ -1,187 +1,246 @@
|
|||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* XNB - Microsoft XNA Game Studio 4.0 format */
|
||||
VGMSTREAM * init_vgmstream_xnb(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset, xma_chunk_offset = 0;
|
||||
int loop_flag = 0, channel_count, num_samples = 0, loop_start = 0, loop_end = 0;
|
||||
int big_endian, flags, codec, sample_rate, block_align, bps;
|
||||
size_t data_size;
|
||||
char platform;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile,"xnb"))
|
||||
goto fail;
|
||||
if ((read_32bitBE(0,streamFile) & 0xFFFFFF00) != 0x584E4200) /* "XNB" */
|
||||
goto fail;
|
||||
|
||||
/* XNA Studio platforms: 'w' = Windows, 'm' = Windows Phone 7, 'x' = X360
|
||||
* MonoGame extensions: 'i' = iOS, 'a' = Android, 'X' = MacOSX, 'P' = PS4, 'S' = Switch, etc */
|
||||
platform = read_8bit(0x03,streamFile);
|
||||
big_endian = (platform == 'x');
|
||||
|
||||
if (read_8bit(0x04,streamFile) != 0x04 && /* XNA 3.0? found on Scare Me (XBLIG), no notable diffs */
|
||||
read_8bit(0x04,streamFile) != 0x05) /* XNA 4.0 version */
|
||||
goto fail;
|
||||
|
||||
flags = read_8bit(0x05,streamFile);
|
||||
//if (flags & 0x01) goto fail; /* "HiDef profile" content (no actual difference) */
|
||||
if (flags & 0x80) goto fail; /* compressed with LZX/XMemCompress (at 0x0a is decompressed size) */
|
||||
if (flags & 0x40) goto fail; /* compressed with LZ4, MonoGame extension [ex. Square Heroes (PS4)] */
|
||||
|
||||
/* full size */
|
||||
if (read_32bitLE(0x06,streamFile) != get_streamfile_size(streamFile))
|
||||
goto fail;
|
||||
|
||||
|
||||
/* XNB contains "type reader" class references to parse "shared resource" data (can be any implemented filetype) */
|
||||
{
|
||||
char reader_name[255+1];
|
||||
off_t current_offset = 0x0a;
|
||||
size_t reader_string_len;
|
||||
uint32_t fmt_chunk_size;
|
||||
const char * type_sound = "Microsoft.Xna.Framework.Content.SoundEffectReader"; /* partial "fmt" chunk or XMA */
|
||||
//const char * type_song = "Microsoft.Xna.Framework.Content.SongReader"; /* just references a companion .wma */
|
||||
|
||||
/* type reader count, accept only one for now */
|
||||
if (read_8bit(current_offset++, streamFile) != 1)
|
||||
goto fail;
|
||||
|
||||
reader_string_len = read_8bit(current_offset++, streamFile); /* doesn't count null */
|
||||
if (reader_string_len > 255) goto fail;
|
||||
|
||||
/* check SoundEffect type string */
|
||||
if (read_string(reader_name,reader_string_len+1,current_offset,streamFile) != reader_string_len)
|
||||
goto fail;
|
||||
if ( strcmp(reader_name, type_sound) != 0 )
|
||||
goto fail;
|
||||
current_offset += reader_string_len + 1;
|
||||
current_offset += 0x04; /* reader version */
|
||||
|
||||
/* shared resource count */
|
||||
if (read_8bit(current_offset++, streamFile) != 1)
|
||||
goto fail;
|
||||
|
||||
/* shared resource: partial "fmt" chunk */
|
||||
fmt_chunk_size = read_32bitLE(current_offset, streamFile);
|
||||
current_offset += 0x04;
|
||||
|
||||
{
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = big_endian ? read_32bitBE : read_32bitLE;
|
||||
int16_t (*read_16bit)(off_t,STREAMFILE*) = big_endian ? read_16bitBE : read_16bitLE;
|
||||
|
||||
codec = (uint16_t)read_16bit(current_offset+0x00, streamFile);
|
||||
channel_count = read_16bit(current_offset+0x02, streamFile);
|
||||
sample_rate = read_32bit(current_offset+0x04, streamFile);
|
||||
/* 0x08: byte rate */
|
||||
block_align = read_16bit(current_offset+0x0c, streamFile);
|
||||
bps = read_16bit(current_offset+0x0e, streamFile);
|
||||
|
||||
if (codec == 0x0002) {
|
||||
if (!msadpcm_check_coefs(streamFile, current_offset + 0x14))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (codec == 0x0166) {
|
||||
xma2_parse_fmt_chunk_extra(streamFile, current_offset, &loop_flag, &num_samples, &loop_start, &loop_end, big_endian);
|
||||
xma_chunk_offset = current_offset;
|
||||
}
|
||||
|
||||
if (codec == 0xFFFF) {
|
||||
if (platform != 'S') goto fail;
|
||||
sample_rate = read_32bit(current_offset+fmt_chunk_size+0x04+0x08, streamFile);
|
||||
}
|
||||
}
|
||||
|
||||
current_offset += fmt_chunk_size;
|
||||
|
||||
data_size = read_32bitLE(current_offset, streamFile);
|
||||
current_offset += 0x04;
|
||||
|
||||
start_offset = current_offset;
|
||||
}
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->meta_type = meta_XNB;
|
||||
|
||||
switch (codec) {
|
||||
case 0x01: /* Dragon's Blade (Android) */
|
||||
/* null in Metagalactic Blitz (PC) */
|
||||
if (!block_align)
|
||||
block_align = (bps == 8 ? 0x01 : 0x02) * channel_count;
|
||||
|
||||
vgmstream->coding_type = bps == 8 ? coding_PCM8_U : coding_PCM16LE;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = block_align / channel_count;
|
||||
vgmstream->num_samples = pcm_bytes_to_samples(data_size, channel_count, bps);
|
||||
break;
|
||||
|
||||
case 0x02: /* White Noise Online (PC) */
|
||||
if (!block_align) goto fail;
|
||||
vgmstream->coding_type = coding_MSADPCM;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->frame_size = block_align;
|
||||
vgmstream->num_samples = msadpcm_bytes_to_samples(data_size, block_align, channel_count);
|
||||
break;
|
||||
|
||||
case 0x11:
|
||||
if (!block_align) goto fail;
|
||||
vgmstream->coding_type = coding_MS_IMA;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->interleave_block_size = block_align;
|
||||
vgmstream->num_samples = ms_ima_bytes_to_samples(data_size, block_align, channel_count);
|
||||
break;
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case 0x166: { /* Terraria (X360) */
|
||||
uint8_t buf[0x100];
|
||||
int32_t bytes, block_size, block_count;
|
||||
|
||||
block_size = 0x10000; /* guessed */
|
||||
block_count = data_size / block_size + (data_size % block_size ? 1 : 0);
|
||||
|
||||
bytes = ffmpeg_make_riff_xma2(buf,0x100, num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size);
|
||||
vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->num_samples = num_samples;
|
||||
vgmstream->loop_start_sample = loop_start;
|
||||
vgmstream->loop_end_sample = loop_end;
|
||||
|
||||
xma_fix_raw_samples(vgmstream, streamFile, start_offset,data_size, xma_chunk_offset, 1,1);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case 0xFFFF: /* Eagle Island (Switch) */
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = data_size / channel_count;
|
||||
vgmstream->num_samples = read_32bitLE(start_offset + 0x00, streamFile);
|
||||
//vgmstream->num_samples = dsp_bytes_to_samples(data_size - 0x60*channel_count, channel_count);
|
||||
|
||||
dsp_read_coefs(vgmstream,streamFile,start_offset + 0x1c,vgmstream->interleave_block_size,big_endian);
|
||||
dsp_read_hist (vgmstream,streamFile,start_offset + 0x3c,vgmstream->interleave_block_size,big_endian);
|
||||
break;
|
||||
|
||||
default:
|
||||
VGM_LOG("XNB: unknown codec 0x%x\n", codec);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ( !vgmstream_open_stream(vgmstream,streamFile,start_offset) )
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "xnb_streamfile.h"
|
||||
|
||||
|
||||
/* XNB - Microsoft XNA Game Studio 4.0 format */
|
||||
VGMSTREAM* init_vgmstream_xnb(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE* sf_h = NULL;
|
||||
off_t start_offset, offset, xma_chunk_offset = 0;
|
||||
int loop_flag = 0, channel_count, num_samples = 0, loop_start = 0, loop_end = 0;
|
||||
int big_endian, flags, codec, sample_rate, block_align, bps;
|
||||
size_t data_size;
|
||||
char platform;
|
||||
int is_ogg = 0, is_at9 = 0;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf,"xnb"))
|
||||
goto fail;
|
||||
if ((read_32bitBE(0x00, sf) & 0xFFFFFF00) != 0x584E4200) /* "XNB" */
|
||||
goto fail;
|
||||
|
||||
/* XNA Studio platforms: 'w' = Windows, 'm' = Windows Phone 7, 'x' = X360
|
||||
* MonoGame extensions: 'i' = iOS, 'a' = Android, 'X' = MacOSX, 'P' = PS4, 'S' = Switch, etc */
|
||||
platform = read_u8(0x03, sf);
|
||||
big_endian = (platform == 'x');
|
||||
|
||||
if (read_u8(0x04,sf) != 0x04 && /* XNA 3.0? found on Scare Me (XBLIG), no notable diffs */
|
||||
read_u8(0x04,sf) != 0x05) /* XNA 4.0 version */
|
||||
goto fail;
|
||||
|
||||
flags = read_u8(0x05, sf);
|
||||
//if (flags & 0x01) goto fail; /* "HiDef profile" content (no actual difference) */
|
||||
|
||||
/* full size */
|
||||
if (read_32bitLE(0x06, sf) != get_streamfile_size(sf)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* handle XNB compression as a normal non-compressed stream, normally for graphics
|
||||
* and other formats, but confused devs use it with already-compressed audio like Ogg/ATRAC9 */
|
||||
if ((flags & 0x80) || /* LZX/XMemCompress (not used with audio?) */
|
||||
(flags & 0x40)) { /* LZ4 (MonoGame extension) [Square Heroes (PS4), Little Savior (PC)] */
|
||||
size_t compression_start = 0x0e;
|
||||
size_t compression_size = read_u32le(0x0a, sf);
|
||||
sf_h = setup_xnb_streamfile(sf, flags, compression_start, compression_size);
|
||||
if (!sf_h) goto fail;
|
||||
|
||||
//dump_streamfile(sf_h, 0);
|
||||
offset = 0x0e; /* refers to decompressed stream */
|
||||
}
|
||||
else {
|
||||
sf_h = sf;
|
||||
offset = 0x0a;
|
||||
}
|
||||
|
||||
|
||||
/* XNB contains "type reader" class references to parse "shared resource" data (can be any implemented filetype) */
|
||||
{
|
||||
char reader_name[255+1];
|
||||
size_t reader_string_len;
|
||||
uint32_t fmt_chunk_size;
|
||||
const char* type_sound = "Microsoft.Xna.Framework.Content.SoundEffectReader"; /* partial "fmt" chunk or XMA */
|
||||
const char* type_ogg = "SoundEffectFromOggReader"; /* has extra text info after base part */
|
||||
//const char* type_song = "Microsoft.Xna.Framework.Content.SongReader"; /* references a companion .wma */
|
||||
|
||||
/* type reader count, accept only one for now */
|
||||
if (read_u8(offset++, sf_h) != 1)
|
||||
goto fail;
|
||||
|
||||
reader_string_len = read_u8(offset++, sf_h); /* doesn't count null */
|
||||
if (reader_string_len > 255) goto fail;
|
||||
|
||||
/* check SoundEffect type string */
|
||||
if (read_string(reader_name, reader_string_len+1, offset, sf_h) != reader_string_len)
|
||||
goto fail;
|
||||
if (strcmp(reader_name, type_sound) != 0) {
|
||||
is_ogg = strncmp(reader_name, type_ogg, strlen(type_ogg)) == 0;
|
||||
if (!is_ogg)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
offset += reader_string_len + 1;
|
||||
offset += 0x04; /* reader version, 0 */
|
||||
|
||||
/* shared resource count */
|
||||
if (read_u8(offset++, sf_h) != 1)
|
||||
goto fail;
|
||||
|
||||
/* shared resource: partial "fmt" chunk */
|
||||
fmt_chunk_size = read_32bitLE(offset, sf_h);
|
||||
offset += 0x04;
|
||||
|
||||
{
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = big_endian ? read_32bitBE : read_32bitLE;
|
||||
int16_t (*read_16bit)(off_t,STREAMFILE*) = big_endian ? read_16bitBE : read_16bitLE;
|
||||
|
||||
codec = (uint16_t)read_16bit(offset+0x00, sf_h);
|
||||
channel_count = read_16bit(offset+0x02, sf_h);
|
||||
sample_rate = read_32bit(offset+0x04, sf_h);
|
||||
/* 0x08: byte rate */
|
||||
block_align = read_16bit(offset+0x0c, sf_h);
|
||||
bps = read_16bit(offset+0x0e, sf_h);
|
||||
|
||||
if (codec == 0x0002) {
|
||||
if (!msadpcm_check_coefs(sf_h, offset + 0x14))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (codec == 0x0166) {
|
||||
xma2_parse_fmt_chunk_extra(sf_h, offset, &loop_flag, &num_samples, &loop_start, &loop_end, big_endian);
|
||||
xma_chunk_offset = offset;
|
||||
}
|
||||
|
||||
if (codec == 0xFFFF) {
|
||||
if (platform != 'S') goto fail;
|
||||
sample_rate = read_32bit(offset+fmt_chunk_size+0x04+0x08, sf_h);
|
||||
}
|
||||
|
||||
/* mini-fmt has AT9 stuff then a regular RIFF [Square Heroes (PS4)] */
|
||||
if (codec == 0xFFFE) {
|
||||
is_at9 = 1;
|
||||
}
|
||||
|
||||
/* regular (with loop tags) Ogg poses as PCM [Little Savior (PC)] */
|
||||
}
|
||||
|
||||
offset += fmt_chunk_size;
|
||||
|
||||
data_size = read_32bitLE(offset, sf_h);
|
||||
offset += 0x04;
|
||||
|
||||
start_offset = offset;
|
||||
}
|
||||
|
||||
/* container handling */
|
||||
if (is_ogg || is_at9) {
|
||||
STREAMFILE* temp_sf = NULL;
|
||||
const char* fake_ext = is_ogg ? "ogg" : "at9";
|
||||
|
||||
/* after data_size is loop start + loop length and offset? (same as loop tags), 0 if not enabled */
|
||||
|
||||
temp_sf = setup_subfile_streamfile(sf_h, start_offset, data_size, fake_ext);
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
if (is_ogg) {
|
||||
#ifdef VGM_USE_VORBIS
|
||||
vgmstream = init_vgmstream_ogg_vorbis(temp_sf);
|
||||
#endif
|
||||
} else {
|
||||
vgmstream = init_vgmstream_riff(temp_sf);
|
||||
}
|
||||
close_streamfile(temp_sf);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_XNB;
|
||||
if (sf_h != sf) close_streamfile(sf_h);
|
||||
return vgmstream;
|
||||
}
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->meta_type = meta_XNB;
|
||||
|
||||
switch (codec) {
|
||||
case 0x01: /* Dragon's Blade (Android) */
|
||||
if (!block_align) /* null in Metagalactic Blitz (PC) */
|
||||
block_align = (bps == 8 ? 0x01 : 0x02) * channel_count;
|
||||
|
||||
vgmstream->coding_type = bps == 8 ? coding_PCM8_U : coding_PCM16LE;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = block_align / channel_count;
|
||||
vgmstream->num_samples = pcm_bytes_to_samples(data_size, channel_count, bps);
|
||||
break;
|
||||
|
||||
case 0x02: /* White Noise Online (PC) */
|
||||
if (!block_align) goto fail;
|
||||
vgmstream->coding_type = coding_MSADPCM;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->frame_size = block_align;
|
||||
vgmstream->num_samples = msadpcm_bytes_to_samples(data_size, block_align, channel_count);
|
||||
break;
|
||||
|
||||
case 0x11:
|
||||
if (!block_align) goto fail;
|
||||
vgmstream->coding_type = coding_MS_IMA;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->interleave_block_size = block_align;
|
||||
vgmstream->num_samples = ms_ima_bytes_to_samples(data_size, block_align, channel_count);
|
||||
break;
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case 0x166: { /* Terraria (X360) */
|
||||
uint8_t buf[0x100];
|
||||
int32_t bytes, block_size, block_count;
|
||||
|
||||
block_size = 0x10000; /* guessed */
|
||||
block_count = data_size / block_size + (data_size % block_size ? 1 : 0);
|
||||
|
||||
bytes = ffmpeg_make_riff_xma2(buf,0x100, num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size);
|
||||
vgmstream->codec_data = init_ffmpeg_header_offset(sf_h, buf,bytes, start_offset,data_size);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->num_samples = num_samples;
|
||||
vgmstream->loop_start_sample = loop_start;
|
||||
vgmstream->loop_end_sample = loop_end;
|
||||
|
||||
xma_fix_raw_samples(vgmstream, sf_h, start_offset,data_size, xma_chunk_offset, 1,1);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
case 0xFFFF: /* Eagle Island (Switch) */
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = data_size / channel_count;
|
||||
vgmstream->num_samples = read_32bitLE(start_offset + 0x00, sf_h);
|
||||
//vgmstream->num_samples = dsp_bytes_to_samples(data_size - 0x60*channel_count, channel_count);
|
||||
|
||||
dsp_read_coefs(vgmstream, sf_h, start_offset + 0x1c, vgmstream->interleave_block_size, big_endian);
|
||||
dsp_read_hist (vgmstream, sf_h, start_offset + 0x3c, vgmstream->interleave_block_size, big_endian);
|
||||
break;
|
||||
|
||||
default:
|
||||
VGM_LOG("XNB: unknown codec 0x%x\n", codec);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf_h, start_offset))
|
||||
goto fail;
|
||||
|
||||
if (sf_h != sf) close_streamfile(sf_h);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
if (sf_h != sf) close_streamfile(sf_h);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,266 @@
|
|||
#ifndef _XNB_LZ4MG_H_
|
||||
#define _XNB_LZ4MG_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Decompresses LZ4 from MonoGame. The original C lib has a lot of modes and configs, but
|
||||
* MonoGame only uses the core 'block' part, which is a fairly simple LZ77 (has one command
|
||||
* to copy literal and window values, with variable copy lengths).
|
||||
*
|
||||
* This is a basic re-implementation (not tuned for performance) for practice/test purposes,
|
||||
* that handles streaming decompression as a state machine since we can run out of src or dst
|
||||
* bytes anytime and LZ4 allows any copy length, with copy window as a circular buffer. Not
|
||||
* sure what's the best/standard way to do it though. Info:
|
||||
* - https://github.com/lz4/lz4
|
||||
* - https://github.com/MonoGame/MonoGame/blob/develop/MonoGame.Framework/Utilities/Lz4Stream/Lz4DecoderStream.cs
|
||||
*/
|
||||
|
||||
#define LZ4MG_OK 0
|
||||
#define LZ4MG_ERROR -1
|
||||
#define LZ4MG_WINDOW_SIZE (1 << 16)
|
||||
#define LZ4MG_WINDOW_BYTES 2
|
||||
#define LZ4MG_MIN_MATCH_LEN 4
|
||||
#define LZ4MG_VARLEN_MARK 15
|
||||
#define LZ4MG_VARLEN_CONTINUE 255
|
||||
|
||||
|
||||
typedef enum {
|
||||
READ_TOKEN,
|
||||
READ_LITERAL,
|
||||
COPY_LITERAL,
|
||||
READ_OFFSET,
|
||||
READ_MATCH,
|
||||
SET_MATCH,
|
||||
COPY_MATCH
|
||||
} lz4mg_state_t;
|
||||
|
||||
typedef struct {
|
||||
lz4mg_state_t state;
|
||||
|
||||
uint8_t token;
|
||||
int literal_len;
|
||||
int offset_cur;
|
||||
int offset_pos;
|
||||
int match_len;
|
||||
int match_pos;
|
||||
|
||||
int window_pos;
|
||||
uint8_t window[LZ4MG_WINDOW_SIZE];
|
||||
} lz4mg_context_t;
|
||||
|
||||
typedef struct {
|
||||
lz4mg_context_t ctx;
|
||||
|
||||
uint8_t *next_out; /* next bytes to write (reassign when avail is 0) */
|
||||
int avail_out; /* bytes available at next_out */
|
||||
int total_out; /* written bytes, for reference (set to 0 per call if needed) */
|
||||
|
||||
const uint8_t *next_in; /* next bytes to read (reassign when avail is 0) */
|
||||
int avail_in; /* bytes available at next_in */
|
||||
int total_in; /* read bytes, for reference (set to 0 per call if needed) */
|
||||
} lz4mg_stream_t;
|
||||
|
||||
static void lz4mg_reset(lz4mg_stream_t* strm) {
|
||||
memset(strm, 0, sizeof(lz4mg_stream_t));
|
||||
}
|
||||
|
||||
/* Decompress src into dst, returning a code and number of bytes used. Caller must handle
|
||||
* stop (when no more input data or all data has been decompressed) as LZ4 has no end markers. */
|
||||
static int lz4mg_decompress(lz4mg_stream_t* strm) {
|
||||
lz4mg_context_t* ctx = &strm->ctx;
|
||||
uint8_t* dst = strm->next_out;
|
||||
const uint8_t* src = strm->next_in;
|
||||
int dst_size = strm->avail_out;
|
||||
int src_size = strm->avail_in;
|
||||
int dst_pos = 0;
|
||||
int src_pos = 0;
|
||||
uint8_t next_len, next_val;
|
||||
|
||||
|
||||
while (1) {
|
||||
/* mostly linear state machine, but it may break anytime when reaching dst or src
|
||||
* end, and resume from same state in next call */
|
||||
switch(ctx->state) {
|
||||
|
||||
case READ_TOKEN:
|
||||
if (src_pos >= src_size)
|
||||
goto buffer_end;
|
||||
ctx->token = src[src_pos++];
|
||||
|
||||
ctx->literal_len = (ctx->token >> 4) & 0xF;
|
||||
if (ctx->literal_len == LZ4MG_VARLEN_MARK)
|
||||
ctx->state = READ_LITERAL;
|
||||
else
|
||||
ctx->state = COPY_LITERAL;
|
||||
break;
|
||||
|
||||
case READ_LITERAL:
|
||||
do {
|
||||
if (src_pos >= src_size)
|
||||
goto buffer_end;
|
||||
next_len = src[src_pos++];
|
||||
ctx->literal_len += next_len;
|
||||
} while (next_len == LZ4MG_VARLEN_CONTINUE);
|
||||
|
||||
ctx->state = COPY_LITERAL;
|
||||
break;
|
||||
|
||||
case COPY_LITERAL:
|
||||
while (ctx->literal_len > 0) { /* may be 0 */
|
||||
if (src_pos >= src_size || dst_pos >= dst_size)
|
||||
goto buffer_end;
|
||||
next_val = src[src_pos++];
|
||||
|
||||
dst[dst_pos++] = next_val;
|
||||
|
||||
ctx->window[ctx->window_pos++] = next_val;
|
||||
if (ctx->window_pos == LZ4MG_WINDOW_SIZE)
|
||||
ctx->window_pos = 0;
|
||||
|
||||
ctx->literal_len--;
|
||||
};
|
||||
|
||||
/* LZ4 is designed to reach EOF with a literal in this state with some empty values */
|
||||
|
||||
ctx->offset_cur = 0;
|
||||
ctx->offset_pos = 0;
|
||||
ctx->state = READ_OFFSET;
|
||||
break;
|
||||
|
||||
case READ_OFFSET:
|
||||
do {
|
||||
if (src_pos >= src_size)
|
||||
goto buffer_end;
|
||||
ctx->offset_pos |= (src[src_pos++] << ctx->offset_cur*8);
|
||||
ctx->offset_cur++;
|
||||
} while (ctx->offset_cur < LZ4MG_WINDOW_BYTES);
|
||||
|
||||
ctx->match_len = (ctx->token & 0xF);
|
||||
if (ctx->match_len == LZ4MG_VARLEN_MARK)
|
||||
ctx->state = READ_MATCH;
|
||||
else
|
||||
ctx->state = SET_MATCH;
|
||||
break;
|
||||
|
||||
case READ_MATCH:
|
||||
do {
|
||||
if (src_pos >= src_size)
|
||||
goto buffer_end;
|
||||
next_len = src[src_pos++];
|
||||
ctx->match_len += next_len;
|
||||
} while (next_len == LZ4MG_VARLEN_CONTINUE);
|
||||
|
||||
ctx->state = SET_MATCH;
|
||||
break;
|
||||
|
||||
case SET_MATCH:
|
||||
ctx->match_len += LZ4MG_MIN_MATCH_LEN;
|
||||
|
||||
ctx->match_pos = ctx->window_pos - ctx->offset_pos;
|
||||
if (ctx->match_pos < 0) /* circular buffer so negative is from window end */
|
||||
ctx->match_pos = LZ4MG_WINDOW_SIZE + ctx->match_pos;
|
||||
|
||||
ctx->state = COPY_MATCH;
|
||||
break;
|
||||
|
||||
case COPY_MATCH:
|
||||
while (ctx->match_len > 0) {
|
||||
if (dst_pos >= dst_size)
|
||||
goto buffer_end;
|
||||
|
||||
next_val = ctx->window[ctx->match_pos++];
|
||||
if (ctx->match_pos == LZ4MG_WINDOW_SIZE)
|
||||
ctx->match_pos = 0;
|
||||
|
||||
dst[dst_pos++] = next_val;
|
||||
|
||||
ctx->window[ctx->window_pos++] = next_val;
|
||||
if (ctx->window_pos == LZ4MG_WINDOW_SIZE)
|
||||
ctx->window_pos = 0;
|
||||
|
||||
ctx->match_len--;
|
||||
};
|
||||
|
||||
ctx->state = READ_TOKEN;
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
buffer_end:
|
||||
strm->next_out += dst_pos;
|
||||
strm->next_in += src_pos;
|
||||
strm->avail_out -= dst_pos;
|
||||
strm->avail_in -= src_pos;
|
||||
strm->total_out += dst_pos;
|
||||
strm->total_in += src_pos;
|
||||
|
||||
return LZ4MG_OK;
|
||||
fail:
|
||||
return LZ4MG_ERROR;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* non-streamed form for reference, assumes buffers are big enough */
|
||||
static void decompress_lz4mg(uint8_t* dst, size_t dst_size, const uint8_t* src, size_t src_size) {
|
||||
size_t src_pos = 0;
|
||||
size_t dst_pos = 0;
|
||||
uint8_t token;
|
||||
int literal_len, match_len, next_len;
|
||||
int match_pos, match_offset;
|
||||
int i;
|
||||
|
||||
while (src_pos < src_size && dst_pos < dst_size) {
|
||||
|
||||
token = src[src_pos++];
|
||||
if (src_pos > src_size)
|
||||
break;
|
||||
|
||||
/* handle literals */
|
||||
literal_len = token >> 4;
|
||||
if (literal_len == 15) {
|
||||
do {
|
||||
next_len = src[src_pos++];
|
||||
literal_len += next_len;
|
||||
if (src_pos > src_size)
|
||||
break;
|
||||
} while (next_len == 255);
|
||||
}
|
||||
|
||||
for (i = 0; i < literal_len; i++) {
|
||||
dst[dst_pos++] = src[src_pos++];
|
||||
}
|
||||
|
||||
/* can happen at EOF */
|
||||
if (dst_pos >= dst_size)
|
||||
break;
|
||||
|
||||
/* handle window matches */
|
||||
match_offset = src[src_pos++];
|
||||
match_offset |= (src[src_pos++] << 8);
|
||||
|
||||
match_len = (token & 0xF);
|
||||
if (match_len == 15) {
|
||||
do {
|
||||
next_len = src[src_pos++];
|
||||
match_len += next_len;
|
||||
if (src_pos > src_size)
|
||||
break;
|
||||
} while (next_len == 255);
|
||||
}
|
||||
match_len += 4; /* min len */
|
||||
|
||||
match_pos = dst_pos - match_offset;
|
||||
for(i = 0; i < match_len; i++) {
|
||||
dst[dst_pos++] = dst[match_pos++]; /* note RLE with short offsets */
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _XNB_LZ4MG_H_ */
|
|
@ -0,0 +1,300 @@
|
|||
#ifndef _XNB_STREAMFILE_H_
|
||||
#define _XNB_STREAMFILE_H_
|
||||
|
||||
//#define XNB_ENABLE_LZX 1
|
||||
|
||||
#ifdef XNB_ENABLE_LZX
|
||||
/* lib from https://github.com/sumatrapdfreader/chmlib
|
||||
* which is a cleaned-up version of https://github.com/jedwing/CHMLib */
|
||||
#include "lzx.h"
|
||||
|
||||
#define LZX_XNB_WINDOW_BITS 16
|
||||
#endif
|
||||
|
||||
#include "xnb_lz4mg.h"
|
||||
|
||||
|
||||
#define XNB_TYPE_LZX 1
|
||||
#define XNB_TYPE_LZ4 2
|
||||
|
||||
typedef struct {
|
||||
/* config */
|
||||
int type;
|
||||
off_t compression_start;
|
||||
size_t compression_size;
|
||||
|
||||
/* state */
|
||||
off_t logical_offset; /* offset that corresponds to physical_offset */
|
||||
off_t physical_offset; /* actual file offset */
|
||||
|
||||
size_t block_size; /* current block size */
|
||||
size_t skip_size; /* size to skip from a block start to reach data start */
|
||||
size_t data_size; /* logical size of the block */
|
||||
|
||||
size_t logical_size;
|
||||
|
||||
/* decompression state (dst size min for LZX) */
|
||||
uint8_t dst[0x10000];
|
||||
uint8_t src[0x10000];
|
||||
|
||||
lz4mg_stream_t lz4s;
|
||||
#ifdef XNB_ENABLE_LZX
|
||||
struct lzx_state* lzxs;
|
||||
#endif
|
||||
} xnb_io_data;
|
||||
|
||||
static int xnb_io_init(STREAMFILE* sf, xnb_io_data* data) {
|
||||
/* When a new IO SF is opened, the data struct is malloc'd and cloned. This works
|
||||
* well enough in other cases, but b/c the decompression context works with buf ptrs
|
||||
* the clone will point to the original struct bufs, so we need to force
|
||||
* reset on new open so that new bufs of the clone are used. */
|
||||
data->logical_offset = -1;
|
||||
|
||||
#ifdef XNB_ENABLE_LZX
|
||||
if (data->type == XNB_TYPE_LZX) {
|
||||
data->lzxs = lzx_init(LZX_XNB_WINDOW_BITS);
|
||||
if (!data->lzxs)
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void xnb_io_close(STREAMFILE* sf, xnb_io_data* data) {
|
||||
#ifdef XNB_ENABLE_LZX
|
||||
if (data->type == XNB_TYPE_LZX) {
|
||||
lzx_teardown(data->lzxs);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef XNB_ENABLE_LZX
|
||||
/* Decompresses LZX used in XNB. Has 16b window and headered blocks (with input size and
|
||||
* optionally output size) and standard LZX inside, probably what's known as XMemCompress.
|
||||
* Info: https://github.com/MonoGame/MonoGame/blob/develop/MonoGame.Framework/Utilities/LzxStream/LzxDecoderStream.cs */
|
||||
static int decompress_lzx_block(STREAMFILE* sf, xnb_io_data* data) {
|
||||
int src_size, dst_size, ret;
|
||||
uint8_t hi, lo;
|
||||
off_t head_size;
|
||||
off_t offset = data->physical_offset;
|
||||
|
||||
|
||||
hi = read_u8(offset + 0x00, sf);
|
||||
if (hi == 0xFF) {
|
||||
hi = read_u8(offset + 0x01, sf);
|
||||
lo = read_u8(offset + 0x02, sf);
|
||||
dst_size = (hi << 8) | lo;
|
||||
|
||||
hi = read_u8(offset + 0x03, sf);
|
||||
lo = read_u8(offset + 0x04, sf);
|
||||
src_size = (hi << 8) | lo;
|
||||
|
||||
head_size = 0x05;
|
||||
}
|
||||
else {
|
||||
dst_size = 0x8000; /* default */
|
||||
|
||||
lo = read_u8(offset + 0x01, sf);
|
||||
src_size = (hi << 8) | lo;
|
||||
|
||||
head_size = 0x02;
|
||||
}
|
||||
|
||||
if (src_size == 0 || dst_size == 0) {
|
||||
VGM_LOG("LZX: EOF\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
read_streamfile(data->src, offset + head_size, src_size, sf);
|
||||
|
||||
ret = lzx_decompress(data->lzxs, data->src, data->dst, src_size, dst_size);
|
||||
if (ret != DECR_OK) {
|
||||
VGM_LOG("LZX: decompression error %i\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
data->data_size = dst_size;
|
||||
data->block_size = head_size + src_size;
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int decompress_lz4_block(STREAMFILE* sf, xnb_io_data* data) {
|
||||
int ret;
|
||||
|
||||
if (data->lz4s.avail_in == 0) {
|
||||
data->lz4s.next_in = data->src;
|
||||
data->lz4s.avail_in = read_streamfile(data->src, data->physical_offset, sizeof(data->src), sf);
|
||||
|
||||
/* shouldn't happen since we have decomp size */
|
||||
if (data->lz4s.avail_in <= 0) {
|
||||
VGM_LOG("XNB: EOF reached\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
data->block_size = data->lz4s.avail_in; /* physical data (smaller) */
|
||||
}
|
||||
else {
|
||||
data->block_size = 0; /* physical data doesn't move until new read */
|
||||
}
|
||||
|
||||
data->lz4s.total_out = 0;
|
||||
data->lz4s.avail_out = sizeof(data->dst);
|
||||
data->lz4s.next_out = data->dst;
|
||||
|
||||
ret = lz4mg_decompress(&data->lz4s);
|
||||
|
||||
data->data_size = data->lz4s.total_out; /* logical data (bigger) */
|
||||
|
||||
if (ret != LZ4MG_OK) {
|
||||
VGM_LOG("XNB: LZ4 error %i\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t xnb_io_read(STREAMFILE* sf, uint8_t *dest, off_t offset, size_t length, xnb_io_data* data) {
|
||||
size_t total_read = 0;
|
||||
|
||||
/* reset */
|
||||
if (data->logical_offset < 0 || offset < data->logical_offset) {
|
||||
data->physical_offset = 0x00;
|
||||
data->logical_offset = 0x00;
|
||||
data->block_size = 0;
|
||||
data->data_size = 0;
|
||||
data->skip_size = 0;
|
||||
|
||||
switch(data->type) {
|
||||
#ifdef XNB_ENABLE_LZX
|
||||
case XNB_TYPE_LZX: lzx_reset(data->lzxs); break;
|
||||
#endif
|
||||
case XNB_TYPE_LZ4: lz4mg_reset(&data->lz4s); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
/* read blocks, one at a time */
|
||||
while (length > 0) {
|
||||
|
||||
/* ignore EOF */
|
||||
if (offset < 0 || data->logical_offset >= data->logical_size) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* process new block */
|
||||
if (data->data_size <= 0) {
|
||||
|
||||
if (data->physical_offset < data->compression_start) {
|
||||
/* copy data */
|
||||
int to_read = data->compression_start - data->physical_offset;
|
||||
|
||||
data->data_size = read_streamfile(data->dst, data->physical_offset, to_read, sf);
|
||||
data->block_size = data->data_size;
|
||||
}
|
||||
else {
|
||||
/* decompress data */
|
||||
int ok = 0;
|
||||
|
||||
switch(data->type) {
|
||||
#ifdef XNB_ENABLE_LZX
|
||||
case XNB_TYPE_LZX: ok = decompress_lzx_block(sf, data); break;
|
||||
#endif
|
||||
case XNB_TYPE_LZ4: ok = decompress_lz4_block(sf, data); break;
|
||||
default: break;
|
||||
}
|
||||
if (!ok) {
|
||||
VGM_LOG("XNB: decompression error\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* move to next block */
|
||||
if (data->data_size == 0 || offset >= data->logical_offset + data->data_size) {
|
||||
data->physical_offset += data->block_size;
|
||||
data->logical_offset += data->data_size;
|
||||
data->data_size = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* read/copy block data */
|
||||
{
|
||||
size_t bytes_consumed, bytes_done, to_read;
|
||||
|
||||
bytes_consumed = offset - data->logical_offset;
|
||||
to_read = data->data_size - bytes_consumed;
|
||||
if (to_read > length)
|
||||
to_read = length;
|
||||
memcpy(dest, data->dst + data->skip_size + bytes_consumed, to_read);
|
||||
bytes_done = to_read;
|
||||
|
||||
total_read += bytes_done;
|
||||
dest += bytes_done;
|
||||
offset += bytes_done;
|
||||
length -= bytes_done;
|
||||
|
||||
if (bytes_done != to_read || bytes_done == 0) {
|
||||
break; /* error/EOF */
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return total_read;
|
||||
}
|
||||
|
||||
|
||||
static size_t xnb_io_size(STREAMFILE *streamfile, xnb_io_data* data) {
|
||||
uint8_t buf[1];
|
||||
|
||||
if (data->logical_size)
|
||||
return data->logical_size;
|
||||
|
||||
/* force a fake read at max offset, to get max logical_offset (will be reset next read) */
|
||||
xnb_io_read(streamfile, buf, 0x7FFFFFFF, 1, data);
|
||||
data->logical_size = data->logical_offset;
|
||||
|
||||
return data->logical_size;
|
||||
}
|
||||
|
||||
|
||||
/* Decompresses XNB streams */
|
||||
static STREAMFILE* setup_xnb_streamfile(STREAMFILE* sf, int flags, off_t compression_start, size_t compression_size) {
|
||||
STREAMFILE *new_sf = NULL;
|
||||
xnb_io_data io_data = {0};
|
||||
|
||||
if (flags & 0x80)
|
||||
io_data.type = XNB_TYPE_LZX;
|
||||
else if (flags & 0x40)
|
||||
io_data.type = XNB_TYPE_LZ4;
|
||||
else
|
||||
goto fail;
|
||||
io_data.compression_start = compression_start;
|
||||
io_data.compression_size = compression_size;
|
||||
io_data.physical_offset = 0x00;
|
||||
io_data.logical_size = compression_start + compression_size;
|
||||
io_data.logical_offset = -1; /* force reset */
|
||||
|
||||
#ifndef XNB_ENABLE_LZX
|
||||
/* no known audio use it (otherwise works if enabled and included lzx.c/lzx.h) */
|
||||
if (io_data.type == XNB_TYPE_LZX)
|
||||
goto fail;
|
||||
#endif
|
||||
|
||||
/* setup subfile */
|
||||
new_sf = open_wrap_streamfile(sf);
|
||||
new_sf = open_io_streamfile_ex_f(new_sf, &io_data, sizeof(xnb_io_data), xnb_io_read, xnb_io_size, xnb_io_init, xnb_io_close);
|
||||
//new_sf = open_buffer_streamfile_f(new_sf, 0); /* useful? we already have a decompression buffer */
|
||||
return new_sf;
|
||||
fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* _XNB_STREAMFILE_H_ */
|
|
@ -1,69 +1,82 @@
|
|||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* XPCM - from Circus games [Eternal Fantasy (PC), D.C. White Season (PC)] */
|
||||
VGMSTREAM * init_vgmstream_xpcm(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
size_t decompressed_size;
|
||||
int loop_flag, channel_count, codec, subcodec, sample_rate;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile, "pcm"))
|
||||
goto fail;
|
||||
|
||||
if (read_32bitBE(0x00,streamFile) != 0x5850434D) /* "XPCM" "*/
|
||||
goto fail;
|
||||
|
||||
decompressed_size = read_32bitLE(0x04,streamFile); /* (data_size for PCM) */
|
||||
codec = read_8bit(0x08, streamFile);
|
||||
subcodec = read_8bit(0x09, streamFile);
|
||||
/* 0x0a: always null */
|
||||
/* 0x0c: always 0x01 (PCM codec) */
|
||||
channel_count = read_16bitLE(0x0e,streamFile);
|
||||
sample_rate = read_32bitLE(0x10,streamFile);
|
||||
/* 0x14: average bitrate */
|
||||
/* 0x18: block size */
|
||||
/* 0x1a: output bits (16) */
|
||||
start_offset = 0x1c; /* compressed size in codec 0x01/03 */
|
||||
|
||||
loop_flag = 0;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_XPCM;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = decompressed_size / sizeof(int16_t) / channel_count;
|
||||
|
||||
switch(codec) {
|
||||
case 0x00:
|
||||
if (subcodec != 0) goto fail;
|
||||
vgmstream->coding_type = coding_PCM16LE;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x02;
|
||||
break;
|
||||
case 0x02:
|
||||
if (subcodec != 0) goto fail;
|
||||
vgmstream->coding_type = coding_CIRCUS_ADPCM;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x01;
|
||||
break;
|
||||
|
||||
case 0x01: /* LZSS + VQ */
|
||||
case 0x03: /* unknown */
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* XPCM - from Circus games [Eternal Fantasy (PC), D.C. White Season (PC)] */
|
||||
VGMSTREAM * init_vgmstream_xpcm(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
size_t decompressed_size;
|
||||
int loop_flag, channel_count, codec, flags, sample_rate;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile, "pcm"))
|
||||
goto fail;
|
||||
|
||||
if (read_32bitBE(0x00,streamFile) != 0x5850434D) /* "XPCM" "*/
|
||||
goto fail;
|
||||
|
||||
decompressed_size = read_32bitLE(0x04,streamFile); /* (data_size for PCM) */
|
||||
codec = read_8bit(0x08, streamFile);
|
||||
flags = read_8bit(0x09, streamFile);
|
||||
/* 0x0a: always null */
|
||||
/* 0x0c: always 0x01 (PCM codec) */
|
||||
channel_count = read_16bitLE(0x0e,streamFile);
|
||||
sample_rate = read_32bitLE(0x10,streamFile);
|
||||
/* 0x14: average bitrate */
|
||||
/* 0x18: block size */
|
||||
/* 0x1a: output bits (16) */
|
||||
start_offset = 0x1c; /* compressed size in codec 0x01/03 */
|
||||
|
||||
loop_flag = 0;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_XPCM;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = decompressed_size / sizeof(int16_t) / channel_count;
|
||||
|
||||
switch(codec) {
|
||||
case 0x00:
|
||||
if (flags != 0) goto fail;
|
||||
vgmstream->coding_type = coding_PCM16LE;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x02;
|
||||
break;
|
||||
|
||||
case 0x02:
|
||||
if (flags != 0) goto fail;
|
||||
vgmstream->coding_type = coding_CIRCUS_ADPCM;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x01;
|
||||
break;
|
||||
|
||||
case 0x01: /* VQ + LZ (usually music) */
|
||||
case 0x03: /* VQ + deflate (usually sfx/voices) */
|
||||
vgmstream->codec_data = init_circus_vq(streamFile, 0x20, codec, flags);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_CIRCUS_VQ;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
/* not too sure about samples, since decompressed size isn't exact sometimes vs
|
||||
* total decompression, though it's what the code uses to alloc bufs (plus + 0x2000 leeway) */
|
||||
//vgmstream->num_samples = decompressed_size / 0x2000 * 4064 / channel_count;
|
||||
//if (decompressed_size % 0x2000 != 0)
|
||||
// vgmstream->num_samples += (decompressed_size % 0x2000) / channel_count;
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -1,125 +1,26 @@
|
|||
#ifndef _XWMA_KONAMI_STREAMFILE_H_
|
||||
#define _XWMA_KONAMI_STREAMFILE_H_
|
||||
#include "../streamfile.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
/* config */
|
||||
off_t stream_offset;
|
||||
size_t stream_size;
|
||||
size_t block_align;
|
||||
|
||||
/* state */
|
||||
off_t logical_offset; /* fake offset */
|
||||
off_t physical_offset; /* actual offset */
|
||||
size_t block_size; /* current size */
|
||||
size_t skip_size; /* size from block start to reach data */
|
||||
size_t data_size; /* usable size in a block */
|
||||
|
||||
size_t logical_size;
|
||||
} xwma_konami_io_data;
|
||||
|
||||
|
||||
static size_t xwma_konami_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, xwma_konami_io_data* data) {
|
||||
size_t total_read = 0;
|
||||
|
||||
|
||||
/* re-start when previous offset (can't map logical<>physical offsets) */
|
||||
if (data->logical_offset < 0 || offset < data->logical_offset) {
|
||||
data->physical_offset = data->stream_offset;
|
||||
data->logical_offset = 0x00;
|
||||
data->data_size = 0;
|
||||
}
|
||||
|
||||
/* read blocks */
|
||||
while (length > 0) {
|
||||
|
||||
/* ignore EOF */
|
||||
if (offset < 0 || data->physical_offset >= data->stream_offset + data->stream_size) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* process new block */
|
||||
if (data->data_size == 0) {
|
||||
data->block_size = align_size_to_block(data->block_align, 0x10);
|
||||
data->data_size = data->block_align;
|
||||
data->skip_size = 0x00;
|
||||
}
|
||||
|
||||
/* move to next block */
|
||||
if (data->data_size == 0 || offset >= data->logical_offset + data->data_size) {
|
||||
data->physical_offset += data->block_size;
|
||||
data->logical_offset += data->data_size;
|
||||
data->data_size = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* read data */
|
||||
{
|
||||
size_t bytes_consumed, bytes_done, to_read;
|
||||
|
||||
bytes_consumed = offset - data->logical_offset;
|
||||
to_read = data->data_size - bytes_consumed;
|
||||
if (to_read > length)
|
||||
to_read = length;
|
||||
bytes_done = read_streamfile(dest, data->physical_offset + data->skip_size + bytes_consumed, to_read, streamfile);
|
||||
|
||||
total_read += bytes_done;
|
||||
dest += bytes_done;
|
||||
offset += bytes_done;
|
||||
length -= bytes_done;
|
||||
|
||||
if (bytes_done != to_read || bytes_done == 0) {
|
||||
break; /* error/EOF */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return total_read;
|
||||
}
|
||||
|
||||
static size_t xwma_konami_io_size(STREAMFILE *streamfile, xwma_konami_io_data* data) {
|
||||
uint8_t buf[1];
|
||||
|
||||
if (data->logical_size)
|
||||
return data->logical_size;
|
||||
|
||||
/* force a fake read at max offset, to get max logical_offset (will be reset next read) */
|
||||
xwma_konami_io_read(streamfile, buf, 0x7FFFFFFF, 1, data);
|
||||
data->logical_size = data->logical_offset;
|
||||
|
||||
return data->logical_size;
|
||||
}
|
||||
|
||||
/* Handles de-padding Konami XWMA blocked streams */
|
||||
static STREAMFILE* setup_xwma_konami_streamfile(STREAMFILE *streamFile, off_t stream_offset, size_t block_align) {
|
||||
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
|
||||
xwma_konami_io_data io_data = {0};
|
||||
size_t io_data_size = sizeof(xwma_konami_io_data);
|
||||
|
||||
io_data.stream_offset = stream_offset;
|
||||
io_data.stream_size = get_streamfile_size(streamFile) - stream_offset;
|
||||
io_data.block_align = block_align;
|
||||
io_data.logical_offset = -1; /* force phys offset reset */
|
||||
|
||||
/* setup subfile */
|
||||
new_streamFile = open_wrap_streamfile(streamFile);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
new_streamFile = open_io_streamfile(new_streamFile, &io_data,io_data_size, xwma_konami_io_read,xwma_konami_io_size);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
new_streamFile = open_buffer_streamfile(new_streamFile,0);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
return temp_streamFile;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_streamFile);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* _XWMA_KONAMI_STREAMFILE_H_ */
|
||||
#ifndef _XWMA_KONAMI_STREAMFILE_H_
|
||||
#define _XWMA_KONAMI_STREAMFILE_H_
|
||||
#include "deblock_streamfile.h"
|
||||
|
||||
static void block_callback(STREAMFILE* sf, deblock_io_data* data) {
|
||||
data->block_size = align_size_to_block(data->cfg.chunk_size, 0x10);
|
||||
data->data_size = data->cfg.chunk_size;
|
||||
data->skip_size = 0x00;
|
||||
}
|
||||
|
||||
/* De-pads Konami XWMA streams */
|
||||
static STREAMFILE* setup_xwma_konami_streamfile(STREAMFILE* sf, off_t stream_offset, size_t block_align) {
|
||||
STREAMFILE* new_sf = NULL;
|
||||
deblock_config_t cfg = {0};
|
||||
|
||||
cfg.stream_start = stream_offset;
|
||||
cfg.chunk_size = block_align;
|
||||
cfg.block_callback = block_callback;
|
||||
|
||||
new_sf = open_wrap_streamfile(sf);
|
||||
new_sf = open_io_deblock_streamfile_f(new_sf, &cfg);
|
||||
//new_sf = open_buffer_streamfile_f(new_sf, 0);
|
||||
return new_sf;
|
||||
}
|
||||
|
||||
#endif /* _XWMA_KONAMI_STREAMFILE_H_ */
|
||||
|
|
|
@ -518,8 +518,8 @@ typedef struct {
|
|||
size_t data_size;
|
||||
size_t (*read_callback)(STREAMFILE *, uint8_t *, off_t, size_t, void*); /* custom read to modify data before copying into buffer */
|
||||
size_t (*size_callback)(STREAMFILE *, void*); /* size when custom reads make data smaller/bigger than underlying streamfile */
|
||||
//todo would need to make sure re-opened streamfiles work with this, maybe should use init_data_callback per call
|
||||
//size_t (*close_data_callback)(STREAMFILE *, void*); /* called during close, allows to free stuff in data */
|
||||
int (*init_callback)(STREAMFILE*, void*); /* init the data struct members somehow, return >= 0 if ok */
|
||||
void (*close_callback)(STREAMFILE*, void*); /* close the data struct members somehow */
|
||||
} IO_STREAMFILE;
|
||||
|
||||
static size_t io_read(IO_STREAMFILE *streamfile, uint8_t *dst, off_t offset, size_t length) {
|
||||
|
@ -538,24 +538,25 @@ static void io_get_name(IO_STREAMFILE *streamfile, char *buffer, size_t length)
|
|||
streamfile->inner_sf->get_name(streamfile->inner_sf, buffer, length); /* default */
|
||||
}
|
||||
static STREAMFILE* io_open(IO_STREAMFILE *streamfile, const char * const filename, size_t buffersize) {
|
||||
//todo should have some flag to decide if opening other files with IO
|
||||
STREAMFILE *new_inner_sf = streamfile->inner_sf->open(streamfile->inner_sf,filename,buffersize);
|
||||
return open_io_streamfile(new_inner_sf, streamfile->data, streamfile->data_size, streamfile->read_callback, streamfile->size_callback);
|
||||
return open_io_streamfile_ex(new_inner_sf, streamfile->data, streamfile->data_size, streamfile->read_callback, streamfile->size_callback, streamfile->init_callback, streamfile->close_callback);
|
||||
}
|
||||
static void io_close(IO_STREAMFILE *streamfile) {
|
||||
if (streamfile->close_callback)
|
||||
streamfile->close_callback(streamfile->inner_sf, streamfile->data);
|
||||
streamfile->inner_sf->close(streamfile->inner_sf);
|
||||
free(streamfile->data);
|
||||
free(streamfile);
|
||||
}
|
||||
|
||||
STREAMFILE* open_io_streamfile(STREAMFILE *streamfile, void *data, size_t data_size, void *read_callback, void *size_callback) {
|
||||
STREAMFILE* open_io_streamfile_ex(STREAMFILE *streamfile, void *data, size_t data_size, void *read_callback, void *size_callback, void* init_callback, void* close_callback) {
|
||||
IO_STREAMFILE *this_sf = NULL;
|
||||
|
||||
if (!streamfile) return NULL;
|
||||
if ((data && !data_size) || (!data && data_size)) return NULL;
|
||||
if (!streamfile) goto fail;
|
||||
if ((data && !data_size) || (!data && data_size)) goto fail;
|
||||
|
||||
this_sf = calloc(1,sizeof(IO_STREAMFILE));
|
||||
if (!this_sf) return NULL;
|
||||
if (!this_sf) goto fail;
|
||||
|
||||
/* set callbacks and internals */
|
||||
this_sf->sf.read = (void*)io_read;
|
||||
|
@ -569,25 +570,42 @@ STREAMFILE* open_io_streamfile(STREAMFILE *streamfile, void *data, size_t data_s
|
|||
this_sf->inner_sf = streamfile;
|
||||
if (data) {
|
||||
this_sf->data = malloc(data_size);
|
||||
if (!this_sf->data) {
|
||||
free(this_sf);
|
||||
return NULL;
|
||||
}
|
||||
if (!this_sf->data) goto fail;
|
||||
memcpy(this_sf->data, data, data_size);
|
||||
}
|
||||
this_sf->data_size = data_size;
|
||||
this_sf->read_callback = read_callback;
|
||||
this_sf->size_callback = size_callback;
|
||||
this_sf->init_callback = init_callback;
|
||||
this_sf->close_callback = close_callback;
|
||||
|
||||
if (this_sf->init_callback) {
|
||||
int ok = this_sf->init_callback(this_sf->inner_sf, this_sf->data);
|
||||
if (ok < 0) goto fail;
|
||||
}
|
||||
|
||||
return &this_sf->sf;
|
||||
|
||||
fail:
|
||||
if (this_sf) free(this_sf->data);
|
||||
free(this_sf);
|
||||
return NULL;
|
||||
}
|
||||
STREAMFILE* open_io_streamfile_f(STREAMFILE *streamfile, void *data, size_t data_size, void *read_callback, void *size_callback) {
|
||||
STREAMFILE *new_sf = open_io_streamfile(streamfile, data, data_size, read_callback, size_callback);
|
||||
|
||||
STREAMFILE* open_io_streamfile_ex_f(STREAMFILE *streamfile, void *data, size_t data_size, void *read_callback, void *size_callback, void* init_callback, void* close_callback) {
|
||||
STREAMFILE *new_sf = open_io_streamfile_ex(streamfile, data, data_size, read_callback, size_callback, init_callback, close_callback);
|
||||
if (!new_sf)
|
||||
close_streamfile(streamfile);
|
||||
return new_sf;
|
||||
}
|
||||
|
||||
STREAMFILE* open_io_streamfile(STREAMFILE *streamfile, void *data, size_t data_size, void *read_callback, void *size_callback) {
|
||||
return open_io_streamfile_ex(streamfile, data, data_size, read_callback, size_callback, NULL, NULL);
|
||||
}
|
||||
STREAMFILE* open_io_streamfile_f(STREAMFILE *streamfile, void *data, size_t data_size, void *read_callback, void *size_callback) {
|
||||
return open_io_streamfile_ex_f(streamfile, data, data_size, read_callback, size_callback, NULL, NULL);
|
||||
}
|
||||
|
||||
/* **************************************************** */
|
||||
|
||||
typedef struct {
|
||||
|
@ -989,11 +1007,14 @@ fail:
|
|||
if (buf) buf[0] = '\0';
|
||||
return 0;
|
||||
}
|
||||
size_t read_string_utf16le(char *buf, size_t buf_size, off_t offset, STREAMFILE *sf) {
|
||||
|
||||
size_t read_string_utf16(char *buf, size_t buf_size, off_t offset, STREAMFILE *sf, int big_endian) {
|
||||
size_t pos, offpos;
|
||||
uint16_t (*read_u16)(off_t,STREAMFILE*) = big_endian ? read_u16be : read_u16le;
|
||||
|
||||
|
||||
for (pos = 0, offpos = 0; pos < buf_size; pos++, offpos += 2) {
|
||||
char c = read_u16le(offset + offpos, sf) & 0xFF; /* lower byte for now */
|
||||
char c = read_u16(offset + offpos, sf) & 0xFF; /* lower byte for now */
|
||||
if (buf) buf[pos] = c;
|
||||
if (c == '\0')
|
||||
return pos;
|
||||
|
@ -1010,6 +1031,13 @@ fail:
|
|||
return 0;
|
||||
}
|
||||
|
||||
size_t read_string_utf16le(char* buf, size_t buf_size, off_t offset, STREAMFILE* sf) {
|
||||
return read_string_utf16(buf, buf_size, offset, sf, 0);
|
||||
}
|
||||
size_t read_string_utf16be(char* buf, size_t buf_size, off_t offset, STREAMFILE* sf) {
|
||||
return read_string_utf16(buf, buf_size, offset, sf, 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
size_t read_key_file(uint8_t *buf, size_t buf_size, STREAMFILE *sf) {
|
||||
|
|
|
@ -109,9 +109,14 @@ STREAMFILE* open_clamp_streamfile(STREAMFILE *streamfile, off_t start, size_t si
|
|||
STREAMFILE* open_clamp_streamfile_f(STREAMFILE *streamfile, off_t start, size_t size);
|
||||
|
||||
/* Opens a STREAMFILE that uses custom IO for streamfile reads.
|
||||
* Can be used to modify data on the fly (ex. decryption), or even transform it from a format to another. */
|
||||
* Can be used to modify data on the fly (ex. decryption), or even transform it from a format to another.
|
||||
* Data is an optional state struct of some size what will be malloc+copied on open. */
|
||||
STREAMFILE* open_io_streamfile(STREAMFILE *streamfile, void *data, size_t data_size, void *read_callback, void *size_callback);
|
||||
STREAMFILE* open_io_streamfile_f(STREAMFILE *streamfile, void *data, size_t data_size, void *read_callback, void *size_callback);
|
||||
/* Same, but calls init on SF open and close on close, when malloc/free is needed.
|
||||
* Data struct may be used to hold malloc'd pointers and stuff. */
|
||||
STREAMFILE* open_io_streamfile_ex(STREAMFILE *streamfile, void *data, size_t data_size, void *read_callback, void *size_callback, void* init_callback, void* close_callback);
|
||||
STREAMFILE* open_io_streamfile_ex_f(STREAMFILE *streamfile, void *data, size_t data_size, void *read_callback, void *size_callback, void* init_callback, void* close_callback);
|
||||
|
||||
/* Opens a STREAMFILE that reports a fake name, but still re-opens itself properly.
|
||||
* Can be used to trick a meta's extension check (to call from another, with a modified SF).
|
||||
|
@ -331,7 +336,9 @@ size_t read_line(char *buf, int buf_size, off_t offset, STREAMFILE *sf, int *p_l
|
|||
/* reads a c-string (ANSI only), up to bufsize or NULL, returning size. buf is optional (works as get_string_size). */
|
||||
size_t read_string(char *buf, size_t buf_size, off_t offset, STREAMFILE *sf);
|
||||
/* reads a UTF16 string... but actually only as ANSI (discards the upper byte) */
|
||||
size_t read_string_utf16le(char *buf, size_t buf_size, off_t offset, STREAMFILE *sf);
|
||||
size_t read_string_utf16(char* buf, size_t buf_size, off_t offset, STREAMFILE* sf, int big_endian);
|
||||
size_t read_string_utf16le(char* buf, size_t buf_size, off_t offset, STREAMFILE* sf);
|
||||
size_t read_string_utf16be(char* buf, size_t buf_size, off_t offset, STREAMFILE* sf);
|
||||
|
||||
/* Opens a file containing decryption keys and copies to buffer.
|
||||
* Tries "(name.ext)key" (per song), "(.ext)key" (per folder) keynames.
|
||||
|
|
|
@ -58,7 +58,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
|
|||
init_vgmstream_ngc_str,
|
||||
init_vgmstream_ea_schl,
|
||||
init_vgmstream_caf,
|
||||
init_vgmstream_ps2_vpk,
|
||||
init_vgmstream_vpk,
|
||||
init_vgmstream_genh,
|
||||
#ifdef VGM_USE_VORBIS
|
||||
init_vgmstream_ogg_vorbis,
|
||||
|
@ -490,9 +490,12 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
|
|||
init_vgmstream_tgc,
|
||||
init_vgmstream_kwb,
|
||||
init_vgmstream_lrmd,
|
||||
init_vgmstream_bkhd,
|
||||
init_vgmstream_bkhd_fx,
|
||||
|
||||
/* 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 */
|
||||
init_vgmstream_encrypted, /* encrypted stuff */
|
||||
init_vgmstream_raw_int, /* .int raw PCM */
|
||||
init_vgmstream_ps_headerless, /* tries to detect a bunch of PS-ADPCM formats */
|
||||
init_vgmstream_raw_snds, /* .snds raw SNDS IMA (*after* ps_headerless) */
|
||||
|
@ -661,6 +664,10 @@ void reset_vgmstream(VGMSTREAM * vgmstream) {
|
|||
}
|
||||
#endif
|
||||
|
||||
if (vgmstream->coding_type == coding_CIRCUS_VQ) {
|
||||
reset_circus_vq(vgmstream->codec_data);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_RELIC) {
|
||||
reset_relic(vgmstream->codec_data);
|
||||
}
|
||||
|
@ -832,6 +839,11 @@ void close_vgmstream(VGMSTREAM * vgmstream) {
|
|||
}
|
||||
#endif
|
||||
|
||||
if (vgmstream->coding_type == coding_CIRCUS_VQ) {
|
||||
free_circus_vq(vgmstream->codec_data);
|
||||
vgmstream->codec_data = NULL;
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_RELIC) {
|
||||
free_relic(vgmstream->codec_data);
|
||||
vgmstream->codec_data = NULL;
|
||||
|
@ -1097,6 +1109,8 @@ void render_vgmstream(sample_t * buffer, int32_t sample_count, VGMSTREAM * vgmst
|
|||
case layout_blocked_h4m:
|
||||
case layout_blocked_xa_aiff:
|
||||
case layout_blocked_vs_square:
|
||||
case layout_blocked_vid1:
|
||||
case layout_blocked_ubi_sce:
|
||||
render_vgmstream_blocked(buffer,sample_count,vgmstream);
|
||||
break;
|
||||
case layout_segmented:
|
||||
|
@ -1288,6 +1302,8 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
|
|||
return 0; /* varies per mode */
|
||||
case coding_EA_MT:
|
||||
return 0; /* 432, but variable in looped files */
|
||||
case coding_CIRCUS_VQ:
|
||||
return 0;
|
||||
case coding_RELIC:
|
||||
return 0; /* 512 */
|
||||
case coding_CRI_HCA:
|
||||
|
@ -1775,6 +1791,9 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
|
|||
samples_to_do,vgmstream->channels);
|
||||
break;
|
||||
#endif
|
||||
case coding_CIRCUS_VQ:
|
||||
decode_circus_vq(vgmstream->codec_data, buffer+samples_written*vgmstream->channels, samples_to_do, vgmstream->channels);
|
||||
break;
|
||||
case coding_RELIC:
|
||||
decode_relic(&vgmstream->ch[0], vgmstream->codec_data, buffer+samples_written*vgmstream->channels,
|
||||
samples_to_do);
|
||||
|
@ -1930,7 +1949,7 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
|
|||
case coding_UBI_IMA:
|
||||
for (ch = 0; ch < vgmstream->channels; ch++) {
|
||||
decode_ubi_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch,
|
||||
vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch);
|
||||
vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch, vgmstream->codec_config);
|
||||
}
|
||||
break;
|
||||
case coding_H4M_IMA:
|
||||
|
@ -2227,6 +2246,10 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) {
|
|||
|
||||
/* prepare certain codecs' internal state for looping */
|
||||
|
||||
if (vgmstream->coding_type == coding_CIRCUS_VQ) {
|
||||
seek_circus_vq(vgmstream->codec_data, vgmstream->loop_sample);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_RELIC) {
|
||||
seek_relic(vgmstream->codec_data, vgmstream->loop_sample);
|
||||
}
|
||||
|
@ -2426,12 +2449,12 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
|
|||
snprintf(temp,TEMPSIZE, "interleave: %#x bytes\n", (int32_t)vgmstream->interleave_block_size);
|
||||
concatn(length,desc,temp);
|
||||
|
||||
if (vgmstream->interleave_first_block_size) {
|
||||
if (vgmstream->interleave_first_block_size && vgmstream->interleave_first_block_size != vgmstream->interleave_block_size) {
|
||||
snprintf(temp,TEMPSIZE, "interleave first block: %#x bytes\n", (int32_t)vgmstream->interleave_first_block_size);
|
||||
concatn(length,desc,temp);
|
||||
}
|
||||
|
||||
if (vgmstream->interleave_last_block_size) {
|
||||
if (vgmstream->interleave_last_block_size && vgmstream->interleave_last_block_size != vgmstream->interleave_block_size) {
|
||||
snprintf(temp,TEMPSIZE, "interleave last block: %#x bytes\n", (int32_t)vgmstream->interleave_last_block_size);
|
||||
concatn(length,desc,temp);
|
||||
}
|
||||
|
|
|
@ -190,7 +190,7 @@ typedef enum {
|
|||
coding_UBI_ADPCM, /* Ubisoft 4/6-bit ADPCM */
|
||||
|
||||
coding_EA_MT, /* Electronic Arts MicroTalk (linear-predictive speech codec) */
|
||||
|
||||
coding_CIRCUS_VQ, /* Circus VQ */
|
||||
coding_RELIC, /* Relic Codec (DCT-based) */
|
||||
coding_CRI_HCA, /* CRI High Compression Audio (MDCT-based) */
|
||||
|
||||
|
@ -283,6 +283,8 @@ typedef enum {
|
|||
layout_blocked_h4m, /* H4M video */
|
||||
layout_blocked_xa_aiff, /* XA in AIFF files [Crusader: No Remorse (SAT), Road Rash (3DO)] */
|
||||
layout_blocked_vs_square,
|
||||
layout_blocked_vid1,
|
||||
layout_blocked_ubi_sce,
|
||||
|
||||
/* otherwise odd */
|
||||
layout_segmented, /* song divided in segments (song sections) */
|
||||
|
@ -629,7 +631,7 @@ typedef enum {
|
|||
meta_VXN, /* Gameloft mobile games */
|
||||
meta_EA_SNR_SNS, /* Electronic Arts SNR+SNS (Burnout Paradise) */
|
||||
meta_EA_SPS, /* Electronic Arts SPS (Burnout Crash) */
|
||||
meta_NGC_VID1, /* Neversoft .ogg (Gun GC) */
|
||||
meta_VID1,
|
||||
meta_PC_FLX, /* Ultima IX PC */
|
||||
meta_MOGG, /* Harmonix Music Systems MOGG Vorbis */
|
||||
meta_OGG_VORBIS, /* Ogg Vorbis */
|
||||
|
@ -732,6 +734,7 @@ typedef enum {
|
|||
meta_TGC,
|
||||
meta_KWB,
|
||||
meta_LRMD,
|
||||
meta_WWISE_FX,
|
||||
} meta_t;
|
||||
|
||||
/* standard WAVEFORMATEXTENSIBLE speaker positions */
|
||||
|
|
Loading…
Reference in New Issue