Update vgmstream to r1896 #390

Closed
faxx1080 wants to merge 3 commits from vgmstream-r1896 into main
52 changed files with 2256 additions and 729 deletions

View file

@ -7,6 +7,15 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* 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 */; }; 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 */; }; 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 */; }; 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 */; }; 8306B0DB20984590000302D4 /* nxap.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0C02098458C000302D4 /* nxap.c */; };
8306B0DC20984590000302D4 /* sthd.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0C12098458C000302D4 /* sthd.c */; }; 8306B0DC20984590000302D4 /* sthd.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0C12098458C000302D4 /* sthd.c */; };
8306B0DD20984590000302D4 /* waf.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0C22098458C000302D4 /* waf.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 */; }; 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 */; }; 8306B0E020984590000302D4 /* ppst_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 8306B0C52098458D000302D4 /* ppst_streamfile.h */; };
8306B0E220984590000302D4 /* smv.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0C72098458D000302D4 /* smv.c */; }; 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 */; }; 83269DD22399F5DE00F49FE3 /* nus3bank_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 83269DD02399F5DD00F49FE3 /* nus3bank_streamfile.h */; };
83269DD32399F5DE00F49FE3 /* ivag.c in Sources */ = {isa = PBXBuildFile; fileRef = 83269DD12399F5DE00F49FE3 /* ivag.c */; }; 83269DD32399F5DE00F49FE3 /* ivag.c in Sources */ = {isa = PBXBuildFile; fileRef = 83269DD12399F5DE00F49FE3 /* ivag.c */; };
83299FD01E7660C7003A3242 /* bik.c in Sources */ = {isa = PBXBuildFile; fileRef = 83299FCE1E7660C7003A3242 /* bik.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 */; }; 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 */; }; 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 */; }; 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 */; }; 8349A9121FE6258200E26435 /* vsf_tta.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8F91FE6257E00E26435 /* vsf_tta.c */; };
8349A9141FE6258200E26435 /* omu.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8FB1FE6257F00E26435 /* omu.c */; }; 8349A9141FE6258200E26435 /* omu.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8FB1FE6257F00E26435 /* omu.c */; };
8349A9161FE6258200E26435 /* flx.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8FD1FE6257F00E26435 /* flx.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 */; }; 8349A9181FE6258200E26435 /* ea_1snh.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8FF1FE6258000E26435 /* ea_1snh.c */; };
8349A9191FE6258200E26435 /* afc.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A9001FE6258000E26435 /* afc.c */; }; 8349A9191FE6258200E26435 /* afc.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A9001FE6258000E26435 /* afc.c */; };
8349A91A1FE6258200E26435 /* vxn.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A9011FE6258000E26435 /* vxn.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 */; }; 834FE101215C79ED000A5D3D /* csmp.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0D8215C79EA000A5D3D /* csmp.c */; };
834FE102215C79ED000A5D3D /* rfrm.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0D9215C79EA000A5D3D /* rfrm.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 */; }; 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 */; }; 834FE105215C79ED000A5D3D /* xmd.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0DC215C79EA000A5D3D /* xmd.c */; };
834FE106215C79ED000A5D3D /* utk.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0DD215C79EB000A5D3D /* utk.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 */; }; 834FE107215C79ED000A5D3D /* mib_mih.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0DE215C79EB000A5D3D /* mib_mih.c */; };
@ -852,6 +857,15 @@
/* End PBXCopyFilesBuildPhase section */ /* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 834FE0DE215C79EB000A5D3D /* mib_mih.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mib_mih.c; sourceTree = "<group>"; };
@ -2001,6 +2011,13 @@
836F6E2718BDC2180095E648 /* meta */ = { 836F6E2718BDC2180095E648 /* meta */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( 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 */, 836F6E2918BDC2180095E648 /* 2dx9.c */,
83C727FD22BC893900678B4A /* 9tav_streamfile.h */, 83C727FD22BC893900678B4A /* 9tav_streamfile.h */,
83C727FE22BC893900678B4A /* 9tav.c */, 83C727FE22BC893900678B4A /* 9tav.c */,
@ -2042,7 +2059,6 @@
83A21F7C201D897F000F04B9 /* atx.c */, 83A21F7C201D897F000F04B9 /* atx.c */,
83EED5D2203A8BC7008BEB45 /* aus.c */, 83EED5D2203A8BC7008BEB45 /* aus.c */,
83A16D2722D2ADE700B90C4C /* awb.c */, 83A16D2722D2ADE700B90C4C /* awb.c */,
8306B0C32098458C000302D4 /* awc_xma_streamfile.h */,
83AA5D201F6E2F9B0020821C /* awc.c */, 83AA5D201F6E2F9B0020821C /* awc.c */,
83FBB1702A4FF4EC00CD0580 /* awd.c */, 83FBB1702A4FF4EC00CD0580 /* awd.c */,
836F6E3618BDC2180095E648 /* baf.c */, 836F6E3618BDC2180095E648 /* baf.c */,
@ -2089,7 +2105,6 @@
836F6E4318BDC2180095E648 /* dmsg_segh.c */, 836F6E4318BDC2180095E648 /* dmsg_segh.c */,
83AA7F742519C041004C5298 /* dsb.c */, 83AA7F742519C041004C5298 /* dsb.c */,
8351F32C2212B57000A606E4 /* dsf.c */, 8351F32C2212B57000A606E4 /* dsf.c */,
83299FCF1E7660C7003A3242 /* dsp_adx.c */,
8349A8FF1FE6258000E26435 /* ea_1snh.c */, 8349A8FF1FE6258000E26435 /* ea_1snh.c */,
83AFABBA23795202002F3947 /* ea_eaac_opus_streamfile.h */, 83AFABBA23795202002F3947 /* ea_eaac_opus_streamfile.h */,
8306B0BD2098458B000302D4 /* ea_eaac_streamfile.h */, 8306B0BD2098458B000302D4 /* ea_eaac_streamfile.h */,
@ -2236,7 +2251,6 @@
834FE0D1215C79E9000A5D3D /* nus3bank.c */, 834FE0D1215C79E9000A5D3D /* nus3bank.c */,
836F6E8118BDC2180095E648 /* nwa.c */, 836F6E8118BDC2180095E648 /* nwa.c */,
832BF81421E0514A006F50F1 /* nwav.c */, 832BF81421E0514A006F50F1 /* nwav.c */,
834FE0DB215C79EA000A5D3D /* nxa.c */,
8306B0C02098458C000302D4 /* nxap.c */, 8306B0C02098458C000302D4 /* nxap.c */,
832BF81321E05149006F50F1 /* ogg_opus.c */, 832BF81321E05149006F50F1 /* ogg_opus.c */,
835C883522CC17BE001B4B3F /* ogg_vorbis_streamfile.h */, 835C883522CC17BE001B4B3F /* ogg_vorbis_streamfile.h */,
@ -2249,7 +2263,6 @@
836F6E8318BDC2180095E648 /* otm.c */, 836F6E8318BDC2180095E648 /* otm.c */,
836F6E8418BDC2180095E648 /* p3d.c */, 836F6E8418BDC2180095E648 /* p3d.c */,
831BA6171EAC61A500CF89B0 /* pasx.c */, 831BA6171EAC61A500CF89B0 /* pasx.c */,
8349A8FE1FE6257F00E26435 /* pc_adp_otns.c */,
8349A8F01FE6257C00E26435 /* pc_ast.c */, 8349A8F01FE6257C00E26435 /* pc_ast.c */,
836F6E8618BDC2180095E648 /* pc_mxst.c */, 836F6E8618BDC2180095E648 /* pc_mxst.c */,
8306B0D12098458F000302D4 /* pcm_sre.c */, 8306B0D12098458F000302D4 /* pcm_sre.c */,
@ -2483,6 +2496,8 @@
83D26A7C26E66DC2001A9475 /* util */ = { 83D26A7C26E66DC2001A9475 /* util */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
01BC2CC02B5C31480026C0A4 /* cipher_blowfish.c */,
01BC2CC12B5C31480026C0A4 /* cipher_blowfish.h */,
833E82F92A28595A00CD0580 /* bitstream_lsb.h */, 833E82F92A28595A00CD0580 /* bitstream_lsb.h */,
836DF620298F83F400CD0580 /* bitstream_msb.h */, 836DF620298F83F400CD0580 /* bitstream_msb.h */,
833E82C72A28566700CD0580 /* channel_mappings.h */, 833E82C72A28566700CD0580 /* channel_mappings.h */,
@ -2588,6 +2603,7 @@
83256CD328666C620036D9C0 /* getcpuflags.h in Headers */, 83256CD328666C620036D9C0 /* getcpuflags.h in Headers */,
8349A90F1FE6258200E26435 /* aix_streamfile.h in Headers */, 8349A90F1FE6258200E26435 /* aix_streamfile.h in Headers */,
83B46FD52707FB9A00847FC9 /* endianness.h in Headers */, 83B46FD52707FB9A00847FC9 /* endianness.h in Headers */,
01BC2CBF2B5C300C0026C0A4 /* str_wav_streamfile.h in Headers */,
836F705718BDC2190095E648 /* util.h in Headers */, 836F705718BDC2190095E648 /* util.h in Headers */,
83256CC128666C620036D9C0 /* index.h in Headers */, 83256CC128666C620036D9C0 /* index.h in Headers */,
834FE0ED215C79ED000A5D3D /* fsb_interleave_streamfile.h in Headers */, 834FE0ED215C79ED000A5D3D /* fsb_interleave_streamfile.h in Headers */,
@ -2615,6 +2631,7 @@
8375737721F950ED00F01AF5 /* ubi_sb_streamfile.h in Headers */, 8375737721F950ED00F01AF5 /* ubi_sb_streamfile.h in Headers */,
839E21E01F2EDAF100EE54D7 /* vorbis_custom_data_fsb.h in Headers */, 839E21E01F2EDAF100EE54D7 /* vorbis_custom_data_fsb.h in Headers */,
833E82F42A28587D00CD0580 /* companion_files.h in Headers */, 833E82F42A28587D00CD0580 /* companion_files.h in Headers */,
01BC2CBA2B5C300C0026C0A4 /* ktsr_streamfile.h in Headers */,
83031EDB243C510500C3F3E0 /* xnb_lz4mg.h in Headers */, 83031EDB243C510500C3F3E0 /* xnb_lz4mg.h in Headers */,
836F705418BDC2190095E648 /* streamfile.h in Headers */, 836F705418BDC2190095E648 /* streamfile.h in Headers */,
835B9B922730BF2D00F87EE3 /* hca_bf.h in Headers */, 835B9B922730BF2D00F87EE3 /* hca_bf.h in Headers */,
@ -2636,14 +2653,15 @@
83C0C7692AA437E10056AFD8 /* mixing_fades.h in Headers */, 83C0C7692AA437E10056AFD8 /* mixing_fades.h in Headers */,
839E21E51F2EDAF100EE54D7 /* vorbis_custom_decoder.h in Headers */, 839E21E51F2EDAF100EE54D7 /* vorbis_custom_decoder.h in Headers */,
833E82C82A28566700CD0580 /* channel_mappings.h in Headers */, 833E82C82A28566700CD0580 /* channel_mappings.h in Headers */,
01BC2CBD2B5C300C0026C0A4 /* awc_streamfile.h in Headers */,
83256CC028666C620036D9C0 /* synth_8bit.h in Headers */, 83256CC028666C620036D9C0 /* synth_8bit.h in Headers */,
833E82F32A28587D00CD0580 /* reader_sf.h in Headers */, 833E82F32A28587D00CD0580 /* reader_sf.h in Headers */,
837CEADB23487E8300E62A4A /* bgw_streamfile.h in Headers */, 837CEADB23487E8300E62A4A /* bgw_streamfile.h in Headers */,
01BC2CC32B5C31480026C0A4 /* cipher_blowfish.h in Headers */,
837CEB0323487F2C00E62A4A /* xavs_streamfile.h in Headers */, 837CEB0323487F2C00E62A4A /* xavs_streamfile.h in Headers */,
836F705918BDC2190095E648 /* vgmstream.h in Headers */, 836F705918BDC2190095E648 /* vgmstream.h in Headers */,
8373342623F60CDC00DE14DC /* deblock_streamfile.h in Headers */, 8373342623F60CDC00DE14DC /* deblock_streamfile.h in Headers */,
83C7281522BC893D00678B4A /* txth_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 */, 83031EC5243C50A800C3F3E0 /* circus_decoder_lib_data.h in Headers */,
834FE0B5215C798C000A5D3D /* acm_decoder_libacm.h in Headers */, 834FE0B5215C798C000A5D3D /* acm_decoder_libacm.h in Headers */,
83256CE528666C620036D9C0 /* true.h in Headers */, 83256CE528666C620036D9C0 /* true.h in Headers */,
@ -2837,7 +2855,6 @@
files = ( files = (
839E21E21F2EDAF100EE54D7 /* vorbis_custom_utils_wwise.c in Sources */, 839E21E21F2EDAF100EE54D7 /* vorbis_custom_utils_wwise.c in Sources */,
83E56BA51F2EE3520026BC60 /* vorbis_custom_utils_ogl.c in Sources */, 83E56BA51F2EE3520026BC60 /* vorbis_custom_utils_ogl.c in Sources */,
834FE104215C79ED000A5D3D /* nxa.c in Sources */,
8349A9071FE6258200E26435 /* dec.c in Sources */, 8349A9071FE6258200E26435 /* dec.c in Sources */,
839E21E91F2EDAF100EE54D7 /* vorbis_custom_utils_sk.c in Sources */, 839E21E91F2EDAF100EE54D7 /* vorbis_custom_utils_sk.c in Sources */,
83C7281422BC893D00678B4A /* ffdl.c in Sources */, 83C7281422BC893D00678B4A /* ffdl.c in Sources */,
@ -2914,6 +2931,7 @@
836F6F7018BDC2190095E648 /* apple_caff.c in Sources */, 836F6F7018BDC2190095E648 /* apple_caff.c in Sources */,
836F6FB618BDC2190095E648 /* ngc_sck_dsp.c in Sources */, 836F6FB618BDC2190095E648 /* ngc_sck_dsp.c in Sources */,
836F6F2818BDC2190095E648 /* ima_decoder.c in Sources */, 836F6F2818BDC2190095E648 /* ima_decoder.c in Sources */,
01BC2CC22B5C31480026C0A4 /* cipher_blowfish.c in Sources */,
837CEB072348809400E62A4A /* seb.c in Sources */, 837CEB072348809400E62A4A /* seb.c in Sources */,
83D26A8126E66DC2001A9475 /* log.c in Sources */, 83D26A8126E66DC2001A9475 /* log.c in Sources */,
8306B0DD20984590000302D4 /* waf.c in Sources */, 8306B0DD20984590000302D4 /* waf.c in Sources */,
@ -2945,7 +2963,6 @@
839FBFFB26C354E70016A78A /* wxd_wxh.c in Sources */, 839FBFFB26C354E70016A78A /* wxd_wxh.c in Sources */,
83EDE5D91A70951A005F5D84 /* btsnd.c in Sources */, 83EDE5D91A70951A005F5D84 /* btsnd.c in Sources */,
8306B0E420984590000302D4 /* wave.c in Sources */, 8306B0E420984590000302D4 /* wave.c in Sources */,
8349A9171FE6258200E26435 /* pc_adp_otns.c in Sources */,
836F701E18BDC2190095E648 /* redspark.c in Sources */, 836F701E18BDC2190095E648 /* redspark.c in Sources */,
8306B0ED20984590000302D4 /* txtp.c in Sources */, 8306B0ED20984590000302D4 /* txtp.c in Sources */,
836F6FA018BDC2190095E648 /* musx.c in Sources */, 836F6FA018BDC2190095E648 /* musx.c in Sources */,
@ -3088,7 +3105,6 @@
836F6F8218BDC2190095E648 /* ea_schl.c in Sources */, 836F6F8218BDC2190095E648 /* ea_schl.c in Sources */,
834FBCE826BBC7D00095647F /* tantalus_decoder.c in Sources */, 834FBCE826BBC7D00095647F /* tantalus_decoder.c in Sources */,
836F6F7318BDC2190095E648 /* bcstm.c in Sources */, 836F6F7318BDC2190095E648 /* bcstm.c in Sources */,
83299FD11E7660C7003A3242 /* dsp_adx.c in Sources */,
836F704018BDC2190095E648 /* wii_sng.c in Sources */, 836F704018BDC2190095E648 /* wii_sng.c in Sources */,
834FE10D215C79ED000A5D3D /* vag.c in Sources */, 834FE10D215C79ED000A5D3D /* vag.c in Sources */,
8350C0551E071881009E0A93 /* xma.c in Sources */, 8350C0551E071881009E0A93 /* xma.c in Sources */,
@ -3168,6 +3184,7 @@
836F6FC718BDC2190095E648 /* pona.c in Sources */, 836F6FC718BDC2190095E648 /* pona.c in Sources */,
8306B0B820984552000302D4 /* blocked_ws_aud.c in Sources */, 8306B0B820984552000302D4 /* blocked_ws_aud.c in Sources */,
836F6FE618BDC2190095E648 /* ps2_mcg.c in Sources */, 836F6FE618BDC2190095E648 /* ps2_mcg.c in Sources */,
01BC2CBE2B5C300C0026C0A4 /* nxof.c in Sources */,
83AA5D241F6E2F9C0020821C /* awc.c in Sources */, 83AA5D241F6E2F9C0020821C /* awc.c in Sources */,
8349A8E91FE6253900E26435 /* blocked_ea_1snh.c in Sources */, 8349A8E91FE6253900E26435 /* blocked_ea_1snh.c in Sources */,
8306B0E620984590000302D4 /* msb_msh.c in Sources */, 8306B0E620984590000302D4 /* msb_msh.c in Sources */,
@ -3229,6 +3246,7 @@
836F6FA418BDC2190095E648 /* nds_hwas.c in Sources */, 836F6FA418BDC2190095E648 /* nds_hwas.c in Sources */,
83A21F8A201D8982000F04B9 /* fsb_encrypted.c in Sources */, 83A21F8A201D8982000F04B9 /* fsb_encrypted.c in Sources */,
8306B0B120984552000302D4 /* blocked_halpst.c in Sources */, 8306B0B120984552000302D4 /* blocked_halpst.c in Sources */,
01BC2CBB2B5C300C0026C0A4 /* adp_qd.c in Sources */,
83D26A8426E66DC2001A9475 /* chunks.c in Sources */, 83D26A8426E66DC2001A9475 /* chunks.c in Sources */,
836F6FD018BDC2190095E648 /* ps2_b1s.c in Sources */, 836F6FD018BDC2190095E648 /* ps2_b1s.c in Sources */,
83AA5D181F6E2F600020821C /* mpeg_custom_utils_awc.c in Sources */, 83AA5D181F6E2F600020821C /* mpeg_custom_utils_awc.c in Sources */,
@ -3261,6 +3279,7 @@
836F6FB718BDC2190095E648 /* ngc_ssm.c in Sources */, 836F6FB718BDC2190095E648 /* ngc_ssm.c in Sources */,
8306B0E920984590000302D4 /* opus.c in Sources */, 8306B0E920984590000302D4 /* opus.c in Sources */,
832BF80021E050B7006F50F1 /* mpeg_custom_utils_eamp3.c in Sources */, 832BF80021E050B7006F50F1 /* mpeg_custom_utils_eamp3.c in Sources */,
01BC2CB92B5C300C0026C0A4 /* nxa1.c in Sources */,
83709E051ECBC1A4005C03D3 /* ghs.c in Sources */, 83709E051ECBC1A4005C03D3 /* ghs.c in Sources */,
8306B0A420984552000302D4 /* segmented.c in Sources */, 8306B0A420984552000302D4 /* segmented.c in Sources */,
83A21F86201D8981000F04B9 /* xwc.c in Sources */, 83A21F86201D8981000F04B9 /* xwc.c in Sources */,
@ -3358,6 +3377,7 @@
833E82E42A2857F700CD0580 /* seek.c in Sources */, 833E82E42A2857F700CD0580 /* seek.c in Sources */,
834FE0B8215C798C000A5D3D /* acm_decoder_decode.c in Sources */, 834FE0B8215C798C000A5D3D /* acm_decoder_decode.c in Sources */,
8399335F2591E8C1001855AF /* ifs.c in Sources */, 8399335F2591E8C1001855AF /* ifs.c in Sources */,
01BC2CBC2B5C300C0026C0A4 /* adx_monster.c in Sources */,
8373342A23F60CDC00DE14DC /* lrmd.c in Sources */, 8373342A23F60CDC00DE14DC /* lrmd.c in Sources */,
833E82E22A2857F700CD0580 /* decode.c in Sources */, 833E82E22A2857F700CD0580 /* decode.c in Sources */,
836F6FBD18BDC2190095E648 /* nwa.c in Sources */, 836F6FBD18BDC2190095E648 /* nwa.c in Sources */,

View file

@ -418,7 +418,8 @@ int decode_get_samples_per_frame(VGMSTREAM* vgmstream) {
case coding_XBOX_IMA_int: case coding_XBOX_IMA_int:
case coding_FSB_IMA: case coding_FSB_IMA:
case coding_WWISE_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; return 64;
case coding_APPLE_IMA4: case coding_APPLE_IMA4:
return 64; return 64;
@ -654,6 +655,8 @@ int decode_get_frame_size(VGMSTREAM* vgmstream) {
case coding_WWISE_IMA: case coding_WWISE_IMA:
case coding_CD_IMA: case coding_CD_IMA:
return 0x24; return 0x24;
case coding_CRANKCASE_IMA:
return 0x23;
case coding_XBOX_IMA_mch: case coding_XBOX_IMA_mch:
case coding_FSB_IMA: case coding_FSB_IMA:
return 0x24 * vgmstream->channels; 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); vgmstream->channels, vgmstream->samples_into_block, samples_to_do);
} }
break; 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: case coding_WS:
for (ch = 0; ch < vgmstream->channels; ch++) { for (ch = 0; ch < vgmstream->channels; ch++) {

View file

@ -330,7 +330,7 @@ circus_handle_t* circus_init(off_t start, uint8_t codec, uint8_t flags) {
handle->flags = flags; //(config >> 8) & 0xFF; handle->flags = flags; //(config >> 8) & 0xFF;
scale_index = (handle->flags & 0xF); scale_index = (handle->flags & 0xF);
if (scale_index > 5) goto fail; if (scale_index >= 5) goto fail;
handle->scales = scale_table[scale_index]; handle->scales = scale_table[scale_index];
if (handle->codec == XPCM_CODEC_VQ_DEFLATE) { if (handle->codec == XPCM_CODEC_VQ_DEFLATE) {

View file

@ -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_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_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_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 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 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); size_t xbox_ima_bytes_to_samples(size_t bytes, int channels);

View file

@ -35,8 +35,8 @@ static const int IMA_IndexTable[16] = {
/* Original IMA expansion, using shift+ADDs to avoid MULs (slow back then) */ /* 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) { static void std_ima_expand_nibble_data(uint8_t byte, int shift, int32_t* hist1, int32_t* index) {
int sample_nibble, sample_decoded, step, delta; int code, sample, step, delta;
/* simplified through math from: /* simplified through math from:
* - diff = (code + 1/2) * (step / 4) * - 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) * > diff = (step * nibble / 4) + (step / 8)
* final diff = [signed] (step / 8) + (step / 4) + (step / 2) + (step) [when code = 4+2+1] */ * 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 */ code = (byte >> shift) & 0xf;
sample_decoded = *hist1; /* predictor value */ sample = *hist1; /* predictor value */
step = ADPCMTable[*step_index]; /* current step */ step = ADPCMTable[*index]; /* current step */
delta = step >> 3; delta = step >> 3;
if (sample_nibble & 1) delta += step >> 2; if (code & 1) delta += step >> 2;
if (sample_nibble & 2) delta += step >> 1; if (code & 2) delta += step >> 1;
if (sample_nibble & 4) delta += step; if (code & 4) delta += step;
if (sample_nibble & 8) delta = -delta; if (code & 8) delta = -delta;
sample_decoded += delta; sample += delta;
*hist1 = clamp16(sample_decoded); *hist1 = clamp16(sample);
*step_index += IMA_IndexTable[sample_nibble]; *index += IMA_IndexTable[code];
if (*step_index < 0) *step_index=0; if (*index < 0) *index = 0;
if (*step_index > 88) *step_index=88; 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?) */ /* 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; 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;
}
/* ************************************************************* */ /* ************************************************************* */

View file

@ -188,28 +188,33 @@ fail:
} }
#define AHX_KEY_BUFFER 0x2000 #define AHX_KEY_BUFFER 0x1000 /* not too big since it's read per new key */
#define AHX_KEY_TEST_FRAMES 20 /* wrong keys may work ok in some frames */ #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 */ /* check if current key ends properly in frame syncs */
int test_ahx_key(STREAMFILE* sf, off_t offset, crikey_t* crikey) { int test_ahx_key(STREAMFILE* sf, off_t offset, crikey_t* crikey) {
int bytes; int bytes = 0;
uint8_t buf[AHX_KEY_BUFFER]; uint8_t buf[AHX_KEY_BUFFER];
const int buf_size = sizeof(buf); const int buf_size = sizeof(buf);
int pos = 0; int pos = 0;
uint32_t base_sync, curr_sync; uint32_t base_sync, curr_sync;
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++) { 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);
}
int size = ahx_decrypt(buf + pos, bytes, crikey); int frame_size = ahx_decrypt(buf + pos, bytes, crikey);
if (size <= 0 || size >= bytes - 0x04) goto fail; if (frame_size <= 0 || frame_size >= bytes - 0x04)
goto fail;
bytes -= size; bytes -= frame_size;
pos += size; pos += frame_size;
curr_sync = get_u32be(buf + pos); curr_sync = get_u32be(buf + pos);
if (curr_sync == 0x00800100) /* EOF tag */ if (curr_sync == 0x00800100) /* EOF tag */

View file

@ -392,6 +392,7 @@ static const char* extension_list[] = {
"nwa", "nwa",
"nwav", "nwav",
"nxa", "nxa",
"nxopus",
//"ogg", //common //"ogg", //common
"ogg_", "ogg_",
@ -474,6 +475,7 @@ static const char* extension_list[] = {
"sbr", "sbr",
"sbv", "sbv",
"sig", "sig",
"slb", //txth/reserved [Vingt-et-un Systems PS2 games (Last Escort, etc]
"sm0", "sm0",
"sm1", "sm1",
"sm2", "sm2",
@ -522,6 +524,7 @@ static const char* extension_list[] = {
"sps", "sps",
"spsd", "spsd",
"spw", "spw",
"srsa",
"ss2", "ss2",
"ssd", //txth/reserved [Zack & Wiki (Wii)] "ssd", //txth/reserved [Zack & Wiki (Wii)]
"ssm", "ssm",
@ -535,6 +538,7 @@ static const char* extension_list[] = {
"stream", "stream",
"strm", "strm",
"sts", "sts",
"stv", //txth/reserved [Socio Art Logic PS2 games (Zero no Tsukaima games, Cambrian QTS, Shirogane no Soleil, etc)]
"sts_cp3", "sts_cp3",
"stx", "stx",
"svag", "svag",
@ -628,6 +632,7 @@ static const char* extension_list[] = {
"wd", "wd",
"wem", "wem",
"wii", "wii",
"wiive", //txth/semi [Rubik World (Wii)]
"wic", //txth/reserved [Road Rash (SAT)-videos] "wic", //txth/reserved [Road Rash (SAT)-videos]
"wip", //txth/reserved [Colin McRae DiRT (PC)] "wip", //txth/reserved [Colin McRae DiRT (PC)]
"wlv", //txth/reserved [ToeJam & Earl III: Mission to Earth (DC)] "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_UBI_SCE_IMA, "Ubisoft 4-bit SCE IMA ADPCM"},
{coding_H4M_IMA, "Hudson HVQM4 4-bit IMA ADPCM"}, {coding_H4M_IMA, "Hudson HVQM4 4-bit IMA ADPCM"},
{coding_CD_IMA, "Crystal Dynamics 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, "Microsoft 4-bit ADPCM"},
{coding_MSADPCM_int, "Microsoft 4-bit ADPCM (mono/interleave)"}, {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_XAU_KONAMI, "Konami XAU header"},
{meta_DERF, "Xilam DERF header"}, {meta_DERF, "Xilam DERF header"},
{meta_UTK, "Maxis UTK header"}, {meta_UTK, "Maxis UTK header"},
{meta_NXA, "Entergram NXA header"}, {meta_NXA1, "Entergram NXA1 header"},
{meta_ADPCM_CAPCOM, "Capcom .ADPCM header"}, {meta_ADPCM_CAPCOM, "Capcom .ADPCM header"},
{meta_UE4OPUS, "Epic Games UE4OPUS header"}, {meta_UE4OPUS, "Epic Games UE4OPUS header"},
{meta_XWMA, "Microsoft XWMA RIFF header"}, {meta_XWMA, "Microsoft XWMA RIFF header"},
@ -1415,6 +1421,7 @@ static const meta_info meta_info_list[] = {
{meta_SQUEAKSTREAM, "Torus SqueakStream header"}, {meta_SQUEAKSTREAM, "Torus SqueakStream header"},
{meta_SQUEAKSAMPLE, "Torus SqueakSample header"}, {meta_SQUEAKSAMPLE, "Torus SqueakSample header"},
{meta_SNDS, "Sony SNDS 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) { void get_vgmstream_coding_description(VGMSTREAM* vgmstream, char* out, size_t out_size) {

View file

@ -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) { void block_update_awc(off_t block_offset, VGMSTREAM * vgmstream) {
STREAMFILE* sf = vgmstream->ch[0].streamfile; STREAMFILE* sf = vgmstream->ch[0].streamfile;
int32_t (*read_32bit)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_32bitBE : read_32bitLE; 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; size_t channel_header_size;
int i; int i;
/* assumed only AWC_IMA enters here, MPEG/XMA2 need special parsing as blocked layout is too limited */ /* assumes only AWC_IMA/DSP 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 header (see awc.c for a complete description):
//block_samples = entries * (0x800-4)*2; //todo use * - 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_samples = read_32bit(block_offset + 0x0c, sf);
block_size = vgmstream->full_block_size; 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->next_block_offset = block_offset + block_size;
vgmstream->current_block_samples = block_samples; vgmstream->current_block_samples = block_samples;
/* starts with a header block */ switch(vgmstream->coding_type) {
/* for each channel case coding_NGC_DSP:
* 0x00: start entry within channel (ie. entries * ch) but may be off by +1/+2 channel_header_size = 0x10;
* 0x04: entries frame_size = 0x08;
* 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)
*/
/* 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); 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); 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++) { for (i = 0; i < vgmstream->channels; i++) {
vgmstream->ch[i].offset = block_offset + header_size + 0x800*entries*i; vgmstream->ch[i].offset = block_offset + header_size + frame_size * 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);
} }
} }

View file

@ -92,6 +92,7 @@ fail:
#define ACB_TABLE_BUFFER_TRACKCOMMAND 0x2000 #define ACB_TABLE_BUFFER_TRACKCOMMAND 0x2000
#define ACB_TABLE_BUFFER_SYNTH 0x4000 #define ACB_TABLE_BUFFER_SYNTH 0x4000
#define ACB_TABLE_BUFFER_WAVEFORM 0x4000 #define ACB_TABLE_BUFFER_WAVEFORM 0x4000
#define ACB_TABLE_BUFFER_WAVEFORMEXTENSIONDATA 0x1000
#define ACB_MAX_NAMELIST 255 #define ACB_MAX_NAMELIST 255
#define ACB_MAX_NAME 1024 /* even more is possible in rare cases [Senran Kagura Burst Re:Newal (PC)] */ #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 Id;
uint16_t PortNo; uint16_t PortNo;
uint8_t Streaming; uint8_t Streaming;
uint8_t LoopFlag;
uint16_t ExtensionData;
} Waveform_t; } Waveform_t;
typedef struct {
uint32_t LoopStart;
uint32_t LoopEnd;
} WaveformExtensionData_t;
typedef struct { typedef struct {
STREAMFILE* acbFile; /* original reference, don't close */ STREAMFILE* acbFile; /* original reference, don't close */
@ -178,6 +186,7 @@ typedef struct {
STREAMFILE* TrackCommandSf; STREAMFILE* TrackCommandSf;
STREAMFILE* SynthSf; STREAMFILE* SynthSf;
STREAMFILE* WaveformSf; STREAMFILE* WaveformSf;
STREAMFILE* WaveformExtensionDataSf;
Cue_t* Cue; Cue_t* Cue;
CueName_t* CueName; CueName_t* CueName;
@ -188,6 +197,7 @@ typedef struct {
TrackCommand_t* TrackCommand; TrackCommand_t* TrackCommand;
Synth_t* Synth; Synth_t* Synth;
Waveform_t* Waveform; Waveform_t* Waveform;
WaveformExtensionData_t* WaveformExtensionData;
int Cue_rows; int Cue_rows;
int CueName_rows; int CueName_rows;
@ -198,6 +208,7 @@ typedef struct {
int TrackCommand_rows; int TrackCommand_rows;
int Synth_rows; int Synth_rows;
int Waveform_rows; int Waveform_rows;
int WaveformExtensionData_rows;
/* config */ /* config */
int is_memory; int is_memory;
@ -208,7 +219,8 @@ typedef struct {
int synth_depth; int synth_depth;
int sequence_depth; int sequence_depth;
/* name stuff */ /* name/config stuff */
int16_t waveform_index;
int16_t cuename_index; int16_t cuename_index;
const char* cuename_name; const char* cuename_name;
int awbname_count; 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) { static int preload_acb_waveform(acb_header* acb) {
utf_context* Table = NULL; utf_context* Table = NULL;
int* p_rows = &acb->Waveform_rows; 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) if (*p_rows)
return 1; return 1;
@ -315,6 +327,8 @@ static int preload_acb_waveform(acb_header* acb) {
c_StreamAwbId = utf_get_column(Table, "StreamAwbId"); c_StreamAwbId = utf_get_column(Table, "StreamAwbId");
c_StreamAwbPortNo = utf_get_column(Table, "StreamAwbPortNo"); c_StreamAwbPortNo = utf_get_column(Table, "StreamAwbPortNo");
c_Streaming = utf_get_column(Table, "Streaming"); 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++) { for (i = 0; i < *p_rows; i++) {
Waveform_t* r = &acb->Waveform[i]; Waveform_t* r = &acb->Waveform[i];
@ -332,6 +346,10 @@ static int preload_acb_waveform(acb_header* acb) {
r->PortNo = 0xFFFF; r->PortNo = 0xFFFF;
} }
utf_query_col_u8(Table, i, c_Streaming, &r->Streaming); 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); utf_close(Table);
@ -346,7 +364,7 @@ static int load_acb_waveform(acb_header* acb, uint16_t Index) {
Waveform_t* r; Waveform_t* r;
if (!preload_acb_waveform(acb)) goto fail; 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]; r = &acb->Waveform[Index];
//;VGM_LOG("acb: Waveform[%i]: Id=%i, PortNo=%i, Streaming=%i\n", Index, r->Id, r->PortNo, r->Streaming); //;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)) if ((acb->is_memory && r->Streaming == 1) || (!acb->is_memory && r->Streaming == 0))
return 1; return 1;
/* save waveid <> Index translation */
acb->waveform_index = Index;
/* aaand finally get name (phew) */ /* aaand finally get name (phew) */
add_acb_name(acb, r->Streaming); add_acb_name(acb, r->Streaming);
@ -417,7 +438,7 @@ static int load_acb_synth(acb_header* acb, uint16_t Index) {
int i, count; int i, count;
if (!preload_acb_synth(acb)) goto fail; 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]; r = &acb->Synth[Index];
//;VGM_LOG("acb: Synth[%i]: Type=%x, ReferenceItems={%x,%x}\n", Index, r->Type, r->ReferenceItems_offset, r->ReferenceItems_size); //;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; TrackCommand_t* r;
if (!preload_acb_trackcommand(acb)) goto fail; 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]; r = &acb->TrackCommand[Index];
//;VGM_LOG("acb: TrackEvent/Command[%i]: Command={%x,%x}\n", Index, r->Command_offset, r->Command_size); //;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; Track_t* r;
if (!preload_acb_track(acb)) goto fail; 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]; r = &acb->Track[Index];
//;VGM_LOG("acb: Track[%i]: EventIndex=%i\n", Index, r->EventIndex); //;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; int i;
if (!preload_acb_sequence(acb)) goto fail; 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]; 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); //;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; int i;
if (!preload_acb_block(acb)) goto fail; 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]; r = &acb->Block[Index];
//;VGM_LOG("acb: Block[%i]: NumTracks=%i, TrackIndex={%x, %x}\n", Index, r->NumTracks, r->TrackIndex_offset, r->TrackIndex_size); //;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; int i;
if (!preload_acb_blocksequence(acb)) goto fail; 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]; 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); //;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] */ /* read Cue[Index] */
if (!preload_acb_cue(acb)) goto fail; 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]; r = &acb->Cue[Index];
//;VGM_LOG("acb: Cue[%i]: ReferenceType=%i, ReferenceIndex=%i\n", Index, r->ReferenceType, r->ReferenceIndex); //;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; CueName_t* r;
if (!preload_acb_cuename(acb)) goto fail; 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]; r = &acb->CueName[Index];
//;VGM_LOG("acb: CueName[%i]: CueIndex=%i, CueName=%s\n", Index, r->CueIndex, r->CueName); //;VGM_LOG("acb: CueName[%i]: CueIndex=%i, CueName=%s\n", Index, r->CueIndex, r->CueName);
@ -1081,6 +1102,79 @@ fail:
return 0; 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. /* 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. * 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}; acb_header acb = {0};
int i; 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_waveid = waveid;
acb.target_port = port; acb.target_port = port;
acb.is_memory = is_memory; acb.is_memory = is_memory;
acb.waveform_index = -1;
/* read all possible cue names and find which waveids are referenced by it */ /* read all possible cue names and find which waveids are referenced by it */
preload_acb_cuename(&acb); 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'; vgmstream->stream_name[STREAM_NAME_SIZE - 1] = '\0';
} }
/* uncommon */
if (load_loops) {
load_acb_loops(&acb, vgmstream);
}
/* done */ /* done */
fail: fail:
utf_close(acb.Header); utf_close(acb.Header);
@ -1157,6 +1257,7 @@ fail:
close_streamfile(acb.TrackCommandSf); close_streamfile(acb.TrackCommandSf);
close_streamfile(acb.SynthSf); close_streamfile(acb.SynthSf);
close_streamfile(acb.WaveformSf); close_streamfile(acb.WaveformSf);
close_streamfile(acb.WaveformExtensionDataSf);
free(acb.CueName); free(acb.CueName);
free(acb.Cue); free(acb.Cue);
@ -1167,4 +1268,5 @@ fail:
free(acb.TrackCommand); free(acb.TrackCommand);
free(acb.Synth); free(acb.Synth);
free(acb.Waveform); free(acb.Waveform);
free(acb.WaveformExtensionData);
} }

View file

@ -5,7 +5,8 @@
typedef struct { typedef struct {
int total_subsongs; int total_subsongs;
int target_subsong; int target_subsong;
int version; int file_version;
int header_version; /* major.minor in hex */
uint32_t stream_offset; uint32_t stream_offset;
uint32_t stream_size; uint32_t stream_size;
@ -18,7 +19,7 @@ typedef struct {
static int parse_adm(adm_header_t* adm, STREAMFILE* sf); 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)] */ /* ADM2 - Crankcase Audio REV plugin file [The Grand Tour Game (PC)] */
VGMSTREAM* init_vgmstream_adm2(STREAMFILE* sf) { VGMSTREAM* init_vgmstream_adm2(STREAMFILE* sf) {
@ -38,26 +39,24 @@ VGMSTREAM* init_vgmstream_adm3(STREAMFILE* sf) {
/* checks */ /* checks */
if (!is_id32be(0x00,sf, "ADM3")) if (!is_id32be(0x00,sf, "ADM3"))
return NULL; return NULL;
if (!check_extensions(sf, "wem")) if (!check_extensions(sf, "wem,bnk"))
return NULL; return NULL;
return init_vgmstream_adm(sf, 3); 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; VGMSTREAM* vgmstream = NULL;
adm_header_t adm = {0}; adm_header_t adm = {0};
/* ADMx are files used with the Wwise Crankaudio plugin, that simulate engine noises with /* 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 * base internal samples and some internal RPM config (probably). Actual file seems to
* define some combo of samples, this only plays those separate samples. * 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 */
adm.target_subsong = sf->stream_index; adm.target_subsong = sf->stream_index;
if (adm.target_subsong == 0) adm.target_subsong = 1; if (adm.target_subsong == 0) adm.target_subsong = 1;
adm.version = version; adm.file_version = file_version;
if (!parse_adm(&adm, sf)) if (!parse_adm(&adm, sf))
goto fail; goto fail;
@ -73,9 +72,21 @@ static VGMSTREAM* init_vgmstream_adm(STREAMFILE* sf, int version) {
vgmstream->num_streams = adm.total_subsongs; vgmstream->num_streams = adm.total_subsongs;
vgmstream->stream_size = adm.stream_size; vgmstream->stream_size = adm.stream_size;
vgmstream->coding_type = coding_APPLE_IMA4; switch(adm.header_version) {
vgmstream->layout_type = layout_interleave; case 0x00070000: /* The Crew Motorfest (PC) */
vgmstream->interleave_block_size = 0x22; 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)) if (!vgmstream_open_stream(vgmstream, sf, adm.stream_offset))
goto fail; goto fail;
@ -169,12 +180,12 @@ static int parse_adm(adm_header_t* adm, STREAMFILE* sf) {
uint32_t offset; uint32_t offset;
/* 0x04: null */ /* 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 */ /* 0x0c: header size */
/* 0x10: data start */ /* 0x10: data start */
/* rest unknown, looks mostly the same between files (some floats and stuff) */ /* rest unknown, looks mostly the same between files (some floats and stuff) */
switch(adm->version) { switch(adm->file_version) {
case 2: case 2:
/* low to high */ /* low to high */
offset = read_u32le(0x104, sf); offset = read_u32le(0x104, sf);

View file

@ -202,12 +202,15 @@ static const adxkey_info adxkey8_list[] = {
/* Mirai Nikki: 13-ninme no Nikki Shoyuusha Re-Write (PSP) */ /* Mirai Nikki: 13-ninme no Nikki Shoyuusha Re-Write (PSP) */
{0x58a3,0x66f5,0x599f, "FDRW17th",0}, {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}, {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}, {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[] = { static const adxkey_info adxkey9_list[] = {

View file

@ -4,7 +4,7 @@
//typedef enum { ADX, HCA, VAG, RIFF, CWAV, DSP, CWAC, M4A } awb_type_t; //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 */ /* AFS2/AWB (Atom Wave Bank) - CRI container of streaming audio, often together with a .acb cue sheet */
VGMSTREAM* init_vgmstream_awb(STREAMFILE* sf) { VGMSTREAM* init_vgmstream_awb(STREAMFILE* sf) {
@ -19,6 +19,7 @@ VGMSTREAM* init_vgmstream_awb_memory(STREAMFILE* sf, STREAMFILE* sf_acb) {
uint8_t offset_size; uint8_t offset_size;
uint16_t waveid_alignment, offset_alignment, subkey; uint16_t waveid_alignment, offset_alignment, subkey;
int waveid; int waveid;
int load_loops = 0;
/* checks */ /* checks */
@ -126,6 +127,11 @@ VGMSTREAM* init_vgmstream_awb_memory(STREAMFILE* sf, STREAMFILE* sf_acb) {
extension = "m4a"; extension = "m4a";
} }
#endif #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? */ else { /* 12=XMA? */
vgm_logi("AWB: unknown codec (report)\n"); vgm_logi("AWB: unknown codec (report)\n");
goto fail; goto fail;
@ -144,8 +150,8 @@ VGMSTREAM* init_vgmstream_awb_memory(STREAMFILE* sf, STREAMFILE* sf_acb) {
vgmstream->num_streams = total_subsongs; vgmstream->num_streams = total_subsongs;
} }
/* try to load cue names */ /* try to load cue names+etc */
load_awb_name(sf, sf_acb, vgmstream, waveid); load_acb_info(sf, sf_acb, vgmstream, waveid, load_loops);
close_streamfile(temp_sf); close_streamfile(temp_sf);
return vgmstream; 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 is_memory = (sf_acb != NULL);
int port = 0; int port = 0;
@ -170,7 +176,7 @@ static void load_awb_name(STREAMFILE* sf, STREAMFILE* sf_acb, VGMSTREAM* vgmstre
/* try parsing TXTM if present */ /* try parsing TXTM if present */
sf_acb = read_filemap_file_pos(sf, 0, &port); sf_acb = read_filemap_file_pos(sf, 0, &port);
/* try (name).awb + (name).awb */ /* try (name).awb + (name).acb */
if (!sf_acb) { if (!sf_acb) {
sf_acb = open_streamfile_by_ext(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 */ /* 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); close_streamfile(sf_acb);
} }
else { 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);
} }
} }

View file

@ -1,26 +1,35 @@
#include "meta.h" #include "meta.h"
#include "../coding/coding.h" #include "../coding/coding.h"
#include "../layout/layout.h" #include "../layout/layout.h"
#include "awc_xma_streamfile.h" #include "../util/endianness.h"
#include "awc_streamfile.h"
typedef struct { typedef struct {
int big_endian; int big_endian;
int is_encrypted; int is_encrypted;
int is_music; int is_streamed; /* implicit: streams=music, sfx=memory */
int total_subsongs; int total_subsongs;
int channels; int channels;
int sample_rate; int sample_rate;
int codec;
int num_samples; int num_samples;
uint8_t codec;
int block_count;
int block_chunk; int block_chunk;
off_t stream_offset; uint32_t tags_offset;
size_t stream_size; uint32_t stream_offset;
off_t vorbis_offset[VGMSTREAM_MAX_CHANNELS]; 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; } awc_header;
static int parse_awc_header(STREAMFILE* sf, awc_header* awc); 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); 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* init_vgmstream_awc(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL; VGMSTREAM* vgmstream = NULL;
awc_header awc = {0}; awc_header awc = {0};
/* checks */ /* checks */
if (!check_extensions(sf,"awc"))
goto fail;
if (!parse_awc_header(sf, &awc)) if (!parse_awc_header(sf, &awc))
goto fail; return NULL;
if (!check_extensions(sf,"awc"))
return NULL;
/* build the VGMSTREAM */ /* build the VGMSTREAM */
@ -55,7 +64,7 @@ VGMSTREAM* init_vgmstream_awc(STREAMFILE* sf) {
switch(awc.codec) { switch(awc.codec) {
case 0x00: /* PCM (PC) sfx, very rare, lower sample rates? [Max Payne 3 (PC)] */ case 0x00: /* PCM (PC) sfx, very rare, lower sample rates? [Max Payne 3 (PC)] */
case 0x01: /* PCM (PC/PS3) sfx, rarely */ 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->coding_type = awc.big_endian ? coding_PCM16BE : coding_PCM16LE;
vgmstream->layout_type = layout_interleave; vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x02; vgmstream->interleave_block_size = 0x02;
@ -63,59 +72,18 @@ VGMSTREAM* init_vgmstream_awc(STREAMFILE* sf) {
case 0x04: /* IMA (PC) */ case 0x04: /* IMA (PC) */
vgmstream->coding_type = coding_AWC_IMA; 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->full_block_size = awc.block_chunk;
vgmstream->codec_endian = awc.big_endian; vgmstream->codec_endian = awc.big_endian;
break; break;
#ifdef VGM_USE_FFMPEG #ifdef VGM_USE_FFMPEG
case 0x05: { /* XMA2 (X360) */ case 0x05: { /* XMA2 (X360) */
uint32_t substream_size, substream_offset; if (awc.is_streamed) {
vgmstream->layout_data = build_layered_awc(sf, &awc);
if (awc.is_music) { if (!vgmstream->layout_data) goto fail;
/* 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;
vgmstream->layout_type = layout_layered; vgmstream->layout_type = layout_layered;
vgmstream->coding_type = coding_FFmpeg; 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 { else {
/* regular XMA for sfx */ /* regular XMA for sfx */
@ -129,9 +97,8 @@ VGMSTREAM* init_vgmstream_awc(STREAMFILE* sf) {
break; break;
} }
#endif #endif
#ifdef VGM_USE_MPEG #ifdef VGM_USE_MPEG
case 0x07: { /* MPEG (PS3) */ case 0x07: { /* MPEG (PS3) */
mpeg_custom_config cfg = {0}; mpeg_custom_config cfg = {0};
@ -146,9 +113,10 @@ VGMSTREAM* init_vgmstream_awc(STREAMFILE* sf) {
break; break;
} }
#endif #endif
#ifdef VGM_USE_VORBIS #ifdef VGM_USE_VORBIS
case 0x08: { /* Vorbis (PC) [Red Dead Redemption 2 (PC)] */ 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); vgmstream->layout_data = build_layered_awc(sf, &awc);
if (!vgmstream->layout_data) goto fail; if (!vgmstream->layout_data) goto fail;
vgmstream->layout_type = layout_layered; vgmstream->layout_type = layout_layered;
@ -169,6 +137,62 @@ VGMSTREAM* init_vgmstream_awc(STREAMFILE* sf) {
break; break;
} }
#endif #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: default:
VGM_LOG("AWC: unknown codec 0x%02x\n", awc.codec); VGM_LOG("AWC: unknown codec 0x%02x\n", awc.codec);
goto fail; goto fail;
@ -185,24 +209,42 @@ fail:
} }
/* Parse Rockstar's AWC header (much info from LibertyV: https://github.com/koolkdev/libertyv). /* 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) { static int parse_awc_header(STREAMFILE* sf, awc_header* awc) {
uint64_t (*read_u64)(off_t,STREAMFILE*) = NULL; read_u64_t read_u64 = NULL;
uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL; read_u32_t read_u32 = NULL;
uint16_t (*read_u16)(off_t,STREAMFILE*) = NULL; read_u16_t read_u16 = NULL;
int i, ch, entries; int entries;
uint32_t flags, info_header, tag_count = 0, tags_skip = 0; uint32_t flags, tag_count = 0, tags_skip = 0;
off_t offset; uint32_t offset;
int target_subsong = sf->stream_index; 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) { if (awc->big_endian) {
read_u64 = read_u64be; read_u64 = read_u64be;
read_u32 = read_u32be; read_u32 = read_u32be;
@ -213,25 +255,32 @@ static int parse_awc_header(STREAMFILE* sf, awc_header* awc) {
read_u16 = read_u16le; read_u16 = read_u16le;
} }
flags = read_u32(0x04,sf); flags = read_u32(0x04,sf);
entries = read_u32(0x08,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; offset = 0x10;
/* flags = 8b (always FF) + 8b (actual flags) + 16b (version, 00=rarely, 01=common) */
if ((flags & 0xFF00FFFF) != 0xFF000001 || (flags & 0x00F00000)) { if ((flags & 0xFF00FFFF) != 0xFF000001 || (flags & 0x00F00000)) {
VGM_LOG("AWC: unknown flags 0x%08x\n", flags); VGM_LOG("AWC: unknown flags 0x%08x\n", flags);
goto fail; 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; offset += 0x2 * entries;
//if (flags % 0x00020000) /* seems to indicate chunks are not ordered (ie. header may go after data) */
// ... /* seems to indicate chunks are not ordered (ie. header structures from tags may go after data), usually for non-streams */
//if (flags % 0x00040000) /* music/multichannel flag? (GTA5, not seen in RDR) */ //if (flags % 0x00020000)
// awc->is_music = 1; // awc->is_unordered = 1;
if (flags & 0x00080000) /* encrypted data chunk (most of GTA5 PC) */
/* 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; awc->is_encrypted = 1;
if (awc->is_encrypted) { if (awc->is_encrypted) {
@ -239,48 +288,84 @@ static int parse_awc_header(STREAMFILE* sf, awc_header* awc) {
goto fail; 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). /* When first stream hash/id is 0 AWC it has fake entry with info for all channels = music, sfx pack otherwise.
* Music seems layered (N-1/2 stereo pairs), maybe set with events? */ * sfx = N single streams, music = N interleaved mono channels (even for MP3/XMA/Vorbis/etc).
awc->is_music = (read_u32(offset + 0x00,sf) & 0x1FFFFFFF) == 0x00000000; * Channels set a stream hash/id that typically is one of the defined ones and its tags do apply to that
if (awc->is_music) { /* all streams except id 0 is a channel */ * 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; 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; awc->total_subsongs = entries;
if (target_subsong == 0) target_subsong = 1; if (target_subsong == 0) target_subsong = 1;
if (target_subsong < 0 || target_subsong > awc->total_subsongs || awc->total_subsongs < 1) goto fail; if (target_subsong < 0 || target_subsong > awc->total_subsongs || awc->total_subsongs < 1) goto fail;
} }
/* get stream base info */ /** stream ids and tag counts **/
for (i = 0; i < entries; i++) { for (int i = 0; i < entries; i++) {
info_header = read_u32(offset + 0x04*i, sf); uint32_t info_header = read_u32(offset + 0x00, sf);
tag_count = (info_header >> 29) & 0x7; /* 3b */ int entry_count = (info_header >> 29) & 0x7; /* 3b */
//id = (info_header >> 0) & 0x1FFFFFFF; /* 29b */ uint32_t hash_id = (info_header >> 0) & 0x1FFFFFFF; /* 29b */
if (target_subsong-1 == i)
break; if (i + 1 < target_subsong)
tags_skip += tag_count; /* tags to skip to reach target's tags, in the next header */ 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;
} }
offset += 0x04*entries; awc->tags_offset = offset; /* where tags for stream start */
offset += 0x08*tags_skip;
/* get stream tags */ offset += 0x08 * tags_skip; /* ignore tags for other streams */
for (i = 0; i < tag_count; i++) {
uint64_t tag_header;
uint8_t tag_type;
size_t tag_size;
off_t tag_offset;
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);
/* 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) { switch(tag_type) {
case 0x55: /* data */ case 0x55: /* data */
awc->stream_offset = tag_offset; awc->stream_offset = tag_offset;
@ -288,30 +373,31 @@ static int parse_awc_header(STREAMFILE* sf, awc_header* awc) {
break; break;
case 0x48: /* music header */ case 0x48: /* music header */
if (!awc->is_streamed) {
if (!awc->is_music) { VGM_LOG("AWC: music header found but not streamed\n");
VGM_LOG("AWC: music header found in sfx\n");
goto fail; 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->block_chunk = read_u32(tag_offset + 0x04,sf);
awc->channels = read_u32(tag_offset + 0x08,sf); awc->channels = read_u32(tag_offset + 0x08,sf);
if (awc->channels != entries - 1) { /* not counting id-0 */ if (awc->channels != entries - 1) { /* not counting id-0 */
VGM_LOG("AWC: number of music channels doesn't match entries\n"); 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; 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); num_samples = read_u32(tag_offset + 0x0c + 0x10*ch + 0x04,sf);
/* 0x08: headroom */ /* 0x08: headroom */
sample_rate = read_u16(tag_offset + 0x0c + 0x10*ch + 0x0a,sf); sample_rate = read_u16(tag_offset + 0x0c + 0x10*ch + 0x0a,sf);
codec = read_u8(tag_offset + 0x0c + 0x10*ch + 0x0c,sf); codec = read_u8(tag_offset + 0x0c + 0x10*ch + 0x0c,sf);
/* 0x0d(8): round size? */ /* 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 */ /* validate channels differences */
if ((awc->num_samples && !(awc->num_samples >= num_samples - 10 && awc->num_samples <= num_samples + 10)) || 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; break;
case 0xFA: /* sfx header */ case 0xFA: /* sfx header */
if (awc->is_music) { if (awc->is_streamed) {
VGM_LOG("AWC: sfx header found in music\n"); VGM_LOG("AWC: sfx header found but streamed\n");
goto fail; break; //goto fail; /* rare (RDR PC/Switch) */
} }
awc->num_samples = read_u32(tag_offset + 0x00,sf); awc->num_samples = read_u32(tag_offset + 0x00,sf);
/* 0x04: -1? */ /* 0x04: -1? */
awc->sample_rate = read_u16(tag_offset + 0x08,sf); awc->sample_rate = read_u16(tag_offset + 0x08,sf);
/* 0x0a: unknown x4 */ /* 0x0a: headroom */
/* 0x0c: unknown */
/* 0x0e: unknown */
/* 0x10: unknown */
/* 0x12: null? */ /* 0x12: null? */
awc->codec = read_u8(tag_offset + 0x13, sf); awc->codec = read_u8(tag_offset + 0x13, sf);
/* 0x14: ? (PS3 only, for any codec) */
awc->channels = 1; awc->channels = 1;
break; break;
case 0x76: /* sfx header for vorbis */ case 0x76: /* sfx header for vorbis */
if (awc->is_music) { if (awc->is_streamed) {
VGM_LOG("AWC: sfx header found in music\n"); VGM_LOG("AWC: sfx header found but streamed\n");
goto fail; goto fail;
} }
awc->num_samples = read_u32(tag_offset + 0x00,sf); awc->num_samples = read_u32(tag_offset + 0x00,sf);
/* 0x04: -1? */ /* 0x04: -1? */
awc->sample_rate = read_u16(tag_offset + 0x08,sf); awc->sample_rate = read_u16(tag_offset + 0x08,sf);
/* 0x0a: granule start? (negative) */ /* 0x0a: headroom */
/* 0x0c: granule max? */ /* 0x0c: unknown */
/* 0x0e: unknown */
/* 0x10: unknown */ /* 0x10: unknown */
awc->codec = read_u8(tag_offset + 0x1c, sf); /* 16b? */ awc->codec = read_u8(tag_offset + 0x1c, sf); /* 16b? */
/* 0x1e: vorbis header size */ /* 0x1e: vorbis setup size */
awc->channels = 1; 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; 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 0xBD: /* events (32bx4): type_hash, params_hash, timestamp_ms, flags */
case 0x5C: /* animation/RSC config? */ case 0x5C: /* animation/RSC info? */
default: /* 0x68=midi?, 0x36=hash thing?, 0x2B=sizes, 0x5A/0xD9=? */ 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); //VGM_LOG("AWC: ignoring unknown tag 0x%02x\n", tag);
break; 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) { if (!awc->stream_offset) {
VGM_LOG("AWC: stream offset not found\n"); VGM_LOG("AWC: stream offset not found\n");
goto fail; 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; return 1;
fail: fail:
return 0; return 0;
@ -414,140 +543,113 @@ fail:
/* ************************************************************************* */ /* ************************************************************************* */
//TODO: this method won't work properly, needs internal handling of blocks. static VGMSTREAM* build_blocks_vgmstream(STREAMFILE* sf, awc_header* awc, int channel) {
//
// 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;
VGMSTREAM* vgmstream = NULL; VGMSTREAM* vgmstream = NULL;
STREAMFILE* temp_sf = NULL;
int block_channels = 1; 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 */ /* build the VGMSTREAM */
vgmstream = allocate_vgmstream(block_channels, 0); vgmstream = allocate_vgmstream(block_channels, 0);
if (!vgmstream) goto fail; 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->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) { 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 #ifdef VGM_USE_VORBIS
case 0x08: { /* Vorbis (PC) [Red Dead Redemption 2 (PC)] */ case 0x08: {
vorbis_custom_config cfg = {0}; vorbis_custom_config cfg = {0};
cfg.channels = 1; cfg.channels = 1;
cfg.sample_rate = awc->sample_rate; cfg.sample_rate = awc->sample_rate;
cfg.header_offset = awc->vorbis_offset[channel]; cfg.header_offset = awc->vorbis_offset[channel]; /* setup page goes separate */
//cfg.skip_samples = skip_samples; //todo
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; if (!vgmstream->codec_data) goto fail;
vgmstream->layout_type = layout_none; vgmstream->layout_type = layout_none;
vgmstream->coding_type = coding_VORBIS_custom; 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;
} }
break;
#endif #endif
default: default:
goto fail; goto fail;
} }
if (!vgmstream_open_stream(vgmstream, sf, block_start)) if (!vgmstream_open_stream(vgmstream, temp_sf, substream_offset))
goto fail; goto fail;
close_streamfile(temp_sf); close_streamfile(temp_sf);
return vgmstream; return vgmstream;
fail: fail:
;VGM_LOG("AWB: can't open decoder\n");
close_vgmstream(vgmstream);
close_streamfile(temp_sf); 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; 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. * A "music" .awc has N channels = N streams (each using their own mono decoder) chunked in "blocks".
* Each block then has header + seek table for all channels. But in each block there is * Each block then has header + seek table + etc for all channels. But when blocks change, each channel
* a "skip samples" value per channel, and blocks repeat some data from last block * may have a "skip samples" value and blocks repeat some data from last block, so output PCM must be
* for this, so PCM must be discarded. Also, channels in a block don't need to have * discarded to avoid channels desyncing. Channels in a block don't need to have the same number of samples.
* 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) { static layered_layout_data* build_layered_awc(STREAMFILE* sf, awc_header* awc) {
int i; int i;
layered_layout_data* data = NULL; layered_layout_data* data = NULL;
@ -571,4 +673,3 @@ fail:
free_layout_layered(data); free_layout_layered(data);
return NULL; return NULL;
} }

View 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

View file

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

View file

@ -143,6 +143,11 @@ VGMSTREAM* init_vgmstream_bkhd(STREAMFILE* sf) {
vgmstream = init_vgmstream_wwise_bnk(temp_sf, &prefetch); vgmstream = init_vgmstream_wwise_bnk(temp_sf, &prefetch);
if (!vgmstream) goto fail; 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 && else if (read_f32(subfile_offset + 0x02, temp_sf) >= 30.0 &&
read_f32(subfile_offset + 0x02, temp_sf) <= 250.0) { read_f32(subfile_offset + 0x02, temp_sf) <= 250.0) {
is_wmid = 1; is_wmid = 1;

View file

@ -3,7 +3,7 @@
#include "../coding/coding.h" #include "../coding/coding.h"
#include "../util/endianness.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 { typedef struct {
bnk_codec codec; bnk_codec codec;
@ -41,6 +41,7 @@ typedef struct {
int32_t num_samples; int32_t num_samples;
int32_t loop_start; int32_t loop_start;
int32_t loop_end; int32_t loop_end;
int32_t encoder_delay;
uint32_t start_offset; uint32_t start_offset;
uint32_t stream_offset; uint32_t stream_offset;
@ -81,7 +82,7 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) {
vgmstream->meta_type = meta_BNK_SONY; 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; h.stream_name_size = STREAM_NAME_SIZE;
if (!h.bank_name_offset && h.stream_name_offset) { 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) { 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(bank_name, h.stream_name_size, h.bank_name_offset, sf);
read_string(stream_name, h.stream_name_size, h.stream_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; return temp_vs;
} }
#endif #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: case PCM16:
vgmstream->coding_type = h.big_endian ? coding_PCM16BE : coding_PCM16LE; vgmstream->coding_type = h.big_endian ? coding_PCM16BE : coding_PCM16LE;
vgmstream->layout_type = layout_interleave; vgmstream->layout_type = layout_interleave;
@ -417,12 +432,12 @@ static bool process_headers(STREAMFILE* sf, bnk_header_t* h) {
switch(h->sblk_version) { switch(h->sblk_version) {
case 0x01: case 0x01:
/* table2/3 has size 0x28 entries, seemingly: /* table2/3 has size 0x28 entries, seemingly:
* 0x00: subtype(01=sound) * 0x00: subtype(01=sound)
* 0x08: same as other versions (pitch, flags, offset...) * 0x08: same as other versions (pitch, flags, offset...)
* rest: padding * rest: padding
* 0x18: stream offset * 0x18: stream offset
* there is no stream size like in v0x03 * there is no stream size like in v0x03
*/ */
for (i = 0; i < h->grains_entries; i++) { for (i = 0; i < h->grains_entries; i++) {
uint32_t table2_type = read_u32(h->table2_offset + (i*0x28) + 0x00, sf); uint32_t table2_type = read_u32(h->table2_offset + (i*0x28) + 0x00, sf);
@ -527,22 +542,22 @@ static bool process_headers(STREAMFILE* sf, bnk_header_t* h) {
h->sample_rate = (pitch * 48000) / 0x1000; h->sample_rate = (pitch * 48000) / 0x1000;
/* waves can set base sample rate (48/44/22/11/8khz) + pitch in semitones, then converted to center+fine /* waves can set base sample rate (48/44/22/11/8khz) + pitch in semitones, then converted to center+fine
* 48000 + pitch 0.00 > center=0xc4, fine=0x00 * 48000 + pitch 0.00 > center=0xc4, fine=0x00
* 48000 + pitch 0.10 > center=0xc4, fine=0x0c * 48000 + pitch 0.10 > center=0xc4, fine=0x0c
* 48000 + pitch 0.50 > center=0xc4, fine=0x3f * 48000 + pitch 0.50 > center=0xc4, fine=0x3f
* 48000 + pitch 0.99 > center=0xc4, fine=0x7d * 48000 + pitch 0.99 > center=0xc4, fine=0x7d
* 48000 + pitch 1.00 > center=0xc5, fine=0x00 * 48000 + pitch 1.00 > center=0xc5, fine=0x00
* 48000 + pitch 12.00 > center=0xd0, fine=0x00 * 48000 + pitch 12.00 > center=0xd0, fine=0x00
* 48000 + pitch 24.00 > center=0xdc, fine=0x00 * 48000 + pitch 24.00 > center=0xdc, fine=0x00
* 48000 + pitch 56.00 > center=0xfc, fine=0x00 * 48000 + pitch 56.00 > center=0xfc, fine=0x00
* 48000 + pitch 68.00 > center=0x08, fine=0x00 > ? * 48000 + pitch 68.00 > center=0x08, fine=0x00 > ?
* 48000 + pitch -12.00 > center=0xb8, fine=0x00 * 48000 + pitch -12.00 > center=0xb8, fine=0x00
* 48000 + pitch -0.10 > center=0xc3, fine=0x72 * 48000 + pitch -0.10 > center=0xc3, fine=0x72
* 48000 + pitch -0.001 > not allowed * 48000 + pitch -0.001 > not allowed
* 8000 + pitch 1.00 > center=0xa4, fine=0x7c * 8000 + pitch 1.00 > center=0xa4, fine=0x7c
* 8000 + pitch -12.00 > center=0x98, fine=0x7c * 8000 + pitch -12.00 > center=0x98, fine=0x7c
* 8000 + pitch -48.00 > center=0x74, fine=0x7c * 8000 + pitch -48.00 > center=0x74, fine=0x7c
*/ */
break; break;
} }
@ -567,7 +582,7 @@ static bool process_headers(STREAMFILE* sf, bnk_header_t* h) {
goto fail; 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; return true;
fail: fail:
@ -602,17 +617,17 @@ static bool process_names(STREAMFILE* sf, bnk_header_t* h) {
} }
/* table4: /* table4:
* 0x00: bank name (optional) * 0x00: bank name (optional)
* 0x08: name section offset * 0x08: name section offset
* 0x0C-0x14: 3 null pointers (reserved?) * 0x0C-0x18: 3 null pointers (reserved?)
* 0x18-0x58: 32 name chunk offset indices * 0x18-0x58: 32 name chunk offset indices
*/ */
/* Name chunks are organised as /* 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 * and using that as the index for the chunk offsets
* name_sect_offset + (chunk_idx[result] * 0x14); * name_sect_offset + (chunk_idx[result] * 0x14);
*/ */
if (read_u8(h->table4_offset, sf)) if (read_u8(h->table4_offset, sf))
h->bank_name_offset = h->table4_offset; h->bank_name_offset = h->table4_offset;
@ -626,7 +641,7 @@ static bool process_names(STREAMFILE* sf, bnk_header_t* h) {
while (read_u8(h->stream_name_offset, sf)) { while (read_u8(h->stream_name_offset, sf)) {
/* in case it goes somewhere out of bounds unexpectedly */ /* in case it goes somewhere out of bounds unexpectedly */
if (((read_u8(h->stream_name_offset + 0x00, sf) + read_u8(h->stream_name_offset + 0x04, sf) + if (((read_u8(h->stream_name_offset + 0x00, sf) + read_u8(h->stream_name_offset + 0x04, sf) +
read_u8(h->stream_name_offset + 0x08, sf) + read_u8(h->stream_name_offset + 0x0C, sf)) & 0x1F) != i) read_u8(h->stream_name_offset + 0x08, sf) + read_u8(h->stream_name_offset + 0x0C, sf)) & 0x1F) != i)
goto fail; goto fail;
if (read_u16(h->stream_name_offset + 0x10, sf) == table4_entry_id) if (read_u16(h->stream_name_offset + 0x10, sf) == table4_entry_id)
goto loop_break; /* to break out of the for+while loop simultaneously */ goto loop_break; /* to break out of the for+while loop simultaneously */
@ -653,16 +668,16 @@ static bool process_names(STREAMFILE* sf, bnk_header_t* h) {
} }
/* table4: /* table4:
* 0x00: bank name (optional) * 0x00: bank name (optional)
* 0x08: name entries offset * 0x08: name entries offset
* 0x0C: name section offset * 0x0C: name section offset
* *
* name entries offset: * name entries offset:
* 0x00: name offset in name section * 0x00: name offset in name section
* 0x04: name hash(?) * 0x04: name hash(?)
* 0x08: ? (2x int16) * 0x08: ? (2x int16)
* 0x0C: section index (int16) * 0x0C: section index (int16)
*/ */
if (read_u8(h->table4_offset, sf)) if (read_u8(h->table4_offset, sf))
h->bank_name_offset = h->table4_offset; h->bank_name_offset = h->table4_offset;
@ -722,7 +737,7 @@ static bool process_names(STREAMFILE* sf, bnk_header_t* h) {
break; 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; return true;
fail: fail:
@ -785,19 +800,19 @@ static bool process_data(STREAMFILE* sf, bnk_header_t* h) {
h->interleave = h->stream_size / h->channels; h->interleave = h->stream_size / h->channels;
/* PS Home Arcade has other flags? supposedly: /* PS Home Arcade has other flags? supposedly:
* 01 = reverb * 01 = reverb
* 02 = vol scale 20 * 02 = vol scale 20
* 04 = vol scale 50 * 04 = vol scale 50
* 06 = vol scale 100 * 06 = vol scale 100
* 08 = noise * 08 = noise
* 10 = no dry * 10 = no dry
* 20 = no steal * 20 = no steal
* 40 = loop VAG * 40 = loop VAG
* 80 = PCM * 80 = PCM
* 100 = has advanced packets * 100 = has advanced packets
* 200 = send LFE * 200 = send LFE
* 400 = send center * 400 = send center
*/ */
if ((h->stream_flags & 0x80) && h->sblk_version <= 3) { if ((h->stream_flags & 0x80) && h->sblk_version <= 3) {
h->codec = PCM16; /* rare [Wipeout HD (PS3)]-v3 */ h->codec = PCM16; /* rare [Wipeout HD (PS3)]-v3 */
} }
@ -813,8 +828,8 @@ static bool process_data(STREAMFILE* sf, bnk_header_t* h) {
case 0x08: case 0x08:
case 0x09: case 0x09:
subtype = read_u16(h->start_offset+0x00,sf); subtype = read_u32(h->start_offset+0x00,sf);
extradata_size = 0x08 + read_u32(h->start_offset+0x04,sf); /* 0x14 for AT9 */ extradata_size = 0x08 + read_u32(h->start_offset+0x04,sf); /* 0x14 for AT9, 0x10 for PCM, 0x90 for MPEG */
switch(subtype) { switch(subtype) {
case 0x00: case 0x00:
@ -829,22 +844,49 @@ static bool process_data(STREAMFILE* sf, bnk_header_t* h) {
h->interleave = 0x01; h->interleave = 0x01;
break; break;
case 0x02: /* ATRAC9 / MPEG mono */
case 0x02: /* ATRAC9 mono */ case 0x05: /* ATRAC9 / MPEG stereo */
case 0x05: /* ATRAC9 stereo */
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->channels = (subtype == 0x02) ? 1 : 2;
h->atrac9_info = read_u32be(h->start_offset+0x0c,sf); if (h->big_endian) {
/* 0x10: null? */ /* The Last of Us demo (PS3) */
loop_length = read_u32(h->start_offset+0x14,sf);
h->loop_start = read_u32(h->start_offset+0x18,sf);
h->loop_end = h->loop_start + loop_length; /* loop_start is -1 if not set */
h->codec = ATRAC9; /* 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->atrac9_info = read_u32be(h->start_offset+0x0c,sf);
/* 0x10: null? */
loop_length = read_u32(h->start_offset+0x14,sf);
h->loop_start = read_u32(h->start_offset+0x18,sf);
h->loop_end = h->loop_start + loop_length; /* loop_start is -1 if not set */
h->codec = ATRAC9;
}
break; break;
default: default:

View file

@ -282,11 +282,11 @@ static void load_cpk_name(STREAMFILE* sf, STREAMFILE* sf_acb, VGMSTREAM* vgmstre
return; return;
/* companion .acb probably loaded */ /* 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); close_streamfile(sf_acb);
} }
else { else {
load_acb_wave_name(sf_acb, vgmstream, waveid, port, is_memory); load_acb_wave_info(sf_acb, vgmstream, waveid, port, is_memory, 0);
} }
} }

View file

@ -154,8 +154,8 @@ static VGMSTREAM* init_vgmstream_encrypted_rpgmvo_riff(STREAMFILE* sf) {
e.key_size = 0x10; e.key_size = 0x10;
load_key(&cfg, e.keybuf, e.key_size); load_key(&cfg, e.keybuf, e.key_size);
cfg.start = 0x10; cfg.start = 0x10;
cfg.max_offset = 0x10; cfg.max_offset = 0x20;
e.temp_sf = setup_ogg_vorbis_streamfile(sf, &cfg); e.temp_sf = setup_ogg_vorbis_streamfile(sf, &cfg);
if (!e.temp_sf) goto fail; if (!e.temp_sf) goto fail;

View file

@ -57,8 +57,6 @@ static const fsbkey_info fsbkey_list[] = {
{ MODE_FSB4_STD, FSBKEY_ADD("ghfxhslrghfxhslr") }, // Cookie Run: Ovenbreak { 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("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("\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("G0KTrWjS9syqF7vVD6RaVXlFD91gMgkC") }, // Sekiro: Shadows Die Twice (PC)
{ MODE_FSB5_STD, FSBKEY_ADD("BasicEncryptionKey") }, // SCP: Unity (PC) { MODE_FSB5_STD, FSBKEY_ADD("BasicEncryptionKey") }, // SCP: Unity (PC)
{ MODE_FSB5_STD, FSBKEY_ADD("FXnTffGJ9LS855Gc") }, // Worms Rumble Beta (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("Aurogon666") }, // Afterimage demo (PC)
{ MODE_FSB5_STD, FSBKEY_ADD("IfYouLikeThosesSoundsWhyNotRenumerateTheir2Authors?") }, // Blanc (PC/Switch) { MODE_FSB5_STD, FSBKEY_ADD("IfYouLikeThosesSoundsWhyNotRenumerateTheir2Authors?") }, // Blanc (PC/Switch)
{ MODE_FSB5_STD, FSBKEY_ADD("L36nshM520") }, // Nishuihan Mobile (Android) { 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]); static const int fsbkey_list_count = sizeof(fsbkey_list) / sizeof(fsbkey_list[0]);

View file

@ -1254,6 +1254,24 @@ static const hcakey_info hcakey_list[] = {
// BlazBlue Entropy Effect (Early Access) (PC) // BlazBlue Entropy Effect (Early Access) (PC)
{29814655674508831}, // 0069EC457894661F {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_*/ #endif/*_HCA_KEYS_H_*/

View file

@ -389,7 +389,7 @@ VGMSTREAM* init_vgmstream_bsnf(STREAMFILE* sf) {
if (stream_size != get_streamfile_size(sb)) if (stream_size != get_streamfile_size(sb))
goto fail; 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; start_offset = 0x00;
/* build the VGMSTREAM */ /* build the VGMSTREAM */
@ -404,13 +404,18 @@ VGMSTREAM* init_vgmstream_bsnf(STREAMFILE* sf) {
vgmstream->num_streams = num_languages; vgmstream->num_streams = num_languages;
strncpy(vgmstream->stream_name, language, STREAM_NAME_SIZE); 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) { switch (codec) {
#ifdef VGM_USE_MPEG #ifdef VGM_USE_MPEG
case 0x0055: { case 0x0055: {
mpeg_custom_config cfg = { 0 }; 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); vgmstream->codec_data = init_mpeg_custom(sb, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg);
if (!vgmstream->codec_data) goto fail; if (!vgmstream->codec_data) goto fail;
@ -451,14 +456,20 @@ VGMSTREAM* init_vgmstream_bsnf(STREAMFILE* sf) {
case 0x42D2: { case 0x42D2: {
atrac9_config cfg = { 0 }; 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.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); cfg.config_data = read_u32be(extra_offset + 0x1c, sf);
vgmstream->codec_data = init_atrac9(&cfg); vgmstream->codec_data = init_atrac9(&cfg);
if (!vgmstream->codec_data) goto fail; if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_ATRAC9; vgmstream->coding_type = coding_ATRAC9;
vgmstream->layout_type = layout_none; 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; break;
} }
#endif #endif

View file

@ -14,8 +14,7 @@ VGMSTREAM* init_vgmstream_ktsc(STREAMFILE* sf) {
/* checks */ /* checks */
if (!is_id32be(0x00, sf, "KTSC")) if (!is_id32be(0x00, sf, "KTSC"))
goto fail; goto fail;
if (read_u32be(0x04, sf) != 0x01000001) /* version? */ /* 0x04: version? (0x01000001: common, 0x01000004: FE Three Houses) */
goto fail;
/* .ktsl2asbin: common [Atelier Ryza (PC)] /* .ktsl2asbin: common [Atelier Ryza (PC)]
* .asbin: Warriors Orochi 4 (PC) (assumed) */ * .asbin: Warriors Orochi 4 (PC) (assumed) */

View file

@ -1,18 +1,26 @@
#include "meta.h" #include "meta.h"
#include "../coding/coding.h" #include "../coding/coding.h"
#include "../layout/layout.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; typedef enum { NONE, MSADPCM, DSP, GCADPCM, ATRAC9, RIFF_ATRAC9, KOVS, KTSS, } ktsr_codec;
#define MAX_CHANNELS 8 #define MAX_CHANNELS 8
typedef struct { typedef struct {
uint32_t base_offset;
bool is_srsa;
int total_subsongs; int total_subsongs;
int target_subsong; int target_subsong;
ktsr_codec codec; ktsr_codec codec;
uint32_t audio_id;
int platform; int platform;
int format; int format;
uint32_t sound_id;
uint32_t sound_flags;
uint32_t config_flags;
int channels; int channels;
int sample_rate; int sample_rate;
@ -31,32 +39,73 @@ typedef struct {
char name[255+1]; char name[255+1];
} ktsr_header; } ktsr_header;
static VGMSTREAM* init_vgmstream_ktsr_internal(STREAMFILE* sf, bool is_srsa);
static int parse_ktsr(ktsr_header* ktsr, STREAMFILE* sf); 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 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); 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 */ /* KTSR - Koei Tecmo sound resource container */
VGMSTREAM* init_vgmstream_ktsr(STREAMFILE* sf) { 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; VGMSTREAM* vgmstream = NULL;
STREAMFILE* sf_b = NULL; STREAMFILE* sf_b = NULL;
ktsr_header ktsr = {0}; ktsr_header ktsr = {0};
int target_subsong = sf->stream_index; int target_subsong = sf->stream_index;
int separate_offsets = 0; int separate_offsets = 0;
ktsr.is_srsa = is_srsa;
if (ktsr.is_srsa) {
ktsr.base_offset = 0x10;
}
/* checks */ /* checks */
if (!is_id32be(0x00, sf, "KTSR")) if (!is_id32be(ktsr.base_offset + 0x00, sf, "KTSR"))
goto fail; return NULL;
if (read_u32be(0x04, sf) != 0x777B481A) /* hash(?) id: 0x777B481A=as, 0x0294DDFC=st, 0xC638E69E=gc */ if (read_u32be(ktsr.base_offset + 0x04, sf) != 0x777B481A) /* hash id: 0x777B481A=as, 0x0294DDFC=st, 0xC638E69E=gc */
goto fail; return NULL;
/* .ktsl2asbin: common [Atelier Ryza (PC/Switch), Nioh (PC)]
* .asbin: Warriors Orochi 4 (PC) */
if (!check_extensions(sf, "ktsl2asbin,asbin"))
goto fail;
/* KTSR can be a memory file (ktsl2asbin), streams (ktsl2stbin) and global config (ktsl2gcbin) /* KTSR can be a memory file (ktsl2asbin), streams (ktsl2stbin) and global config (ktsl2gcbin)
* This accepts .ktsl2asbin with internal data or external streams as subsongs. * 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; if (target_subsong == 0) target_subsong = 1;
ktsr.target_subsong = target_subsong; ktsr.target_subsong = target_subsong;
@ -66,11 +115,22 @@ VGMSTREAM* init_vgmstream_ktsr(STREAMFILE* sf) {
/* open companion body */ /* open companion body */
if (ktsr.is_external) { if (ktsr.is_external) {
const char* companion_ext = check_extensions(sf, "asbin") ? "stbin" : "ktsl2stbin"; if (ktsr.is_srsa) {
sf_b = open_streamfile_by_ext(sf, companion_ext); /* 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) { if (!sf_b) {
vgm_logi("KTSR: companion file '*.%s' not found\n", companion_ext); /* try (name).(ext), as seen in older games */
goto fail; 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 { else {
@ -172,15 +232,20 @@ fail:
return NULL; 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) { static VGMSTREAM* init_vgmstream_ktsr_sub(STREAMFILE* sf_b, ktsr_header* ktsr, VGMSTREAM* (*init_vgmstream)(STREAMFILE* sf), const char* ext) {
VGMSTREAM* sub_vgmstream = NULL; 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; if (!temp_sf) return NULL;
sub_vgmstream = init_vgmstream(temp_sf); sub_vgmstream = init_vgmstream(temp_sf);
close_streamfile(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->stream_size = ktsr->stream_sizes[0];
sub_vgmstream->num_streams = ktsr->total_subsongs; 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 */ /* platform + format to codec, simplified until more codec combos are found */
switch(ktsr->platform) { switch(ktsr->platform) {
case 0x01: /* PC */ case 0x01: /* PC */
case 0x05: /* PC/Steam [Fate/Samurai Remnant (PC)] */
if (ktsr->is_external) { if (ktsr->is_external) {
if (ktsr->format == 0x0005) if (ktsr->format == 0x0005)
ktsr->codec = KOVS; // Atelier Ryza (PC) ktsr->codec = KOVS; // Atelier Ryza (PC)
@ -284,11 +350,13 @@ static int parse_codec(ktsr_header* ktsr) {
if (ktsr->is_external) { if (ktsr->is_external) {
if (ktsr->format == 0x0005) if (ktsr->format == 0x0005)
ktsr->codec = KTSS; // [Ultra Kaiju Monster Rancher (Switch)] 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 else
goto fail; goto fail;
} }
else if (ktsr->format == 0x0000) else if (ktsr->format == 0x0000)
ktsr->codec = DSP; // Fire Emblem: Three Houses (Switch) ktsr->codec = DSP; // [Fire Emblem: Three Houses (Switch)]
else else
goto fail; goto fail;
break; 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 0x3DEA478D: /* external [Nioh (PC)] (smaller) */
case 0xDF92529F: /* external [Atelier Ryza (PC)] */ case 0xDF92529F: /* external [Atelier Ryza (PC)] */
case 0x6422007C: /* 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) /* 08 subtype? (ex. 0x522B86B9)
* 0c channels * 0c channels
* 10 ? (always 0x002706B8) * 10 ? (always 0x002706B8 / 7864523D in SoP)
* 14 external codec * 14 external codec
* 18 sample rate * 18 sample rate
* 1c num samples * 1c num samples
@ -420,16 +490,53 @@ fail:
return 0; 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) { static void build_name(ktsr_header* ktsr, STREAMFILE* sf) {
char sound_name[255] = {0}; char sound_name[255] = {0};
char config_name[255] = {0}; char config_name[255] = {0};
/* names can be different or same but usually config is better */ /* names can be different or same but usually config is better */
if (ktsr->sound_name_offset) { 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) { 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]) { //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 */ /* more configs than sounds is possible so we need target_id first */
uint32_t offset, end, name_offset; uint32_t offset, end, name_offset;
uint32_t stream_id; uint32_t stream_id;
offset = 0x40; offset = 0x40 + ktsr->base_offset;
end = get_streamfile_size(sf); end = get_streamfile_size(sf) - ktsr->base_offset;
while (offset < end) { while (offset < end) {
uint32_t type = read_u32be(offset + 0x00, sf); /* hash-id? */ uint32_t type = read_u32be(offset + 0x00, sf); /* hash-id? */
uint32_t size = read_u32le(offset + 0x04, sf); uint32_t size = read_u32le(offset + 0x04, sf);
switch(type) { switch(type) {
case 0xBD888C36: /* config */ case 0xBD888C36: /* config */
stream_id = read_u32be(offset + 0x08, sf); stream_id = read_u32le(offset + 0x08, sf);
if (stream_id != target_id) if (stream_id != ktsr->sound_id)
break; break;
ktsr->config_flags = read_u32le(offset + 0x0c, sf);
name_offset = read_u32le(offset + 0x28, sf); name_offset = read_u32le(offset + 0x28, sf);
if (name_offset > 0) if (name_offset > 0)
ktsr->config_name_offset = offset + name_offset; 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) { static int parse_ktsr(ktsr_header* ktsr, STREAMFILE* sf) {
uint32_t offset, end, header_offset, name_offset; uint32_t offset, end, header_offset, name_offset;
uint32_t stream_id = 0, stream_count; uint32_t stream_count;
/* 00: KTSR /* 00: KTSR
* 04: type * 04: type
* 08: version? * 08: version?
* 0a: unknown (usually 00, 02/03 seen in Vita) * 0a: unknown (usually 00, 02/03 seen in Vita)
* 0b: platform (01=PC, 03=Vita, 04=Switch) * 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 * 10: null
* 14: null * 14: null
* 18: file size * 18: file size
@ -491,25 +600,28 @@ static int parse_ktsr(ktsr_header* ktsr, STREAMFILE* sf) {
* up to 40: reserved * up to 40: reserved
* until end: entries (totals not defined) */ * 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; 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; goto fail;
}
offset = 0x40; offset = 0x40 + ktsr->base_offset;
end = get_streamfile_size(sf); end = get_streamfile_size(sf) - ktsr->base_offset;
while (offset < end) { while (offset < end) {
uint32_t type = read_u32be(offset + 0x00, sf); /* hash-id? */ uint32_t type = read_u32be(offset + 0x00, sf); /* hash-id? */
uint32_t size = read_u32le(offset + 0x04, sf); uint32_t size = read_u32le(offset + 0x04, sf);
/* parse chunk-like subfiles, usually N configs then N songs */ /* parse chunk-like subfiles, usually N configs then N songs */
switch(type) { switch(type) {
case 0x6172DBA8: /* padding (empty) */ case 0x6172DBA8: /* ? (mostly empty) */
case 0xBD888C36: /* config (floats, stream id, etc, may have extended name) */ 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 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) */ case 0x836FBECA: /* unknown (~0x300, encrypted? table + data) */
break; break;
@ -517,9 +629,10 @@ static int parse_ktsr(ktsr_header* ktsr, STREAMFILE* sf) {
ktsr->total_subsongs++; ktsr->total_subsongs++;
/* sound table: /* sound table:
* 08: stream id (used in several places) * 08: current/stream id (used in several places)
* 0c: unknown (low number but not version?) * 0c: flags (sounds only; other types are similar but different bits)
* 0e: external flag * 0x08: encrypted names (only used after ASRS was introduced?)
* 0x10000: external flag
* 10: sub-streams? * 10: sub-streams?
* 14: offset to header offset * 14: offset to header offset
* 18: offset to name * 18: offset to name
@ -528,11 +641,10 @@ static int parse_ktsr(ktsr_header* ktsr, STREAMFILE* sf) {
* --: header * --: header
* --: subheader (varies) */ * --: subheader (varies) */
if (ktsr->total_subsongs == ktsr->target_subsong) { if (ktsr->total_subsongs == ktsr->target_subsong) {
stream_id = read_u32be(offset + 0x08,sf); ktsr->sound_id = read_u32le(offset + 0x08,sf);
//ktsr->is_external = read_u16le(offset + 0x0e,sf); ktsr->sound_flags = read_u32le(offset + 0x0c,sf);
stream_count = read_u32le(offset + 0x10,sf); stream_count = read_u32le(offset + 0x10,sf);
if (stream_count != 1) { if (stream_count != 1) {
VGM_LOG("ktsr: unknown stream count\n"); 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) if (ktsr->target_subsong > ktsr->total_subsongs)
goto fail; goto fail;
parse_longname(ktsr, sf, stream_id); parse_longname(ktsr, sf);
build_name(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; return 1;
fail: fail:
vgm_logi("KTSR: unknown variation (report)\n"); vgm_logi("KTSR: unknown variation (report)\n");

View 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

View file

@ -618,7 +618,6 @@ VGMSTREAM* init_vgmstream_opus_nop(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_opus_shinen(STREAMFILE* sf); VGMSTREAM* init_vgmstream_opus_shinen(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_opus_nus3(STREAMFILE* sf); VGMSTREAM* init_vgmstream_opus_nus3(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_opus_sps_n1(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_opusx(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_opus_prototype(STREAMFILE* sf); VGMSTREAM* init_vgmstream_opus_prototype(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_opus_opusnx(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_utk(STREAMFILE *streamFile);
VGMSTREAM* init_vgmstream_nxa1(STREAMFILE* sf);
VGMSTREAM * init_vgmstream_adpcm_capcom(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_adpcm_capcom(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ue4opus(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(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_awb_memory(STREAMFILE * streamFile, STREAMFILE *acbFile); VGMSTREAM * init_vgmstream_awb_memory(STREAMFILE * streamFile, STREAMFILE *acbFile);
VGMSTREAM * init_vgmstream_acb(STREAMFILE * streamFile); VGMSTREAM* init_vgmstream_acb(STREAMFILE* sf);
void load_acb_wave_name(STREAMFILE *acbFile, VGMSTREAM* vgmstream, int waveid, int port, int is_memory); 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); 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_dsp(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_nub_idsp(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_nub_idsp(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_nub_is14(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_nub_is14(STREAMFILE * streamFile);
VGMSTREAM* init_vgmstream_nub_caf(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_xwv_valve(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_kwb(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_xws(STREAMFILE* sf); VGMSTREAM* init_vgmstream_xws(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_snd_koei(STREAMFILE* sf);
VGMSTREAM * init_vgmstream_lrmd(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_imuse(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_ktsr(STREAMFILE* sf); VGMSTREAM* init_vgmstream_ktsr(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_asrs(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_mups(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_snds(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_nxof(STREAMFILE* sf);
#endif /*_META_H*/ #endif /*_META_H*/

View file

@ -30,7 +30,7 @@ VGMSTREAM* init_vgmstream_nub(STREAMFILE* sf) {
goto fail; goto fail;
/* .nub: standard /* .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")) if (!check_extensions(sf, "nub,nub2"))
goto fail; goto fail;
@ -97,47 +97,52 @@ VGMSTREAM* init_vgmstream_nub(STREAMFILE* sf) {
header_size = align_size_to_block(subheader_start + subheader_size, 0x10); header_size = align_size_to_block(subheader_start + subheader_size, 0x10);
switch(codec) { switch(codec) {
case 0x00: /* (none) (xma1) */ case 0x00: /* xma1 */
fake_ext = "xma"; fake_ext = "xma";
init_vgmstream_function = init_vgmstream_nub_xma; init_vgmstream_function = init_vgmstream_nub_xma;
break; break;
case 0x01: /* "wav\0" */ case 0x01:
fake_ext = "wav"; fake_ext = "wav";
init_vgmstream_function = init_vgmstream_nub_wav; init_vgmstream_function = init_vgmstream_nub_wav;
break; break;
case 0x02: /* "vag\0" */ case 0x02:
fake_ext = "vag"; fake_ext = "vag";
init_vgmstream_function = init_vgmstream_nub_vag; init_vgmstream_function = init_vgmstream_nub_vag;
break; break;
case 0x03: /* "at3\0" */ case 0x03:
fake_ext = "at3"; fake_ext = "at3";
init_vgmstream_function = init_vgmstream_nub_at3; init_vgmstream_function = init_vgmstream_nub_at3;
break; break;
case 0x04: /* "xma\0" (xma2 old) */ case 0x04: /* xma2 old */
case 0x08: /* "xma\0" (xma2 new) */ case 0x08: /* xma2 new */
fake_ext = "xma"; fake_ext = "xma";
init_vgmstream_function = init_vgmstream_nub_xma; init_vgmstream_function = init_vgmstream_nub_xma;
break; break;
case 0x05: /* "dsp\0" */ case 0x05:
fake_ext = "dsp"; fake_ext = "dsp";
init_vgmstream_function = init_vgmstream_nub_dsp; init_vgmstream_function = init_vgmstream_nub_dsp;
break; break;
case 0x06: /* "idsp" */ case 0x06:
fake_ext = "idsp"; fake_ext = "idsp";
init_vgmstream_function = init_vgmstream_nub_idsp; init_vgmstream_function = init_vgmstream_nub_idsp;
break; break;
case 0x07: /* "is14" */ case 0x07:
fake_ext = "is14"; fake_ext = "is14";
init_vgmstream_function = init_vgmstream_nub_is14; init_vgmstream_function = init_vgmstream_nub_is14;
break; break;
case 0x09:
fake_ext = "caf";
init_vgmstream_function = init_vgmstream_nub_caf;
break;
default: default:
VGM_LOG("NUB: unknown codec %x\n", codec); VGM_LOG("NUB: unknown codec %x\n", codec);
goto fail; goto fail;
@ -235,9 +240,10 @@ static STREAMFILE* setup_nub_streamfile(STREAMFILE* sf, off_t header_offset, siz
//todo could be simplified //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* init_vgmstream_nub_wav(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL; VGMSTREAM* vgmstream = NULL;
STREAMFILE* temp_sf = NULL;
off_t start_offset; off_t start_offset;
int loop_flag, channel_count, sample_rate; int loop_flag, channel_count, sample_rate;
size_t data_size, loop_start, loop_length; size_t data_size, loop_start, loop_length;
@ -246,10 +252,10 @@ VGMSTREAM* init_vgmstream_nub_wav(STREAMFILE* sf) {
/* checks */ /* checks */
if (!is_id32be(0x00,sf, "wav\0"))
return NULL;
if (!check_extensions(sf, "wav,lwav")) if (!check_extensions(sf, "wav,lwav"))
goto fail; return NULL;
if (read_32bitBE(0x00,sf) != 0x77617600) /* "wav\0" "*/
goto fail;
if (guess_endian32(0x1c, sf)) { if (guess_endian32(0x1c, sf)) {
read_32bit = read_32bitBE; read_32bit = read_32bitBE;
@ -276,6 +282,21 @@ VGMSTREAM* init_vgmstream_nub_wav(STREAMFILE* sf) {
start_offset = 0xD0; 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 */ /* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag); vgmstream = allocate_vgmstream(channel_count,loop_flag);
@ -296,6 +317,7 @@ VGMSTREAM* init_vgmstream_nub_wav(STREAMFILE* sf) {
return vgmstream; return vgmstream;
fail: fail:
close_streamfile(temp_sf);
close_vgmstream(vgmstream); close_vgmstream(vgmstream);
return NULL; return NULL;
} }
@ -310,10 +332,10 @@ VGMSTREAM* init_vgmstream_nub_vag(STREAMFILE* sf) {
/* checks */ /* checks */
if (!is_id32be(0x00,sf, "vag\0"))
return NULL;
if ( !check_extensions(sf, "vag")) if ( !check_extensions(sf, "vag"))
goto fail; return NULL;
if (read_32bitBE(0x00,sf) != 0x76616700) /* "vag\0" */
goto fail;
if (guess_endian32(0x1c, sf)) { if (guess_endian32(0x1c, sf)) {
read_32bit = read_32bitBE; read_32bit = read_32bitBE;
@ -362,15 +384,13 @@ fail:
VGMSTREAM* init_vgmstream_nub_at3(STREAMFILE* sf) { VGMSTREAM* init_vgmstream_nub_at3(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL; VGMSTREAM* vgmstream = NULL;
STREAMFILE* temp_sf = NULL; STREAMFILE* temp_sf = NULL;
off_t subfile_offset = 0;
size_t subfile_size = 0;
/* checks */ /* checks */
if (!is_id32be(0x00,sf, "at3\0"))
return NULL;
if (!check_extensions(sf,"at3")) if (!check_extensions(sf,"at3"))
goto fail; return NULL;
if (read_32bitBE(0x00,sf) != 0x61743300) /* "at3\0" */
goto fail;
/* info header */ /* info header */
/* 0x20: loop start (in samples) */ /* 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) */ /* 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 */ /* we can just ignore and use RIFF at data start since it has the same info */
subfile_offset = 0x100; uint32_t subfile_offset = 0x100;
subfile_size = read_32bitLE(subfile_offset + 0x04, sf) + 0x08; /* RIFF size */ uint32_t subfile_size = read_32bitLE(subfile_offset + 0x04, sf) + 0x08; /* RIFF size */
temp_sf = setup_subfile_streamfile(sf, subfile_offset,subfile_size, NULL); temp_sf = setup_subfile_streamfile(sf, subfile_offset,subfile_size, NULL);
if (!temp_sf) goto fail; if (!temp_sf) goto fail;
@ -410,9 +430,9 @@ VGMSTREAM* init_vgmstream_nub_xma(STREAMFILE* sf) {
/* checks */ /* checks */
if (!check_extensions(sf,"xma")) 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 */ /* nub v2.1 */
nus_codec = read_32bitBE(0x0C,sf); nus_codec = read_32bitBE(0x0C,sf);
data_size = read_32bitBE(0x14,sf); data_size = read_32bitBE(0x14,sf);
@ -435,7 +455,7 @@ VGMSTREAM* init_vgmstream_nub_xma(STREAMFILE* sf) {
chunk_size = header_size; chunk_size = header_size;
} }
else { else {
goto fail; return NULL;
} }
start_offset = align_size_to_block(chunk_offset + header_size, 0x10); start_offset = align_size_to_block(chunk_offset + header_size, 0x10);
@ -520,10 +540,10 @@ VGMSTREAM* init_vgmstream_nub_dsp(STREAMFILE* sf) {
/* checks */ /* checks */
if (!is_id32be(0x00,sf, "dsp\0"))
return NULL;
if (!check_extensions(sf,"dsp")) if (!check_extensions(sf,"dsp"))
goto fail; return NULL;
if (read_32bitBE(0x00,sf) != 0x64737000) /* "dsp\0" */
goto fail;
/* paste header+data together and pass to meta, which has loop info too */ /* paste header+data together and pass to meta, which has loop info too */
header_offset = 0xBC; header_offset = 0xBC;
@ -554,10 +574,10 @@ VGMSTREAM* init_vgmstream_nub_idsp(STREAMFILE* sf) {
/* checks */ /* checks */
if (!is_id32be(0x00,sf, "idsp"))
return NULL;
if (!check_extensions(sf,"idsp")) if (!check_extensions(sf,"idsp"))
goto fail; return NULL;
if (read_32bitBE(0x00,sf) != 0x69647370) /* "idsp" */
goto fail;
/* info header */ /* info header */
/* 0x20: loop start (in samples) */ /* 0x20: loop start (in samples) */
@ -595,10 +615,10 @@ VGMSTREAM* init_vgmstream_nub_is14(STREAMFILE* sf) {
/* checks */ /* checks */
if (!is_id32be(0x00,sf, "is14"))
return NULL;
if (!check_extensions(sf,"is14")) if (!check_extensions(sf,"is14"))
goto fail; return NULL;
if (read_32bitBE(0x00,sf) != 0x69733134) /* "is14" */
goto fail;
if (guess_endian32(0x1c, sf)) { if (guess_endian32(0x1c, sf)) {
read_32bit = read_32bitBE; read_32bit = read_32bitBE;
@ -632,3 +652,41 @@ fail:
close_vgmstream(vgmstream); close_vgmstream(vgmstream);
return NULL; 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;
}

View file

@ -2,8 +2,8 @@
#include "../coding/coding.h" #include "../coding/coding.h"
/* Entergram NXA Opus [Higurashi no Naku Koro ni Hou (Switch), Gensou Rougoku no Kaleidoscope (Switch)] */ /* Entergram NXA1 Opus [Higurashi no Naku Koro ni Hou (Switch), Gensou Rougoku no Kaleidoscope (Switch)] */
VGMSTREAM* init_vgmstream_opus_nxa(STREAMFILE* sf) { VGMSTREAM* init_vgmstream_nxa1(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL; VGMSTREAM* vgmstream = NULL;
off_t start_offset; off_t start_offset;
int loop_flag, channels, type, sample_rate; int loop_flag, channels, type, sample_rate;
@ -11,10 +11,10 @@ VGMSTREAM* init_vgmstream_opus_nxa(STREAMFILE* sf) {
size_t data_size, frame_size; size_t data_size, frame_size;
/* checks */ /* checks */
if (!check_extensions(sf, "nxa"))
goto fail;
if (!is_id32be(0x00, sf, "NXA1")) if (!is_id32be(0x00, sf, "NXA1"))
goto fail; goto fail;
if (!check_extensions(sf, "nxa"))
goto fail;
start_offset = 0x30; start_offset = 0x30;
type = read_u32le(0x04, sf); type = read_u32le(0x04, sf);
@ -36,7 +36,7 @@ VGMSTREAM* init_vgmstream_opus_nxa(STREAMFILE* sf) {
vgmstream = allocate_vgmstream(channels, loop_flag); vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
vgmstream->meta_type = meta_NXA; vgmstream->meta_type = meta_NXA1;
vgmstream->sample_rate = sample_rate; vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = num_samples; vgmstream->num_samples = num_samples;
vgmstream->loop_start_sample = loop_start; vgmstream->loop_start_sample = loop_start;

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

View file

@ -4,7 +4,7 @@
#include "../layout/layout.h" #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 enum { PCM, RIFF_AT3, XMA2, MSADPCM, XWMA, DSP, OPUSNX, RIFF_AT9, VAG } psb_codec_t;
typedef struct { typedef struct {

View file

@ -169,6 +169,10 @@ static int read_fmt(int big_endian, STREAMFILE* sf, off_t offset, riff_fmt_chunk
case 0x0002: /* MSADPCM */ case 0x0002: /* MSADPCM */
if (fmt->bps == 4) { 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; fmt->coding_type = coding_MSADPCM;
if (!msadpcm_check_coefs(sf, fmt->offset + 0x08 + 0x14)) if (!msadpcm_check_coefs(sf, fmt->offset + 0x08 + 0x14))
goto fail; goto fail;
@ -180,7 +184,7 @@ static int read_fmt(int big_endian, STREAMFILE* sf, off_t offset, riff_fmt_chunk
goto fail; goto fail;
} }
break; break;
case 0x003: /* floating point PCM */ case 0x0003: /* floating point PCM */
if (fmt->bps == 32) { if (fmt->bps == 32) {
fmt->coding_type = coding_PCMFLOAT; fmt->coding_type = coding_PCMFLOAT;
} else { } else {
@ -190,6 +194,8 @@ static int read_fmt(int big_endian, STREAMFILE* sf, off_t offset, riff_fmt_chunk
break; break;
case 0x0011: /* MS-IMA ADPCM [Layton Brothers: Mystery Room (iOS/Android)] */ case 0x0011: /* MS-IMA ADPCM [Layton Brothers: Mystery Room (iOS/Android)] */
/* IMAADPCMWAVEFORMAT extra data:
* - samples per frame (16b) */
if (fmt->bps != 4) goto fail; if (fmt->bps != 4) goto fail;
fmt->coding_type = coding_MS_IMA; fmt->coding_type = coding_MS_IMA;
break; break;
@ -391,8 +397,9 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) {
* .xms: Ty the Tasmanian Tiger (Xbox) * .xms: Ty the Tasmanian Tiger (Xbox)
* .mus: Burnout Legends/Dominator (PSP) * .mus: Burnout Legends/Dominator (PSP)
* .dat/ldat: RollerCoaster Tycoon 1/2 (PC) * .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; goto fail;
} }
@ -409,6 +416,9 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) {
else if (codec == 0x0069 && riff_size + 0x04 == file_size) else if (codec == 0x0069 && riff_size + 0x04 == file_size)
riff_size -= 0x04; /* [Halo 2 (PC)] (possibly bad extractor? 'Gravemind Tool') */ 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) else if (codec == 0x0000 && riff_size + 0x04 == file_size)
riff_size -= 0x04; /* [Headhunter (DC), Bomber hehhe (DC)] */ riff_size -= 0x04; /* [Headhunter (DC), Bomber hehhe (DC)] */

View file

@ -4,8 +4,9 @@
/* RSTM - from Rockstar games [Midnight Club 3, Bully - Canis Canim Edit (PS2)] */ /* RSTM - from Rockstar games [Midnight Club 3, Bully - Canis Canim Edit (PS2)] */
VGMSTREAM* init_vgmstream_rstm_rockstar(STREAMFILE* sf) { VGMSTREAM* init_vgmstream_rstm_rockstar(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL; VGMSTREAM* vgmstream = NULL;
uint32_t start_offset; off_t stream_offset, loop_start, loop_end;
int channels, loop_flag; size_t stream_size;
int sample_rate, channels, loop_flag;
/* checks */ /* checks */
@ -14,30 +15,38 @@ VGMSTREAM* init_vgmstream_rstm_rockstar(STREAMFILE* sf) {
/* .rsm: in filelist /* .rsm: in filelist
* .rstm: header id */ * .rstm: header id */
if (!check_extensions(sf,"rsm,rstm")) if (!check_extensions(sf, "rsm,rstm"))
return NULL; return NULL;
loop_flag = (read_s32le(0x24,sf) > 0); sample_rate = read_s32le(0x08, sf);
channels = read_s32le(0x0C,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 */ /* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels,loop_flag); vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
vgmstream->meta_type = meta_RSTM_ROCKSTAR; vgmstream->meta_type = meta_RSTM_ROCKSTAR;
vgmstream->sample_rate = read_s32le(0x08,sf); vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = ps_bytes_to_samples(read_u32le(0x20,sf),channels); vgmstream->num_samples = ps_bytes_to_samples(stream_size, channels);
vgmstream->loop_start_sample = ps_bytes_to_samples(read_u32le(0x24,sf),channels); vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start, channels);
vgmstream->loop_end_sample = vgmstream->num_samples; vgmstream->loop_end_sample = ps_bytes_to_samples(loop_end, channels);
vgmstream->coding_type = coding_PSX; vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave; vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x10; vgmstream->interleave_block_size = 0x10;
/* open the file for reading */ /* open the file for reading */
if ( !vgmstream_open_stream(vgmstream, sf, start_offset) ) if ( !vgmstream_open_stream(vgmstream, sf, stream_offset) )
goto fail; goto fail;
return vgmstream; return vgmstream;

View file

@ -74,27 +74,30 @@ fail:
return NULL; return NULL;
} }
#define SEGMENT_MAX 3
/* Nippon Ichi SPS wrapper (segmented) [Penny-Punching Princess (Switch), Disgaea 4 Complete (PC)] */ /* Nippon Ichi SPS wrapper (segmented) [Penny-Punching Princess (Switch), Disgaea 4 Complete (PC)] */
VGMSTREAM* init_vgmstream_sps_n1_segmented(STREAMFILE* sf) { VGMSTREAM* init_vgmstream_sps_n1_segmented(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL; VGMSTREAM* vgmstream = NULL;
off_t segment_offset;
size_t data_size, max_size; size_t data_size, max_size;
int loop_flag, type, sample_rate; int loop_flag, type, sample_rate;
int i, segment;
init_vgmstream_t init_vgmstream = NULL; init_vgmstream_t init_vgmstream = NULL;
const char* extension; const char* extension;
segmented_layout_data* data = NULL; segmented_layout_data* data = NULL;
int segment_count, loop_start_segment, loop_end_segment; int loop_start_segment, loop_end_segment;
/* checks */ /* 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) */ * .nlsd: Disgaea 4 Complete (PC) */
if (!check_extensions(sf, "at9,nlsd")) if (!check_extensions(sf, "at9,nlsd"))
goto fail; return NULL;
type = read_u32le(0x00,sf);
data_size = read_u32le(0x04,sf); data_size = read_u32le(0x04,sf);
sample_rate = read_u16le(0x08,sf); sample_rate = read_u16le(0x08,sf);
/* 0x0a: flag? (stereo?) */ /* 0x0a: flag? (stereo?) */
@ -113,23 +116,58 @@ VGMSTREAM* init_vgmstream_sps_n1_segmented(STREAMFILE* sf) {
break; break;
default: 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 /* segmented using 3 files (intro/loop/outro). non-segmented wrapper is the same
* but with loop samples instead of sub-sizes */ * 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; max_size = 0;
segment_count = 0; segment_count = 0;
for (i = 0; i < 3; i++) { for (int i = 0; i < SEGMENT_MAX; i++) {
size_t segment_size = read_u32le(0x10 + 0x04*i,sf); /* may only set 1 segment, with empty intro/outro (Disgaea4's bgm_185) */
max_size += segment_size; if (segment_sizes[i])
/* may only set 1 segment (Disgaea4's bgm_185) */
if (segment_size)
segment_count++; segment_count++;
max_size += segment_sizes[i];
} }
if (data_size != max_size) if (data_size != max_size)
goto fail; goto fail;
@ -144,25 +182,21 @@ VGMSTREAM* init_vgmstream_sps_n1_segmented(STREAMFILE* sf) {
/* open each segment subfile */ /* open each segment subfile */
segment = 0; segment = 0;
for (i = 0; i < 3; i++) { for (int i = 0; i < SEGMENT_MAX; i++) {
STREAMFILE* temp_sf; if (!segment_sizes[i])
size_t segment_size = read_u32le(0x10 + 0x04*i,sf);
if (!segment_size)
continue; 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; if (!temp_sf) goto fail;
data->segments[segment] = init_vgmstream(temp_sf); data->segments[segment] = init_vgmstream(temp_sf);
close_streamfile(temp_sf); close_streamfile(temp_sf);
if (!data->segments[segment]) goto fail; if (!data->segments[segment]) goto fail;
segment_offset += segment_size;
segment++; segment++;
if (type == 9) { 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 //not correct for all files, no idea how to calculate
data->segments[segment]->num_samples -= 374; data->segments[segment]->num_samples -= 374;
} }

View file

@ -16,7 +16,6 @@ VGMSTREAM * init_vgmstream_sthd(STREAMFILE *sf) {
goto fail; goto fail;
/* first block has special values */ /* first block has special values */
if (read_u16le(0x04,sf) != 0x0800 || if (read_u16le(0x04,sf) != 0x0800 ||
read_u32le(0x0c,sf) != 0x0001 ||
read_u32le(0x14,sf) != 0x0000) read_u32le(0x14,sf) != 0x0000)
goto fail; goto fail;

View file

@ -1,8 +1,10 @@
#include "meta.h" #include "meta.h"
#include "../coding/coding.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 { typedef struct {
int tracks; int tracks;
int channels; int channels;
@ -103,6 +105,29 @@ VGMSTREAM* init_vgmstream_str_wav(STREAMFILE* sf) {
vgmstream->interleave_block_size = strwav.interleave; vgmstream->interleave_block_size = strwav.interleave;
break; 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: case DSP:
vgmstream->coding_type = coding_NGC_DSP; vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_interleave; vgmstream->layout_type = layout_interleave;
@ -173,6 +198,26 @@ VGMSTREAM* init_vgmstream_str_wav(STREAMFILE* sf) {
} }
#endif #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: default:
goto fail; goto fail;
} }
@ -286,7 +331,7 @@ static int parse_header(STREAMFILE* sf_h, STREAMFILE* sf_b, strwav_header* strwa
/* 0x10c: header size */ /* 0x10c: header size */
strwav->codec = IMA; 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"); ;VGM_LOG("STR+WAV: header TAZd (PC)\n");
return 1; return 1;
} }
@ -435,8 +480,8 @@ static int parse_header(STREAMFILE* sf_h, STREAMFILE* sf_b, strwav_header* strwa
strwav->codec = PSX; strwav->codec = PSX;
strwav->interleave = strwav->tracks > 1 ? 0x8000 : 0x8000; strwav->interleave = strwav->tracks > 1 ? 0x8000 : 0x8000;
//todo: tracks are stereo blocks of size 0x20000*tracks, containing 4 interleaves of 0x8000: if (strwav->tracks > 1) /* hack */
// | 1 2 1 2 | 3 4 3 4 | 5 6 5 6 | 1 2 1 2 | 3 4 3 4 | 5 6 5 6 | ... strwav->codec = PSX_chunked;
;VGM_LOG("STR+WAV: header ZPb (PS2)\n"); ;VGM_LOG("STR+WAV: header ZPb (PS2)\n");
return 1; return 1;
} }
@ -498,6 +543,32 @@ static int parse_header(STREAMFILE* sf_h, STREAMFILE* sf_b, strwav_header* strwa
return 1; 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] */ /* Taz Wanted (PC)[2002] */
/* Zapper: One Wicked Cricket! Beta (Xbox)[2002] */ /* Zapper: One Wicked Cricket! Beta (Xbox)[2002] */
if ( read_u32be(0x04,sf_h) == 0x00000900 && 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) */ /* 0x4c: ? (some low number) */
strwav->tracks = read_u8 (0x4e,sf_h); strwav->tracks = read_u8 (0x4e,sf_h);
/* 0x4f: 16 bps */ /* 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 */ /* 0x64: channels */
/* 0x70+: tables */ /* 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] */ /* Tak and the Guardians of Gross (Wii)[2008] */
/* The House of the Dead: Overkill (Wii)[2009] (not Blitz but still the same format) */ /* The House of the Dead: Overkill (Wii)[2009] (not Blitz but still the same format) */
/* All Star Karate (Wii)[2010] */ /* All Star Karate (Wii)[2010] */
/* Karaoke Revolution (Wii)[2010] */
if ((read_u32be(0x04,sf_h) == 0x00000800 || if ((read_u32be(0x04,sf_h) == 0x00000800 ||
read_u32be(0x04,sf_h) == 0x00000700) && /* rare? */ read_u32be(0x04,sf_h) == 0x00000700) && /* rare? */
read_u32be(0x08,sf_h) != 0x00000000 && 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->codec = DSP;
strwav->coefs_table = 0x7c; strwav->coefs_table = 0x7c;
strwav->interleave = strwav->channels > 4 ? 0x4000 : 0x8000; 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; return 1;
} }
/* The House of the Dead: Overkill (PS3)[2009] (not Blitz but still the same format) */ /* 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 || if ((read_u32be(0x04,sf_h) == 0x00000800 ||
read_u32be(0x04,sf_h) == 0x00000700) && /* rare? */ read_u32be(0x04,sf_h) == 0x00000700) && /* rare? */
read_u32be(0x08,sf_h) != 0x00000000 && 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->sample_rate = read_s32be(0x38,sf_h);
strwav->flags = read_u32be(0x3c,sf_h); strwav->flags = read_u32be(0x3c,sf_h);
strwav->channels = read_s32be(0x70,sf_h); /* tracks of 1ch */ strwav->channels = read_s32be(0x70,sf_h);
strwav->interleave = strwav->channels > 4 ? 0x4000 : 0x8000;
/* 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;
}
strwav->codec = PSX;
;VGM_LOG("STR+WAV: header HOTDO (PS3)\n"); ;VGM_LOG("STR+WAV: header HOTDO (PS3)\n");
return 1; return 1;
} }

View 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

View file

@ -841,7 +841,7 @@ static void set_body_chunk(txth_header* txth) {
if (!txth->sf_body) if (!txth->sf_body)
return; 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) if (txth->subsong_count > 1 && txth->subsong_count == txth->chunk_count)
txth->chunk_number = txth->target_subsong; txth->chunk_number = txth->target_subsong;
if (txth->chunk_number == 0) if (txth->chunk_number == 0)

View file

@ -1012,7 +1012,7 @@ static int parse_values(ubi_bao_header* bao) {
} }
/* set codec */ /* 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; VGM_LOG("UBI BAO: unknown stream_type at %x\n", (uint32_t)bao->header_offset); goto fail;
goto fail; goto fail;
} }

View file

@ -575,6 +575,7 @@ VGMSTREAM* init_vgmstream_wwise_bnk(STREAMFILE* sf, int* p_prefetch) {
switch(ww.channel_layout) { switch(ww.channel_layout) {
case mapping_7POINT1_surround: cfg.coupled_count = 3; break; /* 2ch+2ch+2ch+1ch+1ch, 5 streams */ 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_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_QUAD_side: cfg.coupled_count = 2; break; /* 2ch+2ch, 2 streams */
case mapping_2POINT1_xiph: /* 2ch+1ch, 2 streams */ case mapping_2POINT1_xiph: /* 2ch+1ch, 2 streams */
case mapping_STEREO: cfg.coupled_count = 1; break; /* 2ch, 1 stream */ case mapping_STEREO: cfg.coupled_count = 1; break; /* 2ch, 1 stream */

View file

@ -5,7 +5,7 @@
VGMSTREAM * init_vgmstream_xa_xa30(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_xa_xa30(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
off_t start_offset; off_t start_offset;
int loop_flag, channel_count, codec, interleave_block_size; int loop_flag, channel_count, codec, interleave_block_size;
size_t stream_size; size_t stream_size;
int total_subsongs, target_subsong = streamFile->stream_index; int total_subsongs, target_subsong = streamFile->stream_index;
@ -31,7 +31,7 @@ VGMSTREAM * init_vgmstream_xa_xa30(STREAMFILE *streamFile) {
codec = read_32bitLE(0x0c,streamFile); codec = read_32bitLE(0x0c,streamFile);
start_offset = read_32bitLE(0x10 + 0x04*(target_subsong-1),streamFile); start_offset = read_32bitLE(0x10 + 0x04*(target_subsong-1),streamFile);
stream_size = read_32bitLE(0x18 + 0x04*(target_subsong-1),streamFile); stream_size = read_32bitLE(0x18 + 0x04*(target_subsong-1),streamFile);
interleave_block_size = read_32bitLE(0x24, streamFile); interleave_block_size = read_32bitLE(0x24, streamFile);
/* build the VGMSTREAM */ /* build the VGMSTREAM */
@ -48,14 +48,14 @@ VGMSTREAM * init_vgmstream_xa_xa30(STREAMFILE *streamFile) {
case 0x00: /* PCM (rare, seen in Driver 3) */ case 0x00: /* PCM (rare, seen in Driver 3) */
vgmstream->coding_type = coding_PCM16LE; vgmstream->coding_type = coding_PCM16LE;
vgmstream->layout_type = layout_interleave; vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = interleave_block_size / 2; vgmstream->interleave_block_size = interleave_block_size / 2;
vgmstream->num_samples = pcm_bytes_to_samples(stream_size, channel_count, 16); vgmstream->num_samples = pcm_bytes_to_samples(stream_size, channel_count, 16);
break; break;
case 0x01: /* MS-IMA variation */ case 0x01: /* MS-IMA variation */
vgmstream->coding_type = coding_REF_IMA; vgmstream->coding_type = coding_REF_IMA;
vgmstream->layout_type = layout_none; vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size = interleave_block_size; vgmstream->interleave_block_size = interleave_block_size;
vgmstream->num_samples = ms_ima_bytes_to_samples(stream_size, vgmstream->interleave_block_size, channel_count); vgmstream->num_samples = ms_ima_bytes_to_samples(stream_size, vgmstream->interleave_block_size, channel_count);
break; break;

View file

@ -1,10 +1,11 @@
#ifndef _BITSTREAM_LSB_H #ifndef _BITSTREAM_LSB_H
#define _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. /* 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). */ * 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 /* 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) */ * (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, 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff,
0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff, 0x0001ffff, 0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff, 0x0001ffff,
0x0003ffff, 0x0007ffff, 0x000fffff, 0x001fffff, 0x003fffff, 0x007fffff, 0x00ffffff, 0x01ffffff, 0x03ffffff, 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 */ pos = ib->b_off / 8; /* byte offset */
shift = ib->b_off % 8; /* bit sub-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; val = ib->buf[pos+0] >> shift;
if (bits + shift > 8) { if (bits + shift > 8) {

View file

@ -1,10 +1,11 @@
#ifndef _BITSTREAM_MSB_H #ifndef _BITSTREAM_MSB_H
#define _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. /* 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). */ * Kept in .h since it's slightly faster (compiler can optimize statics better using default compile flags). */
typedef struct { typedef struct {
@ -14,7 +15,7 @@ typedef struct {
uint32_t b_off; /* current offset in bits inside buffer */ uint32_t b_off; /* current offset in bits inside buffer */
} bitstream_t; } bitstream_t;
/* convenience util */
static inline void bm_setup(bitstream_t* bs, uint8_t* buf, size_t bufsize) { static inline void bm_setup(bitstream_t* bs, uint8_t* buf, size_t bufsize) {
bs->buf = buf; bs->buf = buf;
bs->bufsize = bufsize; bs->bufsize = bufsize;
@ -60,10 +61,20 @@ static inline int bm_pos(bitstream_t* bs) {
return bs->b_off; 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). */ /* 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) { static inline int bm_get(bitstream_t* ib, uint32_t bits, uint32_t* value) {
uint32_t shift, pos, val; uint32_t shift, pos, mask;
int i, bit_buf, bit_val; 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) if (bits > 32 || ib->b_off + bits > ib->b_max)
goto fail; 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 */ pos = ib->b_off / 8; /* byte offset */
shift = ib->b_off % 8; /* bit sub-offset */ shift = ib->b_off % 8; /* bit sub-offset */
#if 1 //naive approach #if 0 //naive approach
val = 0; val = 0;
for (i = 0; i < bits; i++) { for (i = 0; i < bits; i++) {
bit_buf = (1U << (8-1-shift)) & 0xFF; /* bit check for buf */ 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++; pos++;
} }
} }
#else //has bugs #else
pos = ib->b_off / 8; /* byte offset */ mask = MASK_TABLE_MSB[bits]; /* to remove upper in highest byte */
shift = ib->b_off % 8; /* bit sub-offset */
uint32_t mask = MASK_TABLE[bits]; /* to remove upper in highest byte */
int left = 0; left = 0;
if (bits == 0) if (bits == 0)
val = 0; val = 0;
else else
@ -102,12 +111,12 @@ static inline int bm_get(bitstream_t* ib, uint32_t bits, uint32_t* value) {
left = 16 - (bits + shift); left = 16 - (bits + shift);
if (bits + shift > 16) { if (bits + shift > 16) {
val = (val << 8u) | ib->buf[pos+2]; val = (val << 8u) | ib->buf[pos+2];
left = 32 - (bits + shift); left = 24 - (bits + shift);
if (bits + shift > 24) { if (bits + shift > 24) {
val = (val << 8u) | ib->buf[pos+3]; val = (val << 8u) | ib->buf[pos+3];
left = 32 - (bits + shift); left = 32 - (bits + shift);
if (bits + shift > 32) { 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); left = 40 - (bits + shift);
} }
} }

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

View 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

View file

@ -2,9 +2,17 @@
#include "../vgmstream.h" #include "../vgmstream.h"
#include "../layout/layout.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; goto fail;
} }
@ -13,9 +21,54 @@ bool layered_add_codec(VGMSTREAM* vs, int layers, int layer_channels) {
if (!layers) if (!layers)
layers = vs->channels / layer_channels; 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; int i;
layered_layout_data* data; 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) { switch(vs->layout_type) {
case layout_segmented: //to be improved case layout_segmented: //to be improved
goto fail; 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]->loop_end_sample = vs->loop_end_sample;
data->layers[i]->codec_data = vs->codec_data; 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]->coding_type = vs->coding_type;
data->layers[i]->layout_type = layout_none; 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 */ 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++; data->curr_layer++;
return true; return true;
@ -59,12 +120,29 @@ fail:
return false; 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) { bool layered_add_done(VGMSTREAM* vs) {
//TODO: some extra checks/setup? //TODO: some extra checks/setup?
if (!setup_layout_layered(vs->layout_data)) if (!setup_layout_layered(vs->layout_data))
goto fail; goto fail;
if (!vs->coding_type) {
layered_layout_data* data = vs->layout_data;
vs->coding_type = data->layers[0]->coding_type;
}
return true; return true;
fail: fail:
return false; return false;

View file

@ -3,6 +3,15 @@
#include "../vgmstream.h" #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) /* add a new layer from codec data (setups layout if needed)
* codec is passed in the vs for easier free/etc control */ * codec is passed in the vs for easier free/etc control */
bool layered_add_codec(VGMSTREAM* vs, int layers, int layer_channels); bool layered_add_codec(VGMSTREAM* vs, int layers, int layer_channels);

View file

@ -67,7 +67,7 @@ void vgm_log_set_callback(void* ctx_p, int level, int type, void* callback);
do { \ do { \
int i; \ int i; \
for (i=0; i < buf_size; 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"); \ if (bytes_per_line && (i+1) % bytes_per_line == 0) printf("\n"); \
} \ } \
printf("\n"); \ printf("\n"); \

View file

@ -1,11 +1,7 @@
#ifndef _READER_PUT_H #ifndef _READER_PUT_H
#define _READER_PUT_H #define _READER_PUT_H
#ifdef BUILD_VGMSTREAM #include "streamtypes.h"
#include "../streamtypes.h"
#else
#include <libvgmstream/streamtypes.h>
#endif
void put_8bit(uint8_t* buf, int8_t i); void put_8bit(uint8_t* buf, int8_t i);

View file

@ -308,7 +308,6 @@ init_vgmstream_t init_vgmstream_functions[] = {
init_vgmstream_opus_shinen, init_vgmstream_opus_shinen,
init_vgmstream_opus_nus3, init_vgmstream_opus_nus3,
init_vgmstream_opus_sps_n1, init_vgmstream_opus_sps_n1,
init_vgmstream_opus_nxa,
init_vgmstream_pc_ast, init_vgmstream_pc_ast,
init_vgmstream_naac, init_vgmstream_naac,
init_vgmstream_ubi_sb, init_vgmstream_ubi_sb,
@ -386,6 +385,7 @@ init_vgmstream_t init_vgmstream_functions[] = {
init_vgmstream_xau_konami, init_vgmstream_xau_konami,
init_vgmstream_derf, init_vgmstream_derf,
init_vgmstream_utk, init_vgmstream_utk,
init_vgmstream_nxa1,
init_vgmstream_adpcm_capcom, init_vgmstream_adpcm_capcom,
init_vgmstream_ue4opus, init_vgmstream_ue4opus,
init_vgmstream_xwma, init_vgmstream_xwma,
@ -458,6 +458,7 @@ init_vgmstream_t init_vgmstream_functions[] = {
init_vgmstream_diva, init_vgmstream_diva,
init_vgmstream_imuse, init_vgmstream_imuse,
init_vgmstream_ktsr, init_vgmstream_ktsr,
init_vgmstream_asrs,
init_vgmstream_mups, init_vgmstream_mups,
init_vgmstream_kat, init_vgmstream_kat,
init_vgmstream_pcm_success, init_vgmstream_pcm_success,
@ -521,6 +522,7 @@ init_vgmstream_t init_vgmstream_functions[] = {
init_vgmstream_squeaksample, init_vgmstream_squeaksample,
init_vgmstream_snds, init_vgmstream_snds,
init_vgmstream_adm2, init_vgmstream_adm2,
init_vgmstream_nxof,
/* lower priority metas (no clean header identity, somewhat ambiguous, or need extension/companion file to identify) */ /* lower priority metas (no clean header identity, somewhat ambiguous, or need extension/companion file to identify) */
init_vgmstream_scd_pcm, init_vgmstream_scd_pcm,
@ -614,13 +616,7 @@ static VGMSTREAM* init_vgmstream_internal(STREAMFILE* sf) {
try_dual_file_stereo(vgmstream, sf, init_vgmstream_function); 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 #ifdef VGM_USE_FFMPEG
/* check FFmpeg streams here, for lack of a better place */ /* check FFmpeg streams here, for lack of a better place */
if (vgmstream->coding_type == coding_FFmpeg) { if (vgmstream->coding_type == coding_FFmpeg) {
@ -676,6 +672,28 @@ static VGMSTREAM* init_vgmstream_internal(STREAMFILE* sf) {
void setup_vgmstream(VGMSTREAM* vgmstream) { 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 */ /* save start things so we can restart when seeking */
memcpy(vgmstream->start_ch, vgmstream->ch, sizeof(VGMSTREAMCHANNEL)*vgmstream->channels); memcpy(vgmstream->start_ch, vgmstream->ch, sizeof(VGMSTREAMCHANNEL)*vgmstream->channels);
memcpy(vgmstream->start_vgmstream, vgmstream, sizeof(VGMSTREAM)); memcpy(vgmstream->start_vgmstream, vgmstream, sizeof(VGMSTREAM));

View file

@ -90,6 +90,7 @@ typedef enum {
coding_H4M_IMA, /* H4M IMA ADPCM (stereo or mono, high nibble first) */ coding_H4M_IMA, /* H4M IMA ADPCM (stereo or mono, high nibble first) */
coding_MTF_IMA, /* Capcom MT Framework IMA ADPCM */ coding_MTF_IMA, /* Capcom MT Framework IMA ADPCM */
coding_CD_IMA, /* Crystal Dynamics IMA ADPCM */ coding_CD_IMA, /* Crystal Dynamics IMA ADPCM */
coding_CRANKCASE_IMA, /* CrankcaseAudio REV IMA ADPCM */
coding_MSADPCM, /* Microsoft ADPCM (stereo/mono) */ coding_MSADPCM, /* Microsoft ADPCM (stereo/mono) */
coding_MSADPCM_int, /* Microsoft ADPCM (mono) */ coding_MSADPCM_int, /* Microsoft ADPCM (mono) */
@ -611,7 +612,7 @@ typedef enum {
meta_DERF, /* Stupid Invaders (PC) */ meta_DERF, /* Stupid Invaders (PC) */
meta_SADF, meta_SADF,
meta_UTK, meta_UTK,
meta_NXA, meta_NXA1,
meta_ADPCM_CAPCOM, meta_ADPCM_CAPCOM,
meta_UE4OPUS, meta_UE4OPUS,
meta_XWMA, meta_XWMA,
@ -701,6 +702,7 @@ typedef enum {
meta_SQUEAKSTREAM, meta_SQUEAKSTREAM,
meta_SQUEAKSAMPLE, meta_SQUEAKSAMPLE,
meta_SNDS, meta_SNDS,
meta_NXOF,
} meta_t; } meta_t;