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