From 9dbe0a0d3e46b242d9b416d4a169018cc6f67a07 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Mon, 20 Jun 2022 16:22:07 -0700 Subject: [PATCH] Updated VGMStream to r1745-33-gbb2a6624 Signed-off-by: Christopher Snowhill --- .../vgmstream/src/layout/segmented.c | 2 +- .../vgmstream/vgmstream/src/meta/ea_swvr.c | 2 +- .../vgmstream/vgmstream/src/meta/fsb_keys.h | 4 + Frameworks/vgmstream/vgmstream/src/meta/mul.c | 42 +- .../vgmstream/vgmstream/src/meta/riff.c | 3 + Frameworks/vgmstream/vgmstream/src/meta/sxd.c | 480 +++++++++--------- .../vgmstream/vgmstream/src/meta/txtp.c | 4 +- .../vgmstream/vgmstream/src/meta/ubi_sb.c | 142 ++++-- .../vgmstream/vgmstream/src/vgmstream.c | 2 +- 9 files changed, 391 insertions(+), 290 deletions(-) diff --git a/Frameworks/vgmstream/vgmstream/src/layout/segmented.c b/Frameworks/vgmstream/vgmstream/src/layout/segmented.c index 649a30636..97d32add8 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/segmented.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/segmented.c @@ -336,7 +336,7 @@ VGMSTREAM* allocate_segmented_vgmstream(segmented_layout_data* data, int loop_fl if (!vgmstream) goto fail; vgmstream->meta_type = data->segments[0]->meta_type; - vgmstream->sample_rate = data->segments[0]->sample_rate; + vgmstream->sample_rate = sample_rate; vgmstream->num_samples = num_samples; vgmstream->loop_start_sample = loop_start; vgmstream->loop_end_sample = loop_end; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_swvr.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_swvr.c index 3f3e8ba2a..53b53a52e 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_swvr.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_swvr.c @@ -122,7 +122,7 @@ VGMSTREAM* init_vgmstream_ea_swvr(STREAMFILE* sf) { goto fail; coding = coding_PCM8_U_int; channels = 1; - sample_rate = 16000; /* assumed */ + sample_rate = 22050; /* assumed */ } else { //todo diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h index d84d6f9e5..ac22d41d3 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h @@ -86,6 +86,9 @@ static const uint8_t key_bbf[] = { 0x71,0x6A,0x76,0x6B,0x65,0x6F,0x71,0x6B,0x72, /* Fall Guys (PC)-update */ //"p@4_ih*srN:UJk&8" static const uint8_t key_fgs[] = { 0x70,0x40,0x34,0x5F,0x69,0x68,0x2A,0x73,0x72,0x4E,0x3A,0x55,0x4A,0x6B,0x26,0x38 }; +/* Achilles: Legends Untold (PC) */ //"Achilles_0_15_DpG" +static const uint8_t key_alu[] = { 0x41,0x63,0x68,0x69,0x6C,0x6C,0x65,0x73,0x5F,0x30,0x5F,0x31,0x35,0x5F,0x44,0x70,0x47 }; + // Unknown: // - Battle: Los Angeles // - Guitar Hero: Warriors of Rock, DJ hero FSB @@ -160,6 +163,7 @@ static const fsbkey_info fsbkey_list[] = { { 1,0, sizeof(key_wrb),key_wrb },// FSB5 { 0,0, sizeof(key_bbf),key_bbf },// FSB4 { 1,0, sizeof(key_fgs),key_fgs },// FSB5 + { 1,0, sizeof(key_alu),key_alu },// FSB5 }; static const int fsbkey_list_count = sizeof(fsbkey_list) / sizeof(fsbkey_list[0]); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/mul.c b/Frameworks/vgmstream/vgmstream/src/meta/mul.c index 1b3c5ed54..15186bdf2 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/mul.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/mul.c @@ -1,6 +1,7 @@ #include "meta.h" #include "../layout/layout.h" #include "../coding/coding.h" +#include "../util/endianness.h" #include "mul_streamfile.h" typedef enum { PSX, DSP, IMA, XMA1, FSB4 } mul_codec; @@ -16,15 +17,15 @@ VGMSTREAM* init_vgmstream_mul(STREAMFILE* sf) { int loop_flag, channel_count, sample_rate, num_samples, loop_start; int big_endian; mul_codec codec; - uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL; + read_u32_t read_u32; + read_f32_t read_f32; /* checks */ - /* .mul: found in the exe, used by the bigfile extractor (Gibbed.TombRaider) + /* .mul: found in the exe/reversed names, used by the bigfile extractor (Gibbed.TombRaider) * (some files have companion .mus/sam files but seem to be sequences/control stuff) - * .(extensionless): filenames as found in the bigfile * .emff: fake extension ('Eidos Music File Format') */ - if (!check_extensions(sf, "mul,,emff")) + if (!check_extensions(sf, "mul,emff")) goto fail; if (read_u32be(0x10,sf) != 0 || read_u32be(0x14,sf) != 0 || @@ -34,6 +35,7 @@ VGMSTREAM* init_vgmstream_mul(STREAMFILE* sf) { big_endian = guess_endianness32bit(0x00, sf); read_u32 = big_endian ? read_u32be : read_u32le; + read_f32 = big_endian ? read_f32be : read_f32le; sample_rate = read_u32(0x00,sf); loop_start = read_u32(0x04,sf); @@ -46,13 +48,15 @@ VGMSTREAM* init_vgmstream_mul(STREAMFILE* sf) { /* 0x28: loop offset within audio data (not file offset) */ /* 0x2c: some value related to loop? */ /* 0x34: id? */ - /* 0x38+: channel config until ~0x100? (multiple 0x3F800000 / 1.0f depending on the number of channels) */ + /* 0x38+: channel config until ~0x100? (multiple 1.0f depending on the number of channels) */ - /* test known "version" (some float) later versions start from 0x24 instead of 0x20 */ - if (!(read_u32(0x38,sf) == 0x3F800000 || /* common */ - read_u32(0x38,sf) == 0x4530F000 || /* Avengers */ - read_u32(0x3c,sf) == 0x3F800000)) /* Tomb Raider Underworld */ - goto fail; + /* extra tests just in case (1.0=common, varies but goes around ~2800.0) */ + { + float check1 = read_f32(0x38,sf); + float check2 = read_f32(0x3c,sf); + if (!(check1 >= 1.0 && check1 <= 3000.0) && check2 != 1.0) + goto fail; + } loop_flag = (loop_start >= 0); /* 0xFFFFFFFF when not looping */ @@ -115,13 +119,20 @@ fail: return NULL; } +/* find first block with header info (probably hardcoded) */ static off_t get_start_offset(STREAMFILE* sf) { + uint32_t test1, test2; - /* find first block with header info */ - if (read_u32be(0x0800,sf) != 0 || read_u32be(0x0804,sf) != 0) /* earlier games */ + /* earlier games */ + test1 = read_u32be(0x0800,sf); + test2 = read_u32be(0x0804,sf); + if ((test1 != 0 && test1 != 0xFFFFFFFF) || (test2 != 0 && test2 != 0xFFFFFFFF)) return 0x800; - if (read_u32be(0x2000,sf) != 0 || read_u32be(0x2004,sf) != 0) /* later games */ + /* later games */ + test1 = read_u32be(0x2000,sf); + test2 = read_u32be(0x2004,sf); + if ((test1 != 0 && test1 != 0xFFFFFFFF) || (test2 != 0 && test2 != 0xFFFFFFFF)) return 0x2000; return 0; @@ -167,13 +178,16 @@ static int guess_codec(STREAMFILE* sf, off_t start_offset, int big_endian, int c uint32_t block_size = read_u32(offset+0x04, sf); uint32_t data_size = read_u32(offset+0x10, sf); + if (block_type == 0xFFFFFFFF || block_size == 0xFFFFFFFF || data_size == 0xFFFFFFFF) + return 0; + if (block_type != 0x00) { offset += 0x10 + block_size; continue; /* not audio */ } /* test FSB4 header */ - if (read_u32be(offset + 0x10, sf) == 0x46534234 || read_u32be(offset + 0x20, sf) == 0x46534234) { + if (is_id32be(offset + 0x10, sf, "FSB4") || is_id32be(offset + 0x20, sf, "FSB4")) { *p_codec = FSB4; return 1; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/riff.c b/Frameworks/vgmstream/vgmstream/src/meta/riff.c index bd2999344..d17b8518f 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/riff.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/riff.c @@ -452,6 +452,9 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) { riff_size + 0x08 + 0x08 == file_size || riff_size + 0x08 + 0x09 == file_size || riff_size + 0x08 - 0x3E == file_size || riff_size + 0x08 - 0x02 == file_size)) ignore_riff_size = 1; /* [Cross Gate (PC)] (last info LIST chunk has wrong size) */ + + else if (codec == 0xFFFE && riff_size + 0x08 + 0x40 == file_size) + file_size -= 0x40; /* [Megami no Etsubo (PSP)] (has extra padding in all files) */ } /* check for truncated RIFF */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sxd.c b/Frameworks/vgmstream/vgmstream/src/meta/sxd.c index 3beec8c5a..841a695d0 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sxd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sxd.c @@ -1,239 +1,241 @@ -#include "meta.h" -#include "../coding/coding.h" - - -/* SXD - Sony/SCE's SNDX lib format (cousin of SGXD) [Gravity Rush, Freedom Wars, Soul Sacrifice PSV] */ -VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - STREAMFILE *streamHeader = NULL, *streamExternal = NULL, *streamData = NULL, *streamHead = NULL, *streamBody = NULL; - off_t start_offset, chunk_offset, first_offset = 0x60, name_offset = 0; - size_t chunk_size, stream_size = 0; - - int is_dual, is_external; - int loop_flag, channels, codec, flags; - int sample_rate, num_samples, loop_start_sample, loop_end_sample; - uint32_t at9_config_data = 0; - int total_subsongs, target_subsong = streamFile->stream_index; - - - /* checks */ - /* .sxd: header+data (SXDF) - * .sxd1: header (SXDF) + .sxd2 = data (SXDS) - * .sxd3: sxd1 + sxd2 pasted together (found in some PS4 games, ex. Fate Extella)*/ - if (!check_extensions(streamFile,"sxd,sxd2,sxd3")) - goto fail; - - /* setup head/body variations */ - if (check_extensions(streamFile,"sxd2")) { - /* sxd1+sxd2: open sxd1 as header */ - - streamHead = open_streamfile_by_ext(streamFile, "sxd1"); - if (!streamHead) goto fail; - - streamHeader = streamHead; - streamExternal = streamFile; - is_dual = 1; - } - else if (check_extensions(streamFile,"sxd3")) { - /* sxd3: make subfiles for head and body to simplify parsing */ - off_t sxd1_offset = 0x00; - size_t sxd1_size = read_32bitLE(0x08, streamFile); - off_t sxd2_offset = sxd1_size; - size_t sxd2_size = get_streamfile_size(streamFile) - sxd1_size; - - streamHead = setup_subfile_streamfile(streamFile, sxd1_offset, sxd1_size, "sxd1"); - if (!streamHead) goto fail; - - streamBody = setup_subfile_streamfile(streamFile, sxd2_offset, sxd2_size, "sxd2"); - if (!streamBody) goto fail; - - streamHeader = streamHead; - streamExternal = streamBody; - is_dual = 1; - } - else { - /* sxd: use the current file as header */ - streamHeader = streamFile; - streamExternal = NULL; - is_dual = 0; - } - - if (streamHeader && read_32bitBE(0x00,streamHeader) != 0x53584446) /* "SXDF" */ - goto fail; - if (streamExternal && read_32bitBE(0x00,streamExternal) != 0x53584453) /* "SXDS" */ - goto fail; - - - /* typical chunks: NAME, WAVE and many control chunks (SXDs don't need to contain any sound data) */ - if (!find_chunk_le(streamHeader, 0x57415645,first_offset,0, &chunk_offset,&chunk_size)) /* "WAVE" */ - goto fail; - - /* check multi-streams (usually only in SFX containers) */ - total_subsongs = read_32bitLE(chunk_offset+0x04,streamHeader); - if (target_subsong == 0) target_subsong = 1; - if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; - // todo rarely a WAVE subsong may point to a repeated data offset, with different tags only - - - /* read stream header */ - { - off_t table_offset, header_offset, stream_offset; - - /* get target offset using table of relative offsets within WAVE */ - table_offset = chunk_offset + 0x08 + 4*(target_subsong-1); - header_offset = table_offset + read_32bitLE(table_offset,streamHeader); - - flags = read_32bitLE(header_offset+0x00,streamHeader); - codec = read_8bit (header_offset+0x04,streamHeader); - channels = read_8bit (header_offset+0x05,streamHeader); - /* 0x06(2): unknown, rarely 0xFF */ - sample_rate = read_32bitLE(header_offset+0x08,streamHeader); - /* 0x0c(4): unknown size? (0x4000/0x3999/0x3333/etc, not related to extra data) */ - /* 0x10(4): ? + volume? + pan? (can be 0 for music) */ - num_samples = read_32bitLE(header_offset+0x14,streamHeader); - loop_start_sample = read_32bitLE(header_offset+0x18,streamHeader); - loop_end_sample = read_32bitLE(header_offset+0x1c,streamHeader); - stream_size = read_32bitLE(header_offset+0x20,streamHeader); - stream_offset = read_32bitLE(header_offset+0x24,streamHeader); - - loop_flag = loop_start_sample != -1 && loop_end_sample != -1; - - /* known flag combos: - * 0x00: Chaos Rings 2 sfx (RAM + no tags) - * 0x01: common (RAM + tags) - * 0x02: Chaos Rings 3 sfx (stream + no tags) - * 0x03: common (stream + tags) - * 0x05: Gravity Rush 2 sfx (RAM + tags) */ - //has_tags = flags & 1; - is_external = flags & 2; - //unknown = flags & 4; /* no apparent differences with/without it? */ - - /* flag 1 signals TLV-like extra data. Format appears to be 0x00(1)=tag?, 0x01(1)=extra size*32b?, 0x02(2)=config? - * but not always (ex. size=0x07 but actually=0), perhaps only some bits are used or varies with tag, or are subflags. - * A tag may appear with or without extra data (even 0x0a), 0x01/03/04/06 are common at the beginnig (imply number of tags?), - * 0x64/7F are common at the end (but not always), 0x0A=ATRAC9 config, 0x0B/0C appear with RAM preloading data - * (most variable in Soul Sacrifice; total TLVs size isn't plainly found in the SXD header AFAIK). */ - - /* manually try to find ATRAC9 tag */ - if (codec == 0x42) { - off_t extra_offset = header_offset+0x28; - off_t max_offset = chunk_offset + chunk_size; - - if (!(flags & 1)) - goto fail; - - while (extra_offset < max_offset) { - uint32_t tag = read_32bitBE(extra_offset, streamHeader); - if (tag == 0x0A010000 || tag == 0x0A010600) { - at9_config_data = read_32bitLE(extra_offset+0x04,streamHeader); /* yes, LE */ - break; - } - - extra_offset += 0x04; - } - if (!at9_config_data) - goto fail; - } - - /* usually .sxd=header+data and .sxd1=header + .sxd2=data, but rarely sxd1 may contain data [The Last Guardian (PS4)] */ - if (is_external) { - start_offset = stream_offset; /* absolute if external */ - } else { - start_offset = header_offset+0x24 + stream_offset; /* from current entry offset if internal */ - } - } - - /* get stream name (NAME is tied to REQD/cues, and SFX cues repeat WAVEs, but should work ok for streams) */ - if (is_dual && find_chunk_le(streamHeader, 0x4E414D45,first_offset,0, &chunk_offset,NULL)) { /* "NAME" */ - /* table: relative offset (32b) + hash? (32b) + cue index (32b) */ - int i; - int num_entries = read_16bitLE(chunk_offset+0x04,streamHeader); /* can be bigger than streams */ - for (i = 0; i < num_entries; i++) { - uint32_t index = (uint32_t)read_32bitLE(chunk_offset+0x08 + 0x08 + i*0x0c,streamHeader); - if (index+1 == target_subsong) { - name_offset = chunk_offset+0x08 + 0x00 + i*0x0c + read_32bitLE(chunk_offset+0x08 + 0x00 + i*0x0c,streamHeader); - break; - } - } - } - - if (is_external && !is_dual) { - VGM_LOG("SXD: found single sxd with external data\n"); - goto fail; - } - - /* even dual files may have some non-external streams */ - if (is_external) { - streamData = streamExternal; - } else { - streamData = streamHeader; - } - - if (start_offset > get_streamfile_size(streamData)) { - VGM_LOG("SXD: wrong location?\n"); - goto fail; - } - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channels,loop_flag); - if (!vgmstream) goto fail; - - vgmstream->sample_rate = sample_rate; - vgmstream->num_samples = num_samples; - vgmstream->loop_start_sample = loop_start_sample; - vgmstream->loop_end_sample = loop_end_sample; - vgmstream->num_streams = total_subsongs; - vgmstream->stream_size = stream_size; - vgmstream->meta_type = meta_SXD; - if (name_offset) - read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamHeader); - - switch (codec) { - case 0x20: /* PS-ADPCM [Hot Shots Golf: World Invitational (Vita) sfx] */ - vgmstream->coding_type = coding_PSX; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x10; - break; - - case 0x21: /* HEVAG [Gravity Rush (Vita) sfx] */ - vgmstream->coding_type = coding_HEVAG; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x10; - break; - -#ifdef VGM_USE_ATRAC9 - case 0x42: { /* ATRAC9 [Soul Sacrifice (Vita), Freedom Wars (Vita), Gravity Rush 2 (PS4)] */ - atrac9_config cfg = {0}; - - cfg.channels = vgmstream->channels; - cfg.config_data = at9_config_data; - - vgmstream->codec_data = init_atrac9(&cfg); - if (!vgmstream->codec_data) goto fail; - vgmstream->coding_type = coding_ATRAC9; - vgmstream->layout_type = layout_none; - break; - } -#endif - //case 0x28: /* dummy codec? (found with 0 samples) [Hot Shots Golf: World Invitational (Vita) sfx] */ - default: - VGM_LOG("SXD: unknown codec 0x%x\n", codec); - goto fail; - } - - - /* open the file for reading */ - if (!vgmstream_open_stream(vgmstream,streamData,start_offset)) - goto fail; - - if (streamHead) close_streamfile(streamHead); - if (streamBody) close_streamfile(streamBody); - return vgmstream; - -fail: - if (streamHead) close_streamfile(streamHead); - if (streamBody) close_streamfile(streamBody); - close_vgmstream(vgmstream); - return NULL; -} +#include "meta.h" +#include "../coding/coding.h" + + +/* SXD - Sony/SCE's SNDX lib format (cousin of SGXD) [Gravity Rush, Freedom Wars, Soul Sacrifice PSV] */ +VGMSTREAM* init_vgmstream_sxd(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* sf_sxd1 = NULL, *sf_sxd2 = NULL, *sf_data = NULL, *sf_h = NULL, *sf_b = NULL; + off_t start_offset, chunk_offset, first_offset = 0x60, name_offset = 0; + size_t chunk_size, stream_size = 0; + + int is_dual, is_external; + int loop_flag, channels, codec, sample_rate; + int32_t num_samples, loop_start_sample, loop_end_sample; + uint32_t flags, at9_config_data = 0; + int total_subsongs, target_subsong = sf->stream_index; + + + /* checks */ + /* .sxd: header+data (SXDF) + * .sxd1: header (SXDF) + .sxd2 = data (SXDS) + * .sxd3: sxd1 + sxd2 pasted together (found in some PS4 games, ex. Fate Extella)*/ + if (!check_extensions(sf,"sxd,sxd2,sxd3")) + goto fail; + + /* setup head/body variations */ + if (check_extensions(sf,"sxd2")) { + /* sxd1+sxd2: open sxd1 as header */ + + sf_h = open_streamfile_by_ext(sf, "sxd1"); + if (!sf_h) goto fail; + + sf_sxd1 = sf_h; + sf_sxd2 = sf; + is_dual = 1; + } + else if (check_extensions(sf,"sxd3")) { + /* sxd3: make subfiles for head and body to simplify parsing */ + off_t sxd1_offset = 0x00; + size_t sxd1_size = read_u32le(0x08, sf); + off_t sxd2_offset = sxd1_size; + size_t sxd2_size = get_streamfile_size(sf) - sxd1_size; + + sf_h = setup_subfile_streamfile(sf, sxd1_offset, sxd1_size, "sxd1"); + if (!sf_h) goto fail; + + sf_b = setup_subfile_streamfile(sf, sxd2_offset, sxd2_size, "sxd2"); + if (!sf_b) goto fail; + + sf_sxd1 = sf_h; + sf_sxd2 = sf_b; + is_dual = 1; + } + else { + /* sxd: use the current file as header */ + sf_sxd1 = sf; + sf_sxd2 = NULL; + is_dual = 0; + } + + if (sf_sxd1 && !is_id32be(0x00, sf_sxd1, "SXDF")) + goto fail; + if (sf_sxd2 && !is_id32be(0x00, sf_sxd2, "SXDS")) + goto fail; + + + /* typical chunks: NAME, WAVE and many control chunks (SXDs don't need to contain any sound data) */ + if (!find_chunk_le(sf_sxd1, get_id32be("WAVE"),first_offset,0, &chunk_offset,&chunk_size)) + goto fail; + + /* check multi-streams (usually only in SFX containers) */ + total_subsongs = read_s32le(chunk_offset + 0x04,sf_sxd1); + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + // todo rarely a WAVE subsong may point to a repeated data offset, with different tags only + + + /* read stream header */ + { + uint32_t table_offset, header_offset, stream_offset; + + /* get target offset using table of relative offsets within WAVE */ + table_offset = chunk_offset + 0x08 + 4 * (target_subsong - 1); + header_offset = table_offset + read_32bitLE(table_offset,sf_sxd1); + + flags = read_u32le(header_offset+0x00,sf_sxd1); + codec = read_u8 (header_offset+0x04,sf_sxd1); + channels = read_u8 (header_offset+0x05,sf_sxd1); + /* 0x06(2): unknown, rarely 0xFF */ + sample_rate = read_s32le(header_offset+0x08,sf_sxd1); + /* 0x0c(4): unknown size? (0x4000/0x3999/0x3333/etc, not related to extra data) */ + /* 0x10(4): ? + volume? + pan? (can be 0 for music) */ + num_samples = read_s32le(header_offset+0x14,sf_sxd1); + loop_start_sample = read_s32le(header_offset+0x18,sf_sxd1); + loop_end_sample = read_s32le(header_offset+0x1c,sf_sxd1); + stream_size = read_u32le(header_offset+0x20,sf_sxd1); + stream_offset = read_u32le(header_offset+0x24,sf_sxd1); + + loop_flag = loop_start_sample != -1 && loop_end_sample != -1; + + /* known flag combos: + * 0x00: Chaos Rings 2 sfx (RAM + no tags) + * 0x01: common (RAM + tags) + * 0x02: Chaos Rings 3 sfx (stream + no tags) + * 0x03: common (stream + tags) + * 0x05: Gravity Rush 2 sfx (RAM + tags) */ + //has_tags = flags & 1; + is_external = flags & 2; + //unknown = flags & 4; /* no apparent differences with/without it? */ + + /* flag 1 signals TLV-like extra data. Format appears to be 0x00(1)=tag?, 0x01(1)=extra size*32b?, 0x02(2)=config? + * but not always (ex. size=0x07 but actually=0), perhaps only some bits are used or varies with tag, or are subflags. + * A tag may appear with or without extra data (even 0x0a), 0x01/03/04/06 are common at the beginnig (imply number of tags?), + * 0x64/7F are common at the end (but not always), 0x0A=ATRAC9 config, 0x0B/0C appear with RAM preloading data + * (most variable in Soul Sacrifice; total TLVs size isn't plainly found in the SXD header AFAIK). */ + + /* manually try to find ATRAC9 tag */ + if (codec == 0x42) { + off_t extra_offset = header_offset + 0x28; + off_t max_offset = chunk_offset + chunk_size; + + if (!(flags & 1)) + goto fail; + + while (extra_offset < max_offset) { + uint32_t tag = read_u32be(extra_offset, sf_sxd1); + if (tag == 0x0A010000 || tag == 0x0A010600) { + at9_config_data = read_u32le(extra_offset+0x04, sf_sxd1); /* yes, LE */ + break; + } + + extra_offset += 0x04; + } + if (!at9_config_data) + goto fail; + } + + /* usually .sxd=header+data and .sxd1=header + .sxd2=data, but rarely sxd1 may contain data [The Last Guardian (PS4)] */ + if (is_external) { + start_offset = stream_offset; /* absolute if external */ + } else { + start_offset = header_offset + 0x24 + stream_offset; /* from current entry offset if internal */ + } + } + + /* get stream name (NAME is tied to REQD/cues, and SFX cues repeat WAVEs, but should work ok for streams) */ + if (is_dual && find_chunk_le(sf_sxd1, get_id32be("NAME"),first_offset,0, &chunk_offset,NULL)) { + /* table: relative offset (32b) + hash? (32b) + cue index (32b) */ + int i; + int num_entries = read_s16le(chunk_offset + 0x04, sf_sxd1); /* can be bigger than streams */ + for (i = 0; i < num_entries; i++) { + uint32_t index = read_u32le(chunk_offset + 0x08 + 0x08 + i * 0x0c,sf_sxd1); + if (index+1 == target_subsong) { + name_offset = chunk_offset + 0x08 + 0x00 + i*0x0c + read_u32le(chunk_offset + 0x08 + 0x00 + i * 0x0c, sf_sxd1); + break; + } + } + } + + if (is_external && !is_dual) { + VGM_LOG("SXD: found single sxd with external data\n"); + goto fail; + } + + /* even dual files may have some non-external streams */ + if (is_external) { + sf_data = sf_sxd2; + } else { + sf_data = sf_sxd1; + } + + if (start_offset > get_streamfile_size(sf_data)) { + VGM_LOG("SXD: wrong location?\n"); + goto fail; + } + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_SXD; + vgmstream->sample_rate = sample_rate; + + vgmstream->num_samples = num_samples; + vgmstream->loop_start_sample = loop_start_sample; + vgmstream->loop_end_sample = loop_end_sample; + + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; + if (name_offset) + read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,sf_sxd1); + + switch (codec) { + case 0x20: /* PS-ADPCM [Hot Shots Golf: World Invitational (Vita) sfx] */ + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x10; + break; + + case 0x21: /* HEVAG [Gravity Rush (Vita) sfx] */ + vgmstream->coding_type = coding_HEVAG; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x10; + break; + +#ifdef VGM_USE_ATRAC9 + case 0x42: { /* ATRAC9 [Soul Sacrifice (Vita), Freedom Wars (Vita), Gravity Rush 2 (PS4)] */ + atrac9_config cfg = {0}; + + cfg.channels = vgmstream->channels; + cfg.config_data = at9_config_data; + + vgmstream->codec_data = init_atrac9(&cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_ATRAC9; + vgmstream->layout_type = layout_none; + break; + } +#endif + //case 0x28: /* dummy codec? (found with 0 samples) [Hot Shots Golf: World Invitational (Vita) sfx] */ + default: + VGM_LOG("SXD: unknown codec 0x%x\n", codec); + goto fail; + } + + + /* open the file for reading */ + if (!vgmstream_open_stream(vgmstream, sf_data, start_offset)) + goto fail; + + if (sf_h) close_streamfile(sf_h); + if (sf_b) close_streamfile(sf_b); + return vgmstream; + +fail: + if (sf_h) close_streamfile(sf_h); + if (sf_b) close_streamfile(sf_b); + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/txtp.c b/Frameworks/vgmstream/vgmstream/src/meta/txtp.c index 72df65d74..69f5e50fb 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/txtp.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/txtp.c @@ -290,7 +290,7 @@ static int parse_entries(txtp_header* txtp, STREAMFILE* sf) { else temp_sf = open_streamfile_by_filename(sf, filename); /* from current path */ if (!temp_sf) { - VGM_LOG("TXTP: cannot open streamfile for %s\n", filename); + vgm_logi("TXTP: cannot open %s\n", filename); goto fail; } temp_sf->stream_index = txtp->entry[i].subsong; @@ -298,7 +298,7 @@ static int parse_entries(txtp_header* txtp, STREAMFILE* sf) { txtp->vgmstream[i] = init_vgmstream_from_STREAMFILE(temp_sf); close_streamfile(temp_sf); if (!txtp->vgmstream[i]) { - VGM_LOG("TXTP: cannot open vgmstream for %s#%i\n", filename, txtp->entry[i].subsong); + vgm_logi("TXTP: cannot parse %s#%i\n", filename, txtp->entry[i].subsong); goto fail; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c b/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c index 10bbdf39c..7003f46ed 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c @@ -2,11 +2,13 @@ #include "meta.h" #include "../layout/layout.h" #include "../coding/coding.h" +#include "../util/endianness.h" #include "ubi_sb_streamfile.h" #define SB_MAX_LAYER_COUNT 16 /* arbitrary max */ #define SB_MAX_CHAIN_COUNT 256 /* +150 exist in Tonic Trouble */ +#define SB_MAX_SUBSONGS 128000 /* arbitrary max to detect incorrect reads */ #define LAYER_HIJACK_GRAW_X360 1 #define LAYER_HIJACK_SCPT_PS2 2 @@ -130,24 +132,26 @@ typedef struct { int is_ps2_bnm; int is_blk; int has_numbered_banks; + + int header_init; STREAMFILE* sf_header; uint32_t version; /* 16b+16b major+minor version */ uint32_t version_empty; /* map sbX versions are empty */ /* events (often share header_id/type with some descriptors, * but may exist without headers or header exist without them) */ - size_t section1_num; + uint32_t section1_num; off_t section1_offset; /* descriptors, audio header or other config types */ - size_t section2_num; + uint32_t section2_num; off_t section2_offset; /* internal streams table, referenced by each header */ - size_t section3_num; + uint32_t section3_num; off_t section3_offset; /* section with sounds in some map versions */ - size_t section4_num; + uint32_t section4_num; off_t section4_offset; /* extra table, config for certain types (DSP coefs, external resources, layer headers, etc) */ - size_t sectionX_size; + uint32_t sectionX_size; off_t sectionX_offset; /* sound bank size */ size_t bank_size; @@ -210,6 +214,7 @@ static VGMSTREAM* init_vgmstream_ubi_sb_header(ubi_sb_header* sb, STREAMFILE* sf static VGMSTREAM *init_vgmstream_ubi_sb_silence(ubi_sb_header *sb); static int config_sb_platform(ubi_sb_header* sb, STREAMFILE* sf); static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf); +static int init_sb_header(ubi_sb_header* sb, STREAMFILE* sf); /* .SBx - banks from Ubisoft's DARE (Digital Audio Rendering Engine) engine games in ~2000-2008+ */ @@ -249,32 +254,9 @@ VGMSTREAM* init_vgmstream_ubi_sb(STREAMFILE* sf) { if (!config_sb_version(&sb, sf)) goto fail; + if (!init_sb_header(&sb, sf)) + goto fail; - if (sb.version <= 0x0000000B) { - sb.section1_num = read_32bit(0x04, sf); - sb.section2_num = read_32bit(0x0c, sf); - sb.section3_num = read_32bit(0x14, sf); - sb.sectionX_size = read_32bit(0x1c, sf); - - sb.section1_offset = 0x20; - } else if (sb.version <= 0x000A0000) { - sb.section1_num = read_32bit(0x04, sf); - sb.section2_num = read_32bit(0x08, sf); - sb.section3_num = read_32bit(0x0c, sf); - sb.sectionX_size = read_32bit(0x10, sf); - sb.flag1 = read_32bit(0x14, sf); - - sb.section1_offset = 0x18; - } else { - sb.section1_num = read_32bit(0x04, sf); - sb.section2_num = read_32bit(0x08, sf); - sb.section3_num = read_32bit(0x0c, sf); - sb.sectionX_size = read_32bit(0x10, sf); - sb.flag1 = read_32bit(0x14, sf); - sb.flag2 = read_32bit(0x18, sf); - - sb.section1_offset = 0x1c; - } if (sb.cfg.is_padded_section1_offset) sb.section1_offset = align_size_to_block(sb.section1_offset, 0x10); @@ -2798,6 +2780,102 @@ static int check_project_file(STREAMFILE *sf_header, const char *name, int has_l return 0; } + +/* Each entry in section1/2 has a type of 16b+16b group+sound identifier. May start from 0 (rarely) but + * always are low-ish numbers, so can be used to a point to detect if entries are correct with some entry_size. */ +static int test_version_sb_entry(ubi_sb_header* sb, STREAMFILE* sf, uint32_t offset, int count, uint32_t entry_size) { + read_u32_t read_u32 = sb->big_endian ? read_u32be : read_u32le; + uint32_t prev_group = 0; + int i; + + prev_group = 0; + for (i = 0; i < count; i++) { + uint32_t curr = read_u32(offset, sf); + uint16_t group, sound; + + if (i > 1 && curr == 0) + return 0; + + /* max seen in ~0x0200 */ + group = (curr >> 16) & 0xFFFF; + sound = (curr >> 0) & 0xFFFF; + if (group > 0x1000 || sound > 0x1000) + return 0; + + /* sounds aren't always ordered, but seems groups are */ + if (prev_group && group < prev_group) + return 0; + + prev_group = group; + offset += entry_size; + } + + return 1; +} + +/* Checks if matches entry sizes, for cases where same ID is reused. Only for SB fow now. */ +static int test_version_sb(ubi_sb_header* sb, STREAMFILE* sf, uint32_t section1_size_entry, uint32_t section2_size_entry) { + uint32_t offset; + + if (!init_sb_header(sb, sf)) + return 0; + + if (sb->section2_num == 0) /* no waves = no point to detect */ + return 0; + + offset = sb->section1_offset; + if (!test_version_sb_entry(sb, sf, offset, sb->section1_num, section1_size_entry)) + return 0; + + offset = sb->section1_offset + sb->section1_num * section1_size_entry; + if (!test_version_sb_entry(sb, sf, offset, sb->section2_num, section2_size_entry)) + return 0; + + return 1; +} + +static int init_sb_header(ubi_sb_header* sb, STREAMFILE* sf) { + read_u32_t read_u32 = sb->big_endian ? read_u32be : read_u32le; + + if (sb->header_init) + return 1; + + if (sb->version <= 0x0000000B) { + sb->section1_num = read_u32(0x04, sf); + sb->section2_num = read_u32(0x0c, sf); + sb->section3_num = read_u32(0x14, sf); + sb->sectionX_size = read_u32(0x1c, sf); + + sb->section1_offset = 0x20; + } + else if (sb->version <= 0x000A0000) { + sb->section1_num = read_u32(0x04, sf); + sb->section2_num = read_u32(0x08, sf); + sb->section3_num = read_u32(0x0c, sf); + sb->sectionX_size = read_u32(0x10, sf); + sb->flag1 = read_u32(0x14, sf); + + sb->section1_offset = 0x18; + } + else { + sb->section1_num = read_u32(0x04, sf); + sb->section2_num = read_u32(0x08, sf); + sb->section3_num = read_u32(0x0c, sf); + sb->sectionX_size = read_u32(0x10, sf); + sb->flag1 = read_u32(0x14, sf); + sb->flag2 = read_u32(0x18, sf); + + sb->section1_offset = 0x1c; + } + + if (sb->section1_num > SB_MAX_SUBSONGS || sb->section2_num > SB_MAX_SUBSONGS || sb->section3_num > SB_MAX_SUBSONGS) + return 0; + + sb->header_init = 1; + return 1; +} + + static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { int is_ttse_pc = 0; int is_bia_ps2 = 0, is_biadd_psp = 0; @@ -3478,9 +3556,9 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { return 1; } - /* two configs with same id; use project file as identifier */ + /* two configs with same id; try to autodetect or use project file as identifier */ if (sb->version == 0x00120006 && sb->platform == UBI_PC) { - if (check_project_file(sf, "gamesnd_myst4.sp0", 1)) { + if (test_version_sb(sb, sf, 0x6c, 0xa4) || check_project_file(sf, "gamesnd_myst4.sp0", 1)) { is_myst4_pc = 1; } } diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.c b/Frameworks/vgmstream/vgmstream/src/vgmstream.c index dd87b4bd8..7a9895611 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.c +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.c @@ -528,7 +528,6 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = { init_vgmstream_bw_riff_mp3, /* lower priority metas (no clean header identity, somewhat ambiguous, or need extension/companion file to identify) */ - init_vgmstream_mpeg, init_vgmstream_agsc, init_vgmstream_dtk, init_vgmstream_rsf, @@ -543,6 +542,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = { /* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */ init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */ + init_vgmstream_mpeg, /* semi-raw MP3 */ init_vgmstream_encrypted, /* encrypted stuff */ init_vgmstream_btsnd, /* semi-headerless */ init_vgmstream_raw_int, /* .int raw PCM */