diff --git a/src/formats.c b/src/formats.c index 658713d7..d16589cd 100644 --- a/src/formats.c +++ b/src/formats.c @@ -427,6 +427,7 @@ static const char* extension_list[] = { "tgq", "thp", "tk5", + "tmx", "tra", "tun", "txth", diff --git a/src/meta/ea_eaac.c b/src/meta/ea_eaac.c index 7bb88023..440491b9 100644 --- a/src/meta/ea_eaac.c +++ b/src/meta/ea_eaac.c @@ -36,7 +36,7 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, STREAMFILE * streamData, off_t header_offset, off_t start_offset, meta_t meta_type); static size_t get_snr_size(STREAMFILE *streamFile, off_t offset); static VGMSTREAM *parse_s10a_header(STREAMFILE *streamFile, off_t offset, uint16_t target_index, off_t ast_offset); - +VGMSTREAM * init_vgmstream_gin_header(STREAMFILE *streamFile, off_t offset); /* .SNR+SNS - from EA latest games (~2008-2013), v0 header */ @@ -569,6 +569,54 @@ fail: return NULL; } +/* EA TMX - used for engine sounds in NFS games (2007-present) */ +VGMSTREAM * init_vgmstream_ea_tmx(STREAMFILE *streamFile) { + uint32_t num_sounds, sound_type; + off_t table_offset, data_offset, entry_offset, sound_offset, sns_offset; + VGMSTREAM *vgmstream = NULL; + int target_stream = streamFile->stream_index; + + if (!check_extensions(streamFile, "tmx")) + goto fail; + + /* always little endian */ + if (read_32bitLE(0x0c, streamFile) != 0x30303031) /* "0001" */ + goto fail; + + num_sounds = read_32bitLE(0x20, streamFile); + table_offset = read_32bitLE(0x58, streamFile); + data_offset = read_32bitLE(0x5c, 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 + (target_stream - 1) * 0x24; + sound_type = read_32bitLE(entry_offset + 0x00, streamFile); + sound_offset = read_32bitLE(entry_offset + 0x08, streamFile) + data_offset; + + switch (sound_type) { + case 0x47494E20: /* "GIN " */ + /* FIXME: need to get GIN size somehow */ + vgmstream = init_vgmstream_gin_header(streamFile, sound_offset); + if (!vgmstream) goto fail; + break; + case 0x534E5220: /* "SNR " */ + sns_offset = sound_offset + get_snr_size(streamFile, sound_offset); + vgmstream = init_vgmstream_eaaudiocore_header(streamFile, streamFile, sound_offset, sns_offset, meta_EA_SNR_SNS); + if (!vgmstream) goto fail; + break; + default: + goto fail; + } + + vgmstream->num_streams = num_sounds; + return vgmstream; + +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; @@ -847,7 +895,8 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST eaac.codec == EAAC_CODEC_EALAYER3_V1 || eaac.codec == EAAC_CODEC_EALAYER3_V2_PCM || eaac.codec == EAAC_CODEC_EALAYER3_V2_SPIKE || - eaac.codec == EAAC_CODEC_EAXMA)) { + eaac.codec == EAAC_CODEC_EAXMA || + eaac.codec == EAAC_CODEC_XAS)) { VGM_LOG("EA EAAC: unknown actual looping for codec %x\n", eaac.codec); goto fail; } @@ -913,8 +962,19 @@ 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_V1; - vgmstream->layout_type = layout_blocked_ea_sns; + + /* special (if hacky) loop handling, see comments */ + if (eaac.loop_start > 0) { + segmented_layout_data *data = build_segmented_eaaudiocore_looping(streamData, &eaac); + if (!data) goto fail; + vgmstream->layout_data = data; + vgmstream->coding_type = data->segments[0]->coding_type; + vgmstream->layout_type = layout_segmented; + } else { + vgmstream->coding_type = coding_EA_XAS_V1; + vgmstream->layout_type = layout_blocked_ea_sns; + } + break; #ifdef VGM_USE_MPEG @@ -1099,11 +1159,11 @@ static size_t calculate_eaac_size(VGMSTREAM *vgmstream, STREAMFILE *streamFile, * * We use the segmented layout, since the eaac_streamfile doesn't handle padding, * and the segments seem fully separate (so even skipping would probably decode wrong). */ -// todo consider better ways to handle this once more looped files for other codecs are found 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->loop_offset }; + off_t start_offset; int num_samples[2] = { eaac->loop_start, eaac->num_samples - eaac->loop_start}; int segment_count = 2; /* intro/loop */ int i; @@ -1128,6 +1188,8 @@ static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *st temp_eaac.num_samples = num_samples[i]; temp_eaac.stream_offset = offsets[i]; + start_offset = 0x00; /* must point to the custom streamfile's beginning */ + /* layers inside segments, how trippy */ data->segments[i]->layout_data = build_layered_eaaudiocore_eaxma(streamData, &temp_eaac); if (!data->segments[i]->layout_data) goto fail; @@ -1137,6 +1199,14 @@ static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *st } #endif + case EAAC_CODEC_XAS: + { + start_offset = offsets[i]; + data->segments[i]->coding_type = coding_EA_XAS_V1; + data->segments[i]->layout_type = layout_blocked_ea_sns; + break; + } + #ifdef VGM_USE_MPEG case EAAC_CODEC_EALAYER3_V1: case EAAC_CODEC_EALAYER3_V2_PCM: @@ -1144,6 +1214,8 @@ static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *st mpeg_custom_config cfg = {0}; mpeg_custom_t type = (eaac->codec == 0x05 ? MPEG_EAL31b : (eaac->codec == 0x06) ? MPEG_EAL32P : MPEG_EAL32S); + start_offset = 0x00; /* must point to the custom streamfile's beginning */ + temp_streamFile[i] = setup_eaac_streamfile(streamData, eaac->version,eaac->codec,eaac->streamed,0,0, offsets[i]); if (!temp_streamFile[i]) goto fail; @@ -1157,11 +1229,11 @@ static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *st goto fail; } - if (!vgmstream_open_stream(data->segments[i],temp_streamFile[i],0x00)) + if (!vgmstream_open_stream(data->segments[i],temp_streamFile[i], start_offset)) goto fail; //todo temp_streamFile doesn't contain EAXMA's streamfile - data->segments[i]->stream_size = calculate_eaac_size(data->segments[i], temp_streamFile[i], eaac, 0x00); + data->segments[i]->stream_size = calculate_eaac_size(data->segments[i], temp_streamFile[i], eaac, start_offset); } if (!setup_layout_segmented(data)) diff --git a/src/meta/ea_schl.c b/src/meta/ea_schl.c index 16d3c57e..df235767 100644 --- a/src/meta/ea_schl.c +++ b/src/meta/ea_schl.c @@ -823,7 +823,7 @@ fail: static VGMSTREAM * parse_bnk_header(STREAMFILE *streamFile, off_t offset, int target_stream, int is_embedded) { uint32_t i; uint16_t num_sounds; - off_t header_offset, start_offset, test_offset, table_offset; + off_t header_offset, start_offset, test_offset, table_offset, entry_offset; size_t header_size; ea_header ea = { 0 }; int32_t(*read_32bit)(off_t, STREAMFILE*) = NULL; @@ -872,15 +872,17 @@ static VGMSTREAM * parse_bnk_header(STREAMFILE *streamFile, off_t offset, int ta if (target_stream < 0 || target_stream >= num_sounds) goto fail; - header_offset = read_32bit(offset + table_offset + 0x04 * target_stream, streamFile); + entry_offset = offset + table_offset + 0x04 * target_stream; + header_offset = entry_offset + read_32bit(offset + entry_offset, streamFile); } else { /* some of these are dummies with zero offset, skip them when opening standalone BNK */ for (i = 0; i < num_sounds; i++) { - test_offset = read_32bit(offset + table_offset + 0x04 * i, streamFile); + entry_offset = offset + table_offset + 0x04 * i; + test_offset = read_32bit(entry_offset, streamFile); if (test_offset != 0) { if (target_stream == real_bnk_sounds) - header_offset = offset + table_offset + 0x04 * i + test_offset; + header_offset = entry_offset + test_offset; real_bnk_sounds++; } diff --git a/src/meta/gin.c b/src/meta/gin.c index 36953a6e..3c5cf47a 100644 --- a/src/meta/gin.c +++ b/src/meta/gin.c @@ -1,18 +1,32 @@ #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) { 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" */ + vgmstream = init_vgmstream_gin_header(streamFile, 0x00); + if (!vgmstream) + goto fail; + + return vgmstream; + +fail: + return NULL; +} + +VGMSTREAM * init_vgmstream_gin_header(STREAMFILE *streamFile, off_t offset) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count, sample_rate, num_samples; + + /* checks */ + if (read_32bitBE(offset + 0x00, streamFile) != 0x476E7375) /* "Gnsu" */ goto fail; /* contains mapped values for engine RPM sounds but we'll just play the whole thing */ @@ -22,11 +36,11 @@ VGMSTREAM * init_vgmstream_gin(STREAMFILE *streamFile) { /* 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; + 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; channel_count = 1; loop_flag = 0; @@ -40,10 +54,12 @@ VGMSTREAM * init_vgmstream_gin(STREAMFILE *streamFile) { vgmstream->num_samples = num_samples; vgmstream->coding_type = coding_EA_XAS_V0; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x13; + vgmstream->layout_type = layout_none; - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + /* calculate size for TMX */ + vgmstream->stream_size = (align_size_to_block(num_samples, 32) / 32) * 0x13; + + if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) goto fail; return vgmstream; diff --git a/src/meta/meta.h b/src/meta/meta.h index 66b4b4a2..6f031b44 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -684,6 +684,7 @@ VGMSTREAM * init_vgmstream_ea_sps(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_eaac(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_ea_tmx(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ea_sbr(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ea_sbr_harmony(STREAMFILE * streamFile); diff --git a/src/meta/ubi_sb.c b/src/meta/ubi_sb.c index 224c48bb..bfaff032 100644 --- a/src/meta/ubi_sb.c +++ b/src/meta/ubi_sb.c @@ -832,7 +832,7 @@ static VGMSTREAM * init_vgmstream_ubi_sb_silence(ubi_sb_header *sb, STREAMFILE * vgmstream->meta_type = meta_UBI_SB; vgmstream->sample_rate = sample_rate; - vgmstream->num_samples = sb->duration * sample_rate; + vgmstream->num_samples = (int32_t)(sb->duration * (float)sample_rate); vgmstream->num_streams = sb->total_subsongs; vgmstream->stream_size = vgmstream->num_samples * channel_count * 0x02; /* PCM size */ @@ -2348,7 +2348,6 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { } /* Open Season (2006)(PC)-map 0x00180003 */ - /* Shaun White Snowboarding (2008)(PC)-map 0x00180003 */ if (sb->version == 0x00180003 && sb->platform == UBI_PC) { config_sb_entry(sb, 0x68, 0x78); @@ -2359,6 +2358,8 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { config_sb_layer_he(sb, 0x20, 0x38, 0x3c, 0x44); config_sb_layer_sh(sb, 0x34, 0x00, 0x08, 0x0c, 0x14); + + config_sb_silence_f(sb, 0x1c); return 1; } @@ -2373,6 +2374,8 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { config_sb_layer_he(sb, 0x20, 0x2c, 0x30, 0x38); config_sb_layer_sh(sb, 0x34, 0x00, 0x08, 0x0c, 0x14); + + config_sb_silence_f(sb, 0x1c); return 1; } @@ -2387,6 +2390,8 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { config_sb_layer_he(sb, 0x20, 0x38, 0x3c, 0x44); config_sb_layer_sh(sb, 0x34, 0x00, 0x08, 0x0c, 0x14); + + config_sb_silence_f(sb, 0x1c); return 1; } diff --git a/src/vgmstream.c b/src/vgmstream.c index 45363a11..5ec7deb4 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -381,6 +381,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_ea_abk_eaac, init_vgmstream_ea_hdr_sth_dat, init_vgmstream_ea_mpf_mus_eaac, + init_vgmstream_ea_tmx, init_vgmstream_ea_sbr, init_vgmstream_ea_sbr_harmony, init_vgmstream_ngc_vid1, @@ -1180,6 +1181,8 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { case coding_UBI_IMA: case coding_OKI16: return 1; + case coding_PCM4: + case coding_PCM4_U: case coding_IMA_int: case coding_DVI_IMA_int: case coding_3DS_IMA: @@ -1357,6 +1360,8 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { case coding_CIRCUS_ADPCM: return 0x01; + case coding_PCM4: + case coding_PCM4_U: case coding_IMA: case coding_IMA_int: case coding_DVI_IMA: