diff --git a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj index 37ded1057..fe0ed4f17 100644 --- a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj +++ b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj @@ -401,7 +401,6 @@ 836F703018BDC2190095E648 /* sqex_scd.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EF418BDC2190095E648 /* sqex_scd.c */; }; 836F703218BDC2190095E648 /* str_asr.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EF618BDC2190095E648 /* str_asr.c */; }; 836F703318BDC2190095E648 /* str_snds.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EF718BDC2190095E648 /* str_snds.c */; }; - 836F703418BDC2190095E648 /* stx.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EF818BDC2190095E648 /* stx.c */; }; 836F703518BDC2190095E648 /* svs.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EF918BDC2190095E648 /* svs.c */; }; 836F703618BDC2190095E648 /* thp.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EFA18BDC2190095E648 /* thp.c */; }; 836F703718BDC2190095E648 /* tun.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EFB18BDC2190095E648 /* tun.c */; }; @@ -522,6 +521,9 @@ 83AA5D251F6E2F9C0020821C /* hca_keys.h in Headers */ = {isa = PBXBuildFile; fileRef = 83AA5D211F6E2F9C0020821C /* hca_keys.h */; }; 83AA5D271F6E2F9C0020821C /* stm.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AA5D231F6E2F9C0020821C /* stm.c */; }; 83AB8C761E8072A100086084 /* x360_ast.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AB8C741E8072A100086084 /* x360_ast.c */; }; + 83AFABBC23795202002F3947 /* xssb.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AFABB923795201002F3947 /* xssb.c */; }; + 83AFABBD23795202002F3947 /* ea_eaac_opus_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 83AFABBA23795202002F3947 /* ea_eaac_opus_streamfile.h */; }; + 83AFABBE23795202002F3947 /* isb.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AFABBB23795202002F3947 /* isb.c */; }; 83BAFB6C19F45EB3005DAB60 /* bfstm.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BAFB6B19F45EB3005DAB60 /* bfstm.c */; }; 83C7280F22BC893D00678B4A /* xwb_xsb.h in Headers */ = {isa = PBXBuildFile; fileRef = 83C727FB22BC893800678B4A /* xwb_xsb.h */; }; 83C7281022BC893D00678B4A /* nps.c in Sources */ = {isa = PBXBuildFile; fileRef = 83C727FC22BC893900678B4A /* nps.c */; }; @@ -1087,7 +1089,6 @@ 836F6EF418BDC2190095E648 /* sqex_scd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sqex_scd.c; sourceTree = "<group>"; }; 836F6EF618BDC2190095E648 /* str_asr.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = str_asr.c; sourceTree = "<group>"; }; 836F6EF718BDC2190095E648 /* str_snds.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = str_snds.c; sourceTree = "<group>"; }; - 836F6EF818BDC2190095E648 /* stx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = stx.c; sourceTree = "<group>"; }; 836F6EF918BDC2190095E648 /* svs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = svs.c; sourceTree = "<group>"; }; 836F6EFA18BDC2190095E648 /* thp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = thp.c; sourceTree = "<group>"; }; 836F6EFB18BDC2190095E648 /* tun.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tun.c; sourceTree = "<group>"; }; @@ -1207,6 +1208,9 @@ 83AA5D211F6E2F9C0020821C /* hca_keys.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hca_keys.h; sourceTree = "<group>"; }; 83AA5D231F6E2F9C0020821C /* stm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = stm.c; sourceTree = "<group>"; }; 83AB8C741E8072A100086084 /* x360_ast.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = x360_ast.c; sourceTree = "<group>"; }; + 83AFABB923795201002F3947 /* xssb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xssb.c; sourceTree = "<group>"; }; + 83AFABBA23795202002F3947 /* ea_eaac_opus_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ea_eaac_opus_streamfile.h; sourceTree = "<group>"; }; + 83AFABBB23795202002F3947 /* isb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = isb.c; sourceTree = "<group>"; }; 83BAFB6B19F45EB3005DAB60 /* bfstm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bfstm.c; sourceTree = "<group>"; }; 83C727FB22BC893800678B4A /* xwb_xsb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xwb_xsb.h; sourceTree = "<group>"; }; 83C727FC22BC893900678B4A /* nps.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nps.c; sourceTree = "<group>"; }; @@ -1600,6 +1604,7 @@ 83299FCF1E7660C7003A3242 /* dsp_adx.c */, 836F6E4418BDC2180095E648 /* dsp_bdsp.c */, 8349A8FF1FE6258000E26435 /* ea_1snh.c */, + 83AFABBA23795202002F3947 /* ea_eaac_opus_streamfile.h */, 8306B0BD2098458B000302D4 /* ea_eaac_streamfile.h */, 8349A8F71FE6257E00E26435 /* ea_eaac.c */, 830165981F256BD000CA0941 /* ea_schl_fixed.c */, @@ -1642,6 +1647,7 @@ 837CEAE623487F2B00E62A4A /* ima.c */, 832BF81121E05149006F50F1 /* imc.c */, 836F6E5518BDC2180095E648 /* ios_psnd.c */, + 83AFABBB23795202002F3947 /* isb.c */, 836F6E5618BDC2180095E648 /* ish_isd.c */, 836F6E5718BDC2180095E648 /* ivaud.c */, 836F6E5818BDC2180095E648 /* ivb.c */, @@ -1849,7 +1855,6 @@ 836F6EF718BDC2190095E648 /* str_snds.c */, 834FE0C2215C79E6000A5D3D /* str_wav.c */, 83C7280722BC893B00678B4A /* strm_abylight.c */, - 836F6EF818BDC2190095E648 /* stx.c */, 834FE0D7215C79EA000A5D3D /* svg.c */, 836F6EF918BDC2190095E648 /* svs.c */, 831BA6121EAC61A500CF89B0 /* sxd.c */, @@ -1923,6 +1928,7 @@ 832BF80A21E05148006F50F1 /* xpcm.c */, 832BF80C21E05148006F50F1 /* xps.c */, 836F6F1218BDC2190095E648 /* xss.c */, + 83AFABB923795201002F3947 /* xssb.c */, 834FE0C6215C79E7000A5D3D /* xvag_streamfile.h */, 83345A4E1F8AEB2800B2EAA4 /* xvag.c */, 837CEADC23487F2900E62A4A /* xvas.c */, @@ -2023,6 +2029,7 @@ 8306B0D820984590000302D4 /* ea_eaac_streamfile.h in Headers */, 837CEB0523487F2C00E62A4A /* sqex_sead_streamfile.h in Headers */, 8306B0E820984590000302D4 /* opus_interleave_streamfile.h in Headers */, + 83AFABBD23795202002F3947 /* ea_eaac_opus_streamfile.h in Headers */, 83C7281822BC893D00678B4A /* sfh_streamfile.h in Headers */, 834FE0EF215C79ED000A5D3D /* xvag_streamfile.h in Headers */, 8349A90C1FE6258200E26435 /* sqex_scd_streamfile.h in Headers */, @@ -2249,7 +2256,6 @@ 836F6FA118BDC2190095E648 /* myspd.c in Sources */, 837CEB0123487F2C00E62A4A /* xmu.c in Sources */, 836F6FD718BDC2190095E648 /* ps2_filp.c in Sources */, - 836F703418BDC2190095E648 /* stx.c in Sources */, 83FF0EBC1E93282100C58054 /* wwise.c in Sources */, 836F6F7018BDC2190095E648 /* apple_caff.c in Sources */, 836F700018BDC2190095E648 /* ps2_svag.c in Sources */, @@ -2295,6 +2301,7 @@ 8349A8EA1FE6253900E26435 /* blocked_ea_schl.c in Sources */, 836F705118BDC2190095E648 /* zsd.c in Sources */, 8349A91F1FE6258200E26435 /* naac.c in Sources */, + 83AFABBE23795202002F3947 /* isb.c in Sources */, 836F6FD218BDC2190095E648 /* ps2_bmdx.c in Sources */, 836F703C18BDC2190095E648 /* wii_bns.c in Sources */, 830EBE132004656E0023AA10 /* xnb.c in Sources */, @@ -2594,6 +2601,7 @@ 83C7282922BC8C1500678B4A /* mixing.c in Sources */, 8375737621F950ED00F01AF5 /* gin.c in Sources */, 836F6FC918BDC2190095E648 /* ps2_2pfs.c in Sources */, + 83AFABBC23795202002F3947 /* xssb.c in Sources */, 836F704818BDC2190095E648 /* xbox_ims.c in Sources */, 837CEAF623487F2C00E62A4A /* mzrt.c in Sources */, 836F6F7518BDC2190095E648 /* bnsf.c in Sources */, diff --git a/Frameworks/vgmstream/vgmstream/src/coding/asf_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/asf_decoder.c index c0db63018..dec827cfa 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/asf_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/asf_decoder.c @@ -2,52 +2,53 @@ /* Decodes Argonaut's ASF ADPCM codec, used in some of their PC games. - * Algorithm should be accurate (reverse engineered from asfcodec.adl DLL). */ -void decode_asf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { + * Reverse engineered from asfcodec.adl DLL. */ +void decode_asf(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { + uint8_t frame[0x11] = {0}; off_t frame_offset; int i, frames_in, sample_count = 0; size_t bytes_per_frame, samples_per_frame; - uint8_t shift, mode; + int shift, mode; int32_t hist1 = stream->adpcm_history1_32; int32_t hist2 = stream->adpcm_history2_32; + /* external interleave (fixed size), mono */ bytes_per_frame = 0x11; samples_per_frame = (bytes_per_frame - 0x01) * 2; frames_in = first_sample / samples_per_frame; - first_sample = first_sample % samples_per_frame; + //first_sample = first_sample % samples_per_frame; /* for flat layout */ /* parse frame header */ frame_offset = stream->offset + bytes_per_frame*frames_in; - shift = ((uint8_t)read_8bit(frame_offset+0x00,stream->streamfile) >> 4) & 0xf; - mode = ((uint8_t)read_8bit(frame_offset+0x00,stream->streamfile) >> 0) & 0xf; + read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */ + shift = (frame[0x00] >> 4) & 0xf; + mode = (frame[0x00] >> 0) & 0xf; /* decode nibbles */ for (i = first_sample; i < first_sample + samples_to_do; i++) { - int32_t new_sample; - uint8_t nibbles = (uint8_t)read_8bit(frame_offset+0x01 + i/2,stream->streamfile); + uint8_t nibbles = frame[0x01 + i/2]; + int32_t sample; - new_sample = i&1 ? /* high nibble first */ + sample = i&1 ? /* high nibble first */ get_low_nibble_signed(nibbles): get_high_nibble_signed(nibbles); - /* move sample to upper nibble, then shift + 2 (IOW: shift + 6) */ - new_sample = (new_sample << 4) << (shift + 2); + sample = (sample << 4) << (shift + 2); /* move sample to upper nibble, then shift + 2 (IOW: shift + 6) */ /* mode is checked as a flag, so there are 2 modes only, but lower nibble * may have other values at last frame (ex 0x02/09), could be control flags (loop related?) */ if (mode & 0x4) { /* ~filters: 2, -1 */ - new_sample = (new_sample + (hist1 << 7) - (hist2 << 6)) >> 6; + sample = (sample + (hist1 << 7) - (hist2 << 6)) >> 6; } else { /* ~filters: 1, 0 */ - new_sample = (new_sample + (hist1 << 6)) >> 6; + sample = (sample + (hist1 << 6)) >> 6; } - //new_sample = clamp16(new_sample); /* must not */ - outbuf[sample_count] = (int16_t)new_sample; + outbuf[sample_count] = (int16_t)sample; /* must not clamp */ sample_count += channelspacing; hist2 = hist1; - hist1 = new_sample; + hist1 = sample; } stream->adpcm_history1_32 = hist1; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/coding.h b/Frameworks/vgmstream/vgmstream/src/coding/coding.h index 18aaf3846..ef9db19b7 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/coding.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/coding.h @@ -55,10 +55,10 @@ void dsp_read_hist_le(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offse void dsp_read_hist(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing, int be); /* ngc_dtk_decoder */ -void decode_ngc_dtk(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_ngc_dtk(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); /* ngc_afc_decoder */ -void decode_ngc_afc(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_ngc_afc(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); /* pcm_decoder */ void decode_pcm16le(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); @@ -138,7 +138,7 @@ size_t yamaha_bytes_to_samples(size_t bytes, int channels); size_t aska_bytes_to_samples(size_t bytes, int channels); /* nds_procyon_decoder */ -void decode_nds_procyon(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_nds_procyon(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); /* l5_555_decoder */ void decode_l5_555(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); @@ -147,28 +147,28 @@ void decode_l5_555(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacin void decode_sassc(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); /* lsf_decode */ -void decode_lsf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_lsf(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); /* mtaf_decoder */ -void decode_mtaf(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_mtaf(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); /* mta2_decoder */ -void decode_mta2(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_mta2(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); /* mc3_decoder */ void decode_mc3(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); /* fadpcm_decoder */ -void decode_fadpcm(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_fadpcm(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); /* asf_decoder */ -void decode_asf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_asf(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); /* dsa_decoder */ -void decode_dsa(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_dsa(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); /* xmd_decoder */ -void decode_xmd(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, size_t frame_size); +void decode_xmd(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, size_t frame_size); /* derf_decoder */ void decode_derf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); @@ -183,7 +183,7 @@ void decode_oki4s(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspaci size_t oki_bytes_to_samples(size_t bytes, int channels); /* ptadpcm_decoder */ -void decode_ptadpcm(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, size_t frame_size); +void decode_ptadpcm(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, size_t frame_size); size_t ptadpcm_bytes_to_samples(size_t bytes, int channels, size_t frame_size); /* ubi_adpcm_decoder */ diff --git a/Frameworks/vgmstream/vgmstream/src/coding/dsa_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/dsa_decoder.c index 74e256128..f5b0c883d 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/dsa_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/dsa_decoder.c @@ -10,37 +10,38 @@ static const int dsa_coefs[16] = { /* Decodes Ocean DSA ADPCM codec from Last Rites (PC). * Reverse engineered from daemon1's reverse engineering. */ -void decode_dsa(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { +void decode_dsa(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { + uint8_t frame[0x08] = {0}; off_t frame_offset; int i, frames_in, sample_count = 0; size_t bytes_per_frame, samples_per_frame; - uint8_t header; - int shift, filter; + int index, shift, coef; int32_t hist1 = stream->adpcm_history1_32; + /* external interleave (fixed size), mono */ bytes_per_frame = 0x08; samples_per_frame = (bytes_per_frame - 0x01) * 2; frames_in = first_sample / samples_per_frame; - first_sample = first_sample % samples_per_frame; + first_sample = first_sample % samples_per_frame; /* for flat layout */ /* parse frame header */ - frame_offset = stream->offset + bytes_per_frame*frames_in; - header = (uint8_t)read_8bit(frame_offset+0x00,stream->streamfile); - shift = 0x0c - ((header >> 4) & 0xf); - filter = dsa_coefs[header & 0xf]; + frame_offset = stream->offset + bytes_per_frame * frames_in; + read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */ + index = ((frame[0] >> 0) & 0xf); + shift = 12 - ((frame[0] >> 4) & 0xf); + coef = dsa_coefs[index]; /* decode nibbles */ for (i = first_sample; i < first_sample + samples_to_do; i++) { + uint8_t nibbles = frame[0x01 + i/2]; int32_t sample; - uint8_t nibbles = (uint8_t)read_8bit(frame_offset+0x01 + i/2,stream->streamfile); sample = i&1 ? /* high nibble first */ (nibbles >> 0) & 0xf : (nibbles >> 4) & 0xf; - - sample = ((int16_t)(sample << 0xC) >> shift); /* 16b sign extend + scale */ - sample = sample + ((hist1 * filter) >> 0x10); + sample = ((int16_t)(sample << 12) >> shift); /* 16b sign extend + scale */ + sample = sample + ((hist1 * coef) >> 16); outbuf[sample_count] = (sample_t)(sample << 2); sample_count += channelspacing; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/fadpcm_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/fadpcm_decoder.c index e70f6adc9..a52a1437d 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/fadpcm_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/fadpcm_decoder.c @@ -1,77 +1,74 @@ #include "coding.h" -/* FADPCM table */ +/* tweaked XA/PSX coefs << 6 */ static const int8_t fadpcm_coefs[8][2] = { - { 0 , 0 }, - { 60 , 0 }, - { 122 , 60 }, - { 115 , 52 }, - { 98 , 55 }, - { 0 , 0 }, - { 0 , 0 }, - { 0 , 0 }, + { 0, 0 }, + { 60, 0 }, + { 122, 60 }, + { 115, 52 }, + { 98, 55 }, + /* rest is 0s */ }; /* FMOD's FADPCM, basically XA/PSX ADPCM with a fancy header layout. * Code/layout could be simplified but tries to emulate FMOD's code. * Algorithm and tables debugged from their PC DLLs (byte-accurate). */ -void decode_fadpcm(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { +void decode_fadpcm(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { + uint8_t frame[0x8c] = {0}; off_t frame_offset; - int i, j, k; - int block_samples, frames_in, samples_done = 0, sample_count = 0; + int i, j, k, frames_in, sample_count = 0, samples_done = 0; + size_t bytes_per_frame, samples_per_frame; uint32_t coefs, shifts; int32_t hist1; //= stream->adpcm_history1_32; int32_t hist2; //= stream->adpcm_history2_32; /* external interleave (fixed size), mono */ - block_samples = (0x8c - 0xc) * 2; - frames_in = first_sample / block_samples; - first_sample = first_sample % block_samples; - - frame_offset = stream->offset + 0x8c*frames_in; - + bytes_per_frame = 0x8c; + samples_per_frame = (bytes_per_frame - 0xc) * 2; + frames_in = first_sample / samples_per_frame; + first_sample = first_sample % samples_per_frame; /* parse 0xc header (header samples are not written to outbuf) */ - coefs = read_32bitLE(frame_offset + 0x00, stream->streamfile); - shifts = read_32bitLE(frame_offset + 0x04, stream->streamfile); - hist1 = read_16bitLE(frame_offset + 0x08, stream->streamfile); - hist2 = read_16bitLE(frame_offset + 0x0a, stream->streamfile); + frame_offset = stream->offset + bytes_per_frame * frames_in; + read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */ + coefs = get_u32le(frame + 0x00); + shifts = get_u32le(frame + 0x04); + hist1 = get_s16le(frame + 0x08); + hist2 = get_s16le(frame + 0x0a); /* decode nibbles, grouped in 8 sets of 0x10 * 0x04 * 2 */ for (i = 0; i < 8; i++) { - int32_t coef1, coef2, shift, coef_index, shift_factor; - off_t group_offset = frame_offset + 0x0c + 0x10*i; + int index, shift, coef1, coef2; /* each set has its own coefs/shifts (indexes > 7 are repeat, ex. 0x9 is 0x2) */ - coef_index = ((coefs >> i*4) & 0x0f) % 0x07; - shift_factor = (shifts >> i*4) & 0x0f; + index = ((coefs >> i*4) & 0x0f) % 0x07; + shift = (shifts >> i*4) & 0x0f; - coef1 = fadpcm_coefs[coef_index][0]; - coef2 = fadpcm_coefs[coef_index][1]; - shift = 0x16 - shift_factor; /* pre-adjust for 32b sign extend */ + coef1 = fadpcm_coefs[index][0]; + coef2 = fadpcm_coefs[index][1]; + shift = 22 - shift; /* pre-adjust for 32b sign extend */ for (j = 0; j < 4; j++) { - uint32_t nibbles = read_32bitLE(group_offset + 0x04*j, stream->streamfile); + uint32_t nibbles = get_u32le(frame + 0x0c + 0x10*i + 0x04*j); for (k = 0; k < 8; k++) { - int32_t new_sample; + int32_t sample; - new_sample = (nibbles >> k*4) & 0x0f; - new_sample = (new_sample << 28) >> shift; /* 32b sign extend + scale */ - new_sample = (new_sample - hist2*coef2 + hist1*coef1); - new_sample = new_sample >> 6; - new_sample = clamp16(new_sample); + sample = (nibbles >> k*4) & 0x0f; + sample = (sample << 28) >> shift; /* 32b sign extend + scale */ + sample = (sample - hist2*coef2 + hist1*coef1) >> 6; + sample = clamp16(sample); if (sample_count >= first_sample && samples_done < samples_to_do) { - outbuf[samples_done * channelspacing] = new_sample; + outbuf[samples_done * channelspacing] = sample; samples_done++; } sample_count++; hist2 = hist1; - hist1 = new_sample; + hist1 = sample; } } } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c index 046d91a7c..c038e2b24 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c @@ -557,16 +557,17 @@ fail: * float requirements, but C99 adds some faster-but-less-precise casting functions * we try to use (returning "long", though). They work ok without "fast float math" compiler * flags, but probably should be enabled anyway to ensure no extra IEEE checks are needed. + * MSVC added this in VS2015 (_MSC_VER 1900) but don't seem correctly optimized and is very slow. */ static inline int float_to_int(float val) { -#if defined(_MSC_VER) && (_MSC_VER < 1900) /* VS2015 */ +#if defined(_MSC_VER) return (int)val; #else return lrintf(val); #endif } static inline int double_to_int(double val) { -#if defined(_MSC_VER) && (_MSC_VER < 1900) /* VS2015 */ +#if defined(_MSC_VER) return (int)val; #else return lrint(val); /* returns long tho */ @@ -580,9 +581,9 @@ static inline int double_to_int(double val) { * keep it simple for now until more tests are done. * * in normal (interleaved) formats samples are laid out straight - * (ibuf[s*chs+ch], ex. 4ch with 8s: 0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3) + * (ibuf[s*chs+ch], ex. 4ch with 4s: 0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3) * in "p" (planar) formats samples are in planes per channel - * (ibuf[ch][s], ex. 4ch with 8s: 0 0 0 0 1 1 1 1 2 2 2 2 3 3 3 3) + * (ibuf[ch][s], ex. 4ch with 4s: 0 0 0 0 1 1 1 1 2 2 2 2 3 3 3 3) * * alt float clamping: * clamp_float(f32) @@ -731,7 +732,6 @@ void decode_ffmpeg(VGMSTREAM *vgmstream, sample_t * outbuf, int32_t samples_to_d copy_samples(data, outbuf, samples_to_get); - //samples_done += samples_to_get; samples_to_do -= samples_to_get; outbuf += samples_to_get * channels; } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_utils.c b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_utils.c index 3b6743104..d1a5feb8e 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_utils.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_utils.c @@ -133,7 +133,8 @@ ffmpeg_codec_data * init_ffmpeg_atrac3_riff(STREAMFILE *sf, off_t offset, int* o /* implicit skip: official tools skip this even with encoder delay forced to 0. Maybe FFmpeg decodes late, * but when forcing tools to decode all frame samples it always ends a bit before last frame, so maybe it's * really an internal skip, since encoder adds extra frames so fact num_samples + encoder delay + implicit skip - * never goes past file. Same for all bitrate/channels, not added to loops. */ + * never goes past file. Same for all bitrate/channels, not added to loops. This is probably "decoder delay" + * also seen in codecs like MP3 */ if (is_at3) { implicit_skip = 69; } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c index c58327565..1c290e4c6 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c @@ -878,7 +878,7 @@ void decode_fsb_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample_t * stream->adpcm_step_index = step_index; } -/* mono XBOX-IMA with header endianness and alt nibble expand (per hcs's decompilation) */ +/* mono XBOX-IMA with header endianness and alt nibble expand (verified vs AK test demos) */ void decode_wwise_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { int i, sample_count = 0, num_frame; int32_t hist1 = stream->adpcm_history1_32; @@ -922,17 +922,6 @@ void decode_wwise_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample_t stream->adpcm_history1_32 = hist1; stream->adpcm_step_index = step_index; } -/* from hcs's analysis Wwise IMA expands nibbles slightly different, reducing dbs. Just "MUL" expand? -<_ZN13CAkADPCMCodec12DecodeSampleEiii>: //From Wwise_v2015.1.6_Build5553_SDK.Linux - 10: 83 e0 07 and $0x7,%eax ; sample - 13: 01 c0 add %eax,%eax ; sample*2 - 15: 83 c0 01 add $0x1,%eax ; sample*2+1 - 18: 0f af 45 e4 imul -0x1c(%rbp),%eax ; (sample*2+1)*scale - 1c: 8d 50 07 lea 0x7(%rax),%edx ; result+7 - 1f: 85 c0 test %eax,%eax ; result negative? - 21: 0f 48 c2 cmovs %edx,%eax ; adjust if negative to fix rounding for below division - 24: c1 f8 03 sar $0x3,%eax ; (sample*2+1)*scale/8 -*/ /* MS-IMA with possibly the XBOX-IMA model of even number of samples per block (more tests are needed) */ void decode_awc_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { diff --git a/Frameworks/vgmstream/vgmstream/src/coding/lsf_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/lsf_decoder.c index 780aed132..66b5510a2 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/lsf_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/lsf_decoder.c @@ -1,50 +1,60 @@ #include "coding.h" #include "../util.h" -/* lsf ADPCM, as seen in Fastlane Street Racing */ -static const short lsf_coefs[5][2] = { - {0x73, -0x34}, - {0, 0}, - {0x62, -0x37}, - {0x3C, 0}, - {0x7A, -0x3c} +/* tweaked XA/PSX coefs << 6 */ +static const short lsf_coefs[16][2] = { + { 115, -52 }, + { 0, 0 }, + { 98, -55 }, + { 60, 0 }, + { 122, -60 }, + /* rest assumed to be 0s */ }; -void decode_lsf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { - int i=first_sample; - int32_t sample_count; - const int bytes_per_frame = 0x1c; - const int samples_per_frame = (bytes_per_frame-1)*2; - - int framesin = first_sample/samples_per_frame; - - uint8_t q = 0xFF - read_8bit(framesin*bytes_per_frame + stream->offset,stream->streamfile); - int scale = (q&0xF0)>>4; - int coef_idx = q&0x0F; +void decode_lsf(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { + uint8_t frame[0x1c] = {0}; + off_t frame_offset; + int i, frames_in, sample_count = 0; + int index, shift, coef1, coef2; + size_t bytes_per_frame, samples_per_frame; int32_t hist1 = stream->adpcm_history1_16; int32_t hist2 = stream->adpcm_history2_16; + uint8_t header; - first_sample = first_sample%samples_per_frame; - for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) { - int sample_byte = read_8bit(framesin*bytes_per_frame+stream->offset+1+i/2,stream->streamfile); + /* external interleave (fixed size), mono */ + bytes_per_frame = 0x1c; + samples_per_frame = (bytes_per_frame - 1) * 2; + frames_in = first_sample / samples_per_frame; + //first_sample = first_sample % samples_per_frame; /* for flat layout */ - int32_t prediction = - (hist1 * lsf_coefs[coef_idx][0] + - hist2 * lsf_coefs[coef_idx][1]) / 0x40; + /* external interleave (fixed size), mono */ + frame_offset = stream->offset + bytes_per_frame * frames_in; + read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */ + header = 0xFF - frame[0x00]; + shift = (header >> 4) & 0xf; + index = (header >> 0) & 0xf; + coef1 = lsf_coefs[index][0]; + coef2 = lsf_coefs[index][1]; - prediction += (i&1? - get_high_nibble_signed(sample_byte): - get_low_nibble_signed(sample_byte) - ) * (1 << (12-scale)); + /* decode nibbles */ + for (i = first_sample; i < first_sample + samples_to_do; i++) { + uint8_t nibbles = frame[0x01 + i/2]; + int32_t sample; - prediction = clamp16(prediction); + sample = i&1 ? /* low nibble first */ + get_high_nibble_signed(nibbles) : + get_low_nibble_signed(nibbles); + sample = sample * (1 << (12 - shift)); + sample = sample + (hist1 * coef1 + hist2 * coef2) / 64; /* >> 6 */ + sample = clamp16(sample); + + outbuf[sample_count] = sample; + sample_count += channelspacing; hist2 = hist1; - hist1 = prediction; - - outbuf[sample_count] = prediction; + hist1 = sample; } stream->adpcm_history1_16 = hist1; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils.c b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils.c index 57ec9dce8..16d2b13b9 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils.c @@ -92,6 +92,14 @@ int mpeg_custom_setup_init_default(STREAMFILE *streamFile, off_t start_offset, m data->skip_samples = data->config.skip_samples; break; case MPEG_STANDARD: data->skip_samples = data->config.skip_samples; break; + case MPEG_EA: + /* typical MP2 decoder delay, verified vs sx.exe, also SCHl blocks header takes discard + * samples into account (so block_samples+240*2+1 = total frame samples) */ + if (info.layer == 2) { + data->skip_samples = 240*2 + 1; + } + /* MP3 probably uses 576 + 528+1 but no known games use it */ + break; default: break; } @@ -234,7 +242,7 @@ fail: * Gets info from a MPEG frame header at offset. Normally you would use mpg123_info but somehow * it's wrong at times (maybe because we use an ancient version) so here we do our thing. */ -int mpeg_get_frame_info(STREAMFILE *streamfile, off_t offset, mpeg_frame_info * info) { +static int mpeg_get_frame_info_h(uint32_t header, mpeg_frame_info *info) { /* index tables */ static const int versions[4] = { /* MPEG 2.5 */ 3, /* reserved */ -1, /* MPEG 2 */ 2, /* MPEG 1 */ 1 }; static const int layers[4] = { -1,3,2,1 }; @@ -257,14 +265,11 @@ int mpeg_get_frame_info(STREAMFILE *streamfile, off_t offset, mpeg_frame_info * { 384, 1152, 576 } /* MPEG2.5 */ }; - uint32_t header; int idx, padding; memset(info, 0, sizeof(*info)); - header = read_32bitBE(offset, streamfile); - if ((header >> 21) != 0x7FF) /* 31-21: sync */ goto fail; @@ -308,34 +313,36 @@ int mpeg_get_frame_info(STREAMFILE *streamfile, off_t offset, mpeg_frame_info * fail: return 0; } +int mpeg_get_frame_info(STREAMFILE *sf, off_t offset, mpeg_frame_info *info) { + uint32_t header = read_u32be(offset, sf); + return mpeg_get_frame_info_h(header, info); +} -size_t mpeg_get_samples(STREAMFILE *streamFile, off_t start_offset, size_t bytes) { +size_t mpeg_get_samples(STREAMFILE *sf, off_t start_offset, size_t bytes) { off_t offset = start_offset; off_t max_offset = start_offset + bytes; - int samples = 0; + int frames = 0, samples = 0, encoder_delay = 0, encoder_padding = 0; mpeg_frame_info info; - size_t prev_size = 0; - int cbr_count = 0; - int is_vbr = 0; - if (!streamFile) + if (!sf) return 0; - if (max_offset > get_streamfile_size(streamFile)) - max_offset = get_streamfile_size(streamFile); + if (max_offset > get_streamfile_size(sf)) + max_offset = get_streamfile_size(sf); /* MPEG may use VBR so must read all frames */ while (offset < max_offset) { + uint32_t header = read_u32be(offset+0x00, sf); /* skip ID3v2 */ - if ((read_32bitBE(offset+0x00, streamFile) & 0xFFFFFF00) == 0x49443300) { /* "ID3\0" */ + if ((header & 0xFFFFFF00) == 0x49443300) { /* "ID3\0" */ size_t frame_size = 0; - uint8_t flags = read_8bit(offset+0x05, streamFile); + uint8_t flags = read_u8(offset+0x05, sf); /* this is how it's officially read :/ */ - frame_size += read_8bit(offset+0x06, streamFile) << 21; - frame_size += read_8bit(offset+0x07, streamFile) << 14; - frame_size += read_8bit(offset+0x08, streamFile) << 7; - frame_size += read_8bit(offset+0x09, streamFile) << 0; + frame_size += read_u8(offset+0x06, sf) << 21; + frame_size += read_u8(offset+0x07, sf) << 14; + frame_size += read_u8(offset+0x08, sf) << 7; + frame_size += read_u8(offset+0x09, sf) << 0; frame_size += 0x0a; if (flags & 0x10) /* footer? */ frame_size += 0x0a; @@ -344,28 +351,71 @@ size_t mpeg_get_samples(STREAMFILE *streamFile, off_t start_offset, size_t bytes continue; } - /* this may fail with unknown ID3 tags */ - if (!mpeg_get_frame_info(streamFile, offset, &info)) - break; - - if (prev_size && prev_size != info.frame_size) { - is_vbr = 1; - } - else if (!is_vbr) { - cbr_count++; + /* skip ID3v1 */ + if ((header & 0xFFFFFF00) == 0x54414700) { /* "TAG\0" */ + ;VGM_LOG("MPEG: ID3v1 at %lx\n", offset); + offset += 0x80; + continue; } - if (cbr_count >= 10) { - /* must be CBR, don't bother counting */ - samples = (bytes / info.frame_size) * info.frame_samples; + /* regular frame */ + if (!mpeg_get_frame_info_h(header, &info)) { + VGM_LOG("MPEG: unknown frame at %lx\n", offset); break; } + /* detect Xing header (disguised as a normal frame) */ + if (frames < 3 && /* should be first after tags */ + info.frame_size >= 0x24 + 0x78 && + read_u32be(offset + 0x04, sf) == 0 && + (read_u32be(offset + 0x24, sf) == 0x58696E67 || /* "Xing" (mainly for VBR) */ + read_u32be(offset + 0x24, sf) == 0x496E666F)) { /* "Info" (mainly for CBR) */ + uint32_t flags = read_u32be(offset + 0x28, sf); + + if (flags & 1) { /* other flags indicate seek table and stuff */ + uint32_t frame_count = read_u32be(offset + 0x2c, sf); + samples = frame_count * info.frame_samples; + } + + /* vendor specific */ + if (info.frame_size > 0x24 + 0x78 + 0x24 && + read_u32be(offset + 0x9c, sf) == 0x4C414D45) { /* "LAME" */ + if (info.layer == 3) { + uint32_t delays = read_u32be(offset + 0xb0, sf); + encoder_delay = ((delays >> 12) & 0xFFF); + encoder_padding = ((delays >> 0) & 0xFFF); + + encoder_delay += (528 + 1); /* implicit MDCT decoder delay (seen in LAME source) */ + if (encoder_padding > 528 + 1) + encoder_padding -= (528 + 1); + } + else { + encoder_delay = 240 + 1; + } + + /* replay gain and stuff */ + } + + /* there is also "iTunes" vendor with no apparent extra info, iTunes delays are in "iTunSMPB" ID3 tag */ + + ;VGM_LOG("MPEG: found Xing header\n"); + break; /* we got samples */ + } + + //TODO: detect "VBRI" header (Fraunhofer encoder) + // https://www.codeproject.com/Articles/8295/MPEG-Audio-Frame-Header#VBRIHeader + + /* could detect VBR/CBR but read frames to remove ID3 end tags */ + + frames++; offset += info.frame_size; - prev_size = info.frame_size; - samples += info.frame_samples; /* header frames may be 0? */ + samples += info.frame_samples; } + ;VGM_LOG("MPEG: samples=%i, ed=%i, ep=%i, end=%i\n", samples,encoder_delay,encoder_padding, samples - encoder_delay - encoder_padding); + + //todo return encoder delay + samples = samples - encoder_delay - encoder_padding; return samples; } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_ealayer3.c b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_ealayer3.c index 4577894b8..536db8308 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_ealayer3.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_ealayer3.c @@ -112,12 +112,16 @@ int mpeg_custom_setup_init_ealayer3(STREAMFILE *streamfile, off_t start_offset, ok = ealayer3_parse_frame(data, -1, &ib, &eaf); if (!ok) goto fail; } - //;VGM_ASSERT(!eaf.mpeg1, "EAL3: mpeg2 found at 0x%lx\n", start_offset); /* rare [FIFA 08 (PS3) abk] */ + ;VGM_ASSERT(!eaf.mpeg1, "EAL3: mpeg2 found at 0x%lx\n", start_offset); /* rare [FIFA 08 (PS3) abk] */ *coding_type = coding_MPEG_ealayer3; data->channels_per_frame = eaf.channels; data->samples_per_frame = eaf.mpeg1 ? 1152 : 576; + /* handled at frame start */ + //data->skip_samples = 576 + 529; + //data->samples_to_discard = data->skip_samples; + /* encoder delay: EALayer3 handles this while decoding (skips samples as writes PCM blocks) */ return 1; @@ -133,6 +137,16 @@ int mpeg_custom_parse_frame_ealayer3(VGMSTREAMCHANNEL *stream, mpeg_codec_data * ealayer3_frame_t eaf_0, eaf_1; + /* the first frame samples must be discarded (verified vs sx.exe with a file without PCM blocks), + * but we can't set samples_to_discard since PCM blocks would be discarded + * SCHl block samples field takes into account this discard (its value already substracts this) */ + if ((data->type == MPEG_EAL31 || data->type == MPEG_EAL31b) && ms->current_size_count == 0) { + /* seems true for MP2/576 frame samples too, though they are rare so it's hard to test */ + ms->decode_to_discard += 529 + 576; /* standard MP3 decoder delay + 1 granule samples */ + ms->current_size_count++; + } + + /* read first frame/granule, or PCM-only frame (found alone at the end of SCHl streams) */ { //;VGM_LOG("s%i: get granule0 at %lx\n", num_stream,stream->offset); @@ -317,7 +331,7 @@ static int ealayer3_parse_frame_v1(ealayer3_buffer_t *ib, ealayer3_frame_t *eaf, /* check PCM block */ if (eaf->v1_pcm_flag == 0xEE) { fill_buf(ib, 32); - r_bits(is, 16,&eaf->v1_offset_samples); /* samples to discard of the next decoded (not PCM block) samples */ + r_bits(is, 16,&eaf->v1_offset_samples); /* PCM block offset in the buffer */ r_bits(is, 16,&eaf->v1_pcm_samples); /* number of PCM samples, can be 0 */ eaf->pre_size += 2+2; /* 16b+16b */ @@ -672,8 +686,7 @@ static void ealayer3_copy_pcm_block(uint8_t* outbuf, off_t pcm_offset, int pcm_n } /* write PCM block directly to sample buffer and setup decode discard (EALayer3 seems to use this as a prefetch of sorts). - * Meant to be written inmediatedly, as those PCM are parts that can be found after 1 decoded frame. - * (ex. EA-frame_gr0, PCM-frame_0, EA-frame_gr1, PCM-frame_1 actually writes PCM-frame_0+1, decode of EA-frame_gr0+1 + discard part */ + * Seems to alter decoded sample buffer to handle encoder delay/padding in a twisted way. */ static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream, ealayer3_frame_t *eaf) { mpeg_custom_stream *ms = data->streams[num_stream]; int channels_per_frame = ms->channels_per_frame; @@ -694,38 +707,34 @@ static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d if (eaf->v1_pcm_samples || eaf->v1_offset_samples) { uint8_t* outbuf = ms->output_buffer + bytes_filled; off_t pcm_offset = stream->offset + eaf->pre_size + eaf->common_size; + size_t decode_to_discard; VGM_ASSERT(eaf->v1_offset_samples > 576, "EAL3: big discard %i at 0x%x\n", eaf->v1_offset_samples, (uint32_t)stream->offset); VGM_ASSERT(eaf->v1_pcm_samples > 0x100, "EAL3: big samples %i at 0x%x\n", eaf->v1_pcm_samples, (uint32_t)stream->offset); VGM_ASSERT(eaf->v1_offset_samples > 0 && eaf->v1_pcm_samples == 0, "EAL3: offset_samples without pcm_samples\n"); /* not seen but could work */ - //;VGM_LOG("EA EAL3 v1: off=%lx, discard=%x, pcm=%i, pcm_o=%lx\n", - // stream->offset, eaf->v1_offset_samples, eaf->v1_pcm_samples, pcm_offset); + //;VGM_LOG("EA EAL3 v1: offset=%lx + %x, offset_samples=%x, pcm_samples=%i, spf=%i\n", + // stream->offset, eaf->pre_size + eaf->common_size, eaf->v1_offset_samples, eaf->v1_pcm_samples, data->samples_per_frame); - /* V1 usually discards + copies samples at the same time - * V1b PCM block is in 'planar' format (ex. NFS:U PS3) */ + /* V1b PCM block is in 'planar' format (ex. NFS:U PS3) */ ealayer3_copy_pcm_block(outbuf, pcm_offset, eaf->v1_pcm_samples, channels_per_frame, (data->type == MPEG_EAL31), stream->streamfile); ms->samples_filled += eaf->v1_pcm_samples; - /* skip decoded samples as PCM block 'overwrites' them w/ special meanings */ - { - size_t decode_to_discard = eaf->v1_offset_samples; + //TODO: we should put samples at offset but most EAL3 use it at first frame, which decodes ok, and rarely + // in the last frame [ex. Celebrity Sports Showdown], which is ~60-80 samples off (could click on segments?) - if (data->type == MPEG_EAL31) { - //todo should also discard v1_pcm_samples, but block layout samples may be exhausted - // and won't move (maybe new block if offset = new offset detected) - if (decode_to_discard == 576) - decode_to_discard = data->samples_per_frame;//+ eaf->v1_pcm_samples; - } - else { - VGM_ASSERT(decode_to_discard > 0, "EAL3: found offset_samples in V1b\n"); - /* probably (576 or samples_per_frame - eaf->v1_offset_samples) but V1b seems to always use 0 and ~47 samples */ - if (decode_to_discard == 0) /* seems ok (ex. comparing NFS:UC PS3 vs PC gets correct waveform this way) */ - decode_to_discard = data->samples_per_frame;//+ eaf->v1_pcm_samples; /* musn't discard pcm_number */ - } - - ms->decode_to_discard += decode_to_discard; - } + /* v1_offset_samples in V1a controls how the PCM block goes in the sample buffer. Value seems to start + * from frame samples end, taking into account that 1st frame discards 576+529 samples. + * ex. with 47 samples: + * - offset 47 puts block at sample 0 (at 576*2-47 w/o 576+529 discard), + * - offset 63 puts block at sample -16 (only 31 samples visible, so 576*2-63 w/o discard), + * - offset 0 seems to cause sample buffer overrun (at 576*2-0 = outside single frame buffer?) + * In V1b seems to works similarly but offset looks adjusted after initial discard (so offset 0 for first frame) + * + * This behaviour matters most in looping sfx (ex. Burnout Paradise), or tracks that start + * without silence (ex. NFS:UG2), and NFS:UC PS3 (EAL3v1b) vs PC (EAXAS) gets correct waveform this way */ + decode_to_discard = eaf->v1_pcm_samples; + ms->decode_to_discard += decode_to_discard; } if (eaf->v2_extended_flag) { @@ -789,6 +798,11 @@ fail: } +//TODO: this causes lots of rebuffering/slowness in multichannel since each stream has to read back +// (frames are interleaved like s0_g0, s1_g0, s2_g0, s0_g1, s1_g1, s2_g1, ..., +// stream0 advances buffers to s0_g1, but stream1 needs to read back to s1_g0, often trashing custom IO) +// would need to store granule0 after reading but not decoding until next? + /* Skip EA-frames from other streams for .sns/sps multichannel (interleaved 1 EA-frame per stream). * Due to EALayer3 being in blocks and other complexities (we can't go past a block) all * streams's offsets should start in the first stream's EA-frame. diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c index 32da9dd14..0a19a050e 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c @@ -204,7 +204,7 @@ static mpg123_handle * init_mpg123_handle() { } mpg123_param(m,MPG123_REMOVE_FLAGS,MPG123_GAPLESS,0.0); /* wonky support */ - mpg123_param(m,MPG123_RESYNC_LIMIT, -1, 0x10000); /* should be enough */ + mpg123_param(m,MPG123_RESYNC_LIMIT, -1, 0x2000); /* just in case, games shouldn't ever need this */ if (mpg123_open_feed(m) != MPG123_OK) { goto fail; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mta2_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/mta2_decoder.c index 06480c6da..a6df7870f 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mta2_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mta2_decoder.c @@ -4,6 +4,7 @@ /* MTA2 decoder based on: * - MGS Developer Wiki: https://www.mgsdevwiki.com/wiki/index.php/MTA2_(Codec) [codec by daemon1] * - Solid4 tools: https://github.com/GHzGangster/Drebin + * (PS3 probably uses floats, so this may not be 100% accurate) * * MTA2 layout: * - data is divided into N tracks of 0x10 header + 0x90 frame per track channel, forming N streams @@ -17,34 +18,28 @@ * expects samples_to_do to be block_samples at most (could be simplified, I guess). */ -/* coefs table (extended XA filters) */ -static const int mta2_coefs1[8] = { - 0, 240, 460, 392, 488, 460, 460, 240 +/* tweaked XA/PSX coefs << 8 */ +static const int16_t mta2_coefs[8][2] = { + { 0, 0 }, + { 240, 0 }, + { 460, -208 }, + { 392, -220 }, + { 488, -240 }, + { 460, -240 }, + { 460, -220 }, + { 240, -104 } }; -static const int mta2_coefs2[8] = { - 0, 0, -208, -220, -240, -240, -220, -104 -}; -/* shift table */ -static const int mta2_shifts[32] = { + +static const int mta2_scales[32] = { 256, 335, 438, 573, 749, 979, 1281, 1675, 2190, 2864, 3746, 4898, 6406, 8377, 10955, 14327, 18736, 24503, 32043, 41905, 54802, 71668, 93724, 122568, 160290, 209620, 274133, 358500, 468831, 613119, 801811, 1048576 }; -/* expands nibble */ -static short mta2_expand_nibble(int nibble, short hist1, short hist2, int coef_index, int shift_index) { - int output; - if (nibble > 7) /* sign extend */ - nibble = nibble - 16; - - output = (hist1 * mta2_coefs1[coef_index] + hist2 * mta2_coefs2[coef_index] + (nibble * mta2_shifts[shift_index]) + 128) >> 8; - output = clamp16(output); - return (short)output; -} - /* decodes a block for a channel */ -void decode_mta2(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { +void decode_mta2(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { + uint8_t frame[0x10 + 0x90*8] = {0}; int samples_done = 0, sample_count = 0, channel_block_samples, channel_first_sample, frame_size = 0; int i, group, row, col; int track_channels = 0, track_channel; @@ -55,14 +50,14 @@ void decode_mta2(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacin int num_track = 0, channel_layout; /* parse track header (0x10) and skip tracks that our current channel doesn't belong to */ - num_track = read_8bit(stream->offset+0x00,stream->streamfile); /* 0=first */ + read_streamfile(frame, stream->offset, 0x10, stream->streamfile); /* ignore EOF errors */ + num_track = get_u8 (frame + 0x00); /* 0=first */ /* 0x01(3): num_frame (0=first) */ /* 0x04(1): 0? */ - channel_layout = read_8bit(stream->offset+0x05,stream->streamfile); /* bitmask, see mta2.c */ - frame_size = read_16bitBE(stream->offset+0x06,stream->streamfile); /* not including this header */ + channel_layout = get_u8 (frame + 0x05); /* bitmask, see mta2.c */ + frame_size = get_u16be(frame + 0x06); /* not including this header */ /* 0x08(8): null */ - VGM_ASSERT(frame_size == 0, "MTA2: empty frame at %x\n", (uint32_t)stream->offset); /* frame_size 0 means silent/empty frame (rarely found near EOF for one track but not others) * negative track only happens for truncated files (EOF) */ @@ -74,13 +69,13 @@ void decode_mta2(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacin } track_channels = 0; - for (i = 0; i < 8; i++) { + for (i = 0; i < 8; i++) { /* max 8ch */ if ((channel_layout >> i) & 0x01) track_channels++; } if (track_channels == 0) { /* bad data, avoid div by 0 */ - VGM_LOG("track_channels 0 at %x\n", (uint32_t)stream->offset); + VGM_LOG("MTA2: track_channels 0 at %x\n", (uint32_t)stream->offset); return; } @@ -93,19 +88,20 @@ void decode_mta2(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacin } while (1); + /* parse stuff */ + read_streamfile(frame + 0x10, stream->offset + 0x10, frame_size, stream->streamfile); /* ignore EOF errors */ track_channel = channel % track_channels; channel_block_samples = (0x80*2); channel_first_sample = first_sample % (0x80*2); - /* parse channel frame (header 0x04*4 + data 0x20*4) */ for (group = 0; group < 4; group++) { - short hist2, hist1, coefs, shift, output; - int group_header = read_32bitBE(stream->offset + 0x10 + track_channel*0x90 + group*0x4, stream->streamfile); + short hist2, hist1, coefs, scale; + uint32_t group_header = get_u32be(frame + 0x10 + track_channel*0x90 + group*0x4); hist2 = (short) ((group_header >> 16) & 0xfff0); /* upper 16b discarding 4b */ hist1 = (short) ((group_header >> 4) & 0xfff0); /* lower 16b discarding 4b */ coefs = (group_header >> 5) & 0x7; /* mid 3b */ - shift = group_header & 0x1f; /* lower 5b */ + scale = group_header & 0x1f; /* lower 5b */ /* write header samples (skips the last 2 group nibbles), like Drebin's decoder * last 2 nibbles and next 2 header hist should match though */ @@ -120,23 +116,31 @@ void decode_mta2(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacin } sample_count++; + /* decode nibbles */ for (row = 0; row < 8; row++) { + int pos = 0x10 + track_channel*0x90 + 0x10 + group*0x4 + row*0x10; for (col = 0; col < 4*2; col++) { - uint8_t nibbles = read_8bit(stream->offset + 0x10 + 0x10 + track_channel*0x90 + group*0x4 + row*0x10 + col/2, stream->streamfile); - int nibble_shift = (!(col&1) ? 4 : 0); /* upper first */ - output = mta2_expand_nibble((nibbles >> nibble_shift) & 0xf, hist1, hist2, coefs, shift); + uint8_t nibbles = frame[pos + col/2]; + int32_t sample; + + sample = col&1 ? /* high nibble first */ + get_low_nibble_signed(nibbles) : + get_high_nibble_signed(nibbles); + sample = sample * mta2_scales[scale]; + sample = (sample + hist1 * mta2_coefs[coefs][0] + hist2 * mta2_coefs[coefs][1] + 128) >> 8; + sample = clamp16(sample); /* ignore last 2 nibbles (uses first 2 header samples) */ if (row < 7 || col < 3*2) { if (sample_count >= channel_first_sample && samples_done < samples_to_do) { - outbuf[samples_done * channelspacing] = output; + outbuf[samples_done * channelspacing] = sample; samples_done++; } sample_count++; } hist2 = hist1; - hist1 = output; + hist1 = sample; } } } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mtaf_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/mtaf_decoder.c index bc07dfcf0..38a5a38a1 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mtaf_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mtaf_decoder.c @@ -8,12 +8,12 @@ * Layout: N tracks of 0x10 header + 0x80*2 (always 2ch; multichannels uses 4ch = 2ch track0 + 2ch track1 xN). */ -static const int index_table[16] = { +static const int mtaf_step_indexes[16] = { -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8 }; -static const int16_t step_size[32][16] = { +static const int16_t mtaf_step_sizes[32][16] = { { 1, 5, 9, 13, 16, 20, 24, 28, -1, -5, -9, -13, -16, -20, -24, -28, }, { 2, 6, 11, 15, 20, 24, 29, 33, @@ -81,52 +81,56 @@ static const int16_t step_size[32][16] = { }; -void decode_mtaf(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { - int32_t sample_count; - int i; - int c = channel%2; /* global channel to track channel */ +void decode_mtaf(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { + uint8_t frame[0x110] = {0}; + off_t frame_offset; + int i, ch, sample_count = 0; + size_t bytes_per_frame /*, samples_per_frame*/; int32_t hist = stream->adpcm_history1_16; - int32_t step_idx = stream->adpcm_step_index; + int32_t step_index = stream->adpcm_step_index; - /* read header when we hit a new track every 0x100 samples */ - first_sample = first_sample % 0x100; + /* special stereo interleave, stereo */ + bytes_per_frame = 0x10 + 0x80*2; + //samples_per_frame = (bytes_per_frame - 0x10) / 2 * 2; /* 256 */ + ch = channel % 2; /* global channel to track channel */ + //first_sample = first_sample % samples_per_frame; /* for flat layout */ + /* read frame */ + frame_offset = stream->offset; + read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */ + + /* parse frame header when we hit a new track every frame samples */ if (first_sample == 0) { /* 0x10 header: track (8b, 0=first), track count (24b, 1=first), step-L, step-R, hist-L, hist-R */ - int32_t init_idx = read_16bitLE(stream->offset+4+0+c*2, stream->streamfile); /* step-L/R */ - int32_t init_hist = read_16bitLE(stream->offset+4+4+c*4, stream->streamfile); /* hist-L/R: hist 16bit + empty 16bit */ + step_index = get_s16le(frame + 0x04 + 0x00 + ch*0x02); /* step-L/R */ + hist = get_s16le(frame + 0x04 + 0x04 + ch*0x04); /* hist-L/R: hist 16bit + empty 16bit */ - VGM_ASSERT(init_idx < 0 || init_idx > 31, "MTAF: bad header idx @ 0x%x\n", (uint32_t)stream->offset); - /* avoid index out of range in corrupt files */ - if (init_idx < 0) { - init_idx = 0; - } else if (init_idx > 31) { - init_idx = 31; + VGM_ASSERT(step_index < 0 || step_index > 31, "MTAF: bad header idx at 0x%x\n", (uint32_t)stream->offset); + if (step_index < 0) { + step_index = 0; + } else if (step_index > 31) { + step_index = 31; } - - step_idx = init_idx; - hist = init_hist; } + /* decode nibbles */ + for (i = first_sample; i < first_sample + samples_to_do; i++) { + uint8_t nibbles = frame[0x10 + 0x80*ch + i/2]; + uint8_t nibble = (nibbles >> (!(i&1)?0:4)) & 0xf; /* lower first */ - /* skip to nibble */ - for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) { - uint8_t byte = read_8bit(stream->offset + 0x10 + 0x80*c + i/2, stream->streamfile); - uint8_t nibble = (byte >> (!(i&1)?0:4)) & 0xf; /* lower first */ - - hist = clamp16(hist+step_size[step_idx][nibble]); + hist = clamp16(hist + mtaf_step_sizes[step_index][nibble]); outbuf[sample_count] = hist; + sample_count += channelspacing; - step_idx += index_table[nibble]; - if (step_idx < 0) { /* clip step */ - step_idx = 0; - } else if (step_idx > 31) { - step_idx = 31; + step_index += mtaf_step_indexes[nibble]; + if (step_index < 0) { + step_index = 0; + } else if (step_index > 31) { + step_index = 31; } } - /* update state */ - stream->adpcm_step_index = step_idx; + stream->adpcm_step_index = step_index; stream->adpcm_history1_16 = hist; } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/nds_procyon_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/nds_procyon_decoder.c index 75f86125b..32466a670 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/nds_procyon_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/nds_procyon_decoder.c @@ -1,58 +1,62 @@ #include "coding.h" #include "../util.h" -/* ADPCM found in NDS games using Procyon Studio Digital Sound Elements */ - -static const int8_t proc_coef[5][2] = -{ - {0x00,0x00}, - {0x3C,0x00}, - {0x73,0xCC}, - {0x62,0xC9}, - {0x7A,0xC4}, +/* standard XA/PSX coefs << 6 */ +static const int8_t proc_coefs[16][2] = { + { 0, 0 }, + { 60, 0 }, + { 115, -52 }, + { 98, -55 }, + { 122, -60 }, + /* rest is 0s */ }; -void decode_nds_procyon(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { - int i=first_sample; - int32_t sample_count; - - int framesin = first_sample/30; - - uint8_t header = read_8bit(framesin*16+15+stream->offset,stream->streamfile) ^ 0x80; - int scale = 12 - (header & 0xf); - int coef_index = (header >> 4) & 0xf; +/* ADPCM found in NDS games using Procyon Studio Digital Sound Elements */ +void decode_nds_procyon(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { + uint8_t frame[0x10] = {0}; + off_t frame_offset; + int i, frames_in, sample_count = 0; + size_t bytes_per_frame, samples_per_frame; + int index, scale, coef1, coef2; int32_t hist1 = stream->adpcm_history1_32; int32_t hist2 = stream->adpcm_history2_32; - int32_t coef1; - int32_t coef2; + uint8_t header; - if (coef_index > 4) coef_index = 0; - coef1 = proc_coef[coef_index][0]; - coef2 = proc_coef[coef_index][1]; - first_sample = first_sample%30; - for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) { - int sample_byte = read_8bit(framesin*16+stream->offset+i/2,stream->streamfile) ^ 0x80; + /* external interleave (fixed size), mono */ + bytes_per_frame = 0x10; + samples_per_frame = (bytes_per_frame - 0x01) * 2; /* 30 */ + frames_in = first_sample / samples_per_frame; - int32_t sample = - (int32_t) - (i&1? - get_high_nibble_signed(sample_byte): - get_low_nibble_signed(sample_byte) - ) * 64 * 64; + /* parse frame header */ + frame_offset = stream->offset + bytes_per_frame * frames_in; + read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */ + header = frame[0x0F] ^ 0x80; + scale = 12 - (header & 0xf); + index = (header >> 4) & 0xf; + coef1 = proc_coefs[index][0]; + coef2 = proc_coefs[index][1]; + + /* decode nibbles */ + for (i = first_sample; i < first_sample + samples_to_do; i++) { + uint8_t nibbles = frame[i/2] ^ 0x80; + int32_t sample = 0; + + sample = i&1 ? /* low nibble first */ + get_high_nibble_signed(nibbles) : + get_low_nibble_signed(nibbles); + sample = sample * 64 * 64; /* << 12 */ if (scale < 0) - { sample <<= -scale; - } else sample >>= scale; - sample = (hist1 * coef1 + hist2 * coef2 + 32) / 64 + (sample * 64); hist2 = hist1; - hist1 = sample; + hist1 = sample; /* clamp *after* this */ - outbuf[sample_count] = clamp16((sample + 32) / 64) / 64 * 64; + outbuf[sample_count] = clamp16((sample + 32) / 64) / 64 * 64; + sample_count += channelspacing; } stream->adpcm_history1_32 = hist1; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ngc_afc_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ngc_afc_decoder.c index 8c796f951..8af87ff38 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ngc_afc_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ngc_afc_decoder.c @@ -1,56 +1,66 @@ #include "coding.h" #include "../util.h" -const short afc_coef[16][2] = -{{0,0}, -{0x0800,0}, -{0,0x0800}, -{0x0400,0x0400}, -{0x1000,0xf800}, -{0x0e00,0xfa00}, -{0x0c00,0xfc00}, -{0x1200,0xf600}, -{0x1068,0xf738}, -{0x12c0,0xf704}, -{0x1400,0xf400}, -{0x0800,0xf800}, -{0x0400,0xfc00}, -{0xfc00,0x0400}, -{0xfc00,0}, -{0xf800,0}}; +static const int16_t afc_coefs[16][2] = { + { 0, 0 }, + { 2048, 0 }, + { 0, 2048 }, + { 1024, 1024 }, + { 4096,-2048 }, + { 3584,-1536 }, + { 3072,-1024 }, + { 4608,-2560 }, + { 4200,-2248 }, + { 4800,-2300 }, + { 5120,-3072 }, + { 2048,-2048 }, + { 1024,-1024 }, + {-1024, 1024 }, + {-1024, 0 }, + {-2048, 0 } +}; -void decode_ngc_afc(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { - int i=first_sample; - int32_t sample_count; - - int framesin = first_sample/16; - - int8_t header = read_8bit(framesin*9+stream->offset,stream->streamfile); - int32_t scale = 1 << ((header>>4) & 0xf); - int coef_index = (header & 0xf); +void decode_ngc_afc(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { + uint8_t frame[0x09] = {0}; + off_t frame_offset; + int i, frames_in, sample_count = 0; + size_t bytes_per_frame, samples_per_frame; + int index, scale, coef1, coef2; int32_t hist1 = stream->adpcm_history1_16; int32_t hist2 = stream->adpcm_history2_16; - int coef1 = afc_coef[coef_index][0]; - int coef2 = afc_coef[coef_index][1]; - /*printf("offset: %x\nscale: %d\nindex: %d (%lf,%lf)\nhist: %d %d\n", - (unsigned)stream->offset,scale,coef_index,coef1/2048.0,coef2/2048.0,hist1,hist2);*/ - first_sample = first_sample%16; - for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) { - int sample_byte = read_8bit(framesin*9+stream->offset+1+i/2,stream->streamfile); + /* external interleave, mono */ + bytes_per_frame = 0x09; + samples_per_frame = (bytes_per_frame - 0x01) * 2; /* always 16 */ + frames_in = first_sample / samples_per_frame; - outbuf[sample_count] = clamp16(( - (((i&1? - get_low_nibble_signed(sample_byte): - get_high_nibble_signed(sample_byte) - ) * scale)<<11) + - (coef1 * hist1 + coef2 * hist2))>>11 - ); - /*printf("%hd\n",outbuf[sample_count]);*/ + /* parse frame header */ + frame_offset = stream->offset + bytes_per_frame * frames_in; + read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */ + scale = 1 << ((frame[0] >> 4) & 0xf); + index = (frame[0] & 0xf); + coef1 = afc_coefs[index][0]; + coef2 = afc_coefs[index][1]; + + /* decode nibbles */ + for (i = first_sample; i < first_sample + samples_to_do; i++) { + uint8_t nibbles = frame[0x01 + i/2]; + int32_t sample; + + sample = i&1 ? /* high nibble first */ + get_low_nibble_signed(nibbles) : + get_high_nibble_signed(nibbles); + sample = ((sample * scale) << 11); + sample = (sample + coef1*hist1 + coef2*hist2) >> 11; + + sample = clamp16(sample); + + outbuf[sample_count] = sample; + sample_count += channelspacing; hist2 = hist1; - hist1 = outbuf[sample_count]; + hist1 = sample; } stream->adpcm_history1_16 = hist1; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ngc_dtk_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ngc_dtk_decoder.c index 4ea57cc07..262a55ab5 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ngc_dtk_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ngc_dtk_decoder.c @@ -2,63 +2,61 @@ #include "../util.h" -/* Nintendo GC Disc TracK streaming ADPCM (similar to CD-XA) */ -void decode_ngc_dtk(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { +/* standard XA coefs << 6 */ +static const int8_t dtk_coefs[16][2] = { + { 0, 0 }, + { 60, 0 }, + { 115, 52 }, + { 98, 55 }, + /* rest assumed to be 0s */ +}; + +/* Nintendo GC Disc TracK streaming ADPCM (similar to XA) */ +void decode_ngc_dtk(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { + uint8_t frame[0x20] = {0}; off_t frame_offset; int i, frames_in, sample_count = 0; size_t bytes_per_frame, samples_per_frame; - uint8_t coef_index, shift_factor; + int index, shift, coef1, coef2; int32_t hist1 = stream->adpcm_history1_32; int32_t hist2 = stream->adpcm_history2_32; /* external interleave (fixed size), stereo */ bytes_per_frame = 0x20; - samples_per_frame = 28; + samples_per_frame = (0x20 - 0x04); /* 28 for each channel */ frames_in = first_sample / samples_per_frame; first_sample = first_sample % samples_per_frame; /* parse frame L/R header (repeated at 0x03/04) */ - frame_offset = stream->offset + bytes_per_frame*frames_in; - coef_index = ((uint8_t)read_8bit(frame_offset+channel,stream->streamfile) >> 4) & 0xf; - shift_factor = ((uint8_t)read_8bit(frame_offset+channel,stream->streamfile) >> 0) & 0xf; + frame_offset = stream->offset + bytes_per_frame * frames_in; + read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */ + index = (frame[channel] >> 4) & 0xf; + shift = (frame[channel] >> 0) & 0xf; + coef1 = dtk_coefs[index][0]; + coef2 = dtk_coefs[index][1]; /* rare but happens, also repeated headers don't match (ex. Ikaruga (GC) SONG02.adp) */ - VGM_ASSERT_ONCE(coef_index > 4 || shift_factor > 12, "DTK: incorrect coefs/shift at %x\n", (uint32_t)frame_offset); + VGM_ASSERT_ONCE(index > 4 || shift > 12, "DTK: incorrect coefs/shift at %x\n", (uint32_t)frame_offset); /* decode nibbles */ - for (i = first_sample; i < first_sample+samples_to_do; i++) { - int32_t hist = 0, new_sample; - uint8_t nibbles = (uint8_t)read_8bit(frame_offset+0x04+i,stream->streamfile); + for (i = first_sample; i < first_sample + samples_to_do; i++) { + int sample, hist; + uint8_t nibbles = frame[0x04 + i]; - /* apply XA filters << 6 */ - switch(coef_index) { - case 0: - hist = 0; // (hist1 * 0) - (hist2 * 0); - break; - case 1: - hist = (hist1 * 60); // - (hist2 * 0); - break; - case 2: - hist = (hist1 * 115) - (hist2 * 52); - break; - case 3: - hist = (hist1 * 98) - (hist2 * 55); - break; - } - hist = (hist + 32) >> 6; - if (hist > 0x1fffff) hist = 0x1fffff; - if (hist < -0x200000) hist = -0x200000; + hist = (hist1*coef1 - hist2*coef2 + 32) >> 6; + if (hist > 2097151) hist = 2097151; + else if (hist < -2097152) hist = -2097152; - new_sample = (channel==0) ? /* L=low nibble first */ + sample = (channel==0) ? /* L=low nibble first */ get_low_nibble_signed(nibbles) : get_high_nibble_signed(nibbles); - new_sample = (new_sample << 12) >> shift_factor; - new_sample = (new_sample << 6) + hist; + sample = (sample << 12) >> shift; + sample = (sample << 6) + hist; hist2 = hist1; - hist1 = new_sample; + hist1 = sample; /* clamp *after* this so hist goes pretty high */ - outbuf[sample_count] = clamp16(new_sample >> 6); + outbuf[sample_count] = clamp16(sample >> 6); sample_count += channelspacing; } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/pcm_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/pcm_decoder.c index cd1b623e5..cb86e2d14 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/pcm_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/pcm_decoder.c @@ -206,15 +206,11 @@ void decode_alaw(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacin void decode_pcmfloat(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int big_endian) { int i, sample_count; - int32_t (*read_32bit)(off_t,STREAMFILE*) = big_endian ? read_32bitBE : read_32bitLE; + float (*read_f32)(off_t,STREAMFILE*) = big_endian ? read_f32be : read_f32le; for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) { - uint32_t sample_int = read_32bit(stream->offset+i*4,stream->streamfile); - float* sample_float; - int sample_pcm; - - sample_float = (float*)&sample_int; - sample_pcm = (int)floor((*sample_float) * 32767.f + .5f); + float sample_float = read_f32(stream->offset+i*4,stream->streamfile); + int sample_pcm = (int)floor(sample_float * 32767.f + .5f); outbuf[sample_count] = clamp16(sample_pcm); } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ptadpcm_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ptadpcm_decoder.c index e128433ce..792466489 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ptadpcm_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ptadpcm_decoder.c @@ -1,54 +1,69 @@ #include "coding.h" -/* a somewhat IMA-like mix of step+next index all in one (maybe an array[][] originally?) */ -static const int32_t ptadpcm_table[(16+16)*16] = { /* 16 of (step+index) + 16 values in a nibble */ - -14, 2, -10, 2, -7, 1, -5, 1, -3, 0, -2, 0, -1, 0, 0, 0, - 0, 0, 1, 0, 2, 0, 3, 0, 5, 1, 7, 1, 10, 2, 14, 2, - -28, 3, -20, 3, -14, 2, -10, 2, -7, 1, -5, 1, -3, 1, -1, 0, - 1, 0, 3, 1, 5, 1, 7, 1, 10, 2, 14, 2, 20, 3, 28, 3, - -56, 4, -40, 4, -28, 3, -20, 3, -14, 2, -10, 2, -6, 2, -2, 1, - 2, 1, 6, 2, 10, 2, 14, 2, 20, 3, 28, 3, 40, 4, 56, 4, - -112, 5, -80, 5, -56, 4, -40, 4, -28, 3, -20, 3, -12, 3, -4, 2, - 4, 2, 12, 3, 20, 3, 28, 3, 40, 4, 56, 4, 80, 5, 112, 5, - -224, 6, -160, 6, -112, 5, -80, 5, -56, 4, -40, 4, -24, 4, -8, 3, - 8, 3, 24, 4, 40, 4, 56, 4, 80, 5, 112, 5, 160, 6, 224, 6, - -448, 7, -320, 7, -224, 6, -160, 6, -112, 5, -80, 5, -48, 5, -16, 4, - 16, 4, 48, 5, 80, 5, 112, 5, 160, 6, 224, 6, 320, 7, 448, 7, - -896, 8, -640, 8, -448, 7, -320, 7, -224, 6, -160, 6, -96, 6, -32, 5, - 32, 5, 96, 6, 160, 6, 224, 6, 320, 7, 448, 7, 640, 8, 896, 8, - -1792, 9, -1280, 9, -896, 8, -640, 8, -448, 7, -320, 7, -192, 7, -64, 6, - 64, 6, 192, 7, 320, 7, 448, 7, 640, 8, 896, 8, 1280, 9, 1792, 9, - -3584, 10, -2560, 10, -1792, 9, -1280, 9, -896, 8, -640, 8, -384, 8, -128, 7, - 128, 7, 384, 8, 640, 8, 896, 8, 1280, 9, 1792, 9, 2560, 10, 3584, 10, - -7168, 11, -5120, 11, -3584, 10, -2560, 10, -1792, 9, -1280, 9, -768, 9, -256, 8, - 256, 8, 768, 9, 1280, 9, 1792, 9, 2560, 10, 3584, 10, 5120, 11, 7168, 11, - -14336, 11, -10240, 11, -7168, 11, -5120, 11, -3584, 10, -2560, 10, -1536, 10, -512, 9, - 512, 9, 1536, 10, 2560, 10, 3584, 10, 5120, 11, 7168, 11, 10240, 11, 14336, 11, - -28672, 11, -20480, 11, -14336, 11, -10240, 11, -7168, 11, -5120, 11, -3072, 11, -1024, 10, - 1024, 10, 3072, 11, 5120, 11, 7168, 11, 10240, 11, 14336, 11, 20480, 11, 28672, 11, - /* rest is 0s */ +/* a somewhat IMA-like mix of pre-calculated [index][nibble][step,index] all in one */ +static const int32_t ptadpcm_table[16][16][2] = { + { + { -14, 2}, { -10, 2}, { -7, 1}, { -5, 1}, { -3, 0}, { -2, 0}, { -1, 0}, { 0, 0}, + { 0, 0}, { 1, 0}, { 2, 0}, { 3, 0}, { 5, 1}, { 7, 1}, { 10, 2}, { 14, 2}, + }, { + { -28, 3}, { -20, 3}, { -14, 2}, { -10, 2}, { -7, 1}, { -5, 1}, { -3, 1}, { -1, 0}, + { 1, 0}, { 3, 1}, { 5, 1}, { 7, 1}, { 10, 2}, { 14, 2}, { 20, 3}, { 28, 3}, + }, { + { -56, 4}, { -40, 4}, { -28, 3}, { -20, 3}, { -14, 2}, { -10, 2}, { -6, 2}, { -2, 1}, + { 2, 1}, { 6, 2}, { 10, 2}, { 14, 2}, { 20, 3}, { 28, 3}, { 40, 4}, { 56, 4}, + }, { + { -112, 5}, { -80, 5}, { -56, 4}, { -40, 4}, { -28, 3}, { -20, 3}, { -12, 3}, { -4, 2}, + { 4, 2}, { 12, 3}, { 20, 3}, { 28, 3}, { 40, 4}, { 56, 4}, { 80, 5}, { 112, 5}, + }, { + { -224, 6}, { -160, 6}, { -112, 5}, { -80, 5}, { -56, 4}, { -40, 4}, { -24, 4}, { -8, 3}, + { 8, 3}, { 24, 4}, { 40, 4}, { 56, 4}, { 80, 5}, { 112, 5}, { 160, 6}, { 224, 6}, + }, { + { -448, 7}, { -320, 7}, { -224, 6}, { -160, 6}, { -112, 5}, { -80, 5}, { -48, 5}, { -16, 4}, + { 16, 4}, { 48, 5}, { 80, 5}, { 112, 5}, { 160, 6}, { 224, 6}, { 320, 7}, { 448, 7}, + }, { + { -896, 8}, { -640, 8}, { -448, 7}, { -320, 7}, { -224, 6}, { -160, 6}, { -96, 6}, { -32, 5}, + { 32, 5}, { 96, 6}, { 160, 6}, { 224, 6}, { 320, 7}, { 448, 7}, { 640, 8}, { 896, 8}, + }, { + { -1792, 9}, { -1280, 9}, { -896, 8}, { -640, 8}, { -448, 7}, { -320, 7}, { -192, 7}, { -64, 6}, + { 64, 6}, { 192, 7}, { 320, 7}, { 448, 7}, { 640, 8}, { 896, 8}, { 1280, 9}, { 1792, 9}, + }, { + { -3584, 10}, { -2560, 10}, { -1792, 9}, { -1280, 9}, { -896, 8}, { -640, 8}, { -384, 8}, { -128, 7}, + { 128, 7}, { 384, 8}, { 640, 8}, { 896, 8}, { 1280, 9}, { 1792, 9}, { 2560, 10}, { 3584, 10}, + }, { + { -7168, 11}, { -5120, 11}, { -3584, 10}, { -2560, 10}, {-1792, 9}, {-1280, 9}, { -768, 9}, { -256, 8}, + { 256, 8}, { 768, 9}, { 1280, 9}, { 1792, 9}, { 2560, 10}, { 3584, 10}, { 5120, 11}, { 7168, 11}, + }, { + {-14336, 11}, {-10240, 11}, { -7168, 11}, { -5120, 11}, {-3584, 10}, {-2560, 10}, {-1536, 10}, { -512, 9}, + { 512, 9}, { 1536, 10}, { 2560, 10}, { 3584, 10}, { 5120, 11}, { 7168, 11}, {10240, 11}, {14336, 11}, + }, { + {-28672, 11}, {-20480, 11}, {-14336, 11}, {-10240, 11}, {-7168, 11}, {-5120, 11}, {-3072, 11}, {-1024, 10}, + { 1024, 10}, { 3072, 11}, { 5120, 11}, { 7168, 11}, {10240, 11}, {14336, 11}, {20480, 11}, {28672, 11}, + }, + /* rest is 0s (uses up to index 12) */ }; /* Platinum "PtADPCM" custom ADPCM for Wwise (reverse engineered from .exes). */ -void decode_ptadpcm(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, size_t frame_size) { +void decode_ptadpcm(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, size_t frame_size) { + uint8_t frame[0x104] = {0}; off_t frame_offset; int i, frames_in, sample_count = 0, samples_done = 0; size_t bytes_per_frame, samples_per_frame; int16_t hist1, hist2; - int index, step; + int index, nibble, step; /* external interleave (variable size), mono */ bytes_per_frame = frame_size; samples_per_frame = 2 + (frame_size - 0x05) * 2; frames_in = first_sample / samples_per_frame; - first_sample = first_sample % samples_per_frame; + //first_sample = first_sample % samples_per_frame; /* parse frame header */ frame_offset = stream->offset + bytes_per_frame*frames_in; - hist2 = read_16bitLE(frame_offset+0x00,stream->streamfile); - hist1 = read_16bitLE(frame_offset+0x02,stream->streamfile); - index = (uint8_t)read_8bit(frame_offset+0x04,stream->streamfile); + read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */ + hist2 = get_s16le(frame + 0x00); + hist1 = get_s16le(frame + 0x02); + index = frame[0x04]; VGM_ASSERT_ONCE(index > 12, "PTADPCM: incorrect index at %x\n", (uint32_t)frame_offset); @@ -66,26 +81,25 @@ void decode_ptadpcm(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspa /* decode nibbles */ for (i = first_sample; i < first_sample + samples_to_do; i++) { - int32_t new_sample; - uint8_t nibbles = (uint8_t)read_8bit(frame_offset+0x05 + i/2,stream->streamfile); - uint8_t nibble; + uint8_t nibbles = frame[0x05 + i/2]; + int32_t sample; nibble = !(i&1) ? /* low nibble first */ (nibbles >> 0) & 0xF : (nibbles >> 4) & 0xF; - step = ptadpcm_table[2*(nibble + 16*index) + 0]; - index = ptadpcm_table[2*(nibble + 16*index) + 1]; - new_sample = clamp16(step + 2*hist1 - hist2); + step = ptadpcm_table[index][nibble][0]; + index = ptadpcm_table[index][nibble][1]; + sample = clamp16(step + 2*hist1 - hist2); if (sample_count >= first_sample && samples_done < samples_to_do) { - outbuf[samples_done * channelspacing] = new_sample; + outbuf[samples_done * channelspacing] = sample; samples_done++; } sample_count++; hist2 = hist1; - hist1 = new_sample; + hist1 = sample; } //stream->adpcm_history1_32 = hist1; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/xmd_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/xmd_decoder.c index a438cea5e..43c7ab258 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/xmd_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/xmd_decoder.c @@ -3,25 +3,27 @@ /* Decodes Konami XMD from Xbox games. * Algorithm reverse engineered from SH4/CV:CoD's xbe (byte-accurate). */ -void decode_xmd(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, size_t frame_size) { +void decode_xmd(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, size_t frame_size) { + uint8_t frame[0x15] = {0}; off_t frame_offset; int i, frames_in, sample_count = 0, samples_done = 0; size_t bytes_per_frame, samples_per_frame; int16_t hist1, hist2; uint16_t scale; + /* external interleave (variable size), mono */ bytes_per_frame = frame_size; samples_per_frame = 2 + (frame_size - 0x06) * 2; frames_in = first_sample / samples_per_frame; - first_sample = first_sample % samples_per_frame; + //first_sample = first_sample % samples_per_frame; /* for flat layout */ /* parse frame header */ - frame_offset = stream->offset + bytes_per_frame*frames_in; - hist2 = read_16bitLE(frame_offset+0x00,stream->streamfile); - hist1 = read_16bitLE(frame_offset+0x02,stream->streamfile); - scale = (uint16_t)read_16bitLE(frame_offset+0x04,stream->streamfile); /* scale doesn't go too high though */ - + frame_offset = stream->offset + bytes_per_frame * frames_in; + read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */ + hist2 = get_s16le(frame + 0x00); + hist1 = get_s16le(frame + 0x02); + scale = get_u16le(frame + 0x04); /* scale doesn't go too high though */ /* write header samples (needed) */ if (sample_count >= first_sample && samples_done < samples_to_do) { @@ -37,25 +39,25 @@ void decode_xmd(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, /* decode nibbles */ for (i = first_sample; i < first_sample + samples_to_do; i++) { - int32_t new_sample; - uint8_t nibbles = (uint8_t)read_8bit(frame_offset+0x06 + i/2,stream->streamfile); + uint8_t nibbles = frame[0x06 + i/2]; + int32_t sample; - new_sample = i&1 ? /* low nibble first */ + sample = i&1 ? /* low nibble first */ get_high_nibble_signed(nibbles): get_low_nibble_signed(nibbles); /* Coefs are based on XA's filter 2 (using those creates hissing in some songs though) * ex. 1.796875 * (1 << 14) = 0x7300, -0.8125 * (1 << 14) = -0x3400 */ - new_sample = (new_sample*(scale<<14) + (hist1*0x7298) - (hist2*0x3350)) >> 14; + sample = (sample*(scale<<14) + (hist1*0x7298) - (hist2*0x3350)) >> 14; //new_sample = clamp16(new_sample); /* not needed */ if (sample_count >= first_sample && samples_done < samples_to_do) { - outbuf[samples_done * channelspacing] = (int16_t)new_sample; + outbuf[samples_done * channelspacing] = (int16_t)sample; samples_done++; } sample_count++; hist2 = hist1; - hist1 = new_sample; + hist1 = sample; } //stream->adpcm_history1_32 = hist1; diff --git a/Frameworks/vgmstream/vgmstream/src/formats.c b/Frameworks/vgmstream/vgmstream/src/formats.c index d59e1dca4..ee93d9054 100644 --- a/Frameworks/vgmstream/vgmstream/src/formats.c +++ b/Frameworks/vgmstream/vgmstream/src/formats.c @@ -6,10 +6,16 @@ * to inform plugins that need it. Common extensions are commented out to avoid stealing them * and possibly adding an unwanted association to the player. */ +/* Common extensions (like .wav or .ogg) should go in the common_extension_list. It should only + * contain common formats that vgmstream can also parse, to avoid hijacking them (since their + * plugins typically are faster and have desirable features vgmstream won't handle). Extensions of + * formats not parsed don't need to go there (for example .stm is a Scream Tracker Module elsewhere, + * but our .stm is very different so there is no conflict). */ + /* Some extensions require external libraries and could be #ifdef, not worth. */ /* Formats marked as "not parsed" mean they'll go through FFmpeg, the header/extension isn't - * parsed by vgmstream and typically won't not be fully accurate. May have a .ext.pos pair for fun. */ + * parsed by vgmstream and typically won't not be fully accurate. */ static const char* extension_list[] = { @@ -25,7 +31,6 @@ static const char* extension_list[] = { //"aac", //common "aa3", //FFmpeg/not parsed (ATRAC3/ATRAC3PLUS/MP3/LPCM/WMA) - "aaap", "aax", "abk", //"ac3", //common, FFmpeg/not parsed (AC3) @@ -131,6 +136,7 @@ static const char* extension_list[] = { "data", "dax", "dbm", + "dct", "dcs", "ddsp", "de2", @@ -175,7 +181,7 @@ static const char* extension_list[] = { "gin", "gms", "gsb", - //"gsf", //conflicts with GBA gsf plugins? + "gsf", "gtd", "gwm", @@ -211,6 +217,7 @@ static const char* extension_list[] = { "imc", "int", "is14", + "isb", "isd", "isws", "itl", @@ -268,6 +275,7 @@ static const char* extension_list[] = { "lwma", //fake extension for .wma, FFmpeg/not parsed "mab", + "mad", "map", "matx", "mc3", @@ -444,7 +452,7 @@ static const char* extension_list[] = { "sss", "ster", "sth", - //"stm", //common + "stm", "stma", //fake extension/header id for .stm "str", "stream", @@ -568,6 +576,7 @@ static const char* extension_list[] = { "ydsp", "ymf", + "zic", "zsd", "zsm", "zss", @@ -585,7 +594,6 @@ static const char* common_extension_list[] = { "aiff", //common "bin", //common "flac", //common - "gsf", //conflicts with GBA gsf plugins? "mp+", //common [Moonshine Runners (PC)] "mp2", //common "mp3", //common @@ -593,7 +601,6 @@ static const char* common_extension_list[] = { "mpc", //common "ogg", //common "opus", //common - "stm", //common "wav", //common }; @@ -830,7 +837,7 @@ static const meta_info meta_info_list[] = { {meta_RFRM, "Retro Studios RFRM header"}, {meta_NGC_ADPDTK, "Nintendo ADP raw header"}, {meta_RSF, "Retro Studios RSF raw header"}, - {meta_AFC, "Nintendo AFC header"}, + {meta_AFC, "Nintendo .AFC header"}, {meta_AST, "Nintendo AST header"}, {meta_HALPST, "HAL Laboratory HALPST header"}, {meta_DSP_RS03, "Retro Studios RS03 header"}, @@ -864,6 +871,7 @@ static const meta_info meta_info_list[] = { {meta_PS2_VAGi, "Sony VAGi header"}, {meta_PS2_VAGp, "Sony VAGp header"}, {meta_PS2_pGAV, "Sony pGAV header"}, + {meta_PS2_VAGp_AAAP, "Acclaim Austin AAAp VAG header"}, {meta_SEB, "Game Arts .SEB header"}, {meta_STR_WAV, "Blitz Games .STR+WAV header"}, {meta_PS2_ILD, "ILD header"}, @@ -1021,7 +1029,6 @@ static const meta_info meta_info_list[] = { {meta_WII_BNS, "Nintendo BNS header"}, {meta_WII_WAS, "Sumo Digital iSWS header"}, {meta_XBOX_HLWAV, "Half Life 2 bgm header"}, - {meta_STX, "Nintendo .stx header"}, {meta_MYSPD, "U-Sing .MYSPD header"}, {meta_HIS, "Her Interactive HIS header"}, {meta_PS2_AST, "KOEI AST header"}, @@ -1029,7 +1036,7 @@ static const meta_info meta_info_list[] = { {meta_DMSG, "RIFF/DMSGsegh header"}, {meta_PONA_3DO, "Policenauts BGM header"}, {meta_PONA_PSX, "Policenauts BGM header"}, - {meta_NGC_DSP_AAAP, "Acclaim Austin AAAp header"}, + {meta_NGC_DSP_AAAP, "Acclaim Austin AAAp DSP header"}, {meta_NGC_DSP_KONAMI, "Konami DSP header"}, {meta_PS2_STER, "STER Header"}, {meta_BNSF, "Namco Bandai BNSF header"}, @@ -1240,6 +1247,8 @@ static const meta_info meta_info_list[] = { {meta_XMV_VALVE, "Valve XMV header"}, {meta_UBI_HX, "Ubisoft HXx header"}, {meta_BMP_KONAMI, "Konami BMP header"}, + {meta_ISB, "Creative ISACT header"}, + {meta_XSSB, "Artoon XSSB header"}, }; diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_1snh.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_1snh.c index d784c2fc0..5e34c6158 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_1snh.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_1snh.c @@ -6,89 +6,81 @@ void block_update_ea_1snh(off_t block_offset, VGMSTREAM * vgmstream) { STREAMFILE* streamFile = vgmstream->ch[0].streamfile; int i; - size_t block_size = 0, block_header = 0; + uint32_t block_id; + size_t block_size = 0, block_header = 0, audio_size = 0; int32_t (*read_32bit)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_32bitBE : read_32bitLE; - size_t file_size = get_streamfile_size(streamFile); /* EOF reads: signal we have nothing and let the layout fail */ - if (block_offset >= file_size) { + if (block_offset >= get_streamfile_size(streamFile)) { vgmstream->current_block_offset = block_offset; vgmstream->next_block_offset = block_offset; vgmstream->current_block_samples = -1; return; } + block_id = read_32bitBE(block_offset + 0x00, streamFile); - while (block_offset < file_size) { - uint32_t id = read_32bitBE(block_offset+0x00,streamFile); + /* BE in SAT, but one file may have both BE and LE chunks [FIFA 98 (SAT): movie LE, audio BE] */ + if (guess_endianness32bit(block_offset + 0x04, streamFile)) + block_size = read_32bitBE(block_offset + 0x04, streamFile); + else + block_size = read_32bitLE(block_offset + 0x04, streamFile); - /* BE in SAT, but one file may have both BE and LE chunks [FIFA 98 (SAT): movie LE, audio BE] */ - if (guess_endianness32bit(block_offset+0x04,streamFile)) - block_size = read_32bitBE(block_offset+0x04,streamFile); - else - block_size = read_32bitLE(block_offset+0x04,streamFile); + block_header = 0; - block_header = 0; + if (block_id == 0x31534E68 || block_id == 0x53454144) { /* "1SNh" "SEAD" audio header */ + int is_sead = (block_id == 0x53454144); + int is_eacs = read_32bitBE(block_offset + 0x08, streamFile) == 0x45414353; + int is_zero = read_32bitBE(block_offset + 0x08, streamFile) == 0x00; - if (id == 0x31534E68 || id == 0x53454144) { /* "1SNh" "SEAD" audio header */ - int is_sead = (id == 0x53454144); - int is_eacs = read_32bitBE(block_offset+0x08, streamFile) == 0x45414353; - - block_header = is_eacs ? 0x28 : (is_sead ? 0x14 : 0x2c); - if (block_header >= block_size) /* sometimes has audio data after header */ - block_header = 0; - } - else if (id == 0x31534E64 || id == 0x534E4443) { /* "1SNd" "SNDC" audio data */ - block_header = 0x08; - } - else if (id == 0x00000000 || id == 0xFFFFFFFF || id == 0x31534E65) { /* EOF or "1SNe" */ - vgmstream->current_block_samples = -1; - break; - } - - if (block_header) { - break; - } - - block_offset += block_size; + block_header = (is_eacs || is_zero) ? 0x28 : (is_sead ? 0x14 : 0x2c); + if (block_header >= block_size) /* sometimes has audio data after header */ + block_header = 0; + } else if (block_id == 0x31534E64 || block_id == 0x534E4443) { /* "1SNd" "SNDC" audio data */ + block_header = 0x08; + } else if (block_id == 0x00000000 || block_id == 0xFFFFFFFF || block_id == 0x31534E65) { /* EOF or "1SNe" */ + vgmstream->current_block_samples = -1; + return; } vgmstream->current_block_offset = block_offset; vgmstream->next_block_offset = block_offset + block_size; - vgmstream->current_block_size = block_size - block_header; - if (vgmstream->current_block_samples == -1) + if (block_header == 0) { + /* no audio data, skip this block */ + vgmstream->current_block_samples = 0; return; + } + audio_size = block_size - block_header; /* set new channel offsets and block sizes */ switch(vgmstream->coding_type) { case coding_PCM8_int: case coding_ULAW_int: - vgmstream->current_block_size /= vgmstream->channels; + vgmstream->current_block_samples = pcm_bytes_to_samples(audio_size, vgmstream->channels, 8); for (i=0;i<vgmstream->channels;i++) { vgmstream->ch[i].offset = block_offset + block_header + i; } break; case coding_PCM16_int: - vgmstream->current_block_size /= vgmstream->channels; + vgmstream->current_block_samples = pcm_bytes_to_samples(audio_size, vgmstream->channels, 16); for (i=0;i<vgmstream->channels;i++) { vgmstream->ch[i].offset = block_offset + block_header + (i*2); } break; case coding_PSX: - vgmstream->current_block_size /= vgmstream->channels; + vgmstream->current_block_samples = ps_bytes_to_samples(audio_size, vgmstream->channels); for (i=0;i<vgmstream->channels;i++) { - vgmstream->ch[i].offset = block_offset + block_header + i*vgmstream->current_block_size; + vgmstream->ch[i].offset = block_offset + block_header + i*(audio_size/vgmstream->channels); } break; case coding_DVI_IMA: if (vgmstream->codec_config == 1) { /* ADPCM hist */ vgmstream->current_block_samples = read_32bit(block_offset + block_header, streamFile); - vgmstream->current_block_size = 0; // - (0x04 + 0x08*vgmstream->channels); /* should be equivalent */ for(i = 0; i < vgmstream->channels; i++) { off_t adpcm_offset = block_offset + block_header + 0x04; @@ -101,6 +93,7 @@ void block_update_ea_1snh(off_t block_offset, VGMSTREAM * vgmstream) { // "EA 1SHN blocked: different expected vs block num samples at %lx\n", block_offset); } else { + vgmstream->current_block_samples = ima_bytes_to_samples(audio_size, vgmstream->channels); for(i = 0; i < vgmstream->channels; i++) { vgmstream->ch[i].offset = block_offset + block_header; } diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_schl.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_schl.c index 7b4b024e4..22d3431e0 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_schl.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_schl.c @@ -6,7 +6,6 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) { STREAMFILE* streamFile = vgmstream->ch[0].streamfile; int i; - int new_schl = 0; size_t block_size, block_samples; int32_t (*read_32bit)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_32bitBE : read_32bitLE; @@ -44,9 +43,21 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) { block_samples = 0; /* layout ignores this */ } - /* "SCHl" start block (movie "SHxx" shouldn't use multi files) */ - if (block_id == 0x5343486C) - new_schl = 1; + /* "SCHl" start block, when decoding multi files pasted together */ + if (block_id == 0x5343486C) { + switch(vgmstream->coding_type) { + case coding_MPEG_custom: + case coding_MPEG_layer1: + case coding_MPEG_layer2: + case coding_MPEG_layer3: + case coding_MPEG_ealayer3: + /* need to reset MPEG decoder to reset discards and trailing samples in the buffers */ + flush_mpeg(vgmstream->codec_data); + break; + default: + break; + } + } /* padding between "SCEl" and next "SCHl" (when subfiles exist) */ if (block_id == 0x00000000) @@ -159,12 +170,6 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) { vgmstream->ch[i].offset = block_offset + 0x0C + (0x04*vgmstream->channels) + channel_start; } - - /* SCHl with multiple SCHl need to reset their MPEG decoder as there are trailing samples in the buffers */ - if (new_schl) { - flush_mpeg(vgmstream->codec_data); - } - break; #endif /* id, size, samples, offsets-per-channel, interleaved data (w/ optional hist per channel) */ diff --git a/Frameworks/vgmstream/vgmstream/src/layout/segmented.c b/Frameworks/vgmstream/vgmstream/src/layout/segmented.c index 1b4c953ed..7965dc9b1 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/segmented.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/segmented.c @@ -2,7 +2,7 @@ #include "../vgmstream.h" #include "../mixing.h" -#define VGMSTREAM_MAX_SEGMENTS 512 +#define VGMSTREAM_MAX_SEGMENTS 1024 #define VGMSTREAM_SEGMENT_SAMPLE_BUFFER 8192 diff --git a/Frameworks/vgmstream/vgmstream/src/meta/adx.c b/Frameworks/vgmstream/vgmstream/src/meta/adx.c index 5252b641b..a42c26971 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/adx.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/adx.c @@ -3,14 +3,15 @@ #endif #include <math.h> #include <limits.h> - #include "meta.h" #include "adx_keys.h" #include "../coding/coding.h" -#define MAX_TEST_FRAMES (INT_MAX/0x8000) -static int find_adx_key(STREAMFILE *streamFile, uint8_t type, uint16_t *xor_start, uint16_t *xor_mult, uint16_t *xor_add); +#define ADX_KEY_MAX_TEST_FRAMES 32768 +#define ADX_KEY_TEST_BUFFER_SIZE 0x8000 + +static int find_adx_key(STREAMFILE *sf, uint8_t type, uint16_t *xor_start, uint16_t *xor_mult, uint16_t *xor_add); /* ADX - CRI Middleware format */ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) { @@ -18,7 +19,7 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) { off_t start_offset, hist_offset = 0; int loop_flag = 0, channel_count; int32_t loop_start_sample = 0, loop_end_sample = 0; - uint16_t version_signature; + uint16_t version; uint8_t encoding_type; uint8_t frame_size; @@ -29,79 +30,77 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) { /* checks*/ - /* .adx: standard, .adp: Headhunter (DC) */ + /* .adx: standard + * .adp: Headhunter (DC) */ if (!check_extensions(streamFile,"adx,adp")) goto fail; - /* check first 2 bytes */ - if ((uint16_t)read_16bitBE(0x00,streamFile)!=0x8000) goto fail; + if ((uint16_t)read_16bitBE(0x00,streamFile) != 0x8000) + goto fail; - /* get stream offset, check for CRI signature just before */ - start_offset = (uint16_t)read_16bitBE(0x02,streamFile) + 4; - if ((uint16_t)read_16bitBE(start_offset-6,streamFile)!=0x2863 || /* "(c" */ - (uint32_t)read_32bitBE(start_offset-4,streamFile)!=0x29435249 /* ")CRI" */ - ) goto fail; + start_offset = (uint16_t)read_16bitBE(0x02,streamFile) + 0x04; + if ((uint16_t)read_16bitBE(start_offset - 0x06,streamFile) != 0x2863 || /* "(c" */ + (uint32_t)read_32bitBE(start_offset - 0x04,streamFile) != 0x29435249) /* ")CRI" */ + goto fail; - /* check for encoding type */ - /* 0x02 is for some unknown fixed filter, 0x03 is standard ADX, 0x04 is - * ADX with exponential scale, 0x10 is AHX for DC, 0x11 is AHX */ encoding_type = read_8bit(0x04, streamFile); - switch (encoding_type) { - case 2: + case 0x02: coding_type = coding_CRI_ADX_fixed; break; - case 3: + case 0x03: coding_type = coding_CRI_ADX; break; - case 4: + case 0x04: coding_type = coding_CRI_ADX_exp; break; - default: + default: /* 0x10 is AHX for DC, 0x11 is AHX */ goto fail; } + /* ADX encoders can't set this value, but is honored by ADXPlay if changed and multiple of 0x12, + * though output is unusual and may not be fully supported (works in mono so not an interleave) */ frame_size = read_8bit(0x05, streamFile); - /* check for bits per sample? (only 4 makes sense for ADX) */ - if (read_8bit(0x06,streamFile) != 4) goto fail; + if (read_8bit(0x06,streamFile) != 4) /* bits per sample */ + goto fail; /* older ADX (adxencd) up to 2ch, newer ADX (criatomencd) up to 8 */ channel_count = read_8bit(0x07,streamFile); + /* 0x08: sample rate */ + /* 0x0c: samples */ + /* 0x10: high-pass frequency */ - /* check version signature, read loop info */ - version_signature = read_16bitBE(0x12,streamFile); - + version = read_16bitBE(0x12,streamFile); /* encryption */ - if (version_signature == 0x0408) { + if (version == 0x0408) { if (find_adx_key(streamFile, 8, &xor_start, &xor_mult, &xor_add)) { coding_type = coding_CRI_ADX_enc_8; - version_signature = 0x0400; + version = 0x0400; } } - else if (version_signature == 0x0409) { + else if (version == 0x0409) { if (find_adx_key(streamFile, 9, &xor_start, &xor_mult, &xor_add)) { coding_type = coding_CRI_ADX_enc_9; - version_signature = 0x0400; + version = 0x0400; } } - /* version + extra data */ - if (version_signature == 0x0300) { /* early ADX (~2004?) */ + if (version == 0x0300) { /* early ADX (~1998) [Grandia (SAT), Baroque (SAT)] */ size_t base_size = 0x14, loops_size = 0x18; header_type = meta_ADX_03; /* no sample history */ - if (start_offset - 6 >= base_size + loops_size) { /* enough space for loop info? */ + if (start_offset - 0x06 >= base_size + loops_size) { /* enough space for loop info? */ off_t loops_offset = base_size; - /* off+0x00 (2): initial loop padding (the encoder adds a few blank samples so loop start is block-aligned; max 31) + /* 0x00 (2): initial loop padding (the encoder adds a few blank samples so loop start is block-aligned; max 31) * ex. loop_start=12: enc_start=32, padding=20 (32-20=12); loop_start=35: enc_start=64, padding=29 (64-29=35) - * off+0x02 (2): loop sample(?) flag (always 1) */ + * 0x02 (2): loop sample(?) flag (always 1) */ loop_flag = read_32bitBE(loops_offset+0x04,streamFile) != 0; /* loop offset(?) flag (always 1) */ loop_start_sample = read_32bitBE(loops_offset+0x08,streamFile); //loop_start_offset = read_32bitBE(loops_offset+0x0c,streamFile); @@ -109,37 +108,40 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) { //loop_end_offset = read_32bitBE(loops_offset+0x14,streamFile); } } - else if (version_signature == 0x0400) { /* common */ + else if (version == 0x0400) { /* common */ size_t base_size = 0x18, hist_size, ainf_size = 0, loops_size = 0x18; off_t ainf_offset; header_type = meta_ADX_04; hist_offset = base_size; /* always present but often blank */ - hist_size = (channel_count > 1 ? 4*channel_count : 4+4); /* min is 8, even in 1ch files */ + hist_size = (channel_count > 1 ? 0x04 * channel_count : 0x04 + 0x04); /* min is 8, even in 1ch files */ - ainf_offset = base_size + hist_size + 0x4; /* not seen with >2ch though */ + ainf_offset = base_size + hist_size + 0x04; /* not seen with >2ch though */ if ((uint32_t)read_32bitBE(ainf_offset+0x00,streamFile) == 0x41494E46) /* "AINF" */ ainf_size = read_32bitBE(ainf_offset+0x04,streamFile); - if (start_offset - ainf_size - 6 >= hist_offset + hist_size + loops_size) { /* enough space for loop info? */ + if (start_offset - ainf_size - 0x06 >= hist_offset + hist_size + loops_size) { /* enough space for loop info? */ off_t loops_offset = base_size + hist_size; - /* off+0x00 (2): initial loop padding (the encoder adds a few blank samples so loop start is block-aligned; max 31) + /* 0x00 (2): initial loop padding (the encoder adds a few blank samples so loop start is block-aligned; max 31) * ex. loop_start=12: enc_start=32, padding=20 (32-20=12); loop_start=35: enc_start=64, padding=29 (64-29=35) - * off+0x02 (2): loop sample(?) flag (always 1) */ + * 0x02 (2): loop sample(?) flag (always 1) */ loop_flag = read_32bitBE(loops_offset+0x04,streamFile) != 0; /* loop offset(?) flag (always 1) */ loop_start_sample = read_32bitBE(loops_offset+0x08,streamFile); - //loop_start_offset = read_32bitBE(loops_offset+0x0c,streamFile); + //loop_start_offset = read_32bitBE(loops_offset+0x0c,streamFile); loop_end_sample = read_32bitBE(loops_offset+0x10,streamFile); - //loop_end_offset = read_32bitBE(loops_offset+0x14,streamFile); + //loop_end_offset = read_32bitBE(loops_offset+0x14,streamFile); } /* AINF header info (may be inserted by CRI's tools but is rarely used) * Can also start right after the loop points (base_size + hist_size + loops_size) - * 0x00 (4): "AINF"; 0x04 (4): size; 0x08 (10): str_id + * 0x00 (4): "AINF" + * 0x04 (4): size + * 0x08 (10): str_id * 0x18 (2): volume (0=base/max?, negative=reduce) - * 0x1c (2): pan l; 0x1e (2): pan r (0=base, max +-128) */ + * 0x1c (2): pan l + * 0x1e (2): pan r (0=base, max +-128) */ /* CINF header info (very rare, found after loops) [Sakura Taisen 3 (PS2)] * 0x00 (4): "CINF" @@ -149,7 +151,7 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) { * 0x48 (-): file name, null terminated */ } - else if (version_signature == 0x0500) { /* found in some SFD: Buggy Heat, appears to have no loop */ + else if (version == 0x0500) { /* found in some SFD: Buggy Heat, appears to have no loop */ header_type = meta_ADX_05; } else { /* not a known/supported version signature */ @@ -161,13 +163,13 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) { vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - vgmstream->num_samples = read_32bitBE(0xc,streamFile); - vgmstream->sample_rate = read_32bitBE(0x8,streamFile); + vgmstream->sample_rate = read_32bitBE(0x08,streamFile); + vgmstream->num_samples = read_32bitBE(0x0c,streamFile); vgmstream->loop_start_sample = loop_start_sample; vgmstream->loop_end_sample = loop_end_sample; vgmstream->coding_type = coding_type; - vgmstream->layout_type = channel_count==1 ? layout_none : layout_interleave; + vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = frame_size; vgmstream->meta_type = header_type; @@ -175,6 +177,7 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) { /* calculate filter coefficients */ if (coding_type == coding_CRI_ADX_fixed) { int i; + /* standard XA coefs * (2<<11) */ for (i = 0; i < channel_count; i++) { vgmstream->ch[i].adpcm_coef[0] = 0x0000; vgmstream->ch[i].adpcm_coef[1] = 0x0000; @@ -194,14 +197,14 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) { x = cutoff; y = vgmstream->sample_rate; - z = cos(2.0*M_PI*x/y); + z = cos(2.0 * M_PI * x / y); - a = M_SQRT2-z; - b = M_SQRT2-1.0; - c = (a-sqrt((a+b)*(a-b)))/b; + a = M_SQRT2 - z; + b = M_SQRT2 - 1.0; + c = (a - sqrt((a + b) * (a - b))) / b; - coef1 = (short)(c*8192); - coef2 = (short)(c*c*-4096); + coef1 = (short)(c * 8192); + coef2 = (short)(c * c * -4096); for (i = 0; i < channel_count; i++) { vgmstream->ch[i].adpcm_coef[0] = coef1; @@ -213,7 +216,7 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) { { int i; - for (i=0;i<channel_count;i++) { + for (i = 0; i < channel_count; i++) { /* 2 hist shorts per ch, corresponding to the very first original sample repeated (verified with CRI's encoders). * Not vital as their effect is small, after a few samples they don't matter, and most songs start in silence. */ if (hist_offset) { @@ -228,7 +231,7 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) { vgmstream->ch[i].adx_mult = xor_mult; vgmstream->ch[i].adx_add = xor_add; - for (j=0;j<i;j++) + for (j = 0; j < i; j++) adx_next_key(&vgmstream->ch[i]); } } @@ -245,12 +248,14 @@ fail: } -/* return 0 if not found, 1 if found and set parameters */ -static int find_adx_key(STREAMFILE *streamFile, uint8_t type, uint16_t *xor_start, uint16_t *xor_mult, uint16_t *xor_add) { - uint16_t * scales = NULL; - uint16_t * prescales = NULL; - int bruteframe = 0, bruteframe_count = -1; - int startoff, endoff; +/* ADX key detection works by reading XORed ADPCM scales in frames, and un-XORing with keys in + * a list. If resulting values are within the expected range for N scales we accept that key. */ +static int find_adx_key(STREAMFILE *sf, uint8_t type, uint16_t *xor_start, uint16_t *xor_mult, uint16_t *xor_add) { + const int frame_size = 0x12; + uint16_t *scales = NULL; + uint16_t *prescales = NULL; + int bruteframe_start = 0, bruteframe_count = -1; + off_t start_offset; int i, rc = 0; @@ -260,7 +265,7 @@ static int find_adx_key(STREAMFILE *streamFile, uint8_t type, uint16_t *xor_star size_t key_size; /* handle type8 keystrings, key9 keycodes and derived keys too */ - key_size = read_key_file(keybuf,0x20, streamFile); + key_size = read_key_file(keybuf,0x20, sf); if (key_size > 0) { int i, is_ascii = 0; @@ -299,77 +304,90 @@ static int find_adx_key(STREAMFILE *streamFile, uint8_t type, uint16_t *xor_star /* setup totals */ { int frame_count; + int channels = read_8bit(0x07, sf); + int num_samples = read_32bitBE(0x0c, sf); + off_t end_offset; - startoff = read_16bitBE(2, streamFile) + 4; - endoff = (read_32bitBE(12, streamFile) + 31) / 32 * 18 * read_8bit(7, streamFile) + startoff; + start_offset = read_16bitBE(0x02, sf) + 0x4; + end_offset = (num_samples + 31) / 32 * frame_size * channels + start_offset; /* samples-to-bytes */ - frame_count = (endoff - startoff) / 18; + frame_count = (end_offset - start_offset) / frame_size; if (frame_count < bruteframe_count || bruteframe_count < 0) bruteframe_count = frame_count; } - /* find longest run of nonzero frames */ + /* find longest run of non-zero frames (zero frames aren't good for key testing) */ { - int longest = -1, longest_length = -1; - int length = 0; + static const uint8_t zeroes[0x12] = {0}; + uint8_t frame[0x12]; + int longest_start = -1, longest_count = -1; + int count = 0; + for (i = 0; i < bruteframe_count; i++) { - static const unsigned char zeroes[18] = {0}; - unsigned char buf[18]; - read_streamfile(buf, startoff + i * 18, 18, streamFile); - if (memcmp(zeroes, buf, 18)) - length++; + read_streamfile(frame, start_offset + i*frame_size, frame_size, sf); + if (memcmp(zeroes, frame, frame_size) != 0) + count++; else - length = 0; - if (length > longest_length) { - longest_length = length; - longest = i - length + 1; - if (longest_length >= 0x8000) + count = 0; + + /* update new record of non-zero frames */ + if (count > longest_count) { + longest_count = count; + longest_start = i - count + 1; + if (longest_count >= ADX_KEY_MAX_TEST_FRAMES) break; } } - if (longest == -1) { - goto find_key_cleanup; + + /* no non-zero frames */ + if (longest_start == -1) { + goto done; } - bruteframe_count = longest_length; - bruteframe = longest; + + bruteframe_start = longest_start; + bruteframe_count = longest_count; + if (bruteframe_count > ADX_KEY_MAX_TEST_FRAMES) //? + bruteframe_count = ADX_KEY_MAX_TEST_FRAMES; } - /* try to guess key */ + /* pre-load scales in a table, to avoid re-reading them per key */ { - const adxkey_info * keys = NULL; - int keycount = 0, keymask = 0; - int scales_to_do; - int key_id; - /* allocate storage for scales */ - scales_to_do = (bruteframe_count > MAX_TEST_FRAMES ? MAX_TEST_FRAMES : bruteframe_count); - scales = malloc(scales_to_do*sizeof(uint16_t)); - if (!scales) goto find_key_cleanup; + scales = malloc(bruteframe_count * sizeof(uint16_t)); + if (!scales) goto done; - /* prescales are those scales before the first frame we test - * against, we use these to compute the actual start */ - if (bruteframe > 0) { - /* allocate memory for the prescales */ - prescales = malloc(bruteframe*sizeof(uint16_t)); - if (!prescales) goto find_key_cleanup; + /* prescales are scales before the first test frame, with some blank frames no good + * for key testing, but we must read to compute XOR value at bruteframe_start */ + if (bruteframe_start > 0) { + /* allocate storage for prescales */ + prescales = malloc(bruteframe_start * sizeof(uint16_t)); + if (!prescales) goto done; /* read the prescales */ - for (i=0; i<bruteframe; i++) { - prescales[i] = read_16bitBE(startoff+i*18, streamFile); + for (i = 0; i < bruteframe_start; i++) { + prescales[i] = read_16bitBE(start_offset + i*frame_size, sf); } } /* read in the scales */ - for (i=0; i < scales_to_do; i++) { - scales[i] = read_16bitBE(startoff+(bruteframe+i)*18, streamFile); + for (i = 0; i < bruteframe_count; i++) { + scales[i] = read_16bitBE(start_offset + (bruteframe_start + i)*frame_size, sf); } + } + /* try to guess key */ + { + const adxkey_info *keys = NULL; + int keycount = 0, keymask = 0; + int key_id; + + /* setup test mask (used to check high bits that signal un-XORed scale would be too high to be valid) */ if (type == 8) { keys = adxkey8_list; keycount = adxkey8_list_count; keymask = 0x6000; } - else if (type == 9) { + else { //if (type == 9) /* smarter XOR as seen in PSO2. The scale is technically 13 bits, * but the maximum value assigned by the encoder is 0x1000. * This is written to the ADX file as 0xFFF, leaving the high bit @@ -379,7 +397,7 @@ static int find_adx_key(STREAMFILE *streamFile, uint8_t type, uint16_t *xor_star keymask = 0x1000; } - /* try all keys until one decrypts correctly vs expected values */ + /* try all keys until one decrypts correctly vs expected scales */ for (key_id = 0; key_id < keycount; key_id++) { uint16_t key_xor, key_mul, key_add; uint16_t xor, mul, add; @@ -400,6 +418,7 @@ static int find_adx_key(STREAMFILE *streamFile, uint8_t type, uint16_t *xor_star VGM_LOG("ADX: incorrectly defined key id=%i\n", key_id); continue; } + /* temp test values */ xor = key_xor; mul = key_mul; @@ -428,30 +447,34 @@ static int find_adx_key(STREAMFILE *streamFile, uint8_t type, uint16_t *xor_star } #endif - - /* test vs prescales while xor looks valid */ - for (i = 0; i < bruteframe && ((prescales[i] & keymask) == (xor & keymask) || prescales[i] == 0); i++) { + /* test vs prescales while XOR looks valid */ + for (i = 0; i < bruteframe_start; i++) { + if ((prescales[i] & keymask) != (xor & keymask) && prescales[i] != 0) + break; xor = xor * mul + add; } - if (i == bruteframe) { - /* test vs scales while xor looks valid */ - for (i = 0; i < scales_to_do && (scales[i] & keymask) == (xor & keymask); i++) { - xor = xor * mul + add; - } - /* key is good */ - if (i == scales_to_do) { - *xor_start = key_xor; - *xor_mult = key_mul; - *xor_add = key_add; + if (i != bruteframe_start) + continue; - rc = 1; - goto find_key_cleanup; - } + /* test vs scales while XOR looks valid */ + for (i = 0; i < bruteframe_count; i++) { + if ((scales[i] & keymask) != (xor & keymask)) + break; + xor = xor * mul + add; } + if (i != bruteframe_count) + continue; + + /* all scales are valid, key is good */ + *xor_start = key_xor; + *xor_mult = key_mul; + *xor_add = key_add; + rc = 1; + break; } } -find_key_cleanup: +done: free(scales); free(prescales); return rc; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/afc.c b/Frameworks/vgmstream/vgmstream/src/meta/afc.c index 54269fff4..94ecc7096 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/afc.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/afc.c @@ -1,67 +1,53 @@ #include "meta.h" #include "../util.h" + +/* .AFC - from Nintendo games [Super Mario Sunshine (GC), The Legend of Zelda: Wind Waker (GC)] */ VGMSTREAM * init_vgmstream_afc(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; + off_t start_offset; + int loop_flag, channel_count; - int loop_flag; - const int channel_count = 2; /* .afc seems to be stereo only */ - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("afc",filename_extension(filename))) goto fail; - - /* don't grab AIFF-C with .afc extension */ - if ((uint32_t)read_32bitBE(0x0,streamFile)==0x464F524D) /* FORM */ + /* checks */ + /* .afc: common + * .stx: Pikmin (GC) */ + if (!check_extensions(streamFile, "afc,stx")) goto fail; - /* we will get a sample rate, that's as close to checking as I think - * we can get */ + if (read_u32be(0x00, streamFile) > get_streamfile_size(streamFile)) /* size without padding */ + goto fail; + + if (read_u16be(0x0a, streamFile) != 4) /* bps? */ + goto fail; + if (read_u16be(0x0c, streamFile) != 16) /* samples per frame? */ + goto fail; + /* 0x0e: always 0x1E? */ + + channel_count = 2; + loop_flag = read_s32be(0x10, streamFile); + start_offset = 0x20; + /* build the VGMSTREAM */ - - loop_flag = read_32bitBE(0x10,streamFile); - vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - vgmstream->num_samples = read_32bitBE(0x04,streamFile); - vgmstream->sample_rate = (uint16_t)read_16bitBE(0x08,streamFile); - /* channels and loop flag are set by allocate_vgmstream */ - vgmstream->loop_start_sample = read_32bitBE(0x14,streamFile); + vgmstream->meta_type = meta_AFC; + vgmstream->num_samples = read_s32be(0x04, streamFile); + vgmstream->sample_rate = read_u16be(0x08, streamFile); + vgmstream->loop_start_sample = read_s32be(0x14, streamFile); vgmstream->loop_end_sample = vgmstream->num_samples; vgmstream->coding_type = coding_NGC_AFC; vgmstream->layout_type = layout_interleave; - vgmstream->meta_type = meta_AFC; - - /* frame-level interleave (9 bytes) */ - vgmstream->interleave_block_size = 9; - - /* open the file for reading by each channel */ - { - STREAMFILE *chstreamfile; - int i; - - /* both channels use same buffer, as interleave is so small */ - chstreamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!chstreamfile) goto fail; - - for (i=0;i<channel_count;i++) { - vgmstream->ch[i].streamfile = chstreamfile; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset= - 0x20 + i*vgmstream->interleave_block_size; - } - } + vgmstream->interleave_block_size = 0x09; + if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) + goto fail; return vgmstream; - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_1snh.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_1snh.c index 2ec1da29b..647496925 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_1snh.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_1snh.c @@ -30,40 +30,62 @@ typedef struct { static int parse_header(STREAMFILE* streamFile, ea_header* ea, off_t begin_offset); static VGMSTREAM * init_vgmstream_main(STREAMFILE *streamFile, ea_header* ea); -static void set_ea_1snh_num_samples(STREAMFILE* streamFile, off_t start_offset, ea_header* ea); +static void set_ea_1snh_num_samples(VGMSTREAM *vgmstream, STREAMFILE *streamFile, ea_header *ea, int find_loop); static int get_ea_1snh_ima_version(STREAMFILE* streamFile, off_t start_offset, const ea_header* ea); /* EA 1SNh - from early EA games, stream (~1996, ex. Need for Speed) */ VGMSTREAM * init_vgmstream_ea_1snh(STREAMFILE *streamFile) { - ea_header ea = {0}; - off_t eacs_offset; + ea_header ea = { 0 }; + off_t offset, eacs_offset; + VGMSTREAM *vgmstream = NULL; /* checks */ /* .asf/as4: common, * .lasf: fake for plugins - * .cnk: some PS games + * .cnk: some PS1 games * .sng: fake for plugins (to mimic EA SCHl's common extension) - * .uv/tgq: some SAT games (video only?) */ - if (!check_extensions(streamFile,"asf,lasf,as4,cnk,sng,uv,tgq")) + * .uv/tgq: some SAT games (video only?) + * .tgv: videos + * (extensionless): Need for Speed (SAT) (videos) */ + if (!check_extensions(streamFile, "asf,lasf,as4,cnk,sng,uv,tgq,tgv,")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x31534E68 && /* "1SNh" */ - read_32bitBE(0x00,streamFile) != 0x53454144) /* "SEAD" */ + offset = 0x00; + + /* in TGV videos, either TGVk or 1SNh block comes first */ + if (read_32bitBE(0x00, streamFile) == 0x5447566B) { /* "TGVk" */ + offset = read_32bitBE(0x04, streamFile); + } else if (read_32bitLE(0x00, streamFile) == 0x5447566B) { /* "kVGT" */ + offset = read_32bitLE(0x04, streamFile); + } + + if (read_32bitBE(offset + 0x00, streamFile) != 0x31534E68 && /* "1SNh" */ + read_32bitBE(offset + 0x00, streamFile) != 0x53454144) /* "SEAD" */ goto fail; /* stream is divided into blocks/chunks: 1SNh=audio header, 1SNd=data xN, 1SNl=loop end, 1SNe=end. - * Video uses various blocks (kVGT/fVGT/etc) and sometimes alt audio blocks (SEAD/SNDC/SEND). */ - ea.is_sead = read_32bitBE(0x00,streamFile) == 0x53454144; + * Video uses various blocks (TGVk/TGVf/MUVf/etc) and sometimes alt audio blocks (SEAD/SNDC/SEND). */ + ea.is_sead = read_32bitBE(offset + 0x00, streamFile) == 0x53454144; - /* use block size as endian marker (Saturn = BE) */ - ea.big_endian = guess_endianness32bit(0x04,streamFile); + eacs_offset = offset + 0x08; /* after 1SNh block id+size */ - eacs_offset = 0x08; /* after 1SNh block id+size */ - - if (!parse_header(streamFile,&ea, eacs_offset)) + if (!parse_header(streamFile, &ea, eacs_offset)) goto fail; - return init_vgmstream_main(streamFile, &ea); + vgmstream = init_vgmstream_main(streamFile, &ea); + if (!vgmstream) goto fail; + + if (ea.num_samples == 0) { + /* header does not specify number of samples, need to calculate it manually */ + /* HACK: we need vgmstream object to use blocked layout so we're doing this calc after creating it */ + set_ea_1snh_num_samples(vgmstream, streamFile, &ea, 0); + + /* update samples and loop state */ + vgmstream->num_samples = ea.num_samples; + vgmstream_force_loop(vgmstream, ea.loop_flag, ea.loop_start, ea.loop_end); + } + + return vgmstream; fail: return NULL; @@ -113,6 +135,8 @@ VGMSTREAM * init_vgmstream_ea_eacs(STREAMFILE *streamFile) { if (ea.total_subsongs == target_subsong) { /* 0x00: some id or flags? */ eacs_offset = read_32bitLE(bank_offset + 0x04, streamFile); + if (read_32bitBE(eacs_offset, streamFile) != 0x45414353) + goto fail; /* rest: not sure if part of this header */ } } @@ -161,13 +185,13 @@ static VGMSTREAM * init_vgmstream_main(STREAMFILE *streamFile, ea_header* ea) { vgmstream->coding_type = coding_ULAW_int; break; - case EA_CODEC_IMA: /* Need for Speed II (PC) */ + case EA_CODEC_IMA: /* Need for Speed (PC) */ if (ea->bits && ea->bits != 2) goto fail; /* only in EACS */ vgmstream->coding_type = coding_DVI_IMA; /* stereo/mono, high nibble first */ vgmstream->codec_config = ea->codec_config; break; - case EA_CODEC_PSX: /* Need for Speed (PS) */ + case EA_CODEC_PSX: /* Need for Speed (PS1) */ vgmstream->coding_type = coding_PSX; break; @@ -176,9 +200,9 @@ static VGMSTREAM * init_vgmstream_main(STREAMFILE *streamFile, ea_header* ea) { goto fail; } - if (!vgmstream_open_stream(vgmstream,streamFile,ea->data_offset)) goto fail; + return vgmstream; fail: @@ -187,10 +211,14 @@ fail: } static int parse_header(STREAMFILE* streamFile, ea_header* ea, off_t offset) { - int32_t (*read_32bit)(off_t,STREAMFILE*) = ea->big_endian ? read_32bitBE : read_32bitLE; + /* audio header endianness doesn't always match block headers, use sample rate to detect */ + int32_t (*read_32bit)(off_t,STREAMFILE*); if (read_32bitBE(offset+0x00, streamFile) == 0x45414353) { /* "EACS" */ /* EACS subheader (PC, SAT) */ + ea->big_endian = guess_endianness32bit(offset + 0x04, streamFile); + read_32bit = ea->big_endian ? read_32bitBE : read_32bitLE; + ea->sample_rate = read_32bit(offset+0x04, streamFile); ea->bits = read_8bit(offset+0x08, streamFile); ea->channels = read_8bit(offset+0x09, streamFile); @@ -208,28 +236,40 @@ static int parse_header(STREAMFILE* streamFile, ea_header* ea, off_t offset) { ea->codec_config = get_ea_1snh_ima_version(streamFile, 0x00, ea); /* EACS banks with empty values exist but will be rejected later */ } + else if (read_32bitBE(offset + 0x00, streamFile) == 0x00) { + /* found in early videos, similar to EACS */ + ea->big_endian = guess_endianness32bit(offset + 0x04, streamFile); + read_32bit = ea->big_endian ? read_32bitBE : read_32bitLE; + + ea->sample_rate = read_32bit(offset + 0x04, streamFile); + ea->bits = read_8bit(offset + 0x08, streamFile); + ea->channels = read_8bit(offset + 0x09, streamFile); + ea->codec = read_8bit(offset + 0x0a, streamFile); + ea->type = read_8bit(offset + 0x0b, streamFile); /* block type? 0=1SNh, -1=bank */ + + if (ea->codec == EA_CODEC_IMA) + ea->codec_config = get_ea_1snh_ima_version(streamFile, 0x00, ea); + } else if (ea->is_sead) { /* alt subheader (found in some PC videos) */ + ea->big_endian = guess_endianness32bit(offset + 0x00, streamFile); + read_32bit = ea->big_endian ? read_32bitBE : read_32bitLE; + ea->sample_rate = read_32bit(offset+0x00, streamFile); ea->channels = read_32bit(offset+0x04, streamFile); ea->codec = read_32bit(offset+0x08, streamFile); if (ea->codec == EA_CODEC_IMA) ea->codec_config = get_ea_1snh_ima_version(streamFile, 0x00, ea); - - set_ea_1snh_num_samples(streamFile, 0x00, ea); - if (ea->loop_start_offset) /* offset found, now find actual start sample */ - set_ea_1snh_num_samples(streamFile, 0x00, ea); } else { - /* alt subheader (PS) */ + /* alt subheader (PS1) */ + ea->big_endian = guess_endianness32bit(offset + 0x00, streamFile); + read_32bit = ea->big_endian ? read_32bitBE : read_32bitLE; + ea->sample_rate = read_32bit(offset+0x00, streamFile); ea->channels = read_8bit(offset+0x18, streamFile); ea->codec = EA_CODEC_PSX; - - set_ea_1snh_num_samples(streamFile, 0x00, ea); - if (ea->loop_start_offset) /* offset found, now find actual start sample */ - set_ea_1snh_num_samples(streamFile, 0x00, ea); } ea->loop_flag = (ea->loop_end > 0); @@ -238,69 +278,48 @@ static int parse_header(STREAMFILE* streamFile, ea_header* ea, off_t offset) { } /* get total samples by parsing block headers, needed when EACS isn't present */ -static void set_ea_1snh_num_samples(STREAMFILE* streamFile, off_t start_offset, ea_header* ea) { - int num_samples = 0, loop_start = 0, loop_end = 0, loop_start_offset = 0; - off_t block_offset = start_offset; - size_t block_size, block_header, block_samples; - int32_t (*read_32bit)(off_t,STREAMFILE*) = ea->big_endian ? read_32bitBE : read_32bitLE; - size_t file_size = get_streamfile_size(streamFile); +static void set_ea_1snh_num_samples(VGMSTREAM *vgmstream, STREAMFILE *streamFile, ea_header *ea, int find_loop) { + int32_t num_samples = 0, block_id; + size_t file_size; + int32_t(*read_32bit)(off_t, STREAMFILE *) = ea->big_endian ? read_32bitBE : read_32bitLE; + int loop_end_found = 0; + file_size = get_streamfile_size(streamFile); + vgmstream->next_block_offset = ea->data_offset; - while (block_offset < file_size) { - uint32_t id = read_32bitBE(block_offset+0x00,streamFile); - block_size = read_32bit(block_offset+0x04,streamFile); - block_header = 0; - block_samples = 0; - - if (id == 0x31534E68 || id == 0x53454144) { /* "1SNh" "SEAD" audio header */ - int is_sead = (id == 0x53454144); - int is_eacs = read_32bitBE(block_offset+0x08, streamFile) == 0x45414353; - - block_header = is_eacs ? 0x28 : (is_sead ? 0x14 : 0x2c); - if (block_header >= block_size) /* sometimes has audio data after header */ - block_header = 0; - } - else if (id == 0x31534E64 || id == 0x534E4443) { /* "1SNd" "SNDC" audio data */ - block_header = 0x08; - } - else if (id == 0x00000000 || id == 0xFFFFFFFF || id == 0x31534E65) { /* EOF or "1SNe" */ + while (vgmstream->next_block_offset < file_size) { + block_update_ea_1snh(vgmstream->next_block_offset, vgmstream); + if (vgmstream->current_block_samples < 0) break; - } - else if (id == 0x31534E6C) { /* "1SNl" loop point found */ - loop_start_offset = read_32bit(block_offset+0x08,streamFile); - loop_end = num_samples; - } - if (block_header) { - switch(ea->codec) { - case EA_CODEC_PSX: - block_samples = ps_bytes_to_samples(block_size - block_header, ea->channels); - break; - case EA_CODEC_IMA: - if (ea->codec_config == 1) - block_samples = read_32bit(block_offset + block_header, streamFile); - else - block_samples = ima_bytes_to_samples(block_size - block_header, ea->channels); - break; + block_id = read_32bitBE(vgmstream->current_block_offset, streamFile); + if (find_loop) { + if (vgmstream->current_block_offset == ea->loop_start_offset) { + ea->loop_start = num_samples; + ea->loop_flag = 1; + block_update_ea_1snh(ea->data_offset, vgmstream); + return; + } + } else { + if (block_id == 0x31534E6C) { /* "1SNl" loop point found */ + ea->loop_start_offset = read_32bit(vgmstream->current_block_offset + 0x08, streamFile); + ea->loop_end = num_samples; + loop_end_found = 1; } } - - /* if there is a loop start offset set, this was called again just to find it */ - if (ea->loop_start_offset && ea->loop_start_offset == block_offset) { - ea->loop_start = num_samples; - return; - } - - num_samples += block_samples; - block_offset += block_size; + num_samples += vgmstream->current_block_samples; } - ea->num_samples = num_samples; - ea->loop_start = loop_start; - ea->loop_end = loop_end; - ea->loop_start_offset = loop_start_offset; + + /* reset once we're done */ + block_update_ea_1snh(ea->data_offset, vgmstream); + + if (loop_end_found) { + /* recurse to find loop start sample */ + set_ea_1snh_num_samples(vgmstream, streamFile, ea, 1); + } } /* find codec version used, with or without ADPCM hist per block */ @@ -311,10 +330,13 @@ static int get_ea_1snh_ima_version(STREAMFILE* streamFile, off_t start_offset, c while (block_offset < file_size) { uint32_t id = read_32bitBE(block_offset+0x00,streamFile); + size_t block_size; - size_t block_size = read_32bitLE(block_offset+0x04,streamFile); - if (block_size > 0x00F00000) /* BE in SAT, but one file may have both BE and LE chunks */ - block_size = read_32bitBE(block_offset+0x04,streamFile); + /* BE in SAT, but one file may have both BE and LE chunks */ + if (guess_endianness32bit(block_offset + 0x04, streamFile)) + block_size = read_32bitBE(block_offset + 0x04, streamFile); + else + block_size = read_32bitLE(block_offset + 0x04, streamFile); if (id == 0x31534E64 || id == 0x534E4443) { /* "1SNd" "SNDC" audio data */ size_t ima_samples = read_32bit(block_offset + 0x08, streamFile); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c index 09b4f5674..33e9c95b1 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c @@ -855,8 +855,8 @@ typedef struct { off_t loop_offset; } eaac_header; -static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *streamData, eaac_header *eaac); -static layered_layout_data* build_layered_eaaudiocore_eaxma(STREAMFILE *streamFile, eaac_header *eaac); +static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *sf_data, eaac_header *eaac); +static layered_layout_data* build_layered_eaaudiocore(STREAMFILE *streamFile, eaac_header *eaac); static size_t calculate_eaac_size(VGMSTREAM *vgmstream, STREAMFILE *streamFile, eaac_header *eaac, off_t start_offset); /* EA newest header from RwAudioCore (RenderWare?) / EAAudioCore library (still generated by sx.exe). @@ -992,7 +992,7 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST vgmstream->layout_type = layout_segmented; } else { - vgmstream->layout_data = build_layered_eaaudiocore_eaxma(streamData, &eaac); + vgmstream->layout_data = build_layered_eaaudiocore(streamData, &eaac); if (!vgmstream->layout_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_layered; @@ -1105,21 +1105,10 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST #ifdef VGM_USE_FFMPEG case EAAC_CODEC_EAOPUS: { /* EAOpus (unknown FourCC) [FIFA 17 (PC), FIFA 19 (Switch)]*/ - int skip = 0; - size_t data_size; - - /* We'll remove EA blocks and pass raw data to FFmpeg Opus decoder */ - - temp_streamFile = setup_eaac_streamfile(streamData, eaac.version, eaac.codec, eaac.streamed,0,0, eaac.stream_offset); - if (!temp_streamFile) goto fail; - - skip = ea_opus_get_encoder_delay(0x00, temp_streamFile); - data_size = get_streamfile_size(temp_streamFile); - - vgmstream->codec_data = init_ffmpeg_ea_opus(temp_streamFile, 0x00,data_size, vgmstream->channels, skip, vgmstream->sample_rate); - if (!vgmstream->codec_data) goto fail; + vgmstream->layout_data = build_layered_eaaudiocore(streamData, &eaac); + if (!vgmstream->layout_data) goto fail; vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; + vgmstream->layout_type = layout_layered; break; } #endif @@ -1203,9 +1192,9 @@ static size_t calculate_eaac_size(VGMSTREAM *vgmstream, STREAMFILE *streamFile, * We use the segmented layout, since the eaac_streamfile doesn't handle padding, * and the segments seem fully separate (so even skipping would probably decode wrong). */ // todo reorganize code for more standard init -static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *streamData, eaac_header *eaac) { +static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *sf_data, eaac_header *eaac) { segmented_layout_data *data = NULL; - STREAMFILE* temp_streamFile = NULL; + STREAMFILE* temp_sf = NULL; off_t offsets[2] = { eaac->stream_offset, eaac->loop_offset }; off_t start_offset; int num_samples[2] = { eaac->loop_start, eaac->num_samples - eaac->loop_start}; @@ -1235,7 +1224,7 @@ static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *st start_offset = 0x00; /* must point to the custom streamfile's beginning */ /* layers inside segments, how trippy */ - data->segments[i]->layout_data = build_layered_eaaudiocore_eaxma(streamData, &temp_eaac); + data->segments[i]->layout_data = build_layered_eaaudiocore(sf_data, &temp_eaac); if (!data->segments[i]->layout_data) goto fail; data->segments[i]->coding_type = coding_FFmpeg; data->segments[i]->layout_type = layout_layered; @@ -1260,10 +1249,10 @@ static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *st start_offset = 0x00; /* must point to the custom streamfile's beginning */ - temp_streamFile = setup_eaac_streamfile(streamData, eaac->version,eaac->codec,eaac->streamed,0,0, offsets[i]); - if (!temp_streamFile) goto fail; + temp_sf = setup_eaac_streamfile(sf_data, eaac->version,eaac->codec,eaac->streamed,0,0, offsets[i]); + if (!temp_sf) goto fail; - data->segments[i]->codec_data = init_mpeg_custom(temp_streamFile, 0x00, &data->segments[i]->coding_type, eaac->channels, type, &cfg); + data->segments[i]->codec_data = init_mpeg_custom(temp_sf, 0x00, &data->segments[i]->coding_type, eaac->channels, type, &cfg); if (!data->segments[i]->codec_data) goto fail; data->segments[i]->layout_type = layout_none; break; @@ -1273,14 +1262,14 @@ static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *st goto fail; } - if (!vgmstream_open_stream(data->segments[i],temp_streamFile == NULL ? streamData : temp_streamFile, start_offset)) + if (!vgmstream_open_stream(data->segments[i],temp_sf == NULL ? sf_data : temp_sf, start_offset)) goto fail; - close_streamfile(temp_streamFile); - temp_streamFile = NULL; + close_streamfile(temp_sf); + temp_sf = NULL; //todo temp_streamFile doesn't contain EAXMA's streamfile - data->segments[i]->stream_size = calculate_eaac_size(data->segments[i], temp_streamFile, eaac, start_offset); + data->segments[i]->stream_size = calculate_eaac_size(data->segments[i], temp_sf, eaac, start_offset); } if (!setup_layout_segmented(data)) @@ -1288,14 +1277,14 @@ static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *st return data; fail: - close_streamfile(temp_streamFile); + close_streamfile(temp_sf); free_layout_segmented(data); return NULL; } -static layered_layout_data* build_layered_eaaudiocore_eaxma(STREAMFILE *streamData, eaac_header *eaac) { +static layered_layout_data* build_layered_eaaudiocore(STREAMFILE *sf_data, eaac_header *eaac) { layered_layout_data* data = NULL; - STREAMFILE* temp_streamFile = NULL; + STREAMFILE* temp_sf = NULL; int i, layers = (eaac->channels+1) / 2; @@ -1303,9 +1292,7 @@ static layered_layout_data* build_layered_eaaudiocore_eaxma(STREAMFILE *streamDa data = init_layout_layered(layers); if (!data) goto fail; - /* open each layer subfile (1/2ch streams: 2ch+2ch..+1ch or 2ch+2ch..+2ch). - * EA-XMA uses completely separate 1/2ch streams, unlike standard XMA that interleaves 1/2ch streams - * with a skip counter to reinterleave (so EA-XMA streams don't have skips set) */ + /* open each layer subfile (1/2ch streams: 2ch+2ch..+1ch or 2ch+2ch..+2ch). */ for (i = 0; i < layers; i++) { int layer_channels = (i+1 == layers && eaac->channels % 2 == 1) ? 1 : 2; /* last layer can be 1/2ch */ @@ -1319,44 +1306,71 @@ static layered_layout_data* build_layered_eaaudiocore_eaxma(STREAMFILE *streamDa data->layers[i]->loop_end_sample = eaac->loop_end; #ifdef VGM_USE_FFMPEG - { - uint8_t buf[0x100]; - int bytes, block_size, block_count; - size_t stream_size; + switch(eaac->codec) { + /* EA-XMA uses completely separate 1/2ch streams, unlike standard XMA that interleaves 1/2ch + * streams with a skip counter to reinterleave (so EA-XMA streams don't have skips set) */ + case EAAC_CODEC_EAXMA: { + uint8_t buf[0x100]; + int bytes, block_size, block_count; + size_t stream_size; - temp_streamFile = setup_eaac_streamfile(streamData, eaac->version, eaac->codec, eaac->streamed,i,layers, eaac->stream_offset); - if (!temp_streamFile) goto fail; + temp_sf = setup_eaac_streamfile(sf_data, eaac->version, eaac->codec, eaac->streamed,i,layers, eaac->stream_offset); + if (!temp_sf) goto fail; - stream_size = get_streamfile_size(temp_streamFile); - block_size = 0x10000; /* unused */ - block_count = stream_size / block_size + (stream_size % block_size ? 1 : 0); + stream_size = get_streamfile_size(temp_sf); + block_size = 0x10000; /* unused */ + block_count = stream_size / block_size + (stream_size % block_size ? 1 : 0); - bytes = ffmpeg_make_riff_xma2(buf, 0x100, data->layers[i]->num_samples, stream_size, data->layers[i]->channels, data->layers[i]->sample_rate, block_count, block_size); - data->layers[i]->codec_data = init_ffmpeg_header_offset(temp_streamFile, buf,bytes, 0x00, stream_size); - if (!data->layers[i]->codec_data) goto fail; + bytes = ffmpeg_make_riff_xma2(buf, 0x100, data->layers[i]->num_samples, stream_size, data->layers[i]->channels, data->layers[i]->sample_rate, block_count, block_size); + data->layers[i]->codec_data = init_ffmpeg_header_offset(temp_sf, buf,bytes, 0x00, stream_size); + if (!data->layers[i]->codec_data) goto fail; - data->layers[i]->coding_type = coding_FFmpeg; - data->layers[i]->layout_type = layout_none; - data->layers[i]->stream_size = get_streamfile_size(temp_streamFile); + data->layers[i]->coding_type = coding_FFmpeg; + data->layers[i]->layout_type = layout_none; + data->layers[i]->stream_size = get_streamfile_size(temp_sf); + + xma_fix_raw_samples(data->layers[i], temp_sf, 0x00,stream_size, 0, 0,0); /* samples are ok? */ + break; + } + + /* Opus can do multichannel just fine, but that wasn't weird enough for EA */ + case EAAC_CODEC_EAOPUS: { + int skip; + size_t data_size; + + /* We'll remove EA blocks and pass raw data to FFmpeg Opus decoder */ + temp_sf = setup_eaac_streamfile(sf_data, eaac->version, eaac->codec, eaac->streamed,i,layers, eaac->stream_offset); + if (!temp_sf) goto fail; + + skip = ea_opus_get_encoder_delay(0x00, temp_sf); + data_size = get_streamfile_size(temp_sf); + + data->layers[i]->codec_data = init_ffmpeg_ea_opus(temp_sf, 0x00,data_size, layer_channels, skip, eaac->sample_rate); + if (!data->layers[i]->codec_data) goto fail; + data->layers[i]->coding_type = coding_FFmpeg; + data->layers[i]->layout_type = layout_none; + + //TODO: 6ch channel layout seems L C R BL BR LFE, not sure about other EAAC + break; + } - xma_fix_raw_samples(data->layers[i], temp_streamFile, 0x00,stream_size, 0, 0,0); /* samples are ok? */ } #else goto fail; #endif - if ( !vgmstream_open_stream(data->layers[i], temp_streamFile, 0x00) ) { + if ( !vgmstream_open_stream(data->layers[i], temp_sf, 0x00) ) { goto fail; } } if (!setup_layout_layered(data)) goto fail; - close_streamfile(temp_streamFile); + close_streamfile(temp_sf); return data; fail: - close_streamfile(temp_streamFile); + close_streamfile(temp_sf); free_layout_layered(data); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac_opus_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac_opus_streamfile.h new file mode 100644 index 000000000..a71b810e4 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac_opus_streamfile.h @@ -0,0 +1,242 @@ +#ifndef _EA_EAAC_OPUS_STREAMFILE_H_ +#define _EA_EAAC_OPUS_STREAMFILE_H_ +#include "../streamfile.h" + +typedef struct deblock_config_t deblock_config_t; +typedef struct deblock_io_data deblock_io_data; + + struct deblock_config_t { + /* config (all optional) */ + size_t logical_size; /* pre-calculated size for performance (otherwise has to read the whole thing) */ + off_t stream_start; /* data start */ + size_t stream_size; /* data max */ + + size_t chunk_size; /* some size like a constant interleave */ + size_t skip_size; /* same */ + + int codec; /* codec or type variations */ + int channels; + int big_endian; + uint32_t config; /* some non-standard config value */ + + /* read=blocks from out stream to read) and "steps" (blocks from other streams to skip) */ + int step_start; /* initial step_count at stream start (often 0) */ + int step_count; /* number of blocks to step over from other streams */ + int read_count; /* number of blocks to read from this stream, after steps */ + + size_t track_size; + int track_number; + int track_count; + size_t interleave_count; + size_t interleave_last_count; + + /* callback that setups deblock_io_data state, normally block_size and data_size */ + void (*block_callback)(STREAMFILE *sf, off_t offset, deblock_io_data *data); +} ; + + +struct deblock_io_data{ + /* initial config */ + deblock_config_t cfg; + + /* state */ + off_t logical_offset; /* fake deblocked offset */ + off_t physical_offset; /* actual file offset */ + off_t block_size; /* current block (added to physical offset) */ + off_t skip_size; /* data to skip from block start to reach data (like a header) */ + off_t data_size; /* usable data in a block (added to logical offset) */ +//todo head/foot? + int step_count; /* number of blocks to step over */ + int read_count; /* number of blocks to read */ + + size_t logical_size; + size_t physical_size; + off_t physical_end; +} ; + + +static void block_callback_default(STREAMFILE *sf, off_t offset, deblock_io_data *data) { + data->block_size = data->cfg.chunk_size; + data->skip_size = data->cfg.skip_size; + data->data_size = data->block_size - data->skip_size; +} + +static size_t deblock_io_read(STREAMFILE *sf, uint8_t *dest, off_t offset, size_t length, deblock_io_data* data) { + size_t total_read = 0; + + + /* re-start when previous offset (can't map logical<>physical offsets) */ + if (data->logical_offset < 0 || offset < data->logical_offset) { + ;VGM_LOG("DEBLOCK: restart offset=%lx + %x, po=%lx, lo=%lx\n", offset, length, data->physical_offset, data->logical_offset); + data->physical_offset = data->cfg.stream_start; + data->logical_offset = 0x00; + data->block_size = 0; + data->data_size = 0; + data->skip_size = 0; + + data->step_count = data->cfg.step_start; + data->read_count = data->cfg.read_count; + } + + /* read blocks */ + while (length > 0) { + + /* ignore EOF */ + if (offset < 0 || + (data->physical_offset >= data->cfg.stream_start + data->physical_size) || + (data->logical_size > 0 && offset > data->logical_size)) { + break; + } + + /* process new block */ + if (data->data_size <= 0) { + data->cfg.block_callback(sf, offset, data); + + if (data->block_size <= 0) { + VGM_LOG("DEBLOCK: block size not set at %lx\n", data->physical_offset); + break; + } + } + +#if 1 + if (data->step_count > 0) { + data->step_count--; + data->physical_offset += data->block_size; + data->data_size = 0; + continue; + } +#else + /* handle blocks from multiple streams */ + { + if (data->step_count > 0) { + data->step_count--; + data->data_size = 0; /* step over this block */ + } + else if (data->read_count) {//must detect when blocks has been read + data->read_count--; /* read this block */ + + /* reset */ + if (data->step_count == 0 && data->read_count == 0) { + data->step_count = data->cfg.step_count; + data->read_count = data->cfg.read_count; + } + } + } +#endif + /* move to next block */ + if (data->data_size == 0 || offset >= data->logical_offset + data->data_size) { + data->physical_offset += data->block_size; + data->logical_offset += data->data_size; + data->data_size = 0; + + data->step_count = data->cfg.step_count; + //VGM_LOG("ignore at %lx + %lx, skips=%i, reads=%i\n", data->physical_offset, data->block_size, data->step_count, data->read_count); + continue; + } + + //VGM_LOG("accept at %lx + %lx, skips=%i, reads=%i\n", data->physical_offset, data->block_size, data->step_count, data->read_count); + + /* read block data */ + { + size_t bytes_consumed, bytes_done, to_read; + + bytes_consumed = offset - data->logical_offset; + to_read = data->data_size - bytes_consumed; + if (to_read > length) + to_read = length; + bytes_done = read_streamfile(dest, data->physical_offset + data->skip_size + bytes_consumed, to_read, sf); + + total_read += bytes_done; + dest += bytes_done; + offset += bytes_done; + length -= bytes_done; + + if (bytes_done != to_read || bytes_done == 0) { + break; /* error/EOF */ + } + } + } + + return total_read; +} + +static size_t deblock_io_size(STREAMFILE *streamfile, deblock_io_data* data) { + uint8_t buf[0x04]; + + if (data->logical_size) + return data->logical_size; + + if (data->cfg.logical_size) { + data->logical_size = data->cfg.logical_size; + return data->logical_size; + } + + /* force a fake read at max offset, to get max logical_offset (will be reset next read) */ + deblock_io_read(streamfile, buf, 0x7FFFFFFF, 1, data); + data->logical_size = data->logical_offset; + return data->logical_size; +} + +/* generic "de-blocker" helper for streams divided in blocks that have weird interleaves, their + * decoder can't easily use blocked layout, or some other weird feature. Must pass a + * deblock_config_t with setup and a callback that sets sizes of a single block. */ +static STREAMFILE* open_io_deblocker_streamfile_f(STREAMFILE *sf, deblock_config_t *cfg) { + STREAMFILE *new_sf = NULL; + deblock_io_data io_data = {0}; + + /* prepare data */ + io_data.cfg = *cfg; /* memcpy */ + + if (io_data.cfg.block_callback == NULL) + io_data.cfg.block_callback = block_callback_default; + + if (io_data.cfg.stream_start < 0) + goto fail; + if (io_data.cfg.step_start < 0 || io_data.cfg.step_count < 0) + goto fail; + + if (io_data.cfg.read_count == 0) + io_data.cfg.read_count = 1; + + io_data.physical_size = io_data.cfg.stream_size; + if (io_data.physical_size > get_streamfile_size(sf) + io_data.cfg.stream_start || io_data.physical_size == 0) + io_data.physical_size = get_streamfile_size(sf) - io_data.cfg.stream_start; + io_data.physical_end = io_data.cfg.stream_start + io_data.physical_size; +VGM_LOG("ps=%x, pe=%lx\n", io_data.physical_size, io_data.physical_end); + io_data.logical_offset = -1; /* read reset */ + + //TODO: other validations + + /* setup subfile */ + new_sf = open_io_streamfile_f(sf, &io_data, sizeof(deblock_io_data), deblock_io_read, deblock_io_size); + return new_sf; +fail: + VGM_LOG("DEBLOCK: bad init\n"); + close_streamfile(sf); + return NULL; +} + +/*****************************************************/ + +static void block_callback(STREAMFILE *sf, off_t offset, deblock_io_data *data) { + /* read the whole block, will be skipped for unwanted sub-streams */ + data->block_size = 0x02 + read_u16be(data->physical_offset, sf); + data->data_size = data->block_size; + //VGM_LOG("read at %lx + %lx, skips=%i, reads=%i\n", data->physical_offset, data->block_size, data->step_count, data->read_count); +} + +static STREAMFILE* open_io_eaac_opus_streamfile_f(STREAMFILE *new_sf, int stream_number, int stream_count) { + deblock_config_t cfg = {0}; + + cfg.step_start = stream_number; + cfg.step_count = stream_count - 1; + cfg.block_callback = block_callback; + /* starts from 0 since new_sf is pre-deblocked */ + + /* setup subfile */ + //new_sf = open_wrap_streamfile(sf); /* to be used with others */ + new_sf = open_io_deblocker_streamfile_f(new_sf, &cfg); + return new_sf; +} + +#endif /* _EA_EAAC_OPUS_STREAMFILE_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac_streamfile.h index e74d1ee1c..160517f50 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac_streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac_streamfile.h @@ -1,6 +1,7 @@ #ifndef _EA_EAAC_STREAMFILE_H_ #define _EA_EAAC_STREAMFILE_H_ #include "../streamfile.h" +#include "ea_eaac_opus_streamfile.h" #define XMA_FRAME_SIZE 0x800 @@ -40,7 +41,7 @@ static size_t eaac_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, /* previous offset: re-start as we can't map logical<>physical offsets * (kinda slow as it trashes buffers, but shouldn't happen often) */ if (offset < data->logical_offset) { - //;VGM_LOG("IO restart: offset=%lx + %x, po=%lx, lo=%lx\n", offset, length, data->physical_offset, data->logical_offset); + ;VGM_LOG("EAAC IO: restart offset=%lx + %x, po=%lx, lo=%lx\n", offset, length, data->physical_offset, data->logical_offset); data->physical_offset = data->stream_offset; data->logical_offset = 0x00; data->data_size = 0; @@ -260,8 +261,9 @@ static STREAMFILE* setup_eaac_streamfile(STREAMFILE *sf, int version, int codec, /* setup subfile */ new_sf = open_wrap_streamfile(sf); new_sf = open_io_streamfile_f(new_sf, &io_data, sizeof(eaac_io_data), eaac_io_read, eaac_io_size); - if (codec == 0x03) /* EA-XMA only since logical data is bigger */ - new_sf = open_buffer_streamfile_f(new_sf, 0); + new_sf = open_buffer_streamfile_f(new_sf, 0); /* EA-XMA and multichannel EALayer3 benefit from this */ + if (codec == 0x0c && stream_count > 1) /* multichannel opus */ + new_sf = open_io_eaac_opus_streamfile_f(new_sf, stream_number, stream_count); return new_sf; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c index 792a0d68f..d54c54e2b 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c @@ -173,13 +173,25 @@ VGMSTREAM * init_vgmstream_ea_schl_video(STREAMFILE *streamFile) { /* check extension */ - /* .vp6: ~late */ - if (!check_extensions(streamFile,"vp6")) - goto fail; - - /* check initial movie block id */ - if (read_32bitBE(0x00,streamFile) != 0x4D566864) /* "MVhd" */ + /* .uv: early */ + /* .dct: early-mid [ex. Need for Speed II SE (PC), FIFA 98 (PC)] */ + /* .mad: mid */ + /* .vp6: late */ + if (check_extensions(streamFile, "vp6")) { + /* check initial movie block id */ + if (read_32bitBE(0x00, streamFile) != 0x4D566864) /* "MVhd" */ + goto fail; + } else if (check_extensions(streamFile, "uv,dct")) { + /* starts with audio header block */ + if (read_32bitBE(0x00, streamFile) != EA_BLOCKID_HEADER) /* "SCHl" */ + goto fail; + } else if (check_extensions(streamFile, "mad")) { + /* check initial movie block id */ + if (read_32bitBE(0x00, streamFile) != 0x4D41446B) /* "MADk" */ + goto fail; + } else { goto fail; + } /* use block size to check endianness */ if (guess_endianness32bit(0x04, streamFile)) { @@ -208,7 +220,7 @@ VGMSTREAM * init_vgmstream_ea_schl_video(STREAMFILE *streamFile) { offset += block_size; } - if (start_offset == 0) + if (offset >= get_streamfile_size(streamFile)) goto fail; /* find target subsong (one per each SHxx multilang block) */ @@ -469,6 +481,10 @@ VGMSTREAM * init_vgmstream_ea_hdr_dat(STREAMFILE *streamFile) { STREAMFILE *datFile = NULL; VGMSTREAM *vgmstream; + /* checks */ + if (!check_extensions(streamFile, "hdr")) + goto fail; + /* main header is machine endian but it's not important here */ /* 0x00: ID */ /* 0x02: sub-ID (used for different police voices in NFS games) */ @@ -540,6 +556,10 @@ VGMSTREAM * init_vgmstream_ea_hdr_dat_v2(STREAMFILE *streamFile) { STREAMFILE *datFile = NULL; VGMSTREAM *vgmstream; + /* checks */ + if (!check_extensions(streamFile, "hdr")) + goto fail; + /* main header is machine endian but it's not important here */ /* 0x00: ID */ /* 0x02: userdata size */ @@ -603,12 +623,22 @@ fail: /* open map/mpf+mus pairs that aren't exact pairs, since EA's games can load any combo */ -static STREAMFILE * open_mapfile_pair(STREAMFILE *streamFile) { +static STREAMFILE* open_mapfile_pair(STREAMFILE *streamFile) { static const char *const mapfile_pairs[][2] = { /* standard cases, replace map part with mus part (from the end to preserve prefixes) */ {"MUS_CTRL.MPF","MUS_STR.MUS"}, /* GoldenEye - Rogue Agent (PS2) */ {"mus_ctrl.mpf","mus_str.mus"}, /* GoldenEye - Rogue Agent (others) */ {".mpf","_main.mus"}, /* 007 - Everything or Nothing (GC) */ + {"AKA_Mus.mpf","Track.mus"}, /* Boogie (PS2) */ + //TODO: improve pairs (needs better wildcard support) + //NSF2: + /* ZTRxxROK.MAP > ZTRxx.TRJ */ + /* ZTRxxTEC.MAP > ZTRxx.TRM */ + /* ZZSHOW.MAP and ZZSHOW2.MAP > ZZSHOW.MUS */ + //NSF3: + /* ZTRxxROK.MAP > ZZZTRxxA.TRJ */ + /* ZTRxxTEC.MAP > ZZZTRxxB.TRM */ + /* other extra files that may need the hack below */ /* hack when when multiple maps point to the same mus, uses name before "+" * ex. ZZZTR00A.TRJ+ZTR00PGR.MAP or ZZZTR00A.TRJ+ZTR00R0A.MAP both point to ZZZTR00A.TRJ */ {"+",""}, /* Need for Speed III (PS1) */ @@ -1645,7 +1675,7 @@ static void update_ea_stream_size_and_samples(STREAMFILE* streamFile, off_t star } /* manually read totals */ - block_update(start_offset, vgmstream); + vgmstream->next_block_offset = start_offset; while (vgmstream->next_block_offset < file_size) { block_update_ea_schl(vgmstream->next_block_offset, vgmstream); if (vgmstream->current_block_samples < 0) @@ -1673,7 +1703,7 @@ static void update_ea_stream_size_and_samples(STREAMFILE* streamFile, off_t star } /* reset once we're done */ - block_update(start_offset, vgmstream); + block_update_ea_schl(start_offset, vgmstream); /* only use calculated samples with multiple subfiles (rarely header samples may be less due to padding) */ if (standalone && multiple_schl) { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c b/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c index f8363b0bd..1c6a356ff 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c @@ -25,7 +25,7 @@ VGMSTREAM * init_vgmstream_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, // goto fail; /* don't try to open headers and other mini files */ - if (get_streamfile_size(streamFile) <= 0x100) + if (get_streamfile_size(streamFile) <= 0x1000) goto fail; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h index ba97274f4..420594c94 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h @@ -300,6 +300,9 @@ static const hcakey_info hcakey_list[] = { /* Love Live! School idol festival ALL STARS (Android) */ {6498535309877346413}, // 5A2F6F6F0192806D + /* BLACKSTAR -Theater Starless- (Android) */ + {121837007188}, // 0000001C5E0D3154 + /* Dragalia Lost (Cygames) [iOS/Android] */ {2967411924141, subkeys_dgl, sizeof(subkeys_dgl) / sizeof(subkeys_dgl[0]) }, // 000002B2E7889CAD diff --git a/Frameworks/vgmstream/vgmstream/src/meta/isb.c b/Frameworks/vgmstream/vgmstream/src/meta/isb.c new file mode 100644 index 000000000..bef078c4e --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/isb.c @@ -0,0 +1,191 @@ +#include "meta.h" +#include "../coding/coding.h" + + +/* .ISB - Creative ISACT (Interactive Spatial Audio Composition Tools) middleware [Psychonauts (PC)] */ +VGMSTREAM * init_vgmstream_isb(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset = 0, name_offset = 0; + size_t stream_size = 0, name_size = 0; + int loop_flag = 0, channel_count = 0, sample_rate = 0, codec = 0, pcm_bytes = 0, bps = 0; + int total_subsongs, target_subsong = streamFile->stream_index; + + + /* checks */ + if (!check_extensions(streamFile, "isb")) + goto fail; + + if (read_32bitBE(0x00,streamFile) != 0x52494646) /* "RIFF" */ + goto fail; + if (read_32bitLE(0x04,streamFile) + 0x08 != get_streamfile_size(streamFile)) + goto fail; + if (read_32bitBE(0x08,streamFile) != 0x69736266) /* "isbf" */ + goto fail; + + /* some files have a companion .icb, seems to be a cue file pointing here */ + + /* format is RIFF with many custom chunks, apparently for their DAW-like editor with + * complex functions, but most seem always included by default and unused, and games + * Psychonauts seems to use the format as a simple audio bank. Mass Effect (X360) + * apparently uses ISACT, while Psychonauts Xbox/PS2 don't. */ + + { + off_t offset, max_offset, header_offset = 0; + size_t header_size = 0; + + total_subsongs = 0; /* not specified */ + if (target_subsong == 0) target_subsong = 1; + + /* parse base RIFF */ + offset = 0x0c; + max_offset = get_streamfile_size(streamFile); + while (offset < max_offset) { + uint32_t chunk_type = read_u32be(offset + 0x00,streamFile); + uint32_t chunk_size = read_s32le(offset + 0x04,streamFile); + offset += 0x08; + + switch(chunk_type) { + case 0x4C495354: /* "LIST" */ + if (read_u32be(offset, streamFile) != 0x73616D70) /* "samp" */ + break; /* there are "bfob" LIST without data */ + + total_subsongs++; + if (target_subsong == total_subsongs && header_offset == 0) { + header_offset = offset; + header_size = chunk_size; + } + break; + + default: /* most are common chunks at the start that seem to contain defaults */ + break; + } + + //if (offset + chunk_size+0x01 <= max_offset && chunk_size % 0x02) + // chunk_size += 0x01; + offset += chunk_size; + } + + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + if (header_offset == 0) goto fail; + + /* parse header inside LIST */ + offset = header_offset + 0x04; + max_offset = offset + header_size; + while (offset < max_offset) { + uint32_t chunk_type = read_u32be(offset + 0x00,streamFile); + uint32_t chunk_size = read_s32le(offset + 0x04,streamFile); + offset += 0x08; + + switch(chunk_type) { + case 0x7469746C: /* "titl" */ + name_offset = offset; + name_size = chunk_size; + break; + + case 0x63686E6B: /* "chnk" */ + channel_count = read_u32le(offset + 0x00, streamFile); + break; + + case 0x73696E66: /* "sinf" */ + /* 0x00: null? */ + /* 0x04: some value? */ + sample_rate = read_u32le(offset + 0x08, streamFile); + pcm_bytes = read_u32le(offset + 0x0c, streamFile); + bps = read_u16le(offset + 0x10, streamFile); + /* 0x12: some value? */ + break; + + case 0x636D7069: /* "cmpi" */ + codec = read_u32le(offset + 0x00, streamFile); + if (read_u32le(offset + 0x04, streamFile) != codec) { + VGM_LOG("ISB: unknown compression repeat\n"); + goto fail; + } + /* 0x08: extra value for some codecs? */ + /* 0x0c: block size when codec is XBOX-IMA */ + /* 0x10: null? */ + /* 0x14: flags? */ + break; + + case 0x64617461: /* "data" */ + start_offset = offset; + stream_size = chunk_size; + break; + + default: /* most of the same default chunks */ + break; + } + + //if (offset + chunk_size+0x01 <= max_offset && chunk_size % 0x02) + // chunk_size += 0x01; + offset += chunk_size; + } + + if (start_offset == 0) + goto fail; + } + + + /* some files are marked */ + loop_flag = 0; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_WAF; //todo + vgmstream->sample_rate = sample_rate; + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; + + switch(codec) { + case 0x00: + if (bps == 8) { + vgmstream->coding_type = coding_PCM8_U; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x01; + } + else { + vgmstream->coding_type = coding_PCM16LE; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x02; + } + vgmstream->num_samples = pcm_bytes_to_samples(stream_size, channel_count, bps); /* unsure about pcm_bytes */ + break; + + case 0x01: + vgmstream->coding_type = coding_XBOX_IMA; + vgmstream->layout_type = layout_none; + vgmstream->num_samples = xbox_ima_bytes_to_samples(stream_size, channel_count); /* pcm_bytes has excess data */ + break; + +#ifdef VGM_USE_VORBIS + case 0x02: + vgmstream->codec_data = init_ogg_vorbis(streamFile, start_offset, stream_size, NULL); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_OGG_VORBIS; + vgmstream->layout_type = layout_none; + vgmstream->num_samples = pcm_bytes / channel_count / (bps/8); + break; +#endif + + default: /* according to press releases ISACT may support WMA and XMA */ + VGM_LOG("ISB: unknown codec %i\n", codec); + goto fail; + } + + if (name_offset) { /* UTF16 but only uses lower bytes */ + if (name_size > STREAM_NAME_SIZE) + name_size = STREAM_NAME_SIZE; + read_string_utf16le(vgmstream->stream_name,name_size, name_offset, streamFile); + } + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/lsf.c b/Frameworks/vgmstream/vgmstream/src/meta/lsf.c index 0f7ef2e32..94f7741d9 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/lsf.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/lsf.c @@ -1,59 +1,47 @@ #include "meta.h" #include "../util.h" -/* .lsf - Fastlane Street Racing (iPhone) */ -/* "!n1nj4n" */ - +/* .lsf - from Atod games [Fastlane Street Racing (iPhone), Chicane Street Racing prototype (Gizmondo)] */ VGMSTREAM * init_vgmstream_lsf_n1nj4n(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - - size_t file_size; off_t start_offset; + int loop_flag, channel_count; + size_t file_size; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("lsf",filename_extension(filename))) goto fail; - /* check header */ - if (read_32bitBE(0x0, streamFile) != 0x216E316E || // "!n1n" - read_32bitBE(0x4, streamFile) != 0x6A346E00) // "j4n\0" + /* checks */ + if (!check_extensions(streamFile, "lsf")) + goto fail; + + if (read_32bitBE(0x00, streamFile) != 0x216E316E || // "!n1n" + read_32bitBE(0x04, streamFile) != 0x6A346E00) // "j4n\0" goto fail; - /* check size */ file_size = get_streamfile_size(streamFile); - if (read_32bitLE(0xC, streamFile) + 0x10 != file_size) + if (read_32bitLE(0x0C, streamFile) + 0x10 != file_size) goto fail; + loop_flag = 0; + channel_count = 1; start_offset = 0x10; + /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(1,0); + vgmstream = allocate_vgmstream(channel_count, loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - vgmstream->num_samples = (file_size-0x10)/0x1c*0x1b*2; - vgmstream->sample_rate = read_32bitLE(0x8, streamFile); - - vgmstream->coding_type = coding_LSF; - vgmstream->layout_type = layout_none; vgmstream->meta_type = meta_LSF_N1NJ4N; + vgmstream->sample_rate = read_32bitLE(0x08, streamFile); + vgmstream->num_samples = (file_size-0x10)/0x1c*0x1b*2; + vgmstream->coding_type = coding_LSF; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x1c; - /* open the file for reading */ - { - vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - - if (!vgmstream->ch[0].streamfile) goto fail; - - vgmstream->ch[0].channel_start_offset= - vgmstream->ch[0].offset=start_offset; - } - + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; return vgmstream; - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } - diff --git a/Frameworks/vgmstream/vgmstream/src/meta/meta.h b/Frameworks/vgmstream/vgmstream/src/meta/meta.h index 87b24c54b..81aae0fe3 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/meta.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/meta.h @@ -87,6 +87,7 @@ VGMSTREAM * init_vgmstream_ps2_mic(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_raw_pcm(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_vag(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_vag_aaap(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_seb(STREAMFILE *streamFile); @@ -427,8 +428,6 @@ VGMSTREAM * init_vgmstream_pona_psx(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_xbox_hlwav(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_stx(STREAMFILE* streamFile); - VGMSTREAM * init_vgmstream_myspd(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_his(STREAMFILE* streamFile); @@ -869,4 +868,8 @@ VGMSTREAM * init_vgmstream_ubi_hx(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_bmp_konami(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_isb(STREAMFILE * streamFile); + +VGMSTREAM* init_vgmstream_xssb(STREAMFILE *sf); + #endif /*_META_H*/ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/mta2.c b/Frameworks/vgmstream/vgmstream/src/meta/mta2.c index 48023d375..a1ca1f3cd 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/mta2.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/mta2.c @@ -8,7 +8,6 @@ VGMSTREAM * init_vgmstream_mta2(STREAMFILE *streamFile) { off_t start_offset; int loop_flag, channel_count, sample_rate; int32_t loop_start, loop_end; - uint32_t sample_rate_int; /* checks */ @@ -47,13 +46,9 @@ VGMSTREAM * init_vgmstream_mta2(STREAMFILE *streamFile) { } #endif - sample_rate_int = read_32bitBE(0x7c, streamFile); - if (sample_rate_int) { /* sample rate in 32b float (WHY?) typically 48000.0 */ - float* sample_float = (float*)&sample_rate_int; - sample_rate = (int)*sample_float; - } else { /* default when not specified (most of the time) */ - sample_rate = 48000; - } + sample_rate = (int)read_f32be(0x7c, streamFile); /* sample rate in 32b float (WHY?) typically 48000.0 */ + if (sample_rate == 0) + sample_rate = 48000; /* default when not specified (most of the time) */ /* TRKP chunks (x16) */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/mta2_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/mta2_streamfile.h index c38d20b59..c8d7d28ba 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/mta2_streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/mta2_streamfile.h @@ -20,14 +20,14 @@ typedef struct { } mta2_io_data; -static size_t mta2_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, mta2_io_data* data) { +static size_t mta2_io_read(STREAMFILE *sf, uint8_t *dest, off_t offset, size_t length, mta2_io_data* data) { size_t total_read = 0; uint32_t (*read_u32)(off_t,STREAMFILE*) = data->big_endian ? read_u32be : read_u32le; - /* re-start when previous offset (can't map logical<>physical offsets) */ if (data->logical_offset < 0 || offset < data->logical_offset) { + ;VGM_LOG("IO restart: offset=%lx + %x, po=%lx, lo=%lx\n", offset, length, data->physical_offset, data->logical_offset); data->physical_offset = data->stream_offset; data->logical_offset = 0x00; data->data_size = 0; @@ -45,10 +45,10 @@ static size_t mta2_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, if (data->data_size == 0) { uint32_t block_type, block_size, block_track; - block_type = read_u32(data->physical_offset+0x00, streamfile); /* subtype and type */ - block_size = read_u32(data->physical_offset+0x04, streamfile); + block_type = read_u32(data->physical_offset+0x00, sf); /* subtype and type */ + block_size = read_u32(data->physical_offset+0x04, sf); //block_unk = read_u32(data->physical_offset+0x08, streamfile); /* usually 0 except for 0xF0 'end' block */ - block_track = read_u32(data->physical_offset+0x0c, streamfile); + block_track = read_u32(data->physical_offset+0x0c, sf); if (block_type != data->target_type || block_size == 0xFFFFFFFF) break; @@ -77,7 +77,7 @@ static size_t mta2_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, to_read = data->data_size - bytes_consumed; if (to_read > length) to_read = length; - bytes_done = read_streamfile(dest, data->physical_offset + data->skip_size + bytes_consumed, to_read, streamfile); + bytes_done = read_streamfile(dest, data->physical_offset + data->skip_size + bytes_consumed, to_read, sf); total_read += bytes_done; dest += bytes_done; @@ -96,7 +96,7 @@ static size_t mta2_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, static size_t mta2_io_size(STREAMFILE *streamfile, mta2_io_data* data) { uint8_t buf[1]; - if (data->logical_size) + if (data->logical_size > 0) return data->logical_size; /* force a fake read at max offset, to get max logical_offset (will be reset next read) */ @@ -108,47 +108,28 @@ static size_t mta2_io_size(STREAMFILE *streamfile, mta2_io_data* data) { /* Handles removing KCE Japan-style blocks in MTA2 streams * (these blocks exist in most KCEJ games and aren't actually related to audio) */ -static STREAMFILE* setup_mta2_streamfile(STREAMFILE *streamFile, off_t stream_offset, int big_endian, const char* extension) { - STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; +static STREAMFILE* setup_mta2_streamfile(STREAMFILE *sf, off_t stream_offset, int big_endian, const char *extension) { + STREAMFILE *new_sf = NULL; mta2_io_data io_data = {0}; - size_t io_data_size = sizeof(mta2_io_data); uint32_t (*read_u32)(off_t,STREAMFILE*) = big_endian ? read_u32be : read_u32le; /* blocks must start with a 'new sub-stream' id */ - if (read_u32(stream_offset+0x00, streamFile) != 0x00000010) - goto fail; + if (read_u32(stream_offset+0x00, sf) != 0x00000010) + return NULL; - io_data.target_type = read_u32(stream_offset + 0x0c, streamFile); + io_data.target_type = read_u32(stream_offset + 0x0c, sf); io_data.stream_offset = stream_offset + 0x10; - io_data.stream_size = get_streamfile_size(streamFile) - io_data.stream_offset; + io_data.stream_size = get_streamfile_size(sf) - io_data.stream_offset; io_data.big_endian = big_endian; io_data.logical_offset = -1; /* force phys offset reset */ /* setup subfile */ - new_streamFile = open_wrap_streamfile(streamFile); - if (!new_streamFile) goto fail; - temp_streamFile = new_streamFile; - - new_streamFile = open_io_streamfile(new_streamFile, &io_data,io_data_size, mta2_io_read,mta2_io_size); - if (!new_streamFile) goto fail; - temp_streamFile = new_streamFile; - - new_streamFile = open_buffer_streamfile(new_streamFile,0); - if (!new_streamFile) goto fail; - temp_streamFile = new_streamFile; - - if (extension) { - new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,extension); - if (!new_streamFile) goto fail; - temp_streamFile = new_streamFile; - } - - return temp_streamFile; - -fail: - close_streamfile(temp_streamFile); - return NULL; + new_sf = open_wrap_streamfile(sf); + new_sf = open_io_streamfile_f(new_sf, &io_data, sizeof(mta2_io_data), mta2_io_read, mta2_io_size); + if (extension) + new_sf = open_fakename_streamfile_f(new_sf, NULL, extension); + return new_sf; } #endif /* _MTA2_STREAMFILE_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/nds_sad.c b/Frameworks/vgmstream/vgmstream/src/meta/nds_sad.c index af3c72621..944cf04b0 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/nds_sad.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/nds_sad.c @@ -1,54 +1,32 @@ #include "meta.h" #include "../util.h" -/* sadl (only the Professor Layton interleaved IMA version) */ +/* sadl - from DS games with Procyon Studio audio driver */ VGMSTREAM * init_vgmstream_sadl(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; off_t start_offset; + int loop_flag, channel_count; - int loop_flag; - int channel_count; - int coding_type; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("sad",filename_extension(filename))) goto fail; + /* checks */ + if (!check_extensions(streamFile, "sad")) + goto fail; - /* check header */ if (read_32bitBE(0x00,streamFile) != 0x7361646c) /* "sadl" */ goto fail; - - /* check file size */ - if (read_32bitLE(0x40,streamFile) != get_streamfile_size(streamFile) ) + if (read_32bitLE(0x40,streamFile) != get_streamfile_size(streamFile)) goto fail; - /* check coding type */ - switch (read_8bit(0x33,streamFile)&0xf0) - { - case 0x70: - coding_type = coding_IMA_int; - break; - case 0xb0: - coding_type = coding_NDS_PROCYON; - break; - default: - goto fail; - } loop_flag = read_8bit(0x31,streamFile); channel_count = read_8bit(0x32,streamFile); + start_offset = 0x100; - /* build the VGMSTREAM */ + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - start_offset = 0x100; - vgmstream->channels = channel_count; - - switch (read_8bit(0x33,streamFile) & 6) - { + switch (read_8bit(0x33,streamFile) & 6) { case 4: vgmstream->sample_rate = 32728; break; @@ -59,52 +37,37 @@ VGMSTREAM * init_vgmstream_sadl(STREAMFILE *streamFile) { goto fail; } - vgmstream->coding_type = coding_type; - - if (coding_type == coding_IMA_int) - vgmstream->num_samples = - (read_32bitLE(0x40,streamFile)-start_offset)/channel_count*2; - else if (coding_type == coding_NDS_PROCYON) - vgmstream->num_samples = - (read_32bitLE(0x40,streamFile)-start_offset)/channel_count/16*30; - - vgmstream->interleave_block_size=0x10; - - if (loop_flag) - { - if (coding_type == coding_IMA_int) - vgmstream->loop_start_sample = (read_32bitLE(0x54,streamFile)-start_offset)/channel_count*2; - else - vgmstream->loop_start_sample = (read_32bitLE(0x54,streamFile)-start_offset)/channel_count/16*30; - vgmstream->loop_end_sample = vgmstream->num_samples; - } - - if (channel_count > 1) - vgmstream->layout_type = layout_interleave; - else - vgmstream->layout_type = layout_none; vgmstream->meta_type = meta_SADL; - /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;i<channel_count;i++) { - vgmstream->ch[i].streamfile = file; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x10; - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+ - vgmstream->interleave_block_size*i; + switch(read_8bit(0x33,streamFile) & 0xf0) { + case 0x70: /* Ni no Kuni (DS), Professor Layton and the Curious Village (DS), Soma Bringer (DS) */ + vgmstream->coding_type = coding_IMA_int; - } + vgmstream->num_samples = (read_32bitLE(0x40,streamFile)-start_offset)/channel_count*2; + vgmstream->loop_start_sample = (read_32bitLE(0x54,streamFile)-start_offset)/channel_count*2; + vgmstream->loop_end_sample = vgmstream->num_samples; + break; + + case 0xb0: /* Soma Bringer (DS), Rekishi Taisen Gettenka (DS) */ + vgmstream->coding_type = coding_NDS_PROCYON; + + vgmstream->num_samples = (read_32bitLE(0x40,streamFile)-start_offset)/channel_count/16*30; + vgmstream->loop_start_sample = (read_32bitLE(0x54,streamFile)-start_offset)/channel_count/16*30; + vgmstream->loop_end_sample = vgmstream->num_samples; + break; + + default: + goto fail; } + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; return vgmstream; - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_2pfs.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_2pfs.c index 24df33351..087d2d1a5 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_2pfs.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_2pfs.c @@ -1,56 +1,42 @@ #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" -/* 2PFS (Konami) - - Mahoromatic: Moetto - KiraKira Maid-San (PS2) [.2pfs (V1, 2003)] - - GANTZ The Game (PS2) [.sap (V2, 2005)] - There are two versions of the format, though they use different extensions. - Implemented both versions here in case there are .2pfs with the V2 header out there. - Both loop correctly AFAIK (there is a truncated Mahoromatic rip around, beware). -*/ -VGMSTREAM * init_vgmstream_ps2_2pfs(STREAMFILE *streamFile) -{ +/* 2PFS - from Konami Games [Mahoromatic: Moetto - KiraKira Maid-San (PS2), GANTZ The Game (PS2)] */ +VGMSTREAM * init_vgmstream_ps2_2pfs(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - - off_t start_offset = 0x800; - int interleave = 0x1000; - - int loop_flag; - int channel_count; - int version; /* v1=1, v2=2 */ - - int loop_start_block; /* block number where the loop starts */ - int loop_end_block; /* usually the last block */ - int loop_start_sample_adjust; /* loops start/end a few samples into the start/end block */ - int loop_end_sample_adjust; + off_t start_offset; + int loop_flag, channel_count, version, interleave; + int loop_start_block, loop_end_block; /* block number */ + int loop_start_adjust, loop_end_adjust; /* loops start/end a few samples into the start/end block */ - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if ( strcasecmp("2pfs",filename_extension(filename)) - && strcasecmp("sap",filename_extension(filename)) ) + /* checks */ + /* .sap: standard + * .2psf: header id? (Mahoromatic) */ + if (!check_extensions(streamFile, "sap,2psf")) goto fail; - /* check header ("2PFS") */ - if (read_32bitBE(0x00,streamFile) != 0x32504653) + if (read_32bitBE(0x00,streamFile) != 0x32504653) /* "2PFS" */ goto fail; version = read_16bitLE(0x04,streamFile); - if ( version!=0x01 && version!=0x02 ) + if (version != 0x01 && version != 0x02) /* v1: Mahoromatic, v2: Gantz */ goto fail; channel_count = read_8bit(0x40,streamFile); loop_flag = read_8bit(0x41,streamFile); + start_offset = 0x800; + interleave = 0x1000; + /* other header values - * 0x06 (4): unknown, v1=0x0004 v2=0x0001 - * 0x08 (32): unique file id - * 0x0c (32): base header size (v1=0x50, v2=0x60) + datasize (without the 0x800 full header size) + * 0x06: unknown, v1=0x0004 v2=0x0001 + * 0x08: unique file id + * 0x0c: base header size (v1=0x50, v2=0x60) + datasize (without the 0x800 full header size) * 0x10-0x30: unknown (v1 differs from v2) * 0x38-0x40: unknown (v1 same as v2) - * 0x4c (32) in V2: unknown, some kind of total samples? + * 0x4c: unknown, some kind of total samples? (v2 only) */ @@ -58,59 +44,40 @@ VGMSTREAM * init_vgmstream_ps2_2pfs(STREAMFILE *streamFile) vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - vgmstream->channels = channel_count; - vgmstream->coding_type = coding_PSX; + vgmstream->meta_type = meta_PS2_2PFS; vgmstream->num_samples = read_32bitLE(0x34,streamFile) * 28 / 16 / channel_count; + vgmstream->coding_type = coding_PSX; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = interleave; - vgmstream->meta_type = meta_PS2_2PFS; - if ( version==0x01 ) { + if (version == 0x01) { vgmstream->sample_rate = read_32bitLE(0x44,streamFile); - loop_start_sample_adjust = read_16bitLE(0x42,streamFile); + loop_start_adjust = read_16bitLE(0x42,streamFile); loop_start_block = read_32bitLE(0x48,streamFile); loop_end_block = read_32bitLE(0x4c,streamFile); - } else { + } + else { vgmstream->sample_rate = read_32bitLE(0x48,streamFile); - loop_start_sample_adjust = read_32bitLE(0x44,streamFile); + loop_start_adjust = read_32bitLE(0x44,streamFile); loop_start_block = read_32bitLE(0x50,streamFile); loop_end_block = read_32bitLE(0x54,streamFile); } - loop_end_sample_adjust = interleave; /* loops end after all samples in the end_block AFAIK */ + loop_end_adjust = interleave; /* loops end after all samples in the end_block AFAIK */ - if ( loop_flag ) { - /* block to offset > offset to sample + adjust (number of samples into the block) */ - vgmstream->loop_start_sample = ((loop_start_block * channel_count * interleave) - * 28 / 16 / channel_count) - + (loop_start_sample_adjust * 28 / 16); - vgmstream->loop_end_sample = ((loop_end_block * channel_count * interleave) - * 28 / 16 / channel_count) - + (loop_end_sample_adjust * 28 / 16); - } - - - - /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - - for (i=0;i<channel_count;i++) - { - vgmstream->ch[i].streamfile = file; - - vgmstream->ch[i].channel_start_offset = - vgmstream->ch[i].offset = - start_offset + (vgmstream->interleave_block_size * i); - } + if (loop_flag) { + /* block to offset > offset to sample + adjust (number of frames into the block) */ + vgmstream->loop_start_sample = + ps_bytes_to_samples(loop_start_block * channel_count * interleave, channel_count) + + ps_bytes_to_samples(loop_start_adjust * channel_count, channel_count); + vgmstream->loop_end_sample = + ps_bytes_to_samples(loop_end_block * channel_count * interleave, channel_count) + + ps_bytes_to_samples(loop_end_adjust * channel_count, channel_count); } + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; return vgmstream; - /* clean up anything we may have opened */ fail: if (vgmstream) close_vgmstream(vgmstream); return NULL; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/rwsd.c b/Frameworks/vgmstream/vgmstream/src/meta/rwsd.c index 7dd87f089..7c7b29b0c 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/rwsd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/rwsd.c @@ -129,16 +129,18 @@ VGMSTREAM * init_vgmstream_rwsd(STREAMFILE *streamFile) { { if (strcasecmp("rwav",ext)) { - if (strcasecmp("bcwav",ext) && strcasecmp("bms",ext)) - { - goto fail; - } - else - { - // cwav, similar to little endian rwav - rwav = 1; + /* .bcwav: standard + * .bms: ? + * .sfx: Wizdom (3DS) + * .str: Pac-Man and the Ghostly Adventures 2 (3DS) + * .zic: Wizdom (3DS) */ + if (check_extensions(streamFile, "bcwav,bms,sfx,str,zic")) { + rwav = 1; // cwav, similar to little endian rwav big_endian = 0; } + else { + goto fail; + } } else { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c b/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c index cc9c0f36c..b1cb9a7fc 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c @@ -112,7 +112,7 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamHeader); switch (type) { -#ifdef VGM_USE_FFMPEG +#ifdef VGM_USE_VORBIS case 0x02: /* Ogg Vorbis [Ni no Kuni: Wrath of the White Witch Remastered (PC)] (codec hijack?) */ vgmstream->codec_data = init_ogg_vorbis(streamFile, start_offset, stream_size, NULL); if (!vgmstream->codec_data) goto fail; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/stx.c b/Frameworks/vgmstream/vgmstream/src/meta/stx.c deleted file mode 100644 index 3c8f3f922..000000000 --- a/Frameworks/vgmstream/vgmstream/src/meta/stx.c +++ /dev/null @@ -1,70 +0,0 @@ -#include "meta.h" -#include "../util.h" - -VGMSTREAM * init_vgmstream_stx(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - - const int loop_flag = 0; - const int channel_count = 2; /* .stx seems to be stereo only */ - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("stx",filename_extension(filename))) goto fail; - - /* length of data */ - if (read_32bitBE(0x00,streamFile) != - get_streamfile_size(streamFile) - 0x20) goto fail; - - /* bits per sample? */ - if (read_16bitBE(0x0a,streamFile) != 4) goto fail; - - /* samples per frame? */ - if (read_16bitBE(0x0c,streamFile) != 0x10) goto fail; - - /* ?? */ - if (read_16bitBE(0x0e,streamFile) != 0x1E) goto fail; - - /* build the VGMSTREAM */ - - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - - /* fill in the vital statistics */ - vgmstream->num_samples = read_32bitBE(0x04,streamFile); - vgmstream->sample_rate = (uint16_t)read_16bitBE(0x08,streamFile); - /* channels and loop flag are set by allocate_vgmstream */ - - vgmstream->coding_type = coding_NGC_AFC; - vgmstream->layout_type = layout_interleave; - vgmstream->meta_type = meta_STX; - - /* frame-level interleave (9 bytes) */ - vgmstream->interleave_block_size = 9; - - /* open the file for reading by each channel */ - { - STREAMFILE *chstreamfile; - int i; - - /* both channels use same buffer, as interleave is so small */ - chstreamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!chstreamfile) goto fail; - - for (i=0;i<channel_count;i++) { - vgmstream->ch[i].streamfile = chstreamfile; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset= - 0x20 + i*vgmstream->interleave_block_size; - } - } - - return vgmstream; - - /* clean up anything we may have opened */ -fail: - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/txth.c b/Frameworks/vgmstream/vgmstream/src/meta/txth.c index eb307fad5..d8c869248 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/txth.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/txth.c @@ -1058,6 +1058,9 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char /* COEFS */ else if (is_string(key,"coef_offset")) { if (!parse_num(txth->streamHead,txth,val, &txth->coef_offset)) goto fail; + /* special adjustment */ + if (txth->subsong_offset) + txth->coef_offset = txth->base_offset + txth->coef_offset + txth->subsong_offset * (txth->target_subsong - 1); } else if (is_string(key,"coef_spacing")) { if (!parse_num(txth->streamHead,txth,val, &txth->coef_spacing)) goto fail; @@ -1081,6 +1084,9 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char else if (is_string(key,"hist_offset")) { if (!parse_num(txth->streamHead,txth,val, &txth->hist_offset)) goto fail; txth->hist_set = 1; + /* special adjustment */ + if (txth->subsong_offset) + txth->hist_offset = txth->base_offset + txth->hist_offset + txth->subsong_offset * (txth->target_subsong - 1); } else if (is_string(key,"hist_spacing")) { if (!parse_num(txth->streamHead,txth,val, &txth->hist_spacing)) goto fail; @@ -1103,9 +1109,9 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char else if (is_string(key,"name_offset")) { if (!parse_num(txth->streamHead,txth,val, &txth->name_offset)) goto fail; txth->name_offset_set = 1; - /* special subsong adjustment */ + /* special adjustment */ if (txth->subsong_offset) - txth->name_offset = txth->name_offset + txth->subsong_offset * (txth->target_subsong - 1); + txth->name_offset = txth->base_offset + txth->name_offset + txth->subsong_offset * (txth->target_subsong - 1); } else if (is_string(key,"name_size")) { if (!parse_num(txth->streamHead,txth,val, &txth->name_size)) goto fail; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_bao.c b/Frameworks/vgmstream/vgmstream/src/meta/ubi_bao.c index c6309982c..7d44c6353 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ubi_bao.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_bao.c @@ -1013,10 +1013,8 @@ fail: } static int parse_type_silence(ubi_bao_header * bao, off_t offset, STREAMFILE* streamFile) { - int32_t (*read_32bit)(off_t,STREAMFILE*) = bao->big_endian ? read_32bitBE : read_32bitLE; + float (*read_f32)(off_t,STREAMFILE*) = bao->big_endian ? read_f32be : read_f32le; off_t h_offset = offset + bao->header_skip; - uint32_t duration_int; - float* duration_float; /* silence header */ bao->type = UBI_SILENCE; @@ -1025,13 +1023,8 @@ static int parse_type_silence(ubi_bao_header * bao, off_t offset, STREAMFILE* st goto fail; } - { - duration_int = (uint32_t)read_32bit(h_offset + bao->cfg.silence_duration_float, streamFile); - duration_float = (float*)&duration_int; - bao->duration = *duration_float; - } - - if (bao->duration <= 0) { + bao->duration = read_f32(h_offset + bao->cfg.silence_duration_float, streamFile); + if (bao->duration <= 0.0f) { VGM_LOG("UBI BAO: bad duration %f at %x\n", bao->duration, (uint32_t)offset); goto fail; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c b/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c index fe3cb3cc1..5bfdd28c7 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c @@ -1369,9 +1369,8 @@ fail: } static int parse_type_silence(ubi_sb_header * sb, off_t offset, STREAMFILE* streamFile) { + float (*read_f32)(off_t,STREAMFILE*) = sb->big_endian ? read_f32be : read_f32le; int32_t (*read_32bit)(off_t,STREAMFILE*) = sb->big_endian ? read_32bitBE : read_32bitLE; - uint32_t duration_int; - float* duration_float; /* silence header */ sb->type = UBI_SILENCE; @@ -1381,13 +1380,11 @@ static int parse_type_silence(ubi_sb_header * sb, off_t offset, STREAMFILE* stre } if (sb->cfg.silence_duration_int) { - duration_int = (uint32_t)read_32bit(offset + sb->cfg.silence_duration_int, streamFile); + uint32_t duration_int = (uint32_t)read_32bit(offset + sb->cfg.silence_duration_int, streamFile); sb->duration = (float)duration_int / 65536.0f; /* 65536.0 is common so probably means 1.0 */ } else if (sb->cfg.silence_duration_float) { - duration_int = (uint32_t)read_32bit(offset + sb->cfg.silence_duration_float, streamFile); - duration_float = (float*)&duration_int; - sb->duration = *duration_float; + sb->duration = read_f32(offset + sb->cfg.silence_duration_float, streamFile); } return 1; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/vag.c b/Frameworks/vgmstream/vgmstream/src/meta/vag.c index b3eb80b96..cbb2cc160 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/vag.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/vag.c @@ -146,20 +146,6 @@ VGMSTREAM * init_vgmstream_vag(STREAMFILE *streamFile) { loop_flag = ps_find_loop_offsets(streamFile, start_offset, channel_size*channel_count, channel_count, interleave, &loop_start_sample, &loop_end_sample); } - else if (read_32bitBE(0x30,streamFile) == 0x56414770) { /* "VAGp" */ - /* The Red Star (PS2) */ - start_offset = 0x60; /* two VAGp headers */ - channel_count = 2; - - if ((file_size - start_offset) % 0x4000 == 0) - interleave = 0x4000; - else if ((file_size - start_offset) % 0x4180 == 0) - interleave = 0x4180; - else - goto fail; - - loop_flag = 0; /* loop segments */ - } else if (version == 0x40000000) { /* Killzone (PS2) */ start_offset = 0x30; @@ -217,9 +203,32 @@ VGMSTREAM * init_vgmstream_vag(STREAMFILE *streamFile) { /* Need for Speed: Hot Pursuit 2 (PS2) */ start_offset = 0x30; channel_count = read_32bitBE(0x2c, streamFile); - interleave = 0x8000; channel_size = channel_size / channel_count; loop_flag = 0; + + /* detect interleave using end markers */ + interleave = 0; + + if (channel_count > 1) { + off_t offset = file_size; + off_t end_off = 0; + uint8_t flag; + + while (1) { + offset -= 0x10; + flag = read_8bit(offset + 0x01, streamFile); + if (flag == 0x01) { + if (!end_off) { + end_off = offset; + } else { + interleave = end_off - offset; + break; + } + } + + if (offset == start_offset) goto fail; + } + } } else { /* standard PS1/PS2/PS3 .vag [Ecco the Dolphin (PS2), Legasista (PS3)] */ @@ -267,3 +276,59 @@ fail: close_vgmstream(vgmstream); return NULL; } + +/* AAAp - Acclaim Austin Audio VAG header [The Red Star (PS2)] */ +VGMSTREAM* init_vgmstream_vag_aaap(STREAMFILE* streamFile) { + VGMSTREAM* vgmstream = NULL; + off_t vag_offset, start_offset; + uint32_t channel_size, sample_rate; + uint16_t interleave, channels; + uint32_t i; + int loop_flag; + + /* checks */ + /* .vag - assumed, we don't know the original filenames */ + if (!check_extensions(streamFile, "vag")) + goto fail; + + if (read_u32be(0x00, streamFile) != 0x41414170) /* "AAAp" */ + goto fail; + + interleave = read_u16le(0x04, streamFile); + channels = read_u16le(0x06, streamFile); + vag_offset = 0x08; + + /* file has VAGp header for each channel */ + for (i = 0; i < channels; i++) { + if (read_u32be(vag_offset + i * 0x30, streamFile) != 0x56414770) /* "VAGp" */ + goto fail; + } + + /* check version */ + if (read_u32be(vag_offset + 0x04, streamFile) != 0x20) + goto fail; + + channel_size = read_u32be(vag_offset + 0x0c, streamFile); + sample_rate = read_u32be(vag_offset + 0x10, streamFile); + start_offset = vag_offset + channels * 0x30; + loop_flag = 0; + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_PS2_VAGp_AAAP; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = ps_bytes_to_samples(channel_size, 1); + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + + if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/wave.c b/Frameworks/vgmstream/vgmstream/src/meta/wave.c index fe6c69453..3b7a761f7 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/wave.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/wave.c @@ -8,10 +8,9 @@ VGMSTREAM * init_vgmstream_wave(STREAMFILE *streamFile) { int loop_flag = 0, channel_count, sample_rate, codec; int32_t num_samples, loop_start = 0, loop_end = 0; size_t interleave; - int big_endian; int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; - //int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; + float (*read_f32)(off_t,STREAMFILE*) = NULL; /* checks */ if (!check_extensions(streamFile, "wave")) @@ -27,10 +26,10 @@ VGMSTREAM * init_vgmstream_wave(STREAMFILE *streamFile) { big_endian = read_32bitBE(0x00,streamFile) == 0xE5B7ECFE; if (big_endian) { read_32bit = read_32bitBE; - //read_16bit = read_16bitBE; + read_f32 = read_f32be; } else { read_32bit = read_32bitLE; - //read_16bit = read_16bitLE; + read_f32 = read_f32le; } channel_count = read_8bit(0x05,streamFile); @@ -40,15 +39,7 @@ VGMSTREAM * init_vgmstream_wave(STREAMFILE *streamFile) { if (read_8bit(0x0c,streamFile) != 0x00) /* ? */ goto fail; - /* sample rate in 32b float (WHY?)*/ - { - uint32_t sample_int = (uint32_t)read_32bit(0x0c, streamFile); - float* sample_float; - sample_float = (float*)&sample_int; - - sample_rate = (int)(*sample_float); - } - + sample_rate = (int)read_f32(0x0c, streamFile); /* sample rate in 32b float (WHY?) */ num_samples = read_32bit(0x10, streamFile); loop_start = read_32bit(0x14, streamFile); loop_end = read_32bit(0x18, streamFile); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xssb.c b/Frameworks/vgmstream/vgmstream/src/meta/xssb.c new file mode 100644 index 000000000..8f63c793a --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/xssb.c @@ -0,0 +1,140 @@ +#include "meta.h" +#include "../coding/coding.h" + + +//todo test and rethink usefulness/viability of globally using this +/* generic helper with a usable fields to describe static header values */ +typedef struct { + int codec; + int type; + int channels; + int sample_rate; + int loop_flag; + int loop_start; + int loop_end; + + int total_subsongs; + int target_subsong; + + size_t file_size; + + off_t info_start; + + off_t header_start; + size_t header_entry; + off_t header_offset; + + off_t data_start; + size_t data_size; + + off_t stream_start; + size_t stream_size; +} header_t; + +/* XSSB - from Artoon games [Blinx (Xbox), Blinx 2 (Xbox)] */ +VGMSTREAM* init_vgmstream_xssb(STREAMFILE *sf) { + VGMSTREAM *vgmstream = NULL; + //off_t start_offset, header_offset, data_start, info_start, header_start; + //size_t header_size, stream_size; + //int loop_flag, channel_count, sample_rate, codec, loop_start, loop_end; + //int total_subsongs, target_subsong = streamFile->stream_index; + header_t h; + + + /* checks */ + /* .bin: from named files inside .ipk bigfiles */ + if (!check_extensions(sf, "bin,lbin")) + goto fail; + + if (read_u32be(0x00, sf) != 0x58535342) /* "XSSB" */ + goto fail; + /* 0x04: null */ + /* 0x08: date-version ('20011217' in hex) */ + /* 0x0c: null */ + + h.info_start = read_s32le(0x10, sf); + h.header_start = read_s32le(0x14, sf); + h.data_start = read_s32le(0x18, sf); + /* 0x1c: null */ + + h.header_entry = read_s16le(h.info_start + 0x00, sf); + /* 0x02: always 127 */ + + /* get subsongs from header entries */ + { + off_t offset = h.header_start; + + h.total_subsongs = 0; + h.target_subsong = sf->stream_index <= 0 ? 1 : sf->stream_index; + + h.header_offset = 0; + while (offset < h.data_start) { + /* headers are just pasted together and then padding */ + if (read_u32be(offset, sf) == 0) + break; + h.total_subsongs++; + + if (h.target_subsong == h.total_subsongs) { + h.header_offset = offset; + } + + offset += h.header_entry; + } + + if (h.header_offset == 0) + goto fail; + if (h.target_subsong > h.total_subsongs || h.total_subsongs < 1) + goto fail; + } + + /* read header */ + h.codec = read_s16le(h.header_offset + 0x00, sf); + h.channels = read_s16le(h.header_offset + 0x02, sf); + h.sample_rate = read_u16le(h.header_offset + 0x04, sf); + /* 0x08: bitrate */ + /* 0x0c: block align/bps */ + /* 0x10: 0=PCM, 2=XBOX-IMA? */ + h.stream_start = read_s32le(h.header_offset + 0x14, sf) + h.data_start; + h.stream_size = read_s32le(h.header_offset + 0x18, sf); + h.loop_start = read_s32le(h.header_offset + 0x1c, sf); + h.loop_end = read_s32le(h.header_offset + 0x20, sf); + /* others: unknown and mostly fixed values */ + h.loop_flag = (h.loop_end > 0); + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(h.channels, h.loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_XSSB; + vgmstream->sample_rate = h.sample_rate; + vgmstream->loop_start_sample = h.loop_start; + vgmstream->loop_end_sample = h.loop_end; + vgmstream->num_streams = h.total_subsongs; + vgmstream->stream_size = h.stream_size; + + switch(h.codec) { + case 0x01: + vgmstream->coding_type = coding_PCM16LE; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x01; + + vgmstream->num_samples = pcm_bytes_to_samples(h.stream_size, h.channels, 16); + break; + + case 0x69: + vgmstream->coding_type = coding_XBOX_IMA; + vgmstream->layout_type = layout_none; + + vgmstream->num_samples = xbox_ima_bytes_to_samples(h.stream_size, h.channels); + break; + } + + if (!vgmstream_open_stream(vgmstream, sf, h.stream_start)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xwb_xsb.h b/Frameworks/vgmstream/vgmstream/src/meta/xwb_xsb.h index b254a7fe6..7772f4622 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xwb_xsb.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/xwb_xsb.h @@ -30,6 +30,9 @@ typedef struct { int cue_names_size; off_t cue_names_offset; + int index_size; + int entry_size; + /* output */ int parse_done; char name[STREAM_NAME_SIZE]; @@ -38,9 +41,16 @@ typedef struct { } xsb_header; -static void xsb_check_stream(xsb_header * xsb, int stream_index, int wavebank_index, off_t name_offset, STREAMFILE *sf) { +static void xsb_check_stream(xsb_header *xsb, int stream_index, int wavebank_index, off_t name_offset, STREAMFILE *sf) { if (xsb->parse_done) return; + //;VGM_LOG("XSB old: found stream=%i vs %i, wavebank=%i vs %i, name_offset=%lx\n", stream_index, xsb->selected_stream, wavebank_index, xsb->selected_wavebank, name_offset); + + if (stream_index < 0 || stream_index > 0xFFF || wavebank_index < 0 || wavebank_index > xsb->wavebanks_count) { + VGM_LOG("XSB old: bad stream=%i, wavebank=%i\n", stream_index, wavebank_index); + return; + } + /* multiple names may correspond to a stream (ex. Blue Dragon), so we concat all */ if (xsb->selected_stream == stream_index && @@ -48,7 +58,7 @@ static void xsb_check_stream(xsb_header * xsb, int stream_index, int wavebank_in char name[STREAM_NAME_SIZE]; size_t name_size; - name_size = read_string(name,sizeof(name), name_offset,sf); /* null-terminated */ + name_size = read_string(name,sizeof(name), name_offset, sf); /* null-terminated */ if (xsb->name_len) { const char *cat = "; "; @@ -68,103 +78,238 @@ static void xsb_check_stream(xsb_header * xsb, int stream_index, int wavebank_in } } -/* old XACT1 is a bit different and much of it is unknown but this seems to work. - * - after header is the simple(?) cues table then complex(?) cues table - * - simple cues point to complex cues by index - * - complex cues may have stream/wavebank or point again to a sound(?) with the stream/wavebank - */ -static int parse_xsb_cues_old(xsb_header * xsb, STREAMFILE *sf) { + +static int parse_xsb_old_cue_entry(xsb_header *xsb, STREAMFILE *sf, off_t name_offset, int entry) { int32_t (*read_s32)(off_t,STREAMFILE*) = xsb->big_endian ? read_s32be : read_s32le; int16_t (*read_s16)(off_t,STREAMFILE*) = xsb->big_endian ? read_s16be : read_s16le; - uint8_t flags, subflags; - int cue_index, stream_index, wavebank_index = 0; - off_t offset, name_offset, cue_offset, sound_offset; - int i; - size_t simple_entry, complex_entry; + uint32_t sound_type, sound_size; + int stream_index, wavebank_index; + off_t offset, jump_offset, sound_offset, min_sections_offset, max_sections_offset; + int i, j, sound_count, table_count; - if (xsb->version <= XSB_XACT1_1_MAX) { - simple_entry = 0x10; - complex_entry = 0x14; - } - else if (xsb->version <= XSB_XACT1_2_MAX) { - simple_entry = 0x14; - complex_entry = 0x14; - } - else { - VGM_LOG("XSB: unknown old format for version %x\n", xsb->version); + if (entry < 0 || entry > xsb->complex_cues_count) { + VGM_LOG("XSB old: ignored bad cue entry %i\n", entry); goto fail; } + min_sections_offset = xsb->sounds_offset + xsb->simple_cues_count*xsb->index_size + xsb->complex_cues_offset*xsb->entry_size; + max_sections_offset = get_streamfile_size(sf); - offset = xsb->sounds_offset; - for (i = 0; i < xsb->simple_cues_count; i++) { + offset = xsb->sounds_offset + xsb->simple_cues_count*xsb->index_size + entry*xsb->entry_size; - /* *** simple sound *** */ - /* 00(2): flags? */ - cue_index = read_s16(offset + 0x02,sf); - name_offset = read_s32(offset + 0x04,sf); - /* 06-14: unknown */ - //;VGM_LOG("XSB old simple at %lx: cue=%i, name_offset=%lx\n", offset, cue_index, name_offset); - offset += simple_entry; + /*** cue entry ***/ + /* 0x00: offset or stream/wave */ + /* others: mostly 1 byte fields, probably config for sfx/complex entries */ + flags = read_u8(offset + 0x0b, sf); + //;VGM_LOG("XSB old entry %i at %lx: flags=%x\n", entry, offset, flags); - /* when cue_index is -1 @0x08 points to some offset (random sound type?) [ex. ATV 3 Lawless (Xbox)] */ - if (cue_index < 0 && cue_index > xsb->complex_cues_count) { - VGM_LOG("XSB old: ignored cue index %i\n", cue_index); - continue; + if (flags & 0x10) { /* multi entry (found with lower bits but not with 8) */ + jump_offset = read_s32(offset + 0x00, sf); + + if (jump_offset < min_sections_offset || jump_offset > max_sections_offset) { + VGM_LOG("XSB old entry %i at %lx: bad multi jump offset=%lx\n", entry, offset, jump_offset); + goto fail; } + /*** table to streams ***/ + table_count = read_s8(jump_offset + 0x00, sf); + /* 0x01: null? */ + /* 0x02: always count*2? */ + //;VGM_LOG("XSB old multi stream table at %lx: count=%x\n", jump_offset, table_count); - /* *** complex sound *** */ - cue_offset = xsb->sounds_offset + xsb->simple_cues_count*simple_entry + cue_index*complex_entry; + for (j = 0; j < table_count; j++) { + stream_index = read_s16(jump_offset + 0x04 + 0x08*j + 0x00, sf); + wavebank_index = read_s16(jump_offset + 0x04 + 0x08*j + 0x02, sf); + /* 0x04: config? */ - /* most fields looks like more flags and optional offsets depending of flags */ - flags = read_u8(cue_offset + 0x0b,sf); - - if (flags & 8) { /* simple */ - stream_index = read_s16(cue_offset + 0x00,sf); - wavebank_index = read_s16(cue_offset + 0x02,sf); + xsb_check_stream(xsb, stream_index, wavebank_index, name_offset, sf); + if (xsb->parse_done) return 1; } - //else if (flags & 4) { /* unsure */ - // VGM_LOG("XSB old complex at %lx: unknown flags=%x\n", cue_offset, flags); - // continue; - //} - else { /* complex (flags none/1/2) */ - sound_offset = read_s32(cue_offset + 0x00,sf); + } + else if (flags & 0x8) { /* simple entry (also found with lower bits) */ + stream_index = read_s16(offset + 0x00, sf); + wavebank_index = read_s16(offset + 0x02, sf); - /* *** jump entry *** */ - /* 00(1): flags? */ - sound_offset = read_s32(sound_offset + 0x01,sf) & 0x00FFFFFF; /* 24b */ - - /* *** sound entry *** */ - subflags = read_u8(sound_offset + 0x00,sf); - if (subflags == 0x00) { /* 0x0c entry */ - stream_index = read_s16(sound_offset + 0x08,sf); - wavebank_index = read_s16(sound_offset + 0x0a,sf); - } - else if (subflags == 0x0a) { /* 0x20 entry */ - stream_index = read_s16(sound_offset + 0x1c,sf); - wavebank_index = read_s16(sound_offset + 0x1e,sf); - } - else { - VGM_LOG("XSB old sound at %lx: unknown subflags=%x\n", sound_offset, subflags); - continue; - } - } - - //;VGM_LOG("XSB old complex at %lx: flags=%x, stream=%i, wavebank=%i, name_offset=%lx\n", cue_offset, flags, stream_index, wavebank_index, name_offset); - xsb_check_stream(xsb, stream_index, wavebank_index, name_offset,sf); + xsb_check_stream(xsb, stream_index, wavebank_index, name_offset, sf); if (xsb->parse_done) return 1; } + else { /* complex entry (lower flags) */ + jump_offset = read_s32(offset + 0x00, sf); + + if (jump_offset < min_sections_offset || jump_offset > max_sections_offset) { + VGM_LOG("XSB old entry %i at %lx: bad complex jump offset=%lx\n", entry, offset, jump_offset); + goto fail; + } + + /*** sound table ***/ + sound_count = read_s8 (jump_offset + 0x00, sf); + sound_offset = read_s32(jump_offset + 0x01, sf) & 0x00FFFFFF; /* 24b */ + //;VGM_LOG("XSB old entry %i sound table at %lx: count=%x\n", entry, jump_offset, sound_count); + + /* read all sounds (seems ordered higher types to lower) */ + for (i = 0; i < sound_count; i++) { + /*** sound entry ***/ + sound_type = read_u8(sound_offset + 0x00, sf); + /* 0x01: rarely set but possible */ + /* 0x02: null? */ + sound_size = read_u8(sound_offset + 0x04, sf); + //;VGM_LOG("XSB old entry sound %i at %lx: type=%x\n", i, sound_offset, sound_type); + + switch(sound_type) { + case 0x12: + case 0x11: + case 0x10: + case 0x07: + case 0x05: + /* config? (doesn't seem they contain entries or offsets) */ + break; +#if 0 + case 0x0a /* used? (0x20 entry)? */ + stream_index = read_s16(sound_offset + 0x1c, sf); + wavebank_index = read_s16(sound_offset + 0x1e, sf); + break; +#endif + + case 0x01: /* has more fields, uses subflag 0x04 */ + case 0x00: /* smaller, uses subflag 0x44 (rare) */ + subflags = read_u8(sound_offset + 0x05, sf); + + if (subflags == 0x00 || subflags == 0x40) { + stream_index = read_s16(sound_offset + 0x08, sf); + wavebank_index = read_s16(sound_offset + 0x0a, sf); + + xsb_check_stream(xsb, stream_index, wavebank_index, name_offset, sf); + if (xsb->parse_done) return 1; + } + else if (subflags == 0x04 || subflags == 0x44) { + jump_offset = read_s32(sound_offset + 0x08, sf); + + if (jump_offset < min_sections_offset || jump_offset > max_sections_offset) { + VGM_LOG("XSB old entry %i at %lx: bad complex multi jump offset=%lx at %lx\n", entry, offset, jump_offset, sound_offset); + break; + } + + /*** table to streams ***/ + table_count = read_s8(jump_offset + 0x00, sf); + /* 0x01: null? */ + /* 0x02: always count*2? */ + //;VGM_LOG("XSB old complex stream table at %lx: count=%x\n", jump_offset, table_count); + + for (j = 0; j < table_count; j++) { + stream_index = read_s16(jump_offset + 0x04 + 0x08*j + 0x00, sf); + wavebank_index = read_s16(jump_offset + 0x04 + 0x08*j + 0x02, sf); + /* 0x04: config? */ + + xsb_check_stream(xsb, stream_index, wavebank_index, name_offset, sf); + if (xsb->parse_done) return 1; + } + } + else { + VGM_LOG("XSB old entry %i at %lx: bad complex multi flags at %lx\n", entry, offset, sound_offset); + } + break; + + stream_index = read_s16(sound_offset + 0x08, sf); + wavebank_index = read_s16(sound_offset + 0x0a, sf); + + xsb_check_stream(xsb, stream_index, wavebank_index, name_offset, sf); + if (xsb->parse_done) return 1; + break; + + default: + VGM_LOG("XSB old entry %i at %lx: unknown sound type=%x at %lx\n", entry, offset, sound_type, sound_offset); + break; + } + + sound_offset += 0x04 + 0x04 + sound_size; + } + } return 1; fail: return 0; } -static int parse_xsb_clip(xsb_header * xsb, off_t offset, off_t name_offset, STREAMFILE *sf) { +/* old XACT1 is a bit different and much of it is unknown but this seems ok: + * - after header is the cue index table then cue entry table + * - each cue index points to a cue entry by number + * - each cue entry have a stream/wavebank, directly or first pointing to a "sound" + * sound entries are more complex with multi-parts and subtables (mainly used for sfx, + * ex. ATV 3 Lawless (Xbox), Psychonauts (Xbox) have more complex types. + * + * Some streams may not be pointed at all as they don't have an apparent name, or have an + * entry in the sound table but no reference to it (ex. CommonMusic.xsb or BBFX.xsb in Psychonauts) + * + * Data is divided like: + * - header + * - cue indexes + * - cue entries + * - wavebank names + * - cue names + * - unknown table + * - sounds jump table + * - sounds entries + * - multi entry jump table + * - others + */ +static int parse_xsb_old_cues(xsb_header *xsb, STREAMFILE *sf) { + int32_t (*read_s32)(off_t,STREAMFILE*) = xsb->big_endian ? read_s32be : read_s32le; + int16_t (*read_s16)(off_t,STREAMFILE*) = xsb->big_endian ? read_s16be : read_s16le; + uint16_t flags; + int cue_entry; + off_t offset, name_offset, jump_offset; + int i, j, table_count; + + + //;VGM_LOG("XSB old: s.offset=%lx, index count=%i, entry count=%i\n", xsb->sounds_offset, xsb->simple_cues_count, xsb->complex_cues_count); + + offset = xsb->sounds_offset; + for (i = 0; i < xsb->simple_cues_count; i++) { + + /*** cue index ***/ + flags = read_s16(offset + 0x00, sf); /* 0 is normal, 2 exists and 8 often goes with -1 (random) entry */ + cue_entry = read_s16(offset + 0x02, sf); + name_offset = read_s32(offset + 0x04, sf); + /* 0x08: table offset, or -1 */ + /* 0x0c: some low value or flag? */ + /* 0x0e: some index? */ + /* 0x10: 4 fields? (-1 or 7) */ + //;VGM_LOG("XSB old index %i at %lx: flags=%x, entry=%i, name_offset=%lx\n", i, offset, flags, cue_entry, name_offset); + + if (cue_entry < 0) { + jump_offset = read_s32(offset + 0x08, sf); + /* 0x0c/0e: some count? */ + /* 0x10: offset to some empty-ish table */ + + /*** table (random?) to cue entry ***/ + table_count = read_s8(jump_offset + 0x00, sf); + /* 0x01: often 0x60? */ + /* 0x02: always count*2? */ + //;VGM_LOG("XSB old entry table at %lx: count=%x\n", jump_offset, table_count); + + for (j = 0; j < table_count; j++) { + cue_entry = read_s16(jump_offset + 0x04 + 0x08*j, sf); + /* 0x02: null? */ + /* 0x04/6: related to randomness? */ + parse_xsb_old_cue_entry(xsb, sf, name_offset, cue_entry); + if (xsb->parse_done) return 1; + } + } + else { + parse_xsb_old_cue_entry(xsb, sf, name_offset, cue_entry); + if (xsb->parse_done) return 1; + } + + offset += xsb->index_size; + } + + return 1; +} + +static int parse_xsb_clip(xsb_header *xsb, off_t offset, off_t name_offset, STREAMFILE *sf) { uint32_t (*read_u32)(off_t,STREAMFILE*) = xsb->big_endian ? read_u32be : read_u32le; int16_t (*read_s16)(off_t,STREAMFILE*) = xsb->big_endian ? read_s16be : read_s16le; @@ -173,13 +318,13 @@ static int parse_xsb_clip(xsb_header * xsb, off_t offset, off_t name_offset, STR int i, t, track_count, event_count; - event_count = read_s8(offset + 0x00,sf); + event_count = read_s8(offset + 0x00, sf); //;VGM_LOG("XSB clip at %lx\n", offset); offset += 0x01; for (i = 0; i < event_count; i++) { - flags = read_u32(offset + 0x00,sf); + flags = read_u32(offset + 0x00, sf); /* 04(2): random offset */ //;VGM_LOG("XSB clip event: %x at %lx\n", flags, offset); @@ -190,8 +335,8 @@ static int parse_xsb_clip(xsb_header * xsb, off_t offset, off_t name_offset, STR case 0x01: /* playwave event */ /* 00(1): unknown */ /* 01(1): flags */ - stream_index = read_s16(offset + 0x02,sf); - wavebank_index = read_s8 (offset + 0x04,sf); + stream_index = read_s16(offset + 0x02, sf); + wavebank_index = read_s8 (offset + 0x04, sf); /* 05(1): loop count */ /* 06(2): pan angle */ /* 08(2): pan arc */ @@ -199,7 +344,7 @@ static int parse_xsb_clip(xsb_header * xsb, off_t offset, off_t name_offset, STR //;VGM_LOG("XSB clip event 1 at %lx: stream=%i, wavebank=%i\n", offset, stream_index, wavebank_index); offset += 0x0a; - xsb_check_stream(xsb, stream_index, wavebank_index, name_offset,sf); + xsb_check_stream(xsb, stream_index, wavebank_index, name_offset, sf); if (xsb->parse_done) return 1; break; @@ -209,7 +354,7 @@ static int parse_xsb_clip(xsb_header * xsb, off_t offset, off_t name_offset, STR /* 02(1): loop count */ /* 03(2): pan angle */ /* 05(2): pan arc */ - track_count = read_s16(offset + 0x07,sf); + track_count = read_s16(offset + 0x07, sf); /* 09(1): flags? */ /* 0a(5): unknown */ @@ -217,15 +362,15 @@ static int parse_xsb_clip(xsb_header * xsb, off_t offset, off_t name_offset, STR offset += 0x0F; for (t = 0; t < track_count; t++) { - stream_index = read_s16(offset + 0x00,sf); - wavebank_index = read_s8 (offset + 0x02,sf); + stream_index = read_s16(offset + 0x00, sf); + wavebank_index = read_s8 (offset + 0x02, sf); /* 03(1): min weight */ /* 04(1): min weight */ //;VGM_LOG("XSB clip event 3: track=%i, stream=%i, wavebank=%i\n", t, stream_index, wavebank_index); offset += 0x05; - xsb_check_stream(xsb, stream_index, wavebank_index, name_offset,sf); + xsb_check_stream(xsb, stream_index, wavebank_index, name_offset, sf); if (xsb->parse_done) return 1; } break; @@ -233,8 +378,8 @@ static int parse_xsb_clip(xsb_header * xsb, off_t offset, off_t name_offset, STR case 0x04: /* playwave event */ /* 00(1): unknown */ /* 01(1): flags */ - stream_index = read_s16(offset + 0x02,sf); - wavebank_index = read_s8 (offset + 0x04,sf); + stream_index = read_s16(offset + 0x02, sf); + wavebank_index = read_s8 (offset + 0x04, sf); /* 05(1): loop count */ /* 06(2): pan angle */ /* 08(2): pan arc */ @@ -252,7 +397,7 @@ static int parse_xsb_clip(xsb_header * xsb, off_t offset, off_t name_offset, STR //;VGM_LOG("XSB clip event 4 at %lx: stream=%i, wavebank=%i\n", offset, stream_index, wavebank_index); offset += 0x1c; - xsb_check_stream(xsb, stream_index, wavebank_index, name_offset,sf); + xsb_check_stream(xsb, stream_index, wavebank_index, name_offset, sf); if (xsb->parse_done) return 1; break; @@ -272,7 +417,7 @@ static int parse_xsb_clip(xsb_header * xsb, off_t offset, off_t name_offset, STR /* 16(1): max Q */ /* 17(1): unknown */ /* 18(1): variation flags */ - track_count = read_s16(offset + 0x19,sf); + track_count = read_s16(offset + 0x19, sf); /* 1a(1): flags 2 */ /* 1b(5): unknown 2 */ @@ -280,15 +425,15 @@ static int parse_xsb_clip(xsb_header * xsb, off_t offset, off_t name_offset, STR offset += 0x20; for (t = 0; t < track_count; t++) { - stream_index = read_s16(offset + 0x00,sf); - wavebank_index = read_s8 (offset + 0x02,sf); + stream_index = read_s16(offset + 0x00, sf); + wavebank_index = read_s8 (offset + 0x02, sf); /* 03(1): min weight */ /* 04(1): min weight */ //;VGM_LOG("XSB clip event 6: track=%i, stream=%i, wavebank=%i at %lx\n", t, stream_index, wavebank_index, offset); offset += 0x05; - xsb_check_stream(xsb, stream_index, wavebank_index, name_offset,sf); + xsb_check_stream(xsb, stream_index, wavebank_index, name_offset, sf); if (xsb->parse_done) return 1; } break; @@ -318,7 +463,7 @@ fail: return 0; } -static int parse_xsb_sound(xsb_header * xsb, off_t offset, off_t name_offset, STREAMFILE *sf) { +static int parse_xsb_sound(xsb_header *xsb, off_t offset, off_t name_offset, STREAMFILE *sf) { int32_t (*read_s32)(off_t,STREAMFILE*) = xsb->big_endian ? read_s32be : read_s32le; int16_t (*read_s16)(off_t,STREAMFILE*) = xsb->big_endian ? read_s16be : read_s16le; @@ -327,7 +472,7 @@ static int parse_xsb_sound(xsb_header * xsb, off_t offset, off_t name_offset, ST int i, clip_count = 0; - flags = read_u8 (offset + 0x00,sf); + flags = read_u8 (offset + 0x00, sf); /* 0x01(2): category */ /* 0x03(1): decibels */ /* 0x04(2): pitch */ @@ -338,24 +483,24 @@ static int parse_xsb_sound(xsb_header * xsb, off_t offset, off_t name_offset, ST offset += 0x09; if (flags & 0x01) { /* complex sound */ - clip_count = read_u8 (offset + 0x00,sf); + clip_count = read_u8 (offset + 0x00, sf); //;VGM_LOG("XSB sound: complex with clips=%i\n", clip_count); offset += 0x01; } else { - stream_index = read_s16(offset + 0x00,sf); - wavebank_index = read_s8(offset + 0x02,sf); + stream_index = read_s16(offset + 0x00, sf); + wavebank_index = read_s8(offset + 0x02, sf); //;VGM_LOG("XSB sound: simple with stream=%i, wavebank=%i\n", stream_index, wavebank_index); offset += 0x03; - xsb_check_stream(xsb, stream_index, wavebank_index, name_offset,sf); + xsb_check_stream(xsb, stream_index, wavebank_index, name_offset, sf); if (xsb->parse_done) return 1; } if (flags & 0x0E) { /* has RPCs */ - size_t rpc_size = read_s16(offset + 0x00,sf); + size_t rpc_size = read_s16(offset + 0x00, sf); /* 0x02(2): preset count */ /* 0x04(4*count): RPC indexes */ /* (presets per flag 2/4/8 flag) */ @@ -363,7 +508,7 @@ static int parse_xsb_sound(xsb_header * xsb, off_t offset, off_t name_offset, ST } if (flags & 0x10) { /* has DSPs */ - size_t dsp_size = read_s16(offset + 0x00,sf); + size_t dsp_size = read_s16(offset + 0x00, sf); /* follows RPC format? */ offset += dsp_size; } @@ -372,14 +517,14 @@ static int parse_xsb_sound(xsb_header * xsb, off_t offset, off_t name_offset, ST off_t clip_offset; for (i = 0; i < clip_count; i++) { /* 00(1): decibels */ - clip_offset = read_s32(offset + 0x01,sf); + clip_offset = read_s32(offset + 0x01, sf); /* 05(2): filter config */ /* 07(2): filter frequency */ //;VGM_LOG("XSB sound clip %i at %lx\n", i, offset); offset += 0x09; - parse_xsb_clip(xsb, clip_offset, name_offset,sf); + parse_xsb_clip(xsb, clip_offset, name_offset, sf); if (xsb->parse_done) return 1; } } @@ -387,7 +532,7 @@ static int parse_xsb_sound(xsb_header * xsb, off_t offset, off_t name_offset, ST return 0; } -static int parse_xsb_variation(xsb_header * xsb, off_t offset, off_t name_offset, STREAMFILE *sf) { +static int parse_xsb_variation(xsb_header *xsb, off_t offset, off_t name_offset, STREAMFILE *sf) { int32_t (*read_s32)(off_t,STREAMFILE*) = xsb->big_endian ? read_s32be : read_s32le; uint16_t (*read_u16)(off_t,STREAMFILE*) = xsb->big_endian ? read_u16be : read_u16le; int16_t (*read_s16)(off_t,STREAMFILE*) = xsb->big_endian ? read_s16be : read_s16le; @@ -397,8 +542,8 @@ static int parse_xsb_variation(xsb_header * xsb, off_t offset, off_t name_offset int i, variation_count; - variation_count = read_s16(offset + 0x00,sf); - flags = read_u16(offset + 0x02,sf); + variation_count = read_s16(offset + 0x00, sf); + flags = read_u16(offset + 0x02, sf); //;VGM_LOG("XSB variation at %lx\n", offset); offset += 0x04; @@ -408,32 +553,32 @@ static int parse_xsb_variation(xsb_header * xsb, off_t offset, off_t name_offset switch ((flags >> 3) & 0x7) { case 0: /* wave */ - stream_index = read_s16(offset + 0x00,sf); - wavebank_index = read_s8(offset + 0x02,sf); + stream_index = read_s16(offset + 0x00, sf); + wavebank_index = read_s8(offset + 0x02, sf); /* 03(1): weight min */ /* 04(1): weight max */ //;VGM_LOG("XSB variation: type 0 with stream=%i, wavebank=%i\n", stream_index, wavebank_index); offset += 0x05; - xsb_check_stream(xsb, stream_index, wavebank_index, name_offset,sf); + xsb_check_stream(xsb, stream_index, wavebank_index, name_offset, sf); if (xsb->parse_done) return 1; break; case 1: /* sound */ - sound_offset = read_s32(offset + 0x00,sf); + sound_offset = read_s32(offset + 0x00, sf); /* 04(1): weight min */ /* 05(1): weight max */ //;VGM_LOG("XSB variation: type 1\n"); offset += 0x06; - parse_xsb_sound(xsb, sound_offset, name_offset,sf); + parse_xsb_sound(xsb, sound_offset, name_offset, sf); if (xsb->parse_done) return 1; break; case 3: /* sound */ - sound_offset = read_s32(offset + 0x00,sf); + sound_offset = read_s32(offset + 0x00, sf); /* 04(4): weight min */ /* 08(4): weight max */ /* 0c(4): flags */ @@ -441,18 +586,18 @@ static int parse_xsb_variation(xsb_header * xsb, off_t offset, off_t name_offset //;VGM_LOG("XSB variation: type 3\n"); offset += 0x10; - parse_xsb_sound(xsb, sound_offset, name_offset,sf); + parse_xsb_sound(xsb, sound_offset, name_offset, sf); if (xsb->parse_done) return 1; break; case 4: /* compact wave */ - stream_index = read_s16(offset + 0x00,sf); - wavebank_index = read_s8(offset + 0x02,sf); + stream_index = read_s16(offset + 0x00, sf); + wavebank_index = read_s8(offset + 0x02, sf); //;VGM_LOG("XSB variation: type 4 with stream=%i, wavebank=%i\n", stream_index, wavebank_index); offset += 0x03; - xsb_check_stream(xsb, stream_index, wavebank_index, name_offset,sf); + xsb_check_stream(xsb, stream_index, wavebank_index, name_offset, sf); if (xsb->parse_done) return 1; break; @@ -474,7 +619,7 @@ fail: } -static int parse_xsb_cues_new(xsb_header * xsb, STREAMFILE *sf) { +static int parse_xsb_cues(xsb_header *xsb, STREAMFILE *sf) { int32_t (*read_s32)(off_t,STREAMFILE*) = xsb->big_endian ? read_s32be : read_s32le; uint8_t flags; @@ -486,23 +631,23 @@ static int parse_xsb_cues_new(xsb_header * xsb, STREAMFILE *sf) { offset = xsb->simple_cues_offset; for (i = 0; i < xsb->simple_cues_count; i++) { /* 00(1): flags */ - sound_offset = read_s32(offset + 0x01,sf); + sound_offset = read_s32(offset + 0x01, sf); //;VGM_LOG("XSB cues: simple %i at %lx\n", i, offset); offset += 0x05; - name_offset = read_s32(names_offset + 0x00,sf); + name_offset = read_s32(names_offset + 0x00, sf); /* 04(2): unknown (-1) */ names_offset += 0x06; - parse_xsb_sound(xsb, sound_offset, name_offset,sf); + parse_xsb_sound(xsb, sound_offset, name_offset, sf); if (xsb->parse_done) break; } offset = xsb->complex_cues_offset; for (i = 0; i < xsb->complex_cues_count; i++) { - flags = read_u8(offset + 0x00,sf); - sound_offset = read_s32(offset + 0x01,sf); + flags = read_u8(offset + 0x00, sf); + sound_offset = read_s32(offset + 0x01, sf); /* 05(4): unknown (sound) / transition table offset (variation) */ /* 09(1): instance limit */ /* 0a(2): fade in sec */ @@ -512,14 +657,14 @@ static int parse_xsb_cues_new(xsb_header * xsb, STREAMFILE *sf) { //;VGM_LOG("XSB cues: complex %i at %lx\n", i, offset); offset += 0x0f; - name_offset = read_s32(names_offset + 0x00,sf); + name_offset = read_s32(names_offset + 0x00, sf); /* 04(2): unknown (-1) */ names_offset += 0x06; if (flags & (1<<2)) - parse_xsb_sound(xsb, sound_offset, name_offset,sf); + parse_xsb_sound(xsb, sound_offset, name_offset, sf); else - parse_xsb_variation(xsb, sound_offset, name_offset,sf); + parse_xsb_variation(xsb, sound_offset, name_offset, sf); if (xsb->parse_done) break; } @@ -547,7 +692,7 @@ static int parse_xsb_cues_new(xsb_header * xsb, STREAMFILE *sf) { * - https://github.com/MonoGame/MonoGame/blob/master/MonoGame.Framework/Audio/Xact/ * - https://github.com/espes/MacTerrariaWrapper/tree/master/xactxtract */ -static int parse_xsb(xsb_header * xsb, STREAMFILE *sf, char *xwb_wavebank_name) { +static int parse_xsb(xsb_header *xsb, STREAMFILE *sf, char *xwb_wavebank_name) { int32_t (*read_s32)(off_t,STREAMFILE*) = NULL; int16_t (*read_s16)(off_t,STREAMFILE*) = NULL; @@ -563,77 +708,84 @@ static int parse_xsb(xsb_header * xsb, STREAMFILE *sf, char *xwb_wavebank_name) /* parse sound bank header */ - xsb->version = read_s16(0x04,sf); /* tool version */ + xsb->version = read_s16(0x04, sf); /* tool version */ if (xsb->version <= XSB_XACT1_0_MAX) { /* 06(2): crc */ - xsb->wavebanks_offset = read_s32(0x08,sf); + xsb->wavebanks_offset = read_s32(0x08, sf); /* 0c(4): unknown1 offset (entry: 0x04) */ /* 10(4): unknown2 offset */ /* 14(2): element count? */ /* 16(2): empty? */ /* 18(2): empty? */ - xsb->complex_cues_count = read_s16(0x1a,sf); - xsb->simple_cues_count = read_s16(0x1c,sf); - xsb->wavebanks_count = read_s16(0x1e,sf); + xsb->complex_cues_count = read_s16(0x1a, sf); + xsb->simple_cues_count = read_s16(0x1c, sf); + xsb->wavebanks_count = read_s16(0x1e, sf); /* 20(10): xsb name */ xsb->sounds_offset = 0x30; xsb->wavebanks_name_size = 0x10; + xsb->index_size = 0x10; + xsb->entry_size = 0x14; + } else if (xsb->version <= XSB_XACT1_1_MAX) { /* 06(2): crc */ - xsb->wavebanks_offset = read_s32(0x08,sf); + xsb->wavebanks_offset = read_s32(0x08, sf); /* 0c(4): unknown1 offset (entry: 0x04) */ /* 10(4): unknown2 offset */ /* 14(4): unknown3 offset */ /* 18(2): empty? */ /* 1a(2): element count? */ - xsb->complex_cues_count = read_s16(0x1c,sf); - xsb->simple_cues_count = read_s16(0x1e,sf); + xsb->complex_cues_count = read_s16(0x1c, sf); + xsb->simple_cues_count = read_s16(0x1e, sf); /* 20(2): unknown count? (related to unknown2?) */ - xsb->wavebanks_count = read_s16(0x22,sf); + xsb->wavebanks_count = read_s16(0x22, sf); /* 24(10): xsb name */ xsb->sounds_offset = 0x34; xsb->wavebanks_name_size = 0x10; + xsb->index_size = 0x10; + xsb->entry_size = 0x14; } else if (xsb->version <= XSB_XACT1_2_MAX) { /* 06(2): crc */ - xsb->wavebanks_offset = read_s32(0x08,sf); + xsb->wavebanks_offset = read_s32(0x08, sf); /* 0c(4): unknown1 offset (entry: 0x14) */ /* 10(4): unknown2 offset (entry: variable) */ /* 14(4): unknown3 offset */ /* 18(2): empty? */ /* 1a(2): element count? */ - xsb->complex_cues_count = read_s16(0x1c,sf); - xsb->simple_cues_count = read_s16(0x1e,sf); + xsb->complex_cues_count = read_s16(0x1c, sf); + xsb->simple_cues_count = read_s16(0x1e, sf); /* 20(2): unknown count? (related to unknown2?) */ - xsb->wavebanks_count = read_s16(0x22,sf); + xsb->wavebanks_count = read_s16(0x22, sf); /* 24(4): null? */ /* 28(10): xsb name */ xsb->sounds_offset = 0x38; xsb->wavebanks_name_size = 0x10; + xsb->index_size = 0x14; + xsb->entry_size = 0x14; } else if (xsb->version <= XSB_XACT2_MAX) { /* 06(2): crc */ /* 08(1): platform? (3=X360) */ - xsb->simple_cues_count = read_s16(0x09,sf); - xsb->complex_cues_count = read_s16(0x0B,sf); - xsb->wavebanks_count = read_s8 (0x11,sf); - xsb->sounds_count = read_s16(0x12,sf); + xsb->simple_cues_count = read_s16(0x09, sf); + xsb->complex_cues_count = read_s16(0x0B, sf); + xsb->wavebanks_count = read_s8 (0x11, sf); + xsb->sounds_count = read_s16(0x12, sf); /* 14(2): unknown */ - xsb->cue_names_size = read_s32(0x16,sf); - xsb->simple_cues_offset = read_s32(0x1a,sf); - xsb->complex_cues_offset = read_s32(0x1e,sf); - xsb->cue_names_offset = read_s32(0x22,sf); + xsb->cue_names_size = read_s32(0x16, sf); + xsb->simple_cues_offset = read_s32(0x1a, sf); + xsb->complex_cues_offset = read_s32(0x1e, sf); + xsb->cue_names_offset = read_s32(0x22, sf); /* 26(4): unknown */ /* 2a(4): unknown */ /* 2e(4): unknown */ - xsb->wavebanks_offset = read_s32(0x32,sf); + xsb->wavebanks_offset = read_s32(0x32, sf); /* 36(4): cue name hash table offset? */ - xsb->nameoffsets_offset = read_s32(0x3a,sf); - xsb->sounds_offset = read_s32(0x3e,sf); + xsb->nameoffsets_offset = read_s32(0x3a, sf); + xsb->sounds_offset = read_s32(0x3e, sf); /* 42(4): unknown */ /* 46(4): unknown */ /* 4a(64): xsb name */ @@ -646,23 +798,23 @@ static int parse_xsb(xsb_header * xsb, STREAMFILE *sf, char *xwb_wavebank_name) /* 0a(4): last modified low */ /* 0e(4): last modified high */ /* 12(1): platform? (1=PC, 3=X360) */ - xsb->simple_cues_count = read_s16(0x13,sf); - xsb->complex_cues_count = read_s16(0x15,sf); + xsb->simple_cues_count = read_s16(0x13, sf); + xsb->complex_cues_count = read_s16(0x15, sf); /* 17(2): unknown count? */ /* 19(2): element count? (often simple+complex cues, but may be more) */ - xsb->wavebanks_count = read_s8 (0x1b,sf); - xsb->sounds_count = read_s16(0x1c,sf); - xsb->cue_names_size = read_s32(0x1e,sf); - xsb->simple_cues_offset = read_s32(0x22,sf); - xsb->complex_cues_offset = read_s32(0x26,sf); - xsb->cue_names_offset = read_s32(0x2a,sf); + xsb->wavebanks_count = read_s8 (0x1b, sf); + xsb->sounds_count = read_s16(0x1c, sf); + xsb->cue_names_size = read_s32(0x1e, sf); + xsb->simple_cues_offset = read_s32(0x22, sf); + xsb->complex_cues_offset = read_s32(0x26, sf); + xsb->cue_names_offset = read_s32(0x2a, sf); /* 0x2E(4): unknown offset */ /* 0x32(4): variation tables offset */ /* 0x36(4): unknown offset */ - xsb->wavebanks_offset = read_s32(0x3a,sf); + xsb->wavebanks_offset = read_s32(0x3a, sf); /* 0x3E(4): cue name hash table offset (16b each) */ - xsb->nameoffsets_offset = read_s32(0x42,sf); - xsb->sounds_offset = read_s32(0x46,sf); + xsb->nameoffsets_offset = read_s32(0x42, sf); + xsb->sounds_offset = read_s32(0x46, sf); /* 4a(64): xsb name */ xsb->wavebanks_name_size = 0x40; @@ -692,7 +844,7 @@ static int parse_xsb(xsb_header * xsb, STREAMFILE *sf, char *xwb_wavebank_name) offset = xsb->wavebanks_offset; for (i = 0; i < xsb->wavebanks_count; i++) { - read_string(xsb_wavebank_name,xsb->wavebanks_name_size, offset,sf); + read_string(xsb_wavebank_name,xsb->wavebanks_name_size, offset, sf); //;VGM_LOG("XSB wavebanks: bank %i=%s\n", i, wavebank_name); if (strcasecmp(xsb_wavebank_name, xwb_wavebank_name)==0) { //;VGM_LOG("XSB banks: current xwb is wavebank %i=%s\n", i, xsb_wavebank_name); @@ -705,17 +857,17 @@ static int parse_xsb(xsb_header * xsb, STREAMFILE *sf, char *xwb_wavebank_name) //;VGM_LOG("xsb: selected wavebank=%i\n", xsb->selected_wavebank); if (xsb->selected_wavebank == -1) { VGM_LOG("XSB: current wavebank not found, selecting first\n"); - xsb->selected_wavebank = 0; //todo goto fail? + xsb->selected_wavebank = 0; } } /* find cue pointing to stream */ if (xsb->version <= XSB_XACT1_2_MAX) { - parse_xsb_cues_old(xsb, sf); + parse_xsb_old_cues(xsb, sf); } else { - parse_xsb_cues_new(xsb, sf); + parse_xsb_cues(xsb, sf); } return 1; @@ -739,6 +891,12 @@ static STREAMFILE * open_xsb_filename_pair(STREAMFILE *streamXwb) { {"StreamBank_*.xwb","SoundBank_*.xsb"}, /* Ginga Force (X360) */ {"WaveBank_*.xwb","SoundBank_*.xsb"}, /* Ginga Force (X360) */ {"*_WB.xwb","*_SB.xsb"}, /* Ninja Blade (X360) */ + {"*_WB.xwb","*_SB.xsb"}, /* Ninja Blade (X360) */ + {"CA_NightMusic.xwb","CAMusic.xsb"}, /* Psychonauts (Xbox) */ + {"CAJAMusic.xwb","CAMusic.xsb"}, /* "" */ + {"STFX.xwb","CommonMusic.xsb"}, /* "" */ + {"CALI_NightFX.xwb","CAFX.xsb"}, /* "" */ + /* Psychonauts has a bunch more pairs for sfx too, improve */ {"*.xwb","*.xsb"}, /* default */ }; int i; diff --git a/Frameworks/vgmstream/vgmstream/src/streamfile.c b/Frameworks/vgmstream/vgmstream/src/streamfile.c index 6323e5276..b9b90d50d 100644 --- a/Frameworks/vgmstream/vgmstream/src/streamfile.c +++ b/Frameworks/vgmstream/vgmstream/src/streamfile.c @@ -29,6 +29,8 @@ static size_t read_stdio(STDIO_STREAMFILE *streamfile, uint8_t *dst, off_t offse if (!streamfile->infile || !dst || length <= 0 || offset < 0) return 0; + //;VGM_LOG("STDIO: read %lx + %x (buf %lx + %x)\n", offset, length, streamfile->buffer_offset, streamfile->validsize); + /* is the part of the requested length in the buffer? */ if (offset >= streamfile->buffer_offset && offset < streamfile->buffer_offset + streamfile->validsize) { size_t length_to_read; @@ -38,6 +40,8 @@ static size_t read_stdio(STDIO_STREAMFILE *streamfile, uint8_t *dst, off_t offse if (length_to_read > length) length_to_read = length; + //;VGM_LOG("STDIO: copy buf %lx + %x (+ %x) (buf %lx + %x)\n", offset, length_to_read, (length - length_to_read), streamfile->buffer_offset, streamfile->validsize); + memcpy(dst, streamfile->buffer + offset_into_buffer, length_to_read); length_read_total += length_to_read; length -= length_to_read; @@ -46,8 +50,10 @@ static size_t read_stdio(STDIO_STREAMFILE *streamfile, uint8_t *dst, off_t offse } #ifdef VGM_DEBUG_OUTPUT - if (offset < streamfile->buffer_offset) { + if (offset < streamfile->buffer_offset && length > 0) { VGM_LOG("STDIO: rebuffer, requested %lx vs %lx (sf %x)\n", offset, streamfile->buffer_offset, (uint32_t)streamfile); + //streamfile->rebuffer++; + //if (rebuffer > N) ... } #endif @@ -78,6 +84,7 @@ static size_t read_stdio(STDIO_STREAMFILE *streamfile, uint8_t *dst, off_t offse /* fill the buffer (offset now is beyond buffer_offset) */ streamfile->buffer_offset = offset; streamfile->validsize = fread(streamfile->buffer, sizeof(uint8_t), streamfile->buffersize, streamfile->infile); + //;VGM_LOG("STDIO: read buf %lx + %x\n", streamfile->buffer_offset, streamfile->validsize); /* decide how much must be read this time */ if (length > streamfile->buffersize) @@ -126,20 +133,21 @@ static STREAMFILE* open_stdio(STDIO_STREAMFILE *streamfile, const char * const f return NULL; #if !defined (__ANDROID__) - // if same name, duplicate the file pointer we already have open + /* if same name, duplicate the file descriptor we already have open */ if (streamfile->infile && !strcmp(streamfile->name,filename)) { - int newfd; - FILE *newfile; - STREAMFILE *new_sf; + int new_fd; + FILE *new_file = NULL; - if ( ((newfd = dup(fileno(streamfile->infile))) >= 0) && (newfile = fdopen(newfd, "rb")) ) { - new_sf = open_stdio_streamfile_buffer_by_file(newfile, filename, buffersize); - if (new_sf) { + if (((new_fd = dup(fileno(streamfile->infile))) >= 0) && (new_file = fdopen(new_fd, "rb"))) { + STREAMFILE *new_sf = open_stdio_streamfile_buffer_by_file(new_file, filename, buffersize); + if (new_sf) return new_sf; - } - // failure, close it and try the default path (which will probably fail a second time) - fclose(newfile); + fclose(new_file); } + if (new_fd >= 0 && !new_file) + close(new_fd); /* fdopen may fail when opening too many files */ + + /* on failure just close and try the default path (which will probably fail a second time) */ } #endif // a normal open, open a new file @@ -981,6 +989,27 @@ fail: if (buf) buf[0] = '\0'; return 0; } +size_t read_string_utf16le(char *buf, size_t buf_size, off_t offset, STREAMFILE *sf) { + size_t pos, offpos; + + for (pos = 0, offpos = 0; pos < buf_size; pos++, offpos += 2) { + char c = read_u16le(offset + offpos, sf) & 0xFF; /* lower byte for now */ + if (buf) buf[pos] = c; + if (c == '\0') + return pos; + if (pos+1 == buf_size) { /* null at maxsize and don't validate (expected to be garbage) */ + if (buf) buf[pos] = '\0'; + return buf_size; + } + if (c < 0x20 || (uint8_t)c > 0xA5) + goto fail; + } + +fail: + if (buf) buf[0] = '\0'; + return 0; +} + size_t read_key_file(uint8_t *buf, size_t buf_size, STREAMFILE *sf) { diff --git a/Frameworks/vgmstream/vgmstream/src/streamfile.h b/Frameworks/vgmstream/vgmstream/src/streamfile.h index e66564252..0e184fec2 100644 --- a/Frameworks/vgmstream/vgmstream/src/streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/streamfile.h @@ -45,8 +45,6 @@ #define fseeko fseek #endif -#define STREAMFILE_DEFAULT_BUFFER_SIZE 0x8000 - #ifndef DIR_SEPARATOR #if defined (_WIN32) || defined (WIN32) #define DIR_SEPARATOR '\\' @@ -55,6 +53,14 @@ #endif #endif +/* Streamfiles normally use an internal buffer to increase performance, configurable + * but usually of this size. Lower increases the number of freads/system calls (slower). + * However some formats need to jump around causing more buffer trashing than usual, + * higher may needlessly read data that may be going to be trashed. + * + * Value can be adjusted freely but 8k is a good enough compromise. */ +#define STREAMFILE_DEFAULT_BUFFER_SIZE 0x8000 + /* struct representing a file with callbacks. Code should use STREAMFILEs and not std C functions * to do file operations, as plugins may need to provide their own callbacks. * Reads from arbitrary offsets, meaning internally may need fseek equivalents during reads. */ @@ -202,30 +208,81 @@ static inline int8_t read_8bit(off_t offset, STREAMFILE * streamfile) { } /* alias of the above */ -static inline int8_t read_s8(off_t offset, STREAMFILE * streamfile) { return read_8bit(offset, streamfile); } -static inline uint8_t read_u8(off_t offset, STREAMFILE * streamfile) { return (uint8_t)read_8bit(offset, streamfile); } -static inline int16_t read_s16le(off_t offset, STREAMFILE * streamfile) { return read_16bitLE(offset, streamfile); } -static inline uint16_t read_u16le(off_t offset, STREAMFILE * streamfile) { return (uint16_t)read_16bitLE(offset, streamfile); } -static inline int16_t read_s16be(off_t offset, STREAMFILE * streamfile) { return read_16bitBE(offset, streamfile); } -static inline uint16_t read_u16be(off_t offset, STREAMFILE * streamfile) { return (uint16_t)read_16bitBE(offset, streamfile); } -static inline int32_t read_s32le(off_t offset, STREAMFILE * streamfile) { return read_32bitLE(offset, streamfile); } -static inline uint32_t read_u32le(off_t offset, STREAMFILE * streamfile) { return (uint32_t)read_32bitLE(offset, streamfile); } -static inline int32_t read_s32be(off_t offset, STREAMFILE * streamfile) { return read_32bitBE(offset, streamfile); } -static inline uint32_t read_u32be(off_t offset, STREAMFILE * streamfile) { return (uint32_t)read_32bitBE(offset, streamfile); } -static inline int64_t read_s64be(off_t offset, STREAMFILE * streamfile) { return read_64bitBE(offset, streamfile); } -static inline uint64_t read_u64be(off_t offset, STREAMFILE * streamfile) { return (uint64_t)read_64bitBE(offset, streamfile); } -static inline int64_t read_s64le(off_t offset, STREAMFILE * streamfile) { return read_64bitLE(offset, streamfile); } -static inline uint64_t read_u64le(off_t offset, STREAMFILE * streamfile) { return (uint64_t)read_64bitLE(offset, streamfile); } +static inline int8_t read_s8 (off_t offset, STREAMFILE *sf) { return read_8bit(offset, sf); } +static inline uint8_t read_u8 (off_t offset, STREAMFILE *sf) { return (uint8_t) read_8bit(offset, sf); } +static inline int16_t read_s16le(off_t offset, STREAMFILE *sf) { return read_16bitLE(offset, sf); } +static inline uint16_t read_u16le(off_t offset, STREAMFILE *sf) { return (uint16_t)read_16bitLE(offset, sf); } +static inline int16_t read_s16be(off_t offset, STREAMFILE *sf) { return read_16bitBE(offset, sf); } +static inline uint16_t read_u16be(off_t offset, STREAMFILE *sf) { return (uint16_t)read_16bitBE(offset, sf); } +static inline int32_t read_s32le(off_t offset, STREAMFILE *sf) { return read_32bitLE(offset, sf); } +static inline uint32_t read_u32le(off_t offset, STREAMFILE *sf) { return (uint32_t)read_32bitLE(offset, sf); } +static inline int32_t read_s32be(off_t offset, STREAMFILE *sf) { return read_32bitBE(offset, sf); } +static inline uint32_t read_u32be(off_t offset, STREAMFILE *sf) { return (uint32_t)read_32bitBE(offset, sf); } +static inline int64_t read_s64be(off_t offset, STREAMFILE *sf) { return read_64bitBE(offset, sf); } +static inline uint64_t read_u64be(off_t offset, STREAMFILE *sf) { return (uint64_t)read_64bitBE(offset, sf); } +static inline int64_t read_s64le(off_t offset, STREAMFILE *sf) { return read_64bitLE(offset, sf); } +static inline uint64_t read_u64le(off_t offset, STREAMFILE *sf) { return (uint64_t)read_64bitLE(offset, sf); } -#if 0 //todo improve + test + simplify code (maybe not inline?) -static inline float read_f32be(off_t offset, STREAMFILE * streamfile) { - uint32_t sample_int = read_s32be(offset,streamfile); +/* The recommended int-to-float type punning in C is through union, but pointer casting + * works too (though less portable due to aliasing rules?). For C++ memcpy seems + * recommended. Both work in GCC and VS2015+ (not sure about older, ifdef as needed). */ +static inline float read_f32be(off_t offset, STREAMFILE *sf) { + union { + uint32_t u32; + float f32; + } temp; + temp.u32 = read_u32be(offset, sf); + return temp.f32; +} +static inline float read_f32le(off_t offset, STREAMFILE * sf) { + union { + uint32_t u32; + float f32; + } temp; + temp.u32 = read_u32le(offset, sf); + return temp.f32; +} +#if 0 +static inline float read_f32be_p(off_t offset, STREAMFILE *sf) { + uint32_t sample_int = read_u32be(offset, sf); float* sample_float = (float*)&sample_int; return *sample_float; } -static inline float read_f32le(off_t offset, STREAMFILE * streamfile) { - ... +static inline float read_f32be_m(off_t offset, STREAMFILE *sf) { + uint32_t sample_int = read_u32be(offset, sf); + float sample_float; + memcpy(&sample_float, &sample_int, sizeof(uint32_t)); + return sample_float; } +#endif +#if 0 +/* collection of callbacks for quick access */ +typedef struct sf_reader { + int32_t (*read_s32)(off_t,STREAMFILE*); //maybe s32 + float (*read_f32)(off_t,STREAMFILE*); + /* ... */ +} sf_reader; + +void init_reader(sf_reader *r, int big_endian); +/* ... */ +void sf_reader_init(sf_reader *r, int big_endian) { + memset(r, 0, sizeof(sf_reader)); + if (big_endian) { + r->read_s32 = read_s32be; + r->read_f32 = read_f32be; + } + else { + r->read_s32 = read_s32le; + r->read_f32 = read_f32le; + } +} +/* sf_reader r; + * ... + * sf_reader_init(&r, big_endian); + * val = r.read_s32; //maybe r.s32? + */ +#endif +#if 0 //todo improve + test + simplify code (maybe not inline?) static inline int read_s4h(off_t offset, STREAMFILE * streamfile) { uint8_t byte = read_u8(offset, streamfile); return get_nibble_signed(byte, 1); @@ -245,6 +302,7 @@ static inline int min_s32(int32_t a, int32_t b) { return a < b ? a : b; } //align32, align16, clamp16, etc #endif +//TODO: maybe move to streamfile.c /* guess byte endianness from a given value, return true if big endian and false if little endian */ static inline int guess_endianness16bit(off_t offset, STREAMFILE * streamfile) { uint8_t buf[0x02]; @@ -272,6 +330,8 @@ size_t read_line(char *buf, int buf_size, off_t offset, STREAMFILE *sf, int *p_l /* reads a c-string (ANSI only), up to bufsize or NULL, returning size. buf is optional (works as get_string_size). */ size_t read_string(char *buf, size_t buf_size, off_t offset, STREAMFILE *sf); +/* reads a UTF16 string... but actually only as ANSI (discards the upper byte) */ +size_t read_string_utf16le(char *buf, size_t buf_size, off_t offset, STREAMFILE *sf); /* Opens a file containing decryption keys and copies to buffer. * Tries "(name.ext)key" (per song), "(.ext)key" (per folder) keynames. diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.c b/Frameworks/vgmstream/vgmstream/src/vgmstream.c index 4bec02e99..8d27fb097 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.c +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.c @@ -51,6 +51,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_ps2_mic, init_vgmstream_ngc_dsp_std_int, init_vgmstream_vag, + init_vgmstream_vag_aaap, init_vgmstream_seb, init_vgmstream_ps2_ild, init_vgmstream_ps2_pnb, @@ -221,7 +222,6 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_pona_3do, init_vgmstream_pona_psx, init_vgmstream_xbox_hlwav, - init_vgmstream_stx, init_vgmstream_myspd, init_vgmstream_his, init_vgmstream_ps2_ast, @@ -481,6 +481,8 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_bmp_konami, init_vgmstream_opus_opusnx, init_vgmstream_opus_sqex, + init_vgmstream_isb, + init_vgmstream_xssb, /* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */ init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */ @@ -2444,13 +2446,13 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *streamFile, VGMSTREAM*(*init_vgmstream_function)(STREAMFILE *)) { /* filename search pairs for dual file stereo */ static const char * const dfs_pairs[][2] = { - {"L","R"}, - {"l","r"}, - {"left","right"}, - {"Left","Right"}, + {"L","R"}, /* most common in .dsp and .vag */ + {"l","r"}, /* same */ + {"left","right"}, /* Freaky Flyers (GC) .adp, Velocity (PSP) .vag, Hyper Fighters (Wii) .dsp */ + {"Left","Right"}, /* Geometry Wars: Galaxies (Wii) .dsp */ {".V0",".V1"}, /* Homura (PS2) */ {".L",".R"}, /* Crash Nitro Racing (PS2), Gradius V (PS2) */ - {"_0","_1"}, /* fake for Homura/unneeded? */ + {"_0.dsp","_1.dsp"}, /* Wario World (GC) */ {".adpcm","_NxEncoderOut_.adpcm"}, /* Kill la Kill: IF (Switch) */ //todo can't match R>L }; char new_filename[PATH_LIMIT]; @@ -2470,7 +2472,7 @@ static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *strea //todo other layouts work but some stereo codecs do weird things //if (opened_vgmstream->layout != layout_none) return; - get_streamfile_name(streamFile,new_filename,sizeof(new_filename)); + get_streamfile_name(streamFile, new_filename, sizeof(new_filename)); filename_len = strlen(new_filename); if (filename_len < 2) return; @@ -2493,15 +2495,15 @@ static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *strea //;VGM_LOG("DFS: l=%s, r=%s\n", this_suffix,that_suffix); /* if suffix matches paste opposite suffix (+ terminator) to extension pointer, thus to new_filename */ - if (this_suffix[0] == '.' && extension_len == this_suffix_len) { /* same extension */ - //;VGM_LOG("DFS: same ext %s vs %s len %i\n", extension, this_suffix, this_suffix_len); - if (memcmp(extension,this_suffix,this_suffix_len) == 0) { + if (filename_len > this_suffix_len && strchr(this_suffix, '.') != NULL) { /* same suffix with extension */ + //;VGM_LOG("DFS: suf+ext %s vs %s len %i\n", new_filename, this_suffix, this_suffix_len); + if (memcmp(new_filename + (filename_len - this_suffix_len), this_suffix, this_suffix_len) == 0) { dfs_pair = j; - memcpy (extension, that_suffix,that_suffix_len+1); + memcpy (new_filename + (filename_len - this_suffix_len), that_suffix,that_suffix_len+1); } } - else if (filename_len - extension_len > this_suffix_len) { /* same suffix (without extension) */ - //;VGM_LOG("DFS: same suf %s vs %s len %i\n", extension - this_suffix_len, this_suffix, this_suffix_len); + else if (filename_len - extension_len > this_suffix_len) { /* same suffix without extension */ + //;VGM_LOG("DFS: suf-ext %s vs %s len %i\n", extension - this_suffix_len, this_suffix, this_suffix_len); if (memcmp(extension - this_suffix_len, this_suffix,this_suffix_len) == 0) { dfs_pair = j; memmove(extension + that_suffix_len - this_suffix_len, extension,extension_len+1); /* move old extension to end */ @@ -2517,7 +2519,7 @@ static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *strea //;VGM_LOG("DFS: match %i filename=%s\n", dfs_pair, new_filename); /* try to init other channel (new_filename now has the opposite name) */ - dual_streamFile = open_streamfile(streamFile,new_filename); + dual_streamFile = open_streamfile(streamFile, new_filename); if (!dual_streamFile) goto fail; new_vgmstream = init_vgmstream_function(dual_streamFile); /* use the init that just worked, no other should work */ @@ -2819,7 +2821,8 @@ int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t s } /* if blocked layout (implicit) use multiple streamfiles; using only one leads to - * lots of buffer-trashing, with all the jumping around in the block layout */ + * lots of buffer-trashing, with all the jumping around in the block layout + * (this increases total of data read but still seems faster) */ if (vgmstream->layout_type != layout_none && vgmstream->layout_type != layout_interleave) { use_streamfile_per_channel = 1; } @@ -2860,7 +2863,8 @@ int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t s offset = start_offset + vgmstream->interleave_block_size*ch; } - /* open new one if needed */ + /* open new one if needed, useful to avoid jumping around when each channel data is too apart + * (don't use when data is close as it'd make buffers read the full file multiple times) */ if (use_streamfile_per_channel) { file = open_streamfile(streamFile,filename); if (!file) goto fail; diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.h b/Frameworks/vgmstream/vgmstream/src/vgmstream.h index cb27e6189..488060900 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.h +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.h @@ -332,7 +332,6 @@ typedef enum { meta_NDS_SWAV, /* Asphalt Urban GT 1 & 2 */ meta_NDS_RRDS, /* Ridge Racer DS */ meta_WII_BNS, /* Wii BNS Banner Sound (similar to RSTM) */ - meta_STX, /* Pikmin .stx */ meta_WIIU_BTSND, /* Wii U Boot Sound */ meta_ADX_03, /* CRI ADX "type 03" */ @@ -364,6 +363,7 @@ typedef enum { meta_PS2_VAGi, /* VAGi Interleaved File */ meta_PS2_VAGp, /* VAGp Mono File */ meta_PS2_pGAV, /* VAGp with Little Endian Header */ + meta_PS2_VAGp_AAAP, /* Acclaim Austin Audio VAG header */ meta_SEB, meta_STR_WAV, /* Blitz Games STR+WAV files */ meta_PS2_ILD, /* ILD File */ @@ -726,6 +726,8 @@ typedef enum { meta_XMV_VALVE, meta_UBI_HX, meta_BMP_KONAMI, + meta_ISB, + meta_XSSB, } meta_t; @@ -870,14 +872,14 @@ typedef struct { int32_t samples_into_block; /* number of samples into the current block/interleave/segment/etc */ off_t current_block_offset; /* start of this block (offset of block header) */ size_t current_block_size; /* size in usable bytes of the block we're in now (used to calculate num_samples per block) */ - size_t current_block_samples; /* size in samples of the block we're in now (used over current_block_size if possible) */ + int32_t current_block_samples; /* size in samples of the block we're in now (used over current_block_size if possible) */ off_t next_block_offset; /* offset of header of the next block */ /* layout/block loop state */ int32_t loop_sample; /* saved from current_sample (same as loop_start_sample, but more state-like) */ int32_t loop_samples_into_block;/* saved from samples_into_block */ off_t loop_block_offset; /* saved from current_block_offset */ size_t loop_block_size; /* saved from current_block_size */ - size_t loop_block_samples; /* saved from current_block_samples */ + int32_t loop_block_samples; /* saved from current_block_samples */ off_t loop_next_block_offset; /* saved from next_block_offset */ /* loop state */