Updated VGMStream to r1050-2622-g72b9f540

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

View File

@ -401,7 +401,6 @@
836F703018BDC2190095E648 /* sqex_scd.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EF418BDC2190095E648 /* sqex_scd.c */; };
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 */,

View File

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

View File

@ -55,10 +55,10 @@ void dsp_read_hist_le(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offse
void dsp_read_hist(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing, int be);
/* 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 */

View File

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

View File

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

View File

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

View File

@ -133,7 +133,8 @@ ffmpeg_codec_data * init_ffmpeg_atrac3_riff(STREAMFILE *sf, off_t offset, int* o
/* implicit skip: official tools skip this even with encoder delay forced to 0. Maybe FFmpeg decodes late,
* 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;
}

View File

@ -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) {

View File

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

View File

@ -92,6 +92,14 @@ int mpeg_custom_setup_init_default(STREAMFILE *streamFile, off_t start_offset, m
data->skip_samples = data->config.skip_samples; break;
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;
}

View File

@ -112,12 +112,16 @@ int mpeg_custom_setup_init_ealayer3(STREAMFILE *streamfile, off_t start_offset,
ok = ealayer3_parse_frame(data, -1, &ib, &eaf);
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.

View File

@ -204,7 +204,7 @@ static mpg123_handle * init_mpg123_handle() {
}
mpg123_param(m,MPG123_REMOVE_FLAGS,MPG123_GAPLESS,0.0); /* wonky support */
mpg123_param(m,MPG123_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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -206,15 +206,11 @@ void decode_alaw(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacin
void decode_pcmfloat(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int big_endian) {
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);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,242 @@
#ifndef _EA_EAAC_OPUS_STREAMFILE_H_
#define _EA_EAAC_OPUS_STREAMFILE_H_
#include "../streamfile.h"
typedef struct deblock_config_t deblock_config_t;
typedef struct deblock_io_data deblock_io_data;
struct deblock_config_t {
/* config (all optional) */
size_t logical_size; /* pre-calculated size for performance (otherwise has to read the whole thing) */
off_t stream_start; /* data start */
size_t stream_size; /* data max */
size_t chunk_size; /* some size like a constant interleave */
size_t skip_size; /* same */
int codec; /* codec or type variations */
int channels;
int big_endian;
uint32_t config; /* some non-standard config value */
/* read=blocks from out stream to read) and "steps" (blocks from other streams to skip) */
int step_start; /* initial step_count at stream start (often 0) */
int step_count; /* number of blocks to step over from other streams */
int read_count; /* number of blocks to read from this stream, after steps */
size_t track_size;
int track_number;
int track_count;
size_t interleave_count;
size_t interleave_last_count;
/* callback that setups deblock_io_data state, normally block_size and data_size */
void (*block_callback)(STREAMFILE *sf, off_t offset, deblock_io_data *data);
} ;
struct deblock_io_data{
/* initial config */
deblock_config_t cfg;
/* state */
off_t logical_offset; /* fake deblocked offset */
off_t physical_offset; /* actual file offset */
off_t block_size; /* current block (added to physical offset) */
off_t skip_size; /* data to skip from block start to reach data (like a header) */
off_t data_size; /* usable data in a block (added to logical offset) */
//todo head/foot?
int step_count; /* number of blocks to step over */
int read_count; /* number of blocks to read */
size_t logical_size;
size_t physical_size;
off_t physical_end;
} ;
static void block_callback_default(STREAMFILE *sf, off_t offset, deblock_io_data *data) {
data->block_size = data->cfg.chunk_size;
data->skip_size = data->cfg.skip_size;
data->data_size = data->block_size - data->skip_size;
}
static size_t deblock_io_read(STREAMFILE *sf, uint8_t *dest, off_t offset, size_t length, deblock_io_data* data) {
size_t total_read = 0;
/* re-start when previous offset (can't map logical<>physical offsets) */
if (data->logical_offset < 0 || offset < data->logical_offset) {
;VGM_LOG("DEBLOCK: restart offset=%lx + %x, po=%lx, lo=%lx\n", offset, length, data->physical_offset, data->logical_offset);
data->physical_offset = data->cfg.stream_start;
data->logical_offset = 0x00;
data->block_size = 0;
data->data_size = 0;
data->skip_size = 0;
data->step_count = data->cfg.step_start;
data->read_count = data->cfg.read_count;
}
/* read blocks */
while (length > 0) {
/* ignore EOF */
if (offset < 0 ||
(data->physical_offset >= data->cfg.stream_start + data->physical_size) ||
(data->logical_size > 0 && offset > data->logical_size)) {
break;
}
/* process new block */
if (data->data_size <= 0) {
data->cfg.block_callback(sf, offset, data);
if (data->block_size <= 0) {
VGM_LOG("DEBLOCK: block size not set at %lx\n", data->physical_offset);
break;
}
}
#if 1
if (data->step_count > 0) {
data->step_count--;
data->physical_offset += data->block_size;
data->data_size = 0;
continue;
}
#else
/* handle blocks from multiple streams */
{
if (data->step_count > 0) {
data->step_count--;
data->data_size = 0; /* step over this block */
}
else if (data->read_count) {//must detect when blocks has been read
data->read_count--; /* read this block */
/* reset */
if (data->step_count == 0 && data->read_count == 0) {
data->step_count = data->cfg.step_count;
data->read_count = data->cfg.read_count;
}
}
}
#endif
/* move to next block */
if (data->data_size == 0 || offset >= data->logical_offset + data->data_size) {
data->physical_offset += data->block_size;
data->logical_offset += data->data_size;
data->data_size = 0;
data->step_count = data->cfg.step_count;
//VGM_LOG("ignore at %lx + %lx, skips=%i, reads=%i\n", data->physical_offset, data->block_size, data->step_count, data->read_count);
continue;
}
//VGM_LOG("accept at %lx + %lx, skips=%i, reads=%i\n", data->physical_offset, data->block_size, data->step_count, data->read_count);
/* read block data */
{
size_t bytes_consumed, bytes_done, to_read;
bytes_consumed = offset - data->logical_offset;
to_read = data->data_size - bytes_consumed;
if (to_read > length)
to_read = length;
bytes_done = read_streamfile(dest, data->physical_offset + data->skip_size + bytes_consumed, to_read, sf);
total_read += bytes_done;
dest += bytes_done;
offset += bytes_done;
length -= bytes_done;
if (bytes_done != to_read || bytes_done == 0) {
break; /* error/EOF */
}
}
}
return total_read;
}
static size_t deblock_io_size(STREAMFILE *streamfile, deblock_io_data* data) {
uint8_t buf[0x04];
if (data->logical_size)
return data->logical_size;
if (data->cfg.logical_size) {
data->logical_size = data->cfg.logical_size;
return data->logical_size;
}
/* force a fake read at max offset, to get max logical_offset (will be reset next read) */
deblock_io_read(streamfile, buf, 0x7FFFFFFF, 1, data);
data->logical_size = data->logical_offset;
return data->logical_size;
}
/* generic "de-blocker" helper for streams divided in blocks that have weird interleaves, their
* decoder can't easily use blocked layout, or some other weird feature. Must pass a
* deblock_config_t with setup and a callback that sets sizes of a single block. */
static STREAMFILE* open_io_deblocker_streamfile_f(STREAMFILE *sf, deblock_config_t *cfg) {
STREAMFILE *new_sf = NULL;
deblock_io_data io_data = {0};
/* prepare data */
io_data.cfg = *cfg; /* memcpy */
if (io_data.cfg.block_callback == NULL)
io_data.cfg.block_callback = block_callback_default;
if (io_data.cfg.stream_start < 0)
goto fail;
if (io_data.cfg.step_start < 0 || io_data.cfg.step_count < 0)
goto fail;
if (io_data.cfg.read_count == 0)
io_data.cfg.read_count = 1;
io_data.physical_size = io_data.cfg.stream_size;
if (io_data.physical_size > get_streamfile_size(sf) + io_data.cfg.stream_start || io_data.physical_size == 0)
io_data.physical_size = get_streamfile_size(sf) - io_data.cfg.stream_start;
io_data.physical_end = io_data.cfg.stream_start + io_data.physical_size;
VGM_LOG("ps=%x, pe=%lx\n", io_data.physical_size, io_data.physical_end);
io_data.logical_offset = -1; /* read reset */
//TODO: other validations
/* setup subfile */
new_sf = open_io_streamfile_f(sf, &io_data, sizeof(deblock_io_data), deblock_io_read, deblock_io_size);
return new_sf;
fail:
VGM_LOG("DEBLOCK: bad init\n");
close_streamfile(sf);
return NULL;
}
/*****************************************************/
static void block_callback(STREAMFILE *sf, off_t offset, deblock_io_data *data) {
/* read the whole block, will be skipped for unwanted sub-streams */
data->block_size = 0x02 + read_u16be(data->physical_offset, sf);
data->data_size = data->block_size;
//VGM_LOG("read at %lx + %lx, skips=%i, reads=%i\n", data->physical_offset, data->block_size, data->step_count, data->read_count);
}
static STREAMFILE* open_io_eaac_opus_streamfile_f(STREAMFILE *new_sf, int stream_number, int stream_count) {
deblock_config_t cfg = {0};
cfg.step_start = stream_number;
cfg.step_count = stream_count - 1;
cfg.block_callback = block_callback;
/* starts from 0 since new_sf is pre-deblocked */
/* setup subfile */
//new_sf = open_wrap_streamfile(sf); /* to be used with others */
new_sf = open_io_deblocker_streamfile_f(new_sf, &cfg);
return new_sf;
}
#endif /* _EA_EAAC_OPUS_STREAMFILE_H_ */

View File

@ -1,6 +1,7 @@
#ifndef _EA_EAAC_STREAMFILE_H_
#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;
}

View File

@ -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) {

View File

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

View File

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

View File

@ -0,0 +1,191 @@
#include "meta.h"
#include "../coding/coding.h"
/* .ISB - Creative ISACT (Interactive Spatial Audio Composition Tools) middleware [Psychonauts (PC)] */
VGMSTREAM * init_vgmstream_isb(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset = 0, name_offset = 0;
size_t stream_size = 0, name_size = 0;
int loop_flag = 0, channel_count = 0, sample_rate = 0, codec = 0, pcm_bytes = 0, bps = 0;
int total_subsongs, target_subsong = streamFile->stream_index;
/* checks */
if (!check_extensions(streamFile, "isb"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x52494646) /* "RIFF" */
goto fail;
if (read_32bitLE(0x04,streamFile) + 0x08 != get_streamfile_size(streamFile))
goto fail;
if (read_32bitBE(0x08,streamFile) != 0x69736266) /* "isbf" */
goto fail;
/* some files have a companion .icb, seems to be a cue file pointing here */
/* format is RIFF with many custom chunks, apparently for their DAW-like editor with
* complex functions, but most seem always included by default and unused, and games
* Psychonauts seems to use the format as a simple audio bank. Mass Effect (X360)
* apparently uses ISACT, while Psychonauts Xbox/PS2 don't. */
{
off_t offset, max_offset, header_offset = 0;
size_t header_size = 0;
total_subsongs = 0; /* not specified */
if (target_subsong == 0) target_subsong = 1;
/* parse base RIFF */
offset = 0x0c;
max_offset = get_streamfile_size(streamFile);
while (offset < max_offset) {
uint32_t chunk_type = read_u32be(offset + 0x00,streamFile);
uint32_t chunk_size = read_s32le(offset + 0x04,streamFile);
offset += 0x08;
switch(chunk_type) {
case 0x4C495354: /* "LIST" */
if (read_u32be(offset, streamFile) != 0x73616D70) /* "samp" */
break; /* there are "bfob" LIST without data */
total_subsongs++;
if (target_subsong == total_subsongs && header_offset == 0) {
header_offset = offset;
header_size = chunk_size;
}
break;
default: /* most are common chunks at the start that seem to contain defaults */
break;
}
//if (offset + chunk_size+0x01 <= max_offset && chunk_size % 0x02)
// chunk_size += 0x01;
offset += chunk_size;
}
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
if (header_offset == 0) goto fail;
/* parse header inside LIST */
offset = header_offset + 0x04;
max_offset = offset + header_size;
while (offset < max_offset) {
uint32_t chunk_type = read_u32be(offset + 0x00,streamFile);
uint32_t chunk_size = read_s32le(offset + 0x04,streamFile);
offset += 0x08;
switch(chunk_type) {
case 0x7469746C: /* "titl" */
name_offset = offset;
name_size = chunk_size;
break;
case 0x63686E6B: /* "chnk" */
channel_count = read_u32le(offset + 0x00, streamFile);
break;
case 0x73696E66: /* "sinf" */
/* 0x00: null? */
/* 0x04: some value? */
sample_rate = read_u32le(offset + 0x08, streamFile);
pcm_bytes = read_u32le(offset + 0x0c, streamFile);
bps = read_u16le(offset + 0x10, streamFile);
/* 0x12: some value? */
break;
case 0x636D7069: /* "cmpi" */
codec = read_u32le(offset + 0x00, streamFile);
if (read_u32le(offset + 0x04, streamFile) != codec) {
VGM_LOG("ISB: unknown compression repeat\n");
goto fail;
}
/* 0x08: extra value for some codecs? */
/* 0x0c: block size when codec is XBOX-IMA */
/* 0x10: null? */
/* 0x14: flags? */
break;
case 0x64617461: /* "data" */
start_offset = offset;
stream_size = chunk_size;
break;
default: /* most of the same default chunks */
break;
}
//if (offset + chunk_size+0x01 <= max_offset && chunk_size % 0x02)
// chunk_size += 0x01;
offset += chunk_size;
}
if (start_offset == 0)
goto fail;
}
/* some files are marked */
loop_flag = 0;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_WAF; //todo
vgmstream->sample_rate = sample_rate;
vgmstream->num_streams = total_subsongs;
vgmstream->stream_size = stream_size;
switch(codec) {
case 0x00:
if (bps == 8) {
vgmstream->coding_type = coding_PCM8_U;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x01;
}
else {
vgmstream->coding_type = coding_PCM16LE;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x02;
}
vgmstream->num_samples = pcm_bytes_to_samples(stream_size, channel_count, bps); /* unsure about pcm_bytes */
break;
case 0x01:
vgmstream->coding_type = coding_XBOX_IMA;
vgmstream->layout_type = layout_none;
vgmstream->num_samples = xbox_ima_bytes_to_samples(stream_size, channel_count); /* pcm_bytes has excess data */
break;
#ifdef VGM_USE_VORBIS
case 0x02:
vgmstream->codec_data = init_ogg_vorbis(streamFile, start_offset, stream_size, NULL);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_OGG_VORBIS;
vgmstream->layout_type = layout_none;
vgmstream->num_samples = pcm_bytes / channel_count / (bps/8);
break;
#endif
default: /* according to press releases ISACT may support WMA and XMA */
VGM_LOG("ISB: unknown codec %i\n", codec);
goto fail;
}
if (name_offset) { /* UTF16 but only uses lower bytes */
if (name_size > STREAM_NAME_SIZE)
name_size = STREAM_NAME_SIZE;
read_string_utf16le(vgmstream->stream_name,name_size, name_offset, streamFile);
}
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -1,59 +1,47 @@
#include "meta.h"
#include "../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;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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
{

View File

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

View File

@ -1,70 +0,0 @@
#include "meta.h"
#include "../util.h"
VGMSTREAM * init_vgmstream_stx(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
const int loop_flag = 0;
const int channel_count = 2; /* .stx seems to be stereo only */
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("stx",filename_extension(filename))) goto fail;
/* length of data */
if (read_32bitBE(0x00,streamFile) !=
get_streamfile_size(streamFile) - 0x20) goto fail;
/* bits per sample? */
if (read_16bitBE(0x0a,streamFile) != 4) goto fail;
/* samples per frame? */
if (read_16bitBE(0x0c,streamFile) != 0x10) goto fail;
/* ?? */
if (read_16bitBE(0x0e,streamFile) != 0x1E) goto fail;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
vgmstream->num_samples = read_32bitBE(0x04,streamFile);
vgmstream->sample_rate = (uint16_t)read_16bitBE(0x08,streamFile);
/* channels and loop flag are set by allocate_vgmstream */
vgmstream->coding_type = coding_NGC_AFC;
vgmstream->layout_type = layout_interleave;
vgmstream->meta_type = meta_STX;
/* frame-level interleave (9 bytes) */
vgmstream->interleave_block_size = 9;
/* open the file for reading by each channel */
{
STREAMFILE *chstreamfile;
int i;
/* both channels use same buffer, as interleave is so small */
chstreamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!chstreamfile) goto fail;
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].streamfile = chstreamfile;
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=
0x20 + i*vgmstream->interleave_block_size;
}
}
return vgmstream;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
}

View File

@ -1058,6 +1058,9 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char
/* COEFS */
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;

View File

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

View File

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

View File

@ -146,20 +146,6 @@ VGMSTREAM * init_vgmstream_vag(STREAMFILE *streamFile) {
loop_flag = ps_find_loop_offsets(streamFile, start_offset, channel_size*channel_count, channel_count, interleave, &loop_start_sample, &loop_end_sample);
}
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;
}

View File

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

View File

@ -0,0 +1,140 @@
#include "meta.h"
#include "../coding/coding.h"
//todo test and rethink usefulness/viability of globally using this
/* generic helper with a usable fields to describe static header values */
typedef struct {
int codec;
int type;
int channels;
int sample_rate;
int loop_flag;
int loop_start;
int loop_end;
int total_subsongs;
int target_subsong;
size_t file_size;
off_t info_start;
off_t header_start;
size_t header_entry;
off_t header_offset;
off_t data_start;
size_t data_size;
off_t stream_start;
size_t stream_size;
} header_t;
/* XSSB - from Artoon games [Blinx (Xbox), Blinx 2 (Xbox)] */
VGMSTREAM* init_vgmstream_xssb(STREAMFILE *sf) {
VGMSTREAM *vgmstream = NULL;
//off_t start_offset, header_offset, data_start, info_start, header_start;
//size_t header_size, stream_size;
//int loop_flag, channel_count, sample_rate, codec, loop_start, loop_end;
//int total_subsongs, target_subsong = streamFile->stream_index;
header_t h;
/* checks */
/* .bin: from named files inside .ipk bigfiles */
if (!check_extensions(sf, "bin,lbin"))
goto fail;
if (read_u32be(0x00, sf) != 0x58535342) /* "XSSB" */
goto fail;
/* 0x04: null */
/* 0x08: date-version ('20011217' in hex) */
/* 0x0c: null */
h.info_start = read_s32le(0x10, sf);
h.header_start = read_s32le(0x14, sf);
h.data_start = read_s32le(0x18, sf);
/* 0x1c: null */
h.header_entry = read_s16le(h.info_start + 0x00, sf);
/* 0x02: always 127 */
/* get subsongs from header entries */
{
off_t offset = h.header_start;
h.total_subsongs = 0;
h.target_subsong = sf->stream_index <= 0 ? 1 : sf->stream_index;
h.header_offset = 0;
while (offset < h.data_start) {
/* headers are just pasted together and then padding */
if (read_u32be(offset, sf) == 0)
break;
h.total_subsongs++;
if (h.target_subsong == h.total_subsongs) {
h.header_offset = offset;
}
offset += h.header_entry;
}
if (h.header_offset == 0)
goto fail;
if (h.target_subsong > h.total_subsongs || h.total_subsongs < 1)
goto fail;
}
/* read header */
h.codec = read_s16le(h.header_offset + 0x00, sf);
h.channels = read_s16le(h.header_offset + 0x02, sf);
h.sample_rate = read_u16le(h.header_offset + 0x04, sf);
/* 0x08: bitrate */
/* 0x0c: block align/bps */
/* 0x10: 0=PCM, 2=XBOX-IMA? */
h.stream_start = read_s32le(h.header_offset + 0x14, sf) + h.data_start;
h.stream_size = read_s32le(h.header_offset + 0x18, sf);
h.loop_start = read_s32le(h.header_offset + 0x1c, sf);
h.loop_end = read_s32le(h.header_offset + 0x20, sf);
/* others: unknown and mostly fixed values */
h.loop_flag = (h.loop_end > 0);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(h.channels, h.loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_XSSB;
vgmstream->sample_rate = h.sample_rate;
vgmstream->loop_start_sample = h.loop_start;
vgmstream->loop_end_sample = h.loop_end;
vgmstream->num_streams = h.total_subsongs;
vgmstream->stream_size = h.stream_size;
switch(h.codec) {
case 0x01:
vgmstream->coding_type = coding_PCM16LE;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x01;
vgmstream->num_samples = pcm_bytes_to_samples(h.stream_size, h.channels, 16);
break;
case 0x69:
vgmstream->coding_type = coding_XBOX_IMA;
vgmstream->layout_type = layout_none;
vgmstream->num_samples = xbox_ima_bytes_to_samples(h.stream_size, h.channels);
break;
}
if (!vgmstream_open_stream(vgmstream, sf, h.stream_start))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -30,6 +30,9 @@ typedef struct {
int cue_names_size;
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;

View File

@ -29,6 +29,8 @@ static size_t read_stdio(STDIO_STREAMFILE *streamfile, uint8_t *dst, off_t offse
if (!streamfile->infile || !dst || length <= 0 || offset < 0)
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) {

View File

@ -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.

View File

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

View File

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