Update vgmstream to r1896 #390
52 changed files with 2256 additions and 729 deletions
|
@ -7,6 +7,15 @@
|
|||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
01BC2CB92B5C300C0026C0A4 /* nxa1.c in Sources */ = {isa = PBXBuildFile; fileRef = 01BC2CB22B5C300C0026C0A4 /* nxa1.c */; };
|
||||
01BC2CBA2B5C300C0026C0A4 /* ktsr_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 01BC2CB32B5C300C0026C0A4 /* ktsr_streamfile.h */; };
|
||||
01BC2CBB2B5C300C0026C0A4 /* adp_qd.c in Sources */ = {isa = PBXBuildFile; fileRef = 01BC2CB42B5C300C0026C0A4 /* adp_qd.c */; };
|
||||
01BC2CBC2B5C300C0026C0A4 /* adx_monster.c in Sources */ = {isa = PBXBuildFile; fileRef = 01BC2CB52B5C300C0026C0A4 /* adx_monster.c */; };
|
||||
01BC2CBD2B5C300C0026C0A4 /* awc_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 01BC2CB62B5C300C0026C0A4 /* awc_streamfile.h */; };
|
||||
01BC2CBE2B5C300C0026C0A4 /* nxof.c in Sources */ = {isa = PBXBuildFile; fileRef = 01BC2CB72B5C300C0026C0A4 /* nxof.c */; };
|
||||
01BC2CBF2B5C300C0026C0A4 /* str_wav_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 01BC2CB82B5C300C0026C0A4 /* str_wav_streamfile.h */; };
|
||||
01BC2CC22B5C31480026C0A4 /* cipher_blowfish.c in Sources */ = {isa = PBXBuildFile; fileRef = 01BC2CC02B5C31480026C0A4 /* cipher_blowfish.c */; };
|
||||
01BC2CC32B5C31480026C0A4 /* cipher_blowfish.h in Headers */ = {isa = PBXBuildFile; fileRef = 01BC2CC12B5C31480026C0A4 /* cipher_blowfish.h */; };
|
||||
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 */; };
|
||||
|
@ -56,7 +65,6 @@
|
|||
8306B0DB20984590000302D4 /* nxap.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0C02098458C000302D4 /* nxap.c */; };
|
||||
8306B0DC20984590000302D4 /* sthd.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0C12098458C000302D4 /* sthd.c */; };
|
||||
8306B0DD20984590000302D4 /* waf.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0C22098458C000302D4 /* waf.c */; };
|
||||
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 */; };
|
||||
8306B0E220984590000302D4 /* smv.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0C72098458D000302D4 /* smv.c */; };
|
||||
|
@ -145,7 +153,6 @@
|
|||
83269DD22399F5DE00F49FE3 /* nus3bank_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 83269DD02399F5DD00F49FE3 /* nus3bank_streamfile.h */; };
|
||||
83269DD32399F5DE00F49FE3 /* ivag.c in Sources */ = {isa = PBXBuildFile; fileRef = 83269DD12399F5DE00F49FE3 /* ivag.c */; };
|
||||
83299FD01E7660C7003A3242 /* bik.c in Sources */ = {isa = PBXBuildFile; fileRef = 83299FCE1E7660C7003A3242 /* bik.c */; };
|
||||
83299FD11E7660C7003A3242 /* dsp_adx.c in Sources */ = {isa = PBXBuildFile; fileRef = 83299FCF1E7660C7003A3242 /* dsp_adx.c */; };
|
||||
832BF7FF21E050B7006F50F1 /* circus_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 832BF7FC21E050B6006F50F1 /* circus_decoder.c */; };
|
||||
832BF80021E050B7006F50F1 /* mpeg_custom_utils_eamp3.c in Sources */ = {isa = PBXBuildFile; fileRef = 832BF7FD21E050B7006F50F1 /* mpeg_custom_utils_eamp3.c */; };
|
||||
832BF80521E050DC006F50F1 /* blocked_mul.c in Sources */ = {isa = PBXBuildFile; fileRef = 832BF80221E050DB006F50F1 /* blocked_mul.c */; };
|
||||
|
@ -238,7 +245,6 @@
|
|||
8349A9121FE6258200E26435 /* vsf_tta.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8F91FE6257E00E26435 /* vsf_tta.c */; };
|
||||
8349A9141FE6258200E26435 /* omu.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8FB1FE6257F00E26435 /* omu.c */; };
|
||||
8349A9161FE6258200E26435 /* flx.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8FD1FE6257F00E26435 /* flx.c */; };
|
||||
8349A9171FE6258200E26435 /* pc_adp_otns.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8FE1FE6257F00E26435 /* pc_adp_otns.c */; };
|
||||
8349A9181FE6258200E26435 /* ea_1snh.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8FF1FE6258000E26435 /* ea_1snh.c */; };
|
||||
8349A9191FE6258200E26435 /* afc.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A9001FE6258000E26435 /* afc.c */; };
|
||||
8349A91A1FE6258200E26435 /* vxn.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A9011FE6258000E26435 /* vxn.c */; };
|
||||
|
@ -286,7 +292,6 @@
|
|||
834FE101215C79ED000A5D3D /* csmp.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0D8215C79EA000A5D3D /* csmp.c */; };
|
||||
834FE102215C79ED000A5D3D /* rfrm.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0D9215C79EA000A5D3D /* rfrm.c */; };
|
||||
834FE103215C79ED000A5D3D /* ea_schl_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 834FE0DA215C79EA000A5D3D /* ea_schl_streamfile.h */; };
|
||||
834FE104215C79ED000A5D3D /* nxa.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0DB215C79EA000A5D3D /* nxa.c */; };
|
||||
834FE105215C79ED000A5D3D /* xmd.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0DC215C79EA000A5D3D /* xmd.c */; };
|
||||
834FE106215C79ED000A5D3D /* utk.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0DD215C79EB000A5D3D /* utk.c */; };
|
||||
834FE107215C79ED000A5D3D /* mib_mih.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0DE215C79EB000A5D3D /* mib_mih.c */; };
|
||||
|
@ -852,6 +857,15 @@
|
|||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
01BC2CB22B5C300C0026C0A4 /* nxa1.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nxa1.c; sourceTree = "<group>"; };
|
||||
01BC2CB32B5C300C0026C0A4 /* ktsr_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ktsr_streamfile.h; sourceTree = "<group>"; };
|
||||
01BC2CB42B5C300C0026C0A4 /* adp_qd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = adp_qd.c; sourceTree = "<group>"; };
|
||||
01BC2CB52B5C300C0026C0A4 /* adx_monster.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = adx_monster.c; sourceTree = "<group>"; };
|
||||
01BC2CB62B5C300C0026C0A4 /* awc_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = awc_streamfile.h; sourceTree = "<group>"; };
|
||||
01BC2CB72B5C300C0026C0A4 /* nxof.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nxof.c; sourceTree = "<group>"; };
|
||||
01BC2CB82B5C300C0026C0A4 /* str_wav_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = str_wav_streamfile.h; sourceTree = "<group>"; };
|
||||
01BC2CC02B5C31480026C0A4 /* cipher_blowfish.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cipher_blowfish.c; sourceTree = "<group>"; };
|
||||
01BC2CC12B5C31480026C0A4 /* cipher_blowfish.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cipher_blowfish.h; sourceTree = "<group>"; };
|
||||
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>"; };
|
||||
|
@ -901,7 +915,6 @@
|
|||
8306B0C02098458C000302D4 /* nxap.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nxap.c; sourceTree = "<group>"; };
|
||||
8306B0C12098458C000302D4 /* sthd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sthd.c; sourceTree = "<group>"; };
|
||||
8306B0C22098458C000302D4 /* waf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = waf.c; sourceTree = "<group>"; };
|
||||
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>"; };
|
||||
8306B0C72098458D000302D4 /* smv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = smv.c; sourceTree = "<group>"; };
|
||||
|
@ -990,7 +1003,6 @@
|
|||
83269DD02399F5DD00F49FE3 /* nus3bank_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nus3bank_streamfile.h; sourceTree = "<group>"; };
|
||||
83269DD12399F5DE00F49FE3 /* ivag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ivag.c; sourceTree = "<group>"; };
|
||||
83299FCE1E7660C7003A3242 /* bik.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bik.c; sourceTree = "<group>"; };
|
||||
83299FCF1E7660C7003A3242 /* dsp_adx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dsp_adx.c; sourceTree = "<group>"; };
|
||||
832BF7FC21E050B6006F50F1 /* circus_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = circus_decoder.c; sourceTree = "<group>"; };
|
||||
832BF7FD21E050B7006F50F1 /* mpeg_custom_utils_eamp3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mpeg_custom_utils_eamp3.c; sourceTree = "<group>"; };
|
||||
832BF80221E050DB006F50F1 /* blocked_mul.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_mul.c; sourceTree = "<group>"; };
|
||||
|
@ -1083,7 +1095,6 @@
|
|||
8349A8F91FE6257E00E26435 /* vsf_tta.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vsf_tta.c; sourceTree = "<group>"; };
|
||||
8349A8FB1FE6257F00E26435 /* omu.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = omu.c; sourceTree = "<group>"; };
|
||||
8349A8FD1FE6257F00E26435 /* flx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = flx.c; sourceTree = "<group>"; };
|
||||
8349A8FE1FE6257F00E26435 /* pc_adp_otns.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pc_adp_otns.c; sourceTree = "<group>"; };
|
||||
8349A8FF1FE6258000E26435 /* ea_1snh.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ea_1snh.c; sourceTree = "<group>"; };
|
||||
8349A9001FE6258000E26435 /* afc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = afc.c; sourceTree = "<group>"; };
|
||||
8349A9011FE6258000E26435 /* vxn.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vxn.c; sourceTree = "<group>"; };
|
||||
|
@ -1131,7 +1142,6 @@
|
|||
834FE0D8215C79EA000A5D3D /* csmp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = csmp.c; sourceTree = "<group>"; };
|
||||
834FE0D9215C79EA000A5D3D /* rfrm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rfrm.c; sourceTree = "<group>"; };
|
||||
834FE0DA215C79EA000A5D3D /* ea_schl_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ea_schl_streamfile.h; sourceTree = "<group>"; };
|
||||
834FE0DB215C79EA000A5D3D /* nxa.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nxa.c; sourceTree = "<group>"; };
|
||||
834FE0DC215C79EA000A5D3D /* xmd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xmd.c; sourceTree = "<group>"; };
|
||||
834FE0DD215C79EB000A5D3D /* utk.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = utk.c; sourceTree = "<group>"; };
|
||||
834FE0DE215C79EB000A5D3D /* mib_mih.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mib_mih.c; sourceTree = "<group>"; };
|
||||
|
@ -2001,6 +2011,13 @@
|
|||
836F6E2718BDC2180095E648 /* meta */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
01BC2CB42B5C300C0026C0A4 /* adp_qd.c */,
|
||||
01BC2CB52B5C300C0026C0A4 /* adx_monster.c */,
|
||||
01BC2CB62B5C300C0026C0A4 /* awc_streamfile.h */,
|
||||
01BC2CB32B5C300C0026C0A4 /* ktsr_streamfile.h */,
|
||||
01BC2CB22B5C300C0026C0A4 /* nxa1.c */,
|
||||
01BC2CB72B5C300C0026C0A4 /* nxof.c */,
|
||||
01BC2CB82B5C300C0026C0A4 /* str_wav_streamfile.h */,
|
||||
836F6E2918BDC2180095E648 /* 2dx9.c */,
|
||||
83C727FD22BC893900678B4A /* 9tav_streamfile.h */,
|
||||
83C727FE22BC893900678B4A /* 9tav.c */,
|
||||
|
@ -2042,7 +2059,6 @@
|
|||
83A21F7C201D897F000F04B9 /* atx.c */,
|
||||
83EED5D2203A8BC7008BEB45 /* aus.c */,
|
||||
83A16D2722D2ADE700B90C4C /* awb.c */,
|
||||
8306B0C32098458C000302D4 /* awc_xma_streamfile.h */,
|
||||
83AA5D201F6E2F9B0020821C /* awc.c */,
|
||||
83FBB1702A4FF4EC00CD0580 /* awd.c */,
|
||||
836F6E3618BDC2180095E648 /* baf.c */,
|
||||
|
@ -2089,7 +2105,6 @@
|
|||
836F6E4318BDC2180095E648 /* dmsg_segh.c */,
|
||||
83AA7F742519C041004C5298 /* dsb.c */,
|
||||
8351F32C2212B57000A606E4 /* dsf.c */,
|
||||
83299FCF1E7660C7003A3242 /* dsp_adx.c */,
|
||||
8349A8FF1FE6258000E26435 /* ea_1snh.c */,
|
||||
83AFABBA23795202002F3947 /* ea_eaac_opus_streamfile.h */,
|
||||
8306B0BD2098458B000302D4 /* ea_eaac_streamfile.h */,
|
||||
|
@ -2236,7 +2251,6 @@
|
|||
834FE0D1215C79E9000A5D3D /* nus3bank.c */,
|
||||
836F6E8118BDC2180095E648 /* nwa.c */,
|
||||
832BF81421E0514A006F50F1 /* nwav.c */,
|
||||
834FE0DB215C79EA000A5D3D /* nxa.c */,
|
||||
8306B0C02098458C000302D4 /* nxap.c */,
|
||||
832BF81321E05149006F50F1 /* ogg_opus.c */,
|
||||
835C883522CC17BE001B4B3F /* ogg_vorbis_streamfile.h */,
|
||||
|
@ -2249,7 +2263,6 @@
|
|||
836F6E8318BDC2180095E648 /* otm.c */,
|
||||
836F6E8418BDC2180095E648 /* p3d.c */,
|
||||
831BA6171EAC61A500CF89B0 /* pasx.c */,
|
||||
8349A8FE1FE6257F00E26435 /* pc_adp_otns.c */,
|
||||
8349A8F01FE6257C00E26435 /* pc_ast.c */,
|
||||
836F6E8618BDC2180095E648 /* pc_mxst.c */,
|
||||
8306B0D12098458F000302D4 /* pcm_sre.c */,
|
||||
|
@ -2483,6 +2496,8 @@
|
|||
83D26A7C26E66DC2001A9475 /* util */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
01BC2CC02B5C31480026C0A4 /* cipher_blowfish.c */,
|
||||
01BC2CC12B5C31480026C0A4 /* cipher_blowfish.h */,
|
||||
833E82F92A28595A00CD0580 /* bitstream_lsb.h */,
|
||||
836DF620298F83F400CD0580 /* bitstream_msb.h */,
|
||||
833E82C72A28566700CD0580 /* channel_mappings.h */,
|
||||
|
@ -2588,6 +2603,7 @@
|
|||
83256CD328666C620036D9C0 /* getcpuflags.h in Headers */,
|
||||
8349A90F1FE6258200E26435 /* aix_streamfile.h in Headers */,
|
||||
83B46FD52707FB9A00847FC9 /* endianness.h in Headers */,
|
||||
01BC2CBF2B5C300C0026C0A4 /* str_wav_streamfile.h in Headers */,
|
||||
836F705718BDC2190095E648 /* util.h in Headers */,
|
||||
83256CC128666C620036D9C0 /* index.h in Headers */,
|
||||
834FE0ED215C79ED000A5D3D /* fsb_interleave_streamfile.h in Headers */,
|
||||
|
@ -2615,6 +2631,7 @@
|
|||
8375737721F950ED00F01AF5 /* ubi_sb_streamfile.h in Headers */,
|
||||
839E21E01F2EDAF100EE54D7 /* vorbis_custom_data_fsb.h in Headers */,
|
||||
833E82F42A28587D00CD0580 /* companion_files.h in Headers */,
|
||||
01BC2CBA2B5C300C0026C0A4 /* ktsr_streamfile.h in Headers */,
|
||||
83031EDB243C510500C3F3E0 /* xnb_lz4mg.h in Headers */,
|
||||
836F705418BDC2190095E648 /* streamfile.h in Headers */,
|
||||
835B9B922730BF2D00F87EE3 /* hca_bf.h in Headers */,
|
||||
|
@ -2636,14 +2653,15 @@
|
|||
83C0C7692AA437E10056AFD8 /* mixing_fades.h in Headers */,
|
||||
839E21E51F2EDAF100EE54D7 /* vorbis_custom_decoder.h in Headers */,
|
||||
833E82C82A28566700CD0580 /* channel_mappings.h in Headers */,
|
||||
01BC2CBD2B5C300C0026C0A4 /* awc_streamfile.h in Headers */,
|
||||
83256CC028666C620036D9C0 /* synth_8bit.h in Headers */,
|
||||
833E82F32A28587D00CD0580 /* reader_sf.h in Headers */,
|
||||
837CEADB23487E8300E62A4A /* bgw_streamfile.h in Headers */,
|
||||
01BC2CC32B5C31480026C0A4 /* cipher_blowfish.h in Headers */,
|
||||
837CEB0323487F2C00E62A4A /* xavs_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 */,
|
||||
83256CE528666C620036D9C0 /* true.h in Headers */,
|
||||
|
@ -2837,7 +2855,6 @@
|
|||
files = (
|
||||
839E21E21F2EDAF100EE54D7 /* vorbis_custom_utils_wwise.c in Sources */,
|
||||
83E56BA51F2EE3520026BC60 /* vorbis_custom_utils_ogl.c in Sources */,
|
||||
834FE104215C79ED000A5D3D /* nxa.c in Sources */,
|
||||
8349A9071FE6258200E26435 /* dec.c in Sources */,
|
||||
839E21E91F2EDAF100EE54D7 /* vorbis_custom_utils_sk.c in Sources */,
|
||||
83C7281422BC893D00678B4A /* ffdl.c in Sources */,
|
||||
|
@ -2914,6 +2931,7 @@
|
|||
836F6F7018BDC2190095E648 /* apple_caff.c in Sources */,
|
||||
836F6FB618BDC2190095E648 /* ngc_sck_dsp.c in Sources */,
|
||||
836F6F2818BDC2190095E648 /* ima_decoder.c in Sources */,
|
||||
01BC2CC22B5C31480026C0A4 /* cipher_blowfish.c in Sources */,
|
||||
837CEB072348809400E62A4A /* seb.c in Sources */,
|
||||
83D26A8126E66DC2001A9475 /* log.c in Sources */,
|
||||
8306B0DD20984590000302D4 /* waf.c in Sources */,
|
||||
|
@ -2945,7 +2963,6 @@
|
|||
839FBFFB26C354E70016A78A /* wxd_wxh.c in Sources */,
|
||||
83EDE5D91A70951A005F5D84 /* btsnd.c in Sources */,
|
||||
8306B0E420984590000302D4 /* wave.c in Sources */,
|
||||
8349A9171FE6258200E26435 /* pc_adp_otns.c in Sources */,
|
||||
836F701E18BDC2190095E648 /* redspark.c in Sources */,
|
||||
8306B0ED20984590000302D4 /* txtp.c in Sources */,
|
||||
836F6FA018BDC2190095E648 /* musx.c in Sources */,
|
||||
|
@ -3088,7 +3105,6 @@
|
|||
836F6F8218BDC2190095E648 /* ea_schl.c in Sources */,
|
||||
834FBCE826BBC7D00095647F /* tantalus_decoder.c in Sources */,
|
||||
836F6F7318BDC2190095E648 /* bcstm.c in Sources */,
|
||||
83299FD11E7660C7003A3242 /* dsp_adx.c in Sources */,
|
||||
836F704018BDC2190095E648 /* wii_sng.c in Sources */,
|
||||
834FE10D215C79ED000A5D3D /* vag.c in Sources */,
|
||||
8350C0551E071881009E0A93 /* xma.c in Sources */,
|
||||
|
@ -3168,6 +3184,7 @@
|
|||
836F6FC718BDC2190095E648 /* pona.c in Sources */,
|
||||
8306B0B820984552000302D4 /* blocked_ws_aud.c in Sources */,
|
||||
836F6FE618BDC2190095E648 /* ps2_mcg.c in Sources */,
|
||||
01BC2CBE2B5C300C0026C0A4 /* nxof.c in Sources */,
|
||||
83AA5D241F6E2F9C0020821C /* awc.c in Sources */,
|
||||
8349A8E91FE6253900E26435 /* blocked_ea_1snh.c in Sources */,
|
||||
8306B0E620984590000302D4 /* msb_msh.c in Sources */,
|
||||
|
@ -3229,6 +3246,7 @@
|
|||
836F6FA418BDC2190095E648 /* nds_hwas.c in Sources */,
|
||||
83A21F8A201D8982000F04B9 /* fsb_encrypted.c in Sources */,
|
||||
8306B0B120984552000302D4 /* blocked_halpst.c in Sources */,
|
||||
01BC2CBB2B5C300C0026C0A4 /* adp_qd.c in Sources */,
|
||||
83D26A8426E66DC2001A9475 /* chunks.c in Sources */,
|
||||
836F6FD018BDC2190095E648 /* ps2_b1s.c in Sources */,
|
||||
83AA5D181F6E2F600020821C /* mpeg_custom_utils_awc.c in Sources */,
|
||||
|
@ -3261,6 +3279,7 @@
|
|||
836F6FB718BDC2190095E648 /* ngc_ssm.c in Sources */,
|
||||
8306B0E920984590000302D4 /* opus.c in Sources */,
|
||||
832BF80021E050B7006F50F1 /* mpeg_custom_utils_eamp3.c in Sources */,
|
||||
01BC2CB92B5C300C0026C0A4 /* nxa1.c in Sources */,
|
||||
83709E051ECBC1A4005C03D3 /* ghs.c in Sources */,
|
||||
8306B0A420984552000302D4 /* segmented.c in Sources */,
|
||||
83A21F86201D8981000F04B9 /* xwc.c in Sources */,
|
||||
|
@ -3358,6 +3377,7 @@
|
|||
833E82E42A2857F700CD0580 /* seek.c in Sources */,
|
||||
834FE0B8215C798C000A5D3D /* acm_decoder_decode.c in Sources */,
|
||||
8399335F2591E8C1001855AF /* ifs.c in Sources */,
|
||||
01BC2CBC2B5C300C0026C0A4 /* adx_monster.c in Sources */,
|
||||
8373342A23F60CDC00DE14DC /* lrmd.c in Sources */,
|
||||
833E82E22A2857F700CD0580 /* decode.c in Sources */,
|
||||
836F6FBD18BDC2190095E648 /* nwa.c in Sources */,
|
||||
|
|
|
@ -418,7 +418,8 @@ int decode_get_samples_per_frame(VGMSTREAM* vgmstream) {
|
|||
case coding_XBOX_IMA_int:
|
||||
case coding_FSB_IMA:
|
||||
case coding_WWISE_IMA:
|
||||
case coding_CD_IMA:
|
||||
case coding_CD_IMA: /* (0x24 - 0x04) * 2 */
|
||||
case coding_CRANKCASE_IMA: /* (0x23 - 0x3) * 2 */
|
||||
return 64;
|
||||
case coding_APPLE_IMA4:
|
||||
return 64;
|
||||
|
@ -654,6 +655,8 @@ int decode_get_frame_size(VGMSTREAM* vgmstream) {
|
|||
case coding_WWISE_IMA:
|
||||
case coding_CD_IMA:
|
||||
return 0x24;
|
||||
case coding_CRANKCASE_IMA:
|
||||
return 0x23;
|
||||
case coding_XBOX_IMA_mch:
|
||||
case coding_FSB_IMA:
|
||||
return 0x24 * vgmstream->channels;
|
||||
|
@ -1264,6 +1267,12 @@ void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_
|
|||
vgmstream->channels, vgmstream->samples_into_block, samples_to_do);
|
||||
}
|
||||
break;
|
||||
case coding_CRANKCASE_IMA:
|
||||
for (ch = 0; ch < vgmstream->channels; ch++) {
|
||||
decode_crankcase_ima(&vgmstream->ch[ch], buffer+ch,
|
||||
vgmstream->channels, vgmstream->samples_into_block, samples_to_do);
|
||||
}
|
||||
break;
|
||||
|
||||
case coding_WS:
|
||||
for (ch = 0; ch < vgmstream->channels; ch++) {
|
||||
|
|
|
@ -330,7 +330,7 @@ circus_handle_t* circus_init(off_t start, uint8_t codec, uint8_t flags) {
|
|||
handle->flags = flags; //(config >> 8) & 0xFF;
|
||||
|
||||
scale_index = (handle->flags & 0xF);
|
||||
if (scale_index > 5) goto fail;
|
||||
if (scale_index >= 5) goto fail;
|
||||
handle->scales = scale_table[scale_index];
|
||||
|
||||
if (handle->codec == XPCM_CODEC_VQ_DEFLATE) {
|
||||
|
|
|
@ -45,6 +45,7 @@ void decode_ubi_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspaci
|
|||
void decode_ubi_sce_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
|
||||
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);
|
||||
void decode_cd_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
void decode_crankcase_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
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);
|
||||
size_t xbox_ima_bytes_to_samples(size_t bytes, int channels);
|
||||
|
|
|
@ -35,8 +35,8 @@ static const int IMA_IndexTable[16] = {
|
|||
|
||||
|
||||
/* Original IMA expansion, using shift+ADDs to avoid MULs (slow back then) */
|
||||
static void std_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t * hist1, int32_t * step_index) {
|
||||
int sample_nibble, sample_decoded, step, delta;
|
||||
static void std_ima_expand_nibble_data(uint8_t byte, int shift, int32_t* hist1, int32_t* index) {
|
||||
int code, sample, step, delta;
|
||||
|
||||
/* simplified through math from:
|
||||
* - diff = (code + 1/2) * (step / 4)
|
||||
|
@ -44,21 +44,26 @@ static void std_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset,
|
|||
* > diff = (step * nibble / 4) + (step / 8)
|
||||
* final diff = [signed] (step / 8) + (step / 4) + (step / 2) + (step) [when code = 4+2+1] */
|
||||
|
||||
sample_nibble = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift)&0xf; /* ADPCM code */
|
||||
sample_decoded = *hist1; /* predictor value */
|
||||
step = ADPCMTable[*step_index]; /* current step */
|
||||
code = (byte >> shift) & 0xf;
|
||||
sample = *hist1; /* predictor value */
|
||||
step = ADPCMTable[*index]; /* current step */
|
||||
|
||||
delta = step >> 3;
|
||||
if (sample_nibble & 1) delta += step >> 2;
|
||||
if (sample_nibble & 2) delta += step >> 1;
|
||||
if (sample_nibble & 4) delta += step;
|
||||
if (sample_nibble & 8) delta = -delta;
|
||||
sample_decoded += delta;
|
||||
if (code & 1) delta += step >> 2;
|
||||
if (code & 2) delta += step >> 1;
|
||||
if (code & 4) delta += step;
|
||||
if (code & 8) delta = -delta;
|
||||
sample += delta;
|
||||
|
||||
*hist1 = clamp16(sample_decoded);
|
||||
*step_index += IMA_IndexTable[sample_nibble];
|
||||
if (*step_index < 0) *step_index=0;
|
||||
if (*step_index > 88) *step_index=88;
|
||||
*hist1 = clamp16(sample);
|
||||
*index += IMA_IndexTable[code];
|
||||
if (*index < 0) *index = 0;
|
||||
if (*index > 88) *index = 88;
|
||||
}
|
||||
|
||||
static void std_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t * hist1, int32_t * step_index) {
|
||||
uint8_t byte = read_u8(byte_offset,stream->streamfile);
|
||||
std_ima_expand_nibble_data(byte, nibble_shift, hist1, step_index);
|
||||
}
|
||||
|
||||
/* Apple's IMA variation. Exactly the same except it uses 16b history (probably more sensitive to overflow/sign extend?) */
|
||||
|
@ -1287,6 +1292,43 @@ void decode_cd_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacin
|
|||
stream->adpcm_step_index = step_index;
|
||||
}
|
||||
|
||||
/* Crankcase Audio IMA, from libs (internally CrankcaseAudio::ADPCM and revadpcm) */
|
||||
void decode_crankcase_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
|
||||
uint8_t frame[0x23] = {0};
|
||||
int i, frames_in, sample_pos = 0, block_samples, frame_size;
|
||||
int32_t hist1 = stream->adpcm_history1_32;
|
||||
int step_index = stream->adpcm_step_index;
|
||||
uint32_t frame_offset;
|
||||
|
||||
/* external interleave (fixed size), mono */
|
||||
frame_size = 0x23;
|
||||
block_samples = (frame_size - 0x3) * 2;
|
||||
frames_in = first_sample / block_samples;
|
||||
first_sample = first_sample % block_samples;
|
||||
|
||||
frame_offset = stream->offset + frame_size * frames_in;
|
||||
read_streamfile(frame, frame_offset, frame_size, stream->streamfile); /* ignore EOF errors */
|
||||
|
||||
/* normal header (hist+step), mono */
|
||||
if (first_sample == 0) {
|
||||
hist1 = get_s16be(frame + 0x00);
|
||||
step_index = get_u8(frame + 0x02); /* no reserved value at 0x03 unlike other IMAs (misaligned reads?) */
|
||||
step_index = _clamp_s32(step_index, 0, 88);
|
||||
}
|
||||
|
||||
/* decode nibbles (layout: straight in mono) */
|
||||
for (i = first_sample; i < first_sample + samples_to_do; i++) {
|
||||
int pos = 0x03 + (i/2);
|
||||
int shift = (i & 1 ? 4:0); /* low first */
|
||||
|
||||
std_ima_expand_nibble_data(frame[pos], shift, &hist1, &step_index);
|
||||
outbuf[sample_pos] = (short)(hist1); /* internally output to float using "sample / 32767.0" */
|
||||
sample_pos += channelspacing;
|
||||
}
|
||||
|
||||
stream->adpcm_history1_32 = hist1;
|
||||
stream->adpcm_step_index = step_index;
|
||||
}
|
||||
|
||||
/* ************************************************************* */
|
||||
|
||||
|
|
|
@ -188,28 +188,33 @@ fail:
|
|||
}
|
||||
|
||||
|
||||
#define AHX_KEY_BUFFER 0x2000
|
||||
#define AHX_KEY_TEST_FRAMES 20 /* wrong keys may work ok in some frames */
|
||||
#define AHX_KEY_BUFFER 0x1000 /* not too big since it's read per new key */
|
||||
#define AHX_KEY_TEST_FRAMES 25 /* wrong keys may work ok in some frames (specially blank) */
|
||||
|
||||
/* check if current key ends properly in frame syncs */
|
||||
int test_ahx_key(STREAMFILE* sf, off_t offset, crikey_t* crikey) {
|
||||
int bytes;
|
||||
int bytes = 0;
|
||||
uint8_t buf[AHX_KEY_BUFFER];
|
||||
const int buf_size = sizeof(buf);
|
||||
int pos = 0;
|
||||
uint32_t base_sync, curr_sync;
|
||||
|
||||
|
||||
for (int i = 0; i < AHX_KEY_TEST_FRAMES; i++) {
|
||||
if (bytes < MPEG_AHX_EXPECTED_FRAME_SIZE) {
|
||||
offset += pos;
|
||||
pos = 0;
|
||||
bytes = read_streamfile(buf, offset, buf_size, sf);
|
||||
//if (bytes != buf_size) goto fail; /* possible in small AHX */
|
||||
|
||||
base_sync = get_u32be(buf + 0x00);
|
||||
for (int i = 0; i < AHX_KEY_TEST_FRAMES; i++) {
|
||||
}
|
||||
|
||||
int size = ahx_decrypt(buf + pos, bytes, crikey);
|
||||
if (size <= 0 || size >= bytes - 0x04) goto fail;
|
||||
int frame_size = ahx_decrypt(buf + pos, bytes, crikey);
|
||||
if (frame_size <= 0 || frame_size >= bytes - 0x04)
|
||||
goto fail;
|
||||
|
||||
bytes -= size;
|
||||
pos += size;
|
||||
bytes -= frame_size;
|
||||
pos += frame_size;
|
||||
|
||||
curr_sync = get_u32be(buf + pos);
|
||||
if (curr_sync == 0x00800100) /* EOF tag */
|
||||
|
|
|
@ -392,6 +392,7 @@ static const char* extension_list[] = {
|
|||
"nwa",
|
||||
"nwav",
|
||||
"nxa",
|
||||
"nxopus",
|
||||
|
||||
//"ogg", //common
|
||||
"ogg_",
|
||||
|
@ -474,6 +475,7 @@ static const char* extension_list[] = {
|
|||
"sbr",
|
||||
"sbv",
|
||||
"sig",
|
||||
"slb", //txth/reserved [Vingt-et-un Systems PS2 games (Last Escort, etc]
|
||||
"sm0",
|
||||
"sm1",
|
||||
"sm2",
|
||||
|
@ -522,6 +524,7 @@ static const char* extension_list[] = {
|
|||
"sps",
|
||||
"spsd",
|
||||
"spw",
|
||||
"srsa",
|
||||
"ss2",
|
||||
"ssd", //txth/reserved [Zack & Wiki (Wii)]
|
||||
"ssm",
|
||||
|
@ -535,6 +538,7 @@ static const char* extension_list[] = {
|
|||
"stream",
|
||||
"strm",
|
||||
"sts",
|
||||
"stv", //txth/reserved [Socio Art Logic PS2 games (Zero no Tsukaima games, Cambrian QTS, Shirogane no Soleil, etc)]
|
||||
"sts_cp3",
|
||||
"stx",
|
||||
"svag",
|
||||
|
@ -628,6 +632,7 @@ static const char* extension_list[] = {
|
|||
"wd",
|
||||
"wem",
|
||||
"wii",
|
||||
"wiive", //txth/semi [Rubik World (Wii)]
|
||||
"wic", //txth/reserved [Road Rash (SAT)-videos]
|
||||
"wip", //txth/reserved [Colin McRae DiRT (PC)]
|
||||
"wlv", //txth/reserved [ToeJam & Earl III: Mission to Earth (DC)]
|
||||
|
@ -831,6 +836,7 @@ static const coding_info coding_info_list[] = {
|
|||
{coding_UBI_SCE_IMA, "Ubisoft 4-bit SCE IMA ADPCM"},
|
||||
{coding_H4M_IMA, "Hudson HVQM4 4-bit IMA ADPCM"},
|
||||
{coding_CD_IMA, "Crystal Dynamics 4-bit IMA ADPCM"},
|
||||
{coding_CRANKCASE_IMA, "CrankcaseAudio REV 4-bit IMA ADPCM"},
|
||||
|
||||
{coding_MSADPCM, "Microsoft 4-bit ADPCM"},
|
||||
{coding_MSADPCM_int, "Microsoft 4-bit ADPCM (mono/interleave)"},
|
||||
|
@ -1325,7 +1331,7 @@ static const meta_info meta_info_list[] = {
|
|||
{meta_XAU_KONAMI, "Konami XAU header"},
|
||||
{meta_DERF, "Xilam DERF header"},
|
||||
{meta_UTK, "Maxis UTK header"},
|
||||
{meta_NXA, "Entergram NXA header"},
|
||||
{meta_NXA1, "Entergram NXA1 header"},
|
||||
{meta_ADPCM_CAPCOM, "Capcom .ADPCM header"},
|
||||
{meta_UE4OPUS, "Epic Games UE4OPUS header"},
|
||||
{meta_XWMA, "Microsoft XWMA RIFF header"},
|
||||
|
@ -1415,6 +1421,7 @@ static const meta_info meta_info_list[] = {
|
|||
{meta_SQUEAKSTREAM, "Torus SqueakStream header"},
|
||||
{meta_SQUEAKSAMPLE, "Torus SqueakSample header"},
|
||||
{meta_SNDS, "Sony SNDS header"},
|
||||
{meta_NXOF, "Nihon Falcom FDK Opus Header"},
|
||||
};
|
||||
|
||||
void get_vgmstream_coding_description(VGMSTREAM* vgmstream, char* out, size_t out_size) {
|
||||
|
|
|
@ -11,13 +11,19 @@ static size_t get_block_header_size(STREAMFILE* sf, off_t offset, size_t channel
|
|||
void block_update_awc(off_t block_offset, VGMSTREAM * vgmstream) {
|
||||
STREAMFILE* sf = vgmstream->ch[0].streamfile;
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_32bitBE : read_32bitLE;
|
||||
size_t header_size, entries, block_size, block_samples;
|
||||
size_t header_size, entries, block_size, block_samples, frame_size;
|
||||
size_t channel_header_size;
|
||||
int i;
|
||||
|
||||
/* assumed only AWC_IMA enters here, MPEG/XMA2 need special parsing as blocked layout is too limited */
|
||||
entries = read_32bit(block_offset + 0x04, sf); /* se first channel, assume all are the same */
|
||||
//block_samples = entries * (0x800-4)*2; //todo use
|
||||
/* assumes only AWC_IMA/DSP enters here, MPEG/XMA2 need special parsing as blocked layout is too limited.
|
||||
* Block header (see awc.c for a complete description):
|
||||
* - per channel: header table (size 0x18 or 0x10)
|
||||
* - per channel: seek table (32b * entries = global samples per frame in each block) (not in DSP/Vorbis)
|
||||
* - per channel: extra table (DSP only)
|
||||
* - padding (not in ATRAC9/DSP)
|
||||
*/
|
||||
|
||||
entries = read_32bit(block_offset + 0x04, sf); /* se first channel, assume all are the same (not true in MPEG/XMA) */
|
||||
block_samples = read_32bit(block_offset + 0x0c, sf);
|
||||
block_size = vgmstream->full_block_size;
|
||||
|
||||
|
@ -25,24 +31,32 @@ void block_update_awc(off_t block_offset, VGMSTREAM * vgmstream) {
|
|||
vgmstream->next_block_offset = block_offset + block_size;
|
||||
vgmstream->current_block_samples = block_samples;
|
||||
|
||||
/* starts with a header block */
|
||||
/* for each channel
|
||||
* 0x00: start entry within channel (ie. entries * ch) but may be off by +1/+2
|
||||
* 0x04: entries
|
||||
* 0x08: samples to discard in the beginning of this block (MPEG only?)
|
||||
* 0x0c: samples in channel (for MPEG/XMA2 can vary between channels)
|
||||
* (next fields don't exist in later versions for IMA)
|
||||
* 0x10: (MPEG only, empty otherwise) close to number of frames but varies a bit?
|
||||
* 0x14: (MPEG only, empty otherwise) channel usable data size (not counting padding)
|
||||
* for each channel
|
||||
* 32b * entries = global samples per frame in each block (for MPEG probably per full frame)
|
||||
*/
|
||||
switch(vgmstream->coding_type) {
|
||||
case coding_NGC_DSP:
|
||||
channel_header_size = 0x10;
|
||||
frame_size = 0x08;
|
||||
|
||||
/* coefs on every block but it's always the same */
|
||||
dsp_read_coefs_le(vgmstream, sf, block_offset + channel_header_size * vgmstream->channels + 0x10 + 0x1c + 0x00, 0x10 + 0x60);
|
||||
dsp_read_hist_le (vgmstream, sf, block_offset + channel_header_size * vgmstream->channels + 0x10 + 0x1c + 0x20, 0x10 + 0x60);
|
||||
|
||||
header_size = 0;
|
||||
header_size += channel_header_size * vgmstream->channels; /* header table */
|
||||
/* no seek table */
|
||||
header_size += 0x70 * vgmstream->channels; /* extra table */
|
||||
/* no padding */
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
channel_header_size = get_channel_header_size(sf, block_offset, vgmstream->codec_endian);
|
||||
header_size = get_block_header_size(sf, block_offset, channel_header_size, vgmstream->channels, vgmstream->codec_endian);
|
||||
frame_size = 0x800;
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < vgmstream->channels; i++) {
|
||||
vgmstream->ch[i].offset = block_offset + header_size + 0x800*entries*i;
|
||||
VGM_ASSERT(entries != read_32bit(block_offset + channel_header_size*i + 0x04, sf), "AWC: variable number of entries found at %lx\n", block_offset);
|
||||
vgmstream->ch[i].offset = block_offset + header_size + frame_size * entries * i;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -92,6 +92,7 @@ fail:
|
|||
#define ACB_TABLE_BUFFER_TRACKCOMMAND 0x2000
|
||||
#define ACB_TABLE_BUFFER_SYNTH 0x4000
|
||||
#define ACB_TABLE_BUFFER_WAVEFORM 0x4000
|
||||
#define ACB_TABLE_BUFFER_WAVEFORMEXTENSIONDATA 0x1000
|
||||
|
||||
#define ACB_MAX_NAMELIST 255
|
||||
#define ACB_MAX_NAME 1024 /* even more is possible in rare cases [Senran Kagura Burst Re:Newal (PC)] */
|
||||
|
@ -159,8 +160,15 @@ typedef struct {
|
|||
uint16_t Id;
|
||||
uint16_t PortNo;
|
||||
uint8_t Streaming;
|
||||
uint8_t LoopFlag;
|
||||
uint16_t ExtensionData;
|
||||
} Waveform_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t LoopStart;
|
||||
uint32_t LoopEnd;
|
||||
} WaveformExtensionData_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
STREAMFILE* acbFile; /* original reference, don't close */
|
||||
|
@ -178,6 +186,7 @@ typedef struct {
|
|||
STREAMFILE* TrackCommandSf;
|
||||
STREAMFILE* SynthSf;
|
||||
STREAMFILE* WaveformSf;
|
||||
STREAMFILE* WaveformExtensionDataSf;
|
||||
|
||||
Cue_t* Cue;
|
||||
CueName_t* CueName;
|
||||
|
@ -188,6 +197,7 @@ typedef struct {
|
|||
TrackCommand_t* TrackCommand;
|
||||
Synth_t* Synth;
|
||||
Waveform_t* Waveform;
|
||||
WaveformExtensionData_t* WaveformExtensionData;
|
||||
|
||||
int Cue_rows;
|
||||
int CueName_rows;
|
||||
|
@ -198,6 +208,7 @@ typedef struct {
|
|||
int TrackCommand_rows;
|
||||
int Synth_rows;
|
||||
int Waveform_rows;
|
||||
int WaveformExtensionData_rows;
|
||||
|
||||
/* config */
|
||||
int is_memory;
|
||||
|
@ -208,7 +219,8 @@ typedef struct {
|
|||
int synth_depth;
|
||||
int sequence_depth;
|
||||
|
||||
/* name stuff */
|
||||
/* name/config stuff */
|
||||
int16_t waveform_index;
|
||||
int16_t cuename_index;
|
||||
const char* cuename_name;
|
||||
int awbname_count;
|
||||
|
@ -297,7 +309,7 @@ static void add_acb_name(acb_header* acb, int8_t Streaming) {
|
|||
static int preload_acb_waveform(acb_header* acb) {
|
||||
utf_context* Table = NULL;
|
||||
int* p_rows = &acb->Waveform_rows;
|
||||
int i, c_Id, c_MemoryAwbId, c_StreamAwbId, c_StreamAwbPortNo, c_Streaming;
|
||||
int i, c_Id, c_MemoryAwbId, c_StreamAwbId, c_StreamAwbPortNo, c_Streaming, c_LoopFlag, c_ExtensionData;
|
||||
|
||||
if (*p_rows)
|
||||
return 1;
|
||||
|
@ -315,6 +327,8 @@ static int preload_acb_waveform(acb_header* acb) {
|
|||
c_StreamAwbId = utf_get_column(Table, "StreamAwbId");
|
||||
c_StreamAwbPortNo = utf_get_column(Table, "StreamAwbPortNo");
|
||||
c_Streaming = utf_get_column(Table, "Streaming");
|
||||
c_LoopFlag = utf_get_column(Table, "LoopFlag");
|
||||
c_ExtensionData = utf_get_column(Table, "ExtensionData");
|
||||
|
||||
for (i = 0; i < *p_rows; i++) {
|
||||
Waveform_t* r = &acb->Waveform[i];
|
||||
|
@ -332,6 +346,10 @@ static int preload_acb_waveform(acb_header* acb) {
|
|||
r->PortNo = 0xFFFF;
|
||||
}
|
||||
utf_query_col_u8(Table, i, c_Streaming, &r->Streaming);
|
||||
utf_query_col_u8(Table, i, c_LoopFlag, &r->LoopFlag);
|
||||
|
||||
r->ExtensionData = -1;
|
||||
utf_query_col_u16(Table, i, c_ExtensionData, &r->ExtensionData); /* optional for newer/Switch acb */
|
||||
}
|
||||
|
||||
utf_close(Table);
|
||||
|
@ -346,7 +364,7 @@ static int load_acb_waveform(acb_header* acb, uint16_t Index) {
|
|||
Waveform_t* r;
|
||||
|
||||
if (!preload_acb_waveform(acb)) goto fail;
|
||||
if (Index > acb->Waveform_rows) goto fail;
|
||||
if (Index >= acb->Waveform_rows) goto fail;
|
||||
r = &acb->Waveform[Index];
|
||||
//;VGM_LOG("acb: Waveform[%i]: Id=%i, PortNo=%i, Streaming=%i\n", Index, r->Id, r->PortNo, r->Streaming);
|
||||
|
||||
|
@ -362,6 +380,9 @@ static int load_acb_waveform(acb_header* acb, uint16_t Index) {
|
|||
if ((acb->is_memory && r->Streaming == 1) || (!acb->is_memory && r->Streaming == 0))
|
||||
return 1;
|
||||
|
||||
/* save waveid <> Index translation */
|
||||
acb->waveform_index = Index;
|
||||
|
||||
/* aaand finally get name (phew) */
|
||||
add_acb_name(acb, r->Streaming);
|
||||
|
||||
|
@ -417,7 +438,7 @@ static int load_acb_synth(acb_header* acb, uint16_t Index) {
|
|||
int i, count;
|
||||
|
||||
if (!preload_acb_synth(acb)) goto fail;
|
||||
if (Index > acb->Synth_rows) goto fail;
|
||||
if (Index >= acb->Synth_rows) goto fail;
|
||||
r = &acb->Synth[Index];
|
||||
//;VGM_LOG("acb: Synth[%i]: Type=%x, ReferenceItems={%x,%x}\n", Index, r->Type, r->ReferenceItems_offset, r->ReferenceItems_size);
|
||||
|
||||
|
@ -615,7 +636,7 @@ static int load_acb_trackcommand(acb_header* acb, uint16_t Index) {
|
|||
TrackCommand_t* r;
|
||||
|
||||
if (!preload_acb_trackcommand(acb)) goto fail;
|
||||
if (Index > acb->TrackCommand_rows) goto fail;
|
||||
if (Index >= acb->TrackCommand_rows) goto fail;
|
||||
r = &acb->TrackCommand[Index];
|
||||
//;VGM_LOG("acb: TrackEvent/Command[%i]: Command={%x,%x}\n", Index, r->Command_offset, r->Command_size);
|
||||
|
||||
|
@ -669,7 +690,7 @@ static int load_acb_track(acb_header* acb, uint16_t Index) {
|
|||
Track_t* r;
|
||||
|
||||
if (!preload_acb_track(acb)) goto fail;
|
||||
if (Index > acb->Track_rows) goto fail;
|
||||
if (Index >= acb->Track_rows) goto fail;
|
||||
r = &acb->Track[Index];
|
||||
//;VGM_LOG("acb: Track[%i]: EventIndex=%i\n", Index, r->EventIndex);
|
||||
|
||||
|
@ -736,7 +757,7 @@ static int load_acb_sequence(acb_header* acb, uint16_t Index) {
|
|||
int i;
|
||||
|
||||
if (!preload_acb_sequence(acb)) goto fail;
|
||||
if (Index > acb->Sequence_rows) goto fail;
|
||||
if (Index >= acb->Sequence_rows) goto fail;
|
||||
r = &acb->Sequence[Index];
|
||||
//;VGM_LOG("acb: Sequence[%i]: NumTracks=%i, TrackIndex={%x, %x}, Type=%x\n", Index, r->NumTracks, r->TrackIndex_offset, r->TrackIndex_size, r->Type);
|
||||
|
||||
|
@ -832,7 +853,7 @@ static int load_acb_block(acb_header* acb, uint16_t Index) {
|
|||
int i;
|
||||
|
||||
if (!preload_acb_block(acb)) goto fail;
|
||||
if (Index > acb->Block_rows) goto fail;
|
||||
if (Index >= acb->Block_rows) goto fail;
|
||||
r = &acb->Block[Index];
|
||||
//;VGM_LOG("acb: Block[%i]: NumTracks=%i, TrackIndex={%x, %x}\n", Index, r->NumTracks, r->TrackIndex_offset, r->TrackIndex_size);
|
||||
|
||||
|
@ -900,7 +921,7 @@ static int load_acb_blocksequence(acb_header* acb, uint16_t Index) {
|
|||
int i;
|
||||
|
||||
if (!preload_acb_blocksequence(acb)) goto fail;
|
||||
if (Index > acb->BlockSequence_rows) goto fail;
|
||||
if (Index >= acb->BlockSequence_rows) goto fail;
|
||||
r = &acb->BlockSequence[Index];
|
||||
//;VGM_LOG("acb: BlockSequence[%i]: NumTracks=%i, TrackIndex={%x, %x}, NumBlocks=%i, BlockIndex={%x, %x}\n", Index, r->NumTracks, r->TrackIndex_offset,r->TrackIndex_size, r->NumBlocks, r->BlockIndex_offset, r->BlockIndex_size);
|
||||
|
||||
|
@ -977,7 +998,7 @@ static int load_acb_cue(acb_header* acb, uint16_t Index) {
|
|||
|
||||
/* read Cue[Index] */
|
||||
if (!preload_acb_cue(acb)) goto fail;
|
||||
if (Index > acb->Cue_rows) goto fail;
|
||||
if (Index >= acb->Cue_rows) goto fail;
|
||||
r = &acb->Cue[Index];
|
||||
//;VGM_LOG("acb: Cue[%i]: ReferenceType=%i, ReferenceIndex=%i\n", Index, r->ReferenceType, r->ReferenceIndex);
|
||||
|
||||
|
@ -1064,7 +1085,7 @@ static int load_acb_cuename(acb_header* acb, uint16_t Index) {
|
|||
CueName_t* r;
|
||||
|
||||
if (!preload_acb_cuename(acb)) goto fail;
|
||||
if (Index > acb->CueName_rows) goto fail;
|
||||
if (Index >= acb->CueName_rows) goto fail;
|
||||
r = &acb->CueName[Index];
|
||||
//;VGM_LOG("acb: CueName[%i]: CueIndex=%i, CueName=%s\n", Index, r->CueIndex, r->CueName);
|
||||
|
||||
|
@ -1081,6 +1102,79 @@ fail:
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int preload_acb_waveformextensiondata(acb_header* acb) {
|
||||
utf_context* Table = NULL;
|
||||
int* p_rows = &acb->WaveformExtensionData_rows;
|
||||
int i, c_LoopStart, c_LoopEnd;
|
||||
|
||||
if (*p_rows)
|
||||
return 1;
|
||||
if (!open_utf_subtable(acb, &acb->WaveformExtensionDataSf, &Table, "WaveformExtensionDataTable", p_rows, ACB_TABLE_BUFFER_WAVEFORMEXTENSIONDATA))
|
||||
goto fail;
|
||||
if (!*p_rows)
|
||||
return 1;
|
||||
//;VGM_LOG("acb: preload WaveformExtensionData=%i\n", *p_rows);
|
||||
|
||||
acb->WaveformExtensionData = malloc(*p_rows * sizeof(WaveformExtensionData_t));
|
||||
if (!acb->WaveformExtensionData) goto fail;
|
||||
|
||||
c_LoopStart = utf_get_column(Table, "LoopStart");
|
||||
c_LoopEnd = utf_get_column(Table, "LoopEnd");
|
||||
|
||||
for (i = 0; i < *p_rows; i++) {
|
||||
WaveformExtensionData_t* r = &acb->WaveformExtensionData[i];
|
||||
|
||||
utf_query_col_u32(Table, i, c_LoopStart, &r->LoopStart);
|
||||
utf_query_col_u32(Table, i, c_LoopEnd, &r->LoopEnd);
|
||||
}
|
||||
|
||||
utf_close(Table);
|
||||
return 1;
|
||||
fail:
|
||||
VGM_LOG("acb: failed WaveformExtensionData preload\n");
|
||||
utf_close(Table);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* for Switch Opus that has loop info in a separate "WaveformExtensionData" table (pointed by a field in Waveform) */
|
||||
static int load_acb_loops(acb_header* acb, VGMSTREAM* vgmstream) {
|
||||
Waveform_t* rw;
|
||||
WaveformExtensionData_t* r;
|
||||
uint16_t WaveIndex = acb->waveform_index;
|
||||
uint16_t ExtensionIndex = -1;
|
||||
|
||||
if (vgmstream->loop_flag)
|
||||
return 0;
|
||||
|
||||
/* assumes that will be init'd before while searching for names */
|
||||
if (WaveIndex < 0) goto fail;
|
||||
//if (!preload_acb_waveform(acb)) goto fail;
|
||||
if (WaveIndex >= acb->Waveform_rows) goto fail;
|
||||
rw = &acb->Waveform[WaveIndex];
|
||||
|
||||
/* 1=no loop, 2=loop, ignore others/0(default)/255 just in case */
|
||||
if (rw->LoopFlag != 2)
|
||||
return 0;
|
||||
|
||||
ExtensionIndex = rw->ExtensionData;
|
||||
if (ExtensionIndex < 0) goto fail; /* not init'd? */
|
||||
|
||||
if (!preload_acb_waveformextensiondata(acb)) goto fail;
|
||||
if (ExtensionIndex >= acb->WaveformExtensionData_rows) goto fail;
|
||||
|
||||
r = &acb->WaveformExtensionData[ExtensionIndex];
|
||||
|
||||
//;VGM_LOG("acb: WaveformExtensionData[%i]: LoopStart=%i, LoopEnd=%i\n", Index, r->LoopStart, r->LoopEnd);
|
||||
|
||||
vgmstream_force_loop(vgmstream, 1, r->LoopStart, r->LoopEnd);
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
VGM_LOG("acb: failed WaveformExtensionData %i\n", ExtensionIndex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/* Normally games load a .acb + .awb, and asks the .acb to play a cue by name or index.
|
||||
|
@ -1111,7 +1205,7 @@ fail:
|
|||
* per table, meaning it uses a decent chunk of memory, but having to re-read with streamfiles is much slower.
|
||||
*/
|
||||
|
||||
void load_acb_wave_name(STREAMFILE* sf, VGMSTREAM* vgmstream, int waveid, int port, int is_memory) {
|
||||
void load_acb_wave_info(STREAMFILE* sf, VGMSTREAM* vgmstream, int waveid, int port, int is_memory, int load_loops) {
|
||||
acb_header acb = {0};
|
||||
int i;
|
||||
|
||||
|
@ -1129,6 +1223,7 @@ void load_acb_wave_name(STREAMFILE* sf, VGMSTREAM* vgmstream, int waveid, int po
|
|||
acb.target_waveid = waveid;
|
||||
acb.target_port = port;
|
||||
acb.is_memory = is_memory;
|
||||
acb.waveform_index = -1;
|
||||
|
||||
/* read all possible cue names and find which waveids are referenced by it */
|
||||
preload_acb_cuename(&acb);
|
||||
|
@ -1143,6 +1238,11 @@ void load_acb_wave_name(STREAMFILE* sf, VGMSTREAM* vgmstream, int waveid, int po
|
|||
vgmstream->stream_name[STREAM_NAME_SIZE - 1] = '\0';
|
||||
}
|
||||
|
||||
/* uncommon */
|
||||
if (load_loops) {
|
||||
load_acb_loops(&acb, vgmstream);
|
||||
}
|
||||
|
||||
/* done */
|
||||
fail:
|
||||
utf_close(acb.Header);
|
||||
|
@ -1157,6 +1257,7 @@ fail:
|
|||
close_streamfile(acb.TrackCommandSf);
|
||||
close_streamfile(acb.SynthSf);
|
||||
close_streamfile(acb.WaveformSf);
|
||||
close_streamfile(acb.WaveformExtensionDataSf);
|
||||
|
||||
free(acb.CueName);
|
||||
free(acb.Cue);
|
||||
|
@ -1167,4 +1268,5 @@ fail:
|
|||
free(acb.TrackCommand);
|
||||
free(acb.Synth);
|
||||
free(acb.Waveform);
|
||||
free(acb.WaveformExtensionData);
|
||||
}
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
typedef struct {
|
||||
int total_subsongs;
|
||||
int target_subsong;
|
||||
int version;
|
||||
int file_version;
|
||||
int header_version; /* major.minor in hex */
|
||||
|
||||
uint32_t stream_offset;
|
||||
uint32_t stream_size;
|
||||
|
@ -18,7 +19,7 @@ typedef struct {
|
|||
|
||||
static int parse_adm(adm_header_t* adm, STREAMFILE* sf);
|
||||
|
||||
static VGMSTREAM* init_vgmstream_adm(STREAMFILE* sf, int version);
|
||||
static VGMSTREAM* init_vgmstream_adm(STREAMFILE* sf, int file_version);
|
||||
|
||||
/* ADM2 - Crankcase Audio REV plugin file [The Grand Tour Game (PC)] */
|
||||
VGMSTREAM* init_vgmstream_adm2(STREAMFILE* sf) {
|
||||
|
@ -38,26 +39,24 @@ VGMSTREAM* init_vgmstream_adm3(STREAMFILE* sf) {
|
|||
/* checks */
|
||||
if (!is_id32be(0x00,sf, "ADM3"))
|
||||
return NULL;
|
||||
if (!check_extensions(sf, "wem"))
|
||||
if (!check_extensions(sf, "wem,bnk"))
|
||||
return NULL;
|
||||
|
||||
return init_vgmstream_adm(sf, 3);
|
||||
}
|
||||
|
||||
static VGMSTREAM* init_vgmstream_adm(STREAMFILE* sf, int version) {
|
||||
static VGMSTREAM* init_vgmstream_adm(STREAMFILE* sf, int file_version) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
adm_header_t adm = {0};
|
||||
|
||||
/* ADMx are files used with the Wwise Crankaudio plugin, that simulate engine noises with
|
||||
* base internal samples and some internal RPM config (probably). Actual file seems to
|
||||
* define some combo of samples, this only plays those separate samples.
|
||||
* Decoder is basically Apple's IMA (internally just "ADPCMDecoder") but transforms to float
|
||||
* each sample during decode by multiplying by 0.000030518509 */
|
||||
* define some combo of samples, this only plays those separate samples. */
|
||||
|
||||
adm.target_subsong = sf->stream_index;
|
||||
if (adm.target_subsong == 0) adm.target_subsong = 1;
|
||||
|
||||
adm.version = version;
|
||||
adm.file_version = file_version;
|
||||
|
||||
if (!parse_adm(&adm, sf))
|
||||
goto fail;
|
||||
|
@ -73,9 +72,21 @@ static VGMSTREAM* init_vgmstream_adm(STREAMFILE* sf, int version) {
|
|||
vgmstream->num_streams = adm.total_subsongs;
|
||||
vgmstream->stream_size = adm.stream_size;
|
||||
|
||||
switch(adm.header_version) {
|
||||
case 0x00070000: /* The Crew Motorfest (PC) */
|
||||
vgmstream->coding_type = coding_CRANKCASE_IMA;
|
||||
//vgmstream->layout_type = layout_interleave;
|
||||
//vgmstream->interleave_block_size = 0x23;
|
||||
break;
|
||||
|
||||
default: /* The Grand Tour Game (PC) [0x00050000], MotoGP 21 (PC) [0x00060000] */
|
||||
/* Basically Apple's IMA (internally just "ADPCMDecoder") but transforms to float
|
||||
* each sample during decode by multiplying by 0.000030518509 */
|
||||
vgmstream->coding_type = coding_APPLE_IMA4;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x22;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, adm.stream_offset))
|
||||
goto fail;
|
||||
|
@ -169,12 +180,12 @@ static int parse_adm(adm_header_t* adm, STREAMFILE* sf) {
|
|||
uint32_t offset;
|
||||
|
||||
/* 0x04: null */
|
||||
/* 0x08: version? (ADM2: 0x00050000, ADM3: 0x00060000) */
|
||||
adm->header_version = read_u32le(0x08, sf); /* ADM2: 0x00050000, ADM3: 0x00060000 (older) / 0x00070000 (2023) */
|
||||
/* 0x0c: header size */
|
||||
/* 0x10: data start */
|
||||
/* rest unknown, looks mostly the same between files (some floats and stuff) */
|
||||
|
||||
switch(adm->version) {
|
||||
switch(adm->file_version) {
|
||||
case 2:
|
||||
/* low to high */
|
||||
offset = read_u32le(0x104, sf);
|
||||
|
|
|
@ -202,12 +202,15 @@ static const adxkey_info adxkey8_list[] = {
|
|||
/* Mirai Nikki: 13-ninme no Nikki Shoyuusha Re-Write (PSP) */
|
||||
{0x58a3,0x66f5,0x599f, "FDRW17th",0},
|
||||
|
||||
/* Shoujo Yoshitsune-den Ni - Toki wo Koeru Chigiri (PS2) */
|
||||
/* Shoujo Yoshitsune-den Ni: Toki wo Koeru Chigiri (PS2) */
|
||||
{0x62d7,0x483d,0x4fb7, "YOSHI2",0},
|
||||
|
||||
/* Junjou Romantica - Koi no Doki Doki Daisakusen (PS2) (Marvelous) */
|
||||
/* Junjou Romantica: Koi no Doki Doki Daisakusen (PS2) */
|
||||
{0x5827,0x612d,0x5585, "Endress-SETSUNAI!",0},
|
||||
|
||||
/* Corpse Party: Book of Shadows (PSP) */
|
||||
{0x60ad,0x5689,0x5281, "\x83\x76\x83\x89\x83\x60\x83\x69Lovers_Day",0}, // "プラチナLovers_Day" in SHIFT-JIS
|
||||
|
||||
};
|
||||
|
||||
static const adxkey_info adxkey9_list[] = {
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
//typedef enum { ADX, HCA, VAG, RIFF, CWAV, DSP, CWAC, M4A } awb_type_t;
|
||||
|
||||
static void load_awb_name(STREAMFILE* sf, STREAMFILE* sf_acb, VGMSTREAM* vgmstream, int waveid);
|
||||
static void load_acb_info(STREAMFILE* sf, STREAMFILE* sf_acb, VGMSTREAM* vgmstream, int waveid, int load_loops);
|
||||
|
||||
/* AFS2/AWB (Atom Wave Bank) - CRI container of streaming audio, often together with a .acb cue sheet */
|
||||
VGMSTREAM* init_vgmstream_awb(STREAMFILE* sf) {
|
||||
|
@ -19,6 +19,7 @@ VGMSTREAM* init_vgmstream_awb_memory(STREAMFILE* sf, STREAMFILE* sf_acb) {
|
|||
uint8_t offset_size;
|
||||
uint16_t waveid_alignment, offset_alignment, subkey;
|
||||
int waveid;
|
||||
int load_loops = 0;
|
||||
|
||||
|
||||
/* checks */
|
||||
|
@ -126,6 +127,11 @@ VGMSTREAM* init_vgmstream_awb_memory(STREAMFILE* sf, STREAMFILE* sf_acb) {
|
|||
extension = "m4a";
|
||||
}
|
||||
#endif
|
||||
else if (read_u32be(subfile_offset + 0x00,sf) == 0x01000080) { /* (type 24=NXOpus) */
|
||||
init_vgmstream =init_vgmstream_opus_std; /* Super Mario RPG (Switch) */
|
||||
extension = "opus";
|
||||
load_loops = 1; /* loops not in Opus (rare) but in .acb */
|
||||
}
|
||||
else { /* 12=XMA? */
|
||||
vgm_logi("AWB: unknown codec (report)\n");
|
||||
goto fail;
|
||||
|
@ -144,8 +150,8 @@ VGMSTREAM* init_vgmstream_awb_memory(STREAMFILE* sf, STREAMFILE* sf_acb) {
|
|||
vgmstream->num_streams = total_subsongs;
|
||||
}
|
||||
|
||||
/* try to load cue names */
|
||||
load_awb_name(sf, sf_acb, vgmstream, waveid);
|
||||
/* try to load cue names+etc */
|
||||
load_acb_info(sf, sf_acb, vgmstream, waveid, load_loops);
|
||||
|
||||
close_streamfile(temp_sf);
|
||||
return vgmstream;
|
||||
|
@ -157,7 +163,7 @@ fail:
|
|||
}
|
||||
|
||||
|
||||
static void load_awb_name(STREAMFILE* sf, STREAMFILE* sf_acb, VGMSTREAM* vgmstream, int waveid) {
|
||||
static void load_acb_info(STREAMFILE* sf, STREAMFILE* sf_acb, VGMSTREAM* vgmstream, int waveid, int load_loops) {
|
||||
int is_memory = (sf_acb != NULL);
|
||||
int port = 0;
|
||||
|
||||
|
@ -170,7 +176,7 @@ static void load_awb_name(STREAMFILE* sf, STREAMFILE* sf_acb, VGMSTREAM* vgmstre
|
|||
/* try parsing TXTM if present */
|
||||
sf_acb = read_filemap_file_pos(sf, 0, &port);
|
||||
|
||||
/* try (name).awb + (name).awb */
|
||||
/* try (name).awb + (name).acb */
|
||||
if (!sf_acb) {
|
||||
sf_acb = open_streamfile_by_ext(sf, "acb");
|
||||
}
|
||||
|
@ -204,11 +210,11 @@ static void load_awb_name(STREAMFILE* sf, STREAMFILE* sf_acb, VGMSTREAM* vgmstre
|
|||
}
|
||||
|
||||
/* probably loaded */
|
||||
load_acb_wave_name(sf_acb, vgmstream, waveid, port, is_memory);
|
||||
load_acb_wave_info(sf_acb, vgmstream, waveid, port, is_memory, load_loops);
|
||||
|
||||
close_streamfile(sf_acb);
|
||||
}
|
||||
else {
|
||||
load_acb_wave_name(sf_acb, vgmstream, waveid, port, is_memory);
|
||||
load_acb_wave_info(sf_acb, vgmstream, waveid, port, is_memory, load_loops);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,26 +1,35 @@
|
|||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "../layout/layout.h"
|
||||
#include "awc_xma_streamfile.h"
|
||||
#include "../util/endianness.h"
|
||||
#include "awc_streamfile.h"
|
||||
|
||||
typedef struct {
|
||||
int big_endian;
|
||||
int is_encrypted;
|
||||
int is_music;
|
||||
int is_streamed; /* implicit: streams=music, sfx=memory */
|
||||
|
||||
int total_subsongs;
|
||||
|
||||
int channels;
|
||||
int sample_rate;
|
||||
int codec;
|
||||
int num_samples;
|
||||
uint8_t codec;
|
||||
|
||||
int block_count;
|
||||
int block_chunk;
|
||||
|
||||
off_t stream_offset;
|
||||
size_t stream_size;
|
||||
off_t vorbis_offset[VGMSTREAM_MAX_CHANNELS];
|
||||
uint32_t tags_offset;
|
||||
uint32_t stream_offset;
|
||||
uint32_t stream_size;
|
||||
uint32_t vorbis_offset[AWC_MAX_MUSIC_CHANNELS];
|
||||
|
||||
/* stream+music only */
|
||||
uint32_t channel_hash[AWC_MAX_MUSIC_CHANNELS];
|
||||
struct {
|
||||
uint32_t hash_id;
|
||||
int tag_count;
|
||||
} stream_info[AWC_MAX_MUSIC_CHANNELS];
|
||||
} awc_header;
|
||||
|
||||
static int parse_awc_header(STREAMFILE* sf, awc_header* awc);
|
||||
|
@ -28,17 +37,17 @@ static int parse_awc_header(STREAMFILE* sf, awc_header* awc);
|
|||
static layered_layout_data* build_layered_awc(STREAMFILE* sf, awc_header* awc);
|
||||
|
||||
|
||||
/* AWC - from RAGE (Rockstar Advanced Game Engine) audio [Red Dead Redemption, Max Payne 3, GTA5 (multi)] */
|
||||
/* AWC - Audio Wave Container from RAGE (Rockstar Advanced Game Engine) [Red Dead Redemption, Max Payne 3, GTA5 (multi)] */
|
||||
VGMSTREAM* init_vgmstream_awc(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
awc_header awc = {0};
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf,"awc"))
|
||||
goto fail;
|
||||
if (!parse_awc_header(sf, &awc))
|
||||
goto fail;
|
||||
return NULL;
|
||||
if (!check_extensions(sf,"awc"))
|
||||
return NULL;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
|
@ -55,7 +64,7 @@ VGMSTREAM* init_vgmstream_awc(STREAMFILE* sf) {
|
|||
switch(awc.codec) {
|
||||
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 */
|
||||
if (awc.is_streamed) goto fail; /* blocked_awc needs to be prepared */
|
||||
vgmstream->coding_type = awc.big_endian ? coding_PCM16BE : coding_PCM16LE;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x02;
|
||||
|
@ -63,59 +72,18 @@ VGMSTREAM* init_vgmstream_awc(STREAMFILE* sf) {
|
|||
|
||||
case 0x04: /* IMA (PC) */
|
||||
vgmstream->coding_type = coding_AWC_IMA;
|
||||
vgmstream->layout_type = awc.is_music ? layout_blocked_awc : layout_none;
|
||||
vgmstream->layout_type = awc.is_streamed ? layout_blocked_awc : layout_none;
|
||||
vgmstream->full_block_size = awc.block_chunk;
|
||||
vgmstream->codec_endian = awc.big_endian;
|
||||
break;
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case 0x05: { /* XMA2 (X360) */
|
||||
uint32_t substream_size, substream_offset;
|
||||
|
||||
if (awc.is_music) {
|
||||
/* 1ch XMAs in blocks, we'll use layered layout + custom IO to get multi-FFmpegs working */
|
||||
int i;
|
||||
layered_layout_data * data = NULL;
|
||||
|
||||
/* init layout */
|
||||
data = init_layout_layered(awc.channels);
|
||||
if (!data) goto fail;
|
||||
vgmstream->layout_data = data;
|
||||
if (awc.is_streamed) {
|
||||
vgmstream->layout_data = build_layered_awc(sf, &awc);
|
||||
if (!vgmstream->layout_data) goto fail;
|
||||
vgmstream->layout_type = layout_layered;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
|
||||
/* open each layer subfile */
|
||||
for (i = 0; i < awc.channels; i++) {
|
||||
STREAMFILE* temp_sf = NULL;
|
||||
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]->meta_type = meta_AWC;
|
||||
data->layers[i]->coding_type = coding_FFmpeg;
|
||||
data->layers[i]->layout_type = layout_none;
|
||||
data->layers[i]->sample_rate = awc.sample_rate;
|
||||
data->layers[i]->num_samples = awc.num_samples;
|
||||
|
||||
/* setup custom IO streamfile, pass to FFmpeg and hope it's fooled */
|
||||
temp_sf = setup_awc_xma_streamfile(sf, awc.stream_offset, awc.stream_size, awc.block_chunk, awc.channels, i);
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
substream_offset = 0x00; /* where FFmpeg thinks data starts, which our custom sf will clamp */
|
||||
substream_size = get_streamfile_size(temp_sf); /* data of one XMA substream without blocks */
|
||||
|
||||
data->layers[i]->codec_data = init_ffmpeg_xma2_raw(temp_sf, substream_offset, substream_size, awc.num_samples, layer_channels, awc.sample_rate, 0, 0);
|
||||
if (data->layers[i])
|
||||
xma_fix_raw_samples(data->layers[i], temp_sf, substream_offset, substream_size, 0, 0,0); /* samples are ok? */
|
||||
close_streamfile(temp_sf);
|
||||
if (!data->layers[i]->codec_data) goto fail;
|
||||
}
|
||||
|
||||
/* setup layered VGMSTREAMs */
|
||||
if (!setup_layout_layered(data))
|
||||
goto fail;
|
||||
}
|
||||
else {
|
||||
/* regular XMA for sfx */
|
||||
|
@ -129,9 +97,8 @@ VGMSTREAM* init_vgmstream_awc(STREAMFILE* sf) {
|
|||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
case 0x07: { /* MPEG (PS3) */
|
||||
mpeg_custom_config cfg = {0};
|
||||
|
@ -146,9 +113,10 @@ VGMSTREAM* init_vgmstream_awc(STREAMFILE* sf) {
|
|||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
case 0x08: { /* Vorbis (PC) [Red Dead Redemption 2 (PC)] */
|
||||
if (awc.is_music) {
|
||||
if (awc.is_streamed) {
|
||||
vgmstream->layout_data = build_layered_awc(sf, &awc);
|
||||
if (!vgmstream->layout_data) goto fail;
|
||||
vgmstream->layout_type = layout_layered;
|
||||
|
@ -169,6 +137,62 @@ VGMSTREAM* init_vgmstream_awc(STREAMFILE* sf) {
|
|||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_ATRAC9
|
||||
case 0x0F: { /* ATRAC9 (PC) [Red Dead Redemption (PS4)] */
|
||||
if (awc.is_streamed) {
|
||||
vgmstream->layout_data = build_layered_awc(sf, &awc);
|
||||
if (!vgmstream->layout_data) goto fail;
|
||||
vgmstream->layout_type = layout_layered;
|
||||
vgmstream->coding_type = coding_ATRAC9;
|
||||
}
|
||||
else {
|
||||
VGMSTREAM* temp_vs = NULL;
|
||||
STREAMFILE* temp_sf = NULL;
|
||||
|
||||
temp_sf = setup_subfile_streamfile(sf, awc.stream_offset, awc.stream_size, "at9");
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
temp_vs = init_vgmstream_riff(temp_sf);
|
||||
close_streamfile(temp_sf);
|
||||
if (!temp_vs) goto fail;
|
||||
|
||||
temp_vs->num_streams = vgmstream->num_streams;
|
||||
temp_vs->stream_size = vgmstream->stream_size;
|
||||
temp_vs->meta_type = vgmstream->meta_type;
|
||||
strcpy(temp_vs->stream_name, vgmstream->stream_name);
|
||||
|
||||
close_vgmstream(vgmstream);
|
||||
//vgmstream = temp_vs;
|
||||
return temp_vs;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
case 0x0C: /* DSP-sfx (Switch) */
|
||||
case 0x10: /* DSP-music (Switch) */
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = awc.is_streamed ? layout_blocked_awc : layout_none;
|
||||
vgmstream->full_block_size = awc.block_chunk;
|
||||
|
||||
if (!awc.is_streamed) {
|
||||
/* dsp header */
|
||||
dsp_read_coefs_le(vgmstream, sf, awc.stream_offset + 0x1c + 0x00, 0x00);
|
||||
dsp_read_hist_le (vgmstream, sf, awc.stream_offset + 0x1c + 0x20, 0x00);
|
||||
awc.stream_offset += 0x60;
|
||||
|
||||
/* shouldn't be possible since it's only used for sfx anyway */
|
||||
if (awc.channels > 1)
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xFF:
|
||||
vgmstream->coding_type = coding_SILENCE;
|
||||
snprintf(vgmstream->stream_name, STREAM_NAME_SIZE, "[%s]", "midi");
|
||||
break;
|
||||
|
||||
default:
|
||||
VGM_LOG("AWC: unknown codec 0x%02x\n", awc.codec);
|
||||
goto fail;
|
||||
|
@ -186,23 +210,41 @@ fail:
|
|||
|
||||
|
||||
/* Parse Rockstar's AWC header (much info from LibertyV: https://github.com/koolkdev/libertyv).
|
||||
* Made of entries for N streams, each with a number of tags pointing to chunks (header, data, events, etc). */
|
||||
*
|
||||
* AWC defines logical streams/tracks, each with N tags (type+offset+size) that point to headers/tables with info.
|
||||
* First stream may be a "music" type, then other streams are used as channels and not always define tags.
|
||||
* When the "stream" flag is set data is divided into "blocks" (used for music), described later.
|
||||
* Streams are ordered by hash/id and its tags go in order, but data may be unordered (1st stream audio
|
||||
* or headers could go after others). Defined streams also may be unused/dummy.
|
||||
* Hashes are actually reversable and more or less stream names (see other tools).
|
||||
*
|
||||
* Rough file format:
|
||||
* - base header
|
||||
* - stream tag starts [optional]
|
||||
* - stream hash ids and tag counts (stream N has M tags)
|
||||
* - tags per stream
|
||||
* - data from tags (headers, tables, audio data, etc)
|
||||
*/
|
||||
static int parse_awc_header(STREAMFILE* sf, awc_header* awc) {
|
||||
uint64_t (*read_u64)(off_t,STREAMFILE*) = NULL;
|
||||
uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL;
|
||||
uint16_t (*read_u16)(off_t,STREAMFILE*) = NULL;
|
||||
int i, ch, entries;
|
||||
uint32_t flags, info_header, tag_count = 0, tags_skip = 0;
|
||||
off_t offset;
|
||||
read_u64_t read_u64 = NULL;
|
||||
read_u32_t read_u32 = NULL;
|
||||
read_u16_t read_u16 = NULL;
|
||||
int entries;
|
||||
uint32_t flags, tag_count = 0, tags_skip = 0;
|
||||
uint32_t offset;
|
||||
int target_subsong = sf->stream_index;
|
||||
|
||||
/** base header **/
|
||||
if (is_id32be(0x00,sf,"ADAT")) {
|
||||
awc->big_endian = false;
|
||||
}
|
||||
else if (is_id32be(0x00,sf,"TADA")) {
|
||||
awc->big_endian = true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* check header */
|
||||
if (read_u32be(0x00,sf) != 0x41444154 && /* "ADAT" (LE) */
|
||||
read_u32be(0x00,sf) != 0x54414441) /* "TADA" (BE) */
|
||||
goto fail;
|
||||
|
||||
awc->big_endian = read_u32be(0x00,sf) == 0x54414441;
|
||||
if (awc->big_endian) {
|
||||
read_u64 = read_u64be;
|
||||
read_u32 = read_u32be;
|
||||
|
@ -213,25 +255,32 @@ static int parse_awc_header(STREAMFILE* sf, awc_header* awc) {
|
|||
read_u16 = read_u16le;
|
||||
}
|
||||
|
||||
|
||||
flags = read_u32(0x04,sf);
|
||||
entries = read_u32(0x08,sf);
|
||||
//header_size = read_u32(0x0c,sf); /* after to stream id/tags, not including chunks */
|
||||
//header_size = read_u32(0x0c,sf); /* after stream id+tags */
|
||||
|
||||
offset = 0x10;
|
||||
|
||||
/* flags = 8b (always FF) + 8b (actual flags) + 16b (version, 00=rarely, 01=common) */
|
||||
if ((flags & 0xFF00FFFF) != 0xFF000001 || (flags & 0x00F00000)) {
|
||||
VGM_LOG("AWC: unknown flags 0x%08x\n", flags);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (flags & 0x00010000) /* some kind of mini offset table */
|
||||
/* stream tag starts (ex. stream#0 = 0, stream#1 = 4, stream#2 = 7: to read tags from stream#2 skip to 7th tag) */
|
||||
if (flags & 0x00010000)
|
||||
offset += 0x2 * entries;
|
||||
//if (flags % 0x00020000) /* seems to indicate chunks are not ordered (ie. header may go after data) */
|
||||
// ...
|
||||
//if (flags % 0x00040000) /* music/multichannel flag? (GTA5, not seen in RDR) */
|
||||
// awc->is_music = 1;
|
||||
if (flags & 0x00080000) /* encrypted data chunk (most of GTA5 PC) */
|
||||
|
||||
/* seems to indicate chunks are not ordered (ie. header structures from tags may go after data), usually for non-streams */
|
||||
//if (flags % 0x00020000)
|
||||
// awc->is_unordered = 1;
|
||||
|
||||
/* stream/multichannel flag (rare, GTA5/RDR2) */
|
||||
//if (flags % 0x00040000)
|
||||
// awc->is_multichannel = 1;
|
||||
|
||||
/* encrypted data chunk (most of GTA5 PC for licensed audio) */
|
||||
if (flags & 0x00080000)
|
||||
awc->is_encrypted = 1;
|
||||
|
||||
if (awc->is_encrypted) {
|
||||
|
@ -239,48 +288,84 @@ static int parse_awc_header(STREAMFILE* sf, awc_header* awc) {
|
|||
goto fail;
|
||||
}
|
||||
|
||||
/* Music when the first id is 0 (base/fake entry with info for all channels), sfx pack otherwise.
|
||||
* sfx = N single streams, music = N-1 interleaved mono channels (even for MP3/XMA).
|
||||
* Music seems layered (N-1/2 stereo pairs), maybe set with events? */
|
||||
awc->is_music = (read_u32(offset + 0x00,sf) & 0x1FFFFFFF) == 0x00000000;
|
||||
if (awc->is_music) { /* all streams except id 0 is a channel */
|
||||
|
||||
/* When first stream hash/id is 0 AWC it has fake entry with info for all channels = music, sfx pack otherwise.
|
||||
* sfx = N single streams, music = N interleaved mono channels (even for MP3/XMA/Vorbis/etc).
|
||||
* Channels set a stream hash/id that typically is one of the defined ones and its tags do apply to that
|
||||
* channel, but rarely may not exist. Ex.:
|
||||
*
|
||||
* - bgm01.awc
|
||||
* Stream ID 00000000 (implicit: music stream, all others aren't used)
|
||||
* Tag: music header
|
||||
* Channel 0: ID 9d66fe4c
|
||||
* Channel 1: ID 7a3837ef
|
||||
* Channel 2: ID 032c57e9 (not actually defined)
|
||||
* Tag: data chunk
|
||||
* #Tag: sfx header (only in buggy files)
|
||||
* Stream ID 7a3837ef (no tags)
|
||||
* Stream ID 9d66fe4c (notice this is channel 0 but streams are ordered by hash)
|
||||
* Tag: Event config
|
||||
*
|
||||
* - sfx01.awc
|
||||
* Stream ID 9d66fe4c
|
||||
* Tag: sfx header
|
||||
* Tag: data chunk
|
||||
* Stream ID 7a3837ef
|
||||
* Tag: sfx header
|
||||
* Tag: data chunk
|
||||
*
|
||||
* Music 'stream' defines it's own (streamed/blocked) data chunk, so other stream's data or headers aren't used,
|
||||
* but in rare cases they actually define a useless sfx header or even a separate cloned data chunk. That seems
|
||||
* to be a bug and are ignored (ex. RDR's ftr_harmonica_01, or RDR SW's countdown_song_01).
|
||||
*/
|
||||
|
||||
awc->is_streamed = (read_u32(offset + 0x00,sf) & 0x1FFFFFFF) == 0x00000000; /* first stream's hash/id is 0 */
|
||||
if (awc->is_streamed) { /* music with N channels, other streams aren't used ignored */
|
||||
awc->total_subsongs = 1;
|
||||
target_subsong = 1; /* we only need id 0, though channels may have its own tags/chunks */
|
||||
target_subsong = 1;
|
||||
/* array access below */
|
||||
if (entries >= AWC_MAX_MUSIC_CHANNELS)
|
||||
goto fail;
|
||||
}
|
||||
else { /* each stream is a single sound */
|
||||
else { /* sfx pack, each stream is a sound */
|
||||
awc->total_subsongs = entries;
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
if (target_subsong < 0 || target_subsong > awc->total_subsongs || awc->total_subsongs < 1) goto fail;
|
||||
}
|
||||
|
||||
|
||||
/* get stream base info */
|
||||
for (i = 0; i < entries; i++) {
|
||||
info_header = read_u32(offset + 0x04*i, sf);
|
||||
tag_count = (info_header >> 29) & 0x7; /* 3b */
|
||||
//id = (info_header >> 0) & 0x1FFFFFFF; /* 29b */
|
||||
if (target_subsong-1 == i)
|
||||
break;
|
||||
tags_skip += tag_count; /* tags to skip to reach target's tags, in the next header */
|
||||
/** stream ids and tag counts **/
|
||||
for (int i = 0; i < entries; i++) {
|
||||
uint32_t info_header = read_u32(offset + 0x00, sf);
|
||||
int entry_count = (info_header >> 29) & 0x7; /* 3b */
|
||||
uint32_t hash_id = (info_header >> 0) & 0x1FFFFFFF; /* 29b */
|
||||
|
||||
if (i + 1 < target_subsong)
|
||||
tags_skip += entry_count; /* tags to skip to reach target's tags, in the next header */
|
||||
if (target_subsong == i + 1)
|
||||
tag_count = entry_count;
|
||||
|
||||
if (awc->is_streamed) {
|
||||
awc->stream_info[i].hash_id = hash_id;
|
||||
awc->stream_info[i].tag_count = entry_count;
|
||||
}
|
||||
offset += 0x04*entries;
|
||||
offset += 0x08*tags_skip;
|
||||
|
||||
/* get stream tags */
|
||||
for (i = 0; i < tag_count; i++) {
|
||||
uint64_t tag_header;
|
||||
uint8_t tag_type;
|
||||
size_t tag_size;
|
||||
off_t tag_offset;
|
||||
offset += 0x04;
|
||||
}
|
||||
awc->tags_offset = offset; /* where tags for stream start */
|
||||
|
||||
tag_header = read_u64(offset + 0x08*i,sf);
|
||||
tag_type = (uint8_t)((tag_header >> 56) & 0xFF); /* 8b */
|
||||
tag_size = (size_t)((tag_header >> 28) & 0x0FFFFFFF); /* 28b */
|
||||
tag_offset = (off_t)((tag_header >> 0) & 0x0FFFFFFF); /* 28b */
|
||||
;VGM_LOG("AWC: tag%i/%i at %lx: t=%x, o=%lx, s=%x\n", i, tag_count, offset + 0x08*i, tag_type, tag_offset, tag_size);
|
||||
offset += 0x08 * tags_skip; /* ignore tags for other streams */
|
||||
|
||||
/* Tags are apparently part of a hash derived from a word ("data", "format", etc).
|
||||
* If music + 1ch, the header and data chunks can repeat for no reason (sometimes not even pointed). */
|
||||
|
||||
|
||||
/** tags per stream **/
|
||||
for (int i = 0; i < tag_count; i++) {
|
||||
uint64_t tag_header = read_u64(offset + 0x08*i,sf);
|
||||
uint8_t tag_type = ((tag_header >> 56) & 0xFF); /* 8b */
|
||||
uint32_t tag_size = ((tag_header >> 28) & 0x0FFFFFFF); /* 28b */
|
||||
uint32_t tag_offset = ((tag_header >> 0) & 0x0FFFFFFF); /* 28b */
|
||||
|
||||
/* types are apparently part of a hash derived from a word ("data", "format", etc). */
|
||||
switch(tag_type) {
|
||||
case 0x55: /* data */
|
||||
awc->stream_offset = tag_offset;
|
||||
|
@ -288,30 +373,31 @@ static int parse_awc_header(STREAMFILE* sf, awc_header* awc) {
|
|||
break;
|
||||
|
||||
case 0x48: /* music header */
|
||||
|
||||
if (!awc->is_music) {
|
||||
VGM_LOG("AWC: music header found in sfx\n");
|
||||
if (!awc->is_streamed) {
|
||||
VGM_LOG("AWC: music header found but not streamed\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* 0x00(32): unknown (some count?) */
|
||||
awc->block_count = read_u32(tag_offset + 0x00,sf);
|
||||
awc->block_chunk = read_u32(tag_offset + 0x04,sf);
|
||||
awc->channels = read_u32(tag_offset + 0x08,sf);
|
||||
|
||||
if (awc->channels != entries - 1) { /* not counting id-0 */
|
||||
VGM_LOG("AWC: number of music channels doesn't match entries\n");
|
||||
goto fail;
|
||||
/* extremely rare but doesn't seem to matter, some streams are dummies (RDR2 STREAMS/ABIGAIL_HUMMING_*) */
|
||||
//goto fail;
|
||||
}
|
||||
|
||||
for (ch = 0; ch < awc->channels; ch++) {
|
||||
for (int ch = 0; ch < awc->channels; ch++) {
|
||||
int num_samples, sample_rate, codec;
|
||||
/* 0x00): stream id (not always in the header entries order) */
|
||||
|
||||
awc->channel_hash[ch] = read_u32(tag_offset + 0x0c + 0x10*ch + 0x00,sf); /* reference, for vorbis */
|
||||
num_samples = read_u32(tag_offset + 0x0c + 0x10*ch + 0x04,sf);
|
||||
/* 0x08: headroom */
|
||||
sample_rate = read_u16(tag_offset + 0x0c + 0x10*ch + 0x0a,sf);
|
||||
codec = read_u8(tag_offset + 0x0c + 0x10*ch + 0x0c,sf);
|
||||
/* 0x0d(8): round size? */
|
||||
/* 0x0e: unknown (zero/-1) */
|
||||
/* 0x0e: unknown (zero/-1, loop flag? BOB_FINALE_1_A.awc, but also set in stingers) */
|
||||
|
||||
/* validate channels differences */
|
||||
if ((awc->num_samples && !(awc->num_samples >= num_samples - 10 && awc->num_samples <= num_samples + 10)) ||
|
||||
|
@ -336,77 +422,120 @@ static int parse_awc_header(STREAMFILE* sf, awc_header* awc) {
|
|||
break;
|
||||
|
||||
case 0xFA: /* sfx header */
|
||||
if (awc->is_music) {
|
||||
VGM_LOG("AWC: sfx header found in music\n");
|
||||
goto fail;
|
||||
if (awc->is_streamed) {
|
||||
VGM_LOG("AWC: sfx header found but streamed\n");
|
||||
break; //goto fail; /* rare (RDR PC/Switch) */
|
||||
}
|
||||
|
||||
awc->num_samples = read_u32(tag_offset + 0x00,sf);
|
||||
/* 0x04: -1? */
|
||||
awc->sample_rate = read_u16(tag_offset + 0x08,sf);
|
||||
/* 0x0a: unknown x4 */
|
||||
/* 0x0a: headroom */
|
||||
/* 0x0c: unknown */
|
||||
/* 0x0e: unknown */
|
||||
/* 0x10: unknown */
|
||||
/* 0x12: null? */
|
||||
awc->codec = read_u8(tag_offset + 0x13, sf);
|
||||
/* 0x14: ? (PS3 only, for any codec) */
|
||||
|
||||
awc->channels = 1;
|
||||
break;
|
||||
|
||||
case 0x76: /* sfx header for vorbis */
|
||||
if (awc->is_music) {
|
||||
VGM_LOG("AWC: sfx header found in music\n");
|
||||
if (awc->is_streamed) {
|
||||
VGM_LOG("AWC: sfx header found but streamed\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
awc->num_samples = read_u32(tag_offset + 0x00,sf);
|
||||
/* 0x04: -1? */
|
||||
awc->sample_rate = read_u16(tag_offset + 0x08,sf);
|
||||
/* 0x0a: granule start? (negative) */
|
||||
/* 0x0c: granule max? */
|
||||
/* 0x0a: headroom */
|
||||
/* 0x0c: unknown */
|
||||
/* 0x0e: unknown */
|
||||
/* 0x10: unknown */
|
||||
awc->codec = read_u8(tag_offset + 0x1c, sf); /* 16b? */
|
||||
/* 0x1e: vorbis header size */
|
||||
awc->channels = 1;
|
||||
/* 0x1e: vorbis setup size */
|
||||
if (read_u16(tag_offset + 0x1e,sf))/* rarely not set and uses a tag below */
|
||||
awc->vorbis_offset[0] = tag_offset + 0x20; /* data up to vorbis setup size */
|
||||
|
||||
awc->vorbis_offset[0] = tag_offset + 0x20;
|
||||
awc->channels = 1;
|
||||
break;
|
||||
|
||||
case 0xA3: /* block-to-sample table (32b x number of blocks w/ num_samples at the start of each block) */
|
||||
case 0x7F: /* vorbis setup */
|
||||
if (awc->is_streamed) {
|
||||
/* music stream doesn't have this (instead every channel-strem have one, parsed later) */
|
||||
VGM_LOG("AWC: vorbis setup found but streamed\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* very rarely used for sfx: SS_AM/GESTURE01.awc */
|
||||
awc->vorbis_offset[0] = tag_offset;
|
||||
break;
|
||||
|
||||
case 0x68: /* midi data [Red Dead Redemption 2 (PC)] */
|
||||
/* set fake info so awc doesn't break */
|
||||
awc->stream_offset = tag_offset;
|
||||
awc->stream_size = tag_size;
|
||||
|
||||
awc->num_samples = 48000;
|
||||
awc->sample_rate = 48000;
|
||||
awc->codec = 0xFF;
|
||||
awc->channels = 1;
|
||||
break;
|
||||
|
||||
|
||||
case 0xA3: /* block-to-sample table (32b x number of blocks w/ num_samples at the start of each block)
|
||||
* or frame-size table (16b x number of frames) in some cases (ex. sfx+mpeg but not sfx+vorbis) */
|
||||
case 0xBD: /* events (32bx4): type_hash, params_hash, timestamp_ms, flags */
|
||||
case 0x5C: /* animation/RSC config? */
|
||||
default: /* 0x68=midi?, 0x36=hash thing?, 0x2B=sizes, 0x5A/0xD9=? */
|
||||
case 0x5C: /* animation/RSC info? */
|
||||
case 0x81: /* animation/CSR info? */
|
||||
case 0x36: /* list of hash-things? */
|
||||
case 0x2B: /* events/sizes? */
|
||||
default: /* 0x68=midi?, 0x5A/0xD9=? */
|
||||
//VGM_LOG("AWC: ignoring unknown tag 0x%02x\n", tag);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* in music mode there tags for other streams we don't use, except for vorbis. streams have vorbis setup info for channels, but
|
||||
* channel<>stream order doesn't match, so we need to assign setup to channels. All setups seem to be the same though. */
|
||||
if (awc->is_streamed && awc->codec == 0x08) {
|
||||
offset = awc->tags_offset;
|
||||
offset += 0x08 * awc->stream_info[0].tag_count; /* ignore 1st/music stream */
|
||||
|
||||
for (int stream = 1; stream < entries; stream++) {
|
||||
for (int tag = 0; tag < awc->stream_info[stream].tag_count; tag++) {
|
||||
uint64_t tag_header = read_u64(offset,sf);
|
||||
uint8_t tag_type = ((tag_header >> 56) & 0xFF); /* 8b */
|
||||
//uint32_t tag_size = ((tag_header >> 28) & 0x0FFFFFFF); /* 28b */
|
||||
uint32_t tag_offset = ((tag_header >> 0) & 0x0FFFFFFF); /* 28b */
|
||||
|
||||
switch(tag_type) {
|
||||
case 0x7f: /* vorbis setup */
|
||||
/* find which channel uses this stream's data */
|
||||
for (int ch = 0; ch < awc->channels; ch++) {
|
||||
if (awc->channel_hash[ch] == awc->stream_info[stream].hash_id) {
|
||||
awc->vorbis_offset[ch] = tag_offset;
|
||||
//awc->vorbis_size[ch] = tag_size; /* not needed (implicit)*/
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
offset += 0x08;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!awc->stream_offset) {
|
||||
VGM_LOG("AWC: stream offset not found\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* vorbis offset table, somehow offsets are unordered and can go before tags */
|
||||
if (awc->is_music && awc->codec == 0x08) {
|
||||
offset += 0x08 * tag_count;
|
||||
|
||||
for (ch = 0; ch < awc->channels; ch++) {
|
||||
awc->vorbis_offset[ch] = read_u16(offset + 0x08*ch + 0x00, sf);
|
||||
/* 0x02: always 0xB000? */
|
||||
/* 0x04: always 0x00CD? */
|
||||
/* 0x06: always 0x7F00? */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* In music mode, data is divided into blocks of block_chunk size with padding.
|
||||
* Each block has a header/seek table and interleaved data for all channels */
|
||||
{
|
||||
int32_t seek_start = read_u32(awc->stream_offset, sf); /* -1 in later (RDR2) versions */
|
||||
if (awc->is_music && !(seek_start == 0 || seek_start == -1)) {
|
||||
VGM_LOG("AWC: music found, but block doesn't start with seek table at %x\n", (uint32_t)awc->stream_offset);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
|
@ -414,140 +543,113 @@ fail:
|
|||
|
||||
/* ************************************************************************* */
|
||||
|
||||
//TODO: this method won't work properly, needs internal handling of blocks.
|
||||
//
|
||||
// This setups a decoder per block, but seems Vorbis' uses first frame as setup so it
|
||||
// returns samples (576 vs 1024), making num_samples count in each block being off + causing
|
||||
// gaps. So they must be using a single encoder + setting decode_to_discard per block
|
||||
// to ge the thing working.
|
||||
//
|
||||
// However since blocks are probably also used for seeking, maybe they aren't resetting
|
||||
// the decoder when seeking? or they force first frame to be 1024?
|
||||
//
|
||||
// In case of Vorvis, when setting skip samples seems repeated data from last block is
|
||||
// exactly last 0x800 bytes of that channel.
|
||||
|
||||
static VGMSTREAM* build_block_vgmstream(STREAMFILE* sf, awc_header* awc, int channel, int32_t num_samples, int32_t skip_samples, off_t block_start, size_t block_size) {
|
||||
STREAMFILE* temp_sf = NULL;
|
||||
static VGMSTREAM* build_blocks_vgmstream(STREAMFILE* sf, awc_header* awc, int channel) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE* temp_sf = NULL;
|
||||
int block_channels = 1;
|
||||
uint32_t substream_size, substream_offset;
|
||||
|
||||
|
||||
/* setup custom IO streamfile that removes AWC's odd blocks (not perfect but serviceable) */
|
||||
temp_sf = setup_awc_streamfile(sf, awc->stream_offset, awc->stream_size, awc->block_chunk, awc->channels, channel, awc->codec, awc->big_endian);
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
substream_offset = 0x00;
|
||||
substream_size = get_streamfile_size(temp_sf);
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(block_channels, 0);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = awc->sample_rate;
|
||||
vgmstream->num_samples = num_samples - skip_samples;
|
||||
vgmstream->stream_size = block_size;
|
||||
vgmstream->meta_type = meta_AWC;
|
||||
vgmstream->sample_rate = awc->sample_rate;
|
||||
vgmstream->num_samples = awc->num_samples;
|
||||
vgmstream->stream_size = awc->stream_size;
|
||||
|
||||
vgmstream->stream_size = substream_size;
|
||||
|
||||
|
||||
switch(awc->codec) {
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case 0x05: { /* XMA2 (X360) */
|
||||
vgmstream->codec_data = init_ffmpeg_xma2_raw(temp_sf, substream_offset, substream_size, awc->num_samples, block_channels, awc->sample_rate, 0, 0);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
xma_fix_raw_samples(vgmstream, temp_sf, substream_offset, substream_size, 0, 0,0); /* samples are ok? */
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#ifdef VGM_USE_VORBIS
|
||||
case 0x08: { /* Vorbis (PC) [Red Dead Redemption 2 (PC)] */
|
||||
case 0x08: {
|
||||
vorbis_custom_config cfg = {0};
|
||||
|
||||
cfg.channels = 1;
|
||||
cfg.sample_rate = awc->sample_rate;
|
||||
cfg.header_offset = awc->vorbis_offset[channel];
|
||||
//cfg.skip_samples = skip_samples; //todo
|
||||
cfg.header_offset = awc->vorbis_offset[channel]; /* setup page goes separate */
|
||||
|
||||
vgmstream->codec_data = init_vorbis_custom(sf, block_start, VORBIS_AWC, &cfg);
|
||||
/* note that it needs sf on init to read the header + start offset for later, and temp_sf on decode to read data */
|
||||
vgmstream->codec_data = init_vorbis_custom(sf, substream_offset, VORBIS_AWC, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->coding_type = coding_VORBIS_custom;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#ifdef VGM_USE_ATRAC9
|
||||
case 0x0F: {
|
||||
atrac9_config cfg = {0};
|
||||
|
||||
/* read from first block (all blocks have it but same thing), see awc_streamfile.h */
|
||||
uint32_t extradata_offset = awc->stream_offset + 0x10 * awc->channels + 0x70 * channel + 0x0c;
|
||||
|
||||
cfg.channels = block_channels;
|
||||
cfg.encoder_delay = 0; //?
|
||||
cfg.config_data = read_u32be(extradata_offset, sf);
|
||||
|
||||
vgmstream->codec_data = init_atrac9(&cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_ATRAC9;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, block_start))
|
||||
if (!vgmstream_open_stream(vgmstream, temp_sf, substream_offset))
|
||||
goto fail;
|
||||
close_streamfile(temp_sf);
|
||||
return vgmstream;
|
||||
fail:
|
||||
;VGM_LOG("AWB: can't open decoder\n");
|
||||
close_vgmstream(vgmstream);
|
||||
close_streamfile(temp_sf);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static VGMSTREAM* build_blocks_vgmstream(STREAMFILE* sf, awc_header* awc, int channel) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
segmented_layout_data* data = NULL;
|
||||
int i, ch;
|
||||
int blocks = awc->stream_size / awc->block_chunk + (awc->stream_size % awc->block_chunk ? 1 : 0) ;
|
||||
|
||||
|
||||
/* init layout */
|
||||
data = init_layout_segmented(blocks);
|
||||
if (!data) goto fail;
|
||||
|
||||
/* one segment per block of this channel */
|
||||
for (i = 0; i < blocks; i++) {
|
||||
off_t block_offset = awc->stream_offset + i * awc->block_chunk;
|
||||
int32_t num_samples = 0, skip_samples = 0;
|
||||
uint32_t header_skip = 0, block_skip = 0, block_start = 0, block_data = 0;
|
||||
|
||||
/* read stupid block crap to get proper offsets and whatnot, format:
|
||||
* - per channel: number of channel entries + skip samples + num samples
|
||||
* - per channel: seek table with N entries */
|
||||
for (ch = 0; ch < awc->channels; ch++) {
|
||||
/* 0x00: -1 */
|
||||
int entries = read_u32le(block_offset + 0x18 * ch + 0x04, sf);
|
||||
int32_t entry_skip = read_u32le(block_offset + 0x18 * ch + 0x08, sf);
|
||||
int32_t entry_samples = read_u32le(block_offset + 0x18 * ch + 0x0c, sf);
|
||||
|
||||
if (ch == channel) {
|
||||
num_samples = entry_samples;
|
||||
skip_samples = entry_skip;
|
||||
|
||||
block_start = block_offset + block_skip;
|
||||
block_data = entries * 0x800;
|
||||
}
|
||||
|
||||
header_skip += 0x18 + entries * 0x04;
|
||||
block_skip += entries * 0x800;
|
||||
}
|
||||
|
||||
if (!block_start)
|
||||
goto fail;
|
||||
|
||||
header_skip = align_size_to_block(header_skip, 0x800);
|
||||
block_start += header_skip;
|
||||
//;VGM_LOG("AWC: build ch%i, block=%i at %lx, o=%x, s=%x, ns=%i, ss=%i\n", channel, i, block_offset, block_start, block_data, num_samples, skip_samples);
|
||||
|
||||
data->segments[i] = build_block_vgmstream(sf, awc, channel, num_samples, skip_samples, block_start, block_data);
|
||||
if (!data->segments[i]) goto fail;
|
||||
}
|
||||
|
||||
/* setup VGMSTREAMs */
|
||||
if (!setup_layout_segmented(data))
|
||||
goto fail;
|
||||
|
||||
/* build the layout VGMSTREAM */
|
||||
vgmstream = allocate_segmented_vgmstream(data, 0, 0, 0);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
return vgmstream;
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
if (!vgmstream)
|
||||
free_layout_segmented(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ************************************************************************* */
|
||||
|
||||
/* Make layers per channel for AWC's abhorrent blocks.
|
||||
/* Make layers per channel for AWC's abhorrent blocks (see read_awb_block).
|
||||
*
|
||||
* File has N channels = N streams, that use their own mono decoder.
|
||||
* Each block then has header + seek table for all channels. But in each block there is
|
||||
* a "skip samples" value per channel, and blocks repeat some data from last block
|
||||
* for this, so PCM must be discarded. Also, channels in a block don't need to have
|
||||
* the same number of samples.
|
||||
* A "music" .awc has N channels = N streams (each using their own mono decoder) chunked in "blocks".
|
||||
* Each block then has header + seek table + etc for all channels. But when blocks change, each channel
|
||||
* may have a "skip samples" value and blocks repeat some data from last block, so output PCM must be
|
||||
* discarded to avoid channels desyncing. Channels in a block don't need to have the same number of samples.
|
||||
* (mainly seen in MPEG).
|
||||
*/
|
||||
//TODO: this method won't fully work, needs feed decoder + block handler that interacts with decoder(s?)
|
||||
// (doesn't use multiple decoders since default encoder delay in Vorbis would discard too much per block)
|
||||
//
|
||||
// When blocks change presumably block handler needs to tell decoder to finish decoding all from prev block
|
||||
// then skip samples from next decodes. Also since samples may vary per channel, each would handle blocks
|
||||
// independently.
|
||||
//
|
||||
// This can be simulated by making one decoder per block (segmented, but opens too many SFs and can't skip
|
||||
// samples correctly), or with a custom STREAMFILE that skips repeated block (works ok-ish but not all codecs).
|
||||
static layered_layout_data* build_layered_awc(STREAMFILE* sf, awc_header* awc) {
|
||||
int i;
|
||||
layered_layout_data* data = NULL;
|
||||
|
@ -571,4 +673,3 @@ fail:
|
|||
free_layout_layered(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
253
Frameworks/vgmstream/vgmstream/src/meta/awc_streamfile.h
Normal file
253
Frameworks/vgmstream/vgmstream/src/meta/awc_streamfile.h
Normal file
|
@ -0,0 +1,253 @@
|
|||
#ifndef _AWC_STREAMFILE_H_
|
||||
#define _AWC_STREAMFILE_H_
|
||||
#include "deblock_streamfile.h"
|
||||
#include "../util/endianness.h"
|
||||
|
||||
#define AWC_MAX_MUSIC_CHANNELS 32 /* seen ~24 */
|
||||
|
||||
/* ************************************************************************* */
|
||||
|
||||
typedef struct {
|
||||
int start_entry; /* innacurate! */
|
||||
int entries;
|
||||
int32_t channel_skip;
|
||||
int32_t channel_samples;
|
||||
|
||||
uint32_t frame_size;
|
||||
|
||||
/* derived */
|
||||
uint32_t chunk_start; /* relative to block offset */
|
||||
uint32_t chunk_size; /* size of this channel's data (not including padding) */
|
||||
} awc_block_t;
|
||||
|
||||
typedef struct {
|
||||
int big_endian;
|
||||
uint8_t codec;
|
||||
int channels;
|
||||
uint32_t block_offset;
|
||||
awc_block_t blk[AWC_MAX_MUSIC_CHANNELS];
|
||||
} awc_block_info_t;
|
||||
|
||||
/* Block format:
|
||||
* - block header for all channels (needed to find frame start)
|
||||
* - frames from channel 1
|
||||
* - ...
|
||||
* - frames from channel N
|
||||
* - usually there is padding between channels or blocks (usually 0s but seen 0x97 in AT9)
|
||||
*
|
||||
* Header format:
|
||||
* - per channel (frame start table)
|
||||
* 0x00: start entry for that channel? (-1 in vorbis)
|
||||
* may be off by +1/+2?
|
||||
* ex. on block 0, ch0/1 have 0x007F frames, a start entry is: ch0=0x0000, ch1=0x007F (MP3)
|
||||
* ex. on block 0, ch0/1 have 0x02A9 frames, a start entry is: ch0=0x0000, ch1=0x02AA (AT9) !!
|
||||
* (sum of all values from all channels may go beyond all posible frames, no idea)
|
||||
* 0x04: frames in this channel (may be different between channels)
|
||||
* 'frames' here may be actual single decoder frames or a chunk of frames
|
||||
* 0x08: samples to discard in the beginning of this block (MPEG/XMA2/Vorbis only?)
|
||||
* 0x0c: samples in channel (for MPEG/XMA2 can vary between channels)
|
||||
* full samples without removing samples to discard
|
||||
* (next fields only exists for MPEG, Vorbis or some IMA versions)
|
||||
* 0x10: (MPEG only, empty otherwise) close to number of frames but varies a bit?
|
||||
* 0x14: (MPEG only, empty otherwise) channel chunk size (not counting padding)
|
||||
* - for each channel (seek table)
|
||||
* 32b * entries = global samples per frame in each block (for MPEG probably per full frame)
|
||||
* (AT9 doesn't have a seek table as it's CBR)
|
||||
* - per channel (ATRAC9/DSP extra info):
|
||||
* 0x00: "D11A"
|
||||
* 0x04: frame size
|
||||
* 0x06: frame samples
|
||||
* 0x08: flags? (0x0103=AT9, 0x0104=DSP)
|
||||
* 0x0a: sample rate
|
||||
* 0x0c: ATRAC9 config (repeated but same for all blocks) or "D11E" (DSP)
|
||||
* 0x10-0x70: padding with 0x77 (ATRAC3) or standard DSP header for original full file (DSP)
|
||||
* - padding until channel data start, depending on codec (DSP/ATRAC9: one, others: aligned to 0x800)
|
||||
* - per channel:
|
||||
* 0xNN: channel frames
|
||||
* 0xNN: may have padding between channels depending on codec (mainly MPEG/XMA)
|
||||
* - padding until this block's end
|
||||
*/
|
||||
static bool read_awb_block(STREAMFILE* sf, awc_block_info_t* bi) {
|
||||
read_s32_t read_s32 = bi->big_endian ? read_s32be : read_s32le;
|
||||
read_u16_t read_u16 = bi->big_endian ? read_u16be : read_u16le;
|
||||
|
||||
uint32_t channel_entry_size, seek_entry_size, extra_entry_size, header_padding;
|
||||
uint32_t offset = bi->block_offset;
|
||||
int channels = bi->channels;
|
||||
/* read stupid block crap + derived info at once so hopefully it's a bit easier to understand */
|
||||
|
||||
switch(bi->codec) {
|
||||
case 0x05: /* XMA2 */
|
||||
channel_entry_size = 0x10;
|
||||
seek_entry_size = 0x04;
|
||||
extra_entry_size = 0x00;
|
||||
header_padding = 0x800;
|
||||
break;
|
||||
case 0x08: /* Vorbis */
|
||||
channel_entry_size = 0x18;
|
||||
seek_entry_size = 0x04;
|
||||
extra_entry_size = 0x00;
|
||||
header_padding = 0x800;
|
||||
break;
|
||||
case 0x0F: /* ATRAC9 */
|
||||
channel_entry_size = 0x10;
|
||||
seek_entry_size = 0x00;
|
||||
extra_entry_size = 0x70;
|
||||
header_padding = 0x00;
|
||||
break;
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* channel info table */
|
||||
for (int ch = 0; ch < bi->channels; ch++) {
|
||||
bi->blk[ch].start_entry = read_s32(offset + 0x00, sf);
|
||||
bi->blk[ch].entries = read_s32(offset + 0x04, sf);
|
||||
bi->blk[ch].channel_skip = read_s32(offset + 0x08, sf);
|
||||
bi->blk[ch].channel_samples = read_s32(offset + 0x0c, sf);
|
||||
/* others: optional */
|
||||
|
||||
offset += channel_entry_size;
|
||||
}
|
||||
|
||||
/* seek table */
|
||||
for (int ch = 0; ch < channels; ch++) {
|
||||
offset += bi->blk[ch].entries * seek_entry_size;
|
||||
}
|
||||
|
||||
/* extra table and derived info */
|
||||
for (int ch = 0; ch < channels; ch++) {
|
||||
switch(bi->codec) {
|
||||
case 0x05: /* XMA2 */
|
||||
case 0x08: /* Vorbis */
|
||||
/* each 'frame'/entry in Vorbis is actually N vorbis frames then padding up to 0x800
|
||||
* (more or less like a big Ogg page or XMA 'frame'). Padding is considered part of
|
||||
* the data and handled by the decoder, since sfx (non-blocked) algo have it. */
|
||||
bi->blk[ch].frame_size = 0x800;
|
||||
bi->blk[ch].chunk_size = bi->blk[ch].entries * bi->blk[ch].frame_size;
|
||||
break;
|
||||
|
||||
case 0x0F: /* ATRAC9 */
|
||||
bi->blk[ch].frame_size = read_u16(offset + 0x04, sf);
|
||||
bi->blk[ch].chunk_size = bi->blk[ch].entries * bi->blk[ch].frame_size;
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
offset += extra_entry_size;
|
||||
}
|
||||
|
||||
/* header done, move into data start */
|
||||
if (header_padding) {
|
||||
/* padding on the current size rather than file offset (block meant to be read into memory, probably) */
|
||||
uint32_t header_size = offset - bi->block_offset;
|
||||
offset = bi->block_offset + align_size_to_block(header_size, header_padding);
|
||||
}
|
||||
|
||||
/* set frame starts per channel */
|
||||
for (int ch = 0; ch < channels; ch++) {
|
||||
bi->blk[ch].chunk_start = offset - bi->block_offset;
|
||||
offset += bi->blk[ch].chunk_size;
|
||||
}
|
||||
|
||||
/* beyond this is padding until chunk_start */
|
||||
|
||||
return true;
|
||||
fail:
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Find data that repeats in the beginning of a new block at the end of last block.
|
||||
* When a new block starts there is some repeated data + channel_skip (for seeking + encoder delay?).
|
||||
* Detect it so decoder may ignore it. */
|
||||
static uint32_t get_block_repeated_size(STREAMFILE* sf, awc_block_info_t* bi, int channel) {
|
||||
|
||||
if (bi->blk[channel].channel_skip == 0)
|
||||
return 0;
|
||||
|
||||
switch(bi->codec) {
|
||||
case 0x05: { /* XMA2 */
|
||||
const uint32_t samples_per_subframe = 512;
|
||||
uint32_t samples_this_frame;
|
||||
uint8_t subframes;
|
||||
uint32_t offset = bi->block_offset + bi->blk[channel].chunk_start;
|
||||
int repeat_samples = bi->blk[channel].channel_skip;
|
||||
|
||||
//TODO: fix (needs proper decoder + sample discard)
|
||||
/* 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 = (read_u8(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 bi->blk[channel].frame_size;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
case 0x08: /* Vorbis */
|
||||
/* when data repeats seems to clone exactly the last super-frame */
|
||||
return bi->blk[channel].frame_size;
|
||||
|
||||
case 0x0F: /* ATRAC9 */
|
||||
default:
|
||||
VGM_LOG("AWC: found channel skip in codec %x\n", bi->codec); /* not seen */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* ************************************************************************* */
|
||||
|
||||
static void block_callback(STREAMFILE *sf, deblock_io_data* data) {
|
||||
int channel = data->cfg.track_number;
|
||||
awc_block_info_t bi = {0};
|
||||
|
||||
bi.big_endian = data->cfg.big_endian;
|
||||
bi.block_offset = data->physical_offset;
|
||||
bi.channels = data->cfg.track_count;
|
||||
bi.codec = data->cfg.track_type;
|
||||
|
||||
if (!read_awb_block(sf, &bi))
|
||||
return; //???
|
||||
|
||||
uint32_t repeat_size = get_block_repeated_size(sf, &bi, channel);
|
||||
|
||||
data->block_size = data->cfg.chunk_size;
|
||||
data->skip_size = bi.blk[channel].chunk_start + repeat_size;
|
||||
data->data_size = bi.blk[channel].chunk_size - repeat_size;
|
||||
}
|
||||
|
||||
/* deblocks AWC blocks */
|
||||
static STREAMFILE* setup_awc_streamfile(STREAMFILE* sf, uint32_t stream_offset, uint32_t stream_size, uint32_t block_size, int channels, int channel, uint8_t codec, int big_endian) {
|
||||
STREAMFILE* new_sf = NULL;
|
||||
deblock_config_t cfg = {0};
|
||||
|
||||
if (channels >= AWC_MAX_MUSIC_CHANNELS)
|
||||
return NULL;
|
||||
|
||||
cfg.track_number = channel;
|
||||
cfg.track_count = channels;
|
||||
cfg.stream_start = stream_offset;
|
||||
cfg.stream_size = stream_size;
|
||||
cfg.chunk_size = block_size;
|
||||
cfg.track_type = codec;
|
||||
cfg.big_endian = big_endian;
|
||||
//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
|
|
@ -1,114 +0,0 @@
|
|||
#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_ */
|
|
@ -143,6 +143,11 @@ VGMSTREAM* init_vgmstream_bkhd(STREAMFILE* sf) {
|
|||
vgmstream = init_vgmstream_wwise_bnk(temp_sf, &prefetch);
|
||||
if (!vgmstream) goto fail;
|
||||
}
|
||||
else if (is_id32be(0x00, temp_sf, "ADM3")) {
|
||||
// TODO: these may have multiple subsongs
|
||||
vgmstream = init_vgmstream_adm3(temp_sf);
|
||||
if (!vgmstream) goto fail;
|
||||
}
|
||||
else if (read_f32(subfile_offset + 0x02, temp_sf) >= 30.0 &&
|
||||
read_f32(subfile_offset + 0x02, temp_sf) <= 250.0) {
|
||||
is_wmid = 1;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#include "../coding/coding.h"
|
||||
#include "../util/endianness.h"
|
||||
|
||||
typedef enum { NONE, DUMMY, PSX, PCM16, ATRAC9, HEVAG, RIFF_ATRAC9 } bnk_codec;
|
||||
typedef enum { NONE, DUMMY, PSX, PCM16, MPEG, ATRAC9, HEVAG, RIFF_ATRAC9 } bnk_codec;
|
||||
|
||||
typedef struct {
|
||||
bnk_codec codec;
|
||||
|
@ -41,6 +41,7 @@ typedef struct {
|
|||
int32_t num_samples;
|
||||
int32_t loop_start;
|
||||
int32_t loop_end;
|
||||
int32_t encoder_delay;
|
||||
|
||||
uint32_t start_offset;
|
||||
uint32_t stream_offset;
|
||||
|
@ -81,7 +82,7 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
|
|||
|
||||
vgmstream->meta_type = meta_BNK_SONY;
|
||||
|
||||
if (!h.stream_name_size)
|
||||
if (h.stream_name_size >= STREAM_NAME_SIZE || h.stream_name_size <= 0)
|
||||
h.stream_name_size = STREAM_NAME_SIZE;
|
||||
|
||||
if (!h.bank_name_offset && h.stream_name_offset) {
|
||||
|
@ -90,7 +91,7 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
|
|||
else if (h.bank_name_offset && h.stream_name_offset) {
|
||||
read_string(bank_name, h.stream_name_size, h.bank_name_offset, sf);
|
||||
read_string(stream_name, h.stream_name_size, h.stream_name_offset, sf);
|
||||
snprintf(vgmstream->stream_name, h.stream_name_size, "%s/%s", bank_name, stream_name);
|
||||
snprintf(vgmstream->stream_name, h.stream_name_size, "%s%s%s", bank_name, bank_name[0] == '\0' ? "" : "/", stream_name);
|
||||
}
|
||||
|
||||
|
||||
|
@ -147,6 +148,20 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
|
|||
return temp_vs;
|
||||
}
|
||||
#endif
|
||||
#ifdef VGM_USE_MPEG
|
||||
case MPEG: {
|
||||
mpeg_custom_config cfg = {0};
|
||||
cfg.skip_samples = h.encoder_delay;
|
||||
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->codec_data = init_mpeg_custom(sf, h.start_offset, &vgmstream->coding_type, h.channels, MPEG_STANDARD, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
|
||||
vgmstream->num_samples = h.num_samples;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
case PCM16:
|
||||
vgmstream->coding_type = h.big_endian ? coding_PCM16BE : coding_PCM16LE;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
|
@ -567,7 +582,7 @@ static bool process_headers(STREAMFILE* sf, bnk_header_t* h) {
|
|||
goto fail;
|
||||
}
|
||||
|
||||
//;VGM_LOG("BNK: stream at %lx + %x\n", h->stream_offset, h->stream_size);
|
||||
;VGM_LOG("BNK: header %x, stream at %x + %x\n", sndh_offset, h->data_offset + h->stream_offset, h->stream_size);
|
||||
|
||||
return true;
|
||||
fail:
|
||||
|
@ -604,12 +619,12 @@ static bool process_names(STREAMFILE* sf, bnk_header_t* h) {
|
|||
/* table4:
|
||||
* 0x00: bank name (optional)
|
||||
* 0x08: name section offset
|
||||
* 0x0C-0x14: 3 null pointers (reserved?)
|
||||
* 0x0C-0x18: 3 null pointers (reserved?)
|
||||
* 0x18-0x58: 32 name chunk offset indices
|
||||
*/
|
||||
|
||||
/* Name chunks are organised as
|
||||
* (name[0] + name[4] + name[8] + name[12]) & 0x1F;
|
||||
* (name[0] + name[4] + name[8] + name[12]) % 32;
|
||||
* and using that as the index for the chunk offsets
|
||||
* name_sect_offset + (chunk_idx[result] * 0x14);
|
||||
*/
|
||||
|
@ -722,7 +737,7 @@ static bool process_names(STREAMFILE* sf, bnk_header_t* h) {
|
|||
break;
|
||||
}
|
||||
|
||||
//;VGM_LOG("BNK: stream_offset=%lx, stream_size=%x, stream_name_offset=%lx\n", h->stream_offset, h->stream_size, h->stream_name_offset);
|
||||
//;VGM_LOG("BNK: stream_offset=%x, stream_size=%x, stream_name_offset=%x\n", h->stream_offset, h->stream_size, h->stream_name_offset);
|
||||
|
||||
return true;
|
||||
fail:
|
||||
|
@ -813,8 +828,8 @@ static bool process_data(STREAMFILE* sf, bnk_header_t* h) {
|
|||
|
||||
case 0x08:
|
||||
case 0x09:
|
||||
subtype = read_u16(h->start_offset+0x00,sf);
|
||||
extradata_size = 0x08 + read_u32(h->start_offset+0x04,sf); /* 0x14 for AT9 */
|
||||
subtype = read_u32(h->start_offset+0x00,sf);
|
||||
extradata_size = 0x08 + read_u32(h->start_offset+0x04,sf); /* 0x14 for AT9, 0x10 for PCM, 0x90 for MPEG */
|
||||
|
||||
switch(subtype) {
|
||||
case 0x00:
|
||||
|
@ -829,14 +844,40 @@ static bool process_data(STREAMFILE* sf, bnk_header_t* h) {
|
|||
h->interleave = 0x01;
|
||||
break;
|
||||
|
||||
case 0x02: /* ATRAC9 / MPEG mono */
|
||||
case 0x05: /* ATRAC9 / MPEG stereo */
|
||||
h->channels = (subtype == 0x02) ? 1 : 2;
|
||||
|
||||
case 0x02: /* ATRAC9 mono */
|
||||
case 0x05: /* ATRAC9 stereo */
|
||||
if (h->big_endian) {
|
||||
/* The Last of Us demo (PS3) */
|
||||
|
||||
/* 0x08: mpeg version? (1) */
|
||||
/* 0x0C: mpeg layer? (3) */
|
||||
/* 0x10: ? (related to frame size, 0xC0 > 0x40, 0x120 > 0x60) */
|
||||
/* 0x14: sample rate */
|
||||
/* 0x18: mpeg layer? (3) */
|
||||
/* 0x1c: mpeg version? (1) */
|
||||
/* 0x20: channels? */
|
||||
/* 0x24: frame size */
|
||||
/* 0x28: encoder delay */
|
||||
/* 0x2c: num samples */
|
||||
/* 0x30: ? */
|
||||
/* 0x34: ? */
|
||||
/* 0x38: 0? */
|
||||
/* 0x3c: data size */
|
||||
/* padding up to 0x90 */
|
||||
|
||||
h->encoder_delay = read_s32(h->start_offset+0x28,sf);
|
||||
h->num_samples = read_s32(h->start_offset+0x2c,sf);
|
||||
|
||||
h->codec = MPEG;
|
||||
}
|
||||
else {
|
||||
/* Puyo Puyo Tetris (PS4) */
|
||||
if (read_u32(h->start_offset+0x08,sf) + 0x08 != extradata_size) { /* repeat? */
|
||||
VGM_LOG("BNK: unknown subtype\n");
|
||||
goto fail;
|
||||
}
|
||||
h->channels = (subtype == 0x02) ? 1 : 2;
|
||||
|
||||
h->atrac9_info = read_u32be(h->start_offset+0x0c,sf);
|
||||
/* 0x10: null? */
|
||||
|
@ -845,6 +886,7 @@ static bool process_data(STREAMFILE* sf, bnk_header_t* h) {
|
|||
h->loop_end = h->loop_start + loop_length; /* loop_start is -1 if not set */
|
||||
|
||||
h->codec = ATRAC9;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
@ -282,11 +282,11 @@ static void load_cpk_name(STREAMFILE* sf, STREAMFILE* sf_acb, VGMSTREAM* vgmstre
|
|||
return;
|
||||
|
||||
/* companion .acb probably loaded */
|
||||
load_acb_wave_name(sf_acb, vgmstream, waveid, port, is_memory);
|
||||
load_acb_wave_info(sf_acb, vgmstream, waveid, port, is_memory, 0);
|
||||
|
||||
close_streamfile(sf_acb);
|
||||
}
|
||||
else {
|
||||
load_acb_wave_name(sf_acb, vgmstream, waveid, port, is_memory);
|
||||
load_acb_wave_info(sf_acb, vgmstream, waveid, port, is_memory, 0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -154,7 +154,7 @@ static VGMSTREAM* init_vgmstream_encrypted_rpgmvo_riff(STREAMFILE* sf) {
|
|||
e.key_size = 0x10;
|
||||
load_key(&cfg, e.keybuf, e.key_size);
|
||||
cfg.start = 0x10;
|
||||
cfg.max_offset = 0x10;
|
||||
cfg.max_offset = 0x20;
|
||||
|
||||
e.temp_sf = setup_ogg_vorbis_streamfile(sf, &cfg);
|
||||
if (!e.temp_sf) goto fail;
|
||||
|
|
|
@ -57,8 +57,6 @@ static const fsbkey_info fsbkey_list[] = {
|
|||
{ MODE_FSB4_STD, FSBKEY_ADD("ghfxhslrghfxhslr") }, // Cookie Run: Ovenbreak
|
||||
{ MODE_FSB4_ALT, FSBKEY_ADD("truck/impact/carbody") },// Monster Jam (PS2) [FSB3]
|
||||
{ MODE_FSB4_ALT, FSBKEY_ADD("\xFC\xF9\xE4\xB3\xF5\x57\x5C\xA5\xAC\x13\xEC\x4A\x43\x19\x58\xEB\x4E\xF3\x84\x0B\x8B\x78\xFA\xFD\xBB\x18\x46\x7E\x31\xFB\xD0") }, // Guitar Hero 5 (X360)
|
||||
{ MODE_FSB4_ALT, FSBKEY_ADD("\x8C\xFA\xF3\x14\xB1\x53\xDA\xAB\x2B\x82\x6B\xD5\x55\x16\xCF\x01\x90\x20\x28\x14\xB1\x53\xD8") }, // Guitar Hero: Metallica (X360)
|
||||
{ MODE_FSB4_STD, FSBKEY_ADD("\xd2\x37\x70\x39\xa9\x86\xc5\xaf\x5b\x7f\xa2\x23\x98\x7e\xb6\xc2\x7e\x18\x7b\x2d\xd9\x31\x4b\x20\xb0\xc1\x8d\x06\xf2\xa7\xcd") }, // Guitar Hero: Metallica (PS3) [FSB4]
|
||||
{ MODE_FSB5_STD, FSBKEY_ADD("G0KTrWjS9syqF7vVD6RaVXlFD91gMgkC") }, // Sekiro: Shadows Die Twice (PC)
|
||||
{ MODE_FSB5_STD, FSBKEY_ADD("BasicEncryptionKey") }, // SCP: Unity (PC)
|
||||
{ MODE_FSB5_STD, FSBKEY_ADD("FXnTffGJ9LS855Gc") }, // Worms Rumble Beta (PC)
|
||||
|
@ -73,6 +71,13 @@ static const fsbkey_info fsbkey_list[] = {
|
|||
{ MODE_FSB5_STD, FSBKEY_ADD("Aurogon666") }, // Afterimage demo (PC)
|
||||
{ MODE_FSB5_STD, FSBKEY_ADD("IfYouLikeThosesSoundsWhyNotRenumerateTheir2Authors?") }, // Blanc (PC/Switch)
|
||||
{ MODE_FSB5_STD, FSBKEY_ADD("L36nshM520") }, // Nishuihan Mobile (Android)
|
||||
{ MODE_FSB5_STD, FSBKEY_ADD("Forza2!") }, // Forza Motorsport (PC)
|
||||
{ MODE_FSB5_STD, FSBKEY_ADD("cbfjZTlUPaZI") }, // JDM: Japanese Drift Master (PC)
|
||||
|
||||
/* these games use a key per file, generated from the filename; could be possible to add them but there is a lot of songs,
|
||||
so external .fsbkey may be better (use guessfsb 3.1 with --write-key-file or ) */
|
||||
//{ MODE_FSB4_STD, FSBKEY_ADD("...") }, // Guitar Hero: Metallica (PC/PS3/X360) [FSB4]
|
||||
//{ MODE_FSB4_STD, FSBKEY_ADD("...") }, // Guitar Hero: World Tour (PC/PS3/X360) [FSB4]
|
||||
};
|
||||
static const int fsbkey_list_count = sizeof(fsbkey_list) / sizeof(fsbkey_list[0]);
|
||||
|
||||
|
|
|
@ -1254,6 +1254,24 @@ static const hcakey_info hcakey_list[] = {
|
|||
// BlazBlue Entropy Effect (Early Access) (PC)
|
||||
{29814655674508831}, // 0069EC457894661F
|
||||
|
||||
// Star Ocean: The Second Story R (PC, Switch)
|
||||
{533948357975462459}, // 0768F733DD87D23B
|
||||
|
||||
// Girls' Frontline 2: Exilium (Android)
|
||||
{8930254087621254}, // 001FBA04CEA58A86
|
||||
|
||||
// Sonic Superstars (Switch)
|
||||
{1991062320230623}, // 000712DC5252B0DF
|
||||
|
||||
// Persona 5 Tactica (Switch)
|
||||
{48319776512953016}, // 00ABAA94AAAE4AB8
|
||||
|
||||
// THE IDOLM@STER Shiny Colors Song For Prism (PC)
|
||||
{156967709847897761}, // 022DA94CEAB0C6A1
|
||||
|
||||
// Dokapon Kingdom Connect (PC, Switch)
|
||||
{104863924750642073}, // 01748d2f1883eb99
|
||||
|
||||
};
|
||||
|
||||
#endif/*_HCA_KEYS_H_*/
|
||||
|
|
|
@ -389,7 +389,7 @@ VGMSTREAM* init_vgmstream_bsnf(STREAMFILE* sf) {
|
|||
if (stream_size != get_streamfile_size(sb))
|
||||
goto fail;
|
||||
|
||||
loop_flag = (loop_start > 0);
|
||||
loop_flag = (loop_start > 0); /* loops from 0 on some codecs aren't detectable though */
|
||||
start_offset = 0x00;
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
|
@ -404,13 +404,18 @@ VGMSTREAM* init_vgmstream_bsnf(STREAMFILE* sf) {
|
|||
vgmstream->num_streams = num_languages;
|
||||
strncpy(vgmstream->stream_name, language, STREAM_NAME_SIZE);
|
||||
|
||||
/* for codecs with explicit encoder delay (mp3/at9/etc) num_samples includes it
|
||||
* ex. mus_c05_dream_doorloop_* does full loops; with some codecs loop start is encoder delay and num_samples
|
||||
* has extra delay samples compared to codecs with implicit delay (ex. mp3 1152 to 101152 vs ogg 0 to 100000),
|
||||
* but there is no header value for encoder delay, maybe engine hardcodes it? */
|
||||
|
||||
switch (codec) {
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
case 0x0055: {
|
||||
mpeg_custom_config cfg = { 0 };
|
||||
|
||||
cfg.skip_samples = 1152; /* seems ok */
|
||||
//cfg.skip_samples = 1152; /* observed default */
|
||||
|
||||
vgmstream->codec_data = init_mpeg_custom(sb, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
|
@ -451,14 +456,20 @@ VGMSTREAM* init_vgmstream_bsnf(STREAMFILE* sf) {
|
|||
case 0x42D2: {
|
||||
atrac9_config cfg = { 0 };
|
||||
|
||||
/* extra offset: RIFF fmt extra data (extra size, frame size, GUID, etc), no fact samples/delay */
|
||||
|
||||
cfg.channels = vgmstream->channels;
|
||||
cfg.encoder_delay = read_u16le(extra_offset + 0x02, sf);
|
||||
//cfg.encoder_delay = read_u16le(extra_offset + 0x02, sf) / 4; /* seemingly one subframe = 256 */
|
||||
cfg.config_data = read_u32be(extra_offset + 0x1c, sf);
|
||||
|
||||
vgmstream->codec_data = init_atrac9(&cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_ATRAC9;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->num_samples -= cfg.encoder_delay;
|
||||
vgmstream->loop_start_sample -= cfg.encoder_delay;
|
||||
vgmstream->loop_end_sample -= cfg.encoder_delay;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -14,8 +14,7 @@ VGMSTREAM* init_vgmstream_ktsc(STREAMFILE* sf) {
|
|||
/* checks */
|
||||
if (!is_id32be(0x00, sf, "KTSC"))
|
||||
goto fail;
|
||||
if (read_u32be(0x04, sf) != 0x01000001) /* version? */
|
||||
goto fail;
|
||||
/* 0x04: version? (0x01000001: common, 0x01000004: FE Three Houses) */
|
||||
|
||||
/* .ktsl2asbin: common [Atelier Ryza (PC)]
|
||||
* .asbin: Warriors Orochi 4 (PC) (assumed) */
|
||||
|
|
|
@ -1,18 +1,26 @@
|
|||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "../layout/layout.h"
|
||||
#include "../util/companion_files.h"
|
||||
#include "ktsr_streamfile.h"
|
||||
|
||||
typedef enum { NONE, MSADPCM, DSP, GCADPCM, ATRAC9, RIFF_ATRAC9, KOVS, KTSS, } ktsr_codec;
|
||||
|
||||
#define MAX_CHANNELS 8
|
||||
|
||||
typedef struct {
|
||||
uint32_t base_offset;
|
||||
bool is_srsa;
|
||||
int total_subsongs;
|
||||
int target_subsong;
|
||||
ktsr_codec codec;
|
||||
|
||||
uint32_t audio_id;
|
||||
int platform;
|
||||
int format;
|
||||
uint32_t sound_id;
|
||||
uint32_t sound_flags;
|
||||
uint32_t config_flags;
|
||||
|
||||
int channels;
|
||||
int sample_rate;
|
||||
|
@ -31,32 +39,73 @@ typedef struct {
|
|||
char name[255+1];
|
||||
} ktsr_header;
|
||||
|
||||
static VGMSTREAM* init_vgmstream_ktsr_internal(STREAMFILE* sf, bool is_srsa);
|
||||
static int parse_ktsr(ktsr_header* ktsr, STREAMFILE* sf);
|
||||
static layered_layout_data* build_layered_atrac9(ktsr_header* ktsr, STREAMFILE *sf, uint32_t config_data);
|
||||
static VGMSTREAM* init_vgmstream_ktsr_sub(STREAMFILE* sf_b, ktsr_header* ktsr, VGMSTREAM* (*init_vgmstream)(STREAMFILE* sf), const char* ext);
|
||||
|
||||
/* KTSR - Koei Tecmo sound resource container */
|
||||
VGMSTREAM* init_vgmstream_ktsr(STREAMFILE* sf) {
|
||||
|
||||
/* checks */
|
||||
if (!is_id32be(0x00, sf, "KTSR"))
|
||||
return NULL;
|
||||
/* others: see internal */
|
||||
|
||||
/* .ktsl2asbin: common [Atelier Ryza (PC/Switch), Nioh (PC)]
|
||||
* .asbin: Warriors Orochi 4 (PC) */
|
||||
if (!check_extensions(sf, "ktsl2asbin,asbin"))
|
||||
return NULL;
|
||||
|
||||
return init_vgmstream_ktsr_internal(sf, false) ;
|
||||
}
|
||||
|
||||
/* ASRS - container of KTSR found in newer games */
|
||||
VGMSTREAM* init_vgmstream_asrs(STREAMFILE* sf) {
|
||||
|
||||
/* checks */
|
||||
if (!is_id32be(0x00, sf, "ASRS"))
|
||||
return NULL;
|
||||
/* 0x04: null */
|
||||
/* 0x08: file size */
|
||||
/* 0x0c: null */
|
||||
|
||||
/* .srsa: header id (as generated by common tools, probably "(something)asbin") */
|
||||
if (!check_extensions(sf, "srsa"))
|
||||
return NULL;
|
||||
|
||||
/* mini-container of memory-KTSR (streams also have a "TSRS" for stream-KTSR).
|
||||
* .srsa/srst usually have hashed filenames, so it isn't easy to match them, so this
|
||||
* is mainly useful for .srsa with internal streams. */
|
||||
|
||||
return init_vgmstream_ktsr_internal(sf, true);
|
||||
}
|
||||
|
||||
|
||||
static VGMSTREAM* init_vgmstream_ktsr_internal(STREAMFILE* sf, bool is_srsa) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE* sf_b = NULL;
|
||||
ktsr_header ktsr = {0};
|
||||
int target_subsong = sf->stream_index;
|
||||
int separate_offsets = 0;
|
||||
|
||||
ktsr.is_srsa = is_srsa;
|
||||
if (ktsr.is_srsa) {
|
||||
ktsr.base_offset = 0x10;
|
||||
}
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!is_id32be(0x00, sf, "KTSR"))
|
||||
goto fail;
|
||||
if (read_u32be(0x04, sf) != 0x777B481A) /* hash(?) id: 0x777B481A=as, 0x0294DDFC=st, 0xC638E69E=gc */
|
||||
goto fail;
|
||||
/* .ktsl2asbin: common [Atelier Ryza (PC/Switch), Nioh (PC)]
|
||||
* .asbin: Warriors Orochi 4 (PC) */
|
||||
if (!check_extensions(sf, "ktsl2asbin,asbin"))
|
||||
goto fail;
|
||||
if (!is_id32be(ktsr.base_offset + 0x00, sf, "KTSR"))
|
||||
return NULL;
|
||||
if (read_u32be(ktsr.base_offset + 0x04, sf) != 0x777B481A) /* hash id: 0x777B481A=as, 0x0294DDFC=st, 0xC638E69E=gc */
|
||||
return NULL;
|
||||
|
||||
|
||||
/* KTSR can be a memory file (ktsl2asbin), streams (ktsl2stbin) and global config (ktsl2gcbin)
|
||||
* This accepts .ktsl2asbin with internal data or external streams as subsongs.
|
||||
* Some info from KTSR.bt */
|
||||
* Hashes are meant to be read LE, but here are BE for easier comparison (they probably correspond
|
||||
* to some text but are pre-hashed in exes). Some info from KTSR.bt */
|
||||
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
ktsr.target_subsong = target_subsong;
|
||||
|
@ -66,13 +115,24 @@ VGMSTREAM* init_vgmstream_ktsr(STREAMFILE* sf) {
|
|||
|
||||
/* open companion body */
|
||||
if (ktsr.is_external) {
|
||||
if (ktsr.is_srsa) {
|
||||
/* try parsing TXTM if present, since .srsa+srst have hashed names and don't match unless renamed */
|
||||
sf_b = read_filemap_file(sf, 0);
|
||||
}
|
||||
|
||||
if (!sf_b) {
|
||||
/* try (name).(ext), as seen in older games */
|
||||
const char* companion_ext = check_extensions(sf, "asbin") ? "stbin" : "ktsl2stbin";
|
||||
if (ktsr.is_srsa)
|
||||
companion_ext = "srst";
|
||||
|
||||
sf_b = open_streamfile_by_ext(sf, companion_ext);
|
||||
if (!sf_b) {
|
||||
vgm_logi("KTSR: companion file '*.%s' not found\n", companion_ext);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
sf_b = sf;
|
||||
}
|
||||
|
@ -172,15 +232,20 @@ fail:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
// TODO improve, unity with other metas that do similar stuff
|
||||
// TODO improve, unify with other metas that do similar stuff
|
||||
static VGMSTREAM* init_vgmstream_ktsr_sub(STREAMFILE* sf_b, ktsr_header* ktsr, VGMSTREAM* (*init_vgmstream)(STREAMFILE* sf), const char* ext) {
|
||||
VGMSTREAM* sub_vgmstream = NULL;
|
||||
STREAMFILE* temp_sf = setup_subfile_streamfile(sf_b, ktsr->stream_offsets[0], ktsr->stream_sizes[0], ext);
|
||||
STREAMFILE* temp_sf = NULL;
|
||||
|
||||
temp_sf = setup_ktsr_streamfile(sf_b, ktsr->is_external, ktsr->stream_offsets[0], ktsr->stream_sizes[0], ext);
|
||||
if (!temp_sf) return NULL;
|
||||
|
||||
sub_vgmstream = init_vgmstream(temp_sf);
|
||||
close_streamfile(temp_sf);
|
||||
if (!sub_vgmstream) return NULL;
|
||||
if (!sub_vgmstream) {
|
||||
VGM_LOG("ktsr: can't open subfile at %x (size %x)\n", ktsr->stream_offsets[0], ktsr->stream_sizes[0]);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sub_vgmstream->stream_size = ktsr->stream_sizes[0];
|
||||
sub_vgmstream->num_streams = ktsr->total_subsongs;
|
||||
|
@ -253,6 +318,7 @@ static int parse_codec(ktsr_header* ktsr) {
|
|||
/* platform + format to codec, simplified until more codec combos are found */
|
||||
switch(ktsr->platform) {
|
||||
case 0x01: /* PC */
|
||||
case 0x05: /* PC/Steam [Fate/Samurai Remnant (PC)] */
|
||||
if (ktsr->is_external) {
|
||||
if (ktsr->format == 0x0005)
|
||||
ktsr->codec = KOVS; // Atelier Ryza (PC)
|
||||
|
@ -284,11 +350,13 @@ static int parse_codec(ktsr_header* ktsr) {
|
|||
if (ktsr->is_external) {
|
||||
if (ktsr->format == 0x0005)
|
||||
ktsr->codec = KTSS; // [Ultra Kaiju Monster Rancher (Switch)]
|
||||
else if (ktsr->format == 0x1000)
|
||||
ktsr->codec = KTSS; // [Fire Emblem: Three Houses (Switch)-some DSP voices]
|
||||
else
|
||||
goto fail;
|
||||
}
|
||||
else if (ktsr->format == 0x0000)
|
||||
ktsr->codec = DSP; // Fire Emblem: Three Houses (Switch)
|
||||
ktsr->codec = DSP; // [Fire Emblem: Three Houses (Switch)]
|
||||
else
|
||||
goto fail;
|
||||
break;
|
||||
|
@ -318,9 +386,11 @@ static int parse_ktsr_subfile(ktsr_header* ktsr, STREAMFILE* sf, uint32_t offset
|
|||
case 0x3DEA478D: /* external [Nioh (PC)] (smaller) */
|
||||
case 0xDF92529F: /* external [Atelier Ryza (PC)] */
|
||||
case 0x6422007C: /* external [Atelier Ryza (PC)] */
|
||||
case 0x793A1FD7: /* external [Stranger of Paradise (PS4)]-encrypted */
|
||||
case 0xA0F4FC6C: /* external [Stranger of Paradise (PS4)]-encrypted */
|
||||
/* 08 subtype? (ex. 0x522B86B9)
|
||||
* 0c channels
|
||||
* 10 ? (always 0x002706B8)
|
||||
* 10 ? (always 0x002706B8 / 7864523D in SoP)
|
||||
* 14 external codec
|
||||
* 18 sample rate
|
||||
* 1c num samples
|
||||
|
@ -420,16 +490,53 @@ fail:
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* ktsr engine reads+decrypts in the same func based on passed flag tho (reversed from exe)
|
||||
* Strings are usually ASCII but Shift-JIS is used in rare cases (0x0c3e2edf.srsa) */
|
||||
static void decrypt_string_ktsr(char* buf, size_t buf_size, uint32_t seed) {
|
||||
for (int i = 0; i < buf_size - 1; i++) {
|
||||
if (!buf[i]) /* just in case */
|
||||
break;
|
||||
|
||||
seed = 0x343FD * seed + 0x269EC3; /* classic rand */
|
||||
buf[i] ^= (seed >> 16) & 0xFF; /* 3rd byte */
|
||||
if (!buf[i]) /* end null is also encrypted (but there are extra nulls after it anyway) */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* like read_string but allow any value since it can be encrypted */
|
||||
static size_t read_string_ktsr(char* buf, size_t buf_size, off_t offset, STREAMFILE* sf) {
|
||||
int pos;
|
||||
|
||||
for (pos = 0; pos < buf_size; pos++) {
|
||||
uint8_t byte = read_u8(offset + pos, sf);
|
||||
char c = (char)byte;
|
||||
if (buf) buf[pos] = c;
|
||||
if (c == '\0')
|
||||
return pos;
|
||||
if (pos+1 == buf_size) {
|
||||
if (buf) buf[pos] = '\0';
|
||||
return buf_size;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void build_name(ktsr_header* ktsr, STREAMFILE* sf) {
|
||||
char sound_name[255] = {0};
|
||||
char config_name[255] = {0};
|
||||
|
||||
/* names can be different or same but usually config is better */
|
||||
if (ktsr->sound_name_offset) {
|
||||
read_string(sound_name, sizeof(sound_name), ktsr->sound_name_offset, sf);
|
||||
read_string_ktsr(sound_name, sizeof(sound_name), ktsr->sound_name_offset, sf);
|
||||
if (ktsr->sound_flags & 0x0008)
|
||||
decrypt_string_ktsr(sound_name, sizeof(sound_name), ktsr->audio_id);
|
||||
}
|
||||
if (ktsr->config_name_offset) {
|
||||
read_string(config_name, sizeof(config_name), ktsr->config_name_offset, sf);
|
||||
read_string_ktsr(config_name, sizeof(config_name), ktsr->config_name_offset, sf);
|
||||
if (ktsr->config_flags & 0x0200)
|
||||
decrypt_string_ktsr(config_name, sizeof(config_name), ktsr->audio_id);
|
||||
}
|
||||
|
||||
//if (longname[0] && shortname[0]) {
|
||||
|
@ -445,22 +552,24 @@ static void build_name(ktsr_header* ktsr, STREAMFILE* sf) {
|
|||
|
||||
}
|
||||
|
||||
static void parse_longname(ktsr_header* ktsr, STREAMFILE* sf, uint32_t target_id) {
|
||||
static void parse_longname(ktsr_header* ktsr, STREAMFILE* sf) {
|
||||
/* more configs than sounds is possible so we need target_id first */
|
||||
uint32_t offset, end, name_offset;
|
||||
uint32_t stream_id;
|
||||
|
||||
offset = 0x40;
|
||||
end = get_streamfile_size(sf);
|
||||
offset = 0x40 + ktsr->base_offset;
|
||||
end = get_streamfile_size(sf) - ktsr->base_offset;
|
||||
while (offset < end) {
|
||||
uint32_t type = read_u32be(offset + 0x00, sf); /* hash-id? */
|
||||
uint32_t size = read_u32le(offset + 0x04, sf);
|
||||
switch(type) {
|
||||
case 0xBD888C36: /* config */
|
||||
stream_id = read_u32be(offset + 0x08, sf);
|
||||
if (stream_id != target_id)
|
||||
stream_id = read_u32le(offset + 0x08, sf);
|
||||
if (stream_id != ktsr->sound_id)
|
||||
break;
|
||||
|
||||
ktsr->config_flags = read_u32le(offset + 0x0c, sf);
|
||||
|
||||
name_offset = read_u32le(offset + 0x28, sf);
|
||||
if (name_offset > 0)
|
||||
ktsr->config_name_offset = offset + name_offset;
|
||||
|
@ -476,14 +585,14 @@ static void parse_longname(ktsr_header* ktsr, STREAMFILE* sf, uint32_t target_id
|
|||
|
||||
static int parse_ktsr(ktsr_header* ktsr, STREAMFILE* sf) {
|
||||
uint32_t offset, end, header_offset, name_offset;
|
||||
uint32_t stream_id = 0, stream_count;
|
||||
uint32_t stream_count;
|
||||
|
||||
/* 00: KTSR
|
||||
* 04: type
|
||||
* 08: version?
|
||||
* 0a: unknown (usually 00, 02/03 seen in Vita)
|
||||
* 0b: platform (01=PC, 03=Vita, 04=Switch)
|
||||
* 0c: game id?
|
||||
* 0c: audio id? (seen in multiple files/games and used as Ogg stream IDs)
|
||||
* 10: null
|
||||
* 14: null
|
||||
* 18: file size
|
||||
|
@ -491,25 +600,28 @@ static int parse_ktsr(ktsr_header* ktsr, STREAMFILE* sf) {
|
|||
* up to 40: reserved
|
||||
* until end: entries (totals not defined) */
|
||||
|
||||
ktsr->platform = read_u8(0x0b,sf);
|
||||
ktsr->platform = read_u8(ktsr->base_offset + 0x0b,sf);
|
||||
ktsr->audio_id = read_u32le(ktsr->base_offset + 0x0c,sf);
|
||||
|
||||
if (read_u32le(0x18, sf) != read_u32le(0x1c, sf))
|
||||
if (read_u32le(ktsr->base_offset + 0x18, sf) != read_u32le(ktsr->base_offset + 0x1c, sf))
|
||||
goto fail;
|
||||
if (read_u32le(0x1c, sf) != get_streamfile_size(sf))
|
||||
if (read_u32le(ktsr->base_offset + 0x1c, sf) != get_streamfile_size(sf) - ktsr->base_offset) {
|
||||
vgm_logi("KTSR: incorrect file size (bad rip?)\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
offset = 0x40;
|
||||
end = get_streamfile_size(sf);
|
||||
offset = 0x40 + ktsr->base_offset;
|
||||
end = get_streamfile_size(sf) - ktsr->base_offset;
|
||||
while (offset < end) {
|
||||
uint32_t type = read_u32be(offset + 0x00, sf); /* hash-id? */
|
||||
uint32_t size = read_u32le(offset + 0x04, sf);
|
||||
|
||||
/* parse chunk-like subfiles, usually N configs then N songs */
|
||||
switch(type) {
|
||||
case 0x6172DBA8: /* padding (empty) */
|
||||
case 0xBD888C36: /* config (floats, stream id, etc, may have extended name) */
|
||||
case 0x6172DBA8: /* ? (mostly empty) */
|
||||
case 0xBD888C36: /* cue? (floats, stream id, etc, may have extended name; can have sub-chunks)-appears N times */
|
||||
case 0xC9C48EC1: /* unknown (has some string inside like "boss") */
|
||||
case 0xA9D23BF1: /* "state container", some kind of config/floats, witgh names like 'State_bgm01'..N */
|
||||
case 0xA9D23BF1: /* "state container", some kind of config/floats, with names like 'State_bgm01'..N */
|
||||
case 0x836FBECA: /* unknown (~0x300, encrypted? table + data) */
|
||||
break;
|
||||
|
||||
|
@ -517,9 +629,10 @@ static int parse_ktsr(ktsr_header* ktsr, STREAMFILE* sf) {
|
|||
ktsr->total_subsongs++;
|
||||
|
||||
/* sound table:
|
||||
* 08: stream id (used in several places)
|
||||
* 0c: unknown (low number but not version?)
|
||||
* 0e: external flag
|
||||
* 08: current/stream id (used in several places)
|
||||
* 0c: flags (sounds only; other types are similar but different bits)
|
||||
* 0x08: encrypted names (only used after ASRS was introduced?)
|
||||
* 0x10000: external flag
|
||||
* 10: sub-streams?
|
||||
* 14: offset to header offset
|
||||
* 18: offset to name
|
||||
|
@ -528,11 +641,10 @@ static int parse_ktsr(ktsr_header* ktsr, STREAMFILE* sf) {
|
|||
* --: header
|
||||
* --: subheader (varies) */
|
||||
|
||||
|
||||
if (ktsr->total_subsongs == ktsr->target_subsong) {
|
||||
|
||||
stream_id = read_u32be(offset + 0x08,sf);
|
||||
//ktsr->is_external = read_u16le(offset + 0x0e,sf);
|
||||
ktsr->sound_id = read_u32le(offset + 0x08,sf);
|
||||
ktsr->sound_flags = read_u32le(offset + 0x0c,sf);
|
||||
stream_count = read_u32le(offset + 0x10,sf);
|
||||
if (stream_count != 1) {
|
||||
VGM_LOG("ktsr: unknown stream count\n");
|
||||
|
@ -563,9 +675,18 @@ static int parse_ktsr(ktsr_header* ktsr, STREAMFILE* sf) {
|
|||
if (ktsr->target_subsong > ktsr->total_subsongs)
|
||||
goto fail;
|
||||
|
||||
parse_longname(ktsr, sf, stream_id);
|
||||
parse_longname(ktsr, sf);
|
||||
build_name(ktsr, sf);
|
||||
|
||||
/* skip TSRS header (internals are pre-adjusted) */
|
||||
if (ktsr->is_external && ktsr->base_offset) {
|
||||
for (int i = 0; i < ktsr->channels; i++) {
|
||||
ktsr->stream_offsets[i] += ktsr->base_offset;
|
||||
}
|
||||
|
||||
ktsr->extra_offset += ktsr->base_offset; /* ? */
|
||||
}
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
vgm_logi("KTSR: unknown variation (report)\n");
|
||||
|
|
152
Frameworks/vgmstream/vgmstream/src/meta/ktsr_streamfile.h
Normal file
152
Frameworks/vgmstream/vgmstream/src/meta/ktsr_streamfile.h
Normal file
|
@ -0,0 +1,152 @@
|
|||
#ifndef _KTSR_STREAMFILE_H_
|
||||
#define _KTSR_STREAMFILE_H_
|
||||
|
||||
#include "../streamfile.h"
|
||||
#include "../util/log.h"
|
||||
#include "../util/cipher_blowfish.h"
|
||||
|
||||
/* decrypts blowfish in realtime (as done by games) */
|
||||
typedef struct {
|
||||
uint8_t key[0x20];
|
||||
uint8_t block[0x08];
|
||||
blowfish_ctx* ctx;
|
||||
} ktsr_io_data;
|
||||
|
||||
static int ktsr_io_init(STREAMFILE* sf, ktsr_io_data* data) {
|
||||
/* ktsr keys start with size then random bytes (usually 7), assumed max 0x20 */
|
||||
if (data->key[0] >= sizeof(data->key) - 1)
|
||||
return -1;
|
||||
|
||||
data->ctx = blowfish_init_ecb(data->key + 1, data->key[0]);
|
||||
if (!data->ctx)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ktsr_io_close(STREAMFILE* sf, ktsr_io_data* data) {
|
||||
blowfish_free(data->ctx);
|
||||
}
|
||||
|
||||
|
||||
static int read_part(STREAMFILE* sf, uint8_t* dest, off_t offset, size_t length, ktsr_io_data* data) {
|
||||
|
||||
off_t offset_rem = offset % 0x08;
|
||||
offset -= offset_rem;
|
||||
|
||||
if (offset_rem == 0 && length >= 0x08) /* handled in main */
|
||||
return 0;
|
||||
|
||||
/* read one full block, regardless of requested length */
|
||||
int bytes = read_streamfile(data->block, offset, 0x08, sf);
|
||||
|
||||
/* useless since KTSR data is padded and blocks don't work otherwise but for determinability */
|
||||
if (bytes < 0x08)
|
||||
memset(data->block + bytes, 0, 0x08 - bytes);
|
||||
|
||||
blowfish_decrypt_ecb(data->ctx, data->block);
|
||||
|
||||
int max_copy = bytes - offset_rem;
|
||||
if (max_copy > length)
|
||||
max_copy = length;
|
||||
|
||||
memcpy(dest, data->block + offset_rem, max_copy);
|
||||
|
||||
return max_copy;
|
||||
}
|
||||
|
||||
static int read_main(STREAMFILE* sf, uint8_t* dest, off_t offset, size_t length, ktsr_io_data* data) {
|
||||
int read = 0;
|
||||
|
||||
off_t offset_rem = offset % 0x08;
|
||||
size_t length_rem = length % 0x08;
|
||||
length -= length_rem;
|
||||
|
||||
if (offset_rem != 0 || length == 0) /* handled in part */
|
||||
return 0;
|
||||
|
||||
int bytes = read_streamfile(dest, offset, length, sf);
|
||||
|
||||
while (read < bytes) {
|
||||
blowfish_decrypt_ecb(data->ctx, dest);
|
||||
dest += 0x08;
|
||||
read += 0x08;
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/* blowfish is a 64-bit block cipher, so arbitrary reads will need to handle partial cases. ex
|
||||
* - reading 0x00 to 0x20: direct decrypt (4 blocks of 0x08)
|
||||
* - reading 0x03 to 0x07: decrypt 0x00 to 0x08 but copy 4 bytes at 0x03
|
||||
* - reading 0x03 to 0x22: handle as 0x00 to 0x08 (head, copy 5 at 0x3), 0x08 to 0x20 (body, direct), and 0x20 to 0x28 (tail, copy 2 at 0x0). */
|
||||
static size_t ktsr_io_read(STREAMFILE* sf, uint8_t* dest, off_t offset, size_t length, ktsr_io_data* data) {
|
||||
int bytes = 0;
|
||||
|
||||
|
||||
/* head */
|
||||
if (length) {
|
||||
int done = read_part(sf, dest, offset, length, data);
|
||||
dest += done;
|
||||
offset += done;
|
||||
length -= done;
|
||||
|
||||
bytes += done;
|
||||
}
|
||||
|
||||
/* body */
|
||||
if (length) {
|
||||
int done = read_main(sf, dest, offset, length, data);
|
||||
dest += done;
|
||||
offset += done;
|
||||
length -= done;
|
||||
|
||||
bytes += done;
|
||||
}
|
||||
|
||||
/* tail */
|
||||
if (length) {
|
||||
int done = read_part(sf, dest, offset, length, data);
|
||||
dest += done;
|
||||
offset += done;
|
||||
length -= done;
|
||||
|
||||
bytes += done;
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Decrypts blowfish KTSR streams */
|
||||
static STREAMFILE* setup_ktsr_streamfile(STREAMFILE* sf, bool is_external, uint32_t subfile_offset, uint32_t subfile_size, const char* extension) {
|
||||
STREAMFILE* new_sf = NULL;
|
||||
ktsr_io_data io_data = {0};
|
||||
|
||||
if (is_external) {
|
||||
uint32_t offset = 0x00;
|
||||
if (is_id32be(0x00, sf, "TSRS"))
|
||||
offset += 0x10;
|
||||
if (!is_id32be(offset + 0x00, sf, "KTSR"))
|
||||
goto fail;
|
||||
|
||||
read_streamfile(io_data.key, offset + 0x20, sizeof(io_data.key), sf);
|
||||
}
|
||||
|
||||
/* setup subfile */
|
||||
new_sf = open_wrap_streamfile(sf);
|
||||
|
||||
/* no apparent flag other than key at offset. Only data at subfile is encrypted, so this reader assumes it will be clamped */
|
||||
if (io_data.key[0] != 0)
|
||||
new_sf = open_io_streamfile_ex_f(new_sf, &io_data, sizeof(ktsr_io_data), ktsr_io_read, NULL, ktsr_io_init, ktsr_io_close);
|
||||
|
||||
new_sf = open_clamp_streamfile_f(new_sf, subfile_offset, subfile_size);
|
||||
if (extension)
|
||||
new_sf = open_fakename_streamfile_f(new_sf, NULL, extension);
|
||||
|
||||
return new_sf;
|
||||
fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -618,7 +618,6 @@ VGMSTREAM* init_vgmstream_opus_nop(STREAMFILE* sf);
|
|||
VGMSTREAM* init_vgmstream_opus_shinen(STREAMFILE* sf);
|
||||
VGMSTREAM* init_vgmstream_opus_nus3(STREAMFILE* sf);
|
||||
VGMSTREAM* init_vgmstream_opus_sps_n1(STREAMFILE* sf);
|
||||
VGMSTREAM* init_vgmstream_opus_nxa(STREAMFILE* sf);
|
||||
VGMSTREAM* init_vgmstream_opus_opusx(STREAMFILE* sf);
|
||||
VGMSTREAM* init_vgmstream_opus_prototype(STREAMFILE* sf);
|
||||
VGMSTREAM* init_vgmstream_opus_opusnx(STREAMFILE* sf);
|
||||
|
@ -758,6 +757,8 @@ VGMSTREAM * init_vgmstream_derf(STREAMFILE *streamFile);
|
|||
|
||||
VGMSTREAM * init_vgmstream_utk(STREAMFILE *streamFile);
|
||||
|
||||
VGMSTREAM* init_vgmstream_nxa1(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM * init_vgmstream_adpcm_capcom(STREAMFILE *streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ue4opus(STREAMFILE *streamFile);
|
||||
|
@ -818,8 +819,8 @@ VGMSTREAM * init_vgmstream_bwav(STREAMFILE * streamFile);
|
|||
VGMSTREAM * init_vgmstream_awb(STREAMFILE * streamFile);
|
||||
VGMSTREAM * init_vgmstream_awb_memory(STREAMFILE * streamFile, STREAMFILE *acbFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_acb(STREAMFILE * streamFile);
|
||||
void load_acb_wave_name(STREAMFILE *acbFile, VGMSTREAM* vgmstream, int waveid, int port, int is_memory);
|
||||
VGMSTREAM* init_vgmstream_acb(STREAMFILE* sf);
|
||||
void load_acb_wave_info(STREAMFILE *acbFile, VGMSTREAM* vgmstream, int waveid, int port, int is_memory, int load_loops);
|
||||
|
||||
VGMSTREAM * init_vgmstream_rad(STREAMFILE * streamFile);
|
||||
|
||||
|
@ -845,6 +846,7 @@ VGMSTREAM * init_vgmstream_nub_xma(STREAMFILE *streamFile);
|
|||
VGMSTREAM * init_vgmstream_nub_dsp(STREAMFILE * streamFile);
|
||||
VGMSTREAM * init_vgmstream_nub_idsp(STREAMFILE * streamFile);
|
||||
VGMSTREAM * init_vgmstream_nub_is14(STREAMFILE * streamFile);
|
||||
VGMSTREAM* init_vgmstream_nub_caf(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM* init_vgmstream_xwv_valve(STREAMFILE* sf);
|
||||
|
||||
|
@ -870,6 +872,7 @@ VGMSTREAM * init_vgmstream_tgc(STREAMFILE *streamFile);
|
|||
|
||||
VGMSTREAM* init_vgmstream_kwb(STREAMFILE* sf);
|
||||
VGMSTREAM* init_vgmstream_xws(STREAMFILE* sf);
|
||||
VGMSTREAM* init_vgmstream_snd_koei(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM * init_vgmstream_lrmd(STREAMFILE* sf);
|
||||
|
||||
|
@ -883,6 +886,7 @@ VGMSTREAM* init_vgmstream_diva(STREAMFILE* sf);
|
|||
VGMSTREAM* init_vgmstream_imuse(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM* init_vgmstream_ktsr(STREAMFILE* sf);
|
||||
VGMSTREAM* init_vgmstream_asrs(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM* init_vgmstream_mups(STREAMFILE* sf);
|
||||
|
||||
|
@ -982,4 +986,6 @@ VGMSTREAM* init_vgmstream_squeaksample(STREAMFILE* sf);
|
|||
|
||||
VGMSTREAM* init_vgmstream_snds(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM* init_vgmstream_nxof(STREAMFILE* sf);
|
||||
|
||||
#endif /*_META_H*/
|
||||
|
|
|
@ -30,7 +30,7 @@ VGMSTREAM* init_vgmstream_nub(STREAMFILE* sf) {
|
|||
goto fail;
|
||||
|
||||
/* .nub: standard
|
||||
* .nub2: rare [iDOLM@STER - Gravure For You (PS3)] */
|
||||
* .nub2: rare [iDOLM@STER: Gravure For You (PS3), Noby Noby Boy (iOS)] */
|
||||
if (!check_extensions(sf, "nub,nub2"))
|
||||
goto fail;
|
||||
|
||||
|
@ -97,47 +97,52 @@ VGMSTREAM* init_vgmstream_nub(STREAMFILE* sf) {
|
|||
header_size = align_size_to_block(subheader_start + subheader_size, 0x10);
|
||||
|
||||
switch(codec) {
|
||||
case 0x00: /* (none) (xma1) */
|
||||
case 0x00: /* xma1 */
|
||||
fake_ext = "xma";
|
||||
init_vgmstream_function = init_vgmstream_nub_xma;
|
||||
break;
|
||||
|
||||
case 0x01: /* "wav\0" */
|
||||
case 0x01:
|
||||
fake_ext = "wav";
|
||||
init_vgmstream_function = init_vgmstream_nub_wav;
|
||||
break;
|
||||
|
||||
case 0x02: /* "vag\0" */
|
||||
case 0x02:
|
||||
fake_ext = "vag";
|
||||
init_vgmstream_function = init_vgmstream_nub_vag;
|
||||
break;
|
||||
|
||||
case 0x03: /* "at3\0" */
|
||||
case 0x03:
|
||||
fake_ext = "at3";
|
||||
init_vgmstream_function = init_vgmstream_nub_at3;
|
||||
break;
|
||||
|
||||
case 0x04: /* "xma\0" (xma2 old) */
|
||||
case 0x08: /* "xma\0" (xma2 new) */
|
||||
case 0x04: /* xma2 old */
|
||||
case 0x08: /* xma2 new */
|
||||
fake_ext = "xma";
|
||||
init_vgmstream_function = init_vgmstream_nub_xma;
|
||||
break;
|
||||
|
||||
case 0x05: /* "dsp\0" */
|
||||
case 0x05:
|
||||
fake_ext = "dsp";
|
||||
init_vgmstream_function = init_vgmstream_nub_dsp;
|
||||
break;
|
||||
|
||||
case 0x06: /* "idsp" */
|
||||
case 0x06:
|
||||
fake_ext = "idsp";
|
||||
init_vgmstream_function = init_vgmstream_nub_idsp;
|
||||
break;
|
||||
|
||||
case 0x07: /* "is14" */
|
||||
case 0x07:
|
||||
fake_ext = "is14";
|
||||
init_vgmstream_function = init_vgmstream_nub_is14;
|
||||
break;
|
||||
|
||||
case 0x09:
|
||||
fake_ext = "caf";
|
||||
init_vgmstream_function = init_vgmstream_nub_caf;
|
||||
break;
|
||||
|
||||
default:
|
||||
VGM_LOG("NUB: unknown codec %x\n", codec);
|
||||
goto fail;
|
||||
|
@ -235,9 +240,10 @@ static STREAMFILE* setup_nub_streamfile(STREAMFILE* sf, off_t header_offset, siz
|
|||
|
||||
//todo could be simplified
|
||||
|
||||
/* .nub wav - from Namco NUB archives [Ridge Racer 7 (PS3)] */
|
||||
/* .nub wav - from Namco NUB archives [Ridge Racer 7 (PS3), Noby Noby Boy (iOS)] */
|
||||
VGMSTREAM* init_vgmstream_nub_wav(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE* temp_sf = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag, channel_count, sample_rate;
|
||||
size_t data_size, loop_start, loop_length;
|
||||
|
@ -246,10 +252,10 @@ VGMSTREAM* init_vgmstream_nub_wav(STREAMFILE* sf) {
|
|||
|
||||
|
||||
/* checks */
|
||||
if (!is_id32be(0x00,sf, "wav\0"))
|
||||
return NULL;
|
||||
if (!check_extensions(sf, "wav,lwav"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00,sf) != 0x77617600) /* "wav\0" "*/
|
||||
goto fail;
|
||||
return NULL;
|
||||
|
||||
if (guess_endian32(0x1c, sf)) {
|
||||
read_32bit = read_32bitBE;
|
||||
|
@ -276,6 +282,21 @@ VGMSTREAM* init_vgmstream_nub_wav(STREAMFILE* sf) {
|
|||
|
||||
start_offset = 0xD0;
|
||||
|
||||
/* seen in Noby Noby Boy (iOS), not sure about flags */
|
||||
if (is_id32be(start_offset, sf, "RIFF")) {
|
||||
uint32_t subfile_offset = start_offset;
|
||||
uint32_t subfile_size = read_32bitLE(subfile_offset + 0x04, sf) + 0x08; /* RIFF size */
|
||||
|
||||
temp_sf = setup_subfile_streamfile(sf, subfile_offset,subfile_size, NULL);
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
vgmstream = init_vgmstream_riff(temp_sf);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
close_streamfile(temp_sf);
|
||||
return vgmstream;
|
||||
}
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
|
@ -296,6 +317,7 @@ VGMSTREAM* init_vgmstream_nub_wav(STREAMFILE* sf) {
|
|||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_sf);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -310,10 +332,10 @@ VGMSTREAM* init_vgmstream_nub_vag(STREAMFILE* sf) {
|
|||
|
||||
|
||||
/* checks */
|
||||
if (!is_id32be(0x00,sf, "vag\0"))
|
||||
return NULL;
|
||||
if ( !check_extensions(sf, "vag"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00,sf) != 0x76616700) /* "vag\0" */
|
||||
goto fail;
|
||||
return NULL;
|
||||
|
||||
if (guess_endian32(0x1c, sf)) {
|
||||
read_32bit = read_32bitBE;
|
||||
|
@ -362,15 +384,13 @@ fail:
|
|||
VGMSTREAM* init_vgmstream_nub_at3(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE* temp_sf = NULL;
|
||||
off_t subfile_offset = 0;
|
||||
size_t subfile_size = 0;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!is_id32be(0x00,sf, "at3\0"))
|
||||
return NULL;
|
||||
if (!check_extensions(sf,"at3"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00,sf) != 0x61743300) /* "at3\0" */
|
||||
goto fail;
|
||||
return NULL;
|
||||
|
||||
/* info header */
|
||||
/* 0x20: loop start (in samples) */
|
||||
|
@ -381,8 +401,8 @@ VGMSTREAM* init_vgmstream_nub_at3(STREAMFILE* sf) {
|
|||
/* format header: mini fmt (WAVEFORMATEX) + fact chunks LE (clone of RIFF's) */
|
||||
/* we can just ignore and use RIFF at data start since it has the same info */
|
||||
|
||||
subfile_offset = 0x100;
|
||||
subfile_size = read_32bitLE(subfile_offset + 0x04, sf) + 0x08; /* RIFF size */
|
||||
uint32_t subfile_offset = 0x100;
|
||||
uint32_t subfile_size = read_32bitLE(subfile_offset + 0x04, sf) + 0x08; /* RIFF size */
|
||||
|
||||
temp_sf = setup_subfile_streamfile(sf, subfile_offset,subfile_size, NULL);
|
||||
if (!temp_sf) goto fail;
|
||||
|
@ -410,9 +430,9 @@ VGMSTREAM* init_vgmstream_nub_xma(STREAMFILE* sf) {
|
|||
|
||||
/* checks */
|
||||
if (!check_extensions(sf,"xma"))
|
||||
goto fail;
|
||||
return NULL;
|
||||
|
||||
if (read_32bitBE(0x00,sf) == 0x786D6100) { /* "xma\0" */
|
||||
if (is_id32be(0x00,sf, "xma\0")) {
|
||||
/* nub v2.1 */
|
||||
nus_codec = read_32bitBE(0x0C,sf);
|
||||
data_size = read_32bitBE(0x14,sf);
|
||||
|
@ -435,7 +455,7 @@ VGMSTREAM* init_vgmstream_nub_xma(STREAMFILE* sf) {
|
|||
chunk_size = header_size;
|
||||
}
|
||||
else {
|
||||
goto fail;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
start_offset = align_size_to_block(chunk_offset + header_size, 0x10);
|
||||
|
@ -520,10 +540,10 @@ VGMSTREAM* init_vgmstream_nub_dsp(STREAMFILE* sf) {
|
|||
|
||||
|
||||
/* checks */
|
||||
if (!is_id32be(0x00,sf, "dsp\0"))
|
||||
return NULL;
|
||||
if (!check_extensions(sf,"dsp"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00,sf) != 0x64737000) /* "dsp\0" */
|
||||
goto fail;
|
||||
return NULL;
|
||||
|
||||
/* paste header+data together and pass to meta, which has loop info too */
|
||||
header_offset = 0xBC;
|
||||
|
@ -554,10 +574,10 @@ VGMSTREAM* init_vgmstream_nub_idsp(STREAMFILE* sf) {
|
|||
|
||||
|
||||
/* checks */
|
||||
if (!is_id32be(0x00,sf, "idsp"))
|
||||
return NULL;
|
||||
if (!check_extensions(sf,"idsp"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00,sf) != 0x69647370) /* "idsp" */
|
||||
goto fail;
|
||||
return NULL;
|
||||
|
||||
/* info header */
|
||||
/* 0x20: loop start (in samples) */
|
||||
|
@ -595,10 +615,10 @@ VGMSTREAM* init_vgmstream_nub_is14(STREAMFILE* sf) {
|
|||
|
||||
|
||||
/* checks */
|
||||
if (!is_id32be(0x00,sf, "is14"))
|
||||
return NULL;
|
||||
if (!check_extensions(sf,"is14"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00,sf) != 0x69733134) /* "is14" */
|
||||
goto fail;
|
||||
return NULL;
|
||||
|
||||
if (guess_endian32(0x1c, sf)) {
|
||||
read_32bit = read_32bitBE;
|
||||
|
@ -632,3 +652,41 @@ fail:
|
|||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* .nub is14 - from Namco NUB archives [Noby Noby Boy (iOS)] */
|
||||
VGMSTREAM* init_vgmstream_nub_caf(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE* temp_sf = NULL;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!is_id32be(0x00,sf, "caf\0"))
|
||||
return NULL;
|
||||
if (!check_extensions(sf,"caf"))
|
||||
return NULL;
|
||||
|
||||
/* info header */
|
||||
/* 0x20: loop start (in samples) */
|
||||
/* 0x24: loop length (in samples) */
|
||||
/* 0x28: loop flag */
|
||||
/* 0x2c: null */
|
||||
|
||||
/* format header: simplified caff with some chunks */
|
||||
/* we can just ignore and use caff at data start since it has the same info */
|
||||
|
||||
uint32_t subfile_offset = 0x110;
|
||||
uint32_t subfile_size = read_u32le(0x14, sf); /* padded but not fully validated */
|
||||
|
||||
temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, NULL);
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
vgmstream = init_vgmstream_apple_caff(temp_sf);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
close_streamfile(temp_sf);
|
||||
return vgmstream;
|
||||
fail:
|
||||
close_streamfile(temp_sf);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
/* Entergram NXA Opus [Higurashi no Naku Koro ni Hou (Switch), Gensou Rougoku no Kaleidoscope (Switch)] */
|
||||
VGMSTREAM* init_vgmstream_opus_nxa(STREAMFILE* sf) {
|
||||
/* Entergram NXA1 Opus [Higurashi no Naku Koro ni Hou (Switch), Gensou Rougoku no Kaleidoscope (Switch)] */
|
||||
VGMSTREAM* init_vgmstream_nxa1(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag, channels, type, sample_rate;
|
||||
|
@ -11,10 +11,10 @@ VGMSTREAM* init_vgmstream_opus_nxa(STREAMFILE* sf) {
|
|||
size_t data_size, frame_size;
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf, "nxa"))
|
||||
goto fail;
|
||||
if (!is_id32be(0x00, sf, "NXA1"))
|
||||
goto fail;
|
||||
if (!check_extensions(sf, "nxa"))
|
||||
goto fail;
|
||||
|
||||
start_offset = 0x30;
|
||||
type = read_u32le(0x04, sf);
|
||||
|
@ -36,7 +36,7 @@ VGMSTREAM* init_vgmstream_opus_nxa(STREAMFILE* sf) {
|
|||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_NXA;
|
||||
vgmstream->meta_type = meta_NXA1;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = num_samples;
|
||||
vgmstream->loop_start_sample = loop_start;
|
53
Frameworks/vgmstream/vgmstream/src/meta/nxof.c
Normal file
53
Frameworks/vgmstream/vgmstream/src/meta/nxof.c
Normal file
|
@ -0,0 +1,53 @@
|
|||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* Nihon Falcom FDK NXOpus [Ys X -NORDICS- (Switch)] */
|
||||
VGMSTREAM* init_vgmstream_nxof(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag, channels, sample_rate;
|
||||
size_t data_size, skip = 0;
|
||||
int32_t num_samples, loop_start, loop_end;
|
||||
|
||||
/* checks */
|
||||
if (!is_id32le(0x00, sf, "nxof"))
|
||||
goto fail;
|
||||
if (!check_extensions(sf,"nxopus"))
|
||||
goto fail;
|
||||
|
||||
channels = read_u8(0x05, sf);
|
||||
sample_rate = read_u32le(0x08, sf);
|
||||
start_offset = read_u32le(0x18, sf);
|
||||
data_size = read_u32le(0x1C, sf);
|
||||
num_samples = read_u32le(0x20, sf);
|
||||
loop_start = read_u32le(0x30, sf);
|
||||
loop_end = read_u32le(0x34, sf);
|
||||
|
||||
loop_flag = (loop_end > 0);
|
||||
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_NXOF;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = num_samples;
|
||||
vgmstream->loop_start_sample = loop_start;
|
||||
vgmstream->loop_end_sample = loop_end;
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
skip = switch_opus_get_encoder_delay(start_offset, sf);
|
||||
vgmstream->codec_data = init_ffmpeg_switch_opus(sf, start_offset, data_size, vgmstream->channels, skip, vgmstream->sample_rate);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
#else
|
||||
goto fail;
|
||||
#endif
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
#include "../layout/layout.h"
|
||||
|
||||
|
||||
#define PSB_MAX_LAYERS 2
|
||||
#define PSB_MAX_LAYERS 6 /* MGS Master Collection Vo.1 (Switch) */
|
||||
|
||||
typedef enum { PCM, RIFF_AT3, XMA2, MSADPCM, XWMA, DSP, OPUSNX, RIFF_AT9, VAG } psb_codec_t;
|
||||
typedef struct {
|
||||
|
|
|
@ -169,6 +169,10 @@ static int read_fmt(int big_endian, STREAMFILE* sf, off_t offset, riff_fmt_chunk
|
|||
|
||||
case 0x0002: /* MSADPCM */
|
||||
if (fmt->bps == 4) {
|
||||
/* ADPCMWAVEFORMAT extra data:
|
||||
* - samples per frame (16b)
|
||||
* - num coefs (16b), always 7
|
||||
* - N x2 coefs (configurable but in practice fixed) */
|
||||
fmt->coding_type = coding_MSADPCM;
|
||||
if (!msadpcm_check_coefs(sf, fmt->offset + 0x08 + 0x14))
|
||||
goto fail;
|
||||
|
@ -180,7 +184,7 @@ static int read_fmt(int big_endian, STREAMFILE* sf, off_t offset, riff_fmt_chunk
|
|||
goto fail;
|
||||
}
|
||||
break;
|
||||
case 0x003: /* floating point PCM */
|
||||
case 0x0003: /* floating point PCM */
|
||||
if (fmt->bps == 32) {
|
||||
fmt->coding_type = coding_PCMFLOAT;
|
||||
} else {
|
||||
|
@ -190,6 +194,8 @@ static int read_fmt(int big_endian, STREAMFILE* sf, off_t offset, riff_fmt_chunk
|
|||
break;
|
||||
|
||||
case 0x0011: /* MS-IMA ADPCM [Layton Brothers: Mystery Room (iOS/Android)] */
|
||||
/* IMAADPCMWAVEFORMAT extra data:
|
||||
* - samples per frame (16b) */
|
||||
if (fmt->bps != 4) goto fail;
|
||||
fmt->coding_type = coding_MS_IMA;
|
||||
break;
|
||||
|
@ -391,8 +397,9 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) {
|
|||
* .xms: Ty the Tasmanian Tiger (Xbox)
|
||||
* .mus: Burnout Legends/Dominator (PSP)
|
||||
* .dat/ldat: RollerCoaster Tycoon 1/2 (PC)
|
||||
* .wma/lwma: SRS: Street Racing Syndicate (Xbox), Fast and the Furious (Xbox)
|
||||
*/
|
||||
if (!check_extensions(sf, "wav,lwav,xwav,mwv,da,dax,cd,med,snd,adx,adp,xss,xsew,adpcm,adw,wd,,sbv,wvx,str,at3,rws,aud,at9,ckd,saf,ima,nsa,pcm,xvag,ogg,logg,p1d,xms,mus,dat,ldat")) {
|
||||
if (!check_extensions(sf, "wav,lwav,xwav,mwv,da,dax,cd,med,snd,adx,adp,xss,xsew,adpcm,adw,wd,,sbv,wvx,str,at3,rws,aud,at9,ckd,saf,ima,nsa,pcm,xvag,ogg,logg,p1d,xms,mus,dat,ldat,wma,lwma")) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
@ -409,6 +416,9 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) {
|
|||
else if (codec == 0x0069 && riff_size + 0x04 == file_size)
|
||||
riff_size -= 0x04; /* [Halo 2 (PC)] (possibly bad extractor? 'Gravemind Tool') */
|
||||
|
||||
else if (codec == 0x0069 && riff_size + 0x10 == file_size)
|
||||
riff_size += 0x08; /* [Fast and the Furious (Xbox)] ("HASH" chunk + 4 byte hash) */
|
||||
|
||||
else if (codec == 0x0000 && riff_size + 0x04 == file_size)
|
||||
riff_size -= 0x04; /* [Headhunter (DC), Bomber hehhe (DC)] */
|
||||
|
||||
|
|
|
@ -4,8 +4,9 @@
|
|||
/* RSTM - from Rockstar games [Midnight Club 3, Bully - Canis Canim Edit (PS2)] */
|
||||
VGMSTREAM* init_vgmstream_rstm_rockstar(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
uint32_t start_offset;
|
||||
int channels, loop_flag;
|
||||
off_t stream_offset, loop_start, loop_end;
|
||||
size_t stream_size;
|
||||
int sample_rate, channels, loop_flag;
|
||||
|
||||
|
||||
/* checks */
|
||||
|
@ -17,9 +18,17 @@ VGMSTREAM* init_vgmstream_rstm_rockstar(STREAMFILE* sf) {
|
|||
if (!check_extensions(sf, "rsm,rstm"))
|
||||
return NULL;
|
||||
|
||||
loop_flag = (read_s32le(0x24,sf) > 0);
|
||||
sample_rate = read_s32le(0x08, sf);
|
||||
channels = read_s32le(0x0C, sf);
|
||||
start_offset = 0x800;
|
||||
/* 0x10-0x18 - empty padding(?) */
|
||||
stream_size = read_s32le(0x18, sf);
|
||||
loop_start = read_s32le(0x1C, sf);
|
||||
loop_end = read_s32le(0x20, sf);
|
||||
/* other loop start/ends after here? (uncommon) */
|
||||
stream_offset = 0x800;
|
||||
|
||||
//loop_flag = (read_s32le(0x24,sf) > 0);
|
||||
loop_flag = loop_end != stream_size;
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
|
@ -27,17 +36,17 @@ VGMSTREAM* init_vgmstream_rstm_rockstar(STREAMFILE* sf) {
|
|||
|
||||
vgmstream->meta_type = meta_RSTM_ROCKSTAR;
|
||||
|
||||
vgmstream->sample_rate = read_s32le(0x08,sf);
|
||||
vgmstream->num_samples = ps_bytes_to_samples(read_u32le(0x20,sf),channels);
|
||||
vgmstream->loop_start_sample = ps_bytes_to_samples(read_u32le(0x24,sf),channels);
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = ps_bytes_to_samples(stream_size, channels);
|
||||
vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start, channels);
|
||||
vgmstream->loop_end_sample = ps_bytes_to_samples(loop_end, channels);
|
||||
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x10;
|
||||
|
||||
/* open the file for reading */
|
||||
if ( !vgmstream_open_stream(vgmstream, sf, start_offset) )
|
||||
if ( !vgmstream_open_stream(vgmstream, sf, stream_offset) )
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
|
|
|
@ -74,27 +74,30 @@ fail:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
#define SEGMENT_MAX 3
|
||||
|
||||
/* Nippon Ichi SPS wrapper (segmented) [Penny-Punching Princess (Switch), Disgaea 4 Complete (PC)] */
|
||||
VGMSTREAM* init_vgmstream_sps_n1_segmented(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t segment_offset;
|
||||
size_t data_size, max_size;
|
||||
int loop_flag, type, sample_rate;
|
||||
int i, segment;
|
||||
|
||||
init_vgmstream_t init_vgmstream = NULL;
|
||||
const char* extension;
|
||||
segmented_layout_data* data = NULL;
|
||||
int segment_count, loop_start_segment, loop_end_segment;
|
||||
int loop_start_segment, loop_end_segment;
|
||||
|
||||
|
||||
/* checks */
|
||||
/* .at9: Penny-Punching Princess (Switch)
|
||||
type = read_u32le(0x00,sf);
|
||||
if (type > 10)
|
||||
return NULL;
|
||||
|
||||
/* .at9: Penny-Punching Princess (Switch), Labyrinth of Galleria (PC)
|
||||
* .nlsd: Disgaea 4 Complete (PC) */
|
||||
if (!check_extensions(sf, "at9,nlsd"))
|
||||
goto fail;
|
||||
return NULL;
|
||||
|
||||
type = read_u32le(0x00,sf);
|
||||
data_size = read_u32le(0x04,sf);
|
||||
sample_rate = read_u16le(0x08,sf);
|
||||
/* 0x0a: flag? (stereo?) */
|
||||
|
@ -113,23 +116,58 @@ VGMSTREAM* init_vgmstream_sps_n1_segmented(STREAMFILE* sf) {
|
|||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
segment_offset = 0x1c;
|
||||
if (data_size + segment_offset != get_streamfile_size(sf))
|
||||
goto fail;
|
||||
|
||||
/* segmented using 3 files (intro/loop/outro). non-segmented wrapper is the same
|
||||
* but with loop samples instead of sub-sizes */
|
||||
|
||||
uint32_t segment_offsets[SEGMENT_MAX];
|
||||
uint32_t segment_sizes[SEGMENT_MAX];
|
||||
uint32_t segment_start, offset;
|
||||
int segment_count, segment;
|
||||
|
||||
if (data_size + 0x1c == get_streamfile_size(sf)) {
|
||||
/* common */
|
||||
segment_start = 0x1c;
|
||||
offset = segment_start;
|
||||
for (int i = 0; i < SEGMENT_MAX; i++) {
|
||||
uint32_t segment_size = read_u32le(0x10 + 0x04*i,sf);
|
||||
|
||||
segment_sizes[i] = segment_size;
|
||||
segment_offsets[i] = offset;
|
||||
offset += segment_sizes[i];
|
||||
}
|
||||
}
|
||||
else if (data_size + 0x18 == get_streamfile_size(sf)) {
|
||||
/* Labyrinth of Galleria (PC) */
|
||||
segment_start = 0x18;
|
||||
offset = segment_start;
|
||||
for (int i = 0; i < SEGMENT_MAX; i++) {
|
||||
uint32_t next_offset;
|
||||
if (i >= 2) {
|
||||
next_offset = get_streamfile_size(sf) - segment_start;
|
||||
}
|
||||
else {
|
||||
next_offset = read_u32le(0x10 + 0x04*i,sf); /* only 2 (not sure if it can be 0) */
|
||||
}
|
||||
|
||||
segment_sizes[i] = next_offset - offset + segment_start;
|
||||
segment_offsets[i] = offset;
|
||||
offset += segment_sizes[i];
|
||||
}
|
||||
}
|
||||
else {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
max_size = 0;
|
||||
segment_count = 0;
|
||||
for (i = 0; i < 3; i++) {
|
||||
size_t segment_size = read_u32le(0x10 + 0x04*i,sf);
|
||||
max_size += segment_size;
|
||||
/* may only set 1 segment (Disgaea4's bgm_185) */
|
||||
if (segment_size)
|
||||
for (int i = 0; i < SEGMENT_MAX; i++) {
|
||||
/* may only set 1 segment, with empty intro/outro (Disgaea4's bgm_185) */
|
||||
if (segment_sizes[i])
|
||||
segment_count++;
|
||||
max_size += segment_sizes[i];
|
||||
}
|
||||
if (data_size != max_size)
|
||||
goto fail;
|
||||
|
@ -144,25 +182,21 @@ VGMSTREAM* init_vgmstream_sps_n1_segmented(STREAMFILE* sf) {
|
|||
|
||||
/* open each segment subfile */
|
||||
segment = 0;
|
||||
for (i = 0; i < 3; i++) {
|
||||
STREAMFILE* temp_sf;
|
||||
size_t segment_size = read_u32le(0x10 + 0x04*i,sf);
|
||||
|
||||
if (!segment_size)
|
||||
for (int i = 0; i < SEGMENT_MAX; i++) {
|
||||
if (!segment_sizes[i])
|
||||
continue;
|
||||
|
||||
temp_sf = setup_subfile_streamfile(sf, segment_offset,segment_size, extension);
|
||||
STREAMFILE* temp_sf = setup_subfile_streamfile(sf, segment_offsets[i],segment_sizes[i], extension);
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
data->segments[segment] = init_vgmstream(temp_sf);
|
||||
close_streamfile(temp_sf);
|
||||
if (!data->segments[segment]) goto fail;
|
||||
|
||||
segment_offset += segment_size;
|
||||
segment++;
|
||||
|
||||
if (type == 9) {
|
||||
//todo there are some trailing samples that must be removed for smooth loops, start skip seems ok
|
||||
//TODO there are some trailing samples that must be removed for smooth loops, start skip seems ok
|
||||
//not correct for all files, no idea how to calculate
|
||||
data->segments[segment]->num_samples -= 374;
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@ VGMSTREAM * init_vgmstream_sthd(STREAMFILE *sf) {
|
|||
goto fail;
|
||||
/* first block has special values */
|
||||
if (read_u16le(0x04,sf) != 0x0800 ||
|
||||
read_u32le(0x0c,sf) != 0x0001 ||
|
||||
read_u32le(0x14,sf) != 0x0000)
|
||||
goto fail;
|
||||
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "../util/layout_utils.h"
|
||||
#include "str_wav_streamfile.h"
|
||||
|
||||
|
||||
typedef enum { PSX, DSP, XBOX, WMA, IMA, XMA2 } strwav_codec;
|
||||
typedef enum { PSX, PSX_chunked, DSP, XBOX, WMA, IMA, XMA2, MPEG } strwav_codec;
|
||||
typedef struct {
|
||||
int tracks;
|
||||
int channels;
|
||||
|
@ -103,6 +105,29 @@ VGMSTREAM* init_vgmstream_str_wav(STREAMFILE* sf) {
|
|||
vgmstream->interleave_block_size = strwav.interleave;
|
||||
break;
|
||||
|
||||
case PSX_chunked: { /* hack */
|
||||
//tracks are stereo blocks of size 0x20000 * tracks, containing 4 interleaves of 0x8000:
|
||||
// | 1 2 1 2 | 3 4 3 4 | 5 6 5 6 | 1 2 1 2 | 3 4 3 4 | 5 6 5 6 | ...
|
||||
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->interleave_block_size = strwav.interleave;
|
||||
for (int i = 0; i < strwav.tracks; i++) {
|
||||
uint32_t chunk_size = 0x20000;
|
||||
int layer_channels = 2;
|
||||
|
||||
STREAMFILE* temp_sf = setup_str_wav_streamfile(sf, 0x00, strwav.tracks, i, chunk_size);
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
bool res = layered_add_sf(vgmstream, strwav.tracks, layer_channels, temp_sf);
|
||||
close_streamfile(temp_sf);
|
||||
if (!res)
|
||||
goto fail;
|
||||
}
|
||||
if (!layered_add_done(vgmstream))
|
||||
goto fail;
|
||||
break;
|
||||
}
|
||||
|
||||
case DSP:
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
|
@ -173,6 +198,26 @@ VGMSTREAM* init_vgmstream_str_wav(STREAMFILE* sf) {
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
case MPEG: {
|
||||
/* regular MP3 starting with ID2, stereo tracks xN (bgm + vocals) but assuming last (or only one) could be mono */
|
||||
int layers = (strwav.channels + 1) / 2;
|
||||
|
||||
for (int i = 0; i < layers; i++) {
|
||||
uint32_t size = strwav.interleave;
|
||||
uint32_t offset = i * size;
|
||||
const char* ext = "mp3";
|
||||
int layer_channels = ((layers % 2) && i + 1 == layers) ? 1 : 2;
|
||||
|
||||
layered_add_subfile(vgmstream, layers, layer_channels, sf, offset, size, ext, init_vgmstream_mpeg);
|
||||
}
|
||||
|
||||
if (!layered_add_done(vgmstream))
|
||||
goto fail;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
@ -286,7 +331,7 @@ static int parse_header(STREAMFILE* sf_h, STREAMFILE* sf_b, strwav_header* strwa
|
|||
/* 0x10c: header size */
|
||||
|
||||
strwav->codec = IMA;
|
||||
strwav->interleave = strwav->tracks > 1 ? 0x8000 : 0x10000;
|
||||
strwav->interleave = strwav->tracks > 1 ? 0x10000 : 0x10000;
|
||||
;VGM_LOG("STR+WAV: header TAZd (PC)\n");
|
||||
return 1;
|
||||
}
|
||||
|
@ -435,8 +480,8 @@ static int parse_header(STREAMFILE* sf_h, STREAMFILE* sf_b, strwav_header* strwa
|
|||
|
||||
strwav->codec = PSX;
|
||||
strwav->interleave = strwav->tracks > 1 ? 0x8000 : 0x8000;
|
||||
//todo: tracks are stereo blocks of size 0x20000*tracks, containing 4 interleaves of 0x8000:
|
||||
// | 1 2 1 2 | 3 4 3 4 | 5 6 5 6 | 1 2 1 2 | 3 4 3 4 | 5 6 5 6 | ...
|
||||
if (strwav->tracks > 1) /* hack */
|
||||
strwav->codec = PSX_chunked;
|
||||
;VGM_LOG("STR+WAV: header ZPb (PS2)\n");
|
||||
return 1;
|
||||
}
|
||||
|
@ -498,6 +543,32 @@ static int parse_header(STREAMFILE* sf_h, STREAMFILE* sf_b, strwav_header* strwa
|
|||
return 1;
|
||||
}
|
||||
|
||||
/* Taz Wanted (beta) (PC)[2002] */
|
||||
if ( read_u32be(0x04,sf_h) == 0x00000900 &&
|
||||
read_u32le(0x0c,sf_h) != header_size &&
|
||||
read_u32le(0x24,sf_h) != 0 &&
|
||||
read_u32le(0xd4,sf_h) != 0 &&
|
||||
read_u32le(0xdc,sf_h) == header_size
|
||||
) {
|
||||
/* 0x08: null */
|
||||
/* 0x0c: hashname */
|
||||
strwav->num_samples = read_s32le(0x20,sf_h);
|
||||
strwav->sample_rate = read_s32le(0x24,sf_h);
|
||||
/* 0x28: 16 bps */
|
||||
strwav->flags = read_u32le(0x2c,sf_h);
|
||||
strwav->loop_start = read_s32le(0x38,sf_h);
|
||||
/* 0x54: number of chunks */
|
||||
strwav->tracks = read_s32le(0xd4,sf_h);
|
||||
/* 0xdc: header size */
|
||||
|
||||
strwav->loop_end = strwav->num_samples;
|
||||
|
||||
strwav->codec = IMA;
|
||||
strwav->interleave = strwav->tracks > 1 ? 0x8000 : 0x10000;
|
||||
;VGM_LOG("STR+WAV: header TAZb (PC)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Taz Wanted (PC)[2002] */
|
||||
/* Zapper: One Wicked Cricket! Beta (Xbox)[2002] */
|
||||
if ( read_u32be(0x04,sf_h) == 0x00000900 &&
|
||||
|
@ -669,7 +740,7 @@ static int parse_header(STREAMFILE* sf_h, STREAMFILE* sf_b, strwav_header* strwa
|
|||
/* 0x4c: ? (some low number) */
|
||||
strwav->tracks = read_u8 (0x4e,sf_h);
|
||||
/* 0x4f: 16 bps */
|
||||
/* 0x54: channels per each track? (ex. 2 stereo track: 0x02,0x02) */
|
||||
/* 0x54: channels per each track (ex. 2 stereo track: 0x02,0x02) */
|
||||
/* 0x64: channels */
|
||||
/* 0x70+: tables */
|
||||
|
||||
|
@ -712,6 +783,7 @@ static int parse_header(STREAMFILE* sf_h, STREAMFILE* sf_b, strwav_header* strwa
|
|||
/* Tak and the Guardians of Gross (Wii)[2008] */
|
||||
/* The House of the Dead: Overkill (Wii)[2009] (not Blitz but still the same format) */
|
||||
/* All Star Karate (Wii)[2010] */
|
||||
/* Karaoke Revolution (Wii)[2010] */
|
||||
if ((read_u32be(0x04,sf_h) == 0x00000800 ||
|
||||
read_u32be(0x04,sf_h) == 0x00000700) && /* rare? */
|
||||
read_u32be(0x08,sf_h) != 0x00000000 &&
|
||||
|
@ -730,11 +802,12 @@ static int parse_header(STREAMFILE* sf_h, STREAMFILE* sf_b, strwav_header* strwa
|
|||
strwav->codec = DSP;
|
||||
strwav->coefs_table = 0x7c;
|
||||
strwav->interleave = strwav->channels > 4 ? 0x4000 : 0x8000;
|
||||
;VGM_LOG("STR+WAV: header TKGG/HOTDO/ASK (Wii)\n");
|
||||
;VGM_LOG("STR+WAV: header TKGG/HOTDO/ASK/KR (Wii)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* The House of the Dead: Overkill (PS3)[2009] (not Blitz but still the same format) */
|
||||
/* Karaoke Revolution (PS3)[2010] */
|
||||
if ((read_u32be(0x04,sf_h) == 0x00000800 ||
|
||||
read_u32be(0x04,sf_h) == 0x00000700) && /* rare? */
|
||||
read_u32be(0x08,sf_h) != 0x00000000 &&
|
||||
|
@ -747,10 +820,30 @@ static int parse_header(STREAMFILE* sf_h, STREAMFILE* sf_b, strwav_header* strwa
|
|||
strwav->sample_rate = read_s32be(0x38,sf_h);
|
||||
strwav->flags = read_u32be(0x3c,sf_h);
|
||||
|
||||
strwav->channels = read_s32be(0x70,sf_h);
|
||||
|
||||
/* other possibly-useful flags (see Karaoke Wii too):
|
||||
* - 0x4b: number of tracks
|
||||
* - 0x60: channels per track, ex. 020202 = 3 tracks of 2ch (max 0x08 = 8)
|
||||
* - 0xa0: sizes per track (max 0x20 = 8)
|
||||
* - 0xc0: samples per track (max 0x20 = 8)
|
||||
* - rest: info/seek table? */
|
||||
if (read_s32be(0x78,sf_h) != 0) { /* KRev */
|
||||
|
||||
strwav->tracks = strwav->channels / 2;
|
||||
strwav->num_samples = strwav->loop_end; /* num_samples here seems to be data size */
|
||||
strwav->interleave = read_s32be(0xA0,sf_h); /* one size per file, but CBR = same for all */
|
||||
//C0: stream samples (same as num_samples)
|
||||
|
||||
strwav->codec = MPEG; /* full CBR MP3 one after other */
|
||||
}
|
||||
else { /* HOTD */
|
||||
strwav->channels = read_s32be(0x70,sf_h); /* tracks of 1ch */
|
||||
strwav->interleave = strwav->channels > 4 ? 0x4000 : 0x8000;
|
||||
|
||||
strwav->codec = PSX;
|
||||
}
|
||||
|
||||
;VGM_LOG("STR+WAV: header HOTDO (PS3)\n");
|
||||
return 1;
|
||||
}
|
||||
|
|
21
Frameworks/vgmstream/vgmstream/src/meta/str_wav_streamfile.h
Normal file
21
Frameworks/vgmstream/vgmstream/src/meta/str_wav_streamfile.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
#ifndef _STR_WAV_STREAMFILE_H_
|
||||
#define _STR_WAV_STREAMFILE_H_
|
||||
#include "deblock_streamfile.h"
|
||||
|
||||
/* Deblocks streams */
|
||||
static STREAMFILE* setup_str_wav_streamfile(STREAMFILE* sf, off_t stream_start, int stream_count, int stream_number, size_t interleave) {
|
||||
STREAMFILE *new_sf = NULL;
|
||||
deblock_config_t cfg = {0};
|
||||
|
||||
cfg.stream_start = stream_start;
|
||||
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
|
|
@ -841,7 +841,7 @@ static void set_body_chunk(txth_header* txth) {
|
|||
if (!txth->sf_body)
|
||||
return;
|
||||
|
||||
/* treat chunks as subsongs */
|
||||
/* treat chunks as subsongs (less subsongs than chunks could be allowed to ignore some chunks but it's kinda odd) */
|
||||
if (txth->subsong_count > 1 && txth->subsong_count == txth->chunk_count)
|
||||
txth->chunk_number = txth->target_subsong;
|
||||
if (txth->chunk_number == 0)
|
||||
|
|
|
@ -1012,7 +1012,7 @@ static int parse_values(ubi_bao_header* bao) {
|
|||
}
|
||||
|
||||
/* set codec */
|
||||
if (bao->stream_type > 0x10) {
|
||||
if (bao->stream_type >= 0x10) {
|
||||
VGM_LOG("UBI BAO: unknown stream_type at %x\n", (uint32_t)bao->header_offset); goto fail;
|
||||
goto fail;
|
||||
}
|
||||
|
|
|
@ -575,6 +575,7 @@ VGMSTREAM* init_vgmstream_wwise_bnk(STREAMFILE* sf, int* p_prefetch) {
|
|||
switch(ww.channel_layout) {
|
||||
case mapping_7POINT1_surround: cfg.coupled_count = 3; break; /* 2ch+2ch+2ch+1ch+1ch, 5 streams */
|
||||
case mapping_5POINT1_surround: /* 2ch+2ch+1ch+1ch, 4 streams */
|
||||
case mapping_5POINT0_surround: /* 2ch+2ch+1ch, 3 streams [Bayonetta 3 (Switch)] */
|
||||
case mapping_QUAD_side: cfg.coupled_count = 2; break; /* 2ch+2ch, 2 streams */
|
||||
case mapping_2POINT1_xiph: /* 2ch+1ch, 2 streams */
|
||||
case mapping_STEREO: cfg.coupled_count = 1; break; /* 2ch, 1 stream */
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
#ifndef _BITSTREAM_LSB_H
|
||||
#define _BITSTREAM_LSB_H
|
||||
|
||||
#include "../streamtypes.h"
|
||||
#include <stdint.h>
|
||||
|
||||
/* Simple bitreader for Vorbis' bit style, in 'least significant byte' (LSB) format.
|
||||
* Example: 0x12345678 is read as 12,34,56,78 (continuous).
|
||||
* Example: with 0x1234 = 00010010 00110100, reading 5b + 6b = 10010 100000
|
||||
* (first lower 5b, then next upper 3b and next lower 3b = 6b)
|
||||
* Kept in .h since it's slightly faster (compiler can optimize statics better using default compile flags). */
|
||||
|
||||
|
||||
|
@ -23,7 +24,7 @@ static inline void bl_setup(bitstream_t* b, uint8_t* buf, size_t bufsize) {
|
|||
|
||||
/* same as (1 << bits) - 1, but that seems to trigger some nasty UB when bits = 32
|
||||
* (though in theory (1 << 32) = 0, 0 - 1 = UINT_MAX, but gives 0 compiling in some cases, but not always) */
|
||||
static const uint32_t MASK_TABLE[33] = {
|
||||
static const uint32_t MASK_TABLE_LSB[33] = {
|
||||
0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff,
|
||||
0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff, 0x0001ffff,
|
||||
0x0003ffff, 0x0007ffff, 0x000fffff, 0x001fffff, 0x003fffff, 0x007fffff, 0x00ffffff, 0x01ffffff, 0x03ffffff,
|
||||
|
@ -40,7 +41,7 @@ static inline int bl_get(bitstream_t* ib, uint32_t bits, uint32_t* value) {
|
|||
|
||||
pos = ib->b_off / 8; /* byte offset */
|
||||
shift = ib->b_off % 8; /* bit sub-offset */
|
||||
mask = MASK_TABLE[bits]; /* to remove upper in highest byte */
|
||||
mask = MASK_TABLE_LSB[bits]; /* to remove upper in highest byte */
|
||||
|
||||
val = ib->buf[pos+0] >> shift;
|
||||
if (bits + shift > 8) {
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
#ifndef _BITSTREAM_MSB_H
|
||||
#define _BITSTREAM_MSB_H
|
||||
|
||||
#include "../streamtypes.h"
|
||||
#include <stdint.h>
|
||||
|
||||
/* Simple bitreader for MPEG/standard bit style, in 'most significant byte' (MSB) format.
|
||||
* Example: 0x12345678 is read as 78,56,34,12 then each byte's bits.
|
||||
* Example: with 0x1234 = 00010010 00110100, reading 5b + 6b = 00010 010001
|
||||
* (first upper 5b, then next lower 3b and next upper 3b = 6b)
|
||||
* Kept in .h since it's slightly faster (compiler can optimize statics better using default compile flags). */
|
||||
|
||||
typedef struct {
|
||||
|
@ -14,7 +15,7 @@ typedef struct {
|
|||
uint32_t b_off; /* current offset in bits inside buffer */
|
||||
} bitstream_t;
|
||||
|
||||
|
||||
/* convenience util */
|
||||
static inline void bm_setup(bitstream_t* bs, uint8_t* buf, size_t bufsize) {
|
||||
bs->buf = buf;
|
||||
bs->bufsize = bufsize;
|
||||
|
@ -60,10 +61,20 @@ static inline int bm_pos(bitstream_t* bs) {
|
|||
return bs->b_off;
|
||||
}
|
||||
|
||||
/* same as (1 << bits) - 1, but that seems to trigger some nasty UB when bits = 32
|
||||
* (though in theory (1 << 32) = 0, 0 - 1 = UINT_MAX, but gives 0 compiling in some cases, but not always) */
|
||||
static const uint32_t MASK_TABLE_MSB[33] = {
|
||||
0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff,
|
||||
0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff, 0x0001ffff,
|
||||
0x0003ffff, 0x0007ffff, 0x000fffff, 0x001fffff, 0x003fffff, 0x007fffff, 0x00ffffff, 0x01ffffff, 0x03ffffff,
|
||||
0x07ffffff, 0x0fffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff, 0xffffffff
|
||||
};
|
||||
|
||||
/* Read bits (max 32) from buf and update the bit offset. Order is BE (MSB). */
|
||||
static inline int bm_get(bitstream_t* ib, uint32_t bits, uint32_t* value) {
|
||||
uint32_t shift, pos, val;
|
||||
int i, bit_buf, bit_val;
|
||||
uint32_t shift, pos, mask;
|
||||
uint64_t val; //TODO: could use u32 with some shift fiddling
|
||||
int i, bit_buf, bit_val, left;
|
||||
|
||||
if (bits > 32 || ib->b_off + bits > ib->b_max)
|
||||
goto fail;
|
||||
|
@ -71,7 +82,7 @@ static inline int bm_get(bitstream_t* ib, uint32_t bits, uint32_t* value) {
|
|||
pos = ib->b_off / 8; /* byte offset */
|
||||
shift = ib->b_off % 8; /* bit sub-offset */
|
||||
|
||||
#if 1 //naive approach
|
||||
#if 0 //naive approach
|
||||
val = 0;
|
||||
for (i = 0; i < bits; i++) {
|
||||
bit_buf = (1U << (8-1-shift)) & 0xFF; /* bit check for buf */
|
||||
|
@ -86,12 +97,10 @@ static inline int bm_get(bitstream_t* ib, uint32_t bits, uint32_t* value) {
|
|||
pos++;
|
||||
}
|
||||
}
|
||||
#else //has bugs
|
||||
pos = ib->b_off / 8; /* byte offset */
|
||||
shift = ib->b_off % 8; /* bit sub-offset */
|
||||
uint32_t mask = MASK_TABLE[bits]; /* to remove upper in highest byte */
|
||||
#else
|
||||
mask = MASK_TABLE_MSB[bits]; /* to remove upper in highest byte */
|
||||
|
||||
int left = 0;
|
||||
left = 0;
|
||||
if (bits == 0)
|
||||
val = 0;
|
||||
else
|
||||
|
@ -102,12 +111,12 @@ static inline int bm_get(bitstream_t* ib, uint32_t bits, uint32_t* value) {
|
|||
left = 16 - (bits + shift);
|
||||
if (bits + shift > 16) {
|
||||
val = (val << 8u) | ib->buf[pos+2];
|
||||
left = 32 - (bits + shift);
|
||||
left = 24 - (bits + shift);
|
||||
if (bits + shift > 24) {
|
||||
val = (val << 8u) | ib->buf[pos+3];
|
||||
left = 32 - (bits + shift);
|
||||
if (bits + shift > 32) {
|
||||
val = (val << 8u) | ib->buf[pos+4]; /* upper bits are lost (shifting over 32) */ TO-DO
|
||||
val = (val << 8u) | ib->buf[pos+4];
|
||||
left = 40 - (bits + shift);
|
||||
}
|
||||
}
|
||||
|
|
303
Frameworks/vgmstream/vgmstream/src/util/cipher_blowfish.c
Normal file
303
Frameworks/vgmstream/vgmstream/src/util/cipher_blowfish.c
Normal file
|
@ -0,0 +1,303 @@
|
|||
#include <stdlib.h>
|
||||
#include "cipher_blowfish.h"
|
||||
#include "reader_get.h"
|
||||
#include "reader_put.h"
|
||||
|
||||
|
||||
/* Simple blowfish implementation, mainly for learning purposes and not really optimized.
|
||||
* Original algorithm:
|
||||
* - https://www.schneier.com/academic/archives/1994/09/description_of_a_new.html
|
||||
*/
|
||||
|
||||
/* ~0x48 + 0x1008, could be done in stack */
|
||||
struct blowfish_ctx {
|
||||
uint32_t P[18]; /* P-array */
|
||||
uint32_t S[4][256]; /* S-boxes */
|
||||
};
|
||||
|
||||
|
||||
/* hex calculation of the fractional part of pi, first P then S-boxes
|
||||
* (not a base change but calc'd in hex directly, unsure about the algorithm).
|
||||
* Pi was chosen for being a "nothing up my sleeves" number. */
|
||||
static const uint32_t INIT_P[18] = { /* 16 rounds + 2 extra positions */
|
||||
0x243F6A88, 0x85A308D3, 0x13198A2E, 0x03707344, 0xA4093822, 0x299F31D0, 0x082EFA98, 0xEC4E6C89,
|
||||
0x452821E6, 0x38D01377, 0xBE5466CF, 0x34E90C6C, 0xC0AC29B7, 0xC97C50DD, 0x3F84D5B5, 0xB5470917,
|
||||
0x9216D5D9, 0x8979FB1B
|
||||
};
|
||||
|
||||
static const uint32_t INIT_S[4][256] = {
|
||||
{
|
||||
0xD1310BA6, 0x98DFB5AC, 0x2FFD72DB, 0xD01ADFB7, 0xB8E1AFED, 0x6A267E96, 0xBA7C9045, 0xF12C7F99,
|
||||
0x24A19947, 0xB3916CF7, 0x0801F2E2, 0x858EFC16, 0x636920D8, 0x71574E69, 0xA458FEA3, 0xF4933D7E,
|
||||
0x0D95748F, 0x728EB658, 0x718BCD58, 0x82154AEE, 0x7B54A41D, 0xC25A59B5, 0x9C30D539, 0x2AF26013,
|
||||
0xC5D1B023, 0x286085F0, 0xCA417918, 0xB8DB38EF, 0x8E79DCB0, 0x603A180E, 0x6C9E0E8B, 0xB01E8A3E,
|
||||
0xD71577C1, 0xBD314B27, 0x78AF2FDA, 0x55605C60, 0xE65525F3, 0xAA55AB94, 0x57489862, 0x63E81440,
|
||||
0x55CA396A, 0x2AAB10B6, 0xB4CC5C34, 0x1141E8CE, 0xA15486AF, 0x7C72E993, 0xB3EE1411, 0x636FBC2A,
|
||||
0x2BA9C55D, 0x741831F6, 0xCE5C3E16, 0x9B87931E, 0xAFD6BA33, 0x6C24CF5C, 0x7A325381, 0x28958677,
|
||||
0x3B8F4898, 0x6B4BB9AF, 0xC4BFE81B, 0x66282193, 0x61D809CC, 0xFB21A991, 0x487CAC60, 0x5DEC8032,
|
||||
0xEF845D5D, 0xE98575B1, 0xDC262302, 0xEB651B88, 0x23893E81, 0xD396ACC5, 0x0F6D6FF3, 0x83F44239,
|
||||
0x2E0B4482, 0xA4842004, 0x69C8F04A, 0x9E1F9B5E, 0x21C66842, 0xF6E96C9A, 0x670C9C61, 0xABD388F0,
|
||||
0x6A51A0D2, 0xD8542F68, 0x960FA728, 0xAB5133A3, 0x6EEF0B6C, 0x137A3BE4, 0xBA3BF050, 0x7EFB2A98,
|
||||
0xA1F1651D, 0x39AF0176, 0x66CA593E, 0x82430E88, 0x8CEE8619, 0x456F9FB4, 0x7D84A5C3, 0x3B8B5EBE,
|
||||
0xE06F75D8, 0x85C12073, 0x401A449F, 0x56C16AA6, 0x4ED3AA62, 0x363F7706, 0x1BFEDF72, 0x429B023D,
|
||||
0x37D0D724, 0xD00A1248, 0xDB0FEAD3, 0x49F1C09B, 0x075372C9, 0x80991B7B, 0x25D479D8, 0xF6E8DEF7,
|
||||
0xE3FE501A, 0xB6794C3B, 0x976CE0BD, 0x04C006BA, 0xC1A94FB6, 0x409F60C4, 0x5E5C9EC2, 0x196A2463,
|
||||
0x68FB6FAF, 0x3E6C53B5, 0x1339B2EB, 0x3B52EC6F, 0x6DFC511F, 0x9B30952C, 0xCC814544, 0xAF5EBD09,
|
||||
0xBEE3D004, 0xDE334AFD, 0x660F2807, 0x192E4BB3, 0xC0CBA857, 0x45C8740F, 0xD20B5F39, 0xB9D3FBDB,
|
||||
0x5579C0BD, 0x1A60320A, 0xD6A100C6, 0x402C7279, 0x679F25FE, 0xFB1FA3CC, 0x8EA5E9F8, 0xDB3222F8,
|
||||
0x3C7516DF, 0xFD616B15, 0x2F501EC8, 0xAD0552AB, 0x323DB5FA, 0xFD238760, 0x53317B48, 0x3E00DF82,
|
||||
0x9E5C57BB, 0xCA6F8CA0, 0x1A87562E, 0xDF1769DB, 0xD542A8F6, 0x287EFFC3, 0xAC6732C6, 0x8C4F5573,
|
||||
0x695B27B0, 0xBBCA58C8, 0xE1FFA35D, 0xB8F011A0, 0x10FA3D98, 0xFD2183B8, 0x4AFCB56C, 0x2DD1D35B,
|
||||
0x9A53E479, 0xB6F84565, 0xD28E49BC, 0x4BFB9790, 0xE1DDF2DA, 0xA4CB7E33, 0x62FB1341, 0xCEE4C6E8,
|
||||
0xEF20CADA, 0x36774C01, 0xD07E9EFE, 0x2BF11FB4, 0x95DBDA4D, 0xAE909198, 0xEAAD8E71, 0x6B93D5A0,
|
||||
0xD08ED1D0, 0xAFC725E0, 0x8E3C5B2F, 0x8E7594B7, 0x8FF6E2FB, 0xF2122B64, 0x8888B812, 0x900DF01C,
|
||||
0x4FAD5EA0, 0x688FC31C, 0xD1CFF191, 0xB3A8C1AD, 0x2F2F2218, 0xBE0E1777, 0xEA752DFE, 0x8B021FA1,
|
||||
0xE5A0CC0F, 0xB56F74E8, 0x18ACF3D6, 0xCE89E299, 0xB4A84FE0, 0xFD13E0B7, 0x7CC43B81, 0xD2ADA8D9,
|
||||
0x165FA266, 0x80957705, 0x93CC7314, 0x211A1477, 0xE6AD2065, 0x77B5FA86, 0xC75442F5, 0xFB9D35CF,
|
||||
0xEBCDAF0C, 0x7B3E89A0, 0xD6411BD3, 0xAE1E7E49, 0x00250E2D, 0x2071B35E, 0x226800BB, 0x57B8E0AF,
|
||||
0x2464369B, 0xF009B91E, 0x5563911D, 0x59DFA6AA, 0x78C14389, 0xD95A537F, 0x207D5BA2, 0x02E5B9C5,
|
||||
0x83260376, 0x6295CFA9, 0x11C81968, 0x4E734A41, 0xB3472DCA, 0x7B14A94A, 0x1B510052, 0x9A532915,
|
||||
0xD60F573F, 0xBC9BC6E4, 0x2B60A476, 0x81E67400, 0x08BA6FB5, 0x571BE91F, 0xF296EC6B, 0x2A0DD915,
|
||||
0xB6636521, 0xE7B9F9B6, 0xFF34052E, 0xC5855664, 0x53B02D5D, 0xA99F8FA1, 0x08BA4799, 0x6E85076A,
|
||||
},
|
||||
{
|
||||
0x4B7A70E9, 0xB5B32944, 0xDB75092E, 0xC4192623, 0xAD6EA6B0, 0x49A7DF7D, 0x9CEE60B8, 0x8FEDB266,
|
||||
0xECAA8C71, 0x699A17FF, 0x5664526C, 0xC2B19EE1, 0x193602A5, 0x75094C29, 0xA0591340, 0xE4183A3E,
|
||||
0x3F54989A, 0x5B429D65, 0x6B8FE4D6, 0x99F73FD6, 0xA1D29C07, 0xEFE830F5, 0x4D2D38E6, 0xF0255DC1,
|
||||
0x4CDD2086, 0x8470EB26, 0x6382E9C6, 0x021ECC5E, 0x09686B3F, 0x3EBAEFC9, 0x3C971814, 0x6B6A70A1,
|
||||
0x687F3584, 0x52A0E286, 0xB79C5305, 0xAA500737, 0x3E07841C, 0x7FDEAE5C, 0x8E7D44EC, 0x5716F2B8,
|
||||
0xB03ADA37, 0xF0500C0D, 0xF01C1F04, 0x0200B3FF, 0xAE0CF51A, 0x3CB574B2, 0x25837A58, 0xDC0921BD,
|
||||
0xD19113F9, 0x7CA92FF6, 0x94324773, 0x22F54701, 0x3AE5E581, 0x37C2DADC, 0xC8B57634, 0x9AF3DDA7,
|
||||
0xA9446146, 0x0FD0030E, 0xECC8C73E, 0xA4751E41, 0xE238CD99, 0x3BEA0E2F, 0x3280BBA1, 0x183EB331,
|
||||
0x4E548B38, 0x4F6DB908, 0x6F420D03, 0xF60A04BF, 0x2CB81290, 0x24977C79, 0x5679B072, 0xBCAF89AF,
|
||||
0xDE9A771F, 0xD9930810, 0xB38BAE12, 0xDCCF3F2E, 0x5512721F, 0x2E6B7124, 0x501ADDE6, 0x9F84CD87,
|
||||
0x7A584718, 0x7408DA17, 0xBC9F9ABC, 0xE94B7D8C, 0xEC7AEC3A, 0xDB851DFA, 0x63094366, 0xC464C3D2,
|
||||
0xEF1C1847, 0x3215D908, 0xDD433B37, 0x24C2BA16, 0x12A14D43, 0x2A65C451, 0x50940002, 0x133AE4DD,
|
||||
0x71DFF89E, 0x10314E55, 0x81AC77D6, 0x5F11199B, 0x043556F1, 0xD7A3C76B, 0x3C11183B, 0x5924A509,
|
||||
0xF28FE6ED, 0x97F1FBFA, 0x9EBABF2C, 0x1E153C6E, 0x86E34570, 0xEAE96FB1, 0x860E5E0A, 0x5A3E2AB3,
|
||||
0x771FE71C, 0x4E3D06FA, 0x2965DCB9, 0x99E71D0F, 0x803E89D6, 0x5266C825, 0x2E4CC978, 0x9C10B36A,
|
||||
0xC6150EBA, 0x94E2EA78, 0xA5FC3C53, 0x1E0A2DF4, 0xF2F74EA7, 0x361D2B3D, 0x1939260F, 0x19C27960,
|
||||
0x5223A708, 0xF71312B6, 0xEBADFE6E, 0xEAC31F66, 0xE3BC4595, 0xA67BC883, 0xB17F37D1, 0x018CFF28,
|
||||
0xC332DDEF, 0xBE6C5AA5, 0x65582185, 0x68AB9802, 0xEECEA50F, 0xDB2F953B, 0x2AEF7DAD, 0x5B6E2F84,
|
||||
0x1521B628, 0x29076170, 0xECDD4775, 0x619F1510, 0x13CCA830, 0xEB61BD96, 0x0334FE1E, 0xAA0363CF,
|
||||
0xB5735C90, 0x4C70A239, 0xD59E9E0B, 0xCBAADE14, 0xEECC86BC, 0x60622CA7, 0x9CAB5CAB, 0xB2F3846E,
|
||||
0x648B1EAF, 0x19BDF0CA, 0xA02369B9, 0x655ABB50, 0x40685A32, 0x3C2AB4B3, 0x319EE9D5, 0xC021B8F7,
|
||||
0x9B540B19, 0x875FA099, 0x95F7997E, 0x623D7DA8, 0xF837889A, 0x97E32D77, 0x11ED935F, 0x16681281,
|
||||
0x0E358829, 0xC7E61FD6, 0x96DEDFA1, 0x7858BA99, 0x57F584A5, 0x1B227263, 0x9B83C3FF, 0x1AC24696,
|
||||
0xCDB30AEB, 0x532E3054, 0x8FD948E4, 0x6DBC3128, 0x58EBF2EF, 0x34C6FFEA, 0xFE28ED61, 0xEE7C3C73,
|
||||
0x5D4A14D9, 0xE864B7E3, 0x42105D14, 0x203E13E0, 0x45EEE2B6, 0xA3AAABEA, 0xDB6C4F15, 0xFACB4FD0,
|
||||
0xC742F442, 0xEF6ABBB5, 0x654F3B1D, 0x41CD2105, 0xD81E799E, 0x86854DC7, 0xE44B476A, 0x3D816250,
|
||||
0xCF62A1F2, 0x5B8D2646, 0xFC8883A0, 0xC1C7B6A3, 0x7F1524C3, 0x69CB7492, 0x47848A0B, 0x5692B285,
|
||||
0x095BBF00, 0xAD19489D, 0x1462B174, 0x23820E00, 0x58428D2A, 0x0C55F5EA, 0x1DADF43E, 0x233F7061,
|
||||
0x3372F092, 0x8D937E41, 0xD65FECF1, 0x6C223BDB, 0x7CDE3759, 0xCBEE7460, 0x4085F2A7, 0xCE77326E,
|
||||
0xA6078084, 0x19F8509E, 0xE8EFD855, 0x61D99735, 0xA969A7AA, 0xC50C06C2, 0x5A04ABFC, 0x800BCADC,
|
||||
0x9E447A2E, 0xC3453484, 0xFDD56705, 0x0E1E9EC9, 0xDB73DBD3, 0x105588CD, 0x675FDA79, 0xE3674340,
|
||||
0xC5C43465, 0x713E38D8, 0x3D28F89E, 0xF16DFF20, 0x153E21E7, 0x8FB03D4A, 0xE6E39F2B, 0xDB83ADF7,
|
||||
},
|
||||
{
|
||||
0xE93D5A68, 0x948140F7, 0xF64C261C, 0x94692934, 0x411520F7, 0x7602D4F7, 0xBCF46B2E, 0xD4A20068,
|
||||
0xD4082471, 0x3320F46A, 0x43B7D4B7, 0x500061AF, 0x1E39F62E, 0x97244546, 0x14214F74, 0xBF8B8840,
|
||||
0x4D95FC1D, 0x96B591AF, 0x70F4DDD3, 0x66A02F45, 0xBFBC09EC, 0x03BD9785, 0x7FAC6DD0, 0x31CB8504,
|
||||
0x96EB27B3, 0x55FD3941, 0xDA2547E6, 0xABCA0A9A, 0x28507825, 0x530429F4, 0x0A2C86DA, 0xE9B66DFB,
|
||||
0x68DC1462, 0xD7486900, 0x680EC0A4, 0x27A18DEE, 0x4F3FFEA2, 0xE887AD8C, 0xB58CE006, 0x7AF4D6B6,
|
||||
0xAACE1E7C, 0xD3375FEC, 0xCE78A399, 0x406B2A42, 0x20FE9E35, 0xD9F385B9, 0xEE39D7AB, 0x3B124E8B,
|
||||
0x1DC9FAF7, 0x4B6D1856, 0x26A36631, 0xEAE397B2, 0x3A6EFA74, 0xDD5B4332, 0x6841E7F7, 0xCA7820FB,
|
||||
0xFB0AF54E, 0xD8FEB397, 0x454056AC, 0xBA489527, 0x55533A3A, 0x20838D87, 0xFE6BA9B7, 0xD096954B,
|
||||
0x55A867BC, 0xA1159A58, 0xCCA92963, 0x99E1DB33, 0xA62A4A56, 0x3F3125F9, 0x5EF47E1C, 0x9029317C,
|
||||
0xFDF8E802, 0x04272F70, 0x80BB155C, 0x05282CE3, 0x95C11548, 0xE4C66D22, 0x48C1133F, 0xC70F86DC,
|
||||
0x07F9C9EE, 0x41041F0F, 0x404779A4, 0x5D886E17, 0x325F51EB, 0xD59BC0D1, 0xF2BCC18F, 0x41113564,
|
||||
0x257B7834, 0x602A9C60, 0xDFF8E8A3, 0x1F636C1B, 0x0E12B4C2, 0x02E1329E, 0xAF664FD1, 0xCAD18115,
|
||||
0x6B2395E0, 0x333E92E1, 0x3B240B62, 0xEEBEB922, 0x85B2A20E, 0xE6BA0D99, 0xDE720C8C, 0x2DA2F728,
|
||||
0xD0127845, 0x95B794FD, 0x647D0862, 0xE7CCF5F0, 0x5449A36F, 0x877D48FA, 0xC39DFD27, 0xF33E8D1E,
|
||||
0x0A476341, 0x992EFF74, 0x3A6F6EAB, 0xF4F8FD37, 0xA812DC60, 0xA1EBDDF8, 0x991BE14C, 0xDB6E6B0D,
|
||||
0xC67B5510, 0x6D672C37, 0x2765D43B, 0xDCD0E804, 0xF1290DC7, 0xCC00FFA3, 0xB5390F92, 0x690FED0B,
|
||||
0x667B9FFB, 0xCEDB7D9C, 0xA091CF0B, 0xD9155EA3, 0xBB132F88, 0x515BAD24, 0x7B9479BF, 0x763BD6EB,
|
||||
0x37392EB3, 0xCC115979, 0x8026E297, 0xF42E312D, 0x6842ADA7, 0xC66A2B3B, 0x12754CCC, 0x782EF11C,
|
||||
0x6A124237, 0xB79251E7, 0x06A1BBE6, 0x4BFB6350, 0x1A6B1018, 0x11CAEDFA, 0x3D25BDD8, 0xE2E1C3C9,
|
||||
0x44421659, 0x0A121386, 0xD90CEC6E, 0xD5ABEA2A, 0x64AF674E, 0xDA86A85F, 0xBEBFE988, 0x64E4C3FE,
|
||||
0x9DBC8057, 0xF0F7C086, 0x60787BF8, 0x6003604D, 0xD1FD8346, 0xF6381FB0, 0x7745AE04, 0xD736FCCC,
|
||||
0x83426B33, 0xF01EAB71, 0xB0804187, 0x3C005E5F, 0x77A057BE, 0xBDE8AE24, 0x55464299, 0xBF582E61,
|
||||
0x4E58F48F, 0xF2DDFDA2, 0xF474EF38, 0x8789BDC2, 0x5366F9C3, 0xC8B38E74, 0xB475F255, 0x46FCD9B9,
|
||||
0x7AEB2661, 0x8B1DDF84, 0x846A0E79, 0x915F95E2, 0x466E598E, 0x20B45770, 0x8CD55591, 0xC902DE4C,
|
||||
0xB90BACE1, 0xBB8205D0, 0x11A86248, 0x7574A99E, 0xB77F19B6, 0xE0A9DC09, 0x662D09A1, 0xC4324633,
|
||||
0xE85A1F02, 0x09F0BE8C, 0x4A99A025, 0x1D6EFE10, 0x1AB93D1D, 0x0BA5A4DF, 0xA186F20F, 0x2868F169,
|
||||
0xDCB7DA83, 0x573906FE, 0xA1E2CE9B, 0x4FCD7F52, 0x50115E01, 0xA70683FA, 0xA002B5C4, 0x0DE6D027,
|
||||
0x9AF88C27, 0x773F8641, 0xC3604C06, 0x61A806B5, 0xF0177A28, 0xC0F586E0, 0x006058AA, 0x30DC7D62,
|
||||
0x11E69ED7, 0x2338EA63, 0x53C2DD94, 0xC2C21634, 0xBBCBEE56, 0x90BCB6DE, 0xEBFC7DA1, 0xCE591D76,
|
||||
0x6F05E409, 0x4B7C0188, 0x39720A3D, 0x7C927C24, 0x86E3725F, 0x724D9DB9, 0x1AC15BB4, 0xD39EB8FC,
|
||||
0xED545578, 0x08FCA5B5, 0xD83D7CD3, 0x4DAD0FC4, 0x1E50EF5E, 0xB161E6F8, 0xA28514D9, 0x6C51133C,
|
||||
0x6FD5C7E7, 0x56E14EC4, 0x362ABFCE, 0xDDC6C837, 0xD79A3234, 0x92638212, 0x670EFA8E, 0x406000E0,
|
||||
},
|
||||
{ 0x3A39CE37, 0xD3FAF5CF, 0xABC27737, 0x5AC52D1B, 0x5CB0679E, 0x4FA33742, 0xD3822740, 0x99BC9BBE,
|
||||
0xD5118E9D, 0xBF0F7315, 0xD62D1C7E, 0xC700C47B, 0xB78C1B6B, 0x21A19045, 0xB26EB1BE, 0x6A366EB4,
|
||||
0x5748AB2F, 0xBC946E79, 0xC6A376D2, 0x6549C2C8, 0x530FF8EE, 0x468DDE7D, 0xD5730A1D, 0x4CD04DC6,
|
||||
0x2939BBDB, 0xA9BA4650, 0xAC9526E8, 0xBE5EE304, 0xA1FAD5F0, 0x6A2D519A, 0x63EF8CE2, 0x9A86EE22,
|
||||
0xC089C2B8, 0x43242EF6, 0xA51E03AA, 0x9CF2D0A4, 0x83C061BA, 0x9BE96A4D, 0x8FE51550, 0xBA645BD6,
|
||||
0x2826A2F9, 0xA73A3AE1, 0x4BA99586, 0xEF5562E9, 0xC72FEFD3, 0xF752F7DA, 0x3F046F69, 0x77FA0A59,
|
||||
0x80E4A915, 0x87B08601, 0x9B09E6AD, 0x3B3EE593, 0xE990FD5A, 0x9E34D797, 0x2CF0B7D9, 0x022B8B51,
|
||||
0x96D5AC3A, 0x017DA67D, 0xD1CF3ED6, 0x7C7D2D28, 0x1F9F25CF, 0xADF2B89B, 0x5AD6B472, 0x5A88F54C,
|
||||
0xE029AC71, 0xE019A5E6, 0x47B0ACFD, 0xED93FA9B, 0xE8D3C48D, 0x283B57CC, 0xF8D56629, 0x79132E28,
|
||||
0x785F0191, 0xED756055, 0xF7960E44, 0xE3D35E8C, 0x15056DD4, 0x88F46DBA, 0x03A16125, 0x0564F0BD,
|
||||
0xC3EB9E15, 0x3C9057A2, 0x97271AEC, 0xA93A072A, 0x1B3F6D9B, 0x1E6321F5, 0xF59C66FB, 0x26DCF319,
|
||||
0x7533D928, 0xB155FDF5, 0x03563482, 0x8ABA3CBB, 0x28517711, 0xC20AD9F8, 0xABCC5167, 0xCCAD925F,
|
||||
0x4DE81751, 0x3830DC8E, 0x379D5862, 0x9320F991, 0xEA7A90C2, 0xFB3E7BCE, 0x5121CE64, 0x774FBE32,
|
||||
0xA8B6E37E, 0xC3293D46, 0x48DE5369, 0x6413E680, 0xA2AE0810, 0xDD6DB224, 0x69852DFD, 0x09072166,
|
||||
0xB39A460A, 0x6445C0DD, 0x586CDECF, 0x1C20C8AE, 0x5BBEF7DD, 0x1B588D40, 0xCCD2017F, 0x6BB4E3BB,
|
||||
0xDDA26A7E, 0x3A59FF45, 0x3E350A44, 0xBCB4CDD5, 0x72EACEA8, 0xFA6484BB, 0x8D6612AE, 0xBF3C6F47,
|
||||
0xD29BE463, 0x542F5D9E, 0xAEC2771B, 0xF64E6370, 0x740E0D8D, 0xE75B1357, 0xF8721671, 0xAF537D5D,
|
||||
0x4040CB08, 0x4EB4E2CC, 0x34D2466A, 0x0115AF84, 0xE1B00428, 0x95983A1D, 0x06B89FB4, 0xCE6EA048,
|
||||
0x6F3F3B82, 0x3520AB82, 0x011A1D4B, 0x277227F8, 0x611560B1, 0xE7933FDC, 0xBB3A792B, 0x344525BD,
|
||||
0xA08839E1, 0x51CE794B, 0x2F32C9B7, 0xA01FBAC9, 0xE01CC87E, 0xBCC7D1F6, 0xCF0111C3, 0xA1E8AAC7,
|
||||
0x1A908749, 0xD44FBD9A, 0xD0DADECB, 0xD50ADA38, 0x0339C32A, 0xC6913667, 0x8DF9317C, 0xE0B12B4F,
|
||||
0xF79E59B7, 0x43F5BB3A, 0xF2D519FF, 0x27D9459C, 0xBF97222C, 0x15E6FC2A, 0x0F91FC71, 0x9B941525,
|
||||
0xFAE59361, 0xCEB69CEB, 0xC2A86459, 0x12BAA8D1, 0xB6C1075E, 0xE3056A0C, 0x10D25065, 0xCB03A442,
|
||||
0xE0EC6E0E, 0x1698DB3B, 0x4C98A0BE, 0x3278E964, 0x9F1F9532, 0xE0D392DF, 0xD3A0342B, 0x8971F21E,
|
||||
0x1B0A7441, 0x4BA3348C, 0xC5BE7120, 0xC37632D8, 0xDF359F8D, 0x9B992F2E, 0xE60B6F47, 0x0FE3F11D,
|
||||
0xE54CDA54, 0x1EDAD891, 0xCE6279CF, 0xCD3E7E6F, 0x1618B166, 0xFD2C1D05, 0x848FD2C5, 0xF6FB2299,
|
||||
0xF523F357, 0xA6327623, 0x93A83531, 0x56CCCD02, 0xACF08162, 0x5A75EBB5, 0x6E163697, 0x88D273CC,
|
||||
0xDE966292, 0x81B949D0, 0x4C50901B, 0x71C65614, 0xE6C6C7BD, 0x327A140A, 0x45E1D006, 0xC3F27B9A,
|
||||
0xC9AA53FD, 0x62A80F00, 0xBB25BFE2, 0x35BDD2F6, 0x71126905, 0xB2040222, 0xB6CBCF7C, 0xCD769C2B,
|
||||
0x53113EC0, 0x1640E3D3, 0x38ABBD60, 0x2547ADF0, 0xBA38209C, 0xF746CE76, 0x77AFA1C5, 0x20756060,
|
||||
0x85CBFE4E, 0x8AE88DD8, 0x7AAAF9B0, 0x4CF9AA7E, 0x1948C25C, 0x02FB8A8C, 0x01C36AE4, 0xD6EBE1F9,
|
||||
0x90D4F869, 0xA65CDEA0, 0x3F09252D, 0xC208E69F, 0xB74E6132, 0xCE77E25B, 0x578FDFE3, 0x3AC372E6,
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static uint32_t blowfish_F(blowfish_ctx* ctx, uint32_t x) {
|
||||
uint8_t a, b, c, d;
|
||||
|
||||
/* divide xl into four eight-bit quarters */
|
||||
a = (x >> 24) & 0xFF;
|
||||
b = (x >> 16) & 0xFF;
|
||||
c = (x >> 8) & 0xFF;
|
||||
d = (x >> 0) & 0xFF;
|
||||
|
||||
return ((ctx->S[0][a] + ctx->S[1][b]) ^ ctx->S[2][c]) + ctx->S[3][d];
|
||||
}
|
||||
|
||||
|
||||
void blowfish_encrypt(blowfish_ctx* ctx, uint32_t* p_xl, uint32_t* p_xr){
|
||||
uint32_t xl, xr, tmp;
|
||||
|
||||
/* divide x into two 32-bit halves (external) */
|
||||
xl = *p_xl;
|
||||
xr = *p_xr;
|
||||
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
xl = xl ^ ctx->P[i];
|
||||
xr = blowfish_F(ctx, xl) ^ xr;
|
||||
|
||||
/* swap */
|
||||
tmp = xl;
|
||||
xl = xr;
|
||||
xr = tmp;
|
||||
}
|
||||
|
||||
/* swap (undo last swap) */
|
||||
tmp = xl;
|
||||
xl = xr;
|
||||
xr = tmp;
|
||||
|
||||
xr = xr ^ ctx->P[16 + 0];
|
||||
xl = xl ^ ctx->P[16 + 1];
|
||||
|
||||
/* recombine (external) */
|
||||
*p_xl = xl;
|
||||
*p_xr = xr;
|
||||
}
|
||||
|
||||
|
||||
/* same as encryption but in reverse order */
|
||||
void blowfish_decrypt(blowfish_ctx* ctx, uint32_t* p_xl, uint32_t* p_xr) {
|
||||
uint32_t xl, xr, tmp;
|
||||
|
||||
/* divide x into two 32-bit halves (external) */
|
||||
xl = *p_xl;
|
||||
xr = *p_xr;
|
||||
|
||||
for (int i = 16 + 1; i > 1; --i) {
|
||||
xl = xl ^ ctx->P[i];
|
||||
xr = blowfish_F(ctx, xl) ^ xr;
|
||||
|
||||
/* swap */
|
||||
tmp = xl;
|
||||
xl = xr;
|
||||
xr = tmp;
|
||||
}
|
||||
|
||||
/* swap */
|
||||
tmp = xl;
|
||||
xl = xr;
|
||||
xr = tmp;
|
||||
|
||||
xr = xr ^ ctx->P[1];
|
||||
xl = xl ^ ctx->P[0];
|
||||
|
||||
/* recombine (external) */
|
||||
*p_xl = xl;
|
||||
*p_xr = xr;
|
||||
}
|
||||
|
||||
|
||||
blowfish_ctx* blowfish_init_ecb(uint8_t* key, int32_t key_len) {
|
||||
uint32_t xl, xr;
|
||||
uint8_t tmpkey[18*4];
|
||||
|
||||
blowfish_ctx* ctx = malloc(sizeof(blowfish_ctx));
|
||||
if (!ctx) return NULL;
|
||||
|
||||
|
||||
/* preload key to simplify usage below; repeat if key is short */
|
||||
for (int i = 0; i < sizeof(tmpkey); i++) {
|
||||
tmpkey[i] = key[i % key_len];
|
||||
}
|
||||
|
||||
/* initialize p-array by XORing Pn by 32-bits of key */
|
||||
for (int i = 0; i < 18; i++) {
|
||||
uint32_t tmp = get_u32be(tmpkey + i * 4);
|
||||
ctx->P[i] = INIT_P[i] ^ tmp;
|
||||
}
|
||||
|
||||
/* initialize four S-boxes for use below */
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < 256; j++) {
|
||||
ctx->S[i][j] = INIT_S[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
/* encrypt 0s with blowfish, using the calculated P subkeys, and replace Pn with each step */
|
||||
xl = 0x00000000;
|
||||
xr = 0x00000000;
|
||||
for (int i = 0; i < 18; i += 2) {
|
||||
blowfish_encrypt(ctx, &xl, &xr);
|
||||
/* Replace P1 and P2 with the output of step */
|
||||
ctx->P[i + 0] = xl;
|
||||
ctx->P[i + 1] = xr;
|
||||
}
|
||||
|
||||
/* continue with four S-boxes in order with the continuous output */
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < 256; j += 2) {
|
||||
blowfish_encrypt(ctx, &xl, &xr);
|
||||
ctx->S[i][j + 0] = xl;
|
||||
ctx->S[i][j + 1] = xr;
|
||||
}
|
||||
}
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
|
||||
void blowfish_free(blowfish_ctx* ctx) {
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
|
||||
void blowfish_decrypt_ecb(blowfish_ctx* ctx, uint8_t* block) {
|
||||
uint32_t xl = get_u32be(block + 0x00);
|
||||
uint32_t xr = get_u32be(block + 0x04);
|
||||
blowfish_decrypt(ctx, &xl, &xr);
|
||||
put_u32be(block + 0x00, xl);
|
||||
put_u32be(block + 0x04, xr);
|
||||
}
|
14
Frameworks/vgmstream/vgmstream/src/util/cipher_blowfish.h
Normal file
14
Frameworks/vgmstream/vgmstream/src/util/cipher_blowfish.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
#ifndef _CIPHER_BLOWFISH_H_
|
||||
#define _CIPHER_BLOWFISH_H_
|
||||
#include <inttypes.h>
|
||||
|
||||
typedef struct blowfish_ctx blowfish_ctx;
|
||||
|
||||
blowfish_ctx* blowfish_init_ecb(uint8_t* key, int key_len);
|
||||
void blowfish_encrypt(blowfish_ctx *ctx, uint32_t* xl, uint32_t* xr);
|
||||
void blowfish_decrypt(blowfish_ctx *ctx, uint32_t* xl, uint32_t* xr);
|
||||
void blowfish_free(blowfish_ctx* ctx);
|
||||
|
||||
/* assumed block size is at least 0x08 */
|
||||
void blowfish_decrypt_ecb(blowfish_ctx* ctx, uint8_t* block);
|
||||
#endif
|
|
@ -2,9 +2,17 @@
|
|||
|
||||
#include "../vgmstream.h"
|
||||
#include "../layout/layout.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
bool layered_add_codec(VGMSTREAM* vs, int layers, int layer_channels) {
|
||||
if (!vs || !vs->codec_data) {
|
||||
|
||||
typedef VGMSTREAM* (*init_vgmstream_t)(STREAMFILE*);
|
||||
|
||||
bool layered_add_subfile(VGMSTREAM* vs, int layers, int layer_channels, STREAMFILE* sf, uint32_t offset, uint32_t size, const char* ext, init_vgmstream_t init_vgmstream) {
|
||||
int i;
|
||||
layered_layout_data* data;
|
||||
STREAMFILE* temp_sf;
|
||||
|
||||
if (!vs) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
@ -13,9 +21,54 @@ bool layered_add_codec(VGMSTREAM* vs, int layers, int layer_channels) {
|
|||
if (!layers)
|
||||
layers = vs->channels / layer_channels;
|
||||
|
||||
switch(vs->layout_type) {
|
||||
case layout_segmented: //to be improved
|
||||
goto fail;
|
||||
|
||||
case layout_layered:
|
||||
data = vs->layout_data;
|
||||
|
||||
i = data->curr_layer;
|
||||
break;
|
||||
|
||||
default:
|
||||
data = init_layout_layered(layers);
|
||||
if (!data) goto fail;
|
||||
vs->layout_data = data;
|
||||
vs->layout_type = layout_layered;
|
||||
|
||||
i = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
temp_sf = setup_subfile_streamfile(sf, offset, size, ext);
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
data->layers[i] = init_vgmstream(temp_sf);
|
||||
close_streamfile(temp_sf);
|
||||
if (!data->layers[i]) goto fail;
|
||||
|
||||
data->curr_layer++;
|
||||
|
||||
return true;
|
||||
fail:
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static bool layered_add_internal(VGMSTREAM* vs, int layers, int layer_channels, STREAMFILE* sf) {
|
||||
int i;
|
||||
layered_layout_data* data;
|
||||
|
||||
if (!vs) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!layer_channels)
|
||||
layer_channels = 1;
|
||||
if (!layers)
|
||||
layers = vs->channels / layer_channels;
|
||||
|
||||
switch(vs->layout_type) {
|
||||
case layout_segmented: //to be improved
|
||||
goto fail;
|
||||
|
@ -46,12 +99,20 @@ bool layered_add_codec(VGMSTREAM* vs, int layers, int layer_channels) {
|
|||
data->layers[i]->loop_end_sample = vs->loop_end_sample;
|
||||
|
||||
data->layers[i]->codec_data = vs->codec_data;
|
||||
if (!data->layers[i]->codec_data) goto fail;
|
||||
data->layers[i]->coding_type = vs->coding_type;
|
||||
|
||||
data->layers[i]->layout_type = layout_none;
|
||||
data->layers[i]->interleave_block_size = vs->interleave_block_size;
|
||||
if (vs->interleave_block_size)
|
||||
data->layers[i]->layout_type = layout_interleave;
|
||||
|
||||
vs->codec_data = NULL; /* moved to layer, don't hold it */
|
||||
|
||||
if (sf) {
|
||||
if (!vgmstream_open_stream(data->layers[i], sf, 0x00))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
data->curr_layer++;
|
||||
|
||||
return true;
|
||||
|
@ -59,12 +120,29 @@ fail:
|
|||
return false;
|
||||
}
|
||||
|
||||
bool layered_add_sf(VGMSTREAM* vs, int layers, int layer_channels, STREAMFILE* sf) {
|
||||
return layered_add_internal(vs, layers, layer_channels, sf);
|
||||
}
|
||||
|
||||
bool layered_add_codec(VGMSTREAM* vs, int layers, int layer_channels) {
|
||||
if (!vs->codec_data)
|
||||
return false;
|
||||
|
||||
return layered_add_internal(vs, layers, layer_channels, NULL);
|
||||
}
|
||||
|
||||
|
||||
bool layered_add_done(VGMSTREAM* vs) {
|
||||
//TODO: some extra checks/setup?
|
||||
|
||||
if (!setup_layout_layered(vs->layout_data))
|
||||
goto fail;
|
||||
|
||||
if (!vs->coding_type) {
|
||||
layered_layout_data* data = vs->layout_data;
|
||||
vs->coding_type = data->layers[0]->coding_type;
|
||||
}
|
||||
|
||||
return true;
|
||||
fail:
|
||||
return false;
|
||||
|
|
|
@ -3,6 +3,15 @@
|
|||
|
||||
#include "../vgmstream.h"
|
||||
|
||||
typedef VGMSTREAM* (*init_vgmstream_t)(STREAMFILE*);
|
||||
|
||||
/* add a new layer from subfile (setups layout if needed) */
|
||||
bool layered_add_subfile(VGMSTREAM* vs, int layers, int layer_channels, STREAMFILE* sf, uint32_t offset, uint32_t size, const char* ext, init_vgmstream_t init_vgmstream);
|
||||
|
||||
|
||||
/* add a new layer from base vgmstream (setups layout if needed) */
|
||||
bool layered_add_sf(VGMSTREAM* vs, int layers, int layer_channels, STREAMFILE* sf);
|
||||
|
||||
/* add a new layer from codec data (setups layout if needed)
|
||||
* codec is passed in the vs for easier free/etc control */
|
||||
bool layered_add_codec(VGMSTREAM* vs, int layers, int layer_channels);
|
||||
|
|
|
@ -67,7 +67,7 @@ void vgm_log_set_callback(void* ctx_p, int level, int type, void* callback);
|
|||
do { \
|
||||
int i; \
|
||||
for (i=0; i < buf_size; i++) { \
|
||||
printf("%02x",buf[i]); \
|
||||
printf("%02x",buf[i] & 0xFF); \
|
||||
if (bytes_per_line && (i+1) % bytes_per_line == 0) printf("\n"); \
|
||||
} \
|
||||
printf("\n"); \
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
#ifndef _READER_PUT_H
|
||||
#define _READER_PUT_H
|
||||
|
||||
#ifdef BUILD_VGMSTREAM
|
||||
#include "../streamtypes.h"
|
||||
#else
|
||||
#include <libvgmstream/streamtypes.h>
|
||||
#endif
|
||||
#include "streamtypes.h"
|
||||
|
||||
|
||||
void put_8bit(uint8_t* buf, int8_t i);
|
||||
|
|
|
@ -308,7 +308,6 @@ init_vgmstream_t init_vgmstream_functions[] = {
|
|||
init_vgmstream_opus_shinen,
|
||||
init_vgmstream_opus_nus3,
|
||||
init_vgmstream_opus_sps_n1,
|
||||
init_vgmstream_opus_nxa,
|
||||
init_vgmstream_pc_ast,
|
||||
init_vgmstream_naac,
|
||||
init_vgmstream_ubi_sb,
|
||||
|
@ -386,6 +385,7 @@ init_vgmstream_t init_vgmstream_functions[] = {
|
|||
init_vgmstream_xau_konami,
|
||||
init_vgmstream_derf,
|
||||
init_vgmstream_utk,
|
||||
init_vgmstream_nxa1,
|
||||
init_vgmstream_adpcm_capcom,
|
||||
init_vgmstream_ue4opus,
|
||||
init_vgmstream_xwma,
|
||||
|
@ -458,6 +458,7 @@ init_vgmstream_t init_vgmstream_functions[] = {
|
|||
init_vgmstream_diva,
|
||||
init_vgmstream_imuse,
|
||||
init_vgmstream_ktsr,
|
||||
init_vgmstream_asrs,
|
||||
init_vgmstream_mups,
|
||||
init_vgmstream_kat,
|
||||
init_vgmstream_pcm_success,
|
||||
|
@ -521,6 +522,7 @@ init_vgmstream_t init_vgmstream_functions[] = {
|
|||
init_vgmstream_squeaksample,
|
||||
init_vgmstream_snds,
|
||||
init_vgmstream_adm2,
|
||||
init_vgmstream_nxof,
|
||||
|
||||
/* lower priority metas (no clean header identity, somewhat ambiguous, or need extension/companion file to identify) */
|
||||
init_vgmstream_scd_pcm,
|
||||
|
@ -614,12 +616,6 @@ static VGMSTREAM* init_vgmstream_internal(STREAMFILE* sf) {
|
|||
try_dual_file_stereo(vgmstream, sf, init_vgmstream_function);
|
||||
}
|
||||
|
||||
/* clean as loops are readable metadata but loop fields may contain garbage
|
||||
* (done *after* dual stereo as it needs loop fields to match) */
|
||||
if (!vgmstream->loop_flag) {
|
||||
vgmstream->loop_start_sample = 0;
|
||||
vgmstream->loop_end_sample = 0;
|
||||
}
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
/* check FFmpeg streams here, for lack of a better place */
|
||||
|
@ -676,6 +672,28 @@ static VGMSTREAM* init_vgmstream_internal(STREAMFILE* sf) {
|
|||
|
||||
void setup_vgmstream(VGMSTREAM* vgmstream) {
|
||||
|
||||
//TODO improve cleanup (done here to handle manually added layers)
|
||||
|
||||
/* sanify loops and remove bad metadata (some layouts will behave incorrectly) */
|
||||
if (vgmstream->loop_flag) {
|
||||
if (vgmstream->loop_end_sample <= vgmstream->loop_start_sample
|
||||
|| vgmstream->loop_end_sample > vgmstream->num_samples
|
||||
|| vgmstream->loop_start_sample < 0) {
|
||||
VGM_LOG("VGMSTREAM: wrong loops ignored (lss=%i, lse=%i, ns=%i)\n",
|
||||
vgmstream->loop_start_sample, vgmstream->loop_end_sample, vgmstream->num_samples);
|
||||
vgmstream->loop_flag = 0;
|
||||
vgmstream->loop_start_sample = 0;
|
||||
vgmstream->loop_end_sample = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* clean as loops are readable metadata but loop fields may contain garbage
|
||||
* (done *after* dual stereo as it needs loop fields to match) */
|
||||
if (!vgmstream->loop_flag) {
|
||||
vgmstream->loop_start_sample = 0;
|
||||
vgmstream->loop_end_sample = 0;
|
||||
}
|
||||
|
||||
/* save start things so we can restart when seeking */
|
||||
memcpy(vgmstream->start_ch, vgmstream->ch, sizeof(VGMSTREAMCHANNEL)*vgmstream->channels);
|
||||
memcpy(vgmstream->start_vgmstream, vgmstream, sizeof(VGMSTREAM));
|
||||
|
|
|
@ -90,6 +90,7 @@ typedef enum {
|
|||
coding_H4M_IMA, /* H4M IMA ADPCM (stereo or mono, high nibble first) */
|
||||
coding_MTF_IMA, /* Capcom MT Framework IMA ADPCM */
|
||||
coding_CD_IMA, /* Crystal Dynamics IMA ADPCM */
|
||||
coding_CRANKCASE_IMA, /* CrankcaseAudio REV IMA ADPCM */
|
||||
|
||||
coding_MSADPCM, /* Microsoft ADPCM (stereo/mono) */
|
||||
coding_MSADPCM_int, /* Microsoft ADPCM (mono) */
|
||||
|
@ -611,7 +612,7 @@ typedef enum {
|
|||
meta_DERF, /* Stupid Invaders (PC) */
|
||||
meta_SADF,
|
||||
meta_UTK,
|
||||
meta_NXA,
|
||||
meta_NXA1,
|
||||
meta_ADPCM_CAPCOM,
|
||||
meta_UE4OPUS,
|
||||
meta_XWMA,
|
||||
|
@ -701,6 +702,7 @@ typedef enum {
|
|||
meta_SQUEAKSTREAM,
|
||||
meta_SQUEAKSAMPLE,
|
||||
meta_SNDS,
|
||||
meta_NXOF,
|
||||
|
||||
} meta_t;
|
||||
|
||||
|
|
Loading…
Reference in a new issue