Updated VMStream to r1050-2028-gac28ccc5

CQTexperiment
Chris Moeller 2019-01-23 17:49:59 -08:00
parent 18c22cd6b2
commit 05de1e5033
31 changed files with 3156 additions and 1736 deletions

View File

@ -93,7 +93,6 @@
83299FD11E7660C7003A3242 /* dsp_adx.c in Sources */ = {isa = PBXBuildFile; fileRef = 83299FCF1E7660C7003A3242 /* dsp_adx.c */; };
832BF7FF21E050B7006F50F1 /* circus_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 832BF7FC21E050B6006F50F1 /* circus_decoder.c */; };
832BF80021E050B7006F50F1 /* mpeg_custom_utils_eamp3.c in Sources */ = {isa = PBXBuildFile; fileRef = 832BF7FD21E050B7006F50F1 /* mpeg_custom_utils_eamp3.c */; };
832BF80121E050B7006F50F1 /* pcfx_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 832BF7FE21E050B7006F50F1 /* pcfx_decoder.c */; };
832BF80521E050DC006F50F1 /* blocked_mul.c in Sources */ = {isa = PBXBuildFile; fileRef = 832BF80221E050DB006F50F1 /* blocked_mul.c */; };
832BF80621E050DC006F50F1 /* blocked_vs_square.c in Sources */ = {isa = PBXBuildFile; fileRef = 832BF80321E050DC006F50F1 /* blocked_vs_square.c */; };
832BF80721E050DC006F50F1 /* blocked_vs_str.c in Sources */ = {isa = PBXBuildFile; fileRef = 832BF80421E050DC006F50F1 /* blocked_vs_str.c */; };
@ -349,7 +348,6 @@
836F6FE018BDC2190095E648 /* ps2_joe.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EA418BDC2180095E648 /* ps2_joe.c */; };
836F6FE118BDC2190095E648 /* ps2_jstm.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EA518BDC2180095E648 /* ps2_jstm.c */; };
836F6FE218BDC2190095E648 /* ps2_kces.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EA618BDC2180095E648 /* ps2_kces.c */; };
836F6FE318BDC2190095E648 /* ps2_khv.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EA718BDC2180095E648 /* ps2_khv.c */; };
836F6FE418BDC2190095E648 /* ps2_leg.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EA818BDC2180095E648 /* ps2_leg.c */; };
836F6FE518BDC2190095E648 /* ps2_lpcm.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EA918BDC2180095E648 /* ps2_lpcm.c */; };
836F6FE618BDC2190095E648 /* ps2_mcg.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EAA18BDC2180095E648 /* ps2_mcg.c */; };
@ -452,6 +450,9 @@
83709E091ECBC1A4005C03D3 /* ta_aac.c in Sources */ = {isa = PBXBuildFile; fileRef = 83709E031ECBC1A4005C03D3 /* ta_aac.c */; };
83709E0D1ECBC1C3005C03D3 /* mc3_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 83709E0B1ECBC1C3005C03D3 /* mc3_decoder.c */; };
83709E0E1ECBC1C3005C03D3 /* psv_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 83709E0C1ECBC1C3005C03D3 /* psv_decoder.c */; };
8375737321F9507D00F01AF5 /* oki_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 8375737221F9507D00F01AF5 /* oki_decoder.c */; };
8375737621F950ED00F01AF5 /* gin.c in Sources */ = {isa = PBXBuildFile; fileRef = 8375737421F950EC00F01AF5 /* gin.c */; };
8375737721F950ED00F01AF5 /* ubi_sb_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 8375737521F950EC00F01AF5 /* ubi_sb_streamfile.h */; };
838BDB641D3AF08C0022CA6F /* libavcodec.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 838BDB611D3AF08C0022CA6F /* libavcodec.a */; };
838BDB651D3AF08C0022CA6F /* libavformat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 838BDB621D3AF08C0022CA6F /* libavformat.a */; };
838BDB661D3AF08C0022CA6F /* libavutil.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 838BDB631D3AF08C0022CA6F /* libavutil.a */; };
@ -733,7 +734,6 @@
83299FCF1E7660C7003A3242 /* dsp_adx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dsp_adx.c; sourceTree = "<group>"; };
832BF7FC21E050B6006F50F1 /* circus_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = circus_decoder.c; sourceTree = "<group>"; };
832BF7FD21E050B7006F50F1 /* mpeg_custom_utils_eamp3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mpeg_custom_utils_eamp3.c; sourceTree = "<group>"; };
832BF7FE21E050B7006F50F1 /* pcfx_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pcfx_decoder.c; sourceTree = "<group>"; };
832BF80221E050DB006F50F1 /* blocked_mul.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_mul.c; sourceTree = "<group>"; };
832BF80321E050DC006F50F1 /* blocked_vs_square.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_vs_square.c; sourceTree = "<group>"; };
832BF80421E050DC006F50F1 /* blocked_vs_str.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_vs_str.c; sourceTree = "<group>"; };
@ -989,7 +989,6 @@
836F6EA418BDC2180095E648 /* ps2_joe.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_joe.c; sourceTree = "<group>"; };
836F6EA518BDC2180095E648 /* ps2_jstm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_jstm.c; sourceTree = "<group>"; };
836F6EA618BDC2180095E648 /* ps2_kces.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_kces.c; sourceTree = "<group>"; };
836F6EA718BDC2180095E648 /* ps2_khv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_khv.c; sourceTree = "<group>"; };
836F6EA818BDC2180095E648 /* ps2_leg.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_leg.c; sourceTree = "<group>"; };
836F6EA918BDC2180095E648 /* ps2_lpcm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_lpcm.c; sourceTree = "<group>"; };
836F6EAA18BDC2180095E648 /* ps2_mcg.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_mcg.c; sourceTree = "<group>"; };
@ -1092,6 +1091,9 @@
83709E031ECBC1A4005C03D3 /* ta_aac.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ta_aac.c; sourceTree = "<group>"; };
83709E0B1ECBC1C3005C03D3 /* mc3_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mc3_decoder.c; sourceTree = "<group>"; };
83709E0C1ECBC1C3005C03D3 /* psv_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = psv_decoder.c; sourceTree = "<group>"; };
8375737221F9507D00F01AF5 /* oki_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = oki_decoder.c; sourceTree = "<group>"; };
8375737421F950EC00F01AF5 /* gin.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = gin.c; sourceTree = "<group>"; };
8375737521F950EC00F01AF5 /* ubi_sb_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ubi_sb_streamfile.h; sourceTree = "<group>"; };
838BDB611D3AF08C0022CA6F /* libavcodec.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libavcodec.a; path = ../../ThirdParty/ffmpeg/lib/libavcodec.a; sourceTree = "<group>"; };
838BDB621D3AF08C0022CA6F /* libavformat.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libavformat.a; path = ../../ThirdParty/ffmpeg/lib/libavformat.a; sourceTree = "<group>"; };
838BDB631D3AF08C0022CA6F /* libavutil.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libavutil.a; path = ../../ThirdParty/ffmpeg/lib/libavutil.a; sourceTree = "<group>"; };
@ -1357,7 +1359,7 @@
836F6DF618BDC2180095E648 /* nwa_decoder.c */,
836F6DF718BDC2180095E648 /* nwa_decoder.h */,
836F6DF818BDC2180095E648 /* ogg_vorbis_decoder.c */,
832BF7FE21E050B7006F50F1 /* pcfx_decoder.c */,
8375737221F9507D00F01AF5 /* oki_decoder.c */,
836F6DF918BDC2180095E648 /* pcm_decoder.c */,
83709E0C1ECBC1C3005C03D3 /* psv_decoder.c */,
836F6DFA18BDC2180095E648 /* psx_decoder.c */,
@ -1516,6 +1518,7 @@
836F6E4D18BDC2180095E648 /* gca.c */,
836F6E4E18BDC2180095E648 /* gcsw.c */,
836F6E4F18BDC2180095E648 /* genh.c */,
8375737421F950EC00F01AF5 /* gin.c */,
836F6E5118BDC2180095E648 /* gsp_gsb.c */,
83709DFF1ECBC1A4005C03D3 /* gtd.c */,
8342469020C4D22F00926E48 /* h4m.c */,
@ -1639,7 +1642,6 @@
836F6EA418BDC2180095E648 /* ps2_joe.c */,
836F6EA518BDC2180095E648 /* ps2_jstm.c */,
836F6EA618BDC2180095E648 /* ps2_kces.c */,
836F6EA718BDC2180095E648 /* ps2_khv.c */,
836F6EA818BDC2180095E648 /* ps2_leg.c */,
836F6EA918BDC2180095E648 /* ps2_lpcm.c */,
836F6EAA18BDC2180095E648 /* ps2_mcg.c */,
@ -1739,6 +1741,7 @@
8306B0C62098458D000302D4 /* ubi_lyn_ogg_streamfile.h */,
8306B0CA2098458E000302D4 /* ubi_lyn.c */,
831BA6131EAC61A500CF89B0 /* ubi_raki.c */,
8375737521F950EC00F01AF5 /* ubi_sb_streamfile.h */,
8349A8F41FE6257D00E26435 /* ubi_sb.c */,
834FE0C5215C79E6000A5D3D /* ue4opus.c */,
834FE0DD215C79EB000A5D3D /* utk.c */,
@ -1852,6 +1855,7 @@
8306B0E020984590000302D4 /* ppst_streamfile.h in Headers */,
834FE0BA215C798C000A5D3D /* ea_mt_decoder_utk.h in Headers */,
832BF82021E0514B006F50F1 /* zsnd_streamfile.h in Headers */,
8375737721F950ED00F01AF5 /* ubi_sb_streamfile.h in Headers */,
839E21E01F2EDAF100EE54D7 /* vorbis_custom_data_fsb.h in Headers */,
836F705418BDC2190095E648 /* streamfile.h in Headers */,
83A3F0761E3AD8B900D6A794 /* stack_alloc.h in Headers */,
@ -2279,7 +2283,6 @@
836F6FAF18BDC2190095E648 /* ngc_dsp_std.c in Sources */,
836F6F7118BDC2190095E648 /* ast.c in Sources */,
834FE0BF215C79A9000A5D3D /* flat.c in Sources */,
836F6FE318BDC2190095E648 /* ps2_khv.c in Sources */,
836F6F6B18BDC2190095E648 /* agsc.c in Sources */,
836F700E18BDC2190095E648 /* ps2_xa2.c in Sources */,
836F6FF718BDC2190095E648 /* ps2_sl3.c in Sources */,
@ -2288,6 +2291,7 @@
836F702118BDC2190095E648 /* rs03.c in Sources */,
836F6F8818BDC2190095E648 /* fsb.c in Sources */,
836F6FE518BDC2190095E648 /* ps2_lpcm.c in Sources */,
8375737321F9507D00F01AF5 /* oki_decoder.c in Sources */,
836F6FB318BDC2190095E648 /* ngc_lps.c in Sources */,
836F6FC018BDC2190095E648 /* p3d.c in Sources */,
836F6FC718BDC2190095E648 /* pona.c in Sources */,
@ -2301,7 +2305,6 @@
834FE106215C79ED000A5D3D /* utk.c in Sources */,
831BA6281EAC61CB00CF89B0 /* coding_utils.c in Sources */,
836F700218BDC2190095E648 /* ps2_tk5.c in Sources */,
832BF80121E050B7006F50F1 /* pcfx_decoder.c in Sources */,
83AA5D271F6E2F9C0020821C /* stm.c in Sources */,
831BA61D1EAC61A500CF89B0 /* ubi_raki.c in Sources */,
8306B0A520984552000302D4 /* blocked_ea_wve_au00.c in Sources */,
@ -2411,6 +2414,7 @@
836F6FB118BDC2190095E648 /* ngc_ffcc_str.c in Sources */,
8306B0B620984552000302D4 /* blocked_hwas.c in Sources */,
836F6FC218BDC2190095E648 /* pc_mxst.c in Sources */,
8375737621F950ED00F01AF5 /* gin.c in Sources */,
836F6FC918BDC2190095E648 /* ps2_2pfs.c in Sources */,
836F704818BDC2190095E648 /* xbox_ims.c in Sources */,
836F6F7518BDC2190095E648 /* bnsf.c in Sources */,

View File

@ -71,6 +71,8 @@ void decode_pcm8_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspac
void decode_pcm8_unsigned(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_pcm8_unsigned_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_pcm8_sb(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_pcm4(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_pcm4_unsigned(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_ulaw(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_ulaw_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_alaw(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
@ -99,7 +101,8 @@ void decode_ea_xa_v2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspac
void decode_maxis_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
/* ea_xas_decoder */
void decode_ea_xas(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_ea_xas_v0(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_ea_xas_v1(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
/* sdx2_decoder */
void decode_sdx2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
@ -168,9 +171,10 @@ void decode_derf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing,
/* circus_decoder */
void decode_circus_adpcm(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
/* pcfx_decoder */
/* oki_decoder */
void decode_pcfx(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int mode);
size_t pcfx_bytes_to_samples(size_t bytes, int channels);
void decode_oki16(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
size_t oki_bytes_to_samples(size_t bytes, int channels);
/* ea_mt_decoder*/
ea_mt_codec_data *init_ea_mt(int channels, int type);
@ -339,6 +343,7 @@ void xma2_parse_fmt_chunk_extra(STREAMFILE *streamFile, off_t chunk_offset, int
void xma2_parse_xma2_chunk(STREAMFILE *streamFile, off_t chunk_offset, int * channels, int * sample_rate, int * loop_flag, int32_t * num_samples, int32_t * loop_start_sample, int32_t * loop_end_sample);
void xma_fix_raw_samples(VGMSTREAM *vgmstream, STREAMFILE*streamFile, off_t stream_offset, size_t stream_size, off_t chunk_offset, int fix_num_samples, int fix_loop_samples);
void xma_fix_raw_samples_ch(VGMSTREAM *vgmstream, STREAMFILE*streamFile, off_t stream_offset, size_t stream_size, int channel_per_stream, int fix_num_samples, int fix_loop_samples);
int riff_get_fact_skip_samples(STREAMFILE * streamFile, off_t start_offset);

View File

@ -799,14 +799,13 @@ void wma_get_samples(ms_sample_data * msd, STREAMFILE *streamFile, int block_ali
/* XMA hell for precise looping and gapless support, fixes raw sample values from headers
* that don't count XMA's final subframe/encoder delay/encoder padding, and FFmpeg stuff.
* Configurable since different headers vary for maximum annoyance. */
void xma_fix_raw_samples(VGMSTREAM *vgmstream, STREAMFILE*streamFile, off_t stream_offset, size_t stream_size, off_t chunk_offset, int fix_num_samples, int fix_loop_samples) {
void xma_fix_raw_samples_ch(VGMSTREAM *vgmstream, STREAMFILE*streamFile, off_t stream_offset, size_t stream_size, int channels_per_stream, int fix_num_samples, int fix_loop_samples) {
const int bytes_per_packet = 2048;
const int samples_per_frame = 512;
const int samples_per_subframe = 128;
const int bits_frame_size = 15;
int xma_version = 2; /* works ok even for XMA1 */
int channels_per_stream = xma_get_channels_per_stream(streamFile, chunk_offset, vgmstream->channels);
off_t first_packet = stream_offset;
off_t last_packet = stream_offset + stream_size - bytes_per_packet;
int32_t start_skip = 0, end_skip = 0;
@ -872,6 +871,12 @@ void xma_fix_raw_samples(VGMSTREAM *vgmstream, STREAMFILE*streamFile, off_t stre
#endif
}
void xma_fix_raw_samples(VGMSTREAM *vgmstream, STREAMFILE*streamFile, off_t stream_offset, size_t stream_size, off_t chunk_offset, int fix_num_samples, int fix_loop_samples) {
int channels_per_stream = xma_get_channels_per_stream(streamFile, chunk_offset, vgmstream->channels);
xma_fix_raw_samples_ch(vgmstream, streamFile, stream_offset, stream_size, channels_per_stream, fix_num_samples, fix_loop_samples);
}
/* ******************************************** */
/* HEADER PARSING */
/* ******************************************** */

View File

@ -9,11 +9,11 @@ static const int EA_XA_TABLE[20] = {
0, -1, -3, -4
};
/* EA-XAS, evolution of EA-XA and cousin of MTA2. From FFmpeg (general info) + MTA2 (layout) + EA-XA (decoding)
/* EA-XAS v1, evolution of EA-XA/XAS and cousin of MTA2. From FFmpeg (general info) + MTA2 (layout) + EA-XA (decoding)
*
* Layout: blocks of 0x4c per channel (128 samples), divided into 4 headers + 4 vertical groups of 15 bytes (for parallelism?).
* To simplify, always decodes the block and discards unneeded samples, so doesn't use external hist. */
void decode_ea_xas(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
void decode_ea_xas_v1(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
int group, row, i;
int samples_done = 0, sample_count = 0;
@ -77,3 +77,64 @@ void decode_ea_xas(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacin
stream->offset += 0x4c * channelspacing;
}
}
/* EA-XAS v0, without complex layouts and closer to EA-XA. Somewhat based on daemon1's decoder */
void decode_ea_xas_v0(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
off_t frame_offset;
int i;
int block_samples, frames_in, samples_done = 0, sample_count = 0;
/* external interleave (fixed size), mono */
block_samples = 32;
frames_in = first_sample / block_samples;
first_sample = first_sample % block_samples;
frame_offset = stream->offset + (0x0f+0x02+0x02)*frames_in;
/* process frames */
{
int coef1, coef2;
int16_t hist1, hist2;
uint8_t shift;
uint32_t frame_header = (uint32_t)read_32bitLE(frame_offset, stream->streamfile); /* always LE */
coef1 = EA_XA_TABLE[(uint8_t)(frame_header & 0x0F) + 0];
coef2 = EA_XA_TABLE[(uint8_t)(frame_header & 0x0F) + 4];
hist2 = (int16_t)(frame_header & 0xFFF0);
hist1 = (int16_t)((frame_header >> 16) & 0xFFF0);
shift = 20 - ((frame_header >> 16) & 0x0F);
/* write header samples (needed) */
if (sample_count >= first_sample && samples_done < samples_to_do) {
outbuf[samples_done * channelspacing] = hist2;
samples_done++;
}
sample_count++;
if (sample_count >= first_sample && samples_done < samples_to_do) {
outbuf[samples_done * channelspacing] = hist1;
samples_done++;
}
sample_count++;
/* process nibbles */
for (i = 0; i < 0x0f*2; i++) {
uint8_t sample_byte = (uint8_t)read_8bit(frame_offset + 0x02 + 0x02 + i/2, stream->streamfile);
int sample;
sample = get_nibble_signed(sample_byte, !(i&1)); /* upper first */
sample = sample << shift;
sample = (sample + hist1 * coef1 + hist2 * coef2 + 128) >> 8;
sample = clamp16(sample);
if (sample_count >= first_sample && samples_done < samples_to_do) {
outbuf[samples_done * channelspacing] = sample;
samples_done++;
}
sample_count++;
hist2 = hist1;
hist1 = sample;
}
}
}

View File

@ -1,14 +1,14 @@
#include "coding.h"
static const int step_sizes[49] = { /* OKI table */
static const int step_sizes[49] = { /* OKI table (subsection of IMA's table) */
16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50,
55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157,
173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449,
494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552
};
static const int stex_indexes[16] = { /* IMA table */
static const int stex_indexes[16] = { /* OKI table (also from IMA) */
-1, -1, -1, -1, 2, 4, 6, 8,
-1, -1, -1, -1, 2, 4, 6, 8
};
@ -54,6 +54,26 @@ static void pcfx_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int
}
}
static void oki16_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t * hist1, int32_t * step_index, int16_t *out_sample) {
int code, step, delta;
code = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift) & 0xf;
step = step_sizes[*step_index];
delta = (code & 0x7);
delta = (((delta * 2) + 1) * step) >> 3; /* IMA 'mul' style (standard OKI uses 'shift-add') */
if (code & 0x8)
delta = -delta;
*hist1 += delta;
/* standard OKI clamps hist to 2047,-2048 here */
*step_index += stex_indexes[code];
if (*step_index < 0) *step_index = 0;
if (*step_index > 48) *step_index = 48;
*out_sample = *hist1;
}
/* PC-FX ADPCM decoding, variation of OKI/Dialogic/VOX ADPCM. Based on mednafen/pcfx-music-dump.
* Apparently most ADPCM was made with a buggy encoder, resulting in incorrect sound in real hardware
@ -65,7 +85,7 @@ static void pcfx_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int
*
* PC-FX ISOs don't have a standard filesystem nor file formats (raw data must be custom-ripped),
* so it's needs GENH/TXTH. Sample rate can only be base_value divided by 1/2/3/4, where
* base_value is approximately ~31468.5 (follows hardware clocks), mono or stereo-interleaved.
* base_value is approximately ~31468.5 (follows hardware clocks), mono or interleaved for stereo.
*/
void decode_pcfx(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int mode) {
int i, sample_count = 0;
@ -86,7 +106,40 @@ void decode_pcfx(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing,
stream->adpcm_step_index = step_index;
}
size_t pcfx_bytes_to_samples(size_t bytes, int channels) {
/* OKI variation with 16-bit output (vs standard's 12-bit), found in FrontWing's PS2 games (Sweet Legacy, Hooligan).
* Reverse engineered from the ELF with help from the folks at hcs. */
void decode_oki16(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
int i, sample_count = 0;
int32_t hist1 = stream->adpcm_history1_32;
int step_index = stream->adpcm_step_index;
int16_t out_sample;
int is_stereo = channelspacing > 1;
/* external interleave */
/* no header (external setup), pre-clamp for wrong values */
if (step_index < 0) step_index=0;
if (step_index > 48) step_index=48;
/* decode nibbles (layout: varies) */
for (i = first_sample; i < first_sample + samples_to_do; i++, sample_count += channelspacing) {
off_t byte_offset = is_stereo ?
stream->offset + i : /* stereo: one nibble per channel */
stream->offset + i/2; /* mono: consecutive nibbles (assumed) */
int nibble_shift =
is_stereo ? (!(channel&1) ? 0:4) : (!(i&1) ? 0:4); /* even = low, odd = high */
oki16_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index, &out_sample);
outbuf[sample_count] = (out_sample);
}
stream->adpcm_history1_32 = hist1;
stream->adpcm_step_index = step_index;
}
size_t oki_bytes_to_samples(size_t bytes, int channels) {
/* 2 samples per byte (2 nibbles) in stereo or mono config */
return bytes * 2 / channels;
}

View File

@ -78,6 +78,52 @@ void decode_pcm8_sb(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspaci
}
}
void decode_pcm4(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
int i, nibble_shift, is_high_first, is_stereo;
int32_t sample_count;
int16_t v;
off_t byte_offset;
is_high_first = (vgmstream->codec_config & 1);
is_stereo = (vgmstream->channels != 1);
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
byte_offset = is_stereo ?
stream->offset + i : /* stereo: one nibble per channel (assumed, not sure if stereo version actually exists) */
stream->offset + i/2; /* mono: consecutive nibbles */
nibble_shift = is_high_first ?
is_stereo ? (!(channel&1) ? 4:0) : (!(i&1) ? 4:0) : /* even = high, odd = low */
is_stereo ? (!(channel&1) ? 0:4) : (!(i&1) ? 0:4); /* even = low, odd = high */
v = (int16_t)read_8bit(byte_offset, stream->streamfile);
v = (v >> nibble_shift) & 0x0F;
outbuf[sample_count] = v*0x11*0x100;
}
}
void decode_pcm4_unsigned(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
int i, nibble_shift, is_high_first, is_stereo;
int32_t sample_count;
int16_t v;
off_t byte_offset;
is_high_first = (vgmstream->codec_config & 1);
is_stereo = (vgmstream->channels != 1);
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
byte_offset = is_stereo ?
stream->offset + i : /* stereo: one nibble per channel (assumed, not sure if stereo version actually exists) */
stream->offset + i/2; /* mono: consecutive nibbles */
nibble_shift = is_high_first ?
is_stereo ? (!(channel&1) ? 4:0) : (!(i&1) ? 4:0) : /* even = high, odd = low */
is_stereo ? (!(channel&1) ? 0:4) : (!(i&1) ? 0:4); /* even = low, odd = high */
v = (int16_t)read_8bit(byte_offset, stream->streamfile);
v = (v >> nibble_shift) & 0x0F;
outbuf[sample_count] = v*0x11*0x100 - 0x8000;
}
}
static int expand_ulaw(uint8_t ulawbyte) {
int sign, segment, quantization, new_sample;
const int bias = 0x84;
@ -175,5 +221,5 @@ void decode_pcmfloat(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspac
}
size_t pcm_bytes_to_samples(size_t bytes, int channels, int bits_per_sample) {
return bytes / channels / (bits_per_sample/8);
return (bytes * 8) / channels / bits_per_sample;
}

View File

@ -155,6 +155,7 @@ static const char* extension_list[] = {
"gcub",
"gcw",
"genh",
"gin",
"gms",
"gsb",
//"gsf", //conflicts with GBA gsf plugins?
@ -194,7 +195,7 @@ static const char* extension_list[] = {
"kces",
"kcey", //fake extension/header id for .pcm (renamed, to be removed)
"khv",
"khv", //fake extension/header id for .vas (renamed, to be removed)
"km9",
"kovs", //fake extension/header id for .kvs
"kns",
@ -205,6 +206,7 @@ static const char* extension_list[] = {
"l",
"laac", //fake extension for .aac (tri-Ace)
"lac3", //fake extension for .ac3, FFmpeg/not parsed
"lasf", //fake extension for .asf (various)
"leg",
"lflac", //fake extension for .flac, FFmpeg/not parsed
"lin",
@ -348,6 +350,7 @@ static const char* extension_list[] = {
"sb5",
"sb6",
"sb7",
"sbr",
"sm0",
"sm1",
"sm2",
@ -551,11 +554,13 @@ static const coding_info coding_info_list[] = {
{coding_PCM16LE, "Little Endian 16-bit PCM"},
{coding_PCM16BE, "Big Endian 16-bit PCM"},
{coding_PCM16_int, "16-bit PCM with 2 byte interleave (block)"},
{coding_PCM8, "8-bit PCM"},
{coding_PCM8_int, "8-bit PCM with 1 byte interleave (block)"},
{coding_PCM8, "8-bit signed PCM"},
{coding_PCM8_int, "8-bit signed PCM with 1 byte interleave (block)"},
{coding_PCM8_U, "8-bit unsigned PCM"},
{coding_PCM8_U_int, "8-bit unsigned PCM with 1 byte interleave (block)"},
{coding_PCM8_SB, "8-bit PCM with sign bit"},
{coding_PCM4, "4-bit signed PCM"},
{coding_PCM4_U, "4-bit unsigned PCM"},
{coding_ULAW, "8-bit u-Law"},
{coding_ULAW_int, "8-bit u-Law with 1 byte interleave (block)"},
{coding_ALAW, "8-bit a-Law"},
@ -584,7 +589,8 @@ static const coding_info coding_info_list[] = {
{coding_EA_XA_int, "Electronic Arts EA-XA 4-bit ADPCM v1 (mono/interleave)"},
{coding_EA_XA_V2, "Electronic Arts EA-XA 4-bit ADPCM v2"},
{coding_MAXIS_XA, "Maxis EA-XA 4-bit ADPCM"},
{coding_EA_XAS, "Electronic Arts EA-XAS 4-bit ADPCM"},
{coding_EA_XAS_V0, "Electronic Arts EA-XAS 4-bit ADPCM v0"},
{coding_EA_XAS_V1, "Electronic Arts EA-XAS 4-bit ADPCM v1"},
{coding_IMA, "IMA 4-bit ADPCM"},
{coding_IMA_int, "IMA 4-bit ADPCM (mono/interleave)"},
@ -615,6 +621,7 @@ static const coding_info coding_info_list[] = {
{coding_H4M_IMA, "Hudson HVQM4 4-bit IMA ADPCM"},
{coding_MSADPCM, "Microsoft 4-bit ADPCM"},
{coding_MSADPCM_int, "Microsoft 4-bit ADPCM (mono/interleave)"},
{coding_MSADPCM_ck, "Microsoft 4-bit ADPCM (Cricket Audio)"},
{coding_WS, "Westwood Studios VBR ADPCM"},
{coding_AICA, "Yamaha AICA 4-bit ADPCM"},
@ -631,6 +638,7 @@ static const coding_info coding_info_list[] = {
{coding_ASF, "Argonaut ASF 4-bit ADPCM"},
{coding_XMD, "Konami XMD 4-bit ADPCM"},
{coding_PCFX, "PC-FX 4-bit ADPCM"},
{coding_OKI16, "OKI 4-bit ADPCM (16-bit output)"},
{coding_SDX2, "Squareroot-delta-exact (SDX2) 8-bit DPCM"},
{coding_SDX2_int, "Squareroot-delta-exact (SDX2) 8-bit DPCM with 1 byte interleave"},
@ -763,7 +771,7 @@ static const meta_info meta_info_list[] = {
{meta_PS_HEADERLESS, "Headerless PS-ADPCM raw header"},
{meta_PS2_MIB_MIH, "Sony MultiStream MIH+MIB header"},
{meta_DSP_MPDSP, "Single DSP header stereo by .mpdsp extension"},
{meta_PS2_MIC, "assume KOEI MIC file by .mic extension"},
{meta_PS2_MIC, "KOEI .MIC header"},
{meta_DSP_JETTERS, "Double DSP header stereo by _lr.dsp extension"},
{meta_DSP_MSS, "Double DSP header stereo by .mss extension"},
{meta_DSP_GCM, "Double DSP header stereo by .gcm extension"},
@ -993,7 +1001,7 @@ static const meta_info meta_info_list[] = {
{meta_PS3_CPS, "tri-Crescendo CPS Header"},
{meta_SQEX_SCD, "Square-Enix SCD header"},
{meta_NGC_NST_DSP, "Animaniacs NST header"},
{meta_BAF, ".baf WAVE header"},
{meta_BAF, "Bizarre Creations .baf header"},
{meta_PS3_MSF, "Sony MSF header"},
{meta_NUB_VAG, "Namco NUB VAG header"},
{meta_PS3_PAST, "SNDP header"},
@ -1153,6 +1161,7 @@ static const meta_info meta_info_list[] = {
{meta_DSP_ADPCMX, "AQUASTYLE ADPY header"},
{meta_OGG_OPUS, "Ogg Opus header"},
{meta_IMC, "iNiS .IMC header"},
{meta_GIN, "Electronic Arts Gnsu header"},
};

View File

@ -9,7 +9,9 @@ VGMSTREAM * init_vgmstream_asf(STREAMFILE *streamFile) {
/* checks */
if (!check_extensions(streamFile, "asf"))
/* .asf: original
* .lasf: fake for plugins */
if (!check_extensions(streamFile, "asf,lasf"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x41534600) /* "ASF\0" */

View File

@ -1,9 +1,189 @@
#include "meta.h"
#include "../coding/coding.h"
/* .BAF - Bizarre Creations (Blur, James Bond 007: Blood Stone, etc) */
/* .BAF - Bizarre Creations bank file [Blur (PS3), Project Gotham Racing 4 (X360), Geometry Wars (PC)] */
VGMSTREAM * init_vgmstream_baf(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t WAVE_size, DATA_size;
off_t start_offset, header_offset, name_offset;
size_t stream_size;
int loop_flag, channel_count, sample_rate, version, codec;
int total_subsongs, target_subsong = streamFile->stream_index;
int32_t (*read_32bit)(off_t,STREAMFILE*);
/* checks */
if (!check_extensions(streamFile, "baf"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x42414E4B) /* "BANK" */
goto fail;
/* use BANK size to check endianness */
if (guess_endianness32bit(0x04,streamFile)) {
read_32bit = read_32bitBE;
} else {
read_32bit = read_32bitLE;
}
/* 0x04: bank size */
version = read_32bit(0x08,streamFile);
if (version != 0x03 && version != 0x04)
goto fail;
total_subsongs = read_32bit(0x0c,streamFile);
if (target_subsong == 0) target_subsong = 1;
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
/* - in v3 */
/* 0x10: 0? */
/* 0x11: bank name */
/* - in v4 */
/* 0x10: 1? */
/* 0x11: padding flag? */
/* 0x12: bank name */
/* find target WAVE chunk */
{
int i;
off_t offset = read_32bit(0x04, streamFile);
for (i = 0; i < total_subsongs; i++) {
if (i+1 == target_subsong)
break;
offset += read_32bit(offset+0x04, streamFile); /* WAVE size, variable per codec */
}
header_offset = offset;
}
/* parse header */
if (read_32bitBE(header_offset+0x00, streamFile) != 0x57415645) /* "WAVE" */
goto fail;
codec = read_32bit(header_offset+0x08, streamFile);
name_offset = header_offset + 0x0c;
start_offset = read_32bit(header_offset+0x2c, streamFile);
stream_size = read_32bit(header_offset+0x30, streamFile);
switch(codec) {
case 0x03:
switch(version) {
case 0x03: /* Geometry Wars (PC) */
sample_rate = read_32bit(header_offset + 0x38, streamFile);
channel_count = read_32bit(header_offset + 0x40, streamFile);
/* no actual flag, just loop +15sec songs */
loop_flag = (pcm_bytes_to_samples(stream_size, channel_count, 16) > 15*sample_rate);
break;
case 0x04: /* Project Gotham Racing 4 (X360) */
sample_rate = read_32bit(header_offset + 0x3c, streamFile);
channel_count = read_32bit(header_offset + 0x44, streamFile);
loop_flag = read_8bit(header_offset+0x4b, streamFile);
break;
}
break;
case 0x07: /* Blur (PS3) */
sample_rate = read_32bit(header_offset+0x40, streamFile);
loop_flag = read_8bit(header_offset+0x48, streamFile);
channel_count = read_8bit(header_offset+0x4b, streamFile);
break;
case 0x08: /* Project Gotham Racing (X360) */
sample_rate = read_32bit(header_offset+0x3c, streamFile);
channel_count = read_32bit(header_offset+0x44, streamFile);
loop_flag = read_8bit(header_offset+0x54, streamFile) != 0;
break;
default:
VGM_LOG("BAF: unknown version %x\n", version);
goto fail;
}
/* others: pan/vol? fixed values? (0x19, 0x10) */
/* after WAVEs there may be padding then DATAs chunks, but offsets point after DATA size */
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_BAF;
vgmstream->sample_rate = sample_rate;
vgmstream->num_streams = total_subsongs;
vgmstream->stream_size = stream_size;
switch(codec) {
case 0x03:
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, 16);
vgmstream->loop_start_sample = 0;
vgmstream->loop_end_sample = vgmstream->num_samples;
break;
case 0x07:
vgmstream->coding_type = coding_PSX_cfg;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x21;
vgmstream->num_samples = read_32bit(header_offset+0x44, streamFile);
vgmstream->loop_start_sample = 0;
vgmstream->loop_end_sample = vgmstream->num_samples;
break;
case 0x08: {
uint8_t buf[0x100];
int bytes;
bytes = ffmpeg_make_riff_xma1(buf,0x100, vgmstream->num_samples, stream_size, vgmstream->channels, vgmstream->sample_rate, 0);
vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,stream_size);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
/* need to manually find sample offsets, it was a thing with XMA1 */
{
ms_sample_data msd = {0};
msd.xma_version = 1;
msd.channels = channel_count;
msd.data_offset = start_offset;
msd.data_size = stream_size;
msd.loop_flag = loop_flag;
msd.loop_start_b = read_32bit(header_offset+0x4c, streamFile);
msd.loop_end_b = read_32bit(header_offset+0x50, streamFile);
msd.loop_start_subframe = (read_8bit(header_offset+0x55, streamFile) >> 0) & 0x0f;
msd.loop_end_subframe = (read_8bit(header_offset+0x55, streamFile) >> 4) & 0x0f;
xma_get_samples(&msd, streamFile);
vgmstream->num_samples = msd.num_samples; /* also at 0x58, but unreliable? */
vgmstream->loop_start_sample = msd.loop_start_sample;
vgmstream->loop_end_sample = msd.loop_end_sample;
}
xma_fix_raw_samples_ch(vgmstream, streamFile, start_offset, stream_size, channel_count, 1,1);
break;
}
default:
VGM_LOG("BAF: unknown codec %x\n", codec);
goto fail;
}
read_string(vgmstream->stream_name,0x20+1, name_offset,streamFile);
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}
/* awful PS3 splits of the above with bad offsets and all */
VGMSTREAM * init_vgmstream_baf_badrip(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t WAVE_size, stream_size;
off_t start_offset;
long sample_count;
int sample_rate;
@ -13,38 +193,31 @@ VGMSTREAM * init_vgmstream_baf(STREAMFILE *streamFile) {
int channels;
int loop_flag = 0;
/* check extensions */
/* checks */
if ( !check_extensions(streamFile, "baf") )
goto fail;
/* check WAVE */
if (read_32bitBE(0,streamFile) != 0x57415645) /* "WAVE" */
goto fail;
WAVE_size = read_32bitBE(4,streamFile);
if (WAVE_size != 0x4c) /* && WAVE_size != 0x50*/
goto fail;
/* check for DATA after WAVE */
if (read_32bitBE(WAVE_size,streamFile) != 0x44415441) /* "DATA"*/
goto fail;
/* check that WAVE size is data size */
DATA_size = read_32bitBE(0x30,streamFile);
if (read_32bitBE(WAVE_size+4,streamFile)-8 != DATA_size) goto fail;
stream_size = read_32bitBE(0x30,streamFile);
if (read_32bitBE(WAVE_size+4,streamFile)-8 != stream_size) goto fail;
/*if (WAVE_size == 0x50) sample_count = DATA_size * frame_samples / frame_size / channels;*/
sample_count = read_32bitBE(0x44,streamFile);
/*if (WAVE_size == 0x50) sample_rate = read_32bitBE(0x3c,streamFile);*/
sample_rate = read_32bitBE(0x40,streamFile);
/* unsure how to detect channel count, so use a hack */
channels = (long long)DATA_size / frame_size * frame_samples / sample_count;
channels = (long long)stream_size / frame_size * frame_samples / sample_count;
start_offset = WAVE_size + 8;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
start_offset = WAVE_size + 8;
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = sample_count;
@ -53,14 +226,11 @@ VGMSTREAM * init_vgmstream_baf(STREAMFILE *streamFile) {
vgmstream->interleave_block_size = frame_size;
vgmstream->meta_type = meta_BAF;
/* open the file for reading */
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -3,16 +3,16 @@
typedef enum { PSX, PCM16, ATRAC9, HEVAG } bnk_codec;
/* BNK - Sony's Scream Tool bank format [Puyo Puyo Tetris (PS4), NekoBuro: Cats Block (Vita)] */
/* .BNK - Sony's Scream Tool bank format [Puyo Puyo Tetris (PS4), NekoBuro: Cats Block (Vita)] */
VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
#if 1
VGMSTREAM * vgmstream = NULL;
off_t start_offset, stream_offset, name_offset = 0;
size_t stream_size, interleave = 0;
off_t sblk_offset, data_offset;
size_t data_size;
int channel_count = 0, loop_flag, sample_rate, parts, version;
int channel_count = 0, loop_flag, sample_rate, parts, version, big_endian;
int loop_start = 0, loop_end = 0;
uint32_t pitch, flags;
uint32_t atrac9_info = 0;
int total_subsongs, target_subsong = streamFile->stream_index;
@ -28,10 +28,12 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
if (read_32bitBE(0x00,streamFile) == 0x00000003) { /* PS3 */
read_32bit = read_32bitBE;
read_16bit = read_16bitBE;
big_endian = 1;
}
else if (read_32bitBE(0x00,streamFile) == 0x03000000) { /* Vita/PS4 */
read_32bit = read_32bitLE;
read_16bit = read_16bitLE;
big_endian = 0;
}
else {
goto fail;
@ -44,11 +46,13 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
/* 0x0c: sklb size */
data_offset = read_32bit(0x10,streamFile);
data_size = read_32bit(0x14,streamFile);
/* when sblk_offset >= 0x20: */
/* 0x18: ZLSD small footer, rare [Yakuza 6's Puyo Puyo (PS4)] */
/* 0x1c: ZLSD size */
/* SE banks, also used for music. Most table fields seems reserved/defaults and
* don't change much between subsongs or files, so they aren't described in detail */
* don't change much between subsongs or files, so they aren't described in detail.
* Entry sizes are variable (usually flag + extra size xN) so table offsets are needed. */
/* SBlk part: parse header */
@ -63,7 +67,7 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
off_t table1_offset, table2_offset, table3_offset, table4_offset;
size_t section_entries, material_entries, stream_entries;
size_t table1_entry_size;
off_t table1_suboffset, table2_suboffset, table3_suboffset;
off_t table1_suboffset, table2_suboffset;
off_t table2_entry_offset = 0, table3_entry_offset = 0;
int table4_entry_id = -1;
off_t table4_entries_offset, table4_names_offset;
@ -75,7 +79,7 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
case 0x09: /* Puyo Puyo Tetris (PS4) */
section_entries = (uint16_t)read_16bit(sblk_offset+0x16,streamFile); /* entry size: ~0x0c */
material_entries = (uint16_t)read_16bit(sblk_offset+0x18,streamFile); /* entry size: ~0x08 */
stream_entries = (uint16_t)read_16bit(sblk_offset+0x1a,streamFile); /* entry size: ~0x60 */
stream_entries = (uint16_t)read_16bit(sblk_offset+0x1a,streamFile); /* entry size: ~0x18 + variable */
table1_offset = sblk_offset + read_32bit(sblk_offset+0x1c,streamFile);
table2_offset = sblk_offset + read_32bit(sblk_offset+0x20,streamFile);
table3_offset = sblk_offset + read_32bit(sblk_offset+0x34,streamFile);
@ -84,7 +88,6 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
table1_entry_size = 0x0c;
table1_suboffset = 0x08;
table2_suboffset = 0x00;
table3_suboffset = 0x10;
break;
case 0x0d: /* Polara (Vita), Crypt of the Necrodancer (Vita) */
@ -95,12 +98,11 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
table4_offset = sblk_offset + read_32bit(sblk_offset+0x30,streamFile);
section_entries = (uint16_t)read_16bit(sblk_offset+0x38,streamFile); /* entry size: ~0x24 */
material_entries = (uint16_t)read_16bit(sblk_offset+0x3a,streamFile); /* entry size: ~0x08 */
stream_entries = (uint16_t)read_16bit(sblk_offset+0x3c,streamFile); /* entry size: ~0x90 + variable (sometimes) */
stream_entries = (uint16_t)read_16bit(sblk_offset+0x3c,streamFile); /* entry size: ~0x5c + variable */
table1_entry_size = 0x24;
table1_suboffset = 0x0c;
table2_suboffset = 0x00;
table3_suboffset = 0x44;
break;
default:
@ -150,14 +152,64 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
/* this means some subsongs repeat streams, that can happen in some sfx banks, whatevs */
if (total_subsongs != stream_entries) {
//;VGM_LOG("BNK: subsongs %i vs table3 %i don't match\n", total_subsongs, stream_entries);
VGM_LOG("BNK: subsongs %i vs table3 %i don't match\n", total_subsongs, stream_entries);
/* find_dupes...? */
}
//;VGM_LOG("BNK: header entry at %lx\n", table3_offset+table3_entry_offset);
/* parse sounds */
stream_offset = read_32bit(table3_offset+table3_entry_offset+table3_suboffset+0x00,streamFile);
stream_size = read_32bit(table3_offset+table3_entry_offset+table3_suboffset+0x04,streamFile);
switch(version) {
case 0x03:
case 0x04:
case 0x09:
pitch = (uint8_t)read_8bit(table3_offset+table3_entry_offset+0x02,streamFile);
flags = (uint8_t)read_8bit(table3_offset+table3_entry_offset+0x0f,streamFile);
stream_offset = read_32bit(table3_offset+table3_entry_offset+0x10,streamFile);
stream_size = read_32bit(table3_offset+table3_entry_offset+0x14,streamFile);
/* must use some log/formula but whatevs */
switch(pitch) {
case 0xC6: sample_rate = 50000; break; //?
case 0xC4: sample_rate = 48000; break;
case 0xC3: sample_rate = 46000; break; //?
case 0xC2: sample_rate = 44100; break;
case 0xBC: sample_rate = 36000; break; //?
case 0xBA: sample_rate = 32000; break; //?
case 0xB6: sample_rate = 22050; break;
case 0xAA: sample_rate = 11025; break;
default:
VGM_LOG("BNK: unknown pitch %x\n", pitch);
goto fail;
}
break;
case 0x0d:
case 0x0e:
flags = (uint8_t)read_8bit(table3_offset+table3_entry_offset+0x12,streamFile);
stream_offset = read_32bit(table3_offset+table3_entry_offset+0x44,streamFile);
stream_size = read_32bit(table3_offset+table3_entry_offset+0x48,streamFile);
pitch = (uint32_t)read_32bit(table3_offset+table3_entry_offset+0x4c,streamFile);
/* this looks like "((pitch >> 9) & 0xC000) | ((pitch >> 8) & 0xFFFF)" but... why??? */
switch(pitch) {
case 0x467A0000: sample_rate = 64000; break; //?
case 0x46BB8000: sample_rate = 48000; break;
case 0x473B8000: sample_rate = 48000; break;
case 0x46AC4400: sample_rate = 44100; break;
case 0x47AC4400: sample_rate = 44100; break;
case 0x472C4400: sample_rate = 44100; break;
default:
VGM_LOG("BNK: unknown pitch %x\n", pitch);
goto fail;
}
break;
default:
goto fail;
}
//;VGM_LOG("BNK: stream at %lx + %x\n", stream_offset, stream_size);
/* parse names */
switch(version) {
@ -215,7 +267,6 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
switch(version) {
case 0x03:
case 0x04:
sample_rate = 48000; /* seems ok */
channel_count = 1;
/* hack for PS3 files that use dual subsongs as stereo */
@ -224,14 +275,19 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
stream_size = stream_size*channel_count;
total_subsongs = 1;
}
interleave = stream_size / channel_count;
if (flags & 0x80) {
codec = PCM16; /* rare [Wipeout HD (PS3)] */
}
else {
loop_flag = ps_find_loop_offsets(streamFile, start_offset, stream_size, channel_count, interleave, &loop_start, &loop_end);
loop_flag = (flags & 0x40); /* no loops values in sight so may only apply to PS-ADPCM flags */
codec = PSX;
}
//postdata_size = 0x10; /* last frame may be garbage */
loop_flag = ps_find_loop_offsets(streamFile, start_offset, stream_size, channel_count, interleave, &loop_start, &loop_end);
loop_flag = (loop_start > 28); /* ignore full loops since they just fadeout + repeat */
codec = PSX;
break;
case 0x09:
@ -243,7 +299,6 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
case 0x05: /* ATRAC9 stereo */
if (read_32bit(start_offset+0x08,streamFile) + 0x08 != extradata_size) /* repeat? */
goto fail;
sample_rate = 48000; /* seems ok */
channel_count = (type == 0x02) ? 1 : 2;
atrac9_info = (uint32_t)read_32bitBE(start_offset+0x0c,streamFile);
@ -274,7 +329,6 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
case 0x05: /* ATRAC9 stereo */
if (read_32bit(start_offset+0x10,streamFile) + 0x10 != extradata_size) /* repeat? */
goto fail;
sample_rate = 48000; /* seems ok */
channel_count = (type == 0x02) ? 1 : 2;
atrac9_info = (uint32_t)read_32bitBE(start_offset+0x14,streamFile);
@ -291,7 +345,6 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
case 0x01: /* PCM16LE mono? (NekoBuro/Polara sfx) */
case 0x04: /* PCM16LE stereo? (NekoBuro/Polara sfx) */
sample_rate = 48000; /* seems ok */
/* 0x10: null? */
channel_count = read_32bit(start_offset+0x14,streamFile);
interleave = 0x02;
@ -304,7 +357,6 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
break;
case 0x00: /* PS-ADPCM (test banks) */
sample_rate = 48000; /* seems ok */
/* 0x10: null? */
channel_count = read_32bit(start_offset+0x14,streamFile);
interleave = 0x02;
@ -366,7 +418,7 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
}
#endif
case PCM16:
vgmstream->coding_type = coding_PCM16LE;
vgmstream->coding_type = big_endian ? coding_PCM16BE : coding_PCM16LE;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = interleave;
@ -376,7 +428,6 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
break;
case PSX:
vgmstream->sample_rate = 48000;
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = interleave;
@ -387,7 +438,6 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
break;
case HEVAG:
vgmstream->sample_rate = 48000;
vgmstream->coding_type = coding_HEVAG;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = interleave;
@ -410,6 +460,22 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
return vgmstream;
fail:
close_vgmstream(vgmstream);
#endif
return NULL;
}
#if 0
/* .BNK - Sony's bank, earlier version [Jak and Daxter (PS2), NCAA Gamebreaker 2001 (PS2)] */
VGMSTREAM * init_vgmstream_bnk_sony_v2(STREAMFILE *streamFile) {
/* 0x00: 0x00000001
* 0x04: sections (2 or 3)
* 0x08+ similar to v3 but "SBv2"
* table formats is a bit different
* header is like v3 but stream size is in other table?
*/
fail:
close_vgmstream(vgmstream);
return NULL;
}
#endif

View File

@ -40,11 +40,12 @@ VGMSTREAM * init_vgmstream_ea_1snh(STREAMFILE *streamFile) {
/* checks */
/* .asf/as4: common
/* .asf/as4: common,
* .lasf: fake for plugins
* .cnk: some PS games
* .sng: fake for plugins (to mimic EA SCHl's common extension)
* .uv/tgq: some SAT games (video only?) */
if (!check_extensions(streamFile,"asf,as4,cnk,sng,uv,tgq"))
if (!check_extensions(streamFile,"asf,lasf,as4,cnk,sng,uv,tgq"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x31534E68 && /* "1SNh" */

View File

@ -121,7 +121,7 @@ VGMSTREAM * init_vgmstream_ea_snu(STREAMFILE *streamFile) {
}
header_offset = 0x10; /* SNR header */
start_offset = read_32bit(0x08,streamFile); /* SPS blocks */
start_offset = read_32bit(0x08,streamFile); /* SNS blocks */
vgmstream = init_vgmstream_eaaudiocore_header(streamFile, streamFile, header_offset, start_offset, meta_EA_SNU);
if (!vgmstream) goto fail;
@ -134,7 +134,7 @@ fail:
}
/* EA ABK - ABK header seems to be same as in the old games but the sound table is different and it contains SNR/SNS sounds instead */
VGMSTREAM * init_vgmstream_ea_abk_new(STREAMFILE *streamFile) {
VGMSTREAM * init_vgmstream_ea_abk_eaac(STREAMFILE *streamFile) {
int is_dupe, total_sounds = 0, target_stream = streamFile->stream_index;
off_t bnk_offset, header_table_offset, base_offset, unk_struct_offset, table_offset, snd_entry_offset, ast_offset;
off_t num_entries_off, base_offset_off, entries_off, sound_table_offset_off;
@ -143,8 +143,8 @@ VGMSTREAM * init_vgmstream_ea_abk_new(STREAMFILE *streamFile) {
uint8_t num_entries, extra_entries;
off_t sound_table_offsets[0x2000];
VGMSTREAM *vgmstream;
int32_t (*read_32bit)(off_t,STREAMFILE*);
int16_t (*read_16bit)(off_t,STREAMFILE*);
int32_t(*read_32bit)(off_t, STREAMFILE*);
int16_t(*read_16bit)(off_t, STREAMFILE*);
/* check extension */
if (!check_extensions(streamFile, "abk"))
@ -154,7 +154,7 @@ VGMSTREAM * init_vgmstream_ea_abk_new(STREAMFILE *streamFile) {
goto fail;
/* use table offset to check endianness */
if (guess_endianness32bit(0x1C,streamFile)) {
if (guess_endianness32bit(0x1C, streamFile)) {
read_32bit = read_32bitBE;
read_16bit = read_16bitBE;
} else {
@ -183,15 +183,13 @@ VGMSTREAM * init_vgmstream_ea_abk_new(STREAMFILE *streamFile) {
base_offset_off = 0x2C;
entries_off = 0x3C;
sound_table_offset_off = 0x04;
}
else if (header_table_offset == 0x78) {
} else if (header_table_offset == 0x78) {
/* FIFA 08 has a bunch of extra zeroes all over the place, don't know what's up with that */
num_entries_off = 0x40;
base_offset_off = 0x54;
entries_off = 0x68;
sound_table_offset_off = 0x0C;
}
else {
} else {
goto fail;
}
@ -207,10 +205,8 @@ VGMSTREAM * init_vgmstream_ea_abk_new(STREAMFILE *streamFile) {
/* For some reason, there are duplicate entries pointing at the same sound tables */
is_dupe = 0;
for (k = 0; k < total_sound_tables; k++)
{
if (table_offset==sound_table_offsets[k])
{
for (k = 0; k < total_sound_tables; k++) {
if (table_offset == sound_table_offsets[k]) {
is_dupe = 1;
break;
}
@ -248,7 +244,7 @@ VGMSTREAM * init_vgmstream_ea_abk_new(STREAMFILE *streamFile) {
if (bnk_target_index == 0xFFFF || ast_offset == 0)
goto fail;
vgmstream = parse_s10a_header(streamFile, bnk_offset, bnk_target_index, ast_offset);
if (!vgmstream)
goto fail;
@ -288,8 +284,7 @@ static VGMSTREAM * parse_s10a_header(STREAMFILE *streamFile, off_t offset, uint1
vgmstream = init_vgmstream_eaaudiocore_header(streamFile, streamFile, snr_offset, sns_offset, meta_EA_SNR_SNS);
if (!vgmstream)
goto fail;
}
else {
} else {
/* streamed asset */
astFile = open_streamfile_by_ext(streamFile, "ast");
if (!astFile)
@ -314,7 +309,90 @@ fail:
return NULL;
}
/* EA HDR/STH/DAT - seen in early 7th-gen games, used for storing speech */
/* EA SBR/SBS - used in older 7th gen games for storing SFX */
VGMSTREAM * init_vgmstream_ea_sbr(STREAMFILE *streamFile) {
uint32_t num_sounds, type_desc;
uint16_t num_metas, meta_type;
uint32_t i;
off_t table_offset, types_offset, entry_offset, metas_offset, data_offset, snr_offset, sns_offset;
STREAMFILE *sbsFile = NULL, *streamData = NULL;
VGMSTREAM *vgmstream = NULL;
int target_stream = streamFile->stream_index;
if (!check_extensions(streamFile, "sbr"))
goto fail;
if (read_32bitBE(0x00, streamFile) != 0x53424B52) /* "SBKR" */
goto fail;
/* SBR files are always big endian */
num_sounds = read_32bitBE(0x1c, streamFile);
table_offset = read_32bitBE(0x24, streamFile);
types_offset = read_32bitBE(0x28, streamFile);
if (target_stream == 0) target_stream = 1;
if (target_stream < 0 || num_sounds == 0 || target_stream > num_sounds)
goto fail;
entry_offset = table_offset + 0x0a * (target_stream - 1);
num_metas = read_16bitBE(entry_offset + 0x04, streamFile);
metas_offset = read_32bitBE(entry_offset + 0x06, streamFile);
snr_offset = 0xFFFFFFFF;
sns_offset = 0xFFFFFFFF;
for (i = 0; i < num_metas; i++) {
entry_offset = metas_offset + 0x06 * i;
meta_type = read_16bitBE(entry_offset, streamFile);
data_offset = read_32bitBE(entry_offset + 0x02, streamFile);
type_desc = read_32bitBE(types_offset + 0x06 * meta_type, streamFile);
switch (type_desc) {
case 0x534E5231: /* "SNR1" */
snr_offset = data_offset;
break;
case 0x534E5331: /* "SNS1" */
sns_offset = read_32bitBE(data_offset, streamFile);
break;
default:
break;
}
}
if (snr_offset == 0xFFFFFFFF)
goto fail;
if (sns_offset == 0xFFFFFFFF) {
/* RAM asset */
streamData = streamFile;
sns_offset = snr_offset + get_snr_size(streamFile, snr_offset);
} else {
/* streamed asset */
sbsFile = open_streamfile_by_ext(streamFile, "sbs");
if (!sbsFile)
goto fail;
if (read_32bitBE(0x00, sbsFile) != 0x53424B53) /* "SBKS" */
goto fail;
streamData = sbsFile;
}
vgmstream = init_vgmstream_eaaudiocore_header(streamFile, streamData, snr_offset, sns_offset, meta_EA_SNR_SNS);
if (!vgmstream)
goto fail;
vgmstream->num_streams = num_sounds;
close_streamfile(sbsFile);
return vgmstream;
fail:
close_streamfile(sbsFile);
return NULL;
}
/* EA HDR/STH/DAT - seen in older 7th gen games, used for storing speech */
VGMSTREAM * init_vgmstream_ea_hdr_sth_dat(STREAMFILE *streamFile) {
int target_stream = streamFile->stream_index;
uint32_t i;
@ -372,7 +450,7 @@ VGMSTREAM * init_vgmstream_ea_hdr_sth_dat(STREAMFILE *streamFile) {
sns_offset = 0;
for (i = 0; i < total_sounds; i++) {
snr_offset = (off_t)read_16bitBE(0x10 + (0x02+userdata_size) * i, streamFile) + 0x04;
snr_offset = (uint16_t)read_16bitBE(0x10 + (0x02+userdata_size) * i, streamFile) + 0x04;
if (i == target_stream - 1)
break;
@ -416,7 +494,7 @@ fail:
}
/* EA MPF/MUS combo - used in older 7th gen games for storing music */
VGMSTREAM * init_vgmstream_ea_mpf_mus_new(STREAMFILE *streamFile) {
VGMSTREAM * init_vgmstream_ea_mpf_mus_eaac(STREAMFILE *streamFile) {
uint32_t num_sounds;
uint8_t version, sub_version, block_id;
off_t table_offset, entry_offset, snr_offset, sns_offset;
@ -491,6 +569,179 @@ fail:
return NULL;
}
/* EA Harmony Sample Bank - used in 8th gen EA Sports games */
VGMSTREAM * init_vgmstream_ea_sbr_harmony(STREAMFILE *streamFile) {
uint32_t num_dsets, set_sounds, chunk_id;
uint32_t i;
uint8_t set_type, flag, offset_size;
off_t data_offset, table_offset, dset_offset, base_offset, sound_table_offset, sound_offset, header_offset, start_offset;
STREAMFILE *sbsFile = NULL, *streamData = NULL;
VGMSTREAM *vgmstream = NULL;
int target_stream = streamFile->stream_index, total_sounds, local_target, is_streamed = 0;
int32_t(*read_32bit)(off_t, STREAMFILE*);
int16_t(*read_16bit)(off_t, STREAMFILE*);
if (!check_extensions(streamFile, "sbr"))
goto fail;
if (read_32bitBE(0x00, streamFile) == 0x53426C65) { /* "SBle" */
read_32bit = read_32bitLE;
read_16bit = read_16bitLE;
/* Logically, big endian version starts with SBbe. However, this format is
* only used on 8th gen systems so far so big endian version probably doesn't exist. */
#if 0
} else if (read_32bitBE(0x00, streamFile) == 0x53426265) { /* "SBbe" */
read_32bit = read_32bitBE;
read_16bit = read_16bitBE;
#endif
} else {
goto fail;
}
num_dsets = read_16bit(0x0a, streamFile);
data_offset = read_32bit(0x20, streamFile);
table_offset = read_32bit(0x24, streamFile);
if (target_stream == 0) target_stream = 1;
if (target_stream < 0)
goto fail;
total_sounds = 0;
sound_offset = 0xFFFFFFFF;
/* The bank is split into DSET sections each of which references one or multiple sounds. */
/* Each set can contain RAM sounds (stored in SBR in data section) or streamed sounds (stored separately in SBS file). */
for (i = 0; i < num_dsets; i++) {
dset_offset = read_32bit(table_offset + 0x08 * i, streamFile);
if (read_32bit(dset_offset, streamFile) != 0x44534554) /* "DSET" */
goto fail;
set_sounds = read_32bit(dset_offset + 0x38, streamFile);
local_target = target_stream - total_sounds - 1;
dset_offset += 0x48;
/* Find RAM or OFF chunk */
while(1) {
chunk_id = read_32bit(dset_offset, streamFile);
if (chunk_id == 0x2E52414D) { /* ".RAM" */
break;
} else if (chunk_id == 0x2E4F4646) { /* ".OFF" */
break;
} else if (chunk_id == 0x2E4C4452 || /* ".LDR" */
chunk_id == 0x2E4F424A || /* ".OBJ" */
(chunk_id & 0xFF00FFFF) == 0x2E00534C) { /* ".?SL */
dset_offset += 0x18;
} else {
goto fail;
}
}
/* Different set types store offsets differently */
set_type = read_8bit(dset_offset + 0x05, streamFile);
if (set_type == 0x00) {
total_sounds++;
if (local_target < 0 || local_target > 0)
continue;
sound_offset = read_32bit(dset_offset + 0x08, streamFile);
} else if (set_type == 0x01) {
total_sounds += 2;
if (local_target < 0 || local_target > 1)
continue;
base_offset = read_32bit(dset_offset + 0x08, streamFile);
if (local_target == 0) {
sound_offset = base_offset;
} else {
sound_offset = base_offset + (uint16_t)read_16bit(dset_offset + 0x06, streamFile);
}
} else if (set_type == 0x02 || set_type == 0x03) {
flag = read_8bit(dset_offset + 0x06, streamFile);
offset_size = read_8bit(dset_offset + 0x07, streamFile);
base_offset = read_32bit(dset_offset + 0x08, streamFile);
sound_table_offset = read_32bit(dset_offset + 0x10, streamFile);
if (offset_size == 0x04 && flag != 0x00) {
set_sounds = base_offset;
}
total_sounds += set_sounds;
if (local_target < 0 || local_target >= set_sounds)
continue;
if (offset_size == 0x02) {
sound_offset = (uint16_t)read_16bit(sound_table_offset + 0x02 * local_target, streamFile);
if (flag != 0x00) sound_offset *= (off_t)pow(2, flag);
sound_offset += base_offset;
} else if (offset_size == 0x04) {
sound_offset = read_32bit(sound_table_offset + 0x04 * local_target, streamFile);
if (flag == 0x00) sound_offset += base_offset;
}
} else if (set_type == 0x04) {
total_sounds += set_sounds;
if (local_target < 0 || local_target >= set_sounds)
continue;
sound_table_offset = read_32bit(dset_offset + 0x10, streamFile);
sound_offset = read_32bit(sound_table_offset + 0x08 * local_target, streamFile);
} else {
goto fail;
}
if (chunk_id == 0x2E52414D) { /* ".RAM" */
is_streamed = 0;
} else if (chunk_id == 0x2E4F4646) { /* ".OFF" */
is_streamed = 1;
}
}
if (sound_offset == 0xFFFFFFFF)
goto fail;
if (!is_streamed) {
/* RAM asset */
if (read_32bitBE(data_offset, streamFile) != 0x64617461) /* "data" */
goto fail;
streamData = streamFile;
sound_offset += data_offset;
} else {
/* streamed asset */
sbsFile = open_streamfile_by_ext(streamFile, "sbs");
if (!sbsFile)
goto fail;
if (read_32bitBE(0x00, sbsFile) != 0x64617461) /* "data" */
goto fail;
streamData = sbsFile;
if (read_32bitBE(sound_offset, streamData) == 0x736C6F74) {
/* skip "slot" section */
sound_offset += 0x30;
}
}
if (read_8bit(sound_offset, streamData) != EAAC_BLOCKID1_HEADER)
goto fail;
header_offset = sound_offset + 0x04;
start_offset = sound_offset + (read_32bitBE(sound_offset, streamData) & 0x00FFFFFF);
vgmstream = init_vgmstream_eaaudiocore_header(streamData, streamData, header_offset, start_offset, meta_EA_SNR_SNS);
if (!vgmstream)
goto fail;
vgmstream->num_streams = total_sounds;
close_streamfile(sbsFile);
return vgmstream;
fail:
close_streamfile(sbsFile);
return NULL;
}
/* ************************************************************************* */
typedef struct {
@ -562,11 +813,28 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST
eaac.loop_start = read_32bitBE(header_offset+0x08, streamHead);
eaac.loop_end = eaac.num_samples;
/* RAM assets only have one block, even if they (rarely) set loop_start > 0 */
if (eaac.streamed)
if (eaac.streamed) {
eaac.loop_offset = read_32bitBE(header_offset+0x0c, streamHead);
else
if (eaac.version == EAAC_VERSION_V0) {
/* SNR+SNS are separate so offsets are relative to the data start
* (first .SNS block, or extra data before the .SNS block in case of .SNU) */
eaac.loop_offset = eaac.stream_offset + eaac.loop_offset;
} else {
/* SPS have headers+data together so offsets are relative to the file start [ex. FIFA 18 (PC)] */
eaac.loop_offset = header_offset - 0x04 + eaac.loop_offset;
}
}
else if (eaac.loop_start > 0) {
/* RAM assets have two blocks in case of actual loops */
/* find the second block by getting the first block size */
eaac.loop_offset = read_32bitBE(eaac.stream_offset, streamData) & 0x00FFFFF;
eaac.loop_offset = eaac.stream_offset + eaac.loop_offset;
}
else {
/* RAM assets only one block in case in case of full loops */
eaac.loop_offset = eaac.stream_offset; /* implicit */
}
//todo EATrax has extra values in header, which would coexist with loop values
if (eaac.codec == EAAC_CODEC_EATRAX) {
@ -621,7 +889,7 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST
case EAAC_CODEC_EAXMA: { /* "EXm0": EA-XMA [Dante's Inferno (X360)] */
/* special (if hacky) loop handling, see comments */
if (eaac.streamed && eaac.loop_start > 0) {
if (eaac.loop_start > 0) {
segmented_layout_data *data = build_segmented_eaaudiocore_looping(streamData, &eaac);
if (!data) goto fail;
vgmstream->layout_data = data;
@ -640,7 +908,7 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST
#endif
case EAAC_CODEC_XAS: /* "Xas1": EA-XAS [Dead Space (PC/PS3)] */
vgmstream->coding_type = coding_EA_XAS;
vgmstream->coding_type = coding_EA_XAS_V1;
vgmstream->layout_type = layout_blocked_ea_sns;
break;
@ -658,7 +926,7 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST
start_offset = 0x00; /* must point to the custom streamfile's beginning */
/* special (if hacky) loop handling, see comments */
if (eaac.streamed && eaac.loop_start > 0) {
if (eaac.loop_start > 0) {
segmented_layout_data *data = build_segmented_eaaudiocore_looping(streamData, &eaac);
if (!data) goto fail;
vgmstream->layout_data = data;
@ -785,7 +1053,7 @@ static size_t get_snr_size(STREAMFILE *streamFile, off_t offset) {
static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *streamData, eaac_header *eaac) {
segmented_layout_data *data = NULL;
STREAMFILE* temp_streamFile[2] = {0};
off_t offsets[2] = { eaac->stream_offset, eaac->stream_offset + eaac->loop_offset };
off_t offsets[2] = { eaac->stream_offset, eaac->loop_offset };
int num_samples[2] = { eaac->loop_start, eaac->num_samples - eaac->loop_start};
int segment_count = 2; /* intro/loop */
int i;

View File

@ -115,11 +115,12 @@ VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile) {
/* check extension */
/* they don't seem enforced by EA's tools but usually:
* .asf: ~early [ex. Need for Speed (PC)]
* .asf: ~early (audio stream file?) [ex. Need for Speed (PC)]
* .lasf: fake for plugins
* .str: ~early [ex. FIFA 2002 (PS1)]
* .eam: ~mid (fake?)
* .exa: ~mid [ex. 007 - From Russia with Love]
* .sng: ~late (fake?)
* .sng: ~late (FIFA games)
* .aud: ~late [ex. FIFA 14 (3DS)]
* .strm: MySims Kingdom (Wii)
* .stm: FIFA 12 (3DS)
@ -129,7 +130,7 @@ VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile) {
* .gsf: 007 - Everything or Nothing (GC)
* .mus: map/mpf+mus only?
* (extensionless): SSX (PS2) (inside .big) */
if (!check_extensions(streamFile,"asf,str,eam,exa,sng,aud,sx,xa,strm,stm,hab,xsf,gsf,mus,"))
if (!check_extensions(streamFile,"asf,lasf,str,eam,exa,sng,aud,sx,xa,strm,stm,hab,xsf,gsf,mus,"))
goto fail;
/* check header */
@ -187,15 +188,16 @@ fail:
/* streamed assets are stored externally in AST file (mostly seen in earlier 6th-gen games) */
VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE *streamFile) {
int bnk_target_stream, is_dupe, total_sounds = 0, target_stream = streamFile->stream_index;
off_t bnk_offset, header_table_offset, base_offset, value_offset, table_offset, entry_offset, target_entry_offset, schl_offset;
off_t bnk_offset, header_table_offset, base_offset, value_offset, table_offset, entry_offset, target_entry_offset, schl_offset, schl_loop_offset;
uint32_t i, j, k, num_sounds, total_sound_tables;
uint16_t num_tables;
uint8_t sound_type, num_entries;
off_t sound_table_offsets[0x2000];
STREAMFILE * astData = NULL;
VGMSTREAM * vgmstream;
int32_t (*read_32bit)(off_t,STREAMFILE*);
int16_t (*read_16bit)(off_t,STREAMFILE*);
VGMSTREAM * vgmstream = NULL;
segmented_layout_data *data_s = NULL;
int32_t(*read_32bit)(off_t, STREAMFILE*);
int16_t(*read_16bit)(off_t, STREAMFILE*);
/* check extension */
if (!check_extensions(streamFile, "abk"))
@ -205,7 +207,7 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE *streamFile) {
goto fail;
/* use table offset to check endianness */
if (guess_endianness32bit(0x1C,streamFile)) {
if (guess_endianness32bit(0x1C, streamFile)) {
read_32bit = read_32bitBE;
read_16bit = read_16bitBE;
} else {
@ -240,10 +242,8 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE *streamFile) {
/* For some reason, there are duplicate entries pointing at the same sound tables */
is_dupe = 0;
for (k = 0; k < total_sound_tables; k++)
{
if (table_offset==sound_table_offsets[k])
{
for (k = 0; k < total_sound_tables; k++) {
if (table_offset == sound_table_offsets[k]) {
is_dupe = 1;
break;
}
@ -278,45 +278,85 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE *streamFile) {
if (target_entry_offset == 0)
goto fail;
/* 0x00: type (0x00 - normal, 0x01 - streamed, 0x02 - streamed and prefetched(?) */
/* 0x00: type (0x00 - normal, 0x01 - streamed, 0x02 - streamed looped */
/* 0x01: ??? */
/* 0x04: index for normal sounds, offset for streamed sounds */
/* 0x08: offset for prefetched sounds */
/* 0x08: loop offset for streamed sounds */
sound_type = read_8bit(target_entry_offset + 0x00, streamFile);
switch (sound_type) {
case 0x00:
if (!bnk_offset)
goto fail;
case 0x00:
if (!bnk_offset)
goto fail;
bnk_target_stream = read_32bit(target_entry_offset + 0x04, streamFile) + 1;
vgmstream = parse_bnk_header(streamFile, bnk_offset, bnk_target_stream, 1);
if (!vgmstream)
goto fail;
break;
bnk_target_stream = read_32bit(target_entry_offset + 0x04, streamFile) + 1;
vgmstream = parse_bnk_header(streamFile, bnk_offset, bnk_target_stream, 1);
if (!vgmstream)
goto fail;
case 0x01:
case 0x02:
astData = open_streamfile_by_ext(streamFile, "ast");
if (!astData)
goto fail;
break;
case 0x01:
astData = open_streamfile_by_ext(streamFile, "ast");
if (!astData)
goto fail;
if (sound_type == 0x01)
schl_offset = read_32bit(target_entry_offset + 0x04, streamFile);
else
schl_offset = read_32bit(target_entry_offset + 0x08, streamFile);
if (read_32bitBE(schl_offset, astData) != EA_BLOCKID_HEADER)
goto fail;
if (read_32bitBE(schl_offset, astData) != EA_BLOCKID_HEADER)
vgmstream = parse_schl_block(astData, schl_offset, 0);
if (!vgmstream)
goto fail;
break;
case 0x02:
astData = open_streamfile_by_ext(streamFile, "ast");
if (!astData)
goto fail;
/* looped sounds basically consist of two independent segments
* the first one is loop start, the second one is loop body */
schl_offset = read_32bit(target_entry_offset + 0x04, streamFile);
schl_loop_offset = read_32bit(target_entry_offset + 0x08, streamFile);
if (read_32bitBE(schl_offset, astData) != EA_BLOCKID_HEADER ||
read_32bitBE(schl_loop_offset, astData) != EA_BLOCKID_HEADER)
goto fail;
/* init layout */
data_s = init_layout_segmented(2);
if (!data_s) goto fail;
/* load intro and loop segments */
data_s->segments[0] = parse_schl_block(astData, schl_offset, 0);
if (!data_s->segments[0]) goto fail;
data_s->segments[1] = parse_schl_block(astData, schl_loop_offset, 0);
if (!data_s->segments[1]) goto fail;
/* setup segmented VGMSTREAMs */
if (!setup_layout_segmented(data_s))
goto fail;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(data_s->segments[0]->channels, 1);
if (!vgmstream) goto fail;
vgmstream->sample_rate = data_s->segments[0]->sample_rate;
vgmstream->num_samples = data_s->segments[0]->num_samples + data_s->segments[1]->num_samples;
vgmstream->loop_start_sample = data_s->segments[0]->num_samples;
vgmstream->loop_end_sample = vgmstream->num_samples;
vgmstream->meta_type = meta_EA_SCHL;
vgmstream->coding_type = data_s->segments[0]->coding_type;
vgmstream->layout_type = layout_segmented;
vgmstream->layout_data = data_s;
break;
default:
goto fail;
vgmstream = parse_schl_block(astData, schl_offset, 0);
if (!vgmstream)
goto fail;
break;
default:
goto fail;
break;
break;
}
vgmstream->num_streams = total_sounds;
@ -325,6 +365,7 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE *streamFile) {
fail:
close_streamfile(astData);
free_layout_segmented(data_s);
return NULL;
}
@ -344,7 +385,7 @@ VGMSTREAM * init_vgmstream_ea_hdr_dat(STREAMFILE *streamFile) {
/* 0x05: number of files */
/* 0x06: ??? */
/* 0x07: offset multiplier flag */
/* 0x08: combined size of all sounds without padding divided by 0x0100 */
/* 0x08: combined size of all sounds without padding divided by offset mult */
/* 0x0C: table start */
/* no nice way to validate these so we do what we can */
@ -358,14 +399,14 @@ VGMSTREAM * init_vgmstream_ea_hdr_dat(STREAMFILE *streamFile) {
userdata_size = read_8bit(0x04, streamFile) & 0x0F;
total_sounds = read_8bit(0x05, streamFile);
offset_mult = (off_t)read_8bit(0x07, streamFile) * 0x0100 + 0x0100;
offset_mult = (uint8_t)read_8bit(0x07, streamFile) * 0x0100 + 0x0100;
if (target_stream == 0) target_stream = 1;
if (target_stream < 0 || total_sounds == 0 || target_stream > total_sounds)
goto fail;
/* offsets are always big endian */
schl_offset = (off_t)read_16bitBE(0x0C + (0x02+userdata_size) * (target_stream-1), streamFile) * offset_mult;
schl_offset = (uint16_t)read_16bitBE(0x0C + (0x02+userdata_size) * (target_stream-1), streamFile) * offset_mult;
if (read_32bitBE(schl_offset, datFile) != EA_BLOCKID_HEADER)
goto fail;
@ -460,7 +501,7 @@ VGMSTREAM * init_vgmstream_ea_map_mus(STREAMFILE *streamFile) {
}
/*
* 0x04: ???
* 0x04: version
* 0x05: intro segment
* 0x06: number of segments
* 0x07: userdata entry size (incorrect?)
@ -548,14 +589,14 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE *streamFile) {
if (version == 3 && sub_version == 1) { /* SSX Tricky */
/* we need to go through the first two sections to find sound table */
sec1_num = read_16bit(0x12, streamFile);
sec2_size = read_8bit(0x0e, streamFile);
sec2_size = read_8bit(0x0d, streamFile) * read_8bit(0x0e, streamFile);
sec2_num = read_8bit(0x0f, streamFile);
sec3_num = read_8bit(0x10, streamFile);
sec4_num = read_8bit(0x11, streamFile);
/* get the last entry offset */
section_offset = 0x24;
entry_offset = read_16bit(section_offset + (sec1_num - 1) * 0x02, streamFile) * 0x04;
entry_offset = (uint16_t)read_16bit(section_offset + (sec1_num - 1) * 0x02, streamFile) * 0x04;
subentry_num = read_8bit(entry_offset + 0x0b, streamFile);
section_offset = entry_offset + 0x0c + subentry_num * 0x04;
@ -570,14 +611,14 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE *streamFile) {
off_mult = 0x04;
} else if (version == 3 && sub_version == 4) { /* Harry Potter and the Chamber of Secrets */
sec1_num = read_16bit(0x12, streamFile);
sec2_size = read_8bit(0x0e, streamFile);
sec2_size = read_8bit(0x0d, streamFile) * read_8bit(0x0e, streamFile);
sec2_num = read_8bit(0x0f, streamFile);
sec3_num = read_8bit(0x10, streamFile);
sec4_num = read_8bit(0x11, streamFile);
/* get the last entry offset */
section_offset = 0x24;
entry_offset = read_16bit(section_offset + (sec1_num - 1) * 0x02, streamFile) * 0x04;
entry_offset = (uint16_t)read_16bit(section_offset + (sec1_num - 1) * 0x02, streamFile) * 0x04;
if (big_endian) {
subentry_num = (read_32bitBE(entry_offset + 0x04, streamFile) >> 19) & 0xFF;
} else {
@ -601,7 +642,7 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE *streamFile) {
/* get the last entry offset */
section_offset = 0x20;
entry_offset = read_16bit(section_offset + (sec1_num - 1) * 0x02, streamFile) * 0x04;
entry_offset = (uint16_t)read_16bit(section_offset + (sec1_num - 1) * 0x02, streamFile) * 0x04;
if (big_endian) {
subentry_num = (read_32bitBE(entry_offset + 0x04, streamFile) >> 15) & 0xFF;
} else {
@ -609,7 +650,7 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE *streamFile) {
}
section_offset = entry_offset + 0x10 + subentry_num * 0x04;
entry_offset = read_16bit(section_offset + (sec2_num - 1) * 0x02, streamFile) * 0x04;
entry_offset = (uint16_t)read_16bit(section_offset + (sec2_num - 1) * 0x02, streamFile) * 0x04;
if (big_endian) {
subentry_num = (read_32bitBE(entry_offset + 0x0c, streamFile) >> 10) & 0xFF;
} else {
@ -1423,15 +1464,15 @@ static void update_ea_stream_size_and_samples(STREAMFILE* streamFile, off_t star
multiple_schl = 1;
}
/* HACK: fix num_samples for streams with multiple SCHl. Need to eventually get rid of this.
* Get total samples by parsing block headers, needed when multiple files are stitched together.
* Some EA files (.mus/eam/sng/etc) concat many small subfiles, used for interactive/mapped
* music (.map/lin). Subfiles always share header, except num_samples. */
num_samples += vgmstream->current_block_samples;
if (vgmstream->current_block_samples > 0) {
/* HACK: fix num_samples for streams with multiple SCHl. Need to eventually get rid of this.
* Get total samples by parsing block headers, needed when multiple files are stitched together.
* Some EA files (.mus/eam/sng/etc) concat many small subfiles, used for interactive/mapped
* music (.map/lin). Subfiles always share header, except num_samples. */
num_samples += vgmstream->current_block_samples;
/* Stream size is almost never provided in bank files so we have to calc it manually */
if (vgmstream->current_block_samples != 0) {
stream_size += vgmstream->next_block_offset - vgmstream->current_block_offset - 0x0c;
/* Stream size is almost never provided in bank files so we have to calc it manually */
stream_size += vgmstream->next_block_offset - vgmstream->ch[0].offset;
}
}

View File

@ -29,8 +29,10 @@ VGMSTREAM * init_vgmstream_ea_schl_fixed(STREAMFILE *streamFile) {
ea_header ea = {0};
/* check extension */
if (!check_extensions(streamFile,"asf"))
/* checks */
/* .asf: original
* .lasf: fake for plugins */
if (!check_extensions(streamFile,"asf,lasf"))
goto fail;
/* check header (see ea_schl.c for more info about blocks) */

View File

@ -32,7 +32,7 @@ VGMSTREAM * init_vgmstream_fag(STREAMFILE *streamFile) {
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_FAG;
vgmstream->sample_rate = 24000;
vgmstream->sample_rate = 22050;
vgmstream->num_streams = total_subsongs;
vgmstream->stream_size = stream_size;

View File

@ -6,38 +6,42 @@
/* known GENH types */
typedef enum {
PSX = 0, /* PSX ADPCM */
XBOX = 1, /* XBOX IMA ADPCM */
NGC_DTK = 2, /* NGC ADP/DTK ADPCM */
PCM16BE = 3, /* 16bit big endian PCM */
PCM16LE = 4, /* 16bit little endian PCM */
PCM8 = 5, /* 8bit PCM */
SDX2 = 6, /* SDX2 (3D0 games) */
DVI_IMA = 7, /* DVI IMA ADPCM */
MPEG = 8, /* MPEG (MP3) */
IMA = 9, /* IMA ADPCM */
AICA = 10, /* AICA ADPCM (dreamcast) */
MSADPCM = 11, /* MS ADPCM (windows) */
NGC_DSP = 12, /* NGC DSP (GC) */
PCM8_U_int = 13, /* 8bit unsigned PCM (interleaved) */
PSX_bf = 14, /* PSX ADPCM bad flagged */
MS_IMA = 15, /* Microsoft IMA ADPCM */
PCM8_U = 16, /* 8bit unsigned PCM */
APPLE_IMA4 = 17, /* Apple Quicktime 4-bit IMA ADPCM */
ATRAC3 = 18, /* raw ATRAC3 */
ATRAC3PLUS = 19, /* raw ATRAC3PLUS */
XMA1 = 20, /* raw XMA1 */
XMA2 = 21, /* raw XMA2 */
FFMPEG = 22, /* any headered FFmpeg format */
AC3 = 23, /* AC3/SPDIF */
PCFX = 24, /* PC-FX ADPCM */
PSX = 0, /* PS-ADPCM */
XBOX = 1, /* XBOX IMA ADPCM */
NGC_DTK = 2, /* NGC ADP/DTK ADPCM */
PCM16BE = 3, /* 16-bit big endian PCM */
PCM16LE = 4, /* 16-bit little endian PCM */
PCM8 = 5, /* 8-bit PCM */
SDX2 = 6, /* SDX2 (3D0 games) */
DVI_IMA = 7, /* DVI IMA ADPCM (high nibble first) */
MPEG = 8, /* MPEG (MP3) */
IMA = 9, /* IMA ADPCM (low nibble first) */
AICA = 10, /* AICA ADPCM (Dreamcast games) */
MSADPCM = 11, /* MS ADPCM (Windows games) */
NGC_DSP = 12, /* NGC DSP (Nintendo games) */
PCM8_U_int = 13, /* 8-bit unsigned PCM (interleaved) */
PSX_bf = 14, /* PS-ADPCM with bad flags */
MS_IMA = 15, /* Microsoft IMA ADPCM */
PCM8_U = 16, /* 8-bit unsigned PCM */
APPLE_IMA4 = 17, /* Apple Quicktime 4-bit IMA ADPCM */
ATRAC3 = 18, /* Raw ATRAC3 */
ATRAC3PLUS = 19, /* Raw ATRAC3PLUS */
XMA1 = 20, /* Raw XMA1 */
XMA2 = 21, /* Raw XMA2 */
FFMPEG = 22, /* Any headered FFmpeg format */
AC3 = 23, /* AC3/SPDIF */
PCFX = 24, /* PC-FX ADPCM */
PCM4 = 25, /* 4-bit signed PCM (3rd and 4th gen games) */
PCM4_U = 26, /* 4-bit unsigned PCM (3rd and 4th gen games) */
OKI16 = 27, /* OKI ADPCM with 16-bit output (unlike OKI/VOX/Dialogic ADPCM's 12-bit) */
} genh_type;
typedef struct {
genh_type codec;
int codec_mode;
size_t interleave;
size_t interleave;
size_t interleave_last;
int channels;
int32_t sample_rate;
@ -114,6 +118,9 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) {
case FFMPEG: coding = coding_FFmpeg; break;
#endif
case PCFX: coding = coding_PCFX; break;
case PCM4: coding = coding_PCM4; break;
case PCM4_U: coding = coding_PCM4_U; break;
case OKI16: coding = coding_OKI16; break;
default:
goto fail;
}
@ -137,6 +144,8 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) {
case coding_PCM16BE:
case coding_PCM8:
case coding_PCM8_U:
case coding_PCM4:
case coding_PCM4_U:
case coding_SDX2:
case coding_PSX:
case coding_PSX_badflags:
@ -145,6 +154,7 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) {
case coding_AICA:
case coding_APPLE_IMA4:
vgmstream->interleave_block_size = genh.interleave;
vgmstream->interleave_last_block_size = genh.interleave_last;
if (vgmstream->channels > 1)
{
if (coding == coding_SDX2) {
@ -185,15 +195,24 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) {
}
}
if (coding == coding_PCM4 || coding == coding_PCM4_U) {
/* high nibble or low nibble first */
vgmstream->codec_config = genh.codec_mode;
}
break;
case coding_PCFX:
vgmstream->interleave_block_size = genh.interleave;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_last_block_size = genh.interleave_last;
if (genh.codec_mode >= 0 && genh.codec_mode <= 3)
vgmstream->codec_config = genh.codec_mode;
break;
case coding_OKI16:
vgmstream->layout_type = layout_none;
break;
case coding_MS_IMA:
if (!genh.interleave) goto fail; /* creates garbage */
@ -211,11 +230,13 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) {
if (genh.codec_mode == 1) { /* mono interleave */
coding = coding_XBOX_IMA_int;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_last_block_size = genh.interleave_last;
vgmstream->interleave_block_size = genh.interleave;
}
else { /* 1ch mono, or stereo interleave */
vgmstream->layout_type = genh.interleave ? layout_interleave : layout_none;
vgmstream->interleave_block_size = genh.interleave;
vgmstream->interleave_last_block_size = genh.interleave_last;
if (vgmstream->channels > 2 && vgmstream->channels % 2 != 0)
goto fail; /* only 2ch+..+2ch layout is known */
}
@ -229,6 +250,7 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) {
if (!genh.interleave) goto fail;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = genh.interleave;
vgmstream->interleave_last_block_size = genh.interleave_last;
} else if (genh.coef_interleave_type == 1) {
if (!genh.interleave) goto fail;
coding = coding_NGC_DSP_subint;
@ -403,8 +425,8 @@ static int parse_genh(STREAMFILE * streamFile, genh_header * genh) {
genh->coef_split_spacing = read_32bitLE(0x38,streamFile);
}
/* extended fields */
if (header_size >= 0x54) {
/* extended + reserved fields */
if (header_size >= 0x100) {
genh->num_samples = read_32bitLE(0x40,streamFile);
genh->skip_samples = read_32bitLE(0x44,streamFile); /* for FFmpeg based codecs */
genh->skip_samples_mode = read_8bit(0x48,streamFile); /* 0=autodetect, 1=force manual value @ 0x44 */
@ -414,6 +436,7 @@ static int parse_genh(STREAMFILE * streamFile, genh_header * genh) {
if ((genh->codec == XMA1 || genh->codec == XMA2) && genh->codec_mode==0)
genh->codec_mode = read_8bit(0x4a,streamFile);
genh->data_size = read_32bitLE(0x50,streamFile);
genh->interleave_last = read_32bitLE(0x54,streamFile);
}
if (genh->data_size == 0)

View File

@ -0,0 +1,53 @@
#include "meta.h"
#include "../coding/coding.h"
/* .gin - EA engine sounds [Need for Speed: Most Wanted (multi)] */
VGMSTREAM * init_vgmstream_gin(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
int loop_flag, channel_count, sample_rate, num_samples;
/* checks */
if (!check_extensions(streamFile, "gin"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x476E7375) /* "Gnsu" */
goto fail;
/* contains mapped values for engine RPM sounds but we'll just play the whole thing */
/* 0x04: size? "20\00\00"? */
/* 0x08/0c: min/max float RPM? */
/* 0x10: RPM up? (pitch/frequency) table size */
/* 0x14: RPM ??? table size */
/* always LE even on X360/PS3 */
num_samples = read_32bitLE(0x18, streamFile);
sample_rate = read_32bitLE(0x1c, streamFile);
start_offset = 0x20 +
(read_32bitLE(0x10, streamFile) + 1) * 0x04 +
(read_32bitLE(0x14, streamFile) + 1) * 0x04;
channel_count = 1;
loop_flag = 0;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_GIN;
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = num_samples;
vgmstream->coding_type = coding_EA_XAS_V0;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x13;
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -472,8 +472,6 @@ VGMSTREAM * init_vgmstream_ps2_msa(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_ps2_voi(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_ps2_khv(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_ngc_rkv(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_p3d(STREAMFILE* streamFile);
@ -511,6 +509,7 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_ngc_nst_dsp(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_baf(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_baf_badrip(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE* streamFile);
@ -679,9 +678,11 @@ VGMSTREAM * init_vgmstream_vxn(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ea_snu(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ea_snr_sns(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ea_sps(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ea_abk_new(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ea_abk_eaac(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ea_hdr_sth_dat(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ea_mpf_mus_new(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ea_mpf_mus_eaac(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ea_sbr(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ea_sbr_harmony(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ngc_vid1(STREAMFILE * streamFile);
@ -823,4 +824,6 @@ VGMSTREAM * init_vgmstream_imc_container(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_smp(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_gin(STREAMFILE * streamFile);
#endif /*_META_H*/

View File

@ -174,7 +174,13 @@ VGMSTREAM * init_vgmstream_ps2_ads(STREAMFILE *streamFile) {
loop_end_offset = loop_end * 0x10;
}
#endif
if (loop_end <= body_size / 0x70 && coding_type == coding_PCM16LE) { /* close to body_size */
if (loop_end <= body_size / 0x200 && coding_type == coding_PCM16LE) { /* close to body_size */
/* Gofun-go no Sekai: loops is address * 0x200 */
loop_flag = 1;
loop_start_offset = loop_start * 0x200;
loop_end_offset = loop_end * 0x200;
}
else if (loop_end <= body_size / 0x70 && coding_type == coding_PCM16LE) { /* close to body_size */
/* Armored Core - Nexus: loops is address * 0x70 */
loop_flag = 1;
loop_start_offset = loop_start * 0x70;

View File

@ -1,65 +0,0 @@
#include "meta.h"
#include "../util.h"
/* KHV (from Kingdom Hearts 2) */
/* VAG files with custom headers */
VGMSTREAM * init_vgmstream_ps2_khv(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
int loop_flag = 0;
int channel_count;
off_t start_offset;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("khv",filename_extension(filename))) goto fail;
/* check header */
if (read_32bitBE(0x00,streamFile) != 0x56414770) /* "VAGp" */
goto fail;
loop_flag = (read_32bitBE(0x14,streamFile)!=0);
channel_count = 2;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
start_offset = 0x60;
vgmstream->channels = channel_count;
vgmstream->sample_rate = read_32bitBE(0x10,streamFile);
vgmstream->coding_type = coding_PSX;
vgmstream->num_samples = read_32bitBE(0x0C,streamFile);
if (loop_flag) {
vgmstream->loop_start_sample = read_32bitBE(0x14,streamFile);
vgmstream->loop_end_sample = read_32bitBE(0x18,streamFile);
}
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x10;
vgmstream->meta_type = meta_PS2_KHV;
/* 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;
}
}
return vgmstream;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
}

View File

@ -1,75 +1,48 @@
#include "meta.h"
#include "../util.h"
/* MIC
PS2 MIC format is an interleaved format found in most of KOEI Games
The header always start the long value 0x800 which is the start
of the BGM datas.
2008-05-15 - Fastelbja : First version ...
*/
#include "../coding/coding.h"
/* .MIC - from KOEI games [Crimson Sea 2 (PS2), Dynasty Tactics 2 (PS2)] */
VGMSTREAM * init_vgmstream_ps2_mic(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t start_offset;
int loop_flag, channel_count, loop_start, loop_end, sample_rate;
size_t interleave, block_size;
int loop_flag=0;
int channel_count;
int i;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("mic",filename_extension(filename))) goto fail;
/* check Header */
if (read_32bitLE(0x00,streamFile) != 0x800)
/* checks */
if (!check_extensions(streamFile, "mic"))
goto fail;
/* check loop */
loop_flag = (read_32bitLE(0x14,streamFile)!=1);
channel_count=read_32bitLE(0x08,streamFile);
start_offset = read_32bitLE(0x00,streamFile);
if (start_offset != 0x800) goto fail;
sample_rate = read_32bitLE(0x04,streamFile);
channel_count = read_32bitLE(0x08,streamFile);
interleave = read_32bitLE(0x0c,streamFile);
loop_end = read_32bitLE(0x10,streamFile);
loop_start = read_32bitLE(0x14,streamFile);
loop_flag = (loop_start != 1);
block_size = interleave * channel_count;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
vgmstream->channels = channel_count;
vgmstream->sample_rate = read_32bitLE(0x04,streamFile);
/* Compression Scheme */
vgmstream->coding_type = coding_PSX;
vgmstream->num_samples = read_32bitLE(0x10,streamFile)*14*channel_count;
/* Get loop point values */
if(vgmstream->loop_flag) {
vgmstream->loop_start_sample = read_32bitLE(0x14,streamFile)*14*channel_count;
vgmstream->loop_end_sample = read_32bitLE(0x10,streamFile)*14*channel_count;
}
vgmstream->interleave_block_size = read_32bitLE(0x0C,streamFile);
vgmstream->layout_type = layout_interleave;
vgmstream->meta_type = meta_PS2_MIC;
vgmstream->sample_rate = sample_rate;
/* open the file for reading by each channel */
{
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
vgmstream->coding_type = coding_PSX;
vgmstream->interleave_block_size = interleave;
vgmstream->layout_type = layout_interleave;
if (!vgmstream->ch[i].streamfile) goto fail;
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=
(off_t)(0x800+vgmstream->interleave_block_size*i);
}
}
vgmstream->num_samples = ps_bytes_to_samples(loop_end * block_size, channel_count);
vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start * block_size, channel_count);
vgmstream->loop_end_sample = vgmstream->num_samples;
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

@ -10,6 +10,7 @@
static VGMSTREAM *parse_riff_ogg(STREAMFILE * streamFile, off_t start_offset, size_t data_size);
#endif
/* return milliseconds */
static long parse_adtl_marker(unsigned char * marker) {
long hh,mm,ss,ms;
@ -236,6 +237,9 @@ fail:
return -1;
}
static int is_ue4_msadpcm(VGMSTREAM* vgmstream, STREAMFILE* streamFile, riff_fmt_chunk* fmt, int fact_sample_count, off_t start_offset);
VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
riff_fmt_chunk fmt = {0};
@ -682,17 +686,74 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
vgmstream->meta_type = meta_RIFF_WAVE_MWV;
}
if ( !vgmstream_open_stream(vgmstream,streamFile,start_offset) )
goto fail;
return vgmstream;
/* UE4 uses half-interleave mono MSADPCM, try to autodetect without breaking normal MSADPCM */
if (fmt.coding_type == coding_MSADPCM && is_ue4_msadpcm(vgmstream, streamFile, &fmt, fact_sample_count, start_offset)) {
int ch;
size_t half_interleave = data_size / vgmstream->channels;
vgmstream->coding_type = coding_MSADPCM_int;
/* only works with half-interleave as frame_size and interleave are merged ATM*/
for (ch = 0; ch < vgmstream->channels; ch++) {
vgmstream->ch[ch].channel_start_offset =
vgmstream->ch[ch].offset = start_offset + half_interleave*ch;
}
}
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}
/* UE4 MSADPCM is quite normal but has a few minor quirks we can use to detect it */
static int is_ue4_msadpcm(VGMSTREAM* vgmstream, STREAMFILE* streamFile, riff_fmt_chunk* fmt, int fact_sample_count, off_t start_offset) {
/* stereo only */
if (fmt->channel_count != 2)
goto fail;
/* UE4 class is "ADPCM", assume it's the extension too */
if (!check_extensions(streamFile, "adpcm"))
goto fail;
/* UE4 encoder doesn't add "fact" */
if (fact_sample_count != 0)
goto fail;
/* fixed block size */
if (fmt->block_size != 0x200)
goto fail;
/* later UE4 versions use 0x36 (at 0x32 may be fact_samples?) */
if (fmt->size != 0x32 && fmt->size != 0x36)
goto fail;
/* size 0x32 in older UE4 matches standard MSADPCM, so add extra detection */
if (fmt->size == 0x32) {
off_t offset = start_offset;
off_t max_offset = 5 * fmt->block_size; /* try N blocks */
if (max_offset > get_streamfile_size(streamFile))
max_offset = get_streamfile_size(streamFile);
/* their encoder doesn't calculate optimal coefs and uses fixed values every frame
* (could do it for fmt size 0x36 too but maybe they'll fix it in the future) */
while (offset <= max_offset) {
if (read_8bit(offset+0x00, streamFile) != 0 || read_16bitLE(offset+0x01, streamFile) != 0x00E6)
goto fail;
offset += fmt->block_size;
}
}
return 1;
fail:
return 0;
}
VGMSTREAM * init_vgmstream_rifx(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
riff_fmt_chunk fmt = {0};

View File

@ -1,81 +1,61 @@
#include "meta.h"
#include "../util.h"
/* SD9 (found in beatmaniaIIDX16 - EMPRESS (Arcade) */
/* SD9 (found in beatmania IIDX Arcade games) */
VGMSTREAM * init_vgmstream_sd9(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t start_offset;
int loop_flag, channel_count;
int loop_flag;
int channel_count;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("sd9",filename_extension(filename))) goto fail;
/* check extension */
if (!check_extensions(streamFile, "sd9"))
goto fail;
/* check header */
if (read_32bitBE(0x0,streamFile) != 0x53443900) /* SD9 */
goto fail;
if (read_32bitBE(0x20,streamFile) != 0x52494646) /* RIFF */
goto fail;
if (read_32bitBE(0x28,streamFile) != 0x57415645) /* WAVE */
goto fail;
if (read_32bitBE(0x2c,streamFile) != 0x666D7420) /* fmt */
goto fail;
if (read_32bitBE(0x72,streamFile) != 0x64617461) /* data */
goto fail;
if (read_32bitBE(0x0, streamFile) != 0x53443900) /* SD9 */
goto fail;
if (read_32bitBE(0x20, streamFile) != 0x52494646) /* RIFF */
goto fail;
if (read_32bitBE(0x28, streamFile) != 0x57415645) /* WAVE */
goto fail;
if (read_32bitBE(0x2c, streamFile) != 0x666D7420) /* fmt */
goto fail;
if (read_32bitBE(0x72, streamFile) != 0x64617461) /* data */
goto fail;
loop_flag = (read_16bitLE(0x0e,streamFile)==0x1);
channel_count = read_16bitLE(0x36,streamFile);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
/* Probably better to check if loop end exists and use as loop flag.
Blame SD9s from beatmania IIDX 21: Spada that have a flase flag
but still "loop" */
//loop_flag = (read_16bitLE(0x0e,streamFile)==0x1);
loop_flag = read_32bitLE(0x18, streamFile); // use loop end
channel_count = read_16bitLE(0x36, streamFile);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
/* fill in the vital statistics */
start_offset = 0x7a;
vgmstream->channels = channel_count;
vgmstream->sample_rate = read_32bitLE(0x38,streamFile);
vgmstream->channels = channel_count;
vgmstream->sample_rate = read_32bitLE(0x38, streamFile);
vgmstream->coding_type = coding_MSADPCM;
vgmstream->num_samples = read_32bitLE(0x6e,streamFile);
if (loop_flag) {
if (read_16bitLE(0x1C,streamFile)==1)
{
vgmstream->loop_start_sample = read_32bitLE(0x14,streamFile)/2/channel_count;
vgmstream->loop_end_sample = read_32bitLE(0x18,streamFile)/2/channel_count;
}
else
{
vgmstream->loop_start_sample = 0;
vgmstream->loop_end_sample = vgmstream->num_samples;
}
vgmstream->num_samples = read_32bitLE(0x6e, streamFile);
if (loop_flag) {
vgmstream->loop_start_sample = read_32bitLE(0x14, streamFile) / 2 / channel_count;
vgmstream->loop_end_sample = read_32bitLE(0x18, streamFile) / 2 / channel_count;
}
vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size = read_16bitLE(0x40,streamFile);
vgmstream->interleave_block_size = read_16bitLE(0x40, streamFile);
vgmstream->meta_type = meta_SD9;
/* 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;
}
}
return vgmstream;
if (!vgmstream_open_stream(vgmstream, streamFile, start_offset))
goto fail;
return vgmstream;
fail:
/* clean up anything we may have opened */
if (vgmstream) close_vgmstream(vgmstream);
close_vgmstream(vgmstream);
return NULL;
}
}

View File

@ -6,31 +6,34 @@
/* known TXTH types */
typedef enum {
PSX = 0, /* PSX ADPCM */
XBOX = 1, /* XBOX IMA ADPCM */
NGC_DTK = 2, /* NGC ADP/DTK ADPCM */
PCM16BE = 3, /* 16bit big endian PCM */
PCM16LE = 4, /* 16bit little endian PCM */
PCM8 = 5, /* 8bit PCM */
SDX2 = 6, /* SDX2 (3D0 games) */
DVI_IMA = 7, /* DVI IMA ADPCM */
MPEG = 8, /* MPEG (MP3) */
IMA = 9, /* IMA ADPCM */
AICA = 10, /* AICA ADPCM (dreamcast) */
MSADPCM = 11, /* MS ADPCM (windows) */
NGC_DSP = 12, /* NGC DSP (GC) */
PCM8_U_int = 13, /* 8bit unsigned PCM (interleaved) */
PSX_bf = 14, /* PSX ADPCM bad flagged */
MS_IMA = 15, /* Microsoft IMA ADPCM */
PCM8_U = 16, /* 8bit unsigned PCM */
APPLE_IMA4 = 17, /* Apple Quicktime 4-bit IMA ADPCM */
ATRAC3 = 18, /* raw ATRAC3 */
ATRAC3PLUS = 19, /* raw ATRAC3PLUS */
XMA1 = 20, /* raw XMA1 */
XMA2 = 21, /* raw XMA2 */
FFMPEG = 22, /* any headered FFmpeg format */
AC3 = 23, /* AC3/SPDIF */
PCFX = 24, /* PC-FX ADPCM */
PSX = 0, /* PS-ADPCM */
XBOX = 1, /* XBOX IMA ADPCM */
NGC_DTK = 2, /* NGC ADP/DTK ADPCM */
PCM16BE = 3, /* 16-bit big endian PCM */
PCM16LE = 4, /* 16-bit little endian PCM */
PCM8 = 5, /* 8-bit PCM */
SDX2 = 6, /* SDX2 (3D0 games) */
DVI_IMA = 7, /* DVI IMA ADPCM (high nibble first) */
MPEG = 8, /* MPEG (MP3) */
IMA = 9, /* IMA ADPCM (low nibble first) */
AICA = 10, /* AICA ADPCM (Dreamcast games) */
MSADPCM = 11, /* MS ADPCM (Windows games) */
NGC_DSP = 12, /* NGC DSP (Nintendo games) */
PCM8_U_int = 13, /* 8-bit unsigned PCM (interleaved) */
PSX_bf = 14, /* PS-ADPCM with bad flags */
MS_IMA = 15, /* Microsoft IMA ADPCM */
PCM8_U = 16, /* 8-bit unsigned PCM */
APPLE_IMA4 = 17, /* Apple Quicktime 4-bit IMA ADPCM */
ATRAC3 = 18, /* Raw ATRAC3 */
ATRAC3PLUS = 19, /* Raw ATRAC3PLUS */
XMA1 = 20, /* Raw XMA1 */
XMA2 = 21, /* Raw XMA2 */
FFMPEG = 22, /* Any headered FFmpeg format */
AC3 = 23, /* AC3/SPDIF */
PCFX = 24, /* PC-FX ADPCM */
PCM4 = 25, /* 4-bit signed PCM (3rd and 4th gen games) */
PCM4_U = 26, /* 4-bit unsigned PCM (3rd and 4th gen games) */
OKI16 = 27, /* OKI ADPCM with 16-bit output (unlike OKI/VOX/Dialogic ADPCM's 12-bit) */
} txth_type;
typedef struct {
@ -46,6 +49,7 @@ typedef struct {
uint32_t id_offset;
uint32_t interleave;
uint32_t interleave_last;
uint32_t channels;
uint32_t sample_rate;
@ -176,6 +180,9 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
case FFMPEG: coding = coding_FFmpeg; break;
#endif
case PCFX: coding = coding_PCFX; break;
case PCM4: coding = coding_PCM4; break;
case PCM4_U: coding = coding_PCM4_U; break;
case OKI16: coding = coding_OKI16; break;
default:
goto fail;
}
@ -212,6 +219,8 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
case coding_PCM16BE:
case coding_PCM8:
case coding_PCM8_U:
case coding_PCM4:
case coding_PCM4_U:
case coding_SDX2:
case coding_PSX:
case coding_PSX_badflags:
@ -220,6 +229,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
case coding_AICA:
case coding_APPLE_IMA4:
vgmstream->interleave_block_size = txth.interleave;
vgmstream->interleave_last_block_size = txth.interleave_last;
if (vgmstream->channels > 1)
{
if (coding == coding_SDX2) {
@ -260,15 +270,25 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
vgmstream->ch[i].adpcm_step_index = 0x7f;
}
}
if (coding == coding_PCM4 || coding == coding_PCM4_U) {
/* high nibble or low nibble first */
vgmstream->codec_config = txth.codec_mode;
}
break;
case coding_PCFX:
vgmstream->interleave_block_size = txth.interleave;
vgmstream->interleave_last_block_size = txth.interleave_last;
vgmstream->layout_type = layout_interleave;
if (txth.codec_mode >= 0 && txth.codec_mode <= 3)
vgmstream->codec_config = txth.codec_mode;
break;
case coding_OKI16:
vgmstream->layout_type = layout_none;
break;
case coding_MS_IMA:
if (!txth.interleave) goto fail; /* creates garbage */
@ -287,10 +307,12 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
coding = coding_XBOX_IMA_int;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = txth.interleave;
vgmstream->interleave_last_block_size = txth.interleave_last;
}
else { /* 1ch mono, or stereo interleave */
vgmstream->layout_type = txth.interleave ? layout_interleave : layout_none;
vgmstream->interleave_block_size = txth.interleave;
vgmstream->interleave_last_block_size = txth.interleave_last;
if (vgmstream->channels > 2 && vgmstream->channels % 2 != 0)
goto fail; /* only 2ch+..+2ch layout is known */
}
@ -303,6 +325,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
if (txth.channels > 1 && txth.codec_mode == 0) {
if (!txth.interleave) goto fail;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_last_block_size = txth.interleave_last;
vgmstream->interleave_block_size = txth.interleave;
} else if (txth.channels > 1 && txth.codec_mode == 1) {
if (!txth.interleave) goto fail;
@ -466,8 +489,10 @@ fail:
static STREAMFILE * open_txth(STREAMFILE * streamFile) {
char basename[PATH_LIMIT];
char filename[PATH_LIMIT];
char fileext[PATH_LIMIT];
const char *subext;
STREAMFILE * streamText;
/* try "(path/)(name.ext).txth" */
@ -476,6 +501,22 @@ static STREAMFILE * open_txth(STREAMFILE * streamFile) {
streamText = open_streamfile(streamFile,filename);
if (streamText) return streamText;
/* try "(path/)(.sub.ext).txth" */
get_streamfile_basename(streamFile,basename,PATH_LIMIT);
subext = filename_extension(basename);
if (subext != NULL) {
get_streamfile_path(streamFile,filename,PATH_LIMIT);
get_streamfile_ext(streamFile,fileext,PATH_LIMIT);
strcat(filename,".");
strcat(filename, subext);
strcat(filename,".");
strcat(filename, fileext);
strcat(filename, ".txth");
streamText = open_streamfile(streamFile,filename);
if (streamText) return streamText;
}
/* try "(path/)(.ext).txth" */
get_streamfile_path(streamFile,filename,PATH_LIMIT);
get_streamfile_ext(streamFile,fileext,PATH_LIMIT);
@ -583,6 +624,9 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char
else if (0==strcmp(val,"FFMPEG")) txth->codec = FFMPEG;
else if (0==strcmp(val,"AC3")) txth->codec = AC3;
else if (0==strcmp(val,"PCFX")) txth->codec = PCFX;
else if (0==strcmp(val,"PCM4")) txth->codec = PCM4;
else if (0==strcmp(val,"PCM4_U")) txth->codec = PCM4_U;
else if (0==strcmp(val,"OKI16")) txth->codec = OKI16;
else goto fail;
}
else if (0==strcmp(key,"codec_mode")) {
@ -617,6 +661,15 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char
if (!parse_num(txth->streamHead,txth,val, &txth->interleave)) goto fail;
}
}
else if (0==strcmp(key,"interleave_last")) {
if (0==strcmp(val,"auto")) {
if (txth->channels > 0 && txth->interleave > 0)
txth->interleave_last = (txth->data_size % (txth->interleave * txth->channels)) / txth->channels;
}
else {
if (!parse_num(txth->streamHead,txth,val, &txth->interleave_last)) goto fail;
}
}
else if (0==strcmp(key,"channels")) {
if (!parse_num(txth->streamHead,txth,val, &txth->channels)) goto fail;
}
@ -863,6 +916,7 @@ static int parse_num(STREAMFILE * streamFile, txth_header * txth, const char * v
}
else { /* known field */
if (0==strcmp(val,"interleave")) *out_value = txth->interleave;
if (0==strcmp(val,"interleave_last")) *out_value = txth->interleave_last;
else if (0==strcmp(val,"channels")) *out_value = txth->channels;
else if (0==strcmp(val,"sample_rate")) *out_value = txth->sample_rate;
else if (0==strcmp(val,"start_offset")) *out_value = txth->start_offset;
@ -913,6 +967,9 @@ static int get_bytes_to_samples(txth_header * txth, uint32_t bytes) {
case PCM8_U_int:
case PCM8_U:
return pcm_bytes_to_samples(bytes, txth->channels, 8);
case PCM4:
case PCM4_U:
return pcm_bytes_to_samples(bytes, txth->channels, 4);
case MSADPCM:
if (!txth->interleave) return 0;
return msadpcm_bytes_to_samples(bytes, txth->interleave, txth->channels);
@ -938,7 +995,8 @@ static int get_bytes_to_samples(txth_header * txth, uint32_t bytes) {
case AICA:
return aica_bytes_to_samples(bytes, txth->channels);
case PCFX:
return pcfx_bytes_to_samples(bytes, txth->channels);
case OKI16:
return oki_bytes_to_samples(bytes, txth->channels);
/* untested */
case SDX2:

View File

@ -32,7 +32,7 @@ typedef struct {
int subtypes_count[9];
} ubi_bao_header;
static int parse_bao(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset);
static int parse_bao(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset, int target_subsong);
static int parse_pk_header(ubi_bao_header * bao, STREAMFILE *streamFile);
static VGMSTREAM * init_vgmstream_ubi_bao_main(ubi_bao_header * bao, STREAMFILE *streamFile);
static STREAMFILE * setup_bao_streamfile(ubi_bao_header *bao, STREAMFILE *streamFile);
@ -265,7 +265,7 @@ static int parse_pk_header(ubi_bao_header * bao, STREAMFILE *streamFile) {
size_t index_size, index_header_size;
off_t bao_offset, resources_offset;
int target_subsong = streamFile->stream_index;
uint8_t *index_buffer = NULL;
STREAMFILE *streamIndex = NULL;
STREAMFILE *streamTest = NULL;
@ -274,6 +274,8 @@ static int parse_pk_header(ubi_bao_header * bao, STREAMFILE *streamFile) {
goto fail;
/* index and resources always LE */
if (target_subsong == 0) target_subsong = 1;
/* 0x01(3): version, major/minor/release (numbering continues from .sb0/sm0) */
index_size = read_32bitLE(0x04, streamFile); /* can be 0, not including */
resources_offset = read_32bitLE(0x08, streamFile); /* always found even if not used */
@ -294,21 +296,22 @@ static int parse_pk_header(ubi_bao_header * bao, STREAMFILE *streamFile) {
VGM_LOG("BAO: index too big\n");
goto fail;
}
index_buffer = malloc(index_size);
read_streamfile(index_buffer, index_header_size, index_size, streamFile);
/* use smaller I/O buffer for performance, as this read lots of small BAO headers all over the place */
/* use smaller I/O buffers for performance, as this read lots of small headers all over the place */
streamIndex = reopen_streamfile(streamFile, index_size);
if (!streamIndex) goto fail;
streamTest = reopen_streamfile(streamFile, 0x100);
if (!streamTest) goto fail;
/* parse index to get target subsong N = Nth audio header BAO */
bao_offset = index_header_size + index_size;
for (i = 0; i < index_entries; i++) {
//uint32_t bao_id = get_32bitLE(index_buffer + 0x08*i+ 0x00);
size_t bao_size = get_32bitLE(index_buffer + 0x08*i + 0x04);
//uint32_t bao_id = read_32bitLE(index_header_size + 0x08*i + 0x00, streamIndex);
size_t bao_size = read_32bitLE(index_header_size + 0x08*i + 0x04, streamIndex);
/* parse and continue to find out total_subsongs */
if (!parse_bao(bao, streamTest, bao_offset))
if (!parse_bao(bao, streamTest, bao_offset, target_subsong))
goto fail;
bao_offset += bao_size; /* files simply concat BAOs */
@ -407,21 +410,20 @@ static int parse_pk_header(ubi_bao_header * bao, STREAMFILE *streamFile) {
;VGM_LOG("BAO stream: id=%x, offset=%x, size=%x, res=%s\n", bao->stream_id, (uint32_t)bao->stream_offset, bao->stream_size, (bao->is_external ? bao->resource_name : "internal"));
free(index_buffer);
close_streamfile(streamIndex);
close_streamfile(streamTest);
return 1;
fail:
free(index_buffer);
close_streamfile(streamIndex);
close_streamfile(streamTest);
return 0;
}
/* parse a single BAO (binary audio object) descriptor */
static int parse_bao(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset) {
static int parse_bao(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset, int target_subsong) {
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
uint32_t bao_version, descriptor_type, descriptor_subtype;
size_t header_size;
int target_subsong = streamFile->stream_index;
/* 0x00(1): class? usually 0x02 but older BAOs have 0x01 too */
@ -463,11 +465,11 @@ static int parse_bao(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset)
/* for debugging purposes */
switch(descriptor_subtype) {
case 0x00000001: bao->subtypes_count[1]++; break; /* standard */
case 0x00000002: bao->subtypes_count[2]++; break; /* multilayer??? related to other header BAOs? */
case 0x00000002: bao->subtypes_count[2]++; break; /* related to localized BAOs? (.lpk) */
case 0x00000003: bao->subtypes_count[3]++; break; /* related to other header BAOs? */
case 0x00000004: bao->subtypes_count[4]++; break; /* related to other header BAOs? */
case 0x00000005: bao->subtypes_count[5]++; break; /* related to other header BAOs? */
case 0x00000006: bao->subtypes_count[6]++; break; /* some multilayer/table? may contain sounds??? */
case 0x00000006: bao->subtypes_count[6]++; break; /* multilayer with multiple sounds */
case 0x00000007: bao->subtypes_count[7]++; break; /* related to other header BAOs? */
case 0x00000008: bao->subtypes_count[8]++; break; /* ? (almost empty with some unknown value) */
default:
@ -476,13 +478,26 @@ static int parse_bao(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset)
}
//;VGM_ASSERT(descriptor_subtype != 0x01, "UBI BAO: subtype %x at %lx (%lx)\n", descriptor_subtype, offset, offset+header_size+0x04);
if (descriptor_subtype == 0x06) {
;VGM_LOG("UBI BAO: layer subtype at %lx (%lx)\n", offset, offset+header_size+0x04);
/* todo fix layers
* for scott pilgrim:
* - 0x50: layer count
* - 0x78: layer headers size?
* - 0x7c: prefetch size
* - 0xb4: layer header xN (size 0x30)
* (this header has sample rate, channels, codec, various sizes/num samples)
* - 0x114: good ol' Ubi SB layer header v0x00100009 with classic v0x03 blocked data
* (standard prefetch style, part of data then cut in the middle and links to stream)
*/
goto fail;
}
/* ignore unknown subtypes */
if (descriptor_subtype != 0x00000001)
return 1;
bao->total_subsongs++;
if (target_subsong == 0) target_subsong = 1;
if (target_subsong != bao->total_subsongs)
return 1;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,363 @@
#ifndef _UBI_SB_STREAMFILE_H_
#define _UBI_SB_STREAMFILE_H_
#include "../streamfile.h"
typedef struct {
/* config */
off_t stream_offset;
off_t stream_size;
int layer_number;
int layer_count;
int layer_max;
int big_endian;
/* internal config */
off_t header_next_start; /* offset to header field */
off_t header_sizes_start; /* offset to header table */
off_t header_data_start; /* offset to header data */
off_t block_next_start; /* offset to block field */
off_t block_sizes_start; /* offset to block table */
off_t block_data_start; /* offset to block data */
size_t header_size; /* derived */
/* state */
off_t logical_offset; /* fake offset */
off_t physical_offset; /* actual offset */
size_t block_size; /* current size */
size_t next_block_size; /* next size */
size_t skip_size; /* size from block start to reach data */
size_t data_size; /* usable size in a block */
size_t logical_size;
} ubi_sb_io_data;
static size_t ubi_sb_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, ubi_sb_io_data* data) {
int32_t(*read_32bit)(off_t, STREAMFILE*) = data->big_endian ? read_32bitBE : read_32bitLE;
size_t total_read = 0;
int i;
/* re-start when previous offset (can't map logical<>physical offsets) */
if (data->logical_offset < 0 || offset < data->logical_offset) {
data->physical_offset = data->stream_offset;
data->logical_offset = 0x00;
data->data_size = 0;
/* process header block (slightly different and data size may be 0) */
{
data->block_size = data->header_size;
data->next_block_size = read_32bit(data->physical_offset + data->header_next_start, streamfile);
if (data->header_sizes_start) {
data->skip_size = data->header_data_start;
for (i = 0; i < data->layer_number; i++) {
data->skip_size += read_32bit(data->physical_offset + data->header_sizes_start + i*0x04, streamfile);
}
data->data_size = read_32bit(data->physical_offset + data->header_sizes_start + data->layer_number*0x04, streamfile);
}
if (data->data_size == 0) {
data->physical_offset += data->block_size;
}
}
}
/* read blocks */
while (length > 0) {
/* ignore EOF */
if (offset < 0 || data->physical_offset >= data->stream_offset + data->stream_size) {
break;
}
/* process new block */
if (data->data_size == 0) {
data->block_size = data->next_block_size;
if (data->block_next_start) /* not set when fixed block size */
data->next_block_size = read_32bit(data->physical_offset + data->block_next_start, streamfile);
data->skip_size = data->block_data_start;
for (i = 0; i < data->layer_number; i++) {
data->skip_size += read_32bit(data->physical_offset + data->block_sizes_start + i*0x04, streamfile);
}
data->data_size = read_32bit(data->physical_offset + data->block_sizes_start + data->layer_number*0x04, streamfile);
}
/* move to next block */
if (offset >= data->logical_offset + data->data_size) {
if (data->block_size == 0 || data->block_size == 0xFFFFFFFF)
break;
data->physical_offset += data->block_size;
data->logical_offset += data->data_size;
data->data_size = 0;
continue;
}
/* read 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, streamfile);
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 ubi_sb_io_size(STREAMFILE *streamfile, ubi_sb_io_data* data) {
uint8_t buf[1];
if (data->logical_size)
return data->logical_size;
/* force a fake read at max offset, to get max logical_offset (will be reset next read) */
ubi_sb_io_read(streamfile, buf, 0x7FFFFFFF, 1, data);
data->logical_size = data->logical_offset;
return data->logical_size;
}
static int ubi_sb_io_init(STREAMFILE *streamfile, ubi_sb_io_data* data) {
int32_t (*read_32bit)(off_t,STREAMFILE*) = data->big_endian ? read_32bitBE : read_32bitLE;
off_t offset = data->stream_offset;
uint32_t version;
int i;
if (data->stream_offset + data->stream_size > get_streamfile_size(streamfile)) {
VGM_LOG("UBI SB: bad size\n");
goto fail;
}
/* Layers have a main header, then headered blocks with data.
* We configure stuff to unify parsing of all variations. */
version = (uint32_t)read_32bit(offset+0x00, streamfile);
switch(version) {
case 0x00000002: /* Splinter Cell */
/* - layer header
* 0x04: layer count
* 0x08: stream size
* 0x0c: block header size
* 0x10: block size (fixed)
* 0x14: min layer size?
* - block header
* 0x00: block number
* 0x04: block offset
* 0x08+(04*N): layer size per layer
* 0xNN: layer data per layer */
data->layer_max = read_32bit(offset+0x04, streamfile);
data->header_next_start = 0x10;
data->header_sizes_start = 0;
data->header_data_start = 0x18;
data->block_next_start = 0;
data->block_sizes_start = 0x08;
data->block_data_start = 0x08 + data->layer_max*0x04;
break;
case 0x00000004: /* Prince of Persia: Sands of Time, Batman: Rise of Sin Tzu */
/* - layer header
* 0x04: layer count
* 0x08: stream size
* 0x0c: block count
* 0x10: block header size
* 0x14: block size (fixed)
* 0x18: min layer data?
* 0x1c: size of header sizes
* 0x20+(04*N): header size per layer
* - block header
* 0x00: block number
* 0x04: block offset
* 0x08: always 0x03
* 0x0c+(04*N): layer size per layer
* 0xNN: layer data per layer */
data->layer_max = read_32bit(offset+0x04, streamfile);
data->header_next_start = 0x14;
data->header_sizes_start = 0x20;
data->header_data_start = 0x20 + data->layer_max*0x04;
data->block_next_start = 0;
data->block_sizes_start = 0x0c;
data->block_data_start = 0x0c + data->layer_max*0x04;
break;
case 0x00000007: /* Splinter Cell: Essentials, Splinter Cell 3D */
/* - layer header
* 0x04: config?
* 0x08: layer count
* 0x0c: stream size
* 0x10: block count
* 0x14: block header size
* 0x18: block size (fixed)
* 0x1c+(04*8): min layer data? for 8 layers (-1 after layer count)
* 0x3c: size of header sizes
* 0x40+(04*N): header size per layer
* 0xNN: header data per layer
* - block header
* 0x00: block number
* 0x04: block offset
* 0x08: always 0x03
* 0x0c+(04*N): layer size per layer
* 0xNN: layer data per layer */
data->layer_max = read_32bit(offset+0x08, streamfile);
data->header_next_start = 0x18;
data->header_sizes_start = 0x40;
data->header_data_start = 0x40 + data->layer_max*0x04;
data->block_next_start = 0;
data->block_sizes_start = 0x0c;
data->block_data_start = 0x0c + data->layer_max*0x04;
break;
case 0x00040008: /* Assassin's Creed */
case 0x000B0008: /* Open Season, Surf's Up, TMNT, Splinter Cell HD */
case 0x000C0008: /* Splinter Cell: Double Agent */
case 0x00100008: /* Rainbow Six 2 */
/* - layer header
* 0x04: config?
* 0x08: layer count
* 0x0c: blocks count
* 0x10: block header size
* 0x14: size of header sizes/data
* 0x18: next block size
* 0x1c+(04*N): layer header size
* 0xNN: header data per layer
* - block header:
* 0x00: always 0x03
* 0x04: next block size
* 0x08+(04*N): layer size per layer
* 0xNN: layer data per layer */
data->layer_max = read_32bit(offset+0x08, streamfile);
data->header_next_start = 0x18;
data->header_sizes_start = 0x1c;
data->header_data_start = 0x1c + data->layer_max*0x04;
data->block_next_start = 0x04;
data->block_sizes_start = 0x08;
data->block_data_start = 0x08 + data->layer_max*0x04;
break;
case 0x00100009: /* Splinter Cell: Pandora Tomorrow HD, Prince of Persia 2008, Scott Pilgrim */
/* - layer header
* 0x04: config?
* 0x08: layer count
* 0x0c: blocks count
* 0x10: block header size
* 0x14: size of header sizes/data
* 0x18: next block size
* 0x1c+(04*10): usable size per layer
* 0x5c+(04*N): layer header size
* 0xNN: header data per layer
* - block header:
* 0x00: always 0x03
* 0x04: next block size
* 0x08+(04*N): layer size per layer
* 0xNN: layer data per layer */
data->layer_max = read_32bit(offset+0x08, streamfile);
data->header_next_start = 0x18;
data->header_sizes_start = 0x5c;
data->header_data_start = 0x5c + data->layer_max*0x04;
data->block_next_start = 0x04;
data->block_sizes_start = 0x08;
data->block_data_start = 0x08 + data->layer_max*0x04;
break;
default:
VGM_LOG("UBI SB: unknown layer header %08x\n", version);
goto fail;
}
/* get base size to simplify later parsing */
data->header_size = data->header_data_start;
if (data->header_sizes_start) {
for (i = 0; i < data->layer_max; i++) {
data->header_size += read_32bit(offset + data->header_sizes_start + i*0x04, streamfile);
}
}
/* force read header block */
data->logical_offset = -1;
/* just in case some headers may use less layers that stream has */
VGM_ASSERT(data->layer_count != data->layer_max, "UBI SB: non-matching layer counts\n");
if (data->layer_count > data->layer_max) {
VGM_LOG("UBI SB: layer count bigger than layer max\n");
goto fail;
}
/* Common layer quirks:
* - layer format depends on its own version and not on platform or DARE engine version
* - codec header may be in the layer header, or in the first block
* - stream size doesn't include padding
* - block number goes from 1 to block_count
* - block offset is relative to layer start
* - blocks data size varies between blocks and between layers in the same block
* - "config?" is a small value that varies between streams of the same game
* - next block size is 0 at last block
* - both Ubi SB and Ubi BAO use same-version layers
*/
return 1;
fail:
return 0;
}
/* Handles deinterleaving of Ubisoft's headered+blocked 'multitrack' streams */
static STREAMFILE* setup_ubi_sb_streamfile(STREAMFILE *streamFile, off_t stream_offset, size_t stream_size, int layer_number, int layer_count, int big_endian) {
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
ubi_sb_io_data io_data = {0};
size_t io_data_size = sizeof(ubi_sb_io_data);
io_data.stream_offset = stream_offset;
io_data.stream_size = stream_size;
io_data.layer_number = layer_number;
io_data.layer_count = layer_count;
io_data.big_endian = big_endian;
if (!ubi_sb_io_init(streamFile, &io_data))
goto fail;
/* setup subfile */
new_streamFile = open_wrap_streamfile(streamFile);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, ubi_sb_io_read,ubi_sb_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;
return temp_streamFile;
fail:
close_streamfile(temp_streamFile);
return NULL;
}
#endif /* _UBI_SB_STREAMFILE_H_ */

View File

@ -19,8 +19,10 @@ VGMSTREAM * init_vgmstream_vag(STREAMFILE *streamFile) {
* .swag: Frantix (PSP)
* .str: Ben10 Galactic Racing
* .vig: MX vs. ATV Untamed (PS2)
* .l/r: Crash Nitro Kart (PS2), Gradius V (PS2) */
if ( !check_extensions(streamFile,"vag,swag,str,vig,l,r") )
* .l/r: Crash Nitro Kart (PS2), Gradius V (PS2)
* .vas: Kingdom Hearts II (PS2)
* .khv: fake for .vas */
if ( !check_extensions(streamFile,"vag,swag,str,vig,l,r,vas,khv") )
goto fail;
/* check VAG Header */
@ -112,7 +114,7 @@ VGMSTREAM * init_vgmstream_vag(STREAMFILE *streamFile) {
interleave = 0x10;
loop_flag = 0;
}
else if (check_extensions(streamFile,"swag")) { /* algo "VAGp" at (file_size / channels) */
else if (check_extensions(streamFile,"swag")) { /* also "VAGp" at (file_size / channels) */
/* Frantix (PSP) */
start_offset = 0x40; /* channel_size ignores empty frame */
channel_count = 2;
@ -188,6 +190,19 @@ VGMSTREAM * init_vgmstream_vag(STREAMFILE *streamFile) {
channel_size = channel_size / channel_count;
loop_flag = ps_find_loop_offsets(streamFile, start_offset, channel_size*channel_count, channel_count, interleave, &loop_start_sample, &loop_end_sample);
}
else if (version == 0x00000004 && channel_size == file_size - 0x60 && read_32bitBE(0x1c, streamFile) != 0) { /* also .vas */
/* Kingdom Hearts II (PS2) */
start_offset = 0x60;
interleave = 0x10;
loop_start_sample = read_32bitBE(0x14,streamFile);
loop_end_sample = read_32bitBE(0x18,streamFile);
loop_flag = (loop_end_sample > 0); /* maybe at 0x1d */
channel_count = read_8bit(0x1e,streamFile);
/* 0x1f: possibly volume */
channel_size = channel_size / channel_count;
/* mono files also have channel/volume, but start at 0x30 and are probably named .vag */
}
else {
/* standard PS1/PS2/PS3 .vag [Ecco the Dolphin (PS2), Legasista (PS3)] */
start_offset = 0x30;

View File

@ -22,7 +22,8 @@ VGMSTREAM * init_vgmstream_xnb(STREAMFILE *streamFile) {
platform = read_8bit(0x03,streamFile);
big_endian = (platform == 'x');
if (read_8bit(0x04,streamFile) != 0x05) /* XNA 4.0 version only */
if (read_8bit(0x04,streamFile) != 0x04 && /* XNA 3.0? found on Scare Me (XBLIG), no notable diffs */
read_8bit(0x04,streamFile) != 0x05) /* XNA 4.0 version */
goto fail;
flags = read_8bit(0x05,streamFile);

View File

@ -253,7 +253,6 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_ps2_smpl,
init_vgmstream_ps2_msa,
init_vgmstream_ps2_voi,
init_vgmstream_ps2_khv,
init_vgmstream_ngc_rkv,
init_vgmstream_dsp_ddsp,
init_vgmstream_p3d,
@ -279,6 +278,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_sqex_scd,
init_vgmstream_ngc_nst_dsp,
init_vgmstream_baf,
init_vgmstream_baf_badrip,
init_vgmstream_ps3_msf,
init_vgmstream_nub_vag,
init_vgmstream_ps3_past,
@ -375,9 +375,11 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_vxn,
init_vgmstream_ea_snr_sns,
init_vgmstream_ea_sps,
init_vgmstream_ea_abk_new,
init_vgmstream_ea_abk_eaac,
init_vgmstream_ea_hdr_sth_dat,
init_vgmstream_ea_mpf_mus_new,
init_vgmstream_ea_mpf_mus_eaac,
init_vgmstream_ea_sbr,
init_vgmstream_ea_sbr_harmony,
init_vgmstream_ngc_vid1,
init_vgmstream_flx,
init_vgmstream_mogg,
@ -460,6 +462,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_imc,
init_vgmstream_imc_container,
init_vgmstream_smp,
init_vgmstream_gin,
/* 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 */
@ -1142,6 +1145,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
case coding_SNDS_IMA:
case coding_OTNS_IMA:
case coding_UBI_IMA:
case coding_OKI16:
return 1;
case coding_IMA_int:
case coding_DVI_IMA_int:
@ -1188,11 +1192,14 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
case coding_EA_XA_V2:
case coding_MAXIS_XA:
return 28;
case coding_EA_XAS:
case coding_EA_XAS_V0:
return 32;
case coding_EA_XAS_V1:
return 128;
case coding_MSADPCM:
return (vgmstream->interleave_block_size - 0x07*vgmstream->channels)*2 / vgmstream->channels + 2;
case coding_MSADPCM_int:
case coding_MSADPCM_ck:
return (vgmstream->interleave_block_size - 0x07)*2 + 2;
case coding_WS: /* only works if output sample size is 8 bit, which always is for WS ADPCM */
@ -1323,6 +1330,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
case coding_ALP_IMA:
case coding_FFTA2_IMA:
case coding_PCFX:
case coding_OKI16:
return 0x01;
case coding_MS_IMA:
case coding_RAD_IMA:
@ -1370,10 +1378,13 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
return 0x0F*vgmstream->channels;
case coding_EA_XA_V2:
return 0; /* variable (ADPCM frames of 0x0f or PCM frames of 0x3d) */
case coding_EA_XAS:
case coding_EA_XAS_V0:
return 0xF+0x02+0x02;
case coding_EA_XAS_V1:
return 0x4c*vgmstream->channels;
case coding_MSADPCM:
case coding_MSADPCM_int:
case coding_MSADPCM_ck:
return vgmstream->interleave_block_size;
case coding_WS:
@ -1550,6 +1561,18 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
vgmstream->channels,vgmstream->samples_into_block,samples_to_do);
}
break;
case coding_PCM4:
for (ch = 0; ch < vgmstream->channels; ch++) {
decode_pcm4(vgmstream,&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch,
vgmstream->channels,vgmstream->samples_into_block,samples_to_do,ch);
}
break;
case coding_PCM4_U:
for (ch = 0; ch < vgmstream->channels; ch++) {
decode_pcm4_unsigned(vgmstream, &vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch,
vgmstream->channels,vgmstream->samples_into_block,samples_to_do,ch);
}
break;
case coding_ULAW:
for (ch = 0; ch < vgmstream->channels; ch++) {
@ -1694,9 +1717,15 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch);
}
break;
case coding_EA_XAS:
case coding_EA_XAS_V0:
for (ch = 0; ch < vgmstream->channels; ch++) {
decode_ea_xas(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch,
decode_ea_xas_v0(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch,
vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch);
}
break;
case coding_EA_XAS_V1:
for (ch = 0; ch < vgmstream->channels; ch++) {
decode_ea_xas_v1(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch,
vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch);
}
break;
@ -1923,14 +1952,17 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
buffer+samples_written*vgmstream->channels, samples_to_do);
break;
case coding_MSADPCM:
if (vgmstream->channels == 2) {
case coding_MSADPCM_int:
if (vgmstream->channels == 1 || vgmstream->coding_type == coding_MSADPCM_int) {
for (ch = 0; ch < vgmstream->channels; ch++) {
decode_msadpcm_mono(vgmstream,buffer+samples_written*vgmstream->channels+ch,
vgmstream->channels,vgmstream->samples_into_block, samples_to_do, ch);
}
}
else if (vgmstream->channels == 2) {
decode_msadpcm_stereo(vgmstream,buffer+samples_written*vgmstream->channels,
vgmstream->samples_into_block,samples_to_do);
}
else if (vgmstream->channels == 1) {
decode_msadpcm_mono(vgmstream,buffer+samples_written*vgmstream->channels,
vgmstream->channels,vgmstream->samples_into_block,samples_to_do, 0);
}
break;
case coding_MSADPCM_ck:
for (ch = 0; ch < vgmstream->channels; ch++) {
@ -2028,6 +2060,12 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
vgmstream->channels,vgmstream->samples_into_block,samples_to_do, vgmstream->codec_config);
}
break;
case coding_OKI16:
for (ch = 0; ch < vgmstream->channels; ch++) {
decode_oki16(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch,
vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch);
}
break;
case coding_EA_MT:
for (ch = 0; ch < vgmstream->channels; ch++) {
@ -2277,11 +2315,18 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
snprintf(temp,TEMPSIZE,
"\nlayout: ");
concatn(length,desc,temp);
description = get_vgmstream_layout_description(vgmstream->layout_type);
if (!description)
description = "INCONCEIVABLE";
switch (vgmstream->layout_type) {
case layout_layered:
snprintf(temp,TEMPSIZE,"%s (%i layers)",description, ((layered_layout_data*)vgmstream->layout_data)->layer_count);
break;
case layout_segmented:
snprintf(temp,TEMPSIZE,"%s (%i segments)",description, ((segmented_layout_data*)vgmstream->layout_data)->segment_count);
break;
default:
description = get_vgmstream_layout_description(vgmstream->layout_type);
if (!description)
description = "INCONCEIVABLE";
snprintf(temp,TEMPSIZE,"%s",description);
break;
}
@ -2309,6 +2354,7 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
if (vgmstream->layout_type == layout_none && vgmstream->interleave_block_size > 0) {
switch (vgmstream->coding_type) {
case coding_MSADPCM:
case coding_MSADPCM_int:
case coding_MSADPCM_ck:
case coding_MS_IMA:
case coding_MC3:
@ -2723,14 +2769,13 @@ int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t s
if (!file) goto fail;
}
for (ch=0; ch < vgmstream->channels; ch++) {
for (ch = 0; ch < vgmstream->channels; ch++) {
off_t offset;
if (use_same_offset_per_channel) {
offset = start_offset;
} else if (is_stereo_codec) {
int ch_mod = (ch & 1) ? ch - 1 : ch; /* adjust odd channels (ch 0,1,2,3,4,5 > ch 0,0,2,2,4,4) */
offset = start_offset + vgmstream->interleave_block_size*ch_mod;
//VGM_LOG("ch%i offset=%lx\n", ch,offset);
} else {
offset = start_offset + vgmstream->interleave_block_size*ch;
}
@ -2741,6 +2786,7 @@ int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t s
if (!file) goto fail;
}
VGM_LOG("ch%i offset=%lx\n", ch,offset);
vgmstream->ch[ch].streamfile = file;
vgmstream->ch[ch].channel_start_offset =
vgmstream->ch[ch].offset = offset;

View File

@ -80,6 +80,8 @@ typedef enum {
coding_PCM8_U, /* 8-bit PCM, unsigned (0x80 = 0) */
coding_PCM8_U_int, /* 8-bit PCM, unsigned (0x80 = 0) with sample-level interleave (for blocks) */
coding_PCM8_SB, /* 8-bit PCM, sign bit (others are 2's complement) */
coding_PCM4, /* 4-bit PCM, signed */
coding_PCM4_U, /* 4-bit PCM, unsigned */
coding_ULAW, /* 8-bit u-Law (non-linear PCM) */
coding_ULAW_int, /* 8-bit u-Law (non-linear PCM) with sample-level interleave (for blocks) */
@ -111,7 +113,8 @@ typedef enum {
coding_EA_XA_int, /* Electronic Arts EA-XA ADPCM v1 (mono/interleave) */
coding_EA_XA_V2, /* Electronic Arts EA-XA ADPCM v2 */
coding_MAXIS_XA, /* Maxis EA-XA ADPCM */
coding_EA_XAS, /* Electronic Arts EA-XAS ADPCM */
coding_EA_XAS_V0, /* Electronic Arts EA-XAS ADPCM v0 */
coding_EA_XAS_V1, /* Electronic Arts EA-XAS ADPCM v1 */
coding_IMA, /* IMA ADPCM (stereo or mono, low nibble first) */
coding_IMA_int, /* IMA ADPCM (mono/interleave, low nibble first) */
@ -141,6 +144,7 @@ typedef enum {
coding_H4M_IMA, /* H4M IMA ADPCM (stereo or mono, high nibble first) */
coding_MSADPCM, /* Microsoft ADPCM (stereo/mono) */
coding_MSADPCM_int, /* Microsoft ADPCM (mono) */
coding_MSADPCM_ck, /* Microsoft ADPCM (Cricket Audio variation) */
coding_WS, /* Westwood Studios VBR ADPCM */
coding_AICA, /* Yamaha AICA ADPCM (stereo) */
@ -157,6 +161,7 @@ typedef enum {
coding_ASF, /* Argonaut ASF 4-bit ADPCM */
coding_XMD, /* Konami XMD 4-bit ADPCM */
coding_PCFX, /* PC-FX 4-bit ADPCM */
coding_OKI16, /* OKI 4-bit ADPCM with 16-bit output */
/* others */
coding_SDX2, /* SDX2 2:1 Squareroot-Delta-Exact compression DPCM */
@ -537,7 +542,6 @@ typedef enum {
meta_PS2_SMPL, /* Homura */
meta_PS2_MSA, /* Psyvariar -Complete Edition- */
meta_PS2_VOI, /* RAW Danger (Zettaizetsumei Toshi 2 - Itetsuita Kiokutachi) [PS2] */
meta_PS2_KHV, /* Kingdom Hearts 2 VAG streams */
meta_P3D, /* Prototype P3D */
meta_PS2_TK1, /* Tekken (NamCollection) */
meta_NGC_RKV, /* Legacy of Kain - Blood Omen 2 (GC) */
@ -717,6 +721,7 @@ typedef enum {
meta_DSP_ADPCMX,
meta_OGG_OPUS,
meta_IMC,
meta_GIN,
} meta_t;