From fd21da2c6118cee0c2377b4aa99a9c89c18f5e51 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 3 Feb 2018 01:27:35 +0100 Subject: [PATCH 1/6] Add SCD v4 ATRAC9 [Kingdom Hearts 2.8 (PS4)] --- src/meta/sqex_scd.c | 83 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 63 insertions(+), 20 deletions(-) diff --git a/src/meta/sqex_scd.c b/src/meta/sqex_scd.c index 203fbd9d..7c524c06 100644 --- a/src/meta/sqex_scd.c +++ b/src/meta/sqex_scd.c @@ -12,12 +12,12 @@ static void scd_ogg_v3_decryption_callback(void *ptr, size_t size, size_t nmemb, VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; char filename[PATH_LIMIT]; - off_t start_offset, tables_offset, meta_offset, post_meta_offset; + off_t start_offset, tables_offset, meta_offset, post_meta_offset, name_offset = 0; int32_t stream_size, subheader_size, loop_start, loop_end; - int total_subsongs, target_subsong = streamFile->stream_index; int loop_flag = 0, channel_count, codec, sample_rate; - int aux_chunk_count; + int version, target_entry, aux_chunk_count; + int total_subsongs, target_subsong = streamFile->stream_index; int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; @@ -33,24 +33,25 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { read_32bitBE(0x04,streamFile) != 0x53534346) /* "SSCF" */ goto fail; - if (read_32bitBE(0x08,streamFile) == 2 || /* version 2 BE, as seen in FFXIII demo for PS3 */ - read_32bitBE(0x08,streamFile) == 3) { /* version 3 BE, as seen in FFXIII for PS3 */ + if (read_8bit(0x0c,streamFile) == 0x01) { /* big endian flag */ //size_offset = 0x14; read_32bit = read_32bitBE; read_16bit = read_16bitBE; - } - else if (read_32bitLE(0x08,streamFile) == 2 || /* version 2/3 LE, as seen in FFXIV for PC (and others) */ - read_32bitLE(0x08,streamFile) == 3) { + } else { //size_offset = 0x10; read_32bit = read_32bitLE; read_16bit = read_16bitLE; } - else { - goto fail; - } - /* 0x0c: probably 0=LE, 1=BE */ - /* 0x0d: unknown (always 0x04) */ + /* SSCF version? (older SSCFs from Crisis Core/FFXI X360 seem to be V3/2) */ + if (read_8bit(0x0d,streamFile) != 0x04) + goto fail; + + /* v2: FFXIII demo (PS3), FFT0 test files (PC); v3: common; v4: Kingdom Hearts 2.8 (PS4) */ + version = read_32bit(0x08,streamFile); + if (version != 2 && version != 3 && version != 4) + goto fail; + tables_offset = read_16bit(0x0e,streamFile); /* usually 0x30 or 0x20 */ #if 0 @@ -74,7 +75,7 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { /* 0x14: always null? */ /* 0x18: table5? (unknown) start offset? */ /* 0x1c: unknown, often null */ - /* each table entry is an uint32_t offset */ + /* each table entry is an uint32_t offset; after entries there is padding */ /* if a table isn't present entries is 0 and offset points to next table */ /* find meta_offset in table3 (headers) and total subsongs */ @@ -89,14 +90,16 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { /* manually find subsongs as entries can be dummy (ex. sfx banks in FF XIV or FF Type-0) */ for (i = 0; i < headers_entries; i++) { - off_t header_offset = read_32bit(headers_offset + i*0x04,streamFile); + off_t entry_offset = read_32bit(headers_offset + i*0x04,streamFile); - if (read_32bit(header_offset+0x0c,streamFile) == -1) + if (read_32bit(entry_offset+0x0c,streamFile) == -1) continue; /* codec -1 when dummy */ total_subsongs++; - if (!meta_offset && total_subsongs == target_subsong) - meta_offset = header_offset; + if (!meta_offset && total_subsongs == target_subsong) { + meta_offset = entry_offset; + target_entry = i; + } } if (meta_offset == 0) goto fail; /* SCD can contain 0 entries too */ @@ -129,10 +132,24 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { post_meta_offset += read_32bit(post_meta_offset+0x04, streamFile); } + /* find name if possible */ + if (version == 4) { + int info_entries = read_16bit(tables_offset+0x00,streamFile); + int headers_entries = read_16bit(tables_offset+0x04,streamFile); + off_t info_offset = tables_offset+0x20; + + /* not very exact as table1 and table3 entries may differ in V3, not sure about V4 */ + if (info_entries == headers_entries) { + off_t entry_offset = read_16bit(info_offset + 0x04*target_entry,streamFile); + name_offset = entry_offset+0x30; + } + } + #ifdef VGM_USE_VORBIS /* special case using init_vgmstream_ogg_vorbis */ if (codec == 0x06) { + VGMSTREAM *ogg_vgmstream; uint8_t ogg_version, ogg_byte; vgm_vorbis_info_t inf = {0}; @@ -176,7 +193,10 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { } /* actual Ogg init */ - return init_vgmstream_ogg_vorbis_callbacks(streamFile, filename, NULL, start_offset, &inf); + ogg_vgmstream = init_vgmstream_ogg_vorbis_callbacks(streamFile, filename, NULL, start_offset, &inf); + if (ogg_vgmstream && name_offset) + read_string(ogg_vgmstream->stream_name, PATH_LIMIT, name_offset, streamFile); + return ogg_vgmstream; } #endif @@ -189,6 +209,8 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { vgmstream->num_streams = total_subsongs; vgmstream->stream_size = stream_size; vgmstream->meta_type = meta_SQEX_SCD; + if (name_offset) + read_string(vgmstream->stream_name, PATH_LIMIT, name_offset, streamFile); switch (codec) { case 0x01: /* PCM */ @@ -319,7 +341,7 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { } #ifdef VGM_USE_FFMPEG - case 0x0B: { /* XMA2 [Final Fantasy (X360), Lightning Returns (X360) sfx] */ + case 0x0B: { /* XMA2 [Final Fantasy (X360), Lightning Returns (X360) sfx, Kingdom Hearts 2.8 (X1)] */ ffmpeg_codec_data *ffmpeg_data = NULL; uint8_t buf[200]; int32_t bytes; @@ -373,6 +395,27 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { } #endif +#ifdef VGM_USE_ATRAC9 + case 0x16: { /* ATRAC9 [Kingdom Hearts 2.8 (PS4)] */ + atrac9_config cfg = {0}; + + /* post header has various typical ATRAC9 values */ + cfg.channels = vgmstream->channels; + cfg.config_data = read_32bit(post_meta_offset+0x0c,streamFile); + cfg.encoder_delay = read_32bit(post_meta_offset+0x18,streamFile); + + vgmstream->codec_data = init_atrac9(&cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_ATRAC9; + vgmstream->layout_type = layout_none; + + vgmstream->num_samples = read_32bit(post_meta_offset+0x10,streamFile); /* loop values above are also weird and ignored */ + vgmstream->loop_start_sample = read_32bit(post_meta_offset+0x20, streamFile) - (loop_flag ? cfg.encoder_delay : 0); //loop_start + vgmstream->loop_end_sample = read_32bit(post_meta_offset+0x24, streamFile) - (loop_flag ? cfg.encoder_delay : 0); //loop_end + break; + } +#endif + case -1: /* used for dummy entries */ default: VGM_LOG("SCD: unknown codec 0x%x\n", codec); From b885ef13eec1d4d4e1e94f90e1417fcef977e589 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 3 Feb 2018 01:27:57 +0100 Subject: [PATCH 2/6] Add MAB encrypted HCA [Final Fantasy XII TZA (PS4/PC)] --- src/meta/sqex_sead.c | 87 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 75 insertions(+), 12 deletions(-) diff --git a/src/meta/sqex_sead.c b/src/meta/sqex_sead.c index 2c98eaa2..3e1d6d08 100644 --- a/src/meta/sqex_sead.c +++ b/src/meta/sqex_sead.c @@ -2,13 +2,14 @@ #include "../coding/coding.h" -static STREAMFILE* setup_sead_hca_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size); +static STREAMFILE* setup_sead_hca_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, int encryption, size_t header_size, size_t key_start); -/* SABF/MABF - Square Enix's "Sead" audio games [Dragon Quest Builders (PS3), Dissidia Opera Omnia (mobile), FF XV (PS4)] */ +/* SABF/MABF - Square Enix's "sead" audio games [Dragon Quest Builders (PS3), Dissidia Opera Omnia (mobile), FF XV (PS4)] */ VGMSTREAM * init_vgmstream_sqex_sead(STREAMFILE * streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset, mtrl_offset, meta_offset, post_meta_offset; //, info_offset, name_offset = 0; - size_t stream_size, subheader_size; //, name_size = 0; + size_t stream_size, subheader_size, special_size; //, name_size = 0; + int loop_flag = 0, channel_count, codec, sample_rate, loop_start, loop_end; int is_sab = 0, is_mab = 0; @@ -114,7 +115,7 @@ VGMSTREAM * init_vgmstream_sqex_sead(STREAMFILE * streamFile) { loop_end = read_32bit(meta_offset+0x10,streamFile); subheader_size = read_32bit(meta_offset+0x14,streamFile); /* including subfile header */ stream_size = read_32bit(meta_offset+0x18,streamFile); /* not including subfile header */ - /* 0x1c: null? */ + special_size = read_32bit(meta_offset+0x1c,streamFile); loop_flag = (loop_end > 0); post_meta_offset = meta_offset + 0x20; @@ -122,7 +123,7 @@ VGMSTREAM * init_vgmstream_sqex_sead(STREAMFILE * streamFile) { /** info section (get stream name) **/ //if (is_sab) { //todo load name based on entry id - /* "snd ": unknown flags/sizes and name */ + /* "snd " */ /* 0x08(2): file number within descriptor */ /* 0x1a(2): base_entry size (-0x10?) */ //name_size = read_32bit(snd_offset+0x20,streamFile); @@ -130,7 +131,11 @@ VGMSTREAM * init_vgmstream_sqex_sead(STREAMFILE * streamFile) { /* 0x24(4): unique id? (referenced in "seq" section) */ //} //else if (is_mab) { - /* "musc": unknown flags sizes and names, another format */ + /* "musc" */ + //looks like a "music cue" section, pointing to one subsection per "material". + // ex. one cue may point to 3 named subsongs/sections. + // some common header info from all materials is repeated (ex. sample rate), while other + // (loops, maybe proper num_samples) are listed per material but don't always match thei header //} @@ -170,12 +175,12 @@ VGMSTREAM * init_vgmstream_sqex_sead(STREAMFILE * streamFile) { cfg.channels = vgmstream->channels; cfg.config_data = read_32bit(post_meta_offset+0x0c,streamFile); cfg.encoder_delay = read_32bit(post_meta_offset+0x18,streamFile); -VGM_LOG("1\n"); + vgmstream->codec_data = init_atrac9(&cfg); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_ATRAC9; vgmstream->layout_type = layout_none; -VGM_LOG("2\n"); + vgmstream->sample_rate = read_32bit(post_meta_offset+0x1c,streamFile); /* SAB's sample rate can be different but it's ignored */ vgmstream->num_samples = read_32bit(post_meta_offset+0x10,streamFile); /* loop values above are also weird and ignored */ vgmstream->loop_start_sample = read_32bit(post_meta_offset+0x20, streamFile) - (loop_flag ? cfg.encoder_delay : 0); //loop_start @@ -185,7 +190,7 @@ VGM_LOG("2\n"); #endif #ifdef VGM_USE_MPEG - case 0x06: { /* MSF subfile (MPEG mode) [Dragon Quest Builders (PS3)] */ + case 0x06: { /* MSF subfile (MPEG mode) [Dragon Quest Builders (PS3)] */ mpeg_codec_data *mpeg_data = NULL; mpeg_custom_config cfg = {0}; @@ -210,10 +215,14 @@ VGM_LOG("2\n"); STREAMFILE *temp_streamFile = NULL; off_t subfile_offset = post_meta_offset + 0x10; size_t subfile_size = stream_size + subheader_size - 0x10; - /* post header has 0x10 unknown + HCA header */ + /* post header: values from the HCA header, in file endianness + HCA header */ + size_t key_start = special_size & 0xff; + size_t header_size = read_16bit(post_meta_offset+0x02, streamFile); + int encryption = read_16bit(post_meta_offset+0x0c, streamFile); //maybe 8bit? + /* encryption type 0x01 found in Final Fantasy XII TZA (PS4/PC) */ - temp_streamFile = setup_sead_hca_streamfile(streamFile, subfile_offset, subfile_size); + temp_streamFile = setup_sead_hca_streamfile(streamFile, subfile_offset, subfile_size, encryption, header_size, key_start); if (!temp_streamFile) goto fail; temp_vgmstream = init_vgmstream_hca(temp_streamFile); @@ -249,7 +258,49 @@ fail: return NULL; } -static STREAMFILE* setup_sead_hca_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size) { + +typedef struct { + size_t header_size; + size_t key_start; +} sead_decryption_data; + +/* Encrypted HCA */ +static size_t sead_decryption_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, sead_decryption_data* data) { + /* Found in FFXII_TZA.exe (same key in SCD Ogg V3) */ + static const uint8_t encryption_key[0x100] = { + 0x3A, 0x32, 0x32, 0x32, 0x03, 0x7E, 0x12, 0xF7, 0xB2, 0xE2, 0xA2, 0x67, 0x32, 0x32, 0x22, 0x32, // 00-0F + 0x32, 0x52, 0x16, 0x1B, 0x3C, 0xA1, 0x54, 0x7B, 0x1B, 0x97, 0xA6, 0x93, 0x1A, 0x4B, 0xAA, 0xA6, // 10-1F + 0x7A, 0x7B, 0x1B, 0x97, 0xA6, 0xF7, 0x02, 0xBB, 0xAA, 0xA6, 0xBB, 0xF7, 0x2A, 0x51, 0xBE, 0x03, // 20-2F + 0xF4, 0x2A, 0x51, 0xBE, 0x03, 0xF4, 0x2A, 0x51, 0xBE, 0x12, 0x06, 0x56, 0x27, 0x32, 0x32, 0x36, // 30-3F + 0x32, 0xB2, 0x1A, 0x3B, 0xBC, 0x91, 0xD4, 0x7B, 0x58, 0xFC, 0x0B, 0x55, 0x2A, 0x15, 0xBC, 0x40, // 40-4F + 0x92, 0x0B, 0x5B, 0x7C, 0x0A, 0x95, 0x12, 0x35, 0xB8, 0x63, 0xD2, 0x0B, 0x3B, 0xF0, 0xC7, 0x14, // 50-5F + 0x51, 0x5C, 0x94, 0x86, 0x94, 0x59, 0x5C, 0xFC, 0x1B, 0x17, 0x3A, 0x3F, 0x6B, 0x37, 0x32, 0x32, // 60-6F + 0x30, 0x32, 0x72, 0x7A, 0x13, 0xB7, 0x26, 0x60, 0x7A, 0x13, 0xB7, 0x26, 0x50, 0xBA, 0x13, 0xB4, // 70-7F + 0x2A, 0x50, 0xBA, 0x13, 0xB5, 0x2E, 0x40, 0xFA, 0x13, 0x95, 0xAE, 0x40, 0x38, 0x18, 0x9A, 0x92, // 80-8F + 0xB0, 0x38, 0x00, 0xFA, 0x12, 0xB1, 0x7E, 0x00, 0xDB, 0x96, 0xA1, 0x7C, 0x08, 0xDB, 0x9A, 0x91, // 90-9F + 0xBC, 0x08, 0xD8, 0x1A, 0x86, 0xE2, 0x70, 0x39, 0x1F, 0x86, 0xE0, 0x78, 0x7E, 0x03, 0xE7, 0x64, // A0-AF + 0x51, 0x9C, 0x8F, 0x34, 0x6F, 0x4E, 0x41, 0xFC, 0x0B, 0xD5, 0xAE, 0x41, 0xFC, 0x0B, 0xD5, 0xAE, // B0-BF + 0x41, 0xFC, 0x3B, 0x70, 0x71, 0x64, 0x33, 0x32, 0x12, 0x32, 0x32, 0x36, 0x70, 0x34, 0x2B, 0x56, // C0-CF + 0x22, 0x70, 0x3A, 0x13, 0xB7, 0x26, 0x60, 0xBA, 0x1B, 0x94, 0xAA, 0x40, 0x38, 0x00, 0xFA, 0xB2, // D0-DF + 0xE2, 0xA2, 0x67, 0x32, 0x32, 0x12, 0x32, 0xB2, 0x32, 0x32, 0x32, 0x32, 0x75, 0xA3, 0x26, 0x7B, // E0-EF + 0x83, 0x26, 0xF9, 0x83, 0x2E, 0xFF, 0xE3, 0x16, 0x7D, 0xC0, 0x1E, 0x63, 0x21, 0x07, 0xE3, 0x01, // F0-FF + }; + size_t bytes_read; + int i; + + bytes_read = streamfile->read(streamfile, dest, offset, length); + + /* decrypt data (xor) */ + if (offset >= data->header_size) { + for (i = 0; i < bytes_read; i++) { + dest[i] ^= encryption_key[(data->key_start + (offset - data->header_size) + i) % 0x100]; + } + } + + return bytes_read; +} + +static STREAMFILE* setup_sead_hca_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, int encryption, size_t header_size, size_t key_start) { STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; /* setup subfile */ @@ -261,6 +312,18 @@ static STREAMFILE* setup_sead_hca_streamfile(STREAMFILE *streamFile, off_t subfi if (!new_streamFile) goto fail; temp_streamFile = new_streamFile; + if (encryption) { + sead_decryption_data io_data = {0}; + size_t io_data_size = sizeof(sead_decryption_data); + + io_data.header_size = header_size; + io_data.key_start = key_start; + + new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, sead_decryption_read); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + } + new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,"hca"); if (!new_streamFile) goto fail; temp_streamFile = new_streamFile; From 87ccbb0eaa6eea7505b6a4a4f2c61b9fbfa36b98 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 3 Feb 2018 15:39:21 +0100 Subject: [PATCH 3/6] Add SAB OGG Vorbis [Final Fantasy XV Benchmark sfx (PC)] --- src/meta/sqex_sead.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/meta/sqex_sead.c b/src/meta/sqex_sead.c index 3e1d6d08..95277853 100644 --- a/src/meta/sqex_sead.c +++ b/src/meta/sqex_sead.c @@ -166,6 +166,37 @@ VGMSTREAM * init_vgmstream_sqex_sead(STREAMFILE * streamFile) { break; } +#ifdef VGM_USE_VORBIS + case 0x03: { /* OGG [Final Fantasy XV Benchmark sfx (PC)] */ + VGMSTREAM *ogg_vgmstream = NULL; + vgm_vorbis_info_t inf = {0}; + off_t subfile_offset = post_meta_offset + subheader_size; + char filename[PATH_LIMIT]; + + streamFile->get_name(streamFile,filename,sizeof(filename)); + + inf.layout_type = layout_ogg_vorbis; + inf.meta_type = vgmstream->meta_type; + inf.total_subsongs = total_subsongs; + inf.stream_size = stream_size; + /* post header has some kind of repeated values, config/table? */ + + ogg_vgmstream = init_vgmstream_ogg_vorbis_callbacks(streamFile, filename, NULL, subfile_offset, &inf); + if (ogg_vgmstream) { + ogg_vgmstream->num_streams = vgmstream->num_streams; + ogg_vgmstream->stream_size = vgmstream->stream_size; + + close_vgmstream(vgmstream); + return ogg_vgmstream; + } + else { + goto fail; + } + + break; + } +#endif + #ifdef VGM_USE_ATRAC9 case 0x04: { /* ATRAC9 [Dragon Quest Builders (Vita), Final Fantaxy XV (PS4)] */ atrac9_config cfg = {0}; From 9a279c35b5c659a7eb89077b14373728803e4739 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 3 Feb 2018 15:40:43 +0100 Subject: [PATCH 4/6] Remove debug crap --- src/layout/blocked_ea_schl.c | 1 - src/meta/atx.c | 6 +++--- src/meta/ea_schl.c | 1 - src/meta/xwb.c | 1 - 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/layout/blocked_ea_schl.c b/src/layout/blocked_ea_schl.c index e71844d0..eea96ffd 100644 --- a/src/layout/blocked_ea_schl.c +++ b/src/layout/blocked_ea_schl.c @@ -149,7 +149,6 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) { for (i = 0; i < vgmstream->channels; i++) { off_t channel_start = read_32bit(block_offset + 0x0C + (0x04*i),streamFile); vgmstream->ch[i].offset = block_offset + 0x0C + (0x04*vgmstream->channels) + channel_start; - VGM_LOG("ch=%x, off=%lx\n", i, vgmstream->ch[i].offset); } /* read ADPCM history before each channel if needed (not actually read in sx.exe) */ diff --git a/src/meta/atx.c b/src/meta/atx.c index a58a8833..e540baa3 100644 --- a/src/meta/atx.c +++ b/src/meta/atx.c @@ -42,7 +42,7 @@ static STREAMFILE* setup_atx_streamfile(STREAMFILE *streamFile) { size_t filename_len; int i, num_segments = 0; size_t riff_size; -VGM_LOG("1\n"); + if (read_16bitLE(0x1c,streamFile) != 0) goto fail; /* this must be first segment */ if (read_16bitLE(0x1e,streamFile) < 1 || read_16bitLE(0x1e,streamFile) > ATX_MAX_SEGMENTS) goto fail; @@ -57,7 +57,7 @@ VGM_LOG("1\n"); for (i = 0; i < num_segments; i++) { off_t subfile_offset; size_t subfile_size; -VGM_LOG("loop\n"); + filename[filename_len - 5] = ('0'+i+1); /* ghetto digit conversion */ new_streamFile = open_stream_name(streamFile, filename); if (!new_streamFile) goto fail; @@ -69,7 +69,7 @@ VGM_LOG("loop\n"); /* parse block/segment header (other Media.Vision's files use it too) */ subfile_offset = read_32bitLE(0x08,segment_streamFiles[i]); /* header size */ subfile_size = read_32bitLE(0x14,segment_streamFiles[i]); /* can be 0 in other containers */ -VGM_LOG("subfile: %lx, %x\n", subfile_offset, subfile_size); + if (read_16bitLE(0x1c,segment_streamFiles[i]) != i) goto fail; /* segment sequence */ /* 0x04: block size (should match subfile_size in .ATX) */ diff --git a/src/meta/ea_schl.c b/src/meta/ea_schl.c index e8cfa8a9..1943a6cd 100644 --- a/src/meta/ea_schl.c +++ b/src/meta/ea_schl.c @@ -342,7 +342,6 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_ case EA_CODEC2_MT10: /* MicroTalk (10:1 compression) */ case EA_CODEC2_MT5: /* MicroTalk (5:1 compression) */ vgmstream->coding_type = coding_EA_MT; - VGM_LOG("mt: codec=%x, cv=%x, v=%x\n", ea->codec2, ea->codec_version, ea->version); vgmstream->codec_data = init_ea_mt(vgmstream->channels, ea->version == EA_VERSION_V3); if (!vgmstream->codec_data) goto fail; break; diff --git a/src/meta/xwb.c b/src/meta/xwb.c index 88335d95..5dc75b45 100644 --- a/src/meta/xwb.c +++ b/src/meta/xwb.c @@ -798,7 +798,6 @@ static void get_xsb_name(char * buf, size_t maxsize, int target_stream, xwb_head /* get name offset */ for (i = start_sound; i < xsb.xsb_sounds_count; i++) { xsb_sound *s = &(xsb.xsb_sounds[i]); - VGM_LOG("wa=%i, sel=%i, si=%i vs ti=%i\n", s->wavebank, cfg__selected_wavebank, s->stream_index, target_stream); if (s->wavebank == cfg__selected_wavebank-1 && s->stream_index == target_stream-1){ name_offset = s->name_offset; From c3b637a199a4e5f563dc241d636e83147db2130d Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 3 Feb 2018 17:19:38 +0100 Subject: [PATCH 5/6] Add EA SNS/SPS DSP [Need for Speed Nitro sfx (Wii)] --- src/layout/blocked_ea_sns.c | 23 +++++++++++++++++++-- src/meta/ea_eaac.c | 41 +++++++++++++++++++++++-------------- 2 files changed, 47 insertions(+), 17 deletions(-) diff --git a/src/layout/blocked_ea_sns.c b/src/layout/blocked_ea_sns.c index 12afab00..dda45807 100644 --- a/src/layout/blocked_ea_sns.c +++ b/src/layout/blocked_ea_sns.c @@ -7,6 +7,8 @@ void block_update_ea_sns(off_t block_offset, VGMSTREAM * vgmstream) { STREAMFILE* streamFile = vgmstream->ch[0].streamfile; uint32_t block_size, block_samples; size_t file_size = get_streamfile_size(streamFile); + off_t channel_start; + size_t channel_interleave; int i; /* always BE */ @@ -26,9 +28,26 @@ void block_update_ea_sns(off_t block_offset, VGMSTREAM * vgmstream) { * - in SPS: 0x48=header, 0x44=normal block, 0x45=last block (empty) */ block_size &= 0x00FFFFFF; + switch(vgmstream->coding_type) { + case coding_NGC_DSP: + /* 0x04: unknown (0x00/02), 0x08: some size?, 0x34: null? */ + channel_start = read_32bitBE(block_offset+0x08+0x00,streamFile); + channel_interleave = read_32bitBE(block_offset+0x08+0x0c,streamFile); + /* guessed as all known EA DSP only have one block with subheader (maybe changes coefs every block?) */ + if (channel_start >= 0x40) { + dsp_read_coefs_be(vgmstream,streamFile, block_offset+0x08+0x10,0x28); + dsp_read_hist_be (vgmstream,streamFile, block_offset+0x08+0x30,0x28);//todo guessed and doesn't fix clicks in full loops + } + break; + + default: + channel_start = 0x00; + channel_interleave = 0x00; + break; + } + for (i = 0; i < vgmstream->channels; i++) { - off_t channel_start = 0x00; - vgmstream->ch[i].offset = block_offset + 0x08 + channel_start; + vgmstream->ch[i].offset = block_offset + 0x08 + channel_start + i*channel_interleave; /* also fix first offset (for EALayer3) */ if (block_offset == vgmstream->ch[i].channel_start_offset) { diff --git a/src/meta/ea_eaac.c b/src/meta/ea_eaac.c index 534350af..d693e5b1 100644 --- a/src/meta/ea_eaac.c +++ b/src/meta/ea_eaac.c @@ -15,10 +15,15 @@ VGMSTREAM * init_vgmstream_ea_snr_sns(STREAMFILE * streamFile) { if (!check_extensions(streamFile,"snr")) goto fail; - /* SNR headers normally need an external SNS file, but some have data */ + /* SNR headers normally need an external SNS file, but some have data [Burnout Paradise, NFL2013 (iOS)] */ if (get_streamfile_size(streamFile) > 0x10) { - /* SNR with data (flag 0x40 not set), seen in Burnout Paradise, NFL2013 iOS */ - off_t start_offset = (read_32bitBE(0x08, streamFile) == 0) ? 0x0c : 0x08; + off_t start_offset; + + switch(read_8bit(0x04,streamFile)) { /* flags */ + case 0x60: start_offset = 0x10; break; + case 0x20: start_offset = 0x0c; break; + default: start_offset = 0x08; break; + } vgmstream = init_vgmstream_eaaudiocore_header(streamFile, streamFile, 0x00, start_offset, meta_EA_SNR_SNS); if (!vgmstream) goto fail; @@ -31,11 +36,11 @@ VGMSTREAM * init_vgmstream_ea_snr_sns(STREAMFILE * streamFile) { if (!vgmstream) goto fail; } - if (streamData) close_streamfile(streamData); + close_streamfile(streamData); return vgmstream; fail: - if (streamData) close_streamfile(streamData); + close_streamfile(streamData); return NULL; } @@ -119,7 +124,8 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST sample_rate = (uint16_t)read_16bitBE(header_offset + 0x02,streamHead); flags = (uint8_t)read_8bit(header_offset + 0x04,streamHead); /* upper nibble only? */ num_samples = (uint32_t)read_32bitBE(header_offset + 0x04,streamHead) & 0x00FFFFFF; - /* optional, in some headers: 0x08: null? 0x0c: varies (ex. null, full size) */ + /* rest is optional, depends on flags header used (ex. SNU and SPS may have bigger headers): + * &0x20: 1 int (usually 0x00), &0x00/40: nothing, &0x60: 2 ints (usually 0x00 and 0x14) */ /* V0: SNR+SNS, V1: SPR+SPS (not apparent differences) */ if (version != 0 && version != 1) { @@ -166,14 +172,14 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST /* EA decoder list and known internal FourCCs */ switch(codec) { - case 0x02: /* "P6B0": PCM16BE (NBA Jam Wii) */ + case 0x02: /* "P6B0": PCM16BE [NBA Jam (Wii)] */ vgmstream->coding_type = coding_PCM16_int; vgmstream->codec_endian = 1; vgmstream->layout_type = layout_blocked_ea_sns; break; #ifdef VGM_USE_FFMPEG - case 0x03: { /* "EXm0": EA-XMA (Dante's Inferno X360) */ + case 0x03: { /* "EXm0": EA-XMA [Dante's Inferno (X360)] */ uint8_t buf[0x100]; int bytes, block_size, block_count; size_t stream_size, virtual_size; @@ -200,15 +206,15 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST } #endif - case 0x04: /* "Xas1": EA-XAS (Dead Space PC/PS3) */ + case 0x04: /* "Xas1": EA-XAS [Dead Space (PC/PS3)] */ vgmstream->coding_type = coding_EA_XAS; vgmstream->layout_type = layout_blocked_ea_sns; break; #ifdef VGM_USE_MPEG - case 0x05: /* "EL31": EALayer3 v1 (Need for Speed: Hot Pursuit PS3) */ - case 0x06: /* "L32P": EALayer3 v2 "PCM" (Battlefield 1943 PS3) */ - case 0x07: { /* "L32S": EALayer3 v2 "Spike" (Dante's Inferno PS3) */ + case 0x05: /* "EL31": EALayer3 v1 [Need for Speed: Hot Pursuit (PS3)] */ + case 0x06: /* "L32P": EALayer3 v2 "PCM" [Battlefield 1943 (PS3)] */ + case 0x07: { /* "L32S": EALayer3 v2 "Spike" [Dante's Inferno (PS3)] */ mpeg_custom_config cfg = {0}; off_t mpeg_start_offset = start_offset + 0x08; mpeg_custom_t type = (codec == 0x05 ? MPEG_EAL31b : (codec == 0x06) ? MPEG_EAL32P : MPEG_EAL32S); @@ -222,7 +228,13 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST } #endif -#if 0 //todo unknown variation + case 0x08: /* "Gca0"?: DSP [Need for Speed: Nitro sfx (Wii)] */ + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_blocked_ea_sns; + /* DSP coefs are read in the blocks */ + break; + +#if 0 //todo buffered ATRAC9 #ifdef VGM_USE_ATRAC9 case 0x0a: { /* EATrax */ atrac9_config cfg = {0}; @@ -241,8 +253,7 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST #endif case 0x00: /* "NONE" (internal 'codec not set' flag) */ - case 0x01: /* not used/reserved? Gca0/MP30/P6L0/P2B0/P2L0/P8S0/P8U0/PFN0? */ - case 0x08: /* ? */ + case 0x01: /* not used/reserved? /MP30/P6L0/P2B0/P2L0/P8S0/P8U0/PFN0? */ case 0x09: /* EASpeex (libspeex variant, base versions vary: 1.0.5, 1.2beta3) */ case 0x0b: /* ? */ case 0x0c: /* EAOpus (inside each SNS/SPS block is 16b frame size + standard? Opus packet) */ From ec052385a07bf17a481ac444cb1cee2ce58a68dd Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 3 Feb 2018 19:26:57 +0100 Subject: [PATCH 6/6] Fix SABs with long descriptors [Final Fantasy XV Benchmark sfx (PC)] --- src/meta/sqex_sead.c | 45 ++++++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/src/meta/sqex_sead.c b/src/meta/sqex_sead.c index 95277853..cd2cbf5c 100644 --- a/src/meta/sqex_sead.c +++ b/src/meta/sqex_sead.c @@ -7,8 +7,8 @@ static STREAMFILE* setup_sead_hca_streamfile(STREAMFILE *streamFile, off_t subfi /* SABF/MABF - Square Enix's "sead" audio games [Dragon Quest Builders (PS3), Dissidia Opera Omnia (mobile), FF XV (PS4)] */ VGMSTREAM * init_vgmstream_sqex_sead(STREAMFILE * streamFile) { VGMSTREAM * vgmstream = NULL; - off_t start_offset, mtrl_offset, meta_offset, post_meta_offset; //, info_offset, name_offset = 0; - size_t stream_size, subheader_size, special_size; //, name_size = 0; + off_t start_offset, tables_offset, mtrl_offset, meta_offset, post_meta_offset; //, info_offset, name_offset = 0; + size_t stream_size, descriptor_size, subheader_size, special_size; //, name_size = 0; int loop_flag = 0, channel_count, codec, sample_rate, loop_start, loop_end; @@ -37,38 +37,43 @@ VGMSTREAM * init_vgmstream_sqex_sead(STREAMFILE * streamFile) { // goto fail; /* 0x04(1): version? (usually 0x02, rarely 0x01, ex FF XV title) */ /* 0x05(1): 0x00/01? */ - /* 0x06(2): chunk size? (usually 0x10, rarely 0x20) */ - if (read_16bitBE(0x06,streamFile) < 0x100) { /* use size as no apparent flag */ + /* 0x06(2): version? (usually 0x10, rarely 0x20) */ + if (read_16bitBE(0x06,streamFile) < 0x100) { /* use some value as no apparent flag */ read_32bit = read_32bitBE; read_16bit = read_16bitBE; } else { read_32bit = read_32bitLE; read_16bit = read_16bitLE; } - /* 0x08(1): ?, 0x09(1): ?, 0x0a(2): ? */ + /* 0x08(1): version 0x04?, 0x0a(2): ? */ + descriptor_size = read_8bit(0x09,streamFile); + if (read_32bit(0x0c,streamFile) != get_streamfile_size(streamFile)) goto fail; - /* 0x10(10): file descriptor ("BGM", "Music", "SE", etc) */ + /* 0x10(n): file descriptor ("BGM", "Music", "SE", etc, long names are ok), padded */ + tables_offset = 0x10 + (descriptor_size + 0x01); /* string null seems counted for padding */ + if (tables_offset % 0x10) + tables_offset += 0x10 - (tables_offset % 0x10); /** offset tables **/ if (is_sab) { - if (read_32bitBE(0x20,streamFile) != 0x736E6420) goto fail; /* "snd " (info) */ - if (read_32bitBE(0x30,streamFile) != 0x73657120) goto fail; /* "seq " (unknown) */ - if (read_32bitBE(0x40,streamFile) != 0x74726B20) goto fail; /* "trk " (unknown) */ - if (read_32bitBE(0x50,streamFile) != 0x6D74726C) goto fail; /* "mtrl" (headers/streams) */ - //info_offset = read_32bit(0x28,streamFile); - //seq_offset = read_32bit(0x38,streamFile); - //trk_offset = read_32bit(0x48,streamFile); - mtrl_offset = read_32bit(0x58,streamFile); + if (read_32bitBE(tables_offset+0x00,streamFile) != 0x736E6420) goto fail; /* "snd " (info) */ + if (read_32bitBE(tables_offset+0x10,streamFile) != 0x73657120) goto fail; /* "seq " (unknown) */ + if (read_32bitBE(tables_offset+0x20,streamFile) != 0x74726B20) goto fail; /* "trk " (unknown) */ + if (read_32bitBE(tables_offset+0x30,streamFile) != 0x6D74726C) goto fail; /* "mtrl" (headers/streams) */ + //info_offset = read_32bit(tables_offset+0x08,streamFile); + //seq_offset = read_32bit(tables_offset+0x18,streamFile); + //trk_offset = read_32bit(tables_offset+0x28,streamFile); + mtrl_offset = read_32bit(tables_offset+0x38,streamFile); } else if (is_mab) { - if (read_32bitBE(0x20,streamFile) != 0x6D757363) goto fail; /* "musc" (info) */ - if (read_32bitBE(0x30,streamFile) != 0x696E7374) goto fail; /* "inst" (unknown) */ - if (read_32bitBE(0x40,streamFile) != 0x6D74726C) goto fail; /* "mtrl" (headers/streams) */ - //info_offset = read_32bit(0x28,streamFile); - //inst_offset = read_32bit(0x38,streamFile); - mtrl_offset = read_32bit(0x48,streamFile); + if (read_32bitBE(tables_offset+0x00,streamFile) != 0x6D757363) goto fail; /* "musc" (info) */ + if (read_32bitBE(tables_offset+0x10,streamFile) != 0x696E7374) goto fail; /* "inst" (unknown) */ + if (read_32bitBE(tables_offset+0x20,streamFile) != 0x6D74726C) goto fail; /* "mtrl" (headers/streams) */ + //info_offset = read_32bit(tables_offset+0x08,streamFile); + //inst_offset = read_32bit(tables_offset+0x18,streamFile); + mtrl_offset = read_32bit(tables_offset+0x28,streamFile); } else { goto fail;