diff --git a/src/coding/ea_xa_decoder.c b/src/coding/ea_xa_decoder.c index 857dd6df..d1e65f10 100644 --- a/src/coding/ea_xa_decoder.c +++ b/src/coding/ea_xa_decoder.c @@ -184,7 +184,7 @@ void decode_ea_xa_v2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspac #endif /* EA XA v1 (mono/stereo) */ -void decode_ea_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel, int is_stereo) { +void decode_ea_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo) { uint8_t frame_info; int32_t coef1, coef2; int i, sample_count, shift; @@ -194,8 +194,9 @@ void decode_ea_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing int frame_samples = 28; first_sample = first_sample % frame_samples; + /* header */ if (is_stereo) { - /* header (coefs ch0+ch1 + shift ch0+ch1) */ + /* coefs ch0+ch1 + shift ch0+ch1 */ frame_info = read_8bit(stream->offset + 0x00, stream->streamfile); coef1 = EA_XA_TABLE[(hn ? frame_info >> 4 : frame_info & 0x0F) + 0]; coef2 = EA_XA_TABLE[(hn ? frame_info >> 4 : frame_info & 0x0F) + 4]; @@ -203,7 +204,7 @@ void decode_ea_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing frame_info = read_8bit(stream->offset + 0x01, stream->streamfile); shift = (hn ? frame_info >> 4 : frame_info & 0x0F) + 8; } else { - /* header (coefs + shift ch0) */ + /* coefs + shift ch0 */ frame_info = read_8bit(stream->offset + 0x00, stream->streamfile); coef1 = EA_XA_TABLE[(frame_info >> 4) + 0]; coef2 = EA_XA_TABLE[(frame_info >> 4) + 4]; @@ -233,7 +234,7 @@ void decode_ea_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing stream->offset += frame_size; } -/* Maxis EA-XA v1 (mono+stereo) with byte-interleave layout in stereo mode */ +/* Maxis EA-XA v1 (mono/stereo) with byte-interleave layout in stereo mode */ void decode_maxis_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { uint8_t frame_info; int32_t coef1, coef2; diff --git a/src/meta/ea_eaac.c b/src/meta/ea_eaac.c index cd7ac390..99c834b3 100644 --- a/src/meta/ea_eaac.c +++ b/src/meta/ea_eaac.c @@ -36,7 +36,6 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAMFILE* sf_data, off_t header_offset, off_t start_offset, meta_t meta_type, int standalone); static VGMSTREAM *parse_s10a_header(STREAMFILE* sf, off_t offset, uint16_t target_index, off_t ast_offset); -VGMSTREAM * init_vgmstream_gin_header(STREAMFILE* sf, off_t offset); /* .SNR+SNS - from EA latest games (~2005-2010), v0 header */ @@ -375,12 +374,13 @@ fail: /* EA HDR/STH/DAT - seen in older 7th gen games, used for storing speech */ VGMSTREAM * init_vgmstream_ea_hdr_sth_dat(STREAMFILE* sf) { int target_stream = sf->stream_index; + uint32_t snr_offset, sns_offset, block_size; + uint16_t sth_offset, sth_offset2; uint8_t userdata_size, total_sounds, block_id; - off_t snr_offset, sns_offset, sth_offset, sth_offset2; - size_t dat_size, block_size; - STREAMFILE *datFile = NULL, *sthFile = NULL; + size_t dat_size; + STREAMFILE *sf_dat = NULL, *sf_sth = NULL; VGMSTREAM *vgmstream; - int32_t(*read_32bit)(off_t, STREAMFILE*); + uint32_t(*read_u32)(off_t, STREAMFILE*); /* 0x00: ID */ /* 0x02: parameters (userdata size, ...) */ @@ -388,46 +388,46 @@ VGMSTREAM * init_vgmstream_ea_hdr_sth_dat(STREAMFILE* sf) { /* 0x04: sub-ID (used for different police voices in NFS games) */ /* 0x08: sample repeat (alt number of files?) */ /* 0x09: block size (always zero?) */ - /* 0x0A: number of blocks (related to size?) */ - /* 0x0C: number of sub-banks (always zero?) */ - /* 0x0E: padding */ + /* 0x0a: number of blocks (related to size?) */ + /* 0x0c: number of sub-banks (always zero?) */ + /* 0x0e: padding */ /* 0x10: table start */ if (!check_extensions(sf, "hdr")) goto fail; - if (read_8bit(0x09, sf) != 0) + if (read_u8(0x09, sf) != 0) goto fail; - if (read_32bitBE(0x0c, sf) != 0) + if (read_u32be(0x0c, sf) != 0) goto fail; /* first offset is always zero */ - if (read_16bitBE(0x10, sf) != 0) + if (read_u16be(0x10, sf) != 0) goto fail; - sthFile = open_streamfile_by_ext(sf, "sth"); - if (!sthFile) + sf_sth = open_streamfile_by_ext(sf, "sth"); + if (!sf_sth) goto fail; - datFile = open_streamfile_by_ext(sf, "dat"); - if (!datFile) + sf_dat = open_streamfile_by_ext(sf, "dat"); + if (!sf_dat) goto fail; /* STH always starts with the first offset of zero */ - sns_offset = read_32bitBE(0x00, sthFile); + sns_offset = read_u32be(0x00, sf_sth); if (sns_offset != 0) goto fail; /* check if DAT starts with a correct SNS block */ - block_id = read_8bit(0x00, datFile); + block_id = read_u8(0x00, sf_dat); if (block_id != EAAC_BLOCKID0_DATA && block_id != EAAC_BLOCKID0_END) goto fail; - userdata_size = read_8bit(0x02, sf); - total_sounds = read_8bit(0x03, sf); + userdata_size = read_u8(0x02, sf) & 0x0F; + total_sounds = read_u8(0x03, sf); - if (read_8bit(0x08, sf) > total_sounds) + if (read_u8(0x08, sf) > total_sounds) goto fail; if (target_stream == 0) target_stream = 1; @@ -435,14 +435,14 @@ VGMSTREAM * init_vgmstream_ea_hdr_sth_dat(STREAMFILE* sf) { goto fail; /* offsets in HDR are always big endian */ - sth_offset = (uint16_t)read_16bitBE(0x10 + (0x02 + userdata_size) * (target_stream - 1), sf); + sth_offset = read_u16be(0x10 + (0x02 + userdata_size) * (target_stream - 1), sf); #if 0 snr_offset = sth_offset + 0x04; - sns_offset = read_32bit(sth_offset + 0x00, sthFile); + sns_offset = read_u32(sth_offset + 0x00, sf_sth); #else - /* we can't reliably detect byte endianness so we're going to find the sound the hacky way */ - dat_size = get_streamfile_size(datFile); + /* overly intricate way to detect byte endianness because of the simplicity of HDR format */ + dat_size = get_streamfile_size(sf_dat); snr_offset = 0; sns_offset = 0; @@ -456,8 +456,8 @@ VGMSTREAM * init_vgmstream_ea_hdr_sth_dat(STREAMFILE* sf) { if (sns_offset >= dat_size) goto fail; - block_id = read_8bit(sns_offset, datFile); - block_size = read_32bitBE(sns_offset, datFile) & 0x00FFFFFF; + block_id = read_u8(sns_offset, sf_dat); + block_size = read_u32be(sns_offset, sf_dat) & 0x00FFFFFF; if (block_size == 0) goto fail; @@ -470,36 +470,37 @@ VGMSTREAM * init_vgmstream_ea_hdr_sth_dat(STREAMFILE* sf) { break; } - sth_offset2 = (uint16_t)read_16bitBE(0x10 + (0x02 + userdata_size) * 1, sf); - if (sns_offset == read_32bitBE(sth_offset2, sthFile)) { - read_32bit = read_32bitBE; - } else if (sns_offset == read_32bitLE(sth_offset2, sthFile)) { - read_32bit = read_32bitLE; + sns_offset = align_size_to_block(sns_offset, 0x40); + sth_offset2 = read_u16be(0x10 + (0x02 + userdata_size) * 1, sf); + if (sns_offset == read_u32be(sth_offset2, sf_sth)) { + read_u32 = read_u32be; + } else if (sns_offset == read_u32le(sth_offset2, sf_sth)) { + read_u32 = read_u32le; } else { goto fail; } snr_offset = sth_offset + 0x04; - sns_offset = read_32bit(sth_offset + 0x00, sthFile); + sns_offset = read_u32(sth_offset + 0x00, sf_sth); } #endif - block_id = read_8bit(sns_offset, datFile); + block_id = read_u8(sns_offset, sf_dat); if (block_id != EAAC_BLOCKID0_DATA && block_id != EAAC_BLOCKID0_END) goto fail; - vgmstream = init_vgmstream_eaaudiocore_header(sthFile, datFile, snr_offset, sns_offset, meta_EA_SNR_SNS, 0); + vgmstream = init_vgmstream_eaaudiocore_header(sf_sth, sf_dat, snr_offset, sns_offset, meta_EA_SNR_SNS, 0); if (!vgmstream) goto fail; vgmstream->num_streams = total_sounds; - close_streamfile(sthFile); - close_streamfile(datFile); + close_streamfile(sf_sth); + close_streamfile(sf_dat); return vgmstream; fail: - close_streamfile(sthFile); - close_streamfile(datFile); + close_streamfile(sf_sth); + close_streamfile(sf_dat); return NULL; } @@ -507,7 +508,7 @@ fail: static STREAMFILE *open_mapfile_pair(STREAMFILE* sf, int track, int num_tracks) { static const char *const mapfile_pairs[][2] = { /* standard cases, replace map part with mus part (from the end to preserve prefixes) */ - {"game.mpf", "Game_Stream.mus"}, /* Skate */ + {"game.mpf", "Game_Stream.mus"}, /* Skate 1/2/3 */ {"ipod.mpf", "Ipod_Stream.mus"}, {"world.mpf", "World_Stream.mus"}, {"FreSkate.mpf", "track.mus,ram.mus"}, /* Skate It */ @@ -606,37 +607,41 @@ static STREAMFILE *open_mapfile_pair(STREAMFILE* sf, int track, int num_tracks) /* EA MPF/MUS combo - used in older 7th gen games for storing interactive music */ VGMSTREAM * init_vgmstream_ea_mpf_mus_eaac(STREAMFILE* sf) { - uint32_t num_tracks, track_start, track_hash = 0, mus_sounds, mus_stream = 0; + uint32_t num_tracks, track_start, track_checksum = 0, mus_sounds, mus_stream = 0; + uint32_t tracks_table, samples_table, eof_offset, table_offset, entry_offset, snr_offset, sns_offset; + uint16_t num_subbanks; uint8_t version, sub_version; - off_t tracks_table, samples_table, eof_offset, table_offset, entry_offset, snr_offset, sns_offset; - int32_t(*read_32bit)(off_t, STREAMFILE*); STREAMFILE *musFile = NULL; VGMSTREAM *vgmstream = NULL; int i; int target_stream = sf->stream_index, total_streams, is_ram = 0; + uint32_t(*read_u32)(off_t, STREAMFILE *); + uint16_t(*read_u16)(off_t, STREAMFILE *); /* check extension */ if (!check_extensions(sf, "mpf")) goto fail; /* detect endianness */ - if (read_32bitBE(0x00, sf) == 0x50464478) { /* "PFDx" */ - read_32bit = read_32bitBE; - } else if (read_32bitLE(0x00, sf) == 0x50464478) { /* "xDFP" */ - read_32bit = read_32bitLE; + if (read_u32be(0x00, sf) == 0x50464478) { /* "PFDx" */ + read_u32 = read_u32be; + read_u16 = read_u16be; + } else if (read_u32le(0x00, sf) == 0x50464478) { /* "xDFP" */ + read_u32 = read_u32le; + read_u16 = read_u16le; } else { goto fail; } - version = read_8bit(0x04, sf); - sub_version = read_8bit(0x05, sf); + version = read_u8(0x04, sf); + sub_version = read_u8(0x05, sf); if (version != 5 || sub_version < 2 || sub_version > 3) goto fail; - num_tracks = read_8bit(0x0d, sf); + num_tracks = read_u8(0x0d, sf); - tracks_table = read_32bit(0x2c, sf); - samples_table = read_32bit(0x34, sf); - eof_offset = read_32bit(0x38, sf); + tracks_table = read_u32(0x2c, sf); + samples_table = read_u32(0x34, sf); + eof_offset = read_u32(0x38, sf); total_streams = (eof_offset - samples_table) / 0x08; if (target_stream == 0) target_stream = 1; @@ -644,24 +649,30 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus_eaac(STREAMFILE* sf) { goto fail; for (i = num_tracks - 1; i >= 0; i--) { - entry_offset = read_32bit(tracks_table + i * 0x04, sf) * 0x04; - track_start = read_32bit(entry_offset + 0x00, sf); + entry_offset = read_u32(tracks_table + i * 0x04, sf) * 0x04; + track_start = read_u32(entry_offset + 0x00, sf); if (track_start == 0 && i != 0) continue; /* empty track */ if (track_start <= target_stream - 1) { - track_hash = read_32bitBE(entry_offset + 0x08, sf); - is_ram = (track_hash == 0xF1F1F1F1); + num_subbanks = read_u16(entry_offset + 0x04, sf); + track_checksum = read_u32be(entry_offset + 0x08, sf); + is_ram = (num_subbanks != 0); + + if (num_subbanks > 1) { + VGM_LOG("EA MPF: Found EAAC MPF with more than 1 RAM sub-bank.\n"); + goto fail; + } /* checks to distinguish it from older versions */ if (is_ram) { - if (read_32bitBE(entry_offset + 0x0c, sf) != 0x00) + if (read_u32(entry_offset + 0x0c, sf) != 0x00) goto fail; - track_hash = read_32bitBE(entry_offset + 0x14, sf); + track_checksum = read_u32be(entry_offset + 0x14, sf); } else { - if (read_32bitBE(entry_offset + 0x0c, sf) == 0x00) + if (read_u32(entry_offset + 0x0c, sf) == 0x00) goto fail; } @@ -675,13 +686,13 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus_eaac(STREAMFILE* sf) { if (!musFile) goto fail; - if (read_32bitBE(0x00, musFile) != track_hash) + if (read_u32be(0x00, musFile) != track_checksum) goto fail; /* sample offsets table is still there but it just holds SNS offsets, it's of little use to us */ /* MUS file has a header, however */ if (sub_version == 2) { - if (read_32bit(0x04, musFile) != 0x00) + if (read_u32(0x04, musFile) != 0x00) goto fail; /* @@ -691,11 +702,11 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus_eaac(STREAMFILE* sf) { */ table_offset = 0x08; entry_offset = table_offset + mus_stream * 0x0c; - snr_offset = read_32bit(entry_offset + 0x04, musFile); - sns_offset = read_32bit(entry_offset + 0x08, musFile); + snr_offset = read_u32(entry_offset + 0x04, musFile); + sns_offset = read_u32(entry_offset + 0x08, musFile); } else if (sub_version == 3) { - /* number of files is always little endian */ - mus_sounds = read_32bitLE(0x04, musFile); + /* number of samples is always little endian */ + mus_sounds = read_u32le(0x04, musFile); if (mus_stream >= mus_sounds) goto fail; @@ -706,9 +717,9 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus_eaac(STREAMFILE* sf) { } /* - * 0x00: hash? + * 0x00: checksum * 0x04: index - * 0x06: zero + * 0x06: sub-index * 0x08: SNR offset * 0x0c: SNS offset * 0x10: SNR size @@ -717,8 +728,8 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus_eaac(STREAMFILE* sf) { */ table_offset = 0x28; entry_offset = table_offset + mus_stream * 0x1c; - snr_offset = read_32bit(entry_offset + 0x08, musFile) * 0x10; - sns_offset = read_32bit(entry_offset + 0x0c, musFile) * 0x80; + snr_offset = read_u32(entry_offset + 0x08, musFile) * 0x10; + sns_offset = read_u32(entry_offset + 0x0c, musFile) * 0x80; } else { goto fail; } @@ -741,6 +752,7 @@ fail: VGMSTREAM * init_vgmstream_ea_tmx(STREAMFILE* sf) { uint32_t num_sounds, sound_type, table_offset, data_offset, entry_offset, sound_offset; VGMSTREAM *vgmstream = NULL; + STREAMFILE *temp_sf = NULL; int target_stream = sf->stream_index; uint32_t(*read_u32)(off_t, STREAMFILE *); @@ -769,8 +781,12 @@ VGMSTREAM * init_vgmstream_ea_tmx(STREAMFILE* sf) { switch (sound_type) { case 0x47494E20: /* "GIN " */ - vgmstream = init_vgmstream_gin_header(sf, sound_offset); + temp_sf = setup_subfile_streamfile(sf, sound_offset, get_streamfile_size(sf) - sound_offset, "gin"); + if (!temp_sf) goto fail; + + vgmstream = init_vgmstream_gin(temp_sf); if (!vgmstream) goto fail; + close_streamfile(temp_sf); break; case 0x534E5220: /* "SNR " */ vgmstream = init_vgmstream_eaaudiocore_header(sf, NULL, sound_offset, 0x00, meta_EA_SNR_SNS, 0); @@ -784,6 +800,7 @@ VGMSTREAM * init_vgmstream_ea_tmx(STREAMFILE* sf) { return vgmstream; fail: + close_streamfile(temp_sf); return NULL; } diff --git a/src/meta/ea_schl.c b/src/meta/ea_schl.c index 75fb332b..0b44bced 100644 --- a/src/meta/ea_schl.c +++ b/src/meta/ea_schl.c @@ -391,10 +391,10 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE* sf) { if (target_entry_offset == 0) goto fail; - /* 0x00: type (0x00 - normal, 0x01 - streamed, 0x02 - streamed looped) */ + /* 0x00: type (0x00 - RAM, 0x01 - streamed, 0x02 - streamed looped) */ /* 0x01: priority */ /* 0x02: padding */ - /* 0x04: index for normal sounds, offset for streamed sounds */ + /* 0x04: index for RAM sounds, offset for streamed sounds */ /* 0x08: loop offset for streamed sounds */ sound_type = read_8bit(target_entry_offset + 0x00, sf); @@ -474,15 +474,14 @@ fail: return NULL; } -/* EA HDR/DAT v1 (2004-2005) - used for storing speech and other streamed sounds (except for music) */ -VGMSTREAM * init_vgmstream_ea_hdr_dat(STREAMFILE* sf) { - VGMSTREAM* vgmstream; - STREAMFILE* sf_dat = NULL; +/* EA HDR/DAT v1 (2004-2005) - used for storing speech, sometimes streamed SFX */ +VGMSTREAM *init_vgmstream_ea_hdr_dat(STREAMFILE *sf) { + VGMSTREAM *vgmstream; + STREAMFILE *sf_dat = NULL, *temp_sf = NULL; int target_stream = sf->stream_index; - uint32_t offset_mult; + uint32_t offset_mult, sound_offset, sound_size; uint8_t userdata_size, total_sounds; size_t dat_size; - off_t schl_offset; /* checks */ if (!check_extensions(sf, "hdr")) @@ -493,10 +492,10 @@ VGMSTREAM * init_vgmstream_ea_hdr_dat(STREAMFILE* sf) { /* 0x02: sub-ID (used for different police voices in NFS games) */ /* 0x04: parameters (userdata size, ...) */ /* 0x05: number of files */ - /* 0x06: alt number of files? */ - /* 0x07: offset multiplier flag */ - /* 0x08: combined size of all sounds without padding divided by offset mult */ - /* 0x0a: zero */ + /* 0x06: sample repeat (alt number of files?) */ + /* 0x07: block size (offset multiplier) */ + /* 0x08: number of blocks (DAT size divided by block size) */ + /* 0x0a: number of sub-banks */ /* 0x0c: table start */ /* no nice way to validate these so we do what we can */ @@ -507,12 +506,13 @@ VGMSTREAM * init_vgmstream_ea_hdr_dat(STREAMFILE* sf) { if (read_u16be(0x0c, sf) != 0) goto fail; - /* must be accompanied by DAT file with SCHl sounds */ + /* must be accompanied by DAT file with SCHl or VAG sounds */ sf_dat = open_streamfile_by_ext(sf, "dat"); if (!sf_dat) goto fail; - if (read_u32be(0x00, sf_dat) != EA_BLOCKID_HEADER) + if (read_u32be(0x00, sf_dat) != EA_BLOCKID_HEADER && + read_u32be(0x00, sf_dat) != 0x56414770) goto fail; userdata_size = read_u8(0x04, sf) & 0x0F; @@ -532,13 +532,23 @@ VGMSTREAM * init_vgmstream_ea_hdr_dat(STREAMFILE* sf) { goto fail; /* offsets are always big endian */ - schl_offset = read_u16be(0x0C + (0x02 + userdata_size) * (target_stream - 1), sf) * offset_mult; - if (read_32bitBE(schl_offset, sf_dat) != EA_BLOCKID_HEADER) - goto fail; + sound_offset = read_u16be(0x0C + (0x02 + userdata_size) * (target_stream - 1), sf) * offset_mult; + if (read_u32be(sound_offset, sf_dat) == EA_BLOCKID_HEADER) { /* "SCHl" */ + vgmstream = parse_schl_block(sf_dat, sound_offset, 0); + if (!vgmstream) + goto fail; + } else if (read_u32be(sound_offset, sf_dat) == 0x56414770) { /* "VAGp" */ + /* Need for Speed: Hot Pursuit 2 (PS2) */ + sound_size = read_u32be(sound_offset + 0x0c, sf_dat) + 0x30; + temp_sf = setup_subfile_streamfile(sf_dat, sound_offset, sound_size, "vag"); + if (!temp_sf) goto fail; - vgmstream = parse_schl_block(sf_dat, schl_offset, 0); - if (!vgmstream) + vgmstream = init_vgmstream_vag(temp_sf); + if (!vgmstream) goto fail; + close_streamfile(temp_sf); + } else { goto fail; + } vgmstream->num_streams = total_sounds; close_streamfile(sf_dat); @@ -546,6 +556,7 @@ VGMSTREAM * init_vgmstream_ea_hdr_dat(STREAMFILE* sf) { fail: close_streamfile(sf_dat); + close_streamfile(temp_sf); return NULL; } @@ -554,10 +565,9 @@ VGMSTREAM * init_vgmstream_ea_hdr_dat_v2(STREAMFILE* sf) { VGMSTREAM *vgmstream; STREAMFILE *sf_dat = NULL; int target_stream = sf->stream_index; - uint32_t offset_mult; + uint32_t offset_mult, sound_offset; uint8_t userdata_size, total_sounds; size_t dat_size; - off_t schl_offset; /* checks */ if (!check_extensions(sf, "hdr")) @@ -570,9 +580,9 @@ VGMSTREAM * init_vgmstream_ea_hdr_dat_v2(STREAMFILE* sf) { /* 0x04: sub-ID (used for different police voices in NFS games) */ /* 0x08: sample repeat (alt number of files?) */ /* 0x09: block size (offset multiplier) */ - /* 0x0A: number of blocks (DAT size divided by block size) */ - /* 0x0C: number of sub-banks (always zero?) */ - /* 0x0E: padding */ + /* 0x0a: number of blocks (DAT size divided by block size) */ + /* 0x0c: number of sub-banks (always zero?) */ + /* 0x0e: padding */ /* 0x10: table start */ /* no nice way to validate these so we do what we can */ @@ -608,11 +618,11 @@ VGMSTREAM * init_vgmstream_ea_hdr_dat_v2(STREAMFILE* sf) { goto fail; /* offsets are always big endian */ - schl_offset = read_u16be(0x10 + (0x02 + userdata_size) * (target_stream - 1), sf) * offset_mult; - if (read_32bitBE(schl_offset, sf_dat) != EA_BLOCKID_HEADER) + sound_offset = read_u16be(0x10 + (0x02 + userdata_size) * (target_stream - 1), sf) * offset_mult; + if (read_u32be(sound_offset, sf_dat) != EA_BLOCKID_HEADER) goto fail; - vgmstream = parse_schl_block(sf_dat, schl_offset, 0); + vgmstream = parse_schl_block(sf_dat, sound_offset, 0); if (!vgmstream) goto fail; @@ -733,6 +743,7 @@ static STREAMFILE* open_mapfile_pair(STREAMFILE* sf, int track, int num_tracks) } /* EA MAP/MUS combo - used in older games for interactive music (for EA's PathFinder tool) */ +/* seen in Need for Speed II, Need for Speed III: Hot Pursuit, SSX */ VGMSTREAM * init_vgmstream_ea_map_mus(STREAMFILE* sf) { VGMSTREAM* vgmstream = NULL; STREAMFILE* sf_mus = NULL; @@ -758,14 +769,14 @@ VGMSTREAM * init_vgmstream_ea_map_mus(STREAMFILE* sf) { * 0x04: version * 0x05: starting node * 0x06: number of nodes - * 0x07: number of sections + * 0x07: number of events * 0x08: three zeroes - * 0x0b: number of events + * 0x0b: number of sections * 0x0c: data start */ num_sounds = read_8bit(0x06, sf); - num_sections = read_8bit(0x07, sf); - num_events = read_8bit(0x0b, sf); + num_events = read_8bit(0x07, sf); + num_sections = read_8bit(0x0b, sf); section_offset = 0x0c; /* section 1: nodes, contains information about segment playback order */ @@ -802,24 +813,25 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE* sf) { VGMSTREAM* vgmstream = NULL; STREAMFILE* sf_mus = NULL; segmented_layout_data *data_s = NULL; - uint32_t track_start, track_end = 0, track_hash = 0, tracks_table, samples_table = 0, section_offset, entry_offset = 0, eof_offset = 0, off_mult, sound_offset; - uint16_t num_nodes; + uint32_t track_start, track_end = 0, track_checksum = 0; + uint32_t tracks_table, samples_table = 0, section_offset, entry_offset = 0, eof_offset = 0, off_mult, sound_offset; + uint16_t num_nodes, num_subbanks = 0; uint8_t version, sub_version, num_tracks, num_sections, num_events, num_routers, num_vars, subentry_num = 0; - uint32_t(*read_u32)(off_t, STREAMFILE*); - uint16_t(*read_u16)(off_t, STREAMFILE*); int i; int target_stream = sf->stream_index, total_streams, big_endian, is_bnk = 0; + uint32_t(*read_u32)(off_t, STREAMFILE *); + uint16_t(*read_u16)(off_t, STREAMFILE *); /* check extension */ if (!check_extensions(sf, "mpf")) goto fail; /* detect endianness */ - if (read_32bitBE(0x00, sf) == 0x50464478) { /* "PFDx" */ + if (read_u32be(0x00, sf) == 0x50464478) { /* "PFDx" */ read_u32 = read_u32be; read_u16 = read_u16be; big_endian = 1; - } else if (read_32bitLE(0x00, sf) == 0x50464478) { /* "xDFP" */ + } else if (read_u32le(0x00, sf) == 0x50464478) { /* "xDFP" */ read_u32 = read_u32le; read_u16 = read_u16le; big_endian = 0; @@ -840,28 +852,47 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE* sf) { num_vars = read_u8(0x11, sf); num_nodes = read_u16(0x12, sf); - /* HACK: number of sub-entries for nodes and events is stored in bitstreams that are different in LE and BE */ - /* I can't figure it out, so let's just use a workaround for now */ + /* Some structs here use C bitfields which are different on LE and BE AND their + * implementation is compiler dependent, fun times. + * Earlier versions don't have section offsets so we have to go through all of them + * to get to the samples table. */ if (target_stream == 0) target_stream = 1; - if (version == 3) - /* SSX Tricky (v3.1), Harry Potter and the Chamber of Secrets (v3.4) */ { - /* we need to go through all the sections to get to the samples table */ - if (sub_version != 1 && sub_version != 2 && sub_version != 4) - goto fail; - - /* get the last entry offset */ + if (version == 3 && (sub_version == 1 || sub_version == 2)) + /* SSX Tricky, Sled Storm */ { section_offset = 0x24; entry_offset = read_u16(section_offset + (num_nodes - 1) * 0x02, sf) * 0x04; - if (sub_version == 1 || sub_version == 2) { - subentry_num = read_u8(entry_offset + 0x0b, sf); - } else if (sub_version == 4) { - if (big_endian) { - subentry_num = (read_u32be(entry_offset + 0x04, sf) >> 19) & 0xFF; - } else { - subentry_num = (read_u32be(entry_offset + 0x04, sf) >> 16) & 0xFF; - } + subentry_num = read_u8(entry_offset + 0x0b, sf); + section_offset = entry_offset + 0x0c + subentry_num * 0x04; + + section_offset += align_size_to_block(num_events * num_tracks * num_sections, 0x04); + section_offset += num_routers * 0x04; + section_offset += num_vars * 0x04; + + tracks_table = read_u32(section_offset, sf) * 0x04; + samples_table = tracks_table + num_tracks * 0x04; + eof_offset = get_streamfile_size(sf); + total_streams = (eof_offset - samples_table) / 0x08; + off_mult = 0x04; + + track_start = total_streams; + + for (i = num_tracks - 1; i >= 0; i--) { + track_end = track_start; + track_start = read_u32(tracks_table + i * 0x04, sf) * 0x04; + track_start = (track_start - samples_table) / 0x08; + if (track_start <= target_stream - 1) + break; + } + } else if (version == 3 && sub_version == 4) + /* Harry Potter and the Chamber of Secrets, Shox */ { + section_offset = 0x24; + entry_offset = read_u16(section_offset + (num_nodes - 1) * 0x02, sf) * 0x04; + if (big_endian) { + subentry_num = (read_u32be(entry_offset + 0x04, sf) >> 19) & 0x1F; + } else { + subentry_num = (read_u32be(entry_offset + 0x04, sf) >> 16) & 0x1F; } section_offset = entry_offset + 0x0c + subentry_num * 0x04; @@ -870,14 +901,8 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE* sf) { section_offset += num_vars * 0x04; tracks_table = read_u32(section_offset, sf) * 0x04; - if (sub_version == 1 || sub_version == 2) - samples_table = tracks_table + num_tracks * 0x04; - else if (sub_version == 4) - samples_table = tracks_table + (num_tracks + 1) * 0x04; - if (sub_version == 1 || sub_version == 2) - eof_offset = get_streamfile_size(sf); - else if (sub_version == 4) - eof_offset = read_u32(tracks_table + num_tracks * 0x04, sf) * 0x04; + samples_table = tracks_table + (num_tracks + 1) * 0x04; + eof_offset = read_u32(tracks_table + num_tracks * 0x04, sf) * 0x04; total_streams = (eof_offset - samples_table) / 0x08; off_mult = 0x04; @@ -892,23 +917,20 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE* sf) { } } else if (version == 4) { /* Need for Speed: Underground 2, SSX 3, Harry Potter and the Prisoner of Azkaban */ - /* we need to go through all the sections to get to the samples table */ - /* get the last entry offset */ section_offset = 0x20; entry_offset = read_u16(section_offset + (num_nodes - 1) * 0x02, sf) * 0x04; if (big_endian) { - subentry_num = (read_u32be(entry_offset + 0x04, sf) >> 15) & 0xFF; + subentry_num = (read_u32be(entry_offset + 0x04, sf) >> 15) & 0x0F; } else { - subentry_num = (read_u32be(entry_offset + 0x04, sf) >> 20) & 0xFF; + subentry_num = (read_u32be(entry_offset + 0x04, sf) >> 20) & 0x0F; } section_offset = entry_offset + 0x10 + subentry_num * 0x04; - /* get the last entry offset */ entry_offset = read_u16(section_offset + (num_events - 1) * 0x02, sf) * 0x04; if (big_endian) { - subentry_num = (read_u32be(entry_offset + 0x0c, sf) >> 10) & 0xFF; + subentry_num = (read_u32be(entry_offset + 0x0c, sf) >> 10) & 0x3F; } else { - subentry_num = (read_u32be(entry_offset + 0x0c, sf) >> 8) & 0xFF; + subentry_num = (read_u32be(entry_offset + 0x0c, sf) >> 8) & 0x3F; } section_offset = entry_offset + 0x10 + subentry_num * 0x10; @@ -933,7 +955,7 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE* sf) { break; } } else if (version == 5) { - /* Need for Speed: Most Wanted, Need for Speed: Carbon */ + /* Need for Speed: Most Wanted, Need for Speed: Carbon, SSX on Tour */ tracks_table = read_u32(0x2c, sf); samples_table = read_u32(0x34, sf); eof_offset = read_u32(0x38, sf); @@ -951,8 +973,9 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE* sf) { continue; /* empty track */ if (track_start <= target_stream - 1) { - track_hash = read_u32be(entry_offset + 0x08, sf); - is_bnk = (track_hash == 0xF1F1F1F1); + num_subbanks = read_u16(entry_offset + 0x04, sf); + track_checksum = read_u32be(entry_offset + 0x08, sf); + is_bnk = (num_subbanks != 0); /* checks to distinguish it from SNR/SNS version */ if (is_bnk) { @@ -1017,11 +1040,15 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE* sf) { } if (version == 5) { - track_hash = read_u32be(entry_offset + 0x14 + 0x10 * bnk_index, sf); - if (read_u32be(0x00, sf_mus) != track_hash) + track_checksum = read_u32be(entry_offset + 0x14 + 0x10 * bnk_index, sf); + if (read_u32be(0x00, sf_mus) != track_checksum) goto fail; } + if (read_u32be(bnk_offset, sf_mus) != EA_BNK_HEADER_LE && + read_u32be(bnk_offset, sf_mus) != EA_BNK_HEADER_BE) + goto fail; + /* play until the next entry in MPF track or the end of BNK */ if (target_stream < track_end) { next_entry = read_u32(samples_table + (target_stream - 0) * 0x08 + 0x00, sf); @@ -1050,11 +1077,11 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE* sf) { if (!vgmstream) goto fail; } else { - if (version == 5 && read_u32be(0x00, sf_mus) != track_hash) + if (version == 5 && read_u32be(0x00, sf_mus) != track_checksum) goto fail; sound_offset *= off_mult;; - if (read_32bitBE(sound_offset, sf_mus) != EA_BLOCKID_HEADER) + if (read_u32be(sound_offset, sf_mus) != EA_BLOCKID_HEADER) goto fail; vgmstream = parse_schl_block(sf_mus, sound_offset, 0); diff --git a/src/meta/gin.c b/src/meta/gin.c index a7e1f85b..08694536 100644 --- a/src/meta/gin.c +++ b/src/meta/gin.c @@ -1,26 +1,17 @@ #include "meta.h" #include "../coding/coding.h" -VGMSTREAM * init_vgmstream_gin_header(STREAMFILE *streamFile, off_t offset); - /* .gin - EA engine sounds [Need for Speed: Most Wanted (multi)] */ VGMSTREAM * init_vgmstream_gin(STREAMFILE *streamFile) { - if (!check_extensions(streamFile, "gin")) - goto fail; - - return init_vgmstream_gin_header(streamFile, 0x00); - -fail: - return NULL; -} - -VGMSTREAM * init_vgmstream_gin_header(STREAMFILE *streamFile, off_t offset) { - VGMSTREAM * vgmstream = NULL; + VGMSTREAM *vgmstream = NULL; off_t start_offset; int loop_flag, channel_count, sample_rate, num_samples; + if (!check_extensions(streamFile, "gin")) + goto fail; + /* checks */ - if (read_32bitBE(offset + 0x00, streamFile) != 0x476E7375) /* "Gnsu" */ + if (read_32bitBE(0x00, streamFile) != 0x476E7375) /* "Gnsu" */ goto fail; /* contains mapped values for engine RPM sounds but we'll just play the whole thing */ @@ -30,11 +21,11 @@ VGMSTREAM * init_vgmstream_gin_header(STREAMFILE *streamFile, off_t offset) { /* 0x14: RPM ??? table size */ /* always LE even on X360/PS3 */ - num_samples = read_32bitLE(offset + 0x18, streamFile); - sample_rate = read_32bitLE(offset + 0x1c, streamFile); - start_offset = offset + 0x20 + - (read_32bitLE(offset + 0x10, streamFile) + 1) * 0x04 + - (read_32bitLE(offset + 0x14, streamFile) + 1) * 0x04; + 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; diff --git a/src/meta/ubi_sb.c b/src/meta/ubi_sb.c index 8115405d..5bc31a09 100644 --- a/src/meta/ubi_sb.c +++ b/src/meta/ubi_sb.c @@ -724,7 +724,7 @@ static VGMSTREAM *init_vgmstream_ubi_dat_main(ubi_sb_header *sb, STREAMFILE *sf_ } case 0x04: { /* standard WAV */ if (!sb->is_external) { - VGM_LOG("Ubi DAT: Found RAM stream_type 0x04\n"); + VGM_LOG("UBI DAT: Found RAM stream_type 0x04\n"); goto fail; } @@ -1157,7 +1157,7 @@ static VGMSTREAM* init_vgmstream_ubi_sb_base(ubi_sb_header* sb, STREAMFILE* sf_h size_t bytes, chunk_size; off_t header_offset; - VGM_ASSERT(sb->is_streamed, "Ubi SB: Raw XMA used for streamed sound\n"); + VGM_ASSERT(sb->is_streamed, "UBI SB: Raw XMA used for streamed sound\n"); /* get XMA header from extra section */ chunk_size = 0x20; @@ -1741,7 +1741,7 @@ fail: } static uint32_t ubi_ps2_pitch_to_freq(uint32_t pitch) { - /* old PS2 games store sample rate in a weird range of 0-65536 remapped from 0-48000 */ + /* old PS2 games store sample rate in a weird range of 0-0x10000 remapped from 0-48000 */ /* strangely, audio res type does have sample rate value but it's unused */ double sample_rate = (((double)pitch / 65536) * 48000); return (uint32_t)ceil(sample_rate); @@ -1803,7 +1803,7 @@ static int parse_type_layer_ps2_old(ubi_sb_header* sb, off_t offset, STREAMFILE* } if (sb->layer_count > SB_MAX_LAYER_COUNT) { - VGM_LOG("Ubi SB: incorrect layer count\n"); + VGM_LOG("UBI SB: incorrect layer count\n"); goto fail; } @@ -1924,7 +1924,7 @@ static int parse_type_sequence(ubi_sb_header* sb, off_t offset, STREAMFILE* sf) /* sequence chain */ sb->type = UBI_SEQUENCE; if (sb->cfg.sequence_sequence_count == 0) { - VGM_LOG("Ubi SB: sequence not configured at %x\n", (uint32_t)offset); + VGM_LOG("UBI SB: sequence not configured at %x\n", (uint32_t)offset); goto fail; } @@ -1934,7 +1934,7 @@ static int parse_type_sequence(ubi_sb_header* sb, off_t offset, STREAMFILE* sf) sb->sequence_count = read_32bit(offset + sb->cfg.sequence_sequence_count, sf); if (sb->sequence_count > SB_MAX_CHAIN_COUNT) { - VGM_LOG("Ubi SB: incorrect sequence count %i vs %i\n", sb->sequence_count, SB_MAX_CHAIN_COUNT); + VGM_LOG("UBI SB: incorrect sequence count %i vs %i\n", sb->sequence_count, SB_MAX_CHAIN_COUNT); goto fail; } @@ -1986,7 +1986,7 @@ static int parse_type_layer(ubi_sb_header* sb, off_t offset, STREAMFILE* sf) { /* layer header */ sb->type = UBI_LAYER; if (sb->cfg.layer_layer_count == 0) { - VGM_LOG("Ubi SB: layers not configured at %x\n", (uint32_t)offset); + VGM_LOG("UBI SB: layers not configured at %x\n", (uint32_t)offset); goto fail; } @@ -2007,7 +2007,7 @@ static int parse_type_layer(ubi_sb_header* sb, off_t offset, STREAMFILE* sf) { } if (sb->layer_count > SB_MAX_LAYER_COUNT) { - VGM_LOG("Ubi SB: incorrect layer count\n"); + VGM_LOG("UBI SB: incorrect layer count\n"); goto fail; } @@ -2031,7 +2031,7 @@ static int parse_type_layer(ubi_sb_header* sb, off_t offset, STREAMFILE* sf) { int num_samples = read_32bit(table_offset + sb->cfg.layer_num_samples, sf); if (sb->sample_rate != sample_rate || sb->stream_type != stream_type) { - VGM_LOG("Ubi SB: %i layer headers don't match at %x > %x\n", sb->layer_count, (uint32_t)offset, (uint32_t)table_offset); + VGM_LOG("UBI SB: %i layer headers don't match at %x > %x\n", sb->layer_count, (uint32_t)offset, (uint32_t)table_offset); if (!sb->cfg.ignore_layer_error) /* layers of different rates happens sometimes */ goto fail; } @@ -2070,7 +2070,7 @@ static int parse_type_silence(ubi_sb_header* sb, off_t offset, STREAMFILE* sf) { /* silence header */ sb->type = UBI_SILENCE; if (sb->cfg.silence_duration_int == 0 && sb->cfg.silence_duration_float == 0) { - VGM_LOG("Ubi SB: silence duration not configured at %x\n", (uint32_t)offset); + VGM_LOG("UBI SB: silence duration not configured at %x\n", (uint32_t)offset); goto fail; } @@ -2096,7 +2096,7 @@ static int parse_type_random(ubi_sb_header* sb, off_t offset, STREAMFILE* sf) { /* sequence chain */ if (sb->cfg.random_entry_size == 0) { - VGM_LOG("Ubi SB: random entry size not configured at %x\n", (uint32_t)offset); + VGM_LOG("UBI SB: random entry size not configured at %x\n", (uint32_t)offset); goto fail; } @@ -2689,8 +2689,7 @@ static void config_sb_sequence(ubi_sb_header* sb, off_t sequence_count, off_t en if (sb->is_bnm || sb->is_dat || sb->is_ps2_bnm) { sb->cfg.sequence_sequence_loop = sequence_count - 0x0c; sb->cfg.sequence_sequence_single= sequence_count - 0x08; - } - if (sb->is_blk) { + } else if (sb->is_blk) { sb->cfg.sequence_sequence_loop = sequence_count - 0x14; sb->cfg.sequence_sequence_single= sequence_count - 0x0c; } @@ -3314,7 +3313,7 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { /* Prince of Persia: The Sands of Time (2003)(PS2)-bank 0x000A0004 / 0x000A0002 (POP1 port/Demo) */ /* Tom Clancy's Rainbow Six 3 (2003)(PS2)-bank 0x000A0007 */ /* Tom Clancy's Ghost Recon 2 (2004)(PS2)-bank 0x000A0007 */ - /* Splinter Cell: Pandora Tomorrow (2006)(PS2)-bank 0x000A0008 (separate banks from main map) */ + /* Splinter Cell: Pandora Tomorrow (2004)(PS2)-bank 0x000A0008 (separate banks from main map) */ /* Prince of Persia: Warrior Within (Demo)(2004)(PS2)-bank 0x00100000 */ /* Prince of Persia: Warrior Within (2004)(PS2)-bank 0x00120009 */ if ((sb->version == 0x000A0002 && sb->platform == UBI_PS2) || @@ -3334,7 +3333,6 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { config_sb_layer_sh(sb, 0x14, 0x00, 0x06, 0x08, 0x10); config_sb_silence_i(sb, 0x18); - return 1; }