EA MPF: Implemented RAM tracks (stored as BNKs)

This commit is contained in:
NicknineTheEagle 2020-11-08 20:50:17 +03:00
parent adb7fa712e
commit cde0eb5520

View File

@ -454,18 +454,9 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE* sf) {
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;
vgmstream = allocate_segmented_vgmstream(data_s, 1, 1, 1);
if (!vgmstream)
goto fail;
break;
default:
@ -810,12 +801,12 @@ fail:
VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
STREAMFILE* sf_mus = NULL;
off_t tracks_table, samples_table, section_offset, entry_offset, eof_offset, off_mult, sound_offset;
uint32_t track_start, track_hash = 0;
segmented_layout_data *data_s = NULL;
uint32_t track_start, track_end, track_hash, tracks_table, samples_table, section_offset, entry_offset, eof_offset, off_mult, sound_offset;
uint16_t num_nodes;
uint8_t version, sub_version, num_tracks, num_sections, num_events, num_routers, num_vars, subentry_num;
int32_t(*read_32bit)(off_t, STREAMFILE*);
int16_t(*read_16bit)(off_t, STREAMFILE*);
int32_t(*read_u32)(off_t, STREAMFILE*);
int16_t(*read_u16)(off_t, STREAMFILE*);
int i;
int target_stream = sf->stream_index, total_streams, big_endian, is_bnk = 0;
@ -825,29 +816,29 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE* sf) {
/* detect endianness */
if (read_32bitBE(0x00, sf) == 0x50464478) { /* "PFDx" */
read_32bit = read_32bitBE;
read_16bit = read_16bitBE;
read_u32 = read_u32be;
read_u16 = read_u16be;
big_endian = 1;
} else if (read_32bitLE(0x00, sf) == 0x50464478) { /* "xDFP" */
read_32bit = read_32bitLE;
read_16bit = read_16bitLE;
read_u32 = read_u32le;
read_u16 = read_u16le;
big_endian = 0;
} 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 < 3 || version > 5) goto fail;
if (version == 5 && sub_version > 2) goto fail; /* newer version using SNR/SNS */
num_tracks = read_8bit(0x0d, sf);
num_sections = read_8bit(0x0e, sf);
num_events = read_8bit(0x0f, sf);
num_routers = read_8bit(0x10, sf);
num_vars = read_8bit(0x11, sf);
num_nodes = read_16bit(0x12, sf);
num_tracks = read_u8(0x0d, sf);
num_sections = read_u8(0x0e, sf);
num_events = read_u8(0x0f, sf);
num_routers = read_u8(0x10, 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 */
@ -857,19 +848,19 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE* sf) {
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 != 4)
if (sub_version != 1 && sub_version != 2 && sub_version != 4)
goto fail;
/* get the last entry offset */
section_offset = 0x24;
entry_offset = (uint16_t)read_16bit(section_offset + (num_nodes - 1) * 0x02, sf) * 0x04;
if (sub_version == 1) {
subentry_num = read_8bit(entry_offset + 0x0b, sf);
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_32bitBE(entry_offset + 0x04, sf) >> 19) & 0xFF;
subentry_num = (read_u32be(entry_offset + 0x04, sf) >> 19) & 0xFF;
} else {
subentry_num = (read_32bitBE(entry_offset + 0x04, sf) >> 16) & 0xFF;
subentry_num = (read_u32be(entry_offset + 0x04, sf) >> 16) & 0xFF;
}
}
section_offset = entry_offset + 0x0c + subentry_num * 0x04;
@ -877,97 +868,104 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE* sf) {
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_32bit(section_offset, sf) * 0x04;
if (sub_version == 1)
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;
total_streams = (eof_offset - samples_table) / 0x08;
off_mult = 0x04;
track_start = total_streams;
for (i = num_tracks - 1; i >= 0; i--) {
track_start = read_32bit(tracks_table + i * 0x04, sf) * 0x04;
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;
}
if (sub_version == 1)
eof_offset = get_streamfile_size(sf);
else if (sub_version == 4)
eof_offset = read_32bit(tracks_table + num_tracks * 0x04, sf) * 0x04;
total_streams = (eof_offset - samples_table) / 0x08;
off_mult = 0x04;
} 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 = (uint16_t)read_16bit(section_offset + (num_nodes - 1) * 0x02, sf) * 0x04;
entry_offset = read_u16(section_offset + (num_nodes - 1) * 0x02, sf) * 0x04;
if (big_endian) {
subentry_num = (read_32bitBE(entry_offset + 0x04, sf) >> 15) & 0xFF;
subentry_num = (read_u32be(entry_offset + 0x04, sf) >> 15) & 0xFF;
} else {
subentry_num = (read_32bitBE(entry_offset + 0x04, sf) >> 20) & 0xFF;
subentry_num = (read_u32be(entry_offset + 0x04, sf) >> 20) & 0xFF;
}
section_offset = entry_offset + 0x10 + subentry_num * 0x04;
/* get the last entry offset */
entry_offset = (uint16_t)read_16bit(section_offset + (num_events - 1) * 0x02, sf) * 0x04;
entry_offset = read_u16(section_offset + (num_events - 1) * 0x02, sf) * 0x04;
if (big_endian) {
subentry_num = (read_32bitBE(entry_offset + 0x0c, sf) >> 10) & 0xFF;
subentry_num = (read_u32be(entry_offset + 0x0c, sf) >> 10) & 0xFF;
} else {
subentry_num = (read_32bitBE(entry_offset + 0x0c, sf) >> 8) & 0xFF;
subentry_num = (read_u32be(entry_offset + 0x0c, sf) >> 8) & 0xFF;
}
section_offset = entry_offset + 0x10 + subentry_num * 0x10;
/* TODO: verify this */
section_offset = read_32bit(section_offset, sf) * 0x04;
section_offset = read_u32(section_offset, sf) * 0x04;
section_offset += num_routers * 0x04;
section_offset += num_vars * 0x04;
tracks_table = section_offset;
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 = 0x80;
track_start = total_streams;
for (i = num_tracks - 1; i >= 0; i--) {
track_start = read_32bit(tracks_table + i * 0x04, sf) * 0x04;
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;
}
eof_offset = read_32bit(tracks_table + num_tracks * 0x04, sf) * 0x04;
total_streams = (eof_offset - samples_table) / 0x08;
off_mult = 0x80;
} else if (version == 5) {
/* Need for Speed: Most Wanted, Need for Speed: Carbon */
tracks_table = read_32bit(0x2c, sf);
samples_table = read_32bit(0x34, 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;
off_mult = 0x80;
track_start = total_streams;
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);
track_end = track_start;
entry_offset = read_u32(tracks_table + i * 0x04, sf) * 0x04;
track_start = read_u32(entry_offset + 0x00, sf);
if (track_start <= target_stream - 1) {
track_hash = read_32bitBE(entry_offset + 0x08, sf);
track_hash = read_u32be(entry_offset + 0x08, sf);
is_bnk = (track_hash == 0xF1F1F1F1);
/* checks to distinguish it from SNR/SNS version */
if (is_bnk) {
if (read_32bitBE(entry_offset + 0x0c, sf) == 0x00)
if (read_u32be(entry_offset + 0x0c, sf) == 0x00)
goto fail;
track_hash = read_32bitBE(entry_offset + 0x14, sf);
track_hash = read_u32be(entry_offset + 0x14, sf);
if (track_hash == 0xF1F1F1F1)
continue; /* empty track */
} else {
if (read_32bitBE(entry_offset + 0x0c, sf) != 0x00)
if (read_u32be(entry_offset + 0x0c, sf) != 0x00)
goto fail;
}
break;
}
}
eof_offset = read_32bit(0x38, sf);
total_streams = (eof_offset - samples_table) / 0x08;
off_mult = 0x80;
} else {
goto fail;
}
@ -981,21 +979,43 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE* sf) {
goto fail;
if (version == 5) {
if (read_32bitBE(0x00, sf_mus) != track_hash)
if (read_u32be(0x00, sf_mus) != track_hash)
goto fail;
} else {
is_bnk = (read_32bitBE(0x00, sf_mus) == (big_endian ? EA_BNK_HEADER_BE : EA_BNK_HEADER_LE));
is_bnk = (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) {
/* TODO: Harry Potter COS appears to reference only the first segments of multi-segment BNK sounds? */
sound_offset = read_32bit(samples_table + (target_stream - 1) * 0x08 + 0x00, sf);
vgmstream = parse_bnk_header(sf_mus, version < 5 ? 0x00 : 0x100, sound_offset, 1);
/* for some reason, RAM segments are almost always split into multiple sounds (usually 4) */
off_t bnk_offset = version < 5 ? 0x00 : 0x100;
int bnk_total_sounds = read_u16(0x06, sf_mus);
int bnk_segments;
/* play until the next entry in MPF track or the end of BNK */
if (target_stream < track_end) {
bnk_segments = read_u32(samples_table + (target_stream - 0) * 0x08 + 0x00, sf) - sound_offset;
} else {
bnk_segments = bnk_total_sounds - sound_offset;
}
/* init layout */
data_s = init_layout_segmented(bnk_segments);
if (!data_s) goto fail;
for (i = 0; i < bnk_segments; i++)
data_s->segments[i] = parse_bnk_header(sf_mus, bnk_offset, sound_offset + i, 1);
/* 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 {
sound_offset = read_32bit(samples_table + (target_stream - 1) * 0x08 + 0x00, sf) * off_mult;
sound_offset *= off_mult;;
if (read_32bitBE(sound_offset, sf_mus) != EA_BLOCKID_HEADER)
goto fail;
@ -1011,6 +1031,8 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE* sf) {
fail:
close_streamfile(sf_mus);
free_layout_segmented(data_s);
return NULL;
}