Updated VGMStream to r1050-2622-g72b9f540

CQTexperiment
Christopher Snowhill 2019-11-11 00:25:59 -08:00
parent 258a88464a
commit 0a9c7693d4
54 changed files with 2261 additions and 1364 deletions

View File

@ -401,7 +401,6 @@
836F703018BDC2190095E648 /* sqex_scd.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EF418BDC2190095E648 /* sqex_scd.c */; }; 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 */; }; 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 */; }; 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 */; }; 836F703518BDC2190095E648 /* svs.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EF918BDC2190095E648 /* svs.c */; };
836F703618BDC2190095E648 /* thp.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EFA18BDC2190095E648 /* thp.c */; }; 836F703618BDC2190095E648 /* thp.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EFA18BDC2190095E648 /* thp.c */; };
836F703718BDC2190095E648 /* tun.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EFB18BDC2190095E648 /* tun.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 */; }; 83AA5D251F6E2F9C0020821C /* hca_keys.h in Headers */ = {isa = PBXBuildFile; fileRef = 83AA5D211F6E2F9C0020821C /* hca_keys.h */; };
83AA5D271F6E2F9C0020821C /* stm.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AA5D231F6E2F9C0020821C /* stm.c */; }; 83AA5D271F6E2F9C0020821C /* stm.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AA5D231F6E2F9C0020821C /* stm.c */; };
83AB8C761E8072A100086084 /* x360_ast.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AB8C741E8072A100086084 /* x360_ast.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 */; }; 83BAFB6C19F45EB3005DAB60 /* bfstm.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BAFB6B19F45EB3005DAB60 /* bfstm.c */; };
83C7280F22BC893D00678B4A /* xwb_xsb.h in Headers */ = {isa = PBXBuildFile; fileRef = 83C727FB22BC893800678B4A /* xwb_xsb.h */; }; 83C7280F22BC893D00678B4A /* xwb_xsb.h in Headers */ = {isa = PBXBuildFile; fileRef = 83C727FB22BC893800678B4A /* xwb_xsb.h */; };
83C7281022BC893D00678B4A /* nps.c in Sources */ = {isa = PBXBuildFile; fileRef = 83C727FC22BC893900678B4A /* nps.c */; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 83C727FC22BC893900678B4A /* nps.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nps.c; sourceTree = "<group>"; };
@ -1600,6 +1604,7 @@
83299FCF1E7660C7003A3242 /* dsp_adx.c */, 83299FCF1E7660C7003A3242 /* dsp_adx.c */,
836F6E4418BDC2180095E648 /* dsp_bdsp.c */, 836F6E4418BDC2180095E648 /* dsp_bdsp.c */,
8349A8FF1FE6258000E26435 /* ea_1snh.c */, 8349A8FF1FE6258000E26435 /* ea_1snh.c */,
83AFABBA23795202002F3947 /* ea_eaac_opus_streamfile.h */,
8306B0BD2098458B000302D4 /* ea_eaac_streamfile.h */, 8306B0BD2098458B000302D4 /* ea_eaac_streamfile.h */,
8349A8F71FE6257E00E26435 /* ea_eaac.c */, 8349A8F71FE6257E00E26435 /* ea_eaac.c */,
830165981F256BD000CA0941 /* ea_schl_fixed.c */, 830165981F256BD000CA0941 /* ea_schl_fixed.c */,
@ -1642,6 +1647,7 @@
837CEAE623487F2B00E62A4A /* ima.c */, 837CEAE623487F2B00E62A4A /* ima.c */,
832BF81121E05149006F50F1 /* imc.c */, 832BF81121E05149006F50F1 /* imc.c */,
836F6E5518BDC2180095E648 /* ios_psnd.c */, 836F6E5518BDC2180095E648 /* ios_psnd.c */,
83AFABBB23795202002F3947 /* isb.c */,
836F6E5618BDC2180095E648 /* ish_isd.c */, 836F6E5618BDC2180095E648 /* ish_isd.c */,
836F6E5718BDC2180095E648 /* ivaud.c */, 836F6E5718BDC2180095E648 /* ivaud.c */,
836F6E5818BDC2180095E648 /* ivb.c */, 836F6E5818BDC2180095E648 /* ivb.c */,
@ -1849,7 +1855,6 @@
836F6EF718BDC2190095E648 /* str_snds.c */, 836F6EF718BDC2190095E648 /* str_snds.c */,
834FE0C2215C79E6000A5D3D /* str_wav.c */, 834FE0C2215C79E6000A5D3D /* str_wav.c */,
83C7280722BC893B00678B4A /* strm_abylight.c */, 83C7280722BC893B00678B4A /* strm_abylight.c */,
836F6EF818BDC2190095E648 /* stx.c */,
834FE0D7215C79EA000A5D3D /* svg.c */, 834FE0D7215C79EA000A5D3D /* svg.c */,
836F6EF918BDC2190095E648 /* svs.c */, 836F6EF918BDC2190095E648 /* svs.c */,
831BA6121EAC61A500CF89B0 /* sxd.c */, 831BA6121EAC61A500CF89B0 /* sxd.c */,
@ -1923,6 +1928,7 @@
832BF80A21E05148006F50F1 /* xpcm.c */, 832BF80A21E05148006F50F1 /* xpcm.c */,
832BF80C21E05148006F50F1 /* xps.c */, 832BF80C21E05148006F50F1 /* xps.c */,
836F6F1218BDC2190095E648 /* xss.c */, 836F6F1218BDC2190095E648 /* xss.c */,
83AFABB923795201002F3947 /* xssb.c */,
834FE0C6215C79E7000A5D3D /* xvag_streamfile.h */, 834FE0C6215C79E7000A5D3D /* xvag_streamfile.h */,
83345A4E1F8AEB2800B2EAA4 /* xvag.c */, 83345A4E1F8AEB2800B2EAA4 /* xvag.c */,
837CEADC23487F2900E62A4A /* xvas.c */, 837CEADC23487F2900E62A4A /* xvas.c */,
@ -2023,6 +2029,7 @@
8306B0D820984590000302D4 /* ea_eaac_streamfile.h in Headers */, 8306B0D820984590000302D4 /* ea_eaac_streamfile.h in Headers */,
837CEB0523487F2C00E62A4A /* sqex_sead_streamfile.h in Headers */, 837CEB0523487F2C00E62A4A /* sqex_sead_streamfile.h in Headers */,
8306B0E820984590000302D4 /* opus_interleave_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 */, 83C7281822BC893D00678B4A /* sfh_streamfile.h in Headers */,
834FE0EF215C79ED000A5D3D /* xvag_streamfile.h in Headers */, 834FE0EF215C79ED000A5D3D /* xvag_streamfile.h in Headers */,
8349A90C1FE6258200E26435 /* sqex_scd_streamfile.h in Headers */, 8349A90C1FE6258200E26435 /* sqex_scd_streamfile.h in Headers */,
@ -2249,7 +2256,6 @@
836F6FA118BDC2190095E648 /* myspd.c in Sources */, 836F6FA118BDC2190095E648 /* myspd.c in Sources */,
837CEB0123487F2C00E62A4A /* xmu.c in Sources */, 837CEB0123487F2C00E62A4A /* xmu.c in Sources */,
836F6FD718BDC2190095E648 /* ps2_filp.c in Sources */, 836F6FD718BDC2190095E648 /* ps2_filp.c in Sources */,
836F703418BDC2190095E648 /* stx.c in Sources */,
83FF0EBC1E93282100C58054 /* wwise.c in Sources */, 83FF0EBC1E93282100C58054 /* wwise.c in Sources */,
836F6F7018BDC2190095E648 /* apple_caff.c in Sources */, 836F6F7018BDC2190095E648 /* apple_caff.c in Sources */,
836F700018BDC2190095E648 /* ps2_svag.c in Sources */, 836F700018BDC2190095E648 /* ps2_svag.c in Sources */,
@ -2295,6 +2301,7 @@
8349A8EA1FE6253900E26435 /* blocked_ea_schl.c in Sources */, 8349A8EA1FE6253900E26435 /* blocked_ea_schl.c in Sources */,
836F705118BDC2190095E648 /* zsd.c in Sources */, 836F705118BDC2190095E648 /* zsd.c in Sources */,
8349A91F1FE6258200E26435 /* naac.c in Sources */, 8349A91F1FE6258200E26435 /* naac.c in Sources */,
83AFABBE23795202002F3947 /* isb.c in Sources */,
836F6FD218BDC2190095E648 /* ps2_bmdx.c in Sources */, 836F6FD218BDC2190095E648 /* ps2_bmdx.c in Sources */,
836F703C18BDC2190095E648 /* wii_bns.c in Sources */, 836F703C18BDC2190095E648 /* wii_bns.c in Sources */,
830EBE132004656E0023AA10 /* xnb.c in Sources */, 830EBE132004656E0023AA10 /* xnb.c in Sources */,
@ -2594,6 +2601,7 @@
83C7282922BC8C1500678B4A /* mixing.c in Sources */, 83C7282922BC8C1500678B4A /* mixing.c in Sources */,
8375737621F950ED00F01AF5 /* gin.c in Sources */, 8375737621F950ED00F01AF5 /* gin.c in Sources */,
836F6FC918BDC2190095E648 /* ps2_2pfs.c in Sources */, 836F6FC918BDC2190095E648 /* ps2_2pfs.c in Sources */,
83AFABBC23795202002F3947 /* xssb.c in Sources */,
836F704818BDC2190095E648 /* xbox_ims.c in Sources */, 836F704818BDC2190095E648 /* xbox_ims.c in Sources */,
837CEAF623487F2C00E62A4A /* mzrt.c in Sources */, 837CEAF623487F2C00E62A4A /* mzrt.c in Sources */,
836F6F7518BDC2190095E648 /* bnsf.c in Sources */, 836F6F7518BDC2190095E648 /* bnsf.c in Sources */,

View File

@ -2,52 +2,53 @@
/* Decodes Argonaut's ASF ADPCM codec, used in some of their PC games. /* Decodes Argonaut's ASF ADPCM codec, used in some of their PC games.
* Algorithm should be accurate (reverse engineered from asfcodec.adl DLL). */ * Reverse engineered from asfcodec.adl DLL. */
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) {
uint8_t frame[0x11] = {0};
off_t frame_offset; off_t frame_offset;
int i, frames_in, sample_count = 0; int i, frames_in, sample_count = 0;
size_t bytes_per_frame, samples_per_frame; size_t bytes_per_frame, samples_per_frame;
uint8_t shift, mode; int shift, mode;
int32_t hist1 = stream->adpcm_history1_32; int32_t hist1 = stream->adpcm_history1_32;
int32_t hist2 = stream->adpcm_history2_32; int32_t hist2 = stream->adpcm_history2_32;
/* external interleave (fixed size), mono */ /* external interleave (fixed size), mono */
bytes_per_frame = 0x11; bytes_per_frame = 0x11;
samples_per_frame = (bytes_per_frame - 0x01) * 2; samples_per_frame = (bytes_per_frame - 0x01) * 2;
frames_in = first_sample / samples_per_frame; 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 */ /* parse frame header */
frame_offset = stream->offset + bytes_per_frame*frames_in; frame_offset = stream->offset + bytes_per_frame*frames_in;
shift = ((uint8_t)read_8bit(frame_offset+0x00,stream->streamfile) >> 4) & 0xf; read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */
mode = ((uint8_t)read_8bit(frame_offset+0x00,stream->streamfile) >> 0) & 0xf; shift = (frame[0x00] >> 4) & 0xf;
mode = (frame[0x00] >> 0) & 0xf;
/* decode nibbles */ /* decode nibbles */
for (i = first_sample; i < first_sample + samples_to_do; i++) { for (i = first_sample; i < first_sample + samples_to_do; i++) {
int32_t new_sample; uint8_t nibbles = frame[0x01 + i/2];
uint8_t nibbles = (uint8_t)read_8bit(frame_offset+0x01 + i/2,stream->streamfile); int32_t sample;
new_sample = i&1 ? /* high nibble first */ sample = i&1 ? /* high nibble first */
get_low_nibble_signed(nibbles): get_low_nibble_signed(nibbles):
get_high_nibble_signed(nibbles); get_high_nibble_signed(nibbles);
/* move sample to upper nibble, then shift + 2 (IOW: shift + 6) */ sample = (sample << 4) << (shift + 2); /* move sample to upper nibble, then shift + 2 (IOW: shift + 6) */
new_sample = (new_sample << 4) << (shift + 2);
/* mode is checked as a flag, so there are 2 modes only, but lower nibble /* 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?) */ * may have other values at last frame (ex 0x02/09), could be control flags (loop related?) */
if (mode & 0x4) { /* ~filters: 2, -1 */ 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 */ 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)sample; /* must not clamp */
outbuf[sample_count] = (int16_t)new_sample;
sample_count += channelspacing; sample_count += channelspacing;
hist2 = hist1; hist2 = hist1;
hist1 = new_sample; hist1 = sample;
} }
stream->adpcm_history1_32 = hist1; stream->adpcm_history1_32 = hist1;

View File

@ -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); void dsp_read_hist(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing, int be);
/* ngc_dtk_decoder */ /* 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 */ /* 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 */ /* pcm_decoder */
void decode_pcm16le(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); 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); size_t aska_bytes_to_samples(size_t bytes, int channels);
/* nds_procyon_decoder */ /* 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 */ /* l5_555_decoder */
void decode_l5_555(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); 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); void decode_sassc(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
/* lsf_decode */ /* 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 */ /* 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 */ /* 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 */ /* mc3_decoder */
void decode_mc3(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); void decode_mc3(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
/* fadpcm_decoder */ /* 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 */ /* 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 */ /* 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 */ /* 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 */ /* derf_decoder */
void decode_derf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); 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); size_t oki_bytes_to_samples(size_t bytes, int channels);
/* ptadpcm_decoder */ /* 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); size_t ptadpcm_bytes_to_samples(size_t bytes, int channels, size_t frame_size);
/* ubi_adpcm_decoder */ /* ubi_adpcm_decoder */

View File

@ -10,37 +10,38 @@ static const int dsa_coefs[16] = {
/* Decodes Ocean DSA ADPCM codec from Last Rites (PC). /* Decodes Ocean DSA ADPCM codec from Last Rites (PC).
* Reverse engineered from daemon1's reverse engineering. */ * 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; off_t frame_offset;
int i, frames_in, sample_count = 0; int i, frames_in, sample_count = 0;
size_t bytes_per_frame, samples_per_frame; size_t bytes_per_frame, samples_per_frame;
uint8_t header; int index, shift, coef;
int shift, filter;
int32_t hist1 = stream->adpcm_history1_32; int32_t hist1 = stream->adpcm_history1_32;
/* external interleave (fixed size), mono */ /* external interleave (fixed size), mono */
bytes_per_frame = 0x08; bytes_per_frame = 0x08;
samples_per_frame = (bytes_per_frame - 0x01) * 2; samples_per_frame = (bytes_per_frame - 0x01) * 2;
frames_in = first_sample / samples_per_frame; 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 */ /* parse frame header */
frame_offset = stream->offset + bytes_per_frame*frames_in; frame_offset = stream->offset + bytes_per_frame * frames_in;
header = (uint8_t)read_8bit(frame_offset+0x00,stream->streamfile); read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */
shift = 0x0c - ((header >> 4) & 0xf); index = ((frame[0] >> 0) & 0xf);
filter = dsa_coefs[header & 0xf]; shift = 12 - ((frame[0] >> 4) & 0xf);
coef = dsa_coefs[index];
/* decode nibbles */ /* decode nibbles */
for (i = first_sample; i < first_sample + samples_to_do; i++) { for (i = first_sample; i < first_sample + samples_to_do; i++) {
uint8_t nibbles = frame[0x01 + i/2];
int32_t sample; int32_t sample;
uint8_t nibbles = (uint8_t)read_8bit(frame_offset+0x01 + i/2,stream->streamfile);
sample = i&1 ? /* high nibble first */ sample = i&1 ? /* high nibble first */
(nibbles >> 0) & 0xf : (nibbles >> 0) & 0xf :
(nibbles >> 4) & 0xf; (nibbles >> 4) & 0xf;
sample = ((int16_t)(sample << 12) >> shift); /* 16b sign extend + scale */
sample = ((int16_t)(sample << 0xC) >> shift); /* 16b sign extend + scale */ sample = sample + ((hist1 * coef) >> 16);
sample = sample + ((hist1 * filter) >> 0x10);
outbuf[sample_count] = (sample_t)(sample << 2); outbuf[sample_count] = (sample_t)(sample << 2);
sample_count += channelspacing; sample_count += channelspacing;

View File

@ -1,77 +1,74 @@
#include "coding.h" #include "coding.h"
/* FADPCM table */ /* tweaked XA/PSX coefs << 6 */
static const int8_t fadpcm_coefs[8][2] = { static const int8_t fadpcm_coefs[8][2] = {
{ 0 , 0 }, { 0, 0 },
{ 60 , 0 }, { 60, 0 },
{ 122 , 60 }, { 122, 60 },
{ 115 , 52 }, { 115, 52 },
{ 98 , 55 }, { 98, 55 },
{ 0 , 0 }, /* rest is 0s */
{ 0 , 0 },
{ 0 , 0 },
}; };
/* FMOD's FADPCM, basically XA/PSX ADPCM with a fancy header layout. /* FMOD's FADPCM, basically XA/PSX ADPCM with a fancy header layout.
* Code/layout could be simplified but tries to emulate FMOD's code. * Code/layout could be simplified but tries to emulate FMOD's code.
* Algorithm and tables debugged from their PC DLLs (byte-accurate). */ * 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; off_t frame_offset;
int i, j, k; int i, j, k, frames_in, sample_count = 0, samples_done = 0;
int block_samples, frames_in, samples_done = 0, sample_count = 0; size_t bytes_per_frame, samples_per_frame;
uint32_t coefs, shifts; uint32_t coefs, shifts;
int32_t hist1; //= stream->adpcm_history1_32; int32_t hist1; //= stream->adpcm_history1_32;
int32_t hist2; //= stream->adpcm_history2_32; int32_t hist2; //= stream->adpcm_history2_32;
/* external interleave (fixed size), mono */ /* external interleave (fixed size), mono */
block_samples = (0x8c - 0xc) * 2; bytes_per_frame = 0x8c;
frames_in = first_sample / block_samples; samples_per_frame = (bytes_per_frame - 0xc) * 2;
first_sample = first_sample % block_samples; frames_in = first_sample / samples_per_frame;
first_sample = first_sample % samples_per_frame;
frame_offset = stream->offset + 0x8c*frames_in;
/* parse 0xc header (header samples are not written to outbuf) */ /* parse 0xc header (header samples are not written to outbuf) */
coefs = read_32bitLE(frame_offset + 0x00, stream->streamfile); frame_offset = stream->offset + bytes_per_frame * frames_in;
shifts = read_32bitLE(frame_offset + 0x04, stream->streamfile); read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */
hist1 = read_16bitLE(frame_offset + 0x08, stream->streamfile); coefs = get_u32le(frame + 0x00);
hist2 = read_16bitLE(frame_offset + 0x0a, stream->streamfile); 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 */ /* decode nibbles, grouped in 8 sets of 0x10 * 0x04 * 2 */
for (i = 0; i < 8; i++) { for (i = 0; i < 8; i++) {
int32_t coef1, coef2, shift, coef_index, shift_factor; int index, shift, coef1, coef2;
off_t group_offset = frame_offset + 0x0c + 0x10*i;
/* each set has its own coefs/shifts (indexes > 7 are repeat, ex. 0x9 is 0x2) */ /* each set has its own coefs/shifts (indexes > 7 are repeat, ex. 0x9 is 0x2) */
coef_index = ((coefs >> i*4) & 0x0f) % 0x07; index = ((coefs >> i*4) & 0x0f) % 0x07;
shift_factor = (shifts >> i*4) & 0x0f; shift = (shifts >> i*4) & 0x0f;
coef1 = fadpcm_coefs[coef_index][0]; coef1 = fadpcm_coefs[index][0];
coef2 = fadpcm_coefs[coef_index][1]; coef2 = fadpcm_coefs[index][1];
shift = 0x16 - shift_factor; /* pre-adjust for 32b sign extend */ shift = 22 - shift; /* pre-adjust for 32b sign extend */
for (j = 0; j < 4; j++) { 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++) { for (k = 0; k < 8; k++) {
int32_t new_sample; int32_t sample;
new_sample = (nibbles >> k*4) & 0x0f; sample = (nibbles >> k*4) & 0x0f;
new_sample = (new_sample << 28) >> shift; /* 32b sign extend + scale */ sample = (sample << 28) >> shift; /* 32b sign extend + scale */
new_sample = (new_sample - hist2*coef2 + hist1*coef1); sample = (sample - hist2*coef2 + hist1*coef1) >> 6;
new_sample = new_sample >> 6; sample = clamp16(sample);
new_sample = clamp16(new_sample);
if (sample_count >= first_sample && samples_done < samples_to_do) { if (sample_count >= first_sample && samples_done < samples_to_do) {
outbuf[samples_done * channelspacing] = new_sample; outbuf[samples_done * channelspacing] = sample;
samples_done++; samples_done++;
} }
sample_count++; sample_count++;
hist2 = hist1; hist2 = hist1;
hist1 = new_sample; hist1 = sample;
} }
} }
} }

View File

@ -557,16 +557,17 @@ fail:
* float requirements, but C99 adds some faster-but-less-precise casting functions * 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 * 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. * 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) { static inline int float_to_int(float val) {
#if defined(_MSC_VER) && (_MSC_VER < 1900) /* VS2015 */ #if defined(_MSC_VER)
return (int)val; return (int)val;
#else #else
return lrintf(val); return lrintf(val);
#endif #endif
} }
static inline int double_to_int(double val) { static inline int double_to_int(double val) {
#if defined(_MSC_VER) && (_MSC_VER < 1900) /* VS2015 */ #if defined(_MSC_VER)
return (int)val; return (int)val;
#else #else
return lrint(val); /* returns long tho */ 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. * keep it simple for now until more tests are done.
* *
* in normal (interleaved) formats samples are laid out straight * 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 * 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: * alt float clamping:
* clamp_float(f32) * 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); copy_samples(data, outbuf, samples_to_get);
//samples_done += samples_to_get;
samples_to_do -= samples_to_get; samples_to_do -= samples_to_get;
outbuf += samples_to_get * channels; outbuf += samples_to_get * channels;
} }

View File

@ -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, /* 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 * 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 * 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) { if (is_at3) {
implicit_skip = 69; implicit_skip = 69;
} }

View File

@ -878,7 +878,7 @@ void decode_fsb_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample_t *
stream->adpcm_step_index = step_index; 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) { 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; int i, sample_count = 0, num_frame;
int32_t hist1 = stream->adpcm_history1_32; 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_history1_32 = hist1;
stream->adpcm_step_index = step_index; 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) */ /* 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) { void decode_awc_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {

View File

@ -1,50 +1,60 @@
#include "coding.h" #include "coding.h"
#include "../util.h" #include "../util.h"
/* lsf ADPCM, as seen in Fastlane Street Racing */
static const short lsf_coefs[5][2] = { /* tweaked XA/PSX coefs << 6 */
{0x73, -0x34}, static const short lsf_coefs[16][2] = {
{0, 0}, { 115, -52 },
{0x62, -0x37}, { 0, 0 },
{0x3C, 0}, { 98, -55 },
{0x7A, -0x3c} { 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) { void decode_lsf(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
int i=first_sample; uint8_t frame[0x1c] = {0};
int32_t sample_count; off_t frame_offset;
const int bytes_per_frame = 0x1c; int i, frames_in, sample_count = 0;
const int samples_per_frame = (bytes_per_frame-1)*2; int index, shift, coef1, coef2;
size_t bytes_per_frame, samples_per_frame;
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;
int32_t hist1 = stream->adpcm_history1_16; int32_t hist1 = stream->adpcm_history1_16;
int32_t hist2 = stream->adpcm_history2_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) { /* external interleave (fixed size), mono */
int sample_byte = read_8bit(framesin*bytes_per_frame+stream->offset+1+i/2,stream->streamfile); 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 = /* external interleave (fixed size), mono */
(hist1 * lsf_coefs[coef_idx][0] + frame_offset = stream->offset + bytes_per_frame * frames_in;
hist2 * lsf_coefs[coef_idx][1]) / 0x40; 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? /* decode nibbles */
get_high_nibble_signed(sample_byte): for (i = first_sample; i < first_sample + samples_to_do; i++) {
get_low_nibble_signed(sample_byte) uint8_t nibbles = frame[0x01 + i/2];
) * (1 << (12-scale)); 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; hist2 = hist1;
hist1 = prediction; hist1 = sample;
outbuf[sample_count] = prediction;
} }
stream->adpcm_history1_16 = hist1; stream->adpcm_history1_16 = hist1;

View File

@ -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; data->skip_samples = data->config.skip_samples; break;
case MPEG_STANDARD: case MPEG_STANDARD:
data->skip_samples = data->config.skip_samples; break; 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: default:
break; break;
} }
@ -234,7 +242,7 @@ fail:
* Gets info from a MPEG frame header at offset. Normally you would use mpg123_info but somehow * 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. * 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 */ /* index tables */
static const int versions[4] = { /* MPEG 2.5 */ 3, /* reserved */ -1, /* MPEG 2 */ 2, /* MPEG 1 */ 1 }; 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 }; 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 */ { 384, 1152, 576 } /* MPEG2.5 */
}; };
uint32_t header;
int idx, padding; int idx, padding;
memset(info, 0, sizeof(*info)); memset(info, 0, sizeof(*info));
header = read_32bitBE(offset, streamfile);
if ((header >> 21) != 0x7FF) /* 31-21: sync */ if ((header >> 21) != 0x7FF) /* 31-21: sync */
goto fail; goto fail;
@ -308,34 +313,36 @@ int mpeg_get_frame_info(STREAMFILE *streamfile, off_t offset, mpeg_frame_info *
fail: fail:
return 0; 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 offset = start_offset;
off_t max_offset = start_offset + bytes; 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; mpeg_frame_info info;
size_t prev_size = 0;
int cbr_count = 0;
int is_vbr = 0;
if (!streamFile) if (!sf)
return 0; return 0;
if (max_offset > get_streamfile_size(streamFile)) if (max_offset > get_streamfile_size(sf))
max_offset = get_streamfile_size(streamFile); max_offset = get_streamfile_size(sf);
/* MPEG may use VBR so must read all frames */ /* MPEG may use VBR so must read all frames */
while (offset < max_offset) { while (offset < max_offset) {
uint32_t header = read_u32be(offset+0x00, sf);
/* skip ID3v2 */ /* skip ID3v2 */
if ((read_32bitBE(offset+0x00, streamFile) & 0xFFFFFF00) == 0x49443300) { /* "ID3\0" */ if ((header & 0xFFFFFF00) == 0x49443300) { /* "ID3\0" */
size_t frame_size = 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 :/ */ /* this is how it's officially read :/ */
frame_size += read_8bit(offset+0x06, streamFile) << 21; frame_size += read_u8(offset+0x06, sf) << 21;
frame_size += read_8bit(offset+0x07, streamFile) << 14; frame_size += read_u8(offset+0x07, sf) << 14;
frame_size += read_8bit(offset+0x08, streamFile) << 7; frame_size += read_u8(offset+0x08, sf) << 7;
frame_size += read_8bit(offset+0x09, streamFile) << 0; frame_size += read_u8(offset+0x09, sf) << 0;
frame_size += 0x0a; frame_size += 0x0a;
if (flags & 0x10) /* footer? */ if (flags & 0x10) /* footer? */
frame_size += 0x0a; frame_size += 0x0a;
@ -344,28 +351,71 @@ size_t mpeg_get_samples(STREAMFILE *streamFile, off_t start_offset, size_t bytes
continue; continue;
} }
/* this may fail with unknown ID3 tags */ /* skip ID3v1 */
if (!mpeg_get_frame_info(streamFile, offset, &info)) if ((header & 0xFFFFFF00) == 0x54414700) { /* "TAG\0" */
break; ;VGM_LOG("MPEG: ID3v1 at %lx\n", offset);
offset += 0x80;
if (prev_size && prev_size != info.frame_size) { continue;
is_vbr = 1;
}
else if (!is_vbr) {
cbr_count++;
} }
if (cbr_count >= 10) { /* regular frame */
/* must be CBR, don't bother counting */ if (!mpeg_get_frame_info_h(header, &info)) {
samples = (bytes / info.frame_size) * info.frame_samples; VGM_LOG("MPEG: unknown frame at %lx\n", offset);
break; 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; offset += info.frame_size;
prev_size = info.frame_size; samples += info.frame_samples;
samples += info.frame_samples; /* header frames may be 0? */
} }
;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; return samples;
} }

View File

@ -112,12 +112,16 @@ int mpeg_custom_setup_init_ealayer3(STREAMFILE *streamfile, off_t start_offset,
ok = ealayer3_parse_frame(data, -1, &ib, &eaf); ok = ealayer3_parse_frame(data, -1, &ib, &eaf);
if (!ok) goto fail; 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; *coding_type = coding_MPEG_ealayer3;
data->channels_per_frame = eaf.channels; data->channels_per_frame = eaf.channels;
data->samples_per_frame = eaf.mpeg1 ? 1152 : 576; 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) */ /* encoder delay: EALayer3 handles this while decoding (skips samples as writes PCM blocks) */
return 1; return 1;
@ -133,6 +137,16 @@ int mpeg_custom_parse_frame_ealayer3(VGMSTREAMCHANNEL *stream, mpeg_codec_data *
ealayer3_frame_t eaf_0, eaf_1; 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) */ /* 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); //;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 */ /* check PCM block */
if (eaf->v1_pcm_flag == 0xEE) { if (eaf->v1_pcm_flag == 0xEE) {
fill_buf(ib, 32); 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 */ r_bits(is, 16,&eaf->v1_pcm_samples); /* number of PCM samples, can be 0 */
eaf->pre_size += 2+2; /* 16b+16b */ 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). /* 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. * Seems to alter decoded sample buffer to handle encoder delay/padding in a twisted way. */
* (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 */
static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream, ealayer3_frame_t *eaf) { 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]; mpeg_custom_stream *ms = data->streams[num_stream];
int channels_per_frame = ms->channels_per_frame; 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) { if (eaf->v1_pcm_samples || eaf->v1_offset_samples) {
uint8_t* outbuf = ms->output_buffer + bytes_filled; uint8_t* outbuf = ms->output_buffer + bytes_filled;
off_t pcm_offset = stream->offset + eaf->pre_size + eaf->common_size; 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_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_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_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", //;VGM_LOG("EA EAL3 v1: offset=%lx + %x, offset_samples=%x, pcm_samples=%i, spf=%i\n",
// stream->offset, eaf->v1_offset_samples, eaf->v1_pcm_samples, pcm_offset); // 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); 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; ms->samples_filled += eaf->v1_pcm_samples;
/* skip decoded samples as PCM block 'overwrites' them w/ special meanings */ //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?)
size_t decode_to_discard = eaf->v1_offset_samples;
if (data->type == MPEG_EAL31) { /* v1_offset_samples in V1a controls how the PCM block goes in the sample buffer. Value seems to start
//todo should also discard v1_pcm_samples, but block layout samples may be exhausted * from frame samples end, taking into account that 1st frame discards 576+529 samples.
// and won't move (maybe new block if offset = new offset detected) * ex. with 47 samples:
if (decode_to_discard == 576) * - offset 47 puts block at sample 0 (at 576*2-47 w/o 576+529 discard),
decode_to_discard = data->samples_per_frame;//+ eaf->v1_pcm_samples; * - 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?)
else { * In V1b seems to works similarly but offset looks adjusted after initial discard (so offset 0 for first frame)
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 */ * This behaviour matters most in looping sfx (ex. Burnout Paradise), or tracks that start
if (decode_to_discard == 0) /* seems ok (ex. comparing NFS:UC PS3 vs PC gets correct waveform this way) */ * without silence (ex. NFS:UG2), and NFS:UC PS3 (EAL3v1b) vs PC (EAXAS) gets correct waveform this way */
decode_to_discard = data->samples_per_frame;//+ eaf->v1_pcm_samples; /* musn't discard pcm_number */ decode_to_discard = eaf->v1_pcm_samples;
} ms->decode_to_discard += decode_to_discard;
ms->decode_to_discard += decode_to_discard;
}
} }
if (eaf->v2_extended_flag) { 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). /* 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 * 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. * streams's offsets should start in the first stream's EA-frame.

View File

@ -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_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) { if (mpg123_open_feed(m) != MPG123_OK) {
goto fail; goto fail;

View File

@ -4,6 +4,7 @@
/* MTA2 decoder based on: /* MTA2 decoder based on:
* - MGS Developer Wiki: https://www.mgsdevwiki.com/wiki/index.php/MTA2_(Codec) [codec by daemon1] * - MGS Developer Wiki: https://www.mgsdevwiki.com/wiki/index.php/MTA2_(Codec) [codec by daemon1]
* - Solid4 tools: https://github.com/GHzGangster/Drebin * - Solid4 tools: https://github.com/GHzGangster/Drebin
* (PS3 probably uses floats, so this may not be 100% accurate)
* *
* MTA2 layout: * MTA2 layout:
* - data is divided into N tracks of 0x10 header + 0x90 frame per track channel, forming N streams * - 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). * expects samples_to_do to be block_samples at most (could be simplified, I guess).
*/ */
/* coefs table (extended XA filters) */ /* tweaked XA/PSX coefs << 8 */
static const int mta2_coefs1[8] = { static const int16_t mta2_coefs[8][2] = {
0, 240, 460, 392, 488, 460, 460, 240 { 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 static const int mta2_scales[32] = {
};
/* shift table */
static const int mta2_shifts[32] = {
256, 335, 438, 573, 749, 979, 1281, 1675, 256, 335, 438, 573, 749, 979, 1281, 1675,
2190, 2864, 3746, 4898, 6406, 8377, 10955, 14327, 2190, 2864, 3746, 4898, 6406, 8377, 10955, 14327,
18736, 24503, 32043, 41905, 54802, 71668, 93724, 122568, 18736, 24503, 32043, 41905, 54802, 71668, 93724, 122568,
160290, 209620, 274133, 358500, 468831, 613119, 801811, 1048576 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 */ /* 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 samples_done = 0, sample_count = 0, channel_block_samples, channel_first_sample, frame_size = 0;
int i, group, row, col; int i, group, row, col;
int track_channels = 0, track_channel; 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; int num_track = 0, channel_layout;
/* parse track header (0x10) and skip tracks that our current channel doesn't belong to */ /* 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) */ /* 0x01(3): num_frame (0=first) */
/* 0x04(1): 0? */ /* 0x04(1): 0? */
channel_layout = read_8bit(stream->offset+0x05,stream->streamfile); /* bitmask, see mta2.c */ channel_layout = get_u8 (frame + 0x05); /* bitmask, see mta2.c */
frame_size = read_16bitBE(stream->offset+0x06,stream->streamfile); /* not including this header */ frame_size = get_u16be(frame + 0x06); /* not including this header */
/* 0x08(8): null */ /* 0x08(8): null */
VGM_ASSERT(frame_size == 0, "MTA2: empty frame at %x\n", (uint32_t)stream->offset); 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) /* 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) */ * 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; track_channels = 0;
for (i = 0; i < 8; i++) { for (i = 0; i < 8; i++) { /* max 8ch */
if ((channel_layout >> i) & 0x01) if ((channel_layout >> i) & 0x01)
track_channels++; track_channels++;
} }
if (track_channels == 0) { /* bad data, avoid div by 0 */ 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; return;
} }
@ -93,19 +88,20 @@ void decode_mta2(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacin
} }
while (1); while (1);
/* parse stuff */
read_streamfile(frame + 0x10, stream->offset + 0x10, frame_size, stream->streamfile); /* ignore EOF errors */
track_channel = channel % track_channels; track_channel = channel % track_channels;
channel_block_samples = (0x80*2); channel_block_samples = (0x80*2);
channel_first_sample = first_sample % (0x80*2); channel_first_sample = first_sample % (0x80*2);
/* parse channel frame (header 0x04*4 + data 0x20*4) */ /* parse channel frame (header 0x04*4 + data 0x20*4) */
for (group = 0; group < 4; group++) { for (group = 0; group < 4; group++) {
short hist2, hist1, coefs, shift, output; short hist2, hist1, coefs, scale;
int group_header = read_32bitBE(stream->offset + 0x10 + track_channel*0x90 + group*0x4, stream->streamfile); uint32_t group_header = get_u32be(frame + 0x10 + track_channel*0x90 + group*0x4);
hist2 = (short) ((group_header >> 16) & 0xfff0); /* upper 16b discarding 4b */ hist2 = (short) ((group_header >> 16) & 0xfff0); /* upper 16b discarding 4b */
hist1 = (short) ((group_header >> 4) & 0xfff0); /* lower 16b discarding 4b */ hist1 = (short) ((group_header >> 4) & 0xfff0); /* lower 16b discarding 4b */
coefs = (group_header >> 5) & 0x7; /* mid 3b */ 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 /* write header samples (skips the last 2 group nibbles), like Drebin's decoder
* last 2 nibbles and next 2 header hist should match though */ * 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++; sample_count++;
/* decode nibbles */
for (row = 0; row < 8; row++) { for (row = 0; row < 8; row++) {
int pos = 0x10 + track_channel*0x90 + 0x10 + group*0x4 + row*0x10;
for (col = 0; col < 4*2; col++) { 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); uint8_t nibbles = frame[pos + col/2];
int nibble_shift = (!(col&1) ? 4 : 0); /* upper first */ int32_t sample;
output = mta2_expand_nibble((nibbles >> nibble_shift) & 0xf, hist1, hist2, coefs, shift);
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) */ /* ignore last 2 nibbles (uses first 2 header samples) */
if (row < 7 || col < 3*2) { if (row < 7 || col < 3*2) {
if (sample_count >= channel_first_sample && samples_done < samples_to_do) { if (sample_count >= channel_first_sample && samples_done < samples_to_do) {
outbuf[samples_done * channelspacing] = output; outbuf[samples_done * channelspacing] = sample;
samples_done++; samples_done++;
} }
sample_count++; sample_count++;
} }
hist2 = hist1; hist2 = hist1;
hist1 = output; hist1 = sample;
} }
} }
} }

View File

@ -8,12 +8,12 @@
* Layout: N tracks of 0x10 header + 0x80*2 (always 2ch; multichannels uses 4ch = 2ch track0 + 2ch track1 xN). * 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,
-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,
-1, -5, -9, -13, -16, -20, -24, -28, }, -1, -5, -9, -13, -16, -20, -24, -28, },
{ 2, 6, 11, 15, 20, 24, 29, 33, { 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) { 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; uint8_t frame[0x110] = {0};
int i; off_t frame_offset;
int c = channel%2; /* global channel to track channel */ int i, ch, sample_count = 0;
size_t bytes_per_frame /*, samples_per_frame*/;
int32_t hist = stream->adpcm_history1_16; 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 */ /* special stereo interleave, stereo */
first_sample = first_sample % 0x100; 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) { if (first_sample == 0) {
/* 0x10 header: track (8b, 0=first), track count (24b, 1=first), step-L, step-R, hist-L, hist-R */ /* 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 */ step_index = get_s16le(frame + 0x04 + 0x00 + ch*0x02); /* step-L/R */
int32_t init_hist = read_16bitLE(stream->offset+4+4+c*4, stream->streamfile); /* hist-L/R: hist 16bit + empty 16bit */ 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); VGM_ASSERT(step_index < 0 || step_index > 31, "MTAF: bad header idx at 0x%x\n", (uint32_t)stream->offset);
/* avoid index out of range in corrupt files */ if (step_index < 0) {
if (init_idx < 0) { step_index = 0;
init_idx = 0; } else if (step_index > 31) {
} else if (init_idx > 31) { step_index = 31;
init_idx = 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 */ hist = clamp16(hist + mtaf_step_sizes[step_index][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]);
outbuf[sample_count] = hist; outbuf[sample_count] = hist;
sample_count += channelspacing;
step_idx += index_table[nibble]; step_index += mtaf_step_indexes[nibble];
if (step_idx < 0) { /* clip step */ if (step_index < 0) {
step_idx = 0; step_index = 0;
} else if (step_idx > 31) { } else if (step_index > 31) {
step_idx = 31; step_index = 31;
} }
} }
/* update state */ stream->adpcm_step_index = step_index;
stream->adpcm_step_index = step_idx;
stream->adpcm_history1_16 = hist; stream->adpcm_history1_16 = hist;
} }

View File

@ -1,58 +1,62 @@
#include "coding.h" #include "coding.h"
#include "../util.h" #include "../util.h"
/* ADPCM found in NDS games using Procyon Studio Digital Sound Elements */ /* standard XA/PSX coefs << 6 */
static const int8_t proc_coefs[16][2] = {
static const int8_t proc_coef[5][2] = { 0, 0 },
{ { 60, 0 },
{0x00,0x00}, { 115, -52 },
{0x3C,0x00}, { 98, -55 },
{0x73,0xCC}, { 122, -60 },
{0x62,0xC9}, /* rest is 0s */
{0x7A,0xC4},
}; };
void decode_nds_procyon(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { /* ADPCM found in NDS games using Procyon Studio Digital Sound Elements */
int i=first_sample; void decode_nds_procyon(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
int32_t sample_count; uint8_t frame[0x10] = {0};
off_t frame_offset;
int framesin = first_sample/30; int i, frames_in, sample_count = 0;
size_t bytes_per_frame, samples_per_frame;
uint8_t header = read_8bit(framesin*16+15+stream->offset,stream->streamfile) ^ 0x80; int index, scale, coef1, coef2;
int scale = 12 - (header & 0xf);
int coef_index = (header >> 4) & 0xf;
int32_t hist1 = stream->adpcm_history1_32; int32_t hist1 = stream->adpcm_history1_32;
int32_t hist2 = stream->adpcm_history2_32; int32_t hist2 = stream->adpcm_history2_32;
int32_t coef1; uint8_t header;
int32_t coef2;
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) { /* external interleave (fixed size), mono */
int sample_byte = read_8bit(framesin*16+stream->offset+i/2,stream->streamfile) ^ 0x80; bytes_per_frame = 0x10;
samples_per_frame = (bytes_per_frame - 0x01) * 2; /* 30 */
frames_in = first_sample / samples_per_frame;
int32_t sample = /* parse frame header */
(int32_t) frame_offset = stream->offset + bytes_per_frame * frames_in;
(i&1? read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */
get_high_nibble_signed(sample_byte): header = frame[0x0F] ^ 0x80;
get_low_nibble_signed(sample_byte) scale = 12 - (header & 0xf);
) * 64 * 64; 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) if (scale < 0)
{
sample <<= -scale; sample <<= -scale;
}
else else
sample >>= scale; sample >>= scale;
sample = (hist1 * coef1 + hist2 * coef2 + 32) / 64 + (sample * 64); sample = (hist1 * coef1 + hist2 * coef2 + 32) / 64 + (sample * 64);
hist2 = hist1; 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; stream->adpcm_history1_32 = hist1;

View File

@ -1,56 +1,66 @@
#include "coding.h" #include "coding.h"
#include "../util.h" #include "../util.h"
const short afc_coef[16][2] = static const int16_t afc_coefs[16][2] = {
{{0,0}, { 0, 0 },
{0x0800,0}, { 2048, 0 },
{0,0x0800}, { 0, 2048 },
{0x0400,0x0400}, { 1024, 1024 },
{0x1000,0xf800}, { 4096,-2048 },
{0x0e00,0xfa00}, { 3584,-1536 },
{0x0c00,0xfc00}, { 3072,-1024 },
{0x1200,0xf600}, { 4608,-2560 },
{0x1068,0xf738}, { 4200,-2248 },
{0x12c0,0xf704}, { 4800,-2300 },
{0x1400,0xf400}, { 5120,-3072 },
{0x0800,0xf800}, { 2048,-2048 },
{0x0400,0xfc00}, { 1024,-1024 },
{0xfc00,0x0400}, {-1024, 1024 },
{0xfc00,0}, {-1024, 0 },
{0xf800,0}}; {-2048, 0 }
};
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) {
int i=first_sample; uint8_t frame[0x09] = {0};
int32_t sample_count; off_t frame_offset;
int i, frames_in, sample_count = 0;
int framesin = first_sample/16; size_t bytes_per_frame, samples_per_frame;
int index, scale, coef1, coef2;
int8_t header = read_8bit(framesin*9+stream->offset,stream->streamfile);
int32_t scale = 1 << ((header>>4) & 0xf);
int coef_index = (header & 0xf);
int32_t hist1 = stream->adpcm_history1_16; int32_t hist1 = stream->adpcm_history1_16;
int32_t hist2 = stream->adpcm_history2_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) { /* external interleave, mono */
int sample_byte = read_8bit(framesin*9+stream->offset+1+i/2,stream->streamfile); 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(( /* parse frame header */
(((i&1? frame_offset = stream->offset + bytes_per_frame * frames_in;
get_low_nibble_signed(sample_byte): read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */
get_high_nibble_signed(sample_byte) scale = 1 << ((frame[0] >> 4) & 0xf);
) * scale)<<11) + index = (frame[0] & 0xf);
(coef1 * hist1 + coef2 * hist2))>>11 coef1 = afc_coefs[index][0];
); coef2 = afc_coefs[index][1];
/*printf("%hd\n",outbuf[sample_count]);*/
/* 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; hist2 = hist1;
hist1 = outbuf[sample_count]; hist1 = sample;
} }
stream->adpcm_history1_16 = hist1; stream->adpcm_history1_16 = hist1;

View File

@ -2,63 +2,61 @@
#include "../util.h" #include "../util.h"
/* Nintendo GC Disc TracK streaming ADPCM (similar to CD-XA) */ /* standard XA coefs << 6 */
void decode_ngc_dtk(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { 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; off_t frame_offset;
int i, frames_in, sample_count = 0; int i, frames_in, sample_count = 0;
size_t bytes_per_frame, samples_per_frame; 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 hist1 = stream->adpcm_history1_32;
int32_t hist2 = stream->adpcm_history2_32; int32_t hist2 = stream->adpcm_history2_32;
/* external interleave (fixed size), stereo */ /* external interleave (fixed size), stereo */
bytes_per_frame = 0x20; bytes_per_frame = 0x20;
samples_per_frame = 28; samples_per_frame = (0x20 - 0x04); /* 28 for each channel */
frames_in = first_sample / samples_per_frame; frames_in = first_sample / samples_per_frame;
first_sample = first_sample % samples_per_frame; first_sample = first_sample % samples_per_frame;
/* parse frame L/R header (repeated at 0x03/04) */ /* parse frame L/R header (repeated at 0x03/04) */
frame_offset = stream->offset + bytes_per_frame*frames_in; frame_offset = stream->offset + bytes_per_frame * frames_in;
coef_index = ((uint8_t)read_8bit(frame_offset+channel,stream->streamfile) >> 4) & 0xf; read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */
shift_factor = ((uint8_t)read_8bit(frame_offset+channel,stream->streamfile) >> 0) & 0xf; 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) */ /* 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 */ /* decode nibbles */
for (i = first_sample; i < first_sample+samples_to_do; i++) { for (i = first_sample; i < first_sample + samples_to_do; i++) {
int32_t hist = 0, new_sample; int sample, hist;
uint8_t nibbles = (uint8_t)read_8bit(frame_offset+0x04+i,stream->streamfile); uint8_t nibbles = frame[0x04 + i];
/* apply XA filters << 6 */ hist = (hist1*coef1 - hist2*coef2 + 32) >> 6;
switch(coef_index) { if (hist > 2097151) hist = 2097151;
case 0: else if (hist < -2097152) hist = -2097152;
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;
new_sample = (channel==0) ? /* L=low nibble first */ sample = (channel==0) ? /* L=low nibble first */
get_low_nibble_signed(nibbles) : get_low_nibble_signed(nibbles) :
get_high_nibble_signed(nibbles); get_high_nibble_signed(nibbles);
new_sample = (new_sample << 12) >> shift_factor; sample = (sample << 12) >> shift;
new_sample = (new_sample << 6) + hist; sample = (sample << 6) + hist;
hist2 = hist1; 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; sample_count += channelspacing;
} }

View File

@ -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) { 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; 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) { 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 = read_f32(stream->offset+i*4,stream->streamfile);
float* sample_float; int sample_pcm = (int)floor(sample_float * 32767.f + .5f);
int sample_pcm;
sample_float = (float*)&sample_int;
sample_pcm = (int)floor((*sample_float) * 32767.f + .5f);
outbuf[sample_count] = clamp16(sample_pcm); outbuf[sample_count] = clamp16(sample_pcm);
} }

View File

@ -1,54 +1,69 @@
#include "coding.h" #include "coding.h"
/* a somewhat IMA-like mix of step+next index all in one (maybe an array[][] originally?) */ /* a somewhat IMA-like mix of pre-calculated [index][nibble][step,index] all in one */
static const int32_t ptadpcm_table[(16+16)*16] = { /* 16 of (step+index) + 16 values in a nibble */ 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, { -14, 2}, { -10, 2}, { -7, 1}, { -5, 1}, { -3, 0}, { -2, 0}, { -1, 0}, { 0, 0},
-28, 3, -20, 3, -14, 2, -10, 2, -7, 1, -5, 1, -3, 1, -1, 0, { 0, 0}, { 1, 0}, { 2, 0}, { 3, 0}, { 5, 1}, { 7, 1}, { 10, 2}, { 14, 2},
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, { -28, 3}, { -20, 3}, { -14, 2}, { -10, 2}, { -7, 1}, { -5, 1}, { -3, 1}, { -1, 0},
2, 1, 6, 2, 10, 2, 14, 2, 20, 3, 28, 3, 40, 4, 56, 4, { 1, 0}, { 3, 1}, { 5, 1}, { 7, 1}, { 10, 2}, { 14, 2}, { 20, 3}, { 28, 3},
-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, { -56, 4}, { -40, 4}, { -28, 3}, { -20, 3}, { -14, 2}, { -10, 2}, { -6, 2}, { -2, 1},
-224, 6, -160, 6, -112, 5, -80, 5, -56, 4, -40, 4, -24, 4, -8, 3, { 2, 1}, { 6, 2}, { 10, 2}, { 14, 2}, { 20, 3}, { 28, 3}, { 40, 4}, { 56, 4},
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, { -112, 5}, { -80, 5}, { -56, 4}, { -40, 4}, { -28, 3}, { -20, 3}, { -12, 3}, { -4, 2},
16, 4, 48, 5, 80, 5, 112, 5, 160, 6, 224, 6, 320, 7, 448, 7, { 4, 2}, { 12, 3}, { 20, 3}, { 28, 3}, { 40, 4}, { 56, 4}, { 80, 5}, { 112, 5},
-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, { -224, 6}, { -160, 6}, { -112, 5}, { -80, 5}, { -56, 4}, { -40, 4}, { -24, 4}, { -8, 3},
-1792, 9, -1280, 9, -896, 8, -640, 8, -448, 7, -320, 7, -192, 7, -64, 6, { 8, 3}, { 24, 4}, { 40, 4}, { 56, 4}, { 80, 5}, { 112, 5}, { 160, 6}, { 224, 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, { -448, 7}, { -320, 7}, { -224, 6}, { -160, 6}, { -112, 5}, { -80, 5}, { -48, 5}, { -16, 4},
128, 7, 384, 8, 640, 8, 896, 8, 1280, 9, 1792, 9, 2560, 10, 3584, 10, { 16, 4}, { 48, 5}, { 80, 5}, { 112, 5}, { 160, 6}, { 224, 6}, { 320, 7}, { 448, 7},
-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, { -896, 8}, { -640, 8}, { -448, 7}, { -320, 7}, { -224, 6}, { -160, 6}, { -96, 6}, { -32, 5},
-14336, 11, -10240, 11, -7168, 11, -5120, 11, -3584, 10, -2560, 10, -1536, 10, -512, 9, { 32, 5}, { 96, 6}, { 160, 6}, { 224, 6}, { 320, 7}, { 448, 7}, { 640, 8}, { 896, 8},
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, { -1792, 9}, { -1280, 9}, { -896, 8}, { -640, 8}, { -448, 7}, { -320, 7}, { -192, 7}, { -64, 6},
1024, 10, 3072, 11, 5120, 11, 7168, 11, 10240, 11, 14336, 11, 20480, 11, 28672, 11, { 64, 6}, { 192, 7}, { 320, 7}, { 448, 7}, { 640, 8}, { 896, 8}, { 1280, 9}, { 1792, 9},
/* rest is 0s */ }, {
{ -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). */ /* 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; off_t frame_offset;
int i, frames_in, sample_count = 0, samples_done = 0; int i, frames_in, sample_count = 0, samples_done = 0;
size_t bytes_per_frame, samples_per_frame; size_t bytes_per_frame, samples_per_frame;
int16_t hist1, hist2; int16_t hist1, hist2;
int index, step; int index, nibble, step;
/* external interleave (variable size), mono */ /* external interleave (variable size), mono */
bytes_per_frame = frame_size; bytes_per_frame = frame_size;
samples_per_frame = 2 + (frame_size - 0x05) * 2; samples_per_frame = 2 + (frame_size - 0x05) * 2;
frames_in = first_sample / samples_per_frame; frames_in = first_sample / samples_per_frame;
first_sample = first_sample % samples_per_frame; //first_sample = first_sample % samples_per_frame;
/* parse frame header */ /* parse frame header */
frame_offset = stream->offset + bytes_per_frame*frames_in; frame_offset = stream->offset + bytes_per_frame*frames_in;
hist2 = read_16bitLE(frame_offset+0x00,stream->streamfile); read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */
hist1 = read_16bitLE(frame_offset+0x02,stream->streamfile); hist2 = get_s16le(frame + 0x00);
index = (uint8_t)read_8bit(frame_offset+0x04,stream->streamfile); hist1 = get_s16le(frame + 0x02);
index = frame[0x04];
VGM_ASSERT_ONCE(index > 12, "PTADPCM: incorrect index at %x\n", (uint32_t)frame_offset); 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 */ /* decode nibbles */
for (i = first_sample; i < first_sample + samples_to_do; i++) { for (i = first_sample; i < first_sample + samples_to_do; i++) {
int32_t new_sample; uint8_t nibbles = frame[0x05 + i/2];
uint8_t nibbles = (uint8_t)read_8bit(frame_offset+0x05 + i/2,stream->streamfile); int32_t sample;
uint8_t nibble;
nibble = !(i&1) ? /* low nibble first */ nibble = !(i&1) ? /* low nibble first */
(nibbles >> 0) & 0xF : (nibbles >> 0) & 0xF :
(nibbles >> 4) & 0xF; (nibbles >> 4) & 0xF;
step = ptadpcm_table[2*(nibble + 16*index) + 0]; step = ptadpcm_table[index][nibble][0];
index = ptadpcm_table[2*(nibble + 16*index) + 1]; index = ptadpcm_table[index][nibble][1];
new_sample = clamp16(step + 2*hist1 - hist2); sample = clamp16(step + 2*hist1 - hist2);
if (sample_count >= first_sample && samples_done < samples_to_do) { if (sample_count >= first_sample && samples_done < samples_to_do) {
outbuf[samples_done * channelspacing] = new_sample; outbuf[samples_done * channelspacing] = sample;
samples_done++; samples_done++;
} }
sample_count++; sample_count++;
hist2 = hist1; hist2 = hist1;
hist1 = new_sample; hist1 = sample;
} }
//stream->adpcm_history1_32 = hist1; //stream->adpcm_history1_32 = hist1;

View File

@ -3,25 +3,27 @@
/* Decodes Konami XMD from Xbox games. /* Decodes Konami XMD from Xbox games.
* Algorithm reverse engineered from SH4/CV:CoD's xbe (byte-accurate). */ * 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; off_t frame_offset;
int i, frames_in, sample_count = 0, samples_done = 0; int i, frames_in, sample_count = 0, samples_done = 0;
size_t bytes_per_frame, samples_per_frame; size_t bytes_per_frame, samples_per_frame;
int16_t hist1, hist2; int16_t hist1, hist2;
uint16_t scale; uint16_t scale;
/* external interleave (variable size), mono */ /* external interleave (variable size), mono */
bytes_per_frame = frame_size; bytes_per_frame = frame_size;
samples_per_frame = 2 + (frame_size - 0x06) * 2; samples_per_frame = 2 + (frame_size - 0x06) * 2;
frames_in = first_sample / samples_per_frame; 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 */ /* parse frame header */
frame_offset = stream->offset + bytes_per_frame*frames_in; frame_offset = stream->offset + bytes_per_frame * frames_in;
hist2 = read_16bitLE(frame_offset+0x00,stream->streamfile); read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */
hist1 = read_16bitLE(frame_offset+0x02,stream->streamfile); hist2 = get_s16le(frame + 0x00);
scale = (uint16_t)read_16bitLE(frame_offset+0x04,stream->streamfile); /* scale doesn't go too high though */ hist1 = get_s16le(frame + 0x02);
scale = get_u16le(frame + 0x04); /* scale doesn't go too high though */
/* write header samples (needed) */ /* write header samples (needed) */
if (sample_count >= first_sample && samples_done < samples_to_do) { 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 */ /* decode nibbles */
for (i = first_sample; i < first_sample + samples_to_do; i++) { for (i = first_sample; i < first_sample + samples_to_do; i++) {
int32_t new_sample; uint8_t nibbles = frame[0x06 + i/2];
uint8_t nibbles = (uint8_t)read_8bit(frame_offset+0x06 + i/2,stream->streamfile); int32_t sample;
new_sample = i&1 ? /* low nibble first */ sample = i&1 ? /* low nibble first */
get_high_nibble_signed(nibbles): get_high_nibble_signed(nibbles):
get_low_nibble_signed(nibbles); get_low_nibble_signed(nibbles);
/* Coefs are based on XA's filter 2 (using those creates hissing in some songs though) /* 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 */ * 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 */ //new_sample = clamp16(new_sample); /* not needed */
if (sample_count >= first_sample && samples_done < samples_to_do) { 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++; samples_done++;
} }
sample_count++; sample_count++;
hist2 = hist1; hist2 = hist1;
hist1 = new_sample; hist1 = sample;
} }
//stream->adpcm_history1_32 = hist1; //stream->adpcm_history1_32 = hist1;

View File

@ -6,10 +6,16 @@
* to inform plugins that need it. Common extensions are commented out to avoid stealing them * to inform plugins that need it. Common extensions are commented out to avoid stealing them
* and possibly adding an unwanted association to the player. */ * 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. */ /* 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 /* 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[] = { static const char* extension_list[] = {
@ -25,7 +31,6 @@ static const char* extension_list[] = {
//"aac", //common //"aac", //common
"aa3", //FFmpeg/not parsed (ATRAC3/ATRAC3PLUS/MP3/LPCM/WMA) "aa3", //FFmpeg/not parsed (ATRAC3/ATRAC3PLUS/MP3/LPCM/WMA)
"aaap",
"aax", "aax",
"abk", "abk",
//"ac3", //common, FFmpeg/not parsed (AC3) //"ac3", //common, FFmpeg/not parsed (AC3)
@ -131,6 +136,7 @@ static const char* extension_list[] = {
"data", "data",
"dax", "dax",
"dbm", "dbm",
"dct",
"dcs", "dcs",
"ddsp", "ddsp",
"de2", "de2",
@ -175,7 +181,7 @@ static const char* extension_list[] = {
"gin", "gin",
"gms", "gms",
"gsb", "gsb",
//"gsf", //conflicts with GBA gsf plugins? "gsf",
"gtd", "gtd",
"gwm", "gwm",
@ -211,6 +217,7 @@ static const char* extension_list[] = {
"imc", "imc",
"int", "int",
"is14", "is14",
"isb",
"isd", "isd",
"isws", "isws",
"itl", "itl",
@ -268,6 +275,7 @@ static const char* extension_list[] = {
"lwma", //fake extension for .wma, FFmpeg/not parsed "lwma", //fake extension for .wma, FFmpeg/not parsed
"mab", "mab",
"mad",
"map", "map",
"matx", "matx",
"mc3", "mc3",
@ -444,7 +452,7 @@ static const char* extension_list[] = {
"sss", "sss",
"ster", "ster",
"sth", "sth",
//"stm", //common "stm",
"stma", //fake extension/header id for .stm "stma", //fake extension/header id for .stm
"str", "str",
"stream", "stream",
@ -568,6 +576,7 @@ static const char* extension_list[] = {
"ydsp", "ydsp",
"ymf", "ymf",
"zic",
"zsd", "zsd",
"zsm", "zsm",
"zss", "zss",
@ -585,7 +594,6 @@ static const char* common_extension_list[] = {
"aiff", //common "aiff", //common
"bin", //common "bin", //common
"flac", //common "flac", //common
"gsf", //conflicts with GBA gsf plugins?
"mp+", //common [Moonshine Runners (PC)] "mp+", //common [Moonshine Runners (PC)]
"mp2", //common "mp2", //common
"mp3", //common "mp3", //common
@ -593,7 +601,6 @@ static const char* common_extension_list[] = {
"mpc", //common "mpc", //common
"ogg", //common "ogg", //common
"opus", //common "opus", //common
"stm", //common
"wav", //common "wav", //common
}; };
@ -830,7 +837,7 @@ static const meta_info meta_info_list[] = {
{meta_RFRM, "Retro Studios RFRM header"}, {meta_RFRM, "Retro Studios RFRM header"},
{meta_NGC_ADPDTK, "Nintendo ADP raw header"}, {meta_NGC_ADPDTK, "Nintendo ADP raw header"},
{meta_RSF, "Retro Studios RSF 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_AST, "Nintendo AST header"},
{meta_HALPST, "HAL Laboratory HALPST header"}, {meta_HALPST, "HAL Laboratory HALPST header"},
{meta_DSP_RS03, "Retro Studios RS03 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_VAGi, "Sony VAGi header"},
{meta_PS2_VAGp, "Sony VAGp header"}, {meta_PS2_VAGp, "Sony VAGp header"},
{meta_PS2_pGAV, "Sony pGAV header"}, {meta_PS2_pGAV, "Sony pGAV header"},
{meta_PS2_VAGp_AAAP, "Acclaim Austin AAAp VAG header"},
{meta_SEB, "Game Arts .SEB header"}, {meta_SEB, "Game Arts .SEB header"},
{meta_STR_WAV, "Blitz Games .STR+WAV header"}, {meta_STR_WAV, "Blitz Games .STR+WAV header"},
{meta_PS2_ILD, "ILD 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_BNS, "Nintendo BNS header"},
{meta_WII_WAS, "Sumo Digital iSWS header"}, {meta_WII_WAS, "Sumo Digital iSWS header"},
{meta_XBOX_HLWAV, "Half Life 2 bgm header"}, {meta_XBOX_HLWAV, "Half Life 2 bgm header"},
{meta_STX, "Nintendo .stx header"},
{meta_MYSPD, "U-Sing .MYSPD header"}, {meta_MYSPD, "U-Sing .MYSPD header"},
{meta_HIS, "Her Interactive HIS header"}, {meta_HIS, "Her Interactive HIS header"},
{meta_PS2_AST, "KOEI AST header"}, {meta_PS2_AST, "KOEI AST header"},
@ -1029,7 +1036,7 @@ static const meta_info meta_info_list[] = {
{meta_DMSG, "RIFF/DMSGsegh header"}, {meta_DMSG, "RIFF/DMSGsegh header"},
{meta_PONA_3DO, "Policenauts BGM header"}, {meta_PONA_3DO, "Policenauts BGM header"},
{meta_PONA_PSX, "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_NGC_DSP_KONAMI, "Konami DSP header"},
{meta_PS2_STER, "STER Header"}, {meta_PS2_STER, "STER Header"},
{meta_BNSF, "Namco Bandai BNSF 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_XMV_VALVE, "Valve XMV header"},
{meta_UBI_HX, "Ubisoft HXx header"}, {meta_UBI_HX, "Ubisoft HXx header"},
{meta_BMP_KONAMI, "Konami BMP header"}, {meta_BMP_KONAMI, "Konami BMP header"},
{meta_ISB, "Creative ISACT header"},
{meta_XSSB, "Artoon XSSB header"},
}; };

View File

@ -6,89 +6,81 @@
void block_update_ea_1snh(off_t block_offset, VGMSTREAM * vgmstream) { void block_update_ea_1snh(off_t block_offset, VGMSTREAM * vgmstream) {
STREAMFILE* streamFile = vgmstream->ch[0].streamfile; STREAMFILE* streamFile = vgmstream->ch[0].streamfile;
int i; 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; 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 */ /* 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->current_block_offset = block_offset;
vgmstream->next_block_offset = block_offset; vgmstream->next_block_offset = block_offset;
vgmstream->current_block_samples = -1; vgmstream->current_block_samples = -1;
return; return;
} }
block_id = read_32bitBE(block_offset + 0x00, streamFile);
while (block_offset < file_size) { /* BE in SAT, but one file may have both BE and LE chunks [FIFA 98 (SAT): movie LE, audio BE] */
uint32_t id = read_32bitBE(block_offset+0x00,streamFile); 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] */ block_header = 0;
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; 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 */ block_header = (is_eacs || is_zero) ? 0x28 : (is_sead ? 0x14 : 0x2c);
int is_sead = (id == 0x53454144); if (block_header >= block_size) /* sometimes has audio data after header */
int is_eacs = read_32bitBE(block_offset+0x08, streamFile) == 0x45414353; block_header = 0;
} else if (block_id == 0x31534E64 || block_id == 0x534E4443) { /* "1SNd" "SNDC" audio data */
block_header = is_eacs ? 0x28 : (is_sead ? 0x14 : 0x2c); block_header = 0x08;
if (block_header >= block_size) /* sometimes has audio data after header */ } else if (block_id == 0x00000000 || block_id == 0xFFFFFFFF || block_id == 0x31534E65) { /* EOF or "1SNe" */
block_header = 0; vgmstream->current_block_samples = -1;
} return;
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;
} }
vgmstream->current_block_offset = block_offset; vgmstream->current_block_offset = block_offset;
vgmstream->next_block_offset = block_offset + block_size; vgmstream->next_block_offset = block_offset + block_size;
vgmstream->current_block_size = block_size - block_header; if (block_header == 0) {
if (vgmstream->current_block_samples == -1) /* no audio data, skip this block */
vgmstream->current_block_samples = 0;
return; return;
}
audio_size = block_size - block_header;
/* set new channel offsets and block sizes */ /* set new channel offsets and block sizes */
switch(vgmstream->coding_type) { switch(vgmstream->coding_type) {
case coding_PCM8_int: case coding_PCM8_int:
case coding_ULAW_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++) { for (i=0;i<vgmstream->channels;i++) {
vgmstream->ch[i].offset = block_offset + block_header + i; vgmstream->ch[i].offset = block_offset + block_header + i;
} }
break; break;
case coding_PCM16_int: 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++) { for (i=0;i<vgmstream->channels;i++) {
vgmstream->ch[i].offset = block_offset + block_header + (i*2); vgmstream->ch[i].offset = block_offset + block_header + (i*2);
} }
break; break;
case coding_PSX: 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++) { 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; break;
case coding_DVI_IMA: case coding_DVI_IMA:
if (vgmstream->codec_config == 1) { /* ADPCM hist */ if (vgmstream->codec_config == 1) { /* ADPCM hist */
vgmstream->current_block_samples = read_32bit(block_offset + block_header, streamFile); 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++) { for(i = 0; i < vgmstream->channels; i++) {
off_t adpcm_offset = block_offset + block_header + 0x04; 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); // "EA 1SHN blocked: different expected vs block num samples at %lx\n", block_offset);
} }
else { else {
vgmstream->current_block_samples = ima_bytes_to_samples(audio_size, vgmstream->channels);
for(i = 0; i < vgmstream->channels; i++) { for(i = 0; i < vgmstream->channels; i++) {
vgmstream->ch[i].offset = block_offset + block_header; vgmstream->ch[i].offset = block_offset + block_header;
} }

View File

@ -6,7 +6,6 @@
void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) { void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) {
STREAMFILE* streamFile = vgmstream->ch[0].streamfile; STREAMFILE* streamFile = vgmstream->ch[0].streamfile;
int i; int i;
int new_schl = 0;
size_t block_size, block_samples; size_t block_size, block_samples;
int32_t (*read_32bit)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_32bitBE : read_32bitLE; int32_t (*read_32bit)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_32bitBE : read_32bitLE;
@ -44,9 +43,21 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) {
block_samples = 0; /* layout ignores this */ block_samples = 0; /* layout ignores this */
} }
/* "SCHl" start block (movie "SHxx" shouldn't use multi files) */ /* "SCHl" start block, when decoding multi files pasted together */
if (block_id == 0x5343486C) if (block_id == 0x5343486C) {
new_schl = 1; 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) */ /* padding between "SCEl" and next "SCHl" (when subfiles exist) */
if (block_id == 0x00000000) 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; 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; break;
#endif #endif
/* id, size, samples, offsets-per-channel, interleaved data (w/ optional hist per channel) */ /* id, size, samples, offsets-per-channel, interleaved data (w/ optional hist per channel) */

View File

@ -2,7 +2,7 @@
#include "../vgmstream.h" #include "../vgmstream.h"
#include "../mixing.h" #include "../mixing.h"
#define VGMSTREAM_MAX_SEGMENTS 512 #define VGMSTREAM_MAX_SEGMENTS 1024
#define VGMSTREAM_SEGMENT_SAMPLE_BUFFER 8192 #define VGMSTREAM_SEGMENT_SAMPLE_BUFFER 8192

View File

@ -3,14 +3,15 @@
#endif #endif
#include <math.h> #include <math.h>
#include <limits.h> #include <limits.h>
#include "meta.h" #include "meta.h"
#include "adx_keys.h" #include "adx_keys.h"
#include "../coding/coding.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 */ /* ADX - CRI Middleware format */
VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) {
@ -18,7 +19,7 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) {
off_t start_offset, hist_offset = 0; off_t start_offset, hist_offset = 0;
int loop_flag = 0, channel_count; int loop_flag = 0, channel_count;
int32_t loop_start_sample = 0, loop_end_sample = 0; int32_t loop_start_sample = 0, loop_end_sample = 0;
uint16_t version_signature; uint16_t version;
uint8_t encoding_type; uint8_t encoding_type;
uint8_t frame_size; uint8_t frame_size;
@ -29,79 +30,77 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) {
/* checks*/ /* checks*/
/* .adx: standard, .adp: Headhunter (DC) */ /* .adx: standard
* .adp: Headhunter (DC) */
if (!check_extensions(streamFile,"adx,adp")) if (!check_extensions(streamFile,"adx,adp"))
goto fail; goto fail;
/* check first 2 bytes */ if ((uint16_t)read_16bitBE(0x00,streamFile) != 0x8000)
if ((uint16_t)read_16bitBE(0x00,streamFile)!=0x8000) goto fail; goto fail;
/* get stream offset, check for CRI signature just before */ start_offset = (uint16_t)read_16bitBE(0x02,streamFile) + 0x04;
start_offset = (uint16_t)read_16bitBE(0x02,streamFile) + 4; if ((uint16_t)read_16bitBE(start_offset - 0x06,streamFile) != 0x2863 || /* "(c" */
if ((uint16_t)read_16bitBE(start_offset-6,streamFile)!=0x2863 || /* "(c" */ (uint32_t)read_32bitBE(start_offset - 0x04,streamFile) != 0x29435249) /* ")CRI" */
(uint32_t)read_32bitBE(start_offset-4,streamFile)!=0x29435249 /* ")CRI" */ goto fail;
) 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); encoding_type = read_8bit(0x04, streamFile);
switch (encoding_type) { switch (encoding_type) {
case 2: case 0x02:
coding_type = coding_CRI_ADX_fixed; coding_type = coding_CRI_ADX_fixed;
break; break;
case 3: case 0x03:
coding_type = coding_CRI_ADX; coding_type = coding_CRI_ADX;
break; break;
case 4: case 0x04:
coding_type = coding_CRI_ADX_exp; coding_type = coding_CRI_ADX_exp;
break; break;
default: default: /* 0x10 is AHX for DC, 0x11 is AHX */
goto fail; 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); frame_size = read_8bit(0x05, streamFile);
/* check for bits per sample? (only 4 makes sense for ADX) */ if (read_8bit(0x06,streamFile) != 4) /* bits per sample */
if (read_8bit(0x06,streamFile) != 4) goto fail; goto fail;
/* older ADX (adxencd) up to 2ch, newer ADX (criatomencd) up to 8 */ /* older ADX (adxencd) up to 2ch, newer ADX (criatomencd) up to 8 */
channel_count = read_8bit(0x07,streamFile); channel_count = read_8bit(0x07,streamFile);
/* 0x08: sample rate */
/* 0x0c: samples */
/* 0x10: high-pass frequency */
/* check version signature, read loop info */ version = read_16bitBE(0x12,streamFile);
version_signature = read_16bitBE(0x12,streamFile);
/* encryption */ /* encryption */
if (version_signature == 0x0408) { if (version == 0x0408) {
if (find_adx_key(streamFile, 8, &xor_start, &xor_mult, &xor_add)) { if (find_adx_key(streamFile, 8, &xor_start, &xor_mult, &xor_add)) {
coding_type = coding_CRI_ADX_enc_8; 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)) { if (find_adx_key(streamFile, 9, &xor_start, &xor_mult, &xor_add)) {
coding_type = coding_CRI_ADX_enc_9; coding_type = coding_CRI_ADX_enc_9;
version_signature = 0x0400; version = 0x0400;
} }
} }
/* version + extra data */ /* 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; size_t base_size = 0x14, loops_size = 0x18;
header_type = meta_ADX_03; header_type = meta_ADX_03;
/* no sample history */ /* 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_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) * 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_flag = read_32bitBE(loops_offset+0x04,streamFile) != 0; /* loop offset(?) flag (always 1) */
loop_start_sample = read_32bitBE(loops_offset+0x08,streamFile); 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);
@ -109,37 +108,40 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) {
//loop_end_offset = read_32bitBE(loops_offset+0x14,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; size_t base_size = 0x18, hist_size, ainf_size = 0, loops_size = 0x18;
off_t ainf_offset; off_t ainf_offset;
header_type = meta_ADX_04; header_type = meta_ADX_04;
hist_offset = base_size; /* always present but often blank */ 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" */ if ((uint32_t)read_32bitBE(ainf_offset+0x00,streamFile) == 0x41494E46) /* "AINF" */
ainf_size = read_32bitBE(ainf_offset+0x04,streamFile); 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_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) * 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_flag = read_32bitBE(loops_offset+0x04,streamFile) != 0; /* loop offset(?) flag (always 1) */
loop_start_sample = read_32bitBE(loops_offset+0x08,streamFile); 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_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) /* 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) * 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) * 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)] /* CINF header info (very rare, found after loops) [Sakura Taisen 3 (PS2)]
* 0x00 (4): "CINF" * 0x00 (4): "CINF"
@ -149,7 +151,7 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) {
* 0x48 (-): file name, null terminated * 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; header_type = meta_ADX_05;
} }
else { /* not a known/supported version signature */ else { /* not a known/supported version signature */
@ -161,13 +163,13 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) {
vgmstream = allocate_vgmstream(channel_count,loop_flag); vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
vgmstream->num_samples = read_32bitBE(0xc,streamFile); vgmstream->sample_rate = read_32bitBE(0x08,streamFile);
vgmstream->sample_rate = read_32bitBE(0x8,streamFile); vgmstream->num_samples = read_32bitBE(0x0c,streamFile);
vgmstream->loop_start_sample = loop_start_sample; vgmstream->loop_start_sample = loop_start_sample;
vgmstream->loop_end_sample = loop_end_sample; vgmstream->loop_end_sample = loop_end_sample;
vgmstream->coding_type = coding_type; 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->interleave_block_size = frame_size;
vgmstream->meta_type = header_type; vgmstream->meta_type = header_type;
@ -175,6 +177,7 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) {
/* calculate filter coefficients */ /* calculate filter coefficients */
if (coding_type == coding_CRI_ADX_fixed) { if (coding_type == coding_CRI_ADX_fixed) {
int i; int i;
/* standard XA coefs * (2<<11) */
for (i = 0; i < channel_count; i++) { for (i = 0; i < channel_count; i++) {
vgmstream->ch[i].adpcm_coef[0] = 0x0000; vgmstream->ch[i].adpcm_coef[0] = 0x0000;
vgmstream->ch[i].adpcm_coef[1] = 0x0000; vgmstream->ch[i].adpcm_coef[1] = 0x0000;
@ -194,14 +197,14 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) {
x = cutoff; x = cutoff;
y = vgmstream->sample_rate; y = vgmstream->sample_rate;
z = cos(2.0*M_PI*x/y); z = cos(2.0 * M_PI * x / y);
a = M_SQRT2-z; a = M_SQRT2 - z;
b = M_SQRT2-1.0; b = M_SQRT2 - 1.0;
c = (a-sqrt((a+b)*(a-b)))/b; c = (a - sqrt((a + b) * (a - b))) / b;
coef1 = (short)(c*8192); coef1 = (short)(c * 8192);
coef2 = (short)(c*c*-4096); coef2 = (short)(c * c * -4096);
for (i = 0; i < channel_count; i++) { for (i = 0; i < channel_count; i++) {
vgmstream->ch[i].adpcm_coef[0] = coef1; vgmstream->ch[i].adpcm_coef[0] = coef1;
@ -213,7 +216,7 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) {
{ {
int i; 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). /* 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. */ * Not vital as their effect is small, after a few samples they don't matter, and most songs start in silence. */
if (hist_offset) { if (hist_offset) {
@ -228,7 +231,7 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) {
vgmstream->ch[i].adx_mult = xor_mult; vgmstream->ch[i].adx_mult = xor_mult;
vgmstream->ch[i].adx_add = xor_add; 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]); adx_next_key(&vgmstream->ch[i]);
} }
} }
@ -245,12 +248,14 @@ fail:
} }
/* return 0 if not found, 1 if found and set parameters */ /* ADX key detection works by reading XORed ADPCM scales in frames, and un-XORing with keys in
static int find_adx_key(STREAMFILE *streamFile, uint8_t type, uint16_t *xor_start, uint16_t *xor_mult, uint16_t *xor_add) { * a list. If resulting values are within the expected range for N scales we accept that key. */
uint16_t * scales = NULL; static int find_adx_key(STREAMFILE *sf, uint8_t type, uint16_t *xor_start, uint16_t *xor_mult, uint16_t *xor_add) {
uint16_t * prescales = NULL; const int frame_size = 0x12;
int bruteframe = 0, bruteframe_count = -1; uint16_t *scales = NULL;
int startoff, endoff; uint16_t *prescales = NULL;
int bruteframe_start = 0, bruteframe_count = -1;
off_t start_offset;
int i, rc = 0; 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; size_t key_size;
/* handle type8 keystrings, key9 keycodes and derived keys too */ /* 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) { if (key_size > 0) {
int i, is_ascii = 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 */ /* setup totals */
{ {
int frame_count; 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; start_offset = read_16bitBE(0x02, sf) + 0x4;
endoff = (read_32bitBE(12, streamFile) + 31) / 32 * 18 * read_8bit(7, streamFile) + startoff; 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) if (frame_count < bruteframe_count || bruteframe_count < 0)
bruteframe_count = frame_count; 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; static const uint8_t zeroes[0x12] = {0};
int length = 0; uint8_t frame[0x12];
int longest_start = -1, longest_count = -1;
int count = 0;
for (i = 0; i < bruteframe_count; i++) { for (i = 0; i < bruteframe_count; i++) {
static const unsigned char zeroes[18] = {0}; read_streamfile(frame, start_offset + i*frame_size, frame_size, sf);
unsigned char buf[18]; if (memcmp(zeroes, frame, frame_size) != 0)
read_streamfile(buf, startoff + i * 18, 18, streamFile); count++;
if (memcmp(zeroes, buf, 18))
length++;
else else
length = 0; count = 0;
if (length > longest_length) {
longest_length = length; /* update new record of non-zero frames */
longest = i - length + 1; if (count > longest_count) {
if (longest_length >= 0x8000) longest_count = count;
longest_start = i - count + 1;
if (longest_count >= ADX_KEY_MAX_TEST_FRAMES)
break; 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 */ /* allocate storage for scales */
scales_to_do = (bruteframe_count > MAX_TEST_FRAMES ? MAX_TEST_FRAMES : bruteframe_count); scales = malloc(bruteframe_count * sizeof(uint16_t));
scales = malloc(scales_to_do*sizeof(uint16_t)); if (!scales) goto done;
if (!scales) goto find_key_cleanup;
/* prescales are those scales before the first frame we test /* prescales are scales before the first test frame, with some blank frames no good
* against, we use these to compute the actual start */ * for key testing, but we must read to compute XOR value at bruteframe_start */
if (bruteframe > 0) { if (bruteframe_start > 0) {
/* allocate memory for the prescales */ /* allocate storage for prescales */
prescales = malloc(bruteframe*sizeof(uint16_t)); prescales = malloc(bruteframe_start * sizeof(uint16_t));
if (!prescales) goto find_key_cleanup; if (!prescales) goto done;
/* read the prescales */ /* read the prescales */
for (i=0; i<bruteframe; i++) { for (i = 0; i < bruteframe_start; i++) {
prescales[i] = read_16bitBE(startoff+i*18, streamFile); prescales[i] = read_16bitBE(start_offset + i*frame_size, sf);
} }
} }
/* read in the scales */ /* read in the scales */
for (i=0; i < scales_to_do; i++) { for (i = 0; i < bruteframe_count; i++) {
scales[i] = read_16bitBE(startoff+(bruteframe+i)*18, streamFile); 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) { if (type == 8) {
keys = adxkey8_list; keys = adxkey8_list;
keycount = adxkey8_list_count; keycount = adxkey8_list_count;
keymask = 0x6000; keymask = 0x6000;
} }
else if (type == 9) { else { //if (type == 9)
/* smarter XOR as seen in PSO2. The scale is technically 13 bits, /* smarter XOR as seen in PSO2. The scale is technically 13 bits,
* but the maximum value assigned by the encoder is 0x1000. * but the maximum value assigned by the encoder is 0x1000.
* This is written to the ADX file as 0xFFF, leaving the high bit * 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; 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++) { for (key_id = 0; key_id < keycount; key_id++) {
uint16_t key_xor, key_mul, key_add; uint16_t key_xor, key_mul, key_add;
uint16_t xor, mul, 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); VGM_LOG("ADX: incorrectly defined key id=%i\n", key_id);
continue; continue;
} }
/* temp test values */ /* temp test values */
xor = key_xor; xor = key_xor;
mul = key_mul; mul = key_mul;
@ -428,30 +447,34 @@ static int find_adx_key(STREAMFILE *streamFile, uint8_t type, uint16_t *xor_star
} }
#endif #endif
/* test vs prescales while XOR looks valid */
/* test vs prescales while xor looks valid */ for (i = 0; i < bruteframe_start; i++) {
for (i = 0; i < bruteframe && ((prescales[i] & keymask) == (xor & keymask) || prescales[i] == 0); i++) { if ((prescales[i] & keymask) != (xor & keymask) && prescales[i] != 0)
break;
xor = xor * mul + add; xor = xor * mul + add;
} }
if (i == bruteframe) { if (i != bruteframe_start)
/* test vs scales while xor looks valid */ continue;
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;
rc = 1; /* test vs scales while XOR looks valid */
goto find_key_cleanup; 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(scales);
free(prescales); free(prescales);
return rc; return rc;

View File

@ -1,67 +1,53 @@
#include "meta.h" #include "meta.h"
#include "../util.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 * init_vgmstream_afc(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; 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 */ /* checks */
streamFile->get_name(streamFile,filename,sizeof(filename)); /* .afc: common
if (strcasecmp("afc",filename_extension(filename))) goto fail; * .stx: Pikmin (GC) */
if (!check_extensions(streamFile, "afc,stx"))
/* don't grab AIFF-C with .afc extension */
if ((uint32_t)read_32bitBE(0x0,streamFile)==0x464F524D) /* FORM */
goto fail; goto fail;
/* we will get a sample rate, that's as close to checking as I think if (read_u32be(0x00, streamFile) > get_streamfile_size(streamFile)) /* size without padding */
* we can get */ 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 */ /* build the VGMSTREAM */
loop_flag = read_32bitBE(0x10,streamFile);
vgmstream = allocate_vgmstream(channel_count,loop_flag); vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
/* fill in the vital statistics */ vgmstream->meta_type = meta_AFC;
vgmstream->num_samples = read_32bitBE(0x04,streamFile); vgmstream->num_samples = read_s32be(0x04, streamFile);
vgmstream->sample_rate = (uint16_t)read_16bitBE(0x08,streamFile); vgmstream->sample_rate = read_u16be(0x08, streamFile);
/* channels and loop flag are set by allocate_vgmstream */ vgmstream->loop_start_sample = read_s32be(0x14, streamFile);
vgmstream->loop_start_sample = read_32bitBE(0x14,streamFile);
vgmstream->loop_end_sample = vgmstream->num_samples; vgmstream->loop_end_sample = vgmstream->num_samples;
vgmstream->coding_type = coding_NGC_AFC; vgmstream->coding_type = coding_NGC_AFC;
vgmstream->layout_type = layout_interleave; vgmstream->layout_type = layout_interleave;
vgmstream->meta_type = meta_AFC; vgmstream->interleave_block_size = 0x09;
/* 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;
}
}
if (!vgmstream_open_stream(vgmstream, streamFile, start_offset))
goto fail;
return vgmstream; return vgmstream;
/* clean up anything we may have opened */
fail: fail:
if (vgmstream) close_vgmstream(vgmstream); close_vgmstream(vgmstream);
return NULL; return NULL;
} }

View File

@ -30,40 +30,62 @@ typedef struct {
static int parse_header(STREAMFILE* streamFile, ea_header* ea, off_t begin_offset); static int parse_header(STREAMFILE* streamFile, ea_header* ea, off_t begin_offset);
static VGMSTREAM * init_vgmstream_main(STREAMFILE *streamFile, ea_header* ea); 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); 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) */ /* EA 1SNh - from early EA games, stream (~1996, ex. Need for Speed) */
VGMSTREAM * init_vgmstream_ea_1snh(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_ea_1snh(STREAMFILE *streamFile) {
ea_header ea = {0}; ea_header ea = { 0 };
off_t eacs_offset; off_t offset, eacs_offset;
VGMSTREAM *vgmstream = NULL;
/* checks */ /* checks */
/* .asf/as4: common, /* .asf/as4: common,
* .lasf: fake for plugins * .lasf: fake for plugins
* .cnk: some PS games * .cnk: some PS1 games
* .sng: fake for plugins (to mimic EA SCHl's common extension) * .sng: fake for plugins (to mimic EA SCHl's common extension)
* .uv/tgq: some SAT games (video only?) */ * .uv/tgq: some SAT games (video only?)
if (!check_extensions(streamFile,"asf,lasf,as4,cnk,sng,uv,tgq")) * .tgv: videos
* (extensionless): Need for Speed (SAT) (videos) */
if (!check_extensions(streamFile, "asf,lasf,as4,cnk,sng,uv,tgq,tgv,"))
goto fail; goto fail;
if (read_32bitBE(0x00,streamFile) != 0x31534E68 && /* "1SNh" */ offset = 0x00;
read_32bitBE(0x00,streamFile) != 0x53454144) /* "SEAD" */
/* 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; goto fail;
/* stream is divided into blocks/chunks: 1SNh=audio header, 1SNd=data xN, 1SNl=loop end, 1SNe=end. /* 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). */ * Video uses various blocks (TGVk/TGVf/MUVf/etc) and sometimes alt audio blocks (SEAD/SNDC/SEND). */
ea.is_sead = read_32bitBE(0x00,streamFile) == 0x53454144; ea.is_sead = read_32bitBE(offset + 0x00, streamFile) == 0x53454144;
/* use block size as endian marker (Saturn = BE) */ eacs_offset = offset + 0x08; /* after 1SNh block id+size */
ea.big_endian = guess_endianness32bit(0x04,streamFile);
eacs_offset = 0x08; /* after 1SNh block id+size */ if (!parse_header(streamFile, &ea, eacs_offset))
if (!parse_header(streamFile,&ea, eacs_offset))
goto fail; 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: fail:
return NULL; return NULL;
@ -113,6 +135,8 @@ VGMSTREAM * init_vgmstream_ea_eacs(STREAMFILE *streamFile) {
if (ea.total_subsongs == target_subsong) { if (ea.total_subsongs == target_subsong) {
/* 0x00: some id or flags? */ /* 0x00: some id or flags? */
eacs_offset = read_32bitLE(bank_offset + 0x04, streamFile); 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 */ /* 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; vgmstream->coding_type = coding_ULAW_int;
break; 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 */ if (ea->bits && ea->bits != 2) goto fail; /* only in EACS */
vgmstream->coding_type = coding_DVI_IMA; /* stereo/mono, high nibble first */ vgmstream->coding_type = coding_DVI_IMA; /* stereo/mono, high nibble first */
vgmstream->codec_config = ea->codec_config; vgmstream->codec_config = ea->codec_config;
break; break;
case EA_CODEC_PSX: /* Need for Speed (PS) */ case EA_CODEC_PSX: /* Need for Speed (PS1) */
vgmstream->coding_type = coding_PSX; vgmstream->coding_type = coding_PSX;
break; break;
@ -176,9 +200,9 @@ static VGMSTREAM * init_vgmstream_main(STREAMFILE *streamFile, ea_header* ea) {
goto fail; goto fail;
} }
if (!vgmstream_open_stream(vgmstream,streamFile,ea->data_offset)) if (!vgmstream_open_stream(vgmstream,streamFile,ea->data_offset))
goto fail; goto fail;
return vgmstream; return vgmstream;
fail: fail:
@ -187,10 +211,14 @@ fail:
} }
static int parse_header(STREAMFILE* streamFile, ea_header* ea, off_t offset) { 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" */ if (read_32bitBE(offset+0x00, streamFile) == 0x45414353) { /* "EACS" */
/* EACS subheader (PC, SAT) */ /* 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->sample_rate = read_32bit(offset+0x04, streamFile);
ea->bits = read_8bit(offset+0x08, streamFile); ea->bits = read_8bit(offset+0x08, streamFile);
ea->channels = read_8bit(offset+0x09, 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); ea->codec_config = get_ea_1snh_ima_version(streamFile, 0x00, ea);
/* EACS banks with empty values exist but will be rejected later */ /* 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) { else if (ea->is_sead) {
/* alt subheader (found in some PC videos) */ /* 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->sample_rate = read_32bit(offset+0x00, streamFile);
ea->channels = read_32bit(offset+0x04, streamFile); ea->channels = read_32bit(offset+0x04, streamFile);
ea->codec = read_32bit(offset+0x08, streamFile); ea->codec = read_32bit(offset+0x08, streamFile);
if (ea->codec == EA_CODEC_IMA) if (ea->codec == EA_CODEC_IMA)
ea->codec_config = get_ea_1snh_ima_version(streamFile, 0x00, ea); 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 { 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->sample_rate = read_32bit(offset+0x00, streamFile);
ea->channels = read_8bit(offset+0x18, streamFile); ea->channels = read_8bit(offset+0x18, streamFile);
ea->codec = EA_CODEC_PSX; 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); 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 */ /* 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) { static void set_ea_1snh_num_samples(VGMSTREAM *vgmstream, STREAMFILE *streamFile, ea_header *ea, int find_loop) {
int num_samples = 0, loop_start = 0, loop_end = 0, loop_start_offset = 0; int32_t num_samples = 0, block_id;
off_t block_offset = start_offset; size_t file_size;
size_t block_size, block_header, block_samples; int32_t(*read_32bit)(off_t, STREAMFILE *) = ea->big_endian ? read_32bitBE : read_32bitLE;
int32_t (*read_32bit)(off_t,STREAMFILE*) = ea->big_endian ? read_32bitBE : read_32bitLE; int loop_end_found = 0;
size_t file_size = get_streamfile_size(streamFile);
file_size = get_streamfile_size(streamFile);
vgmstream->next_block_offset = ea->data_offset;
while (block_offset < file_size) { while (vgmstream->next_block_offset < file_size) {
uint32_t id = read_32bitBE(block_offset+0x00,streamFile); block_update_ea_1snh(vgmstream->next_block_offset, vgmstream);
block_size = read_32bit(block_offset+0x04,streamFile); if (vgmstream->current_block_samples < 0)
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" */
break; 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) { block_id = read_32bitBE(vgmstream->current_block_offset, streamFile);
switch(ea->codec) { if (find_loop) {
case EA_CODEC_PSX: if (vgmstream->current_block_offset == ea->loop_start_offset) {
block_samples = ps_bytes_to_samples(block_size - block_header, ea->channels); ea->loop_start = num_samples;
break; ea->loop_flag = 1;
case EA_CODEC_IMA: block_update_ea_1snh(ea->data_offset, vgmstream);
if (ea->codec_config == 1) return;
block_samples = read_32bit(block_offset + block_header, streamFile); }
else } else {
block_samples = ima_bytes_to_samples(block_size - block_header, ea->channels); if (block_id == 0x31534E6C) { /* "1SNl" loop point found */
break; ea->loop_start_offset = read_32bit(vgmstream->current_block_offset + 0x08, streamFile);
ea->loop_end = num_samples;
loop_end_found = 1;
} }
} }
num_samples += vgmstream->current_block_samples;
/* 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;
} }
ea->num_samples = num_samples; ea->num_samples = num_samples;
ea->loop_start = loop_start;
ea->loop_end = loop_end; /* reset once we're done */
ea->loop_start_offset = loop_start_offset; 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 */ /* 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) { while (block_offset < file_size) {
uint32_t id = read_32bitBE(block_offset+0x00,streamFile); uint32_t id = read_32bitBE(block_offset+0x00,streamFile);
size_t block_size;
size_t block_size = read_32bitLE(block_offset+0x04,streamFile); /* BE in SAT, but one file may have both BE and LE chunks */
if (block_size > 0x00F00000) /* 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); 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 */ if (id == 0x31534E64 || id == 0x534E4443) { /* "1SNd" "SNDC" audio data */
size_t ima_samples = read_32bit(block_offset + 0x08, streamFile); size_t ima_samples = read_32bit(block_offset + 0x08, streamFile);

View File

@ -855,8 +855,8 @@ typedef struct {
off_t loop_offset; off_t loop_offset;
} eaac_header; } eaac_header;
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);
static layered_layout_data* build_layered_eaaudiocore_eaxma(STREAMFILE *streamFile, 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); 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). /* 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; vgmstream->layout_type = layout_segmented;
} }
else { else {
vgmstream->layout_data = build_layered_eaaudiocore_eaxma(streamData, &eaac); vgmstream->layout_data = build_layered_eaaudiocore(streamData, &eaac);
if (!vgmstream->layout_data) goto fail; if (!vgmstream->layout_data) goto fail;
vgmstream->coding_type = coding_FFmpeg; vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_layered; vgmstream->layout_type = layout_layered;
@ -1105,21 +1105,10 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST
#ifdef VGM_USE_FFMPEG #ifdef VGM_USE_FFMPEG
case EAAC_CODEC_EAOPUS: { /* EAOpus (unknown FourCC) [FIFA 17 (PC), FIFA 19 (Switch)]*/ case EAAC_CODEC_EAOPUS: { /* EAOpus (unknown FourCC) [FIFA 17 (PC), FIFA 19 (Switch)]*/
int skip = 0; vgmstream->layout_data = build_layered_eaaudiocore(streamData, &eaac);
size_t data_size; if (!vgmstream->layout_data) goto fail;
/* 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->coding_type = coding_FFmpeg; vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none; vgmstream->layout_type = layout_layered;
break; break;
} }
#endif #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, * 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). */ * and the segments seem fully separate (so even skipping would probably decode wrong). */
// todo reorganize code for more standard init // 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; 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 offsets[2] = { eaac->stream_offset, eaac->loop_offset };
off_t start_offset; off_t start_offset;
int num_samples[2] = { eaac->loop_start, eaac->num_samples - eaac->loop_start}; 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 */ start_offset = 0x00; /* must point to the custom streamfile's beginning */
/* layers inside segments, how trippy */ /* 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; if (!data->segments[i]->layout_data) goto fail;
data->segments[i]->coding_type = coding_FFmpeg; data->segments[i]->coding_type = coding_FFmpeg;
data->segments[i]->layout_type = layout_layered; 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 */ 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]); temp_sf = setup_eaac_streamfile(sf_data, eaac->version,eaac->codec,eaac->streamed,0,0, offsets[i]);
if (!temp_streamFile) goto fail; 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; if (!data->segments[i]->codec_data) goto fail;
data->segments[i]->layout_type = layout_none; data->segments[i]->layout_type = layout_none;
break; break;
@ -1273,14 +1262,14 @@ static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *st
goto fail; 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; goto fail;
close_streamfile(temp_streamFile); close_streamfile(temp_sf);
temp_streamFile = NULL; temp_sf = NULL;
//todo temp_streamFile doesn't contain EAXMA's streamfile //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)) if (!setup_layout_segmented(data))
@ -1288,14 +1277,14 @@ static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *st
return data; return data;
fail: fail:
close_streamfile(temp_streamFile); close_streamfile(temp_sf);
free_layout_segmented(data); free_layout_segmented(data);
return NULL; 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; layered_layout_data* data = NULL;
STREAMFILE* temp_streamFile = NULL; STREAMFILE* temp_sf = NULL;
int i, layers = (eaac->channels+1) / 2; 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); data = init_layout_layered(layers);
if (!data) goto fail; if (!data) goto fail;
/* open each layer subfile (1/2ch streams: 2ch+2ch..+1ch or 2ch+2ch..+2ch). /* 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) */
for (i = 0; i < layers; i++) { for (i = 0; i < layers; i++) {
int layer_channels = (i+1 == layers && eaac->channels % 2 == 1) ? 1 : 2; /* last layer can be 1/2ch */ 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; data->layers[i]->loop_end_sample = eaac->loop_end;
#ifdef VGM_USE_FFMPEG #ifdef VGM_USE_FFMPEG
{ switch(eaac->codec) {
uint8_t buf[0x100]; /* EA-XMA uses completely separate 1/2ch streams, unlike standard XMA that interleaves 1/2ch
int bytes, block_size, block_count; * streams with a skip counter to reinterleave (so EA-XMA streams don't have skips set) */
size_t stream_size; 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); temp_sf = setup_eaac_streamfile(sf_data, eaac->version, eaac->codec, eaac->streamed,i,layers, eaac->stream_offset);
if (!temp_streamFile) goto fail; if (!temp_sf) goto fail;
stream_size = get_streamfile_size(temp_streamFile); stream_size = get_streamfile_size(temp_sf);
block_size = 0x10000; /* unused */ block_size = 0x10000; /* unused */
block_count = stream_size / block_size + (stream_size % block_size ? 1 : 0); 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); 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); data->layers[i]->codec_data = init_ffmpeg_header_offset(temp_sf, buf,bytes, 0x00, stream_size);
if (!data->layers[i]->codec_data) goto fail; if (!data->layers[i]->codec_data) goto fail;
data->layers[i]->coding_type = coding_FFmpeg; data->layers[i]->coding_type = coding_FFmpeg;
data->layers[i]->layout_type = layout_none; data->layers[i]->layout_type = layout_none;
data->layers[i]->stream_size = get_streamfile_size(temp_streamFile); 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 #else
goto fail; goto fail;
#endif #endif
if ( !vgmstream_open_stream(data->layers[i], temp_streamFile, 0x00) ) { if ( !vgmstream_open_stream(data->layers[i], temp_sf, 0x00) ) {
goto fail; goto fail;
} }
} }
if (!setup_layout_layered(data)) if (!setup_layout_layered(data))
goto fail; goto fail;
close_streamfile(temp_streamFile); close_streamfile(temp_sf);
return data; return data;
fail: fail:
close_streamfile(temp_streamFile); close_streamfile(temp_sf);
free_layout_layered(data); free_layout_layered(data);
return NULL; return NULL;
} }

View File

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

View File

@ -1,6 +1,7 @@
#ifndef _EA_EAAC_STREAMFILE_H_ #ifndef _EA_EAAC_STREAMFILE_H_
#define _EA_EAAC_STREAMFILE_H_ #define _EA_EAAC_STREAMFILE_H_
#include "../streamfile.h" #include "../streamfile.h"
#include "ea_eaac_opus_streamfile.h"
#define XMA_FRAME_SIZE 0x800 #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 /* previous offset: re-start as we can't map logical<>physical offsets
* (kinda slow as it trashes buffers, but shouldn't happen often) */ * (kinda slow as it trashes buffers, but shouldn't happen often) */
if (offset < data->logical_offset) { 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->physical_offset = data->stream_offset;
data->logical_offset = 0x00; data->logical_offset = 0x00;
data->data_size = 0; data->data_size = 0;
@ -260,8 +261,9 @@ static STREAMFILE* setup_eaac_streamfile(STREAMFILE *sf, int version, int codec,
/* setup subfile */ /* setup subfile */
new_sf = open_wrap_streamfile(sf); 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); 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); /* EA-XMA and multichannel EALayer3 benefit from this */
new_sf = open_buffer_streamfile_f(new_sf, 0); 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; return new_sf;
} }

View File

@ -173,13 +173,25 @@ VGMSTREAM * init_vgmstream_ea_schl_video(STREAMFILE *streamFile) {
/* check extension */ /* check extension */
/* .vp6: ~late */ /* .uv: early */
if (!check_extensions(streamFile,"vp6")) /* .dct: early-mid [ex. Need for Speed II SE (PC), FIFA 98 (PC)] */
goto fail; /* .mad: mid */
/* .vp6: late */
/* check initial movie block id */ if (check_extensions(streamFile, "vp6")) {
if (read_32bitBE(0x00,streamFile) != 0x4D566864) /* "MVhd" */ /* 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; goto fail;
}
/* use block size to check endianness */ /* use block size to check endianness */
if (guess_endianness32bit(0x04, streamFile)) { if (guess_endianness32bit(0x04, streamFile)) {
@ -208,7 +220,7 @@ VGMSTREAM * init_vgmstream_ea_schl_video(STREAMFILE *streamFile) {
offset += block_size; offset += block_size;
} }
if (start_offset == 0) if (offset >= get_streamfile_size(streamFile))
goto fail; goto fail;
/* find target subsong (one per each SHxx multilang block) */ /* find target subsong (one per each SHxx multilang block) */
@ -469,6 +481,10 @@ VGMSTREAM * init_vgmstream_ea_hdr_dat(STREAMFILE *streamFile) {
STREAMFILE *datFile = NULL; STREAMFILE *datFile = NULL;
VGMSTREAM *vgmstream; VGMSTREAM *vgmstream;
/* checks */
if (!check_extensions(streamFile, "hdr"))
goto fail;
/* main header is machine endian but it's not important here */ /* main header is machine endian but it's not important here */
/* 0x00: ID */ /* 0x00: ID */
/* 0x02: sub-ID (used for different police voices in NFS games) */ /* 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; STREAMFILE *datFile = NULL;
VGMSTREAM *vgmstream; VGMSTREAM *vgmstream;
/* checks */
if (!check_extensions(streamFile, "hdr"))
goto fail;
/* main header is machine endian but it's not important here */ /* main header is machine endian but it's not important here */
/* 0x00: ID */ /* 0x00: ID */
/* 0x02: userdata size */ /* 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 */ /* 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] = { static const char *const mapfile_pairs[][2] = {
/* standard cases, replace map part with mus part (from the end to preserve prefixes) */ /* 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 (PS2) */
{"mus_ctrl.mpf","mus_str.mus"}, /* GoldenEye - Rogue Agent (others) */ {"mus_ctrl.mpf","mus_str.mus"}, /* GoldenEye - Rogue Agent (others) */
{".mpf","_main.mus"}, /* 007 - Everything or Nothing (GC) */ {".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 "+" /* 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 */ * ex. ZZZTR00A.TRJ+ZTR00PGR.MAP or ZZZTR00A.TRJ+ZTR00R0A.MAP both point to ZZZTR00A.TRJ */
{"+",""}, /* Need for Speed III (PS1) */ {"+",""}, /* 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 */ /* manually read totals */
block_update(start_offset, vgmstream); vgmstream->next_block_offset = start_offset;
while (vgmstream->next_block_offset < file_size) { while (vgmstream->next_block_offset < file_size) {
block_update_ea_schl(vgmstream->next_block_offset, vgmstream); block_update_ea_schl(vgmstream->next_block_offset, vgmstream);
if (vgmstream->current_block_samples < 0) 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 */ /* 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) */ /* only use calculated samples with multiple subfiles (rarely header samples may be less due to padding) */
if (standalone && multiple_schl) { if (standalone && multiple_schl) {

View File

@ -25,7 +25,7 @@ VGMSTREAM * init_vgmstream_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start,
// goto fail; // goto fail;
/* don't try to open headers and other mini files */ /* don't try to open headers and other mini files */
if (get_streamfile_size(streamFile) <= 0x100) if (get_streamfile_size(streamFile) <= 0x1000)
goto fail; goto fail;

View File

@ -300,6 +300,9 @@ static const hcakey_info hcakey_list[] = {
/* Love Live! School idol festival ALL STARS (Android) */ /* Love Live! School idol festival ALL STARS (Android) */
{6498535309877346413}, // 5A2F6F6F0192806D {6498535309877346413}, // 5A2F6F6F0192806D
/* BLACKSTAR -Theater Starless- (Android) */
{121837007188}, // 0000001C5E0D3154
/* Dragalia Lost (Cygames) [iOS/Android] */ /* Dragalia Lost (Cygames) [iOS/Android] */
{2967411924141, subkeys_dgl, sizeof(subkeys_dgl) / sizeof(subkeys_dgl[0]) }, // 000002B2E7889CAD {2967411924141, subkeys_dgl, sizeof(subkeys_dgl) / sizeof(subkeys_dgl[0]) }, // 000002B2E7889CAD

View File

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

View File

@ -1,59 +1,47 @@
#include "meta.h" #include "meta.h"
#include "../util.h" #include "../util.h"
/* .lsf - Fastlane Street Racing (iPhone) */ /* .lsf - from Atod games [Fastlane Street Racing (iPhone), Chicane Street Racing prototype (Gizmondo)] */
/* "!n1nj4n" */
VGMSTREAM * init_vgmstream_lsf_n1nj4n(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_lsf_n1nj4n(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
size_t file_size;
off_t start_offset; 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 */ /* checks */
if (read_32bitBE(0x0, streamFile) != 0x216E316E || // "!n1n" if (!check_extensions(streamFile, "lsf"))
read_32bitBE(0x4, streamFile) != 0x6A346E00) // "j4n\0" goto fail;
if (read_32bitBE(0x00, streamFile) != 0x216E316E || // "!n1n"
read_32bitBE(0x04, streamFile) != 0x6A346E00) // "j4n\0"
goto fail; goto fail;
/* check size */
file_size = get_streamfile_size(streamFile); file_size = get_streamfile_size(streamFile);
if (read_32bitLE(0xC, streamFile) + 0x10 != file_size) if (read_32bitLE(0x0C, streamFile) + 0x10 != file_size)
goto fail; goto fail;
loop_flag = 0;
channel_count = 1;
start_offset = 0x10; start_offset = 0x10;
/* build the VGMSTREAM */ /* build the VGMSTREAM */
vgmstream = allocate_vgmstream(1,0); vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail; 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->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 */ if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
{ goto fail;
vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!vgmstream->ch[0].streamfile) goto fail;
vgmstream->ch[0].channel_start_offset=
vgmstream->ch[0].offset=start_offset;
}
return vgmstream; return vgmstream;
/* clean up anything we may have opened */
fail: fail:
if (vgmstream) close_vgmstream(vgmstream); close_vgmstream(vgmstream);
return NULL; return NULL;
} }

View File

@ -87,6 +87,7 @@ VGMSTREAM * init_vgmstream_ps2_mic(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_raw_pcm(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_raw_pcm(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_vag(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_vag(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_vag_aaap(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_seb(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_xbox_hlwav(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_stx(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_myspd(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_myspd(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_his(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_bmp_konami(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_isb(STREAMFILE * streamFile);
VGMSTREAM* init_vgmstream_xssb(STREAMFILE *sf);
#endif /*_META_H*/ #endif /*_META_H*/

View File

@ -8,7 +8,6 @@ VGMSTREAM * init_vgmstream_mta2(STREAMFILE *streamFile) {
off_t start_offset; off_t start_offset;
int loop_flag, channel_count, sample_rate; int loop_flag, channel_count, sample_rate;
int32_t loop_start, loop_end; int32_t loop_start, loop_end;
uint32_t sample_rate_int;
/* checks */ /* checks */
@ -47,13 +46,9 @@ VGMSTREAM * init_vgmstream_mta2(STREAMFILE *streamFile) {
} }
#endif #endif
sample_rate_int = read_32bitBE(0x7c, streamFile); sample_rate = (int)read_f32be(0x7c, streamFile); /* sample rate in 32b float (WHY?) typically 48000.0 */
if (sample_rate_int) { /* sample rate in 32b float (WHY?) typically 48000.0 */ if (sample_rate == 0)
float* sample_float = (float*)&sample_rate_int; sample_rate = 48000; /* default when not specified (most of the time) */
sample_rate = (int)*sample_float;
} else { /* default when not specified (most of the time) */
sample_rate = 48000;
}
/* TRKP chunks (x16) */ /* TRKP chunks (x16) */

View File

@ -20,14 +20,14 @@ typedef struct {
} mta2_io_data; } 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; size_t total_read = 0;
uint32_t (*read_u32)(off_t,STREAMFILE*) = data->big_endian ? read_u32be : read_u32le; 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) */ /* re-start when previous offset (can't map logical<>physical offsets) */
if (data->logical_offset < 0 || offset < data->logical_offset) { 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->physical_offset = data->stream_offset;
data->logical_offset = 0x00; data->logical_offset = 0x00;
data->data_size = 0; 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) { if (data->data_size == 0) {
uint32_t block_type, block_size, block_track; uint32_t block_type, block_size, block_track;
block_type = read_u32(data->physical_offset+0x00, streamfile); /* subtype and type */ block_type = read_u32(data->physical_offset+0x00, sf); /* subtype and type */
block_size = read_u32(data->physical_offset+0x04, streamfile); 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_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) if (block_type != data->target_type || block_size == 0xFFFFFFFF)
break; 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; to_read = data->data_size - bytes_consumed;
if (to_read > length) if (to_read > length)
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; total_read += bytes_done;
dest += 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) { static size_t mta2_io_size(STREAMFILE *streamfile, mta2_io_data* data) {
uint8_t buf[1]; uint8_t buf[1];
if (data->logical_size) if (data->logical_size > 0)
return data->logical_size; return data->logical_size;
/* force a fake read at max offset, to get max logical_offset (will be reset next read) */ /* 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 /* Handles removing KCE Japan-style blocks in MTA2 streams
* (these blocks exist in most KCEJ games and aren't actually related to audio) */ * (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) { static STREAMFILE* setup_mta2_streamfile(STREAMFILE *sf, off_t stream_offset, int big_endian, const char *extension) {
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; STREAMFILE *new_sf = NULL;
mta2_io_data io_data = {0}; 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; uint32_t (*read_u32)(off_t,STREAMFILE*) = big_endian ? read_u32be : read_u32le;
/* blocks must start with a 'new sub-stream' id */ /* blocks must start with a 'new sub-stream' id */
if (read_u32(stream_offset+0x00, streamFile) != 0x00000010) if (read_u32(stream_offset+0x00, sf) != 0x00000010)
goto fail; 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_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.big_endian = big_endian;
io_data.logical_offset = -1; /* force phys offset reset */ io_data.logical_offset = -1; /* force phys offset reset */
/* setup subfile */ /* setup subfile */
new_streamFile = open_wrap_streamfile(streamFile); new_sf = open_wrap_streamfile(sf);
if (!new_streamFile) goto fail; new_sf = open_io_streamfile_f(new_sf, &io_data, sizeof(mta2_io_data), mta2_io_read, mta2_io_size);
temp_streamFile = new_streamFile; if (extension)
new_sf = open_fakename_streamfile_f(new_sf, NULL, extension);
new_streamFile = open_io_streamfile(new_streamFile, &io_data,io_data_size, mta2_io_read,mta2_io_size); return new_sf;
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;
} }
#endif /* _MTA2_STREAMFILE_H_ */ #endif /* _MTA2_STREAMFILE_H_ */

View File

@ -1,54 +1,32 @@
#include "meta.h" #include "meta.h"
#include "../util.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 * init_vgmstream_sadl(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t start_offset; off_t start_offset;
int loop_flag, channel_count;
int loop_flag;
int channel_count;
int coding_type;
/* check extension, case insensitive */ /* checks */
streamFile->get_name(streamFile,filename,sizeof(filename)); if (!check_extensions(streamFile, "sad"))
if (strcasecmp("sad",filename_extension(filename))) goto fail; goto fail;
/* check header */
if (read_32bitBE(0x00,streamFile) != 0x7361646c) /* "sadl" */ if (read_32bitBE(0x00,streamFile) != 0x7361646c) /* "sadl" */
goto fail; goto fail;
if (read_32bitLE(0x40,streamFile) != get_streamfile_size(streamFile))
/* check file size */
if (read_32bitLE(0x40,streamFile) != get_streamfile_size(streamFile) )
goto fail; 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); loop_flag = read_8bit(0x31,streamFile);
channel_count = read_8bit(0x32,streamFile); channel_count = read_8bit(0x32,streamFile);
start_offset = 0x100;
/* build the VGMSTREAM */ /* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag); vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
/* fill in the vital statistics */ switch (read_8bit(0x33,streamFile) & 6) {
start_offset = 0x100;
vgmstream->channels = channel_count;
switch (read_8bit(0x33,streamFile) & 6)
{
case 4: case 4:
vgmstream->sample_rate = 32728; vgmstream->sample_rate = 32728;
break; break;
@ -59,52 +37,37 @@ VGMSTREAM * init_vgmstream_sadl(STREAMFILE *streamFile) {
goto fail; 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; vgmstream->meta_type = meta_SADL;
/* open the file for reading */ vgmstream->layout_type = layout_interleave;
{ vgmstream->interleave_block_size = 0x10;
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= switch(read_8bit(0x33,streamFile) & 0xf0) {
vgmstream->ch[i].offset=start_offset+ case 0x70: /* Ni no Kuni (DS), Professor Layton and the Curious Village (DS), Soma Bringer (DS) */
vgmstream->interleave_block_size*i; 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; return vgmstream;
/* clean up anything we may have opened */
fail: fail:
if (vgmstream) close_vgmstream(vgmstream); close_vgmstream(vgmstream);
return NULL; return NULL;
} }

View File

@ -1,56 +1,42 @@
#include "meta.h" #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. /* 2PFS - from Konami Games [Mahoromatic: Moetto - KiraKira Maid-San (PS2), GANTZ The Game (PS2)] */
Implemented both versions here in case there are .2pfs with the V2 header out there. VGMSTREAM * init_vgmstream_ps2_2pfs(STREAMFILE *streamFile) {
Both loop correctly AFAIK (there is a truncated Mahoromatic rip around, beware).
*/
VGMSTREAM * init_vgmstream_ps2_2pfs(STREAMFILE *streamFile)
{
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT]; off_t start_offset;
int loop_flag, channel_count, version, interleave;
off_t start_offset = 0x800; int loop_start_block, loop_end_block; /* block number */
int interleave = 0x1000; int loop_start_adjust, loop_end_adjust; /* loops start/end a few samples into the start/end block */
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;
/* check extension, case insensitive */ /* checks */
streamFile->get_name(streamFile,filename,sizeof(filename)); /* .sap: standard
if ( strcasecmp("2pfs",filename_extension(filename)) * .2psf: header id? (Mahoromatic) */
&& strcasecmp("sap",filename_extension(filename)) ) if (!check_extensions(streamFile, "sap,2psf"))
goto fail; goto fail;
/* check header ("2PFS") */ if (read_32bitBE(0x00,streamFile) != 0x32504653) /* "2PFS" */
if (read_32bitBE(0x00,streamFile) != 0x32504653)
goto fail; goto fail;
version = read_16bitLE(0x04,streamFile); version = read_16bitLE(0x04,streamFile);
if ( version!=0x01 && version!=0x02 ) if (version != 0x01 && version != 0x02) /* v1: Mahoromatic, v2: Gantz */
goto fail; goto fail;
channel_count = read_8bit(0x40,streamFile); channel_count = read_8bit(0x40,streamFile);
loop_flag = read_8bit(0x41,streamFile); loop_flag = read_8bit(0x41,streamFile);
start_offset = 0x800;
interleave = 0x1000;
/* other header values /* other header values
* 0x06 (4): unknown, v1=0x0004 v2=0x0001 * 0x06: unknown, v1=0x0004 v2=0x0001
* 0x08 (32): unique file id * 0x08: unique file id
* 0x0c (32): base header size (v1=0x50, v2=0x60) + datasize (without the 0x800 full header size) * 0x0c: base header size (v1=0x50, v2=0x60) + datasize (without the 0x800 full header size)
* 0x10-0x30: unknown (v1 differs from v2) * 0x10-0x30: unknown (v1 differs from v2)
* 0x38-0x40: unknown (v1 same as 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); vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
/* fill in the vital statistics */ vgmstream->meta_type = meta_PS2_2PFS;
vgmstream->channels = channel_count;
vgmstream->coding_type = coding_PSX;
vgmstream->num_samples = read_32bitLE(0x34,streamFile) * 28 / 16 / channel_count; vgmstream->num_samples = read_32bitLE(0x34,streamFile) * 28 / 16 / channel_count;
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave; vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 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); 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_start_block = read_32bitLE(0x48,streamFile);
loop_end_block = read_32bitLE(0x4c,streamFile); loop_end_block = read_32bitLE(0x4c,streamFile);
} else { }
else {
vgmstream->sample_rate = read_32bitLE(0x48,streamFile); 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_start_block = read_32bitLE(0x50,streamFile);
loop_end_block = read_32bitLE(0x54,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 ) { if (loop_flag) {
/* block to offset > offset to sample + adjust (number of samples into the block) */ /* block to offset > offset to sample + adjust (number of frames into the block) */
vgmstream->loop_start_sample = ((loop_start_block * channel_count * interleave) vgmstream->loop_start_sample =
* 28 / 16 / channel_count) ps_bytes_to_samples(loop_start_block * channel_count * interleave, channel_count)
+ (loop_start_sample_adjust * 28 / 16); + ps_bytes_to_samples(loop_start_adjust * channel_count, channel_count);
vgmstream->loop_end_sample = ((loop_end_block * channel_count * interleave) vgmstream->loop_end_sample =
* 28 / 16 / channel_count) ps_bytes_to_samples(loop_end_block * channel_count * interleave, channel_count)
+ (loop_end_sample_adjust * 28 / 16); + ps_bytes_to_samples(loop_end_adjust * channel_count, channel_count);
}
/* 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 (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
return vgmstream; return vgmstream;
/* clean up anything we may have opened */
fail: fail:
if (vgmstream) close_vgmstream(vgmstream); if (vgmstream) close_vgmstream(vgmstream);
return NULL; return NULL;

View File

@ -129,16 +129,18 @@ VGMSTREAM * init_vgmstream_rwsd(STREAMFILE *streamFile) {
{ {
if (strcasecmp("rwav",ext)) if (strcasecmp("rwav",ext))
{ {
if (strcasecmp("bcwav",ext) && strcasecmp("bms",ext)) /* .bcwav: standard
{ * .bms: ?
goto fail; * .sfx: Wizdom (3DS)
} * .str: Pac-Man and the Ghostly Adventures 2 (3DS)
else * .zic: Wizdom (3DS) */
{ if (check_extensions(streamFile, "bcwav,bms,sfx,str,zic")) {
// cwav, similar to little endian rwav rwav = 1; // cwav, similar to little endian rwav
rwav = 1;
big_endian = 0; big_endian = 0;
} }
else {
goto fail;
}
} }
else else
{ {

View File

@ -112,7 +112,7 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamHeader); read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamHeader);
switch (type) { 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?) */ 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); vgmstream->codec_data = init_ogg_vorbis(streamFile, start_offset, stream_size, NULL);
if (!vgmstream->codec_data) goto fail; if (!vgmstream->codec_data) goto fail;

View File

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

View File

@ -1058,6 +1058,9 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char
/* COEFS */ /* COEFS */
else if (is_string(key,"coef_offset")) { else if (is_string(key,"coef_offset")) {
if (!parse_num(txth->streamHead,txth,val, &txth->coef_offset)) goto fail; 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")) { else if (is_string(key,"coef_spacing")) {
if (!parse_num(txth->streamHead,txth,val, &txth->coef_spacing)) goto fail; 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")) { else if (is_string(key,"hist_offset")) {
if (!parse_num(txth->streamHead,txth,val, &txth->hist_offset)) goto fail; if (!parse_num(txth->streamHead,txth,val, &txth->hist_offset)) goto fail;
txth->hist_set = 1; 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")) { else if (is_string(key,"hist_spacing")) {
if (!parse_num(txth->streamHead,txth,val, &txth->hist_spacing)) goto fail; 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")) { else if (is_string(key,"name_offset")) {
if (!parse_num(txth->streamHead,txth,val, &txth->name_offset)) goto fail; if (!parse_num(txth->streamHead,txth,val, &txth->name_offset)) goto fail;
txth->name_offset_set = 1; txth->name_offset_set = 1;
/* special subsong adjustment */ /* special adjustment */
if (txth->subsong_offset) 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")) { else if (is_string(key,"name_size")) {
if (!parse_num(txth->streamHead,txth,val, &txth->name_size)) goto fail; if (!parse_num(txth->streamHead,txth,val, &txth->name_size)) goto fail;

View File

@ -1013,10 +1013,8 @@ fail:
} }
static int parse_type_silence(ubi_bao_header * bao, off_t offset, STREAMFILE* streamFile) { 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; off_t h_offset = offset + bao->header_skip;
uint32_t duration_int;
float* duration_float;
/* silence header */ /* silence header */
bao->type = UBI_SILENCE; bao->type = UBI_SILENCE;
@ -1025,13 +1023,8 @@ static int parse_type_silence(ubi_bao_header * bao, off_t offset, STREAMFILE* st
goto fail; goto fail;
} }
{ bao->duration = read_f32(h_offset + bao->cfg.silence_duration_float, streamFile);
duration_int = (uint32_t)read_32bit(h_offset + bao->cfg.silence_duration_float, streamFile); if (bao->duration <= 0.0f) {
duration_float = (float*)&duration_int;
bao->duration = *duration_float;
}
if (bao->duration <= 0) {
VGM_LOG("UBI BAO: bad duration %f at %x\n", bao->duration, (uint32_t)offset); VGM_LOG("UBI BAO: bad duration %f at %x\n", bao->duration, (uint32_t)offset);
goto fail; goto fail;
} }

View File

@ -1369,9 +1369,8 @@ fail:
} }
static int parse_type_silence(ubi_sb_header * sb, off_t offset, STREAMFILE* streamFile) { 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; int32_t (*read_32bit)(off_t,STREAMFILE*) = sb->big_endian ? read_32bitBE : read_32bitLE;
uint32_t duration_int;
float* duration_float;
/* silence header */ /* silence header */
sb->type = UBI_SILENCE; 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) { 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 */ sb->duration = (float)duration_int / 65536.0f; /* 65536.0 is common so probably means 1.0 */
} }
else if (sb->cfg.silence_duration_float) { else if (sb->cfg.silence_duration_float) {
duration_int = (uint32_t)read_32bit(offset + sb->cfg.silence_duration_float, streamFile); sb->duration = read_f32(offset + sb->cfg.silence_duration_float, streamFile);
duration_float = (float*)&duration_int;
sb->duration = *duration_float;
} }
return 1; return 1;

View File

@ -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); 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) { else if (version == 0x40000000) {
/* Killzone (PS2) */ /* Killzone (PS2) */
start_offset = 0x30; start_offset = 0x30;
@ -217,9 +203,32 @@ VGMSTREAM * init_vgmstream_vag(STREAMFILE *streamFile) {
/* Need for Speed: Hot Pursuit 2 (PS2) */ /* Need for Speed: Hot Pursuit 2 (PS2) */
start_offset = 0x30; start_offset = 0x30;
channel_count = read_32bitBE(0x2c, streamFile); channel_count = read_32bitBE(0x2c, streamFile);
interleave = 0x8000;
channel_size = channel_size / channel_count; channel_size = channel_size / channel_count;
loop_flag = 0; 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 { else {
/* standard PS1/PS2/PS3 .vag [Ecco the Dolphin (PS2), Legasista (PS3)] */ /* standard PS1/PS2/PS3 .vag [Ecco the Dolphin (PS2), Legasista (PS3)] */
@ -267,3 +276,59 @@ fail:
close_vgmstream(vgmstream); close_vgmstream(vgmstream);
return NULL; 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;
}

View File

@ -8,10 +8,9 @@ VGMSTREAM * init_vgmstream_wave(STREAMFILE *streamFile) {
int loop_flag = 0, channel_count, sample_rate, codec; int loop_flag = 0, channel_count, sample_rate, codec;
int32_t num_samples, loop_start = 0, loop_end = 0; int32_t num_samples, loop_start = 0, loop_end = 0;
size_t interleave; size_t interleave;
int big_endian; int big_endian;
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
//int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; float (*read_f32)(off_t,STREAMFILE*) = NULL;
/* checks */ /* checks */
if (!check_extensions(streamFile, "wave")) if (!check_extensions(streamFile, "wave"))
@ -27,10 +26,10 @@ VGMSTREAM * init_vgmstream_wave(STREAMFILE *streamFile) {
big_endian = read_32bitBE(0x00,streamFile) == 0xE5B7ECFE; big_endian = read_32bitBE(0x00,streamFile) == 0xE5B7ECFE;
if (big_endian) { if (big_endian) {
read_32bit = read_32bitBE; read_32bit = read_32bitBE;
//read_16bit = read_16bitBE; read_f32 = read_f32be;
} else { } else {
read_32bit = read_32bitLE; read_32bit = read_32bitLE;
//read_16bit = read_16bitLE; read_f32 = read_f32le;
} }
channel_count = read_8bit(0x05,streamFile); channel_count = read_8bit(0x05,streamFile);
@ -40,15 +39,7 @@ VGMSTREAM * init_vgmstream_wave(STREAMFILE *streamFile) {
if (read_8bit(0x0c,streamFile) != 0x00) /* ? */ if (read_8bit(0x0c,streamFile) != 0x00) /* ? */
goto fail; goto fail;
/* sample rate in 32b float (WHY?)*/ sample_rate = (int)read_f32(0x0c, streamFile); /* 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);
}
num_samples = read_32bit(0x10, streamFile); num_samples = read_32bit(0x10, streamFile);
loop_start = read_32bit(0x14, streamFile); loop_start = read_32bit(0x14, streamFile);
loop_end = read_32bit(0x18, streamFile); loop_end = read_32bit(0x18, streamFile);

View File

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

View File

@ -30,6 +30,9 @@ typedef struct {
int cue_names_size; int cue_names_size;
off_t cue_names_offset; off_t cue_names_offset;
int index_size;
int entry_size;
/* output */ /* output */
int parse_done; int parse_done;
char name[STREAM_NAME_SIZE]; char name[STREAM_NAME_SIZE];
@ -38,9 +41,16 @@ typedef struct {
} xsb_header; } 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) if (xsb->parse_done)
return; 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 */ /* multiple names may correspond to a stream (ex. Blue Dragon), so we concat all */
if (xsb->selected_stream == stream_index && 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]; char name[STREAM_NAME_SIZE];
size_t 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) { if (xsb->name_len) {
const char *cat = "; "; 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 static int parse_xsb_old_cue_entry(xsb_header *xsb, STREAMFILE *sf, off_t name_offset, int entry) {
* - 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) {
int32_t (*read_s32)(off_t,STREAMFILE*) = xsb->big_endian ? read_s32be : read_s32le; 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; int16_t (*read_s16)(off_t,STREAMFILE*) = xsb->big_endian ? read_s16be : read_s16le;
uint8_t flags, subflags; uint8_t flags, subflags;
int cue_index, stream_index, wavebank_index = 0; uint32_t sound_type, sound_size;
off_t offset, name_offset, cue_offset, sound_offset; int stream_index, wavebank_index;
int i; off_t offset, jump_offset, sound_offset, min_sections_offset, max_sections_offset;
size_t simple_entry, complex_entry; int i, j, sound_count, table_count;
if (xsb->version <= XSB_XACT1_1_MAX) { if (entry < 0 || entry > xsb->complex_cues_count) {
simple_entry = 0x10; VGM_LOG("XSB old: ignored bad cue entry %i\n", entry);
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);
goto fail; 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; offset = xsb->sounds_offset + xsb->simple_cues_count*xsb->index_size + entry*xsb->entry_size;
for (i = 0; i < xsb->simple_cues_count; i++) {
/* *** 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); /*** cue entry ***/
offset += simple_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 (flags & 0x10) { /* multi entry (found with lower bits but not with 8) */
if (cue_index < 0 && cue_index > xsb->complex_cues_count) { jump_offset = read_s32(offset + 0x00, sf);
VGM_LOG("XSB old: ignored cue index %i\n", cue_index);
continue; 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 *** */ for (j = 0; j < table_count; j++) {
cue_offset = xsb->sounds_offset + xsb->simple_cues_count*simple_entry + cue_index*complex_entry; 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 */ xsb_check_stream(xsb, stream_index, wavebank_index, name_offset, sf);
flags = read_u8(cue_offset + 0x0b,sf); if (xsb->parse_done) return 1;
if (flags & 8) { /* simple */
stream_index = read_s16(cue_offset + 0x00,sf);
wavebank_index = read_s16(cue_offset + 0x02,sf);
} }
//else if (flags & 4) { /* unsure */ }
// VGM_LOG("XSB old complex at %lx: unknown flags=%x\n", cue_offset, flags); else if (flags & 0x8) { /* simple entry (also found with lower bits) */
// continue; stream_index = read_s16(offset + 0x00, sf);
//} wavebank_index = read_s16(offset + 0x02, sf);
else { /* complex (flags none/1/2) */
sound_offset = read_s32(cue_offset + 0x00,sf);
/* *** jump entry *** */ xsb_check_stream(xsb, stream_index, wavebank_index, name_offset, sf);
/* 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);
if (xsb->parse_done) return 1; 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; return 1;
fail: fail:
return 0; 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; 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; 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; 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); //;VGM_LOG("XSB clip at %lx\n", offset);
offset += 0x01; offset += 0x01;
for (i = 0; i < event_count; i++) { for (i = 0; i < event_count; i++) {
flags = read_u32(offset + 0x00,sf); flags = read_u32(offset + 0x00, sf);
/* 04(2): random offset */ /* 04(2): random offset */
//;VGM_LOG("XSB clip event: %x at %lx\n", flags, 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 */ case 0x01: /* playwave event */
/* 00(1): unknown */ /* 00(1): unknown */
/* 01(1): flags */ /* 01(1): flags */
stream_index = read_s16(offset + 0x02,sf); stream_index = read_s16(offset + 0x02, sf);
wavebank_index = read_s8 (offset + 0x04,sf); wavebank_index = read_s8 (offset + 0x04, sf);
/* 05(1): loop count */ /* 05(1): loop count */
/* 06(2): pan angle */ /* 06(2): pan angle */
/* 08(2): pan arc */ /* 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); //;VGM_LOG("XSB clip event 1 at %lx: stream=%i, wavebank=%i\n", offset, stream_index, wavebank_index);
offset += 0x0a; 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; if (xsb->parse_done) return 1;
break; 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 */ /* 02(1): loop count */
/* 03(2): pan angle */ /* 03(2): pan angle */
/* 05(2): pan arc */ /* 05(2): pan arc */
track_count = read_s16(offset + 0x07,sf); track_count = read_s16(offset + 0x07, sf);
/* 09(1): flags? */ /* 09(1): flags? */
/* 0a(5): unknown */ /* 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; offset += 0x0F;
for (t = 0; t < track_count; t++) { for (t = 0; t < track_count; t++) {
stream_index = read_s16(offset + 0x00,sf); stream_index = read_s16(offset + 0x00, sf);
wavebank_index = read_s8 (offset + 0x02,sf); wavebank_index = read_s8 (offset + 0x02, sf);
/* 03(1): min weight */ /* 03(1): min weight */
/* 04(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); //;VGM_LOG("XSB clip event 3: track=%i, stream=%i, wavebank=%i\n", t, stream_index, wavebank_index);
offset += 0x05; 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; if (xsb->parse_done) return 1;
} }
break; 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 */ case 0x04: /* playwave event */
/* 00(1): unknown */ /* 00(1): unknown */
/* 01(1): flags */ /* 01(1): flags */
stream_index = read_s16(offset + 0x02,sf); stream_index = read_s16(offset + 0x02, sf);
wavebank_index = read_s8 (offset + 0x04,sf); wavebank_index = read_s8 (offset + 0x04, sf);
/* 05(1): loop count */ /* 05(1): loop count */
/* 06(2): pan angle */ /* 06(2): pan angle */
/* 08(2): pan arc */ /* 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); //;VGM_LOG("XSB clip event 4 at %lx: stream=%i, wavebank=%i\n", offset, stream_index, wavebank_index);
offset += 0x1c; 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; if (xsb->parse_done) return 1;
break; 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 */ /* 16(1): max Q */
/* 17(1): unknown */ /* 17(1): unknown */
/* 18(1): variation flags */ /* 18(1): variation flags */
track_count = read_s16(offset + 0x19,sf); track_count = read_s16(offset + 0x19, sf);
/* 1a(1): flags 2 */ /* 1a(1): flags 2 */
/* 1b(5): unknown 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; offset += 0x20;
for (t = 0; t < track_count; t++) { for (t = 0; t < track_count; t++) {
stream_index = read_s16(offset + 0x00,sf); stream_index = read_s16(offset + 0x00, sf);
wavebank_index = read_s8 (offset + 0x02,sf); wavebank_index = read_s8 (offset + 0x02, sf);
/* 03(1): min weight */ /* 03(1): min weight */
/* 04(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); //;VGM_LOG("XSB clip event 6: track=%i, stream=%i, wavebank=%i at %lx\n", t, stream_index, wavebank_index, offset);
offset += 0x05; 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; if (xsb->parse_done) return 1;
} }
break; break;
@ -318,7 +463,7 @@ fail:
return 0; 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; 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; 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; int i, clip_count = 0;
flags = read_u8 (offset + 0x00,sf); flags = read_u8 (offset + 0x00, sf);
/* 0x01(2): category */ /* 0x01(2): category */
/* 0x03(1): decibels */ /* 0x03(1): decibels */
/* 0x04(2): pitch */ /* 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; offset += 0x09;
if (flags & 0x01) { /* complex sound */ 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); //;VGM_LOG("XSB sound: complex with clips=%i\n", clip_count);
offset += 0x01; offset += 0x01;
} }
else { else {
stream_index = read_s16(offset + 0x00,sf); stream_index = read_s16(offset + 0x00, sf);
wavebank_index = read_s8(offset + 0x02,sf); wavebank_index = read_s8(offset + 0x02, sf);
//;VGM_LOG("XSB sound: simple with stream=%i, wavebank=%i\n", stream_index, wavebank_index); //;VGM_LOG("XSB sound: simple with stream=%i, wavebank=%i\n", stream_index, wavebank_index);
offset += 0x03; 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 (xsb->parse_done) return 1;
} }
if (flags & 0x0E) { /* has RPCs */ 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 */ /* 0x02(2): preset count */
/* 0x04(4*count): RPC indexes */ /* 0x04(4*count): RPC indexes */
/* (presets per flag 2/4/8 flag) */ /* (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 */ 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? */ /* follows RPC format? */
offset += dsp_size; 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; off_t clip_offset;
for (i = 0; i < clip_count; i++) { for (i = 0; i < clip_count; i++) {
/* 00(1): decibels */ /* 00(1): decibels */
clip_offset = read_s32(offset + 0x01,sf); clip_offset = read_s32(offset + 0x01, sf);
/* 05(2): filter config */ /* 05(2): filter config */
/* 07(2): filter frequency */ /* 07(2): filter frequency */
//;VGM_LOG("XSB sound clip %i at %lx\n", i, offset); //;VGM_LOG("XSB sound clip %i at %lx\n", i, offset);
offset += 0x09; 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; 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; 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; 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; 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; 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; int i, variation_count;
variation_count = read_s16(offset + 0x00,sf); variation_count = read_s16(offset + 0x00, sf);
flags = read_u16(offset + 0x02,sf); flags = read_u16(offset + 0x02, sf);
//;VGM_LOG("XSB variation at %lx\n", offset); //;VGM_LOG("XSB variation at %lx\n", offset);
offset += 0x04; 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) { switch ((flags >> 3) & 0x7) {
case 0: /* wave */ case 0: /* wave */
stream_index = read_s16(offset + 0x00,sf); stream_index = read_s16(offset + 0x00, sf);
wavebank_index = read_s8(offset + 0x02,sf); wavebank_index = read_s8(offset + 0x02, sf);
/* 03(1): weight min */ /* 03(1): weight min */
/* 04(1): weight max */ /* 04(1): weight max */
//;VGM_LOG("XSB variation: type 0 with stream=%i, wavebank=%i\n", stream_index, wavebank_index); //;VGM_LOG("XSB variation: type 0 with stream=%i, wavebank=%i\n", stream_index, wavebank_index);
offset += 0x05; 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; if (xsb->parse_done) return 1;
break; break;
case 1: /* sound */ case 1: /* sound */
sound_offset = read_s32(offset + 0x00,sf); sound_offset = read_s32(offset + 0x00, sf);
/* 04(1): weight min */ /* 04(1): weight min */
/* 05(1): weight max */ /* 05(1): weight max */
//;VGM_LOG("XSB variation: type 1\n"); //;VGM_LOG("XSB variation: type 1\n");
offset += 0x06; 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; if (xsb->parse_done) return 1;
break; break;
case 3: /* sound */ case 3: /* sound */
sound_offset = read_s32(offset + 0x00,sf); sound_offset = read_s32(offset + 0x00, sf);
/* 04(4): weight min */ /* 04(4): weight min */
/* 08(4): weight max */ /* 08(4): weight max */
/* 0c(4): flags */ /* 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"); //;VGM_LOG("XSB variation: type 3\n");
offset += 0x10; 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; if (xsb->parse_done) return 1;
break; break;
case 4: /* compact wave */ case 4: /* compact wave */
stream_index = read_s16(offset + 0x00,sf); stream_index = read_s16(offset + 0x00, sf);
wavebank_index = read_s8(offset + 0x02,sf); wavebank_index = read_s8(offset + 0x02, sf);
//;VGM_LOG("XSB variation: type 4 with stream=%i, wavebank=%i\n", stream_index, wavebank_index); //;VGM_LOG("XSB variation: type 4 with stream=%i, wavebank=%i\n", stream_index, wavebank_index);
offset += 0x03; 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 (xsb->parse_done) return 1;
break; 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; int32_t (*read_s32)(off_t,STREAMFILE*) = xsb->big_endian ? read_s32be : read_s32le;
uint8_t flags; uint8_t flags;
@ -486,23 +631,23 @@ static int parse_xsb_cues_new(xsb_header * xsb, STREAMFILE *sf) {
offset = xsb->simple_cues_offset; offset = xsb->simple_cues_offset;
for (i = 0; i < xsb->simple_cues_count; i++) { for (i = 0; i < xsb->simple_cues_count; i++) {
/* 00(1): flags */ /* 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); //;VGM_LOG("XSB cues: simple %i at %lx\n", i, offset);
offset += 0x05; offset += 0x05;
name_offset = read_s32(names_offset + 0x00,sf); name_offset = read_s32(names_offset + 0x00, sf);
/* 04(2): unknown (-1) */ /* 04(2): unknown (-1) */
names_offset += 0x06; 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; if (xsb->parse_done) break;
} }
offset = xsb->complex_cues_offset; offset = xsb->complex_cues_offset;
for (i = 0; i < xsb->complex_cues_count; i++) { for (i = 0; i < xsb->complex_cues_count; i++) {
flags = read_u8(offset + 0x00,sf); flags = read_u8(offset + 0x00, sf);
sound_offset = read_s32(offset + 0x01,sf); sound_offset = read_s32(offset + 0x01, sf);
/* 05(4): unknown (sound) / transition table offset (variation) */ /* 05(4): unknown (sound) / transition table offset (variation) */
/* 09(1): instance limit */ /* 09(1): instance limit */
/* 0a(2): fade in sec */ /* 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); //;VGM_LOG("XSB cues: complex %i at %lx\n", i, offset);
offset += 0x0f; offset += 0x0f;
name_offset = read_s32(names_offset + 0x00,sf); name_offset = read_s32(names_offset + 0x00, sf);
/* 04(2): unknown (-1) */ /* 04(2): unknown (-1) */
names_offset += 0x06; names_offset += 0x06;
if (flags & (1<<2)) if (flags & (1<<2))
parse_xsb_sound(xsb, sound_offset, name_offset,sf); parse_xsb_sound(xsb, sound_offset, name_offset, sf);
else else
parse_xsb_variation(xsb, sound_offset, name_offset,sf); parse_xsb_variation(xsb, sound_offset, name_offset, sf);
if (xsb->parse_done) break; 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/MonoGame/MonoGame/blob/master/MonoGame.Framework/Audio/Xact/
* - https://github.com/espes/MacTerrariaWrapper/tree/master/xactxtract * - 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; int32_t (*read_s32)(off_t,STREAMFILE*) = NULL;
int16_t (*read_s16)(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 */ /* 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) { if (xsb->version <= XSB_XACT1_0_MAX) {
/* 06(2): crc */ /* 06(2): crc */
xsb->wavebanks_offset = read_s32(0x08,sf); xsb->wavebanks_offset = read_s32(0x08, sf);
/* 0c(4): unknown1 offset (entry: 0x04) */ /* 0c(4): unknown1 offset (entry: 0x04) */
/* 10(4): unknown2 offset */ /* 10(4): unknown2 offset */
/* 14(2): element count? */ /* 14(2): element count? */
/* 16(2): empty? */ /* 16(2): empty? */
/* 18(2): empty? */ /* 18(2): empty? */
xsb->complex_cues_count = read_s16(0x1a,sf); xsb->complex_cues_count = read_s16(0x1a, sf);
xsb->simple_cues_count = read_s16(0x1c,sf); xsb->simple_cues_count = read_s16(0x1c, sf);
xsb->wavebanks_count = read_s16(0x1e,sf); xsb->wavebanks_count = read_s16(0x1e, sf);
/* 20(10): xsb name */ /* 20(10): xsb name */
xsb->sounds_offset = 0x30; xsb->sounds_offset = 0x30;
xsb->wavebanks_name_size = 0x10; xsb->wavebanks_name_size = 0x10;
xsb->index_size = 0x10;
xsb->entry_size = 0x14;
} }
else if (xsb->version <= XSB_XACT1_1_MAX) { else if (xsb->version <= XSB_XACT1_1_MAX) {
/* 06(2): crc */ /* 06(2): crc */
xsb->wavebanks_offset = read_s32(0x08,sf); xsb->wavebanks_offset = read_s32(0x08, sf);
/* 0c(4): unknown1 offset (entry: 0x04) */ /* 0c(4): unknown1 offset (entry: 0x04) */
/* 10(4): unknown2 offset */ /* 10(4): unknown2 offset */
/* 14(4): unknown3 offset */ /* 14(4): unknown3 offset */
/* 18(2): empty? */ /* 18(2): empty? */
/* 1a(2): element count? */ /* 1a(2): element count? */
xsb->complex_cues_count = read_s16(0x1c,sf); xsb->complex_cues_count = read_s16(0x1c, sf);
xsb->simple_cues_count = read_s16(0x1e,sf); xsb->simple_cues_count = read_s16(0x1e, sf);
/* 20(2): unknown count? (related to unknown2?) */ /* 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 */ /* 24(10): xsb name */
xsb->sounds_offset = 0x34; xsb->sounds_offset = 0x34;
xsb->wavebanks_name_size = 0x10; xsb->wavebanks_name_size = 0x10;
xsb->index_size = 0x10;
xsb->entry_size = 0x14;
} }
else if (xsb->version <= XSB_XACT1_2_MAX) { else if (xsb->version <= XSB_XACT1_2_MAX) {
/* 06(2): crc */ /* 06(2): crc */
xsb->wavebanks_offset = read_s32(0x08,sf); xsb->wavebanks_offset = read_s32(0x08, sf);
/* 0c(4): unknown1 offset (entry: 0x14) */ /* 0c(4): unknown1 offset (entry: 0x14) */
/* 10(4): unknown2 offset (entry: variable) */ /* 10(4): unknown2 offset (entry: variable) */
/* 14(4): unknown3 offset */ /* 14(4): unknown3 offset */
/* 18(2): empty? */ /* 18(2): empty? */
/* 1a(2): element count? */ /* 1a(2): element count? */
xsb->complex_cues_count = read_s16(0x1c,sf); xsb->complex_cues_count = read_s16(0x1c, sf);
xsb->simple_cues_count = read_s16(0x1e,sf); xsb->simple_cues_count = read_s16(0x1e, sf);
/* 20(2): unknown count? (related to unknown2?) */ /* 20(2): unknown count? (related to unknown2?) */
xsb->wavebanks_count = read_s16(0x22,sf); xsb->wavebanks_count = read_s16(0x22, sf);
/* 24(4): null? */ /* 24(4): null? */
/* 28(10): xsb name */ /* 28(10): xsb name */
xsb->sounds_offset = 0x38; xsb->sounds_offset = 0x38;
xsb->wavebanks_name_size = 0x10; xsb->wavebanks_name_size = 0x10;
xsb->index_size = 0x14;
xsb->entry_size = 0x14;
} }
else if (xsb->version <= XSB_XACT2_MAX) { else if (xsb->version <= XSB_XACT2_MAX) {
/* 06(2): crc */ /* 06(2): crc */
/* 08(1): platform? (3=X360) */ /* 08(1): platform? (3=X360) */
xsb->simple_cues_count = read_s16(0x09,sf); xsb->simple_cues_count = read_s16(0x09, sf);
xsb->complex_cues_count = read_s16(0x0B,sf); xsb->complex_cues_count = read_s16(0x0B, sf);
xsb->wavebanks_count = read_s8 (0x11,sf); xsb->wavebanks_count = read_s8 (0x11, sf);
xsb->sounds_count = read_s16(0x12,sf); xsb->sounds_count = read_s16(0x12, sf);
/* 14(2): unknown */ /* 14(2): unknown */
xsb->cue_names_size = read_s32(0x16,sf); xsb->cue_names_size = read_s32(0x16, sf);
xsb->simple_cues_offset = read_s32(0x1a,sf); xsb->simple_cues_offset = read_s32(0x1a, sf);
xsb->complex_cues_offset = read_s32(0x1e,sf); xsb->complex_cues_offset = read_s32(0x1e, sf);
xsb->cue_names_offset = read_s32(0x22,sf); xsb->cue_names_offset = read_s32(0x22, sf);
/* 26(4): unknown */ /* 26(4): unknown */
/* 2a(4): unknown */ /* 2a(4): unknown */
/* 2e(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? */ /* 36(4): cue name hash table offset? */
xsb->nameoffsets_offset = read_s32(0x3a,sf); xsb->nameoffsets_offset = read_s32(0x3a, sf);
xsb->sounds_offset = read_s32(0x3e,sf); xsb->sounds_offset = read_s32(0x3e, sf);
/* 42(4): unknown */ /* 42(4): unknown */
/* 46(4): unknown */ /* 46(4): unknown */
/* 4a(64): xsb name */ /* 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 */ /* 0a(4): last modified low */
/* 0e(4): last modified high */ /* 0e(4): last modified high */
/* 12(1): platform? (1=PC, 3=X360) */ /* 12(1): platform? (1=PC, 3=X360) */
xsb->simple_cues_count = read_s16(0x13,sf); xsb->simple_cues_count = read_s16(0x13, sf);
xsb->complex_cues_count = read_s16(0x15,sf); xsb->complex_cues_count = read_s16(0x15, sf);
/* 17(2): unknown count? */ /* 17(2): unknown count? */
/* 19(2): element count? (often simple+complex cues, but may be more) */ /* 19(2): element count? (often simple+complex cues, but may be more) */
xsb->wavebanks_count = read_s8 (0x1b,sf); xsb->wavebanks_count = read_s8 (0x1b, sf);
xsb->sounds_count = read_s16(0x1c,sf); xsb->sounds_count = read_s16(0x1c, sf);
xsb->cue_names_size = read_s32(0x1e,sf); xsb->cue_names_size = read_s32(0x1e, sf);
xsb->simple_cues_offset = read_s32(0x22,sf); xsb->simple_cues_offset = read_s32(0x22, sf);
xsb->complex_cues_offset = read_s32(0x26,sf); xsb->complex_cues_offset = read_s32(0x26, sf);
xsb->cue_names_offset = read_s32(0x2a,sf); xsb->cue_names_offset = read_s32(0x2a, sf);
/* 0x2E(4): unknown offset */ /* 0x2E(4): unknown offset */
/* 0x32(4): variation tables offset */ /* 0x32(4): variation tables offset */
/* 0x36(4): unknown 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) */ /* 0x3E(4): cue name hash table offset (16b each) */
xsb->nameoffsets_offset = read_s32(0x42,sf); xsb->nameoffsets_offset = read_s32(0x42, sf);
xsb->sounds_offset = read_s32(0x46,sf); xsb->sounds_offset = read_s32(0x46, sf);
/* 4a(64): xsb name */ /* 4a(64): xsb name */
xsb->wavebanks_name_size = 0x40; 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; offset = xsb->wavebanks_offset;
for (i = 0; i < xsb->wavebanks_count; i++) { 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); //;VGM_LOG("XSB wavebanks: bank %i=%s\n", i, wavebank_name);
if (strcasecmp(xsb_wavebank_name, xwb_wavebank_name)==0) { if (strcasecmp(xsb_wavebank_name, xwb_wavebank_name)==0) {
//;VGM_LOG("XSB banks: current xwb is wavebank %i=%s\n", i, xsb_wavebank_name); //;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); //;VGM_LOG("xsb: selected wavebank=%i\n", xsb->selected_wavebank);
if (xsb->selected_wavebank == -1) { if (xsb->selected_wavebank == -1) {
VGM_LOG("XSB: current wavebank not found, selecting first\n"); 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 */ /* find cue pointing to stream */
if (xsb->version <= XSB_XACT1_2_MAX) { if (xsb->version <= XSB_XACT1_2_MAX) {
parse_xsb_cues_old(xsb, sf); parse_xsb_old_cues(xsb, sf);
} }
else { else {
parse_xsb_cues_new(xsb, sf); parse_xsb_cues(xsb, sf);
} }
return 1; return 1;
@ -739,6 +891,12 @@ static STREAMFILE * open_xsb_filename_pair(STREAMFILE *streamXwb) {
{"StreamBank_*.xwb","SoundBank_*.xsb"}, /* Ginga Force (X360) */ {"StreamBank_*.xwb","SoundBank_*.xsb"}, /* Ginga Force (X360) */
{"WaveBank_*.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) */
{"*_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 */ {"*.xwb","*.xsb"}, /* default */
}; };
int i; int i;

View File

@ -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) if (!streamfile->infile || !dst || length <= 0 || offset < 0)
return 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? */ /* is the part of the requested length in the buffer? */
if (offset >= streamfile->buffer_offset && offset < streamfile->buffer_offset + streamfile->validsize) { if (offset >= streamfile->buffer_offset && offset < streamfile->buffer_offset + streamfile->validsize) {
size_t length_to_read; 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) if (length_to_read > length)
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); memcpy(dst, streamfile->buffer + offset_into_buffer, length_to_read);
length_read_total += length_to_read; length_read_total += length_to_read;
length -= 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 #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); VGM_LOG("STDIO: rebuffer, requested %lx vs %lx (sf %x)\n", offset, streamfile->buffer_offset, (uint32_t)streamfile);
//streamfile->rebuffer++;
//if (rebuffer > N) ...
} }
#endif #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) */ /* fill the buffer (offset now is beyond buffer_offset) */
streamfile->buffer_offset = offset; streamfile->buffer_offset = offset;
streamfile->validsize = fread(streamfile->buffer, sizeof(uint8_t), streamfile->buffersize, streamfile->infile); 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 */ /* decide how much must be read this time */
if (length > streamfile->buffersize) if (length > streamfile->buffersize)
@ -126,20 +133,21 @@ static STREAMFILE* open_stdio(STDIO_STREAMFILE *streamfile, const char * const f
return NULL; return NULL;
#if !defined (__ANDROID__) #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)) { if (streamfile->infile && !strcmp(streamfile->name,filename)) {
int newfd; int new_fd;
FILE *newfile; FILE *new_file = NULL;
STREAMFILE *new_sf;
if ( ((newfd = dup(fileno(streamfile->infile))) >= 0) && (newfile = fdopen(newfd, "rb")) ) { if (((new_fd = dup(fileno(streamfile->infile))) >= 0) && (new_file = fdopen(new_fd, "rb"))) {
new_sf = open_stdio_streamfile_buffer_by_file(newfile, filename, buffersize); STREAMFILE *new_sf = open_stdio_streamfile_buffer_by_file(new_file, filename, buffersize);
if (new_sf) { if (new_sf)
return new_sf; return new_sf;
} fclose(new_file);
// failure, close it and try the default path (which will probably fail a second time)
fclose(newfile);
} }
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 #endif
// a normal open, open a new file // a normal open, open a new file
@ -981,6 +989,27 @@ fail:
if (buf) buf[0] = '\0'; if (buf) buf[0] = '\0';
return 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) { size_t read_key_file(uint8_t *buf, size_t buf_size, STREAMFILE *sf) {

View File

@ -45,8 +45,6 @@
#define fseeko fseek #define fseeko fseek
#endif #endif
#define STREAMFILE_DEFAULT_BUFFER_SIZE 0x8000
#ifndef DIR_SEPARATOR #ifndef DIR_SEPARATOR
#if defined (_WIN32) || defined (WIN32) #if defined (_WIN32) || defined (WIN32)
#define DIR_SEPARATOR '\\' #define DIR_SEPARATOR '\\'
@ -55,6 +53,14 @@
#endif #endif
#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 /* 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. * 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. */ * 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 */ /* alias of the above */
static inline int8_t read_s8(off_t offset, STREAMFILE * streamfile) { return read_8bit(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 * streamfile) { return (uint8_t)read_8bit(offset, streamfile); } 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 * streamfile) { return read_16bitLE(offset, streamfile); } 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 * streamfile) { return (uint16_t)read_16bitLE(offset, streamfile); } 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 * streamfile) { return read_16bitBE(offset, streamfile); } 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 * streamfile) { return (uint16_t)read_16bitBE(offset, streamfile); } 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 * streamfile) { return read_32bitLE(offset, streamfile); } 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 * streamfile) { return (uint32_t)read_32bitLE(offset, streamfile); } 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 * streamfile) { return read_32bitBE(offset, streamfile); } 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 * streamfile) { return (uint32_t)read_32bitBE(offset, streamfile); } 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 * streamfile) { return read_64bitBE(offset, streamfile); } 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 * streamfile) { return (uint64_t)read_64bitBE(offset, streamfile); } 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 * streamfile) { return read_64bitLE(offset, streamfile); } 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 * streamfile) { return (uint64_t)read_64bitLE(offset, streamfile); } 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?) /* The recommended int-to-float type punning in C is through union, but pointer casting
static inline float read_f32be(off_t offset, STREAMFILE * streamfile) { * works too (though less portable due to aliasing rules?). For C++ memcpy seems
uint32_t sample_int = read_s32be(offset,streamfile); * 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; float* sample_float = (float*)&sample_int;
return *sample_float; 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) { static inline int read_s4h(off_t offset, STREAMFILE * streamfile) {
uint8_t byte = read_u8(offset, streamfile); uint8_t byte = read_u8(offset, streamfile);
return get_nibble_signed(byte, 1); 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 //align32, align16, clamp16, etc
#endif #endif
//TODO: maybe move to streamfile.c
/* guess byte endianness from a given value, return true if big endian and false if little endian */ /* 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) { static inline int guess_endianness16bit(off_t offset, STREAMFILE * streamfile) {
uint8_t buf[0x02]; 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). */ /* 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); 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. /* Opens a file containing decryption keys and copies to buffer.
* Tries "(name.ext)key" (per song), "(.ext)key" (per folder) keynames. * Tries "(name.ext)key" (per song), "(.ext)key" (per folder) keynames.

View File

@ -51,6 +51,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_ps2_mic, init_vgmstream_ps2_mic,
init_vgmstream_ngc_dsp_std_int, init_vgmstream_ngc_dsp_std_int,
init_vgmstream_vag, init_vgmstream_vag,
init_vgmstream_vag_aaap,
init_vgmstream_seb, init_vgmstream_seb,
init_vgmstream_ps2_ild, init_vgmstream_ps2_ild,
init_vgmstream_ps2_pnb, init_vgmstream_ps2_pnb,
@ -221,7 +222,6 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_pona_3do, init_vgmstream_pona_3do,
init_vgmstream_pona_psx, init_vgmstream_pona_psx,
init_vgmstream_xbox_hlwav, init_vgmstream_xbox_hlwav,
init_vgmstream_stx,
init_vgmstream_myspd, init_vgmstream_myspd,
init_vgmstream_his, init_vgmstream_his,
init_vgmstream_ps2_ast, init_vgmstream_ps2_ast,
@ -481,6 +481,8 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_bmp_konami, init_vgmstream_bmp_konami,
init_vgmstream_opus_opusnx, init_vgmstream_opus_opusnx,
init_vgmstream_opus_sqex, 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) */ /* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */
init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */ init_vgmstream_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 *)) { static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *streamFile, VGMSTREAM*(*init_vgmstream_function)(STREAMFILE *)) {
/* filename search pairs for dual file stereo */ /* filename search pairs for dual file stereo */
static const char * const dfs_pairs[][2] = { static const char * const dfs_pairs[][2] = {
{"L","R"}, {"L","R"}, /* most common in .dsp and .vag */
{"l","r"}, {"l","r"}, /* same */
{"left","right"}, {"left","right"}, /* Freaky Flyers (GC) .adp, Velocity (PSP) .vag, Hyper Fighters (Wii) .dsp */
{"Left","Right"}, {"Left","Right"}, /* Geometry Wars: Galaxies (Wii) .dsp */
{".V0",".V1"}, /* Homura (PS2) */ {".V0",".V1"}, /* Homura (PS2) */
{".L",".R"}, /* Crash Nitro Racing (PS2), Gradius V (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 {".adpcm","_NxEncoderOut_.adpcm"}, /* Kill la Kill: IF (Switch) */ //todo can't match R>L
}; };
char new_filename[PATH_LIMIT]; 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 //todo other layouts work but some stereo codecs do weird things
//if (opened_vgmstream->layout != layout_none) return; //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); filename_len = strlen(new_filename);
if (filename_len < 2) if (filename_len < 2)
return; 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); //;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 suffix matches paste opposite suffix (+ terminator) to extension pointer, thus to new_filename */
if (this_suffix[0] == '.' && extension_len == this_suffix_len) { /* same extension */ if (filename_len > this_suffix_len && strchr(this_suffix, '.') != NULL) { /* same suffix with extension */
//;VGM_LOG("DFS: same ext %s vs %s len %i\n", extension, this_suffix, this_suffix_len); //;VGM_LOG("DFS: suf+ext %s vs %s len %i\n", new_filename, this_suffix, this_suffix_len);
if (memcmp(extension,this_suffix,this_suffix_len) == 0) { if (memcmp(new_filename + (filename_len - this_suffix_len), this_suffix, this_suffix_len) == 0) {
dfs_pair = j; 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) */ 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); //;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) { if (memcmp(extension - this_suffix_len, this_suffix,this_suffix_len) == 0) {
dfs_pair = j; dfs_pair = j;
memmove(extension + that_suffix_len - this_suffix_len, extension,extension_len+1); /* move old extension to end */ 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); //;VGM_LOG("DFS: match %i filename=%s\n", dfs_pair, new_filename);
/* try to init other channel (new_filename now has the opposite name) */ /* 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; if (!dual_streamFile) goto fail;
new_vgmstream = init_vgmstream_function(dual_streamFile); /* use the init that just worked, no other should work */ 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 /* 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) { if (vgmstream->layout_type != layout_none && vgmstream->layout_type != layout_interleave) {
use_streamfile_per_channel = 1; 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; 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) { if (use_streamfile_per_channel) {
file = open_streamfile(streamFile,filename); file = open_streamfile(streamFile,filename);
if (!file) goto fail; if (!file) goto fail;

View File

@ -332,7 +332,6 @@ typedef enum {
meta_NDS_SWAV, /* Asphalt Urban GT 1 & 2 */ meta_NDS_SWAV, /* Asphalt Urban GT 1 & 2 */
meta_NDS_RRDS, /* Ridge Racer DS */ meta_NDS_RRDS, /* Ridge Racer DS */
meta_WII_BNS, /* Wii BNS Banner Sound (similar to RSTM) */ meta_WII_BNS, /* Wii BNS Banner Sound (similar to RSTM) */
meta_STX, /* Pikmin .stx */
meta_WIIU_BTSND, /* Wii U Boot Sound */ meta_WIIU_BTSND, /* Wii U Boot Sound */
meta_ADX_03, /* CRI ADX "type 03" */ meta_ADX_03, /* CRI ADX "type 03" */
@ -364,6 +363,7 @@ typedef enum {
meta_PS2_VAGi, /* VAGi Interleaved File */ meta_PS2_VAGi, /* VAGi Interleaved File */
meta_PS2_VAGp, /* VAGp Mono File */ meta_PS2_VAGp, /* VAGp Mono File */
meta_PS2_pGAV, /* VAGp with Little Endian Header */ meta_PS2_pGAV, /* VAGp with Little Endian Header */
meta_PS2_VAGp_AAAP, /* Acclaim Austin Audio VAG header */
meta_SEB, meta_SEB,
meta_STR_WAV, /* Blitz Games STR+WAV files */ meta_STR_WAV, /* Blitz Games STR+WAV files */
meta_PS2_ILD, /* ILD File */ meta_PS2_ILD, /* ILD File */
@ -726,6 +726,8 @@ typedef enum {
meta_XMV_VALVE, meta_XMV_VALVE,
meta_UBI_HX, meta_UBI_HX,
meta_BMP_KONAMI, meta_BMP_KONAMI,
meta_ISB,
meta_XSSB,
} meta_t; } meta_t;
@ -870,14 +872,14 @@ typedef struct {
int32_t samples_into_block; /* number of samples into the current block/interleave/segment/etc */ 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) */ 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_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 */ off_t next_block_offset; /* offset of header of the next block */
/* layout/block loop state */ /* layout/block loop state */
int32_t loop_sample; /* saved from current_sample (same as loop_start_sample, but more state-like) */ 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 */ int32_t loop_samples_into_block;/* saved from samples_into_block */
off_t loop_block_offset; /* saved from current_block_offset */ off_t loop_block_offset; /* saved from current_block_offset */
size_t loop_block_size; /* saved from current_block_size */ 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 */ off_t loop_next_block_offset; /* saved from next_block_offset */
/* loop state */ /* loop state */