Merge pull request #1208 from NicknineTheEagle/ea

EA MPF: Implemented RAM tracks for EAAC varaint
This commit is contained in:
NicknineTheEagle 2022-08-21 02:04:06 +03:00 committed by GitHub
commit 47efdf52d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 142 additions and 73 deletions

View File

@ -610,12 +610,13 @@ static STREAMFILE *open_mapfile_pair(STREAMFILE* sf, int track /*, int num_track
/* 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_checksum = 0, mus_sounds, mus_stream = 0;
uint32_t num_tracks, track_start, track_checksum = 0, mus_sounds, mus_stream = 0, bnk_index = 0, bnk_sound_index = 0;
uint32_t tracks_table, samples_table, eof_offset, table_offset, entry_offset, snr_offset, sns_offset;
uint16_t num_subbanks;
uint16_t num_subbanks, index, sub_index;
uint8_t version, sub_version;
STREAMFILE *musFile = NULL;
STREAMFILE *sf_mus = NULL;
VGMSTREAM* vgmstream = NULL;
segmented_layout_data* data_s = NULL;
int i;
int target_stream = sf->stream_index, total_streams, is_ram = 0;
uint32_t(*read_u32)(off_t, STREAMFILE *);
@ -663,20 +664,8 @@ VGMSTREAM* init_vgmstream_ea_mpf_mus_eaac(STREAMFILE* 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_u32(entry_offset + 0x0c, sf) != 0x00)
goto fail;
track_checksum = read_u32be(entry_offset + 0x14, sf);
} else {
if (read_u32(entry_offset + 0x0c, sf) == 0x00)
goto fail;
}
mus_stream = target_stream - 1 - track_start;
@ -685,37 +674,115 @@ VGMSTREAM* init_vgmstream_ea_mpf_mus_eaac(STREAMFILE* sf) {
}
/* open MUS file that matches this track */
musFile = open_mapfile_pair(sf, i);//, num_tracks
if (!musFile)
sf_mus = open_mapfile_pair(sf, i);//, num_tracks
if (!sf_mus)
goto fail;
if (read_u32be(0x00, musFile) != track_checksum)
goto fail;
/* sample offsets table is still there but it just holds SNS offsets, we only need it for RAM sound indexes */
/* 0x00 - offset/index, 0x04 - duration (in milliseconds) */
sns_offset = read_u32(samples_table + (target_stream - 1) * 0x08 + 0x00, sf);
if (is_ram) {
bnk_sound_index = (sns_offset & 0x0000FFFF);
bnk_index = (sns_offset & 0xFFFF0000) >> 16;
if (bnk_index != 0) {
/* HACK: open proper .mus now since open_mapfile_pair doesn't let us adjust the name */
char filename[PATH_LIMIT], basename[PATH_LIMIT], ext[32];
int basename_len;
STREAMFILE* sf_temp;
get_streamfile_basename(sf_mus, basename, PATH_LIMIT);
basename_len = strlen(basename);
get_streamfile_ext(sf_mus, ext, sizeof(ext));
/* strip off 0 at the end */
basename[basename_len - 1] = '\0';
/* append bank index to the name */
snprintf(filename, PATH_LIMIT, "%s%u.%s", basename, bnk_index, ext);
sf_temp = open_streamfile_by_filename(sf_mus, filename);
if (!sf_temp) goto fail;
close_streamfile(sf_mus);
sf_mus = sf_temp;
}
track_checksum = read_u32be(entry_offset + 0x14 + bnk_index * 0x10, sf);
if (track_checksum && read_u32be(0x00, sf_mus) != track_checksum)
goto fail;
} else {
if (track_checksum && read_u32be(0x00, sf_mus) != 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_u32(0x04, musFile) != 0x00)
if (read_u32(0x04, sf_mus) != 0x00)
goto fail;
/*
* 0x00: flags? index?
* 0x00: index
* 0x02: sub-index
* 0x04: SNR offset
* 0x08: SNS offset (contains garbage for RAM sounds)
*/
table_offset = 0x08;
entry_offset = table_offset + mus_stream * 0x0c;
snr_offset = read_u32(entry_offset + 0x04, musFile);
sns_offset = read_u32(entry_offset + 0x08, musFile);
if (is_ram) {
int ram_segments;
/* find number of parts for this node */
for (i = 0; ; i++) {
entry_offset = table_offset + (bnk_sound_index + i) * 0x0c;
index = read_u16(entry_offset + 0x00, sf_mus);
sub_index = read_u16(entry_offset + 0x02, sf_mus);
if (index == 0xffff) /* EOF check */
goto fail;
entry_offset += 0x0c;
if (read_u16(entry_offset + 0x00, sf_mus) != index ||
read_u16(entry_offset + 0x02, sf_mus) != sub_index + 1)
break;
}
ram_segments = i + 1;
/* init layout */
data_s = init_layout_segmented(ram_segments);
if (!data_s) goto fail;
for (i = 0; i < ram_segments; i++) {
entry_offset = table_offset + (bnk_sound_index + i) * 0x0c;
snr_offset = read_u32(entry_offset + 0x04, sf_mus);
data_s->segments[i] = init_vgmstream_eaaudiocore_header(sf_mus, sf_mus,
snr_offset, 0,
meta_EA_SNR_SNS, 0);
if (!data_s->segments[i]) goto fail;
}
/* setup segmented VGMSTREAMs */
if (!setup_layout_segmented(data_s)) goto fail;
vgmstream = allocate_segmented_vgmstream(data_s, 0, 0, 0);
} else {
entry_offset = table_offset + mus_stream * 0x0c;
snr_offset = read_u32(entry_offset + 0x04, sf_mus);
sns_offset = read_u32(entry_offset + 0x08, sf_mus);
vgmstream = init_vgmstream_eaaudiocore_header(sf_mus, sf_mus,
snr_offset, sns_offset,
meta_EA_SNR_SNS, 0);
}
} else if (sub_version == 3) {
/* number of samples is always little endian */
mus_sounds = read_u32le(0x04, musFile);
mus_sounds = read_u32le(0x04, sf_mus);
if (mus_stream >= mus_sounds)
goto fail;
if (is_ram) {
/* not seen so far */
VGM_LOG("Found RAM SNR in MPF v5.3.\n");
VGM_LOG("Found RAM track in MPF v5.3.\n");
goto fail;
}
@ -731,23 +798,27 @@ VGMSTREAM* init_vgmstream_ea_mpf_mus_eaac(STREAMFILE* sf) {
*/
table_offset = 0x28;
entry_offset = table_offset + mus_stream * 0x1c;
snr_offset = read_u32(entry_offset + 0x08, musFile) * 0x10;
sns_offset = read_u32(entry_offset + 0x0c, musFile) * 0x80;
snr_offset = read_u32(entry_offset + 0x08, sf_mus) * 0x10;
sns_offset = read_u32(entry_offset + 0x0c, sf_mus) * 0x80;
vgmstream = init_vgmstream_eaaudiocore_header(sf_mus, sf_mus,
snr_offset, sns_offset,
meta_EA_SNR_SNS, 0);
} else {
goto fail;
}
vgmstream = init_vgmstream_eaaudiocore_header(musFile, musFile, snr_offset, sns_offset, meta_EA_SNR_SNS, 0);
if (!vgmstream)
goto fail;
vgmstream->num_streams = total_streams;
get_streamfile_filename(musFile, vgmstream->stream_name, STREAM_NAME_SIZE);
close_streamfile(musFile);
get_streamfile_filename(sf_mus, vgmstream->stream_name, STREAM_NAME_SIZE);
close_streamfile(sf_mus);
return vgmstream;
fail:
close_streamfile(musFile);
close_streamfile(sf_mus);
free_layout_segmented(data_s);
return NULL;
}
@ -810,15 +881,16 @@ fail:
/* EA Harmony Sample Bank - used in 8th gen EA Sports games */
VGMSTREAM* init_vgmstream_ea_sbr_harmony(STREAMFILE* sf) {
uint64_t base_offset, sound_offset, offset, prev_offset;
uint32_t chunk_id, data_offset, table_offset, dset_offset, set_values, set_sounds, sound_table_offset;
uint32_t dset_id, dset_offset, num_values, num_fields, field_id,
data_offset, table_offset, set_sounds, sound_table_offset;
int16_t flag;
uint16_t num_dsets;
uint8_t set_type, offset_size;
uint32_t i, j;
char sound_name[STREAM_NAME_SIZE];
STREAMFILE *sf_sbs = NULL, *sf_data = NULL;
VGMSTREAM* vgmstream = NULL;
int target_stream = sf->stream_index, total_sounds, local_target, is_streamed = 0;
int i, j;
uint64_t(*read_u64)(off_t, STREAMFILE *);
uint32_t(*read_u32)(off_t, STREAMFILE*);
uint16_t(*read_u16)(off_t, STREAMFILE*);
@ -857,27 +929,26 @@ VGMSTREAM* init_vgmstream_ea_sbr_harmony(STREAMFILE* sf) {
if (read_u32(dset_offset, sf) != 0x44534554) /* "DSET" */
goto fail;
set_values = read_u32(dset_offset + 0x38, sf);
dset_id = read_u32(dset_offset + 0x08, sf);
num_values = read_u32(dset_offset + 0x38, sf);
num_fields = read_u32(dset_offset + 0x3c, sf);
local_target = target_stream - total_sounds - 1;
dset_offset += 0x48;
/* Find RAM or OFF chunk */
while(1) {
chunk_id = read_u32(dset_offset, sf);
if (chunk_id == 0x2E52414D) { /* ".RAM" */
/* find RAM or OFF field */
for (j = 0; j < num_fields; j++) {
field_id = read_u32(dset_offset, sf);
if (field_id == 0x2E52414D || /* ".RAM" */
field_id == 0x2E4F4646) { /* ".OFF" */
break;
} else if (chunk_id == 0x2E4F4646) { /* ".OFF" */
break;
} else if (chunk_id == 0x2E4C4452 || /* ".LDR" */
chunk_id == 0x2E4F424A || /* ".OBJ" */
chunk_id == 0x2E445552 || /* ".DUR" */
(chunk_id & 0xFF00FFFF) == 0x2E00534C) { /* ".?SL */
dset_offset += 0x18;
} else {
goto fail;
}
dset_offset += 0x18;
}
if (j == num_fields)
goto fail;
/* different set types store offsets differently */
set_type = read_u8(dset_offset + 0x05, sf);
@ -894,7 +965,7 @@ VGMSTREAM* init_vgmstream_ea_sbr_harmony(STREAMFILE* sf) {
flag = (int16_t)read_u16(dset_offset + 0x06, sf);
base_offset = read_u64(dset_offset + 0x08, sf);
set_sounds = set_values;
set_sounds = num_values;
total_sounds += set_sounds;
if (local_target < 0 || local_target >= set_sounds)
continue;
@ -908,7 +979,7 @@ VGMSTREAM* init_vgmstream_ea_sbr_harmony(STREAMFILE* sf) {
set_sounds = 0;
prev_offset = UINT64_MAX;
for (j = 0; j < set_values; j++) {
for (j = 0; j < num_values; j++) {
if (offset_size == 0x01) {
offset = read_u8(sound_table_offset + 0x01 * j, sf);
} else if (offset_size == 0x02) {
@ -955,7 +1026,7 @@ VGMSTREAM* init_vgmstream_ea_sbr_harmony(STREAMFILE* sf) {
set_sounds = 0;
prev_offset = UINT64_MAX;
for (j = 0; j < set_values; j++) {
for (j = 0; j < num_values; j++) {
offset = read_u64(sound_table_offset + 0x08 * j, sf);
if (sound_offset != prev_offset) {
@ -973,11 +1044,11 @@ VGMSTREAM* init_vgmstream_ea_sbr_harmony(STREAMFILE* sf) {
goto fail;
}
snprintf(sound_name, STREAM_NAME_SIZE, "DSET %02d/%04d", i, local_target);
snprintf(sound_name, STREAM_NAME_SIZE, "DSET %08x/%04d", dset_id, local_target);
if (chunk_id == 0x2E52414D) { /* ".RAM" */
if (field_id == 0x2E52414D) { /* ".RAM" */
is_streamed = 0;
} else if (chunk_id == 0x2E4F4646) { /* ".OFF" */
} else if (field_id == 0x2E4F4646) { /* ".OFF" */
is_streamed = 1;
}
}
@ -1153,7 +1224,7 @@ static VGMSTREAM* init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAMF
break;
case EAAC_TYPE_GIGASAMPLE: /* rarely seen [Def Jam Icon (X360)] */
header_size += 0x04;
eaac.prefetch_samples = read_32bitBE(header_offset + eaac.loop_flag ? 0x0c : 0x08, sf_head);
eaac.prefetch_samples = read_32bitBE(header_offset + (eaac.loop_flag ? 0x0c : 0x08), sf_head);
if (eaac.loop_flag && eaac.loop_start >= eaac.prefetch_samples) {
header_size += 0x04;
@ -1779,7 +1850,7 @@ static layered_layout_data* build_layered_eaaudiocore(STREAMFILE *sf_data, eaac_
stream_size = get_streamfile_size(temp_sf);
block_size = 0x10000; /* unused */
block_count = stream_size / block_size + (stream_size % block_size ? 1 : 0);
block_count = stream_size / block_size + ((stream_size % block_size) ? 1 : 0);
/* EA adopted XMA2 when it appeared around 2006, but detection isn't so easy
* (SNS with XMA2 do exist). Decoder should work when playing XMA1 as XMA2, but

View File

@ -827,7 +827,7 @@ VGMSTREAM* init_vgmstream_ea_mpf_mus(STREAMFILE* sf) {
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;
int i;
int target_stream = sf->stream_index, total_streams, big_endian, is_bnk = 0;
int target_stream = sf->stream_index, total_streams, big_endian, is_ram = 0;
uint32_t(*read_u32)(off_t, STREAMFILE *);
uint16_t(*read_u16)(off_t, STREAMFILE *);
@ -984,10 +984,10 @@ VGMSTREAM* init_vgmstream_ea_mpf_mus(STREAMFILE* sf) {
if (track_start <= target_stream - 1) {
num_subbanks = read_u16(entry_offset + 0x04, sf);
track_checksum = read_u32be(entry_offset + 0x08, sf);
is_bnk = (num_subbanks != 0);
is_ram = (num_subbanks != 0);
/* checks to distinguish it from SNR/SNS version */
if (is_bnk) {
if (is_ram) {
if (read_u32(entry_offset + 0x0c, sf) == 0x00)
goto fail;
} else {
@ -1010,13 +1010,13 @@ VGMSTREAM* init_vgmstream_ea_mpf_mus(STREAMFILE* sf) {
goto fail;
if (version < 5) {
is_bnk = (read_u32be(0x00, sf_mus) == (big_endian ? EA_BNK_HEADER_BE : EA_BNK_HEADER_LE));
is_ram = (read_u32be(0x00, sf_mus) == (big_endian ? EA_BNK_HEADER_BE : EA_BNK_HEADER_LE));
}
/* 0x00 - offset/BNK index, 0x04 - duration (in milliseconds) */
sound_offset = read_u32(samples_table + (target_stream - 1) * 0x08 + 0x00, sf);
if (is_bnk) {
if (is_ram) {
/* for some reason, RAM segments are almost always split into multiple sounds (usually 4) */
off_t bnk_offset = version < 5 ? 0x00 : 0x100;
uint32_t bnk_sound_index = (sound_offset & 0x0000FFFF);
@ -1024,12 +1024,12 @@ VGMSTREAM* init_vgmstream_ea_mpf_mus(STREAMFILE* sf) {
uint32_t next_entry;
uint32_t bnk_total_sounds = read_u16(bnk_offset + 0x06, sf_mus);
int bnk_segments;
STREAMFILE *sf_bnk = sf_mus;
if (version == 5 && bnk_index != 0) {
/* HACK: open proper .mus now since open_mapfile_pair doesn't let us adjust the name */
char filename[PATH_LIMIT], basename[PATH_LIMIT], ext[32];
int basename_len;
STREAMFILE* sf_temp;
get_streamfile_basename(sf_mus, basename, PATH_LIMIT);
basename_len = strlen(basename);
@ -1039,13 +1039,13 @@ VGMSTREAM* init_vgmstream_ea_mpf_mus(STREAMFILE* sf) {
basename[basename_len - 1] = '\0';
/* append bank index to the name */
snprintf(filename, PATH_LIMIT, "%s%d.%s", basename, bnk_index, ext);
snprintf(filename, PATH_LIMIT, "%s%u.%s", basename, bnk_index, ext);
sf_bnk = open_streamfile_by_filename(sf_mus, filename);
if (!sf_bnk) goto fail;
bnk_total_sounds = read_u16(bnk_offset + 0x06, sf_bnk);
sf_temp = open_streamfile_by_filename(sf_mus, filename);
if (!sf_temp) goto fail;
bnk_total_sounds = read_u16(bnk_offset + 0x06, sf_temp);
close_streamfile(sf_mus);
sf_mus = sf_bnk;
sf_mus = sf_temp;
}
if (version == 5) {
@ -1081,10 +1081,7 @@ VGMSTREAM* init_vgmstream_ea_mpf_mus(STREAMFILE* sf) {
/* setup segmented VGMSTREAMs */
if (!setup_layout_segmented(data_s)) goto fail;
vgmstream = allocate_segmented_vgmstream(data_s, 0, 0, 0);
if (!vgmstream)
goto fail;
} else {
if (version == 5 && track_checksum && read_u32be(0x00, sf_mus) != track_checksum)
goto fail;
@ -1094,10 +1091,11 @@ VGMSTREAM* init_vgmstream_ea_mpf_mus(STREAMFILE* sf) {
goto fail;
vgmstream = parse_schl_block(sf_mus, sound_offset, 0);
if (!vgmstream)
goto fail;
}
if (!vgmstream)
goto fail;
vgmstream->num_streams = total_streams;
get_streamfile_filename(sf_mus, vgmstream->stream_name, STREAM_NAME_SIZE);
close_streamfile(sf_mus);