From 58384cb831fd36d681a35f1632cf6e9b925b4393 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Sun, 14 Nov 2021 21:43:04 -0800 Subject: [PATCH] Updated VGMStream to r1667-90-gc57f4383 --- Frameworks/vgmstream/vgmstream/src/formats.c | 1 + .../vgmstream/vgmstream/src/meta/bkhd.c | 5 +- .../vgmstream/vgmstream/src/meta/ea_schl.c | 25 +- .../vgmstream/vgmstream/src/meta/encrypted.c | 244 ++- .../vgmstream/vgmstream/src/meta/fsb_keys.h | 4 + .../vgmstream/vgmstream/src/meta/hca_bf.h | 25 +- .../vgmstream/vgmstream/src/meta/hca_keys.h | 41 + .../vgmstream/vgmstream/src/meta/meta.h | 3 +- .../vgmstream/src/meta/ngc_dsp_std.c | 328 ++-- Frameworks/vgmstream/vgmstream/src/meta/psf.c | 1533 +++++++++-------- .../vgmstream/vgmstream/src/meta/riff.c | 22 +- .../vgmstream/vgmstream/src/meta/sqex_scd.c | 3 +- .../vgmstream/vgmstream/src/meta/wwise.c | 48 +- 13 files changed, 1236 insertions(+), 1046 deletions(-) diff --git a/Frameworks/vgmstream/vgmstream/src/formats.c b/Frameworks/vgmstream/vgmstream/src/formats.c index 597b6dfb5..87920ebdd 100644 --- a/Frameworks/vgmstream/vgmstream/src/formats.c +++ b/Frameworks/vgmstream/vgmstream/src/formats.c @@ -465,6 +465,7 @@ static const char* extension_list[] = { "seb", "sed", "seg", + "sem", //txth/reserved [Oretachi Game Center Zoku: Sonic Wings (PS2)] "sf0", "sfl", "sfs", diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bkhd.c b/Frameworks/vgmstream/vgmstream/src/meta/bkhd.c index 459e91479..c2eb3723a 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/bkhd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/bkhd.c @@ -14,6 +14,7 @@ VGMSTREAM* init_vgmstream_bkhd(STREAMFILE* sf) { uint32_t (*read_u32)(off_t,STREAMFILE*); float (*read_f32)(off_t,STREAMFILE*); int total_subsongs, target_subsong = sf->stream_index; + int prefetch = 0; /* checks */ @@ -151,7 +152,7 @@ VGMSTREAM* init_vgmstream_bkhd(STREAMFILE* sf) { if (!temp_sf) goto fail; if (is_riff) { - vgmstream = init_vgmstream_wwise(temp_sf); + vgmstream = init_vgmstream_wwise_bnk(temp_sf, &prefetch); if (!vgmstream) goto fail; } else { @@ -178,6 +179,8 @@ VGMSTREAM* init_vgmstream_bkhd(STREAMFILE* sf) { snprintf(vgmstream->stream_name, STREAM_NAME_SIZE, "%u/%s", subfile_id, info); else snprintf(vgmstream->stream_name, STREAM_NAME_SIZE, "%u", subfile_id); + if (prefetch) + concatn(STREAM_NAME_SIZE, vgmstream->stream_name, " [pre]"); } close_streamfile(temp_sf); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c index 851bfe6c6..fe296deb5 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c @@ -122,8 +122,8 @@ typedef struct { size_t stream_size; } ea_header; -static VGMSTREAM * parse_schl_block(STREAMFILE* sf, off_t offset, int standalone); -static VGMSTREAM * parse_bnk_header(STREAMFILE* sf, off_t offset, int target_stream, int is_embedded); +static VGMSTREAM* parse_schl_block(STREAMFILE* sf, off_t offset, int standalone); +static VGMSTREAM* parse_bnk_header(STREAMFILE* sf, off_t offset, int target_stream, int is_embedded); static int parse_variable_header(STREAMFILE* sf, ea_header* ea, off_t begin_offset, int max_length, int bnk_version); static uint32_t read_patch(STREAMFILE* sf, off_t* offset); static off_t get_ea_stream_mpeg_start_offset(STREAMFILE* sf, off_t start_offset, const ea_header* ea); @@ -1538,17 +1538,18 @@ static int parse_variable_header(STREAMFILE* sf, ea_header* ea, off_t begin_offs ea->codec2 = EA_CODEC2_NONE; /* get platform info */ - platform_id = read_32bitBE(offset, sf); - if (platform_id != 0x47535452 && (platform_id & 0xFFFF0000) != 0x50540000) { + platform_id = read_u32be(offset, sf); + if (platform_id != get_id32be("GSTR") && (platform_id & 0xFFFF0000) != get_id32be("PT\0\0")) { offset += 4; /* skip unknown field (related to blocks/size?) in "nbapsstream" (NBA2000 PS, FIFA2001 PS) */ - platform_id = read_32bitBE(offset, sf); + platform_id = read_u32be(offset, sf); } - if (platform_id == 0x47535452) { /* "GSTR" = Generic STReam */ + + if (platform_id == get_id32be("GSTR")) { /* Generic STReam */ ea->platform = EA_PLATFORM_GENERIC; offset += 4 + 4; /* GSTRs have an extra field (config?): ex. 0x01000000, 0x010000D8 BE */ } - else if ((platform_id & 0xFFFF0000) == 0x50540000) { /* "PT" = PlaTform */ - ea->platform = (uint16_t)read_16bitLE(offset + 2,sf); + else if ((platform_id & 0xFFFF0000) == get_id32be("PT\0\0")) { /* PlaTform */ + ea->platform = read_u16le(offset + 2,sf); offset += 4; } else { @@ -1558,7 +1559,7 @@ static int parse_variable_header(STREAMFILE* sf, ea_header* ea, off_t begin_offs /* parse mini-chunks/tags (variable, ommited if default exists; some are removed in later versions of sx.exe) */ while (!is_header_end && offset - begin_offset < max_length) { - uint8_t patch_type = read_8bit(offset,sf); + uint8_t patch_type = read_u8(offset,sf); offset++; //;{ off_t test = offset; VGM_LOG("EA SCHl: patch=%02x at %lx, value=%x\n", patch_type, offset-1, read_patch(sf, &test)); } @@ -1568,6 +1569,8 @@ static int parse_variable_header(STREAMFILE* sf, ea_header* ea, off_t begin_offs read_patch(sf, &offset); break; + case 0x03: /* unknown (0x3c, rare: Madden NFL 2001 PS1) */ + case 0x04: /* unknown (0x3c, rare: Madden NFL 2001 PS1) */ case 0x05: /* unknown (usually 0x50 except Madden NFL 3DS: 0x3e800) */ case 0x06: /* priority (0..100, always 0x65 for streams, others for BNKs; rarely ommited) */ case 0x07: /* unknown (BNK only: 36|3A|40) */ @@ -1734,7 +1737,7 @@ static int parse_variable_header(STREAMFILE* sf, ea_header* ea, off_t begin_offs break; default: - VGM_LOG("EA SCHl: unknown patch 0x%02x\n", patch_type); + VGM_LOG("EA SCHl: unknown patch 0x%02x at %x\n", patch_type, (uint32_t)offset); goto fail; } } @@ -1901,7 +1904,7 @@ fail: return 0; } -static void update_ea_stream_size(STREAMFILE* sf, off_t start_offset, VGMSTREAM *vgmstream) { +static void update_ea_stream_size(STREAMFILE* sf, off_t start_offset, VGMSTREAM* vgmstream) { uint32_t block_id; size_t stream_size = 0, file_size; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/encrypted.c b/Frameworks/vgmstream/vgmstream/src/meta/encrypted.c index 5075f34c6..123c6d680 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/encrypted.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/encrypted.c @@ -16,98 +16,176 @@ static void load_key(ogg_vorbis_io_config_data* cfg, const uint8_t* key, size_t memcpy(cfg->key, key, size); } -/* parser for various encrypted games */ -VGMSTREAM* init_vgmstream_encrypted(STREAMFILE* sf) { - VGMSTREAM* vgmstream = NULL; - STREAMFILE* temp_sf = NULL; - ogg_vorbis_io_config_data cfg = {0}; +typedef struct { uint32_t id; + uint8_t keybuf[0x100]; + size_t key_size; + off_t start; + + VGMSTREAM* vgmstream; + STREAMFILE* temp_sf; + STREAMFILE* new_sf; +} encrypted_t; - /* checks */ - id = read_u32be(0x00, sf); +/* The Pirate's Fate (PC) */ +static VGMSTREAM* init_vgmstream_encrypted_ogg_tpf(STREAMFILE* sf) { + ogg_vorbis_io_config_data cfg = {0}; + encrypted_t e = {0}; - if (check_extensions(sf,"ogg,logg")) { - /* The Pirate's Fate (PC) */ - if (id == 0x454C513C) { /* "OggS" xored */ - load_key(&cfg, tpf_key, sizeof(tpf_key)); - } - else { - goto fail; - } + e.id = read_u32be(0x00, sf); + if (e.id != 0x454C513C) /* "OggS" xored */ + goto fail; - temp_sf = setup_ogg_vorbis_streamfile(sf, &cfg); - if (!temp_sf) goto fail; + if (!check_extensions(sf,"ogg,logg")) + goto fail; - vgmstream = init_vgmstream_ogg_vorbis(temp_sf); - close_streamfile(temp_sf); - return vgmstream; - } + load_key(&cfg, tpf_key, sizeof(tpf_key)); - if (check_extensions(sf,"mp3")) { - /* The Pirate's Fate (PC) */ - if ((id & 0xFFFFFF00) == 0x436F0500) { /* "ID3\0" xored */ - load_key(&cfg, tpf_key, sizeof(tpf_key)); - } - else { - goto fail; - } - - temp_sf = setup_ogg_vorbis_streamfile(sf, &cfg); - if (!temp_sf) goto fail; - -#ifdef VGM_USE_FFMPEG //TODO: allow MP3 without FFmpeg - vgmstream = init_vgmstream_ffmpeg(temp_sf); -#endif - close_streamfile(temp_sf); - return vgmstream; - } - - if (check_extensions(sf,"wav,lwav")) { - /* The Pirate's Fate (PC) */ - if (id == 0x58627029) { /* "RIFF" xored */ - load_key(&cfg, tpf_key, sizeof(tpf_key)); - } - else { - goto fail; - } - - temp_sf = setup_ogg_vorbis_streamfile(sf, &cfg); - if (!temp_sf) goto fail; - - vgmstream = init_vgmstream_riff(temp_sf); - close_streamfile(temp_sf); - return vgmstream; - } - - if (check_extensions(sf,"bgm,mse,koe")) { - /* Studio Ring games [Nanami to Konomi no Oshiete ABC (PC), Oyatsu no Jikan (PC)] - * .bgm: BGM, .mse: SE, .koe: Voice */ - uint8_t keybuf[0x100]; - size_t key_size; - off_t start; - - if (id != get_id32be("RIFF")) - goto fail; - - /* Standard RIFF xor'd past "data", sometimes including extra chunks like JUNK or smpl. - * If .bgm is added to riff.c this needs to be reworked so detection goes first, or bgm+bgmkey is - * rejected in riff.c (most files are rejected due to the xor'd extra chunks though). */ - key_size = read_key_file(keybuf, sizeof(keybuf), sf); - if (key_size <= 0) goto fail; - - if (!find_chunk_le(sf, get_id32be("data"), 0x0c, 0, &start, NULL)) - goto fail; - - temp_sf = setup_bgm_streamfile(sf, start, keybuf, key_size); - if (!temp_sf) goto fail; - - vgmstream = init_vgmstream_riff(temp_sf); - close_streamfile(temp_sf); - return vgmstream; - } + e.temp_sf = setup_ogg_vorbis_streamfile(sf, &cfg); + if (!e.temp_sf) goto fail; + e.vgmstream = init_vgmstream_ogg_vorbis(e.temp_sf); + close_streamfile(e.temp_sf); + return e.vgmstream; fail: return NULL; } + +/* The Pirate's Fate (PC) */ +static VGMSTREAM* init_vgmstream_encrypted_mp3_tpf(STREAMFILE* sf) { + ogg_vorbis_io_config_data cfg = {0}; + encrypted_t e = {0}; + + e.id = read_u32be(0x00, sf); + if ((e.id & 0xFFFFFF00) != 0x436F0500) /* "ID3\0" xored */ + goto fail; + + if (!check_extensions(sf,"mp3")) + goto fail; + + load_key(&cfg, tpf_key, sizeof(tpf_key)); + + e.temp_sf = setup_ogg_vorbis_streamfile(sf, &cfg); + if (!e.temp_sf) goto fail; + +#ifdef VGM_USE_FFMPEG //TODO: allow MP3 without FFmpeg + e.vgmstream = init_vgmstream_ffmpeg(e.temp_sf); +#endif + close_streamfile(e.temp_sf); + return e.vgmstream; + +fail: + return NULL; +} + +/* Studio Ring games [Nanami to Konomi no Oshiete ABC (PC), Oyatsu no Jikan (PC)] */ +static VGMSTREAM* init_vgmstream_encrypted_riff(STREAMFILE* sf) { + encrypted_t e = {0}; + + e.id = read_u32be(0x00, sf); + if (e.id != get_id32be("RIFF")) + goto fail; + + /* .bgm: BGM, .mse: SE, .koe: Voice */ + if (!check_extensions(sf,"bgm,mse,koe")) + goto fail; + + /* Standard RIFF xor'd past "data", sometimes including extra chunks like JUNK or smpl. + * If .bgm/etc is added to riff.c this needs to be reworked so detection goes first, or bgm+bgmkey is + * rejected in riff.c (most files are rejected due to the xor'd extra chunks though). */ + e.key_size = read_key_file(e.keybuf, sizeof(e.keybuf), sf); + if (e.key_size <= 0) goto fail; + + if (!find_chunk_le(sf, get_id32be("data"), 0x0c, 0, &e.start, NULL)) + goto fail; + + e.temp_sf = setup_bgm_streamfile(sf, e.start, e.keybuf, e.key_size); + if (!e.temp_sf) goto fail; + + e.vgmstream = init_vgmstream_riff(e.temp_sf); + close_streamfile(e.temp_sf); + return e.vgmstream; + +fail: + return NULL; +} + +/* RPGMVO / Omori (PC) */ +static VGMSTREAM* init_vgmstream_encrypted_rpgmvo_riff(STREAMFILE* sf) { + ogg_vorbis_io_config_data cfg = {0}; + encrypted_t e = {0}; + uint32_t xor; + uint32_t riff_size; + + if (!is_id64be(0x00, sf, "RPGMV\0\0\0")) + goto fail; + + if (!check_extensions(sf,"rpgmvo")) + goto fail; + + /* 0x08: version? */ + + + /* Ogg .rpgmvo has per-game key, so this is probably the same. Reversing key is simple though, + * calc suspected key and pass to RIFF to validate (format is the same as Ogg) */ + riff_size = get_streamfile_size(sf) - 0x10; + + xor = read_u32be(0x10, sf); + xor ^= get_id32be("RIFF"); + put_u32be(e.keybuf + 0x00, xor); + + xor = read_u32le(0x14, sf); + xor ^= riff_size - 0x08; + put_u32le(e.keybuf + 0x04, xor); + + xor = read_u32be(0x18, sf); + xor ^= get_id32be("WAVE"); + put_u32be(e.keybuf + 0x08, xor); + + xor = read_u32be(0x1c, sf); + xor ^= get_id32be("fmt "); + put_u32be(e.keybuf + 0x0c, xor); + + e.key_size = 0x10; + load_key(&cfg, e.keybuf, e.key_size); + cfg.start = 0x10; + cfg.max_offset = 0x10; + + e.temp_sf = setup_ogg_vorbis_streamfile(sf, &cfg); + if (!e.temp_sf) goto fail; + + e.new_sf = setup_subfile_streamfile(e.temp_sf, 0x10, riff_size, "wav"); + if (!e.new_sf) goto fail; +dump_streamfile(e.new_sf, 0); + e.vgmstream = init_vgmstream_riff(e.new_sf); + close_streamfile(e.temp_sf); + close_streamfile(e.new_sf); + return e.vgmstream; + +fail: + close_streamfile(e.temp_sf); + close_streamfile(e.new_sf); + return NULL; +} + + +/* parser for various encrypted games */ +VGMSTREAM* init_vgmstream_encrypted(STREAMFILE* sf) { + VGMSTREAM* v = NULL; + + v = init_vgmstream_encrypted_ogg_tpf(sf); + if (v) return v; + + v = init_vgmstream_encrypted_mp3_tpf(sf); + if (v) return v; + + v = init_vgmstream_encrypted_riff(sf); + if (v) return v; + + v = init_vgmstream_encrypted_rpgmvo_riff(sf); + if (v) return v; + + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h index 2084f70b5..d84d6f9e5 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h @@ -83,6 +83,9 @@ static const uint8_t key_wrb[] = { 0x46,0x58,0x6E,0x54,0x66,0x66,0x47,0x4A,0x39, /* Bubble Fighter (PC) */ //"qjvkeoqkrdhkdckd" static const uint8_t key_bbf[] = { 0x71,0x6A,0x76,0x6B,0x65,0x6F,0x71,0x6B,0x72,0x64,0x68,0x6B,0x64,0x63,0x6B,0x64 }; +/* 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 }; + // Unknown: // - Battle: Los Angeles // - Guitar Hero: Warriors of Rock, DJ hero FSB @@ -156,6 +159,7 @@ static const fsbkey_info fsbkey_list[] = { { 0,1, sizeof(key_ghm),key_ghm },// FSB4 { 1,0, sizeof(key_wrb),key_wrb },// FSB5 { 0,0, sizeof(key_bbf),key_bbf },// FSB4 + { 1,0, sizeof(key_fgs),key_fgs },// FSB5 }; static const int fsbkey_list_count = sizeof(fsbkey_list) / sizeof(fsbkey_list[0]); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/hca_bf.h b/Frameworks/vgmstream/vgmstream/src/meta/hca_bf.h index 8d43460a5..0dd1cb15e 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/hca_bf.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/hca_bf.h @@ -40,14 +40,13 @@ static void bruteforce_hca_key_bin_type(STREAMFILE* sf, hca_codec_data* hca_data hca_keytest_t hk = {0}; hk.subkey = subkey; - + *p_keycode = 0; /* load whole file in memory for performance (exes with keys shouldn't be too big) */ sf_keys = open_streamfile_by_filename(sf, "keys.bin"); if (!sf_keys) return; VGM_LOG("HCA BF: test keys.bin (type %i)\n", type); - *p_keycode = 0; keys_size = get_streamfile_size(sf_keys); @@ -110,16 +109,14 @@ done: static void bruteforce_hca_key_bin(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* p_keycode, uint16_t subkey) { bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64LE_1); -/* - bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32LE_1); - bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64BE_1); - bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32BE_1); + //bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32LE_1); + //bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64BE_1); + //bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32BE_1); - bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64LE_4); - bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32LE_4); - bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64BE_4); - bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32BE_4); -*/ + //bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64LE_4); + //bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32LE_4); + //bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_64BE_4); + //bruteforce_hca_key_bin_type(sf, hca_data, p_keycode, subkey, HBF_TYPE_32BE_4); } @@ -137,14 +134,13 @@ static void bruteforce_hca_key_txt(STREAMFILE* sf, hca_codec_data* hca_data, uns hca_keytest_t hk = {0}; hk.subkey = subkey; - + *p_keycode = 0; /* load whole file in memory for performance (exes with keys shouldn't be too big) */ sf_keys = open_streamfile_by_filename(sf, "keys.txt"); if (!sf_keys) return; VGM_LOG("HCA BF: test keys.txt\n"); - *p_keycode = 0; keys_size = get_streamfile_size(sf_keys); @@ -198,14 +194,13 @@ static void bruteforce_hca_key_num(STREAMFILE* sf, hca_codec_data* hca_data, uns hca_keytest_t hk = {0}; hk.subkey = subkey; - + *p_keycode = 0; /* load whole file in memory for performance (exes with keys shouldn't be too big) */ sf_keys = open_streamfile_by_filename(sf, "keys.num"); if (!sf_keys) return; VGM_LOG("HCA BF: test keys.num\n"); - *p_keycode = 0; keys_size = get_streamfile_size(sf_keys); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h index 2708fa19f..040798581 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h @@ -436,6 +436,8 @@ static const hcakey_info hcakey_list[] = { {0x750beaf22ddc700b}, //music_0110018 {0x16ccc93f976a8329}, //music_0110019 {0x9f7a0810034669fe}, //music_0110020 + {0xe8333d53d2779e38}, //music_0110021 + {0x2cdcac4f44f67075}, //music_0110022 {0xfb647d074e53fab6}, //music_0120001 {0xc24049b9f7ed3105}, //music_0120002 {0xdc128f2fd48bf4b}, //music_0120003 @@ -465,6 +467,7 @@ static const hcakey_info hcakey_list[] = { {0xff04547fe629c8bf}, //music_0210009 {0x5ef795cdbcdcba91}, //music_0210010 {0x868acc0102c59a38}, //music_0210011 + {0x6dc5ff77263450a5}, //music_0210012 {0x15bb78c31db0a0b6}, //music_0220001 {0x59b1257242c40109}, //music_0220002 {0xdb402bd08d522f34}, //music_0220003 @@ -481,6 +484,7 @@ static const hcakey_info hcakey_list[] = { {0x4aa31e0c4f787a8}, //music_0220014 {0x94466db0d3c10f4b}, //music_0220015 {0xe6d1fd6effa46736}, //music_0220017 + {0xfceaa73248868ec5}, //music_0220019 {0x6a15a9610d10d210}, //music_0310001 {0x57111c24801b44a1}, //music_0310002 {0x40443974a0a86b8b}, //music_0310003 @@ -493,6 +497,7 @@ static const hcakey_info hcakey_list[] = { {0xdd9ca800a7123d6f}, //music_0310010 {0xc86f8564e0b9078c}, //music_0310011 {0xcc5610c09f472ce9}, //music_0310012 + {0xd447a497c5547a1c}, //music_0310013 {0xb921c3992807dadd}, //music_0320001 {0x38ad99a045dc971f}, //music_0320002 {0xf616642579ba5850}, //music_0320003 @@ -519,6 +524,7 @@ static const hcakey_info hcakey_list[] = { {0x2e107d849959c430}, //music_0410009 {0x75859a7a2b1ed37d}, //music_0410010 {0x2e5f57a6c6e9c97f}, //music_0410011 + {0xa144f6d7de02e000}, //music_0410012 {0x5d1f3fdbbb036f8d}, //music_0420001 {0xc04264e8f34ad5c0}, //music_0420002 {0x8f0e96b4f71f724f}, //music_0420003 @@ -534,6 +540,7 @@ static const hcakey_info hcakey_list[] = { {0xd13a315c0005f0}, //music_0420013 {0x35f2d3cec84aba1}, //music_0420014 {0xdad11fe0e397ede}, //music_0420015 + {0xc94236c936f50cc}, //music_0420016 {0xdf31e26a7b036a2}, //music_0510001 {0xb2770dced3cfd9a7}, //music_0510002 {0x6c6c1fd51e28a1e7}, //music_0510003 @@ -548,6 +555,7 @@ static const hcakey_info hcakey_list[] = { {0x1980271cfe0da9bd}, //music_0510012 {0x75c5bd4e3a01a8a4}, //music_0510013 {0xec5f5fbe92bbb771}, //music_0510014 + {0xb8c3233338ad8e0}, //music_0510015 {0x15f82c1617013c36}, //music_0520001 {0xc7da8e6f0e2fe399}, //music_0520002 {0xe350bffcdc9cb686}, //music_0520003 @@ -560,7 +568,9 @@ static const hcakey_info hcakey_list[] = { {0xde4959221bc2675}, //music_0520010 {0xeeaf8d2458ccdb36}, //music_0520011 {0xb140168a47d55b92}, //music_0520012 + {0x2e8d1134ce415f8c}, //music_0520013 {0x1bf43def1e4b103a}, //music_0520014 + {0x6721ad5109e4840d}, //music_0520015 {0xd2ce91dbfc209b10}, //music_0610001 {0xa662be1601e49476}, //music_0610002 {0xe5e83d31e64273f8}, //music_0610003 @@ -572,6 +582,7 @@ static const hcakey_info hcakey_list[] = { {0x9e68da734cc472f}, //music_0610009 {0xa01c597d1aa13358}, //music_0610010 {0x6492e7708204838}, //music_0610011 + {0x957e4d3948427952}, //music_0610012 {0x8258ddd6a1d0849b}, //music_0620001 {0x1dd21a1244ca12f1}, //music_0620002 {0xfdec74b23d8b494b}, //music_0620003 @@ -586,6 +597,7 @@ static const hcakey_info hcakey_list[] = { {0xfab3596f11cc4d7a}, //music_0620012 {0xe35d52b6d2c094fb}, //music_0620013 {0xcdb9bc2ad7024ca2}, //music_0620014 + {0x5de2b0a34eee1c89}, //music_0620016 {0x2a47feac8dc3ca9c}, //music_3010001 {0x9ebbaf63ffe9d9ef}, //music_3010002 {0xe553dba6592293d8}, //music_3010003 @@ -606,6 +618,8 @@ static const hcakey_info hcakey_list[] = { {0x100293729f35b4de}, //music_3020005 {0x140ac59d2b870a13}, //music_3020006 {0x402b13df5481d4e6}, //music_3020007 + {0x729efd67aede1a40}, //music_3020008 + {0xb7b9a143742fa51e}, //music_3020009 {0xdfad847a86a126bb}, //music_5030001 {0x711ef85045b8c26e}, //music_5030002 {0xff7640b46d72b337}, //music_5030003 @@ -648,8 +662,16 @@ static const hcakey_info hcakey_list[] = { {0x7f617e396e9a1e5c}, //music_5030040 {0xd0471c163265ca1b}, //music_5030041 {0xd689966609595d7d}, //music_5030042 + {0x172171a4ff10fdc1}, //music_5030043 + {0xcb2c44d594252491}, //music_5030045 + {0x458b73844ed5219e}, //music_5030048 + {0x7d83b8da9023ef26}, //music_5030049 {0x32cb728ddab4d956}, //music_5030050 {0x52c5dfb61fe4c87a}, //music_5030054 + {0x3ebbccab07c9a9ba}, //music_5030055 + {0x7a5e0865ba8cafa7}, //music_5030061 + {0x7679587f7292b057}, //music_5030062 + {0xc9c804e6fed3387c}, //music_5030063 {0x444dda6d55d76095}, //music_5040001 {0xcbf4f1324081e0a6}, //music_5040002 {0xf1db3c1d9542063a}, //music_5040003 @@ -711,6 +733,8 @@ static const hcakey_info hcakey_list[] = { {0xfac398719cd9e4a}, //music_5050039 {0x9c4ba796548a019}, //music_5050040 {0x7e7c462ba7d473cf}, //music_5050041 + {0x8a9a7af1379840fb}, //music_5050042 + {0xa0aa0097e5631019}, //music_5050043 {0xe278eccf08eb2565}, //music_5050044 {0x1cf133b26d8160d1}, //music_5050045 {0xda08e9d3961c93f2}, //music_5050046 @@ -746,10 +770,27 @@ static const hcakey_info hcakey_list[] = { {0xf653b47bc8d4d1cd}, //music_5050079 {0xb50f482149140fda}, //music_5050080 {0xd61cc4e14e7073f4}, //music_5050081 + {0xfba77b717e43a39a}, //music_5050082 {0x85a236b5270bac29}, //music_5050083 + {0x818d37d319d4c177}, //music_5050084 + {0xc16fb31c74eb5e59}, //music_5050085 {0x598e133e0673b1e6}, //music_5050086 + {0x4cb2e8101df88d6f}, //music_5050087 {0x3f8abfcd47711be2}, //music_5050088 + {0xcdb3f9edbd51012f}, //music_5050089 + {0xa28c9867b32a60e1}, //music_5050090 + {0xb1e06cf5f6a790c2}, //music_5050091 + {0xcfb9a7e64443e95c}, //music_5050093 + {0xf9ef74ac89bdfb7d}, //music_5050094 + {0x561e1e17dfb055ce}, //music_5050095 + {0x46967c2bc5d4d050}, //music_5050096 + {0xdcdaeb3067868ad9}, //music_5050097 + {0x18fab58c80c85580}, //music_5050098 + {0xba4484d824fb61af}, //music_5050099 + {0xb70fe5c5e12c7a1c}, //music_5050100 + {0x7f5d26ba72161054}, //music_5050101 {0x52c250eade92393b}, //music_9010001 + {0xf66e6bb5b0599b07}, //music_9010002 {0xfea0d6adff136868}, //music_9050001 // Mini 4WD Hyper Dash Grand Prix (Android) diff --git a/Frameworks/vgmstream/vgmstream/src/meta/meta.h b/Frameworks/vgmstream/vgmstream/src/meta/meta.h index 628079ffe..d75b90b1d 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/meta.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/meta.h @@ -594,7 +594,8 @@ VGMSTREAM * init_vgmstream_akb2(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_x360_ast(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile); +VGMSTREAM* init_vgmstream_wwise(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_wwise_bnk(STREAMFILE* sf, int* p_prefetch); VGMSTREAM * init_vgmstream_ubi_raki(STREAMFILE* streamFile); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c b/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c index 9e8390f0b..7807de138 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c @@ -8,12 +8,12 @@ struct dsp_header { uint32_t sample_count; /* 0x00 */ uint32_t nibble_count; /* 0x04 (includes frame headers) */ - uint32_t sample_rate; /* 0x08 (generally 32/48kz but games like Wario World set 32028hz to adjust for GC's rate) */ + uint32_t sample_rate; /* 0x08 (generally 22/32/44/48kz but games like Wario World set 32028hz to adjust for GC's rate) */ uint16_t loop_flag; /* 0x0c */ uint16_t format; /* 0x0e (always 0 for ADPCM) */ uint32_t loop_start_offset; /* 0x10 */ uint32_t loop_end_offset; /* 0x14 */ - uint32_t ca; /* 0x18 (always 0) */ + uint32_t initial_offset; /* 0x18 ("ca", in nibbles, should be 2) */ int16_t coef[16]; /* 0x1c (eight pairs) */ uint16_t gain; /* 0x3c (always 0 for ADPCM) */ uint16_t initial_ps; /* 0x3e (predictor/scale in frame header) */ @@ -22,12 +22,13 @@ struct dsp_header { uint16_t loop_ps; /* 0x44 (predictor/scale in loop frame header) */ int16_t loop_hist1; /* 0x46 */ int16_t loop_hist2; /* 0x48 */ - int16_t channel_count; /* 0x4a (DSPADPCM.exe ~v2.7 extension) */ + int16_t channels; /* 0x4a (DSPADPCM.exe ~v2.7 extension) */ int16_t block_size; /* 0x4c */ - /* padding/reserved up to 0x60, DSPADPCM.exe from GC adds garbage here (uninitialized MSVC memory?) */ + /* padding/reserved up to 0x60, DSPADPCM.exe from GC adds garbage here (uninitialized MSVC memory?) + * [ex. Batallion Wars (GC), Timesplitters 2 (GC)], 0xcccc...cccc with DSPADPCMD */ }; -/* read the above struct; returns nonzero on failure */ +/* read and do basic validations to the above struct */ static int read_dsp_header_endian(struct dsp_header *header, off_t offset, STREAMFILE* sf, int big_endian) { uint32_t (*get_u32)(const uint8_t*) = big_endian ? get_u32be : get_u32le; uint16_t (*get_u16)(const uint8_t*) = big_endian ? get_u16be : get_u16le; @@ -36,31 +37,68 @@ static int read_dsp_header_endian(struct dsp_header *header, off_t offset, STREA uint8_t buf[0x60]; if (offset > get_streamfile_size(sf)) - return 1; + goto fail; if (read_streamfile(buf, offset, 0x60, sf) != 0x60) - return 1; - header->sample_count = get_u32(buf+0x00); - header->nibble_count = get_u32(buf+0x04); - header->sample_rate = get_u32(buf+0x08); - header->loop_flag = get_u16(buf+0x0c); - header->format = get_u16(buf+0x0e); - header->loop_start_offset = get_u32(buf+0x10); - header->loop_end_offset = get_u32(buf+0x14); - header->ca = get_u32(buf+0x18); - for (i=0; i < 16; i++) - header->coef[i] = get_s16(buf+0x1c+i*0x02); - header->gain = get_u16(buf+0x3c); - header->initial_ps = get_u16(buf+0x3e); - header->initial_hist1 = get_s16(buf+0x40); - header->initial_hist2 = get_s16(buf+0x42); - header->loop_ps = get_u16(buf+0x44); - header->loop_hist1 = get_s16(buf+0x46); - header->loop_hist2 = get_s16(buf+0x48); - header->channel_count = get_s16(buf+0x4a); - header->block_size = get_s16(buf+0x4c); + goto fail; + + /* Since header is rather basic add some extra checks. Some validations like samples vs nibbles + * and loop offsets/expected values should be external, since there are buggy variations */ + + /* base */ + header->sample_count = get_u32(buf+0x00); + if (header->sample_count > 0x10000000) + goto fail; /* unlikely to be that big, should catch fourccs */ + header->nibble_count = get_u32(buf+0x04); + if (header->nibble_count > 0x10000000) + goto fail; + + header->sample_rate = get_u32(buf+0x08); + if (header->sample_rate < 8000 || header->sample_rate > 48000) + goto fail; /* validated later but fail faster (unsure of min) */ + + /* context */ + header->loop_flag = get_u16(buf+0x0c); + if (header->loop_flag != 0 && header->loop_flag != 1) + goto fail; + header->format = get_u16(buf+0x0e); + if (header->format != 0) + goto fail; + header->loop_start_offset = get_u32(buf+0x10); + header->loop_end_offset = get_u32(buf+0x14); + + header->initial_offset = get_u32(buf+0x18); + if (header->initial_offset != 2 && header->initial_offset != 0) + goto fail; /* Dr. Muto uses 0 */ + + for (i = 0; i < 16; i++) + header->coef[i] = get_s16(buf+0x1c + i*0x02); + + header->gain = get_u16(buf+0x3c); + if (header->gain != 0) + goto fail; + + /* decoder state */ + header->initial_ps = get_u16(buf+0x3e); + header->initial_hist1 = get_s16(buf+0x40); + header->initial_hist2 = get_s16(buf+0x42); + header->loop_ps = get_u16(buf+0x44); + header->loop_hist1 = get_s16(buf+0x46); + header->loop_hist2 = get_s16(buf+0x48); + + /* reserved, may contain garbage */ + header->channels = get_s16(buf+0x4a); + header->block_size = get_s16(buf+0x4c); + + if (header->channels > 64) /* arbitrary max */ + header->channels = 0; + if (header->block_size > 0x100000) + header->block_size = 0; + + return 1; +fail: return 0; } -static int read_dsp_header(struct dsp_header *header, off_t offset, STREAMFILE* file) { +static int read_dsp_header_be(struct dsp_header *header, off_t offset, STREAMFILE* file) { return read_dsp_header_endian(header, offset, file, 1); } static int read_dsp_header_le(struct dsp_header *header, off_t offset, STREAMFILE* file) { @@ -115,7 +153,7 @@ static VGMSTREAM* init_vgmstream_dsp_common(STREAMFILE* sf, dsp_meta* dspm) { /* load standard DSP header per channel */ { for (i = 0; i < dspm->channels; i++) { - if (read_dsp_header_endian(&ch_header[i], dspm->header_offset + i*dspm->header_spacing, sf, !dspm->little_endian)) { + if (!read_dsp_header_endian(&ch_header[i], dspm->header_offset + i*dspm->header_spacing, sf, !dspm->little_endian)) { //;VGM_LOG("DSP: bad header\n"); goto fail; } @@ -130,16 +168,6 @@ static VGMSTREAM* init_vgmstream_dsp_common(STREAMFILE* sf, dsp_meta* dspm) { } } - /* check type==0 and gain==0 */ - { - for (i = 0; i < dspm->channels; i++) { - if (ch_header[i].format || ch_header[i].gain) { - //;VGM_LOG("DSP: bad type/gain\n"); - goto fail; - } - } - } - /* check for agreement between channels */ if (!dspm->ignore_header_agreement) { for (i = 0; i < dspm->channels - 1; i++) { @@ -269,25 +297,23 @@ VGMSTREAM* init_vgmstream_ngc_dsp_std(STREAMFILE* sf) { struct dsp_header header; const size_t header_size = 0x60; off_t start_offset; - int i, channel_count; + int i, channels; /* checks */ + if (!read_dsp_header_be(&header, 0x00, sf)) + goto fail; + /* .dsp: standard * .adp: Dr. Muto/Battalion Wars (GC) mono files * (extensionless): Tony Hawk's Downhill Jam (Wii) */ if (!check_extensions(sf, "dsp,adp,")) goto fail; - if (read_dsp_header(&header, 0x00, sf)) - goto fail; - - channel_count = 1; + channels = 1; start_offset = header_size; - if (header.initial_ps != (uint8_t)read_8bit(start_offset,sf)) - goto fail; /* check initial predictor/scale */ - if (header.format || header.gain) - goto fail; /* check type==0 and gain==0 */ + if (header.initial_ps != read_u8(start_offset,sf)) + goto fail; /* Check for a matching second header. If we find one and it checks * out thoroughly, we're probably not dealing with a genuine mono DSP. @@ -299,7 +325,7 @@ VGMSTREAM* init_vgmstream_ngc_dsp_std(STREAMFILE* sf) { struct dsp_header header2; /* ignore headers one after another */ - ko = read_dsp_header(&header2, header_size, sf); + ko = !read_dsp_header_be(&header2, header_size, sf); if (!ko && header.sample_count == header2.sample_count && header.nibble_count == header2.nibble_count && @@ -310,7 +336,7 @@ VGMSTREAM* init_vgmstream_ngc_dsp_std(STREAMFILE* sf) { /* ignore headers after interleave [Ultimate Board Collection (Wii)] */ - ko = read_dsp_header(&header2, 0x10000, sf); + ko = !read_dsp_header_be(&header2, 0x10000, sf); if (!ko && header.sample_count == header2.sample_count && header.nibble_count == header2.nibble_count && @@ -324,7 +350,7 @@ VGMSTREAM* init_vgmstream_ngc_dsp_std(STREAMFILE* sf) { off_t loop_off; /* check loop predictor/scale */ loop_off = header.loop_start_offset/16*8; - if (header.loop_ps != (uint8_t)read_8bit(start_offset+loop_off,sf)) { + if (header.loop_ps != read_u8(start_offset+loop_off,sf)) { /* rarely won't match (ex ESPN 2002), not sure if header or calc problem, but doesn't seem to matter * (there may be a "click" when looping, or loop values may be too big and loop disabled anyway) */ VGM_LOG("DSP (std): bad loop_predictor\n"); @@ -335,7 +361,7 @@ VGMSTREAM* init_vgmstream_ngc_dsp_std(STREAMFILE* sf) { /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,header.loop_flag); + vgmstream = allocate_vgmstream(channels, header.loop_flag); if (!vgmstream) goto fail; vgmstream->sample_rate = header.sample_rate; @@ -373,23 +399,21 @@ VGMSTREAM* init_vgmstream_ngc_dsp_std_le(STREAMFILE* sf) { struct dsp_header header; const size_t header_size = 0x60; off_t start_offset; - int i, channel_count; + int i, channels; /* checks */ /* .adpcm: LEGO Worlds */ if (!check_extensions(sf, "adpcm")) goto fail; - if (read_dsp_header_le(&header, 0x00, sf)) + if (!read_dsp_header_le(&header, 0x00, sf)) goto fail; - channel_count = 1; + channels = 1; start_offset = header_size; - if (header.initial_ps != (uint8_t)read_8bit(start_offset,sf)) - goto fail; /* check initial predictor/scale */ - if (header.format || header.gain) - goto fail; /* check type==0 and gain==0 */ + if (header.initial_ps != read_u8(start_offset,sf)) + goto fail; /* Check for a matching second header. If we find one and it checks * out thoroughly, we're probably not dealing with a genuine mono DSP. @@ -397,9 +421,12 @@ VGMSTREAM* init_vgmstream_ngc_dsp_std_le(STREAMFILE* sf) { * predictor/scale check if the first byte is 0 */ { struct dsp_header header2; - read_dsp_header_le(&header2, header_size, sf); + int ko; - if (header.sample_count == header2.sample_count && + ko = !read_dsp_header_le(&header2, header_size, sf); + + if (!ko && + header.sample_count == header2.sample_count && header.nibble_count == header2.nibble_count && header.sample_rate == header2.sample_rate && header.loop_flag == header2.loop_flag) { @@ -411,24 +438,20 @@ VGMSTREAM* init_vgmstream_ngc_dsp_std_le(STREAMFILE* sf) { off_t loop_off; /* check loop predictor/scale */ loop_off = header.loop_start_offset/16*8; - if (header.loop_ps != (uint8_t)read_8bit(start_offset+loop_off,sf)) { - /* rarely won't match (ex ESPN 2002), not sure if header or calc problem, but doesn't seem to matter - * (there may be a "click" when looping, or loop values may be too big and loop disabled anyway) */ - VGM_LOG("DSP (std): bad loop_predictor\n"); - //header.loop_flag = 0; - //goto fail; + if (header.loop_ps != read_u8(start_offset+loop_off,sf)) { + goto fail; } } /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,header.loop_flag); + vgmstream = allocate_vgmstream(channels, header.loop_flag); if (!vgmstream) goto fail; vgmstream->sample_rate = header.sample_rate; vgmstream->num_samples = header.sample_count; vgmstream->loop_start_sample = dsp_nibbles_to_samples(header.loop_start_offset); - vgmstream->loop_end_sample = dsp_nibbles_to_samples(header.loop_end_offset)+1; + vgmstream->loop_end_sample = dsp_nibbles_to_samples(header.loop_end_offset) + 1; if (vgmstream->loop_end_sample > vgmstream->num_samples) /* don't know why, but it does happen */ vgmstream->loop_end_sample = vgmstream->num_samples; @@ -460,29 +483,28 @@ VGMSTREAM* init_vgmstream_ngc_mdsp_std(STREAMFILE* sf) { struct dsp_header header; const size_t header_size = 0x60; off_t start_offset; - int i, c, channel_count; + int i, c, channels; /* checks */ + if (!read_dsp_header_be(&header, 0x00, sf)) + goto fail; + if (!check_extensions(sf, "dsp,mdsp")) goto fail; - if (read_dsp_header(&header, 0x00, sf)) - goto fail; + channels = header.channels==0 ? 1 : header.channels; + start_offset = header_size * channels; - channel_count = header.channel_count==0 ? 1 : header.channel_count; - start_offset = header_size * channel_count; + if (header.initial_ps != read_u8(start_offset, sf)) + goto fail; /* named .dsp and no channels? likely another interleaved dsp */ - if (check_extensions(sf,"dsp") && header.channel_count == 0) + if (check_extensions(sf,"dsp") && header.channels == 0) goto fail; - if (header.initial_ps != (uint8_t)read_8bit(start_offset, sf)) - goto fail; /* check initial predictor/scale */ - if (header.format || header.gain) - goto fail; /* check type==0 and gain==0 */ /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count, header.loop_flag); + vgmstream = allocate_vgmstream(channels, header.loop_flag); if (!vgmstream) goto fail; vgmstream->sample_rate = header.sample_rate; @@ -494,13 +516,14 @@ VGMSTREAM* init_vgmstream_ngc_mdsp_std(STREAMFILE* sf) { vgmstream->meta_type = meta_DSP_STD; vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = channel_count == 1 ? layout_none : layout_interleave; + vgmstream->layout_type = channels == 1 ? layout_none : layout_interleave; vgmstream->interleave_block_size = header.block_size * 8; if (vgmstream->interleave_block_size) vgmstream->interleave_last_block_size = (header.nibble_count / 2 % vgmstream->interleave_block_size + 7) / 8 * 8; - for (i = 0; i < channel_count; i++) { - if (read_dsp_header(&header, header_size * i, sf)) goto fail; + for (i = 0; i < channels; i++) { + if (!read_dsp_header_be(&header, header_size * i, sf)) + goto fail; /* adpcm coeffs/history */ for (c = 0; c < 16; c++) @@ -525,21 +548,23 @@ VGMSTREAM* init_vgmstream_ngc_dsp_stm(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ - /* .lstm/dsp: renamed to avoid hijacking Scream Tracker 2 Modules */ + if (read_u16be(0x00, sf) != 0x0200) + goto fail; + + /* .lstm/dsp: renamed to avoid hijacking Scream Tracker 2 Modules (not needed) */ if (!check_extensions(sf, "stm,lstm,dsp")) goto fail; - if (read_16bitBE(0x00, sf) != 0x0200) - goto fail; - /* 0x02(2): sample rate, 0x08+: channel sizes/loop offsets? */ + /* 0x02: sample rate + * 0x08+: channel sizes/loop offsets? */ - dspm.channels = read_32bitBE(0x04, sf); + dspm.channels = read_u32be(0x04, sf); dspm.max_channels = 2; dspm.fix_looping = 1; dspm.header_offset = 0x40; dspm.header_spacing = 0x60; dspm.start_offset = 0x100; - dspm.interleave = (read_32bitBE(0x08, sf) + 0x20) / 0x20 * 0x20; /* strange rounding, but works */ + dspm.interleave = (read_u32be(0x08, sf) + 0x20) / 0x20 * 0x20; /* strange rounding, but works */ dspm.meta_type = meta_DSP_STM; return init_vgmstream_dsp_common(sf, &dspm); @@ -619,9 +644,10 @@ VGMSTREAM* init_vgmstream_idsp_namco(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ - if (!check_extensions(sf, "idsp")) + if (!is_id32be(0x00,sf, "IDSP")) goto fail; - if (read_32bitBE(0x00,sf) != 0x49445350) /* "IDSP" */ + + if (!check_extensions(sf, "idsp")) goto fail; dspm.max_channels = 8; @@ -653,9 +679,10 @@ VGMSTREAM* init_vgmstream_sadb(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ - if (!check_extensions(sf, "sad")) + if (!is_id32be(0x00,sf, "sadb")) goto fail; - if (read_32bitBE(0x00,sf) != 0x73616462) /* "sadb" */ + + if (!check_extensions(sf, "sad")) goto fail; dspm.channels = read_8bit(0x32, sf); @@ -679,17 +706,17 @@ VGMSTREAM* init_vgmstream_idsp_tt(STREAMFILE* sf) { int version_main, version_sub; /* checks */ + if (!is_id32be(0x00,sf, "IDSP")) + goto fail; + /* .gcm: standard * .idsp: header id? * .wua: Lego Dimensions (Wii U) */ if (!check_extensions(sf, "gcm,idsp,wua")) goto fail; - if (read_32bitBE(0x00,sf) != 0x49445350) /* "IDSP" */ - goto fail; - - version_main = read_32bitBE(0x04, sf); - version_sub = read_32bitBE(0x08, sf); /* extra check since there are other IDSPs */ + version_main = read_u32be(0x04, sf); + version_sub = read_u32be(0x08, sf); /* extra check since there are other IDSPs */ if (version_main == 0x01 && version_sub == 0xc8) { /* Transformers: The Game (Wii) */ dspm.channels = 2; @@ -711,7 +738,7 @@ VGMSTREAM* init_vgmstream_idsp_tt(STREAMFILE* sf) { else if (version_main == 0x03 && version_sub == 0x12c) { /* Lego The Lord of the Rings (Wii) */ /* Lego Dimensions (Wii U) */ - dspm.channels = read_32bitBE(0x10, sf); + dspm.channels = read_u32be(0x10, sf); dspm.max_channels = 2; dspm.header_offset = 0x20; /* 0x14+: "I_AM_PADDING" */ @@ -722,7 +749,7 @@ VGMSTREAM* init_vgmstream_idsp_tt(STREAMFILE* sf) { dspm.header_spacing = 0x60; dspm.start_offset = dspm.header_offset + 0x60 * dspm.channels; - dspm.interleave = read_32bitBE(0x0c, sf); + dspm.interleave = read_u32be(0x0c, sf); dspm.meta_type = meta_IDSP_TT; return init_vgmstream_dsp_common(sf, &dspm); @@ -735,9 +762,9 @@ VGMSTREAM* init_vgmstream_idsp_nl(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ - if (!check_extensions(sf, "idsp")) + if (!is_id32be(0x00,sf, "IDSP")) goto fail; - if (read_32bitBE(0x00,sf) != 0x49445350) /* "IDSP" */ + if (!check_extensions(sf, "idsp")) goto fail; dspm.channels = 2; @@ -773,16 +800,18 @@ VGMSTREAM* init_vgmstream_wii_wsd(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ + if (read_u32be(0x00,sf) != 0x20) + goto fail; if (!check_extensions(sf, "wsd")) goto fail; - if (read_32bitBE(0x08,sf) != read_32bitBE(0x0c,sf)) /* channel sizes */ + if (read_u32be(0x08,sf) != read_u32be(0x0c,sf)) /* channel sizes */ goto fail; dspm.channels = 2; dspm.max_channels = 2; - dspm.header_offset = read_32bitBE(0x00,sf); - dspm.header_spacing = read_32bitBE(0x04,sf) - dspm.header_offset; + dspm.header_offset = read_u32be(0x00,sf); + dspm.header_spacing = read_u32be(0x04,sf) - dspm.header_offset; dspm.start_offset = dspm.header_offset + 0x60; dspm.interleave = dspm.header_spacing; @@ -792,7 +821,7 @@ fail: return NULL; } -/* .ddsp - full interleaved dsp [The Sims 2 - Pets (Wii)] */ +/* .ddsp - full interleaved dsp [The Sims 2: Pets (Wii)] */ VGMSTREAM* init_vgmstream_dsp_ddsp(STREAMFILE* sf) { dsp_meta dspm = {0}; @@ -819,9 +848,9 @@ VGMSTREAM* init_vgmstream_wii_was(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ - if (!check_extensions(sf, "was,dsp,isws")) + if (!is_id32be(0x00,sf, "iSWS")) goto fail; - if (read_32bitBE(0x00,sf) != 0x69535753) /* "iSWS" */ + if (!check_extensions(sf, "was,dsp,isws")) goto fail; dspm.channels = read_32bitBE(0x08,sf); @@ -888,12 +917,12 @@ VGMSTREAM* init_vgmstream_dsp_ndp(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ + if (!is_id32be(0x00,sf, "NDP\0")) + goto fail; /* .nds: standard * .ndp: header id */ if (!check_extensions(sf, "nds,ndp")) goto fail; - if (!is_id32be(0x00,sf, "NDP\0")) - goto fail; if (read_u32le(0x08,sf) + 0x18 != get_streamfile_size(sf)) goto fail; /* 0x0c: sample rate */ @@ -946,18 +975,19 @@ VGMSTREAM* init_vgmstream_ngc_dsp_aaap(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ + if (!is_id32be(0x00,sf, "AAAp")) + goto fail; if (!check_extensions(sf, "dsp")) goto fail; - if (read_32bitBE(0x00,sf) != 0x41414170) /* "AAAp" */ - goto fail; - dspm.channels = read_16bitBE(0x06,sf); + + dspm.interleave = read_u16be(0x04,sf); + dspm.channels = read_u16be(0x06,sf); dspm.max_channels = 2; dspm.header_offset = 0x08; dspm.header_spacing = 0x60; - dspm.start_offset = dspm.header_offset + dspm.channels*dspm.header_spacing; - dspm.interleave = (uint16_t)read_16bitBE(0x04,sf); + dspm.start_offset = dspm.header_offset + dspm.channels * dspm.header_spacing; dspm.meta_type = meta_NGC_DSP_AAAP; return init_vgmstream_dsp_common(sf, &dspm); @@ -970,15 +1000,15 @@ VGMSTREAM* init_vgmstream_dsp_dspw(STREAMFILE* sf) { dsp_meta dspm = {0}; size_t data_size; - /* check extension */ - if (!check_extensions(sf, "dspw")) + /* checks */ + if (!is_id32be(0x00,sf, "DSPW")) goto fail; - if (read_32bitBE(0x00,sf) != 0x44535057) /* "DSPW" */ + if (!check_extensions(sf, "dspw")) goto fail; /* ignore time marker */ data_size = read_32bitBE(0x08, sf); - if (read_32bitBE(data_size - 0x10, sf) == 0x74494D45) /* "tIME" */ + if (is_id32be(data_size - 0x10, sf, "tIME")) data_size -= 0x10; /* (ignore, 2 ints in YYYYMMDD hhmmss00) */ /* some files have a mrkr section with multiple loop regions added at the end (variable size) */ @@ -1016,10 +1046,12 @@ VGMSTREAM* init_vgmstream_ngc_dsp_iadp(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ - /* .adp: actual extension, .iadp: header id */ - if (!check_extensions(sf, "adp,iadp")) + if (!is_id32be(0x00,sf, "iadp")) goto fail; - if (read_32bitBE(0x00,sf) != 0x69616470) /* "iadp" */ + + /* .adp: actual extension + * .iadp: header id */ + if (!check_extensions(sf, "adp,iadp")) goto fail; dspm.channels = read_32bitBE(0x04,sf); @@ -1068,11 +1100,13 @@ VGMSTREAM* init_vgmstream_dsp_switch_audio(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ - /* .switch_audio: possibly UE4 class name rather than extension, .dsp: assumed */ + /* .switch_audio: possibly UE4 class name rather than extension + * .dsp: assumed */ if (!check_extensions(sf, "switch_audio,dsp")) goto fail; /* manual double header test */ + //todo improve to read after first header if (read_32bitLE(0x00, sf) == read_32bitLE(get_streamfile_size(sf) / 2, sf)) dspm.channels = 2; else @@ -1096,13 +1130,14 @@ VGMSTREAM* init_vgmstream_dsp_sps_n1(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ + if (read_u32be(0x00,sf) != 0x08000000) /* file type (see other N1 SPS) */ + goto fail; + /* .vag: Penny-Punching Princess (Switch) * .nlsd: Ys VIII (Switch) */ if (!check_extensions(sf, "vag,nlsd")) goto fail; - if (read_32bitBE(0x00,sf) != 0x08000000) /* file type (see other N1 SPS) */ - goto fail; - if ((uint16_t)read_16bitLE(0x08,sf) != read_32bitLE(0x24,sf)) /* header has various repeated values */ + if (read_u16le(0x08,sf) != read_u32le(0x24,sf)) /* header has various repeated values */ goto fail; dspm.channels = 1; @@ -1133,7 +1168,7 @@ VGMSTREAM* init_vgmstream_dsp_itl_ch(STREAMFILE* sf) { dspm.header_offset = 0x00; dspm.header_spacing = 0x60; - dspm.start_offset = dspm.header_offset + dspm.header_spacing*dspm.channels; + dspm.start_offset = dspm.header_offset + dspm.header_spacing * dspm.channels; dspm.interleave = 0x23C0; dspm.fix_looping = 1; @@ -1149,16 +1184,17 @@ VGMSTREAM* init_vgmstream_dsp_adpy(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ - if (!check_extensions(sf, "adpcmx")) + if (!is_id32be(0x00,sf, "ADPY")) goto fail; - if (read_32bitBE(0x00,sf) != 0x41445059) /* "ADPY" */ + + if (!check_extensions(sf, "adpcmx")) goto fail; /* 0x04(2): 1? */ /* 0x08: some size? */ /* 0x0c: null */ - dspm.channels = read_16bitLE(0x06,sf); + dspm.channels = read_u16le(0x06,sf); dspm.max_channels = 2; dspm.little_endian = 1; @@ -1178,9 +1214,10 @@ VGMSTREAM* init_vgmstream_dsp_adpx(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ - if (!check_extensions(sf, "adpcmx")) + if (!is_id32be(0x00,sf, "ADPX")) goto fail; - if (read_32bitBE(0x00,sf) != 0x41445058) /* "ADPX" */ + + if (!check_extensions(sf, "adpcmx")) goto fail; /* from 0x04 *6 are probably channel sizes, so max would be 6ch; this assumes 2ch */ @@ -1216,6 +1253,7 @@ VGMSTREAM* init_vgmstream_dsp_ds2(STREAMFILE* sf) { read_32bitBE(0x58,sf) == 0 && read_32bitBE(0x5c,sf) != 0)) goto fail; + file_size = get_streamfile_size(sf); channel_offset = read_32bitBE(0x5c,sf); /* absolute offset to 2nd channel */ if (channel_offset < file_size / 2 || channel_offset > file_size) /* just to make sure */ @@ -1272,11 +1310,12 @@ VGMSTREAM* init_vgmstream_dsp_sqex(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ - if (!check_extensions(sf, "wav,lwav")) - goto fail; if (read_u32be(0x00,sf) != 0x00000000) goto fail; + if (!check_extensions(sf, "wav,lwav")) + goto fail; + dspm.channels = read_u32le(0x04,sf); dspm.header_offset = read_u32le(0x08,sf); /* 0x0c: channel size */ @@ -1303,12 +1342,11 @@ VGMSTREAM* init_vgmstream_dsp_wiivoice(STREAMFILE* sf) { /* also see g1l.c for WiiBGM weirder variation */ /* checks */ + if (!is_id64be(0x00,sf, "WiiVoice")) + goto fail; /* .dsp: assumed */ if (!check_extensions(sf, "dsp")) goto fail; - if (read_u32be(0x00,sf) != 0x57696956 && /* "WiiV" */ - read_u32be(0x04,sf) != 0x6F696365) /* "oice" */ - goto fail; dspm.channels = 1; dspm.max_channels = 1; @@ -1331,9 +1369,9 @@ VGMSTREAM* init_vgmstream_dsp_wiiadpcm(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ - if (!check_extensions(sf, "adpcm")) + if (!is_id64be(0x00,sf, "WIIADPCM")) goto fail; - if (!is_id32be(0x00,sf, "WIIA") && !is_id32be(0x00,sf, "DPCM")) + if (!check_extensions(sf, "adpcm")) goto fail; dspm.interleave = read_u32be(0x08,sf); /* interleave offset */ @@ -1361,11 +1399,12 @@ VGMSTREAM* init_vgmstream_dsp_cwac(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ + if (!is_id32be(0x00,sf, "CWAC")) + goto fail; + /* .dsp: assumed */ if (!check_extensions(sf, "dsp")) goto fail; - if (!is_id32be(0x00,sf, "CWAC")) - goto fail; dspm.channels = read_u16be(0x04,sf); dspm.header_offset = read_u32be(0x08,sf); @@ -1388,10 +1427,10 @@ VGMSTREAM* init_vgmstream_idsp_tose(STREAMFILE* sf) { uint32_t blocks; /* checks */ - if (!check_extensions(sf, "idsp")) - goto fail; if (read_u32be(0x00,sf) != 0) goto fail; + if (!check_extensions(sf, "idsp")) + goto fail; dspm.max_channels = 4; /* mainly stereo */ @@ -1419,11 +1458,12 @@ VGMSTREAM* init_vgmstream_dsp_kwa(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ + if (read_u32be(0x00,sf) != 3) + goto fail; + /* .dsp: assumed */ if (!check_extensions(sf, "kwa")) goto fail; - if (read_u32be(0x00,sf) != 3) - goto fail; dspm.max_channels = 4; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/psf.c b/Frameworks/vgmstream/vgmstream/src/meta/psf.c index 809793353..1a421ed3c 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/psf.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/psf.c @@ -1,760 +1,773 @@ -#include "meta.h" -#include "../layout/layout.h" -#include "../coding/coding.h" - - -/* PSF single - Pivotal games single segment (external in some PC/Xbox or inside bigfiles) [The Great Escape, Conflict series] */ -VGMSTREAM * init_vgmstream_psf_single(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - int loop_flag, channel_count, sample_rate, rate_value, interleave; - uint32_t psf_config; - uint8_t flags; - size_t data_size; - coding_t codec; - - - /* checks */ - /* .psf: actual extension - * .swd: bigfile extension */ - if (!check_extensions(streamFile, "psf,swd")) - goto fail; - if ((read_32bitBE(0x00,streamFile) & 0xFFFFFF00) != 0x50534600) /* "PSF\00" */ - goto fail; - - flags = read_8bit(0x03,streamFile); - switch(flags) { - case 0xC0: /* [The Great Escape (PS2), Conflict: Desert Storm (PS2)] */ - case 0x40: /* [The Great Escape (PS2)] */ - case 0xA1: /* [Conflict: Desert Storm 2 (PS2)] */ - case 0x21: /* [Conflict: Desert Storm 2 (PS2), Conflict: Global Storm (PS2)] */ - //case 0x22: /* [Conflict: Vietman (PS2)] */ //todo weird size value, stereo, only one found - codec = coding_PSX; - interleave = 0x10; - - channel_count = 2; - if (flags == 0x21 || flags == 0x40) - channel_count = 1; - start_offset = 0x08; - break; - - case 0x80: /* [The Great Escape (PC/Xbox), Conflict: Desert Storm (Xbox/GC), Conflict: Desert Storm 2 (Xbox)] */ - case 0x81: /* [Conflict: Desert Storm 2 (Xbox), Conflict: Vietnam (Xbox)] */ - case 0x01: /* [Conflict: Global Storm (Xbox)] */ - codec = coding_PSX_pivotal; - interleave = 0x10; - - channel_count = 2; - if (flags == 0x01) - channel_count = 1; - start_offset = 0x08; - break; - - case 0xD1: /* [Conflict: Desert Storm 2 (GC)] */ - codec = coding_NGC_DSP; - interleave = 0x08; - - channel_count = 2; - start_offset = 0x08 + 0x60 * channel_count; - break; - - default: - goto fail; - } - - loop_flag = 0; - - psf_config = read_32bitLE(0x04, streamFile); - - /* pitch/cents? */ - rate_value = (psf_config >> 20) & 0xFFF; - switch(rate_value) { - case 3763: sample_rate = 44100; break; - case 1365: sample_rate = 16000; break; - case 940: sample_rate = 11050; break; - case 460: sample_rate = 5000; break; - default: - VGM_LOG("PSF: unknown rate value %x\n", rate_value); - sample_rate = rate_value * 11.72; /* not exact but works well enough */ - break; - } - - data_size = (psf_config & 0xFFFFF) * (interleave * channel_count); /* in blocks */ - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count, loop_flag); - if (!vgmstream) goto fail; - - vgmstream->meta_type = meta_PSF; - vgmstream->sample_rate = sample_rate; - - switch(codec) { - case coding_PSX: - vgmstream->coding_type = coding_PSX; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = interleave; - - vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count); - break; - - case coding_PSX_pivotal: - vgmstream->coding_type = coding_PSX_pivotal; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = interleave; - - vgmstream->num_samples = ps_cfg_bytes_to_samples(data_size, 0x10, channel_count); - break; - - case coding_NGC_DSP: - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = interleave; - /* has standard DSP headers at 0x08 */ - dsp_read_coefs_be(vgmstream,streamFile,0x08+0x1c,0x60); - dsp_read_hist_be (vgmstream,streamFile,0x08+0x40,0x60); - - vgmstream->num_samples = read_32bitBE(0x08, streamFile);//dsp_bytes_to_samples(data_size, channel_count); - break; - - default: - goto fail; - } - - vgmstream->stream_size = data_size; - - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) - goto fail; - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} - - - -/* PSF segmented - Pivotal games multiple segments (external in some PC/Xbox or inside bigfiles) [The Great Escape, Conflict series] */ -VGMSTREAM * init_vgmstream_psf_segmented(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - STREAMFILE* temp_streamFile = NULL; - segmented_layout_data *data = NULL; - int i,j, sequence_count = 0, loop_flag = 0, loop_start = 0, loop_end = 0; - int sequence[512] = {0}; - off_t offsets[512] = {0}; - int total_subsongs = 0, target_subsong = streamFile->stream_index; - char stream_name[STREAM_NAME_SIZE] = {0}; - size_t stream_size = 0; - - - /* checks */ - /* .psf: actual extension - * .swd: bigfile extension */ - if (!check_extensions(streamFile, "psf,swd")) - goto fail; - - if (read_32bitBE(0x00,streamFile) != 0x50534660 && /* "PSF\60" [The Great Escape (PC/Xbox/PS2), Conflict: Desert Storm (Xbox/GC)] */ - read_32bitBE(0x00,streamFile) != 0x50534631) /* "PSF\31" [Conflict: Desert Storm 2 (Xbox/GC/PS2), Conflict: Global Terror (Xbox)] */ - goto fail; - - - /* transition table info: - * 0x00: offset - * 0x04: 0x02*4 next segment points (one per track) - * (xN segments) - * - * There are 4 possible tracks, like: normal, tension, action, high action. Segment 0 has tracks' - * entry segments (where 1=first, right after segment 0), and each segment has a link point to next - * (or starting) segment of any of other tracks. Thus, follow point 1/2/3/4 to playtrack 1/2/3/4 - * (points also loop back). It's designed to go smoothly between any tracks (1>3, 4>1, etc), - * so sometimes "step" segments (that aren't normally played) are used. - * If a track doesn't exist it may keep repeating silent segments, but still defines points. - * - * ex. sequence could go like this: - * (read segment 0 track1 entry): 1 - * - track0: 1>2>3>4>5>6>7>8>1>2>3, then to track2 goes 3>15>9 - * - track1: 9>10>11>12>13>14>9>10, then to track4 goes 10>33 - * - track2: 33>34>35>36>30>31>32>33, then to track1 goes 33>3 - * - track3: 3>4>5>6... (etc) - * - * Well make a sequence based on target subsong: - * - 1: tracks mixed with transitions, looping back to track1 (only first is used in .sch so we want all) - * - 2~5: track1~4 looping back to themselves - * - 6+: single segment (where 6=first segment) to allow free mixes - */ - { - int track[4][255] = {0}; - int count[4] = {0}; - int current_track, current_point, next_point, repeat_point; - int transition_count = read_32bitLE(0x04, streamFile); - - total_subsongs = 1 + 4 + (transition_count - 1); - if (target_subsong == 0) target_subsong = 1; - if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; - - - if (target_subsong == 1) { - current_track = 0; /* start from first track, will move to others automatically */ - - snprintf(stream_name,sizeof(stream_name), "full"); - } - else if (target_subsong <= 1+4) { - current_track = (target_subsong-1) - 1; /* where 0 = first track */ - - snprintf(stream_name,sizeof(stream_name), "track%i", (current_track+1)); - } - else { - int segment = target_subsong - 1 - 4; /* where 1 = first segment */ - - sequence[0] = segment; - sequence_count = 1; - current_track = -1; - - /* show transitions to help with ordering */ - track[0][0] = read_16bitLE(0x08 + 0x0c*segment + 0x04, streamFile); - track[1][0] = read_16bitLE(0x08 + 0x0c*segment + 0x06, streamFile); - track[2][0] = read_16bitLE(0x08 + 0x0c*segment + 0x08, streamFile); - track[3][0] = read_16bitLE(0x08 + 0x0c*segment + 0x0a, streamFile); - snprintf(stream_name,sizeof(stream_name), "segment%03i to %03i/%03i/%03i/%03i", segment,track[0][0],track[1][0],track[2][0],track[3][0]); - } - - /* find target sequence */ - current_point = 0; /* segment 0 has track entry points */ - while (sequence_count < 512 && current_track >= 0) { - - next_point = read_16bitLE(0x08 + 0x0c*current_point + 0x04 + 0x02*current_track, streamFile); - - /* find if next point repeats in our current track */ - repeat_point = -1; - for (i = 0; i < count[current_track]; i++) { - - if (track[current_track][i] == next_point) { - repeat_point = i; - break; - } - } - - /* set loops and end sequence */ - if (repeat_point >= 0) { - if (target_subsong == 1) { - /* move to next track and change transition to next track too */ - current_track++; - - /* to loop properly we set loop end in track3 and loop start in track0 - * when track3 ends and move to track0 could have a transition segment - * before actually looping track0, so we do this in 2 steps */ - - if (loop_flag) { /* 2nd time repeat is found = loop start in track0 */ - loop_start = repeat_point; - break; /* sequence fully done */ - } - - if (current_track > 3) { /* 1st time repeat is found = loop end in track3 */ - current_track = 0; - loop_flag = 1; - } - - next_point = read_16bitLE(0x08 + 0x0c*current_point + 0x04 + 0x02*current_track, streamFile); - - if (loop_flag) { - loop_end = sequence_count; /* this points to the next_point that will be added below */ - } - } - else { - /* end track N */ - loop_flag = 1; - loop_start = repeat_point; - loop_end = sequence_count - 1; - break; - } - } - - - /* separate track info to find repeated points (since some transitions are common for all tracks) */ - track[current_track][count[current_track]] = next_point; - count[current_track]++; - - sequence[sequence_count] = next_point; - sequence_count++; - - current_point = next_point; - } - - if (sequence_count >= 512 || count[current_track] >= 512) - goto fail; - } - - - /* build segments */ - data = init_layout_segmented(sequence_count); - if (!data) goto fail; - - for (i = 0; i < sequence_count; i++) { - off_t psf_offset; - size_t psf_size; - int old_psf = -1; - - psf_offset = read_32bitLE(0x08 + sequence[i]*0x0c + 0x00, streamFile); - psf_size = get_streamfile_size(streamFile) - psf_offset; /* not ok but meh */ - - /* find repeated sections (sequences often repeat PSFs) */ - offsets[i] = psf_offset; - for (j = 0; j < i; j++) { - if (offsets[j] == psf_offset) { - old_psf = j; - break; - } - } - - /* reuse repeated VGMSTREAMs to improve memory and bitrate calcs a bit */ - if (old_psf >= 0) { - data->segments[i] = data->segments[old_psf]; - } - else { - temp_streamFile = setup_subfile_streamfile(streamFile, psf_offset, psf_size, "psf"); - if (!temp_streamFile) goto fail; - - data->segments[i] = init_vgmstream_psf_single(temp_streamFile); - if (!data->segments[i]) goto fail; - - stream_size += data->segments[i]->stream_size; /* only non-repeats */ - } - } - - /* setup VGMSTREAMs */ - if (!setup_layout_segmented(data)) - goto fail; - vgmstream = allocate_segmented_vgmstream(data,loop_flag, loop_start, loop_end); - if (!vgmstream) goto fail; - - vgmstream->num_streams = total_subsongs; - vgmstream->stream_size = stream_size; - strcpy(vgmstream->stream_name, stream_name); - - return vgmstream; -fail: - if (!vgmstream) free_layout_segmented(data); - close_streamfile(temp_streamFile); - close_vgmstream(vgmstream); - return NULL; -} - -/* ***************************************************** */ - -static VGMSTREAM * init_vgmstream_psf_pfsm(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - int loop_flag, channel_count, sample_rate = 0, rate_value = 0, interleave, big_endian; - size_t data_size; - coding_t codec; - int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; - int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; - - /* standard: - * 0x00: -1/number (lang?) - * 0x04: config/size? - * 0x08: channel size? only ok for PSX-pivotal - * 0x0c: sample rate or rate_value - * 0x0e: 0x4=PSX-pivotal or 0xFF=PSX - * 0x0f: name size (0xCC/FF=null) - * 0x10: data - * - * GC is similar with 0x20-align between some fields - */ - - /* checks */ - //if (!check_extensions(streamFile, "psf")) - // goto fail; - if (read_32bitBE(0x00,streamFile) != 0x5046534D && /* "PFSM" */ - read_32bitLE(0x00,streamFile) != 0x5046534D) /* "PFSM" (BE) */ - goto fail; - - big_endian = (read_32bitLE(0x00,streamFile) == 0x5046534D); - if (big_endian) { - read_32bit = read_32bitBE; - read_16bit = read_16bitBE; - } - else { - read_32bit = read_32bitLE; - read_16bit = read_16bitLE; - } - - loop_flag = 0; - - - if (big_endian && read_32bit(0x50, streamFile) != 0) { /* GC */ - codec = coding_NGC_DSP; - interleave = 0x08; - channel_count = 1; - rate_value = (uint16_t)read_16bit(0x48, streamFile); - - start_offset = 0x60 + 0x60 * channel_count; - } - else if (big_endian) { /* GC */ - codec = coding_PCM16BE; - interleave = 0x02; - channel_count = 1; - rate_value = (uint16_t)read_16bit(0x48, streamFile); - - start_offset = 0x60; - } - else if ((uint8_t)read_8bit(0x16, streamFile) == 0xFF) { /* PS2 */ - codec = coding_PSX; - interleave = 0x10; - rate_value = (uint16_t)read_16bit(0x14, streamFile); - channel_count = 1; - - start_offset = 0x18; - } - else { /* PC/Xbox, some PS2/GC */ - codec = coding_PSX_pivotal; - interleave = 0x10; - sample_rate = (uint16_t)read_16bit(0x14, streamFile); - channel_count = 1; - - start_offset = 0x18; - } - - data_size = get_streamfile_size(streamFile) - start_offset; - - /* pitch/cents? */ - if (sample_rate == 0) { - /* pitch/cents? */ - switch(rate_value) { - case 3763: sample_rate = 44100; break; - case 1365: sample_rate = 16000; break; - case 940: sample_rate = 11050; break; - case 460: sample_rate = 5000; break; - default: - VGM_LOG("PSF: unknown rate value %x\n", rate_value); - sample_rate = rate_value * 11.72; /* not exact but works well enough */ - break; - } - } - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count, loop_flag); - if (!vgmstream) goto fail; - - vgmstream->meta_type = meta_PSF; - vgmstream->sample_rate = sample_rate; - - switch(codec) { - case coding_PCM16BE: - vgmstream->coding_type = coding_PCM16BE; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = interleave; - - vgmstream->num_samples = pcm_bytes_to_samples(data_size, channel_count, 16); - break; - - case coding_PSX: - vgmstream->coding_type = coding_PSX; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = interleave; - - vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count); - break; - - case coding_PSX_pivotal: - vgmstream->coding_type = coding_PSX_pivotal; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = interleave; - - vgmstream->num_samples = ps_cfg_bytes_to_samples(data_size, 0x10, channel_count); - break; - - case coding_NGC_DSP: - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = interleave; - /* has standard DSP headers at 0x08 */ - dsp_read_coefs_be(vgmstream,streamFile,0x60+0x1c,0x60); - dsp_read_hist_be (vgmstream,streamFile,0x60+0x40,0x60); - - vgmstream->num_samples = read_32bitBE(0x60, streamFile);//dsp_bytes_to_samples(data_size, channel_count); - break; - - default: - goto fail; - } - - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) - goto fail; - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} - - - -typedef enum { UNKNOWN, IMUS, PFST, PFSM } sch_type; - - - -/* SCH - Pivotal games multi-audio container [The Great Escape, Conflict series] */ -VGMSTREAM * init_vgmstream_sch(STREAMFILE *streamFile) { - VGMSTREAM *vgmstream = NULL; - STREAMFILE *external_streamFile = NULL; - STREAMFILE *temp_streamFile = NULL; - off_t skip = 0, chunk_offset, target_offset = 0, header_offset, subfile_offset = 0; - size_t file_size, chunk_padding, target_size = 0, subfile_size = 0; - int big_endian; - int total_subsongs = 0, target_subsong = streamFile->stream_index; - int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; - sch_type target_type = UNKNOWN; - char stream_name[STREAM_NAME_SIZE] ={0}; - - - /* checks */ - if (!check_extensions(streamFile, "sch")) - goto fail; - - if (read_32bitBE(0x00,streamFile) == 0x48445253) /* "HDRSND" (found on later games) */ - skip = 0x0E; - if (read_32bitBE(skip + 0x00,streamFile) != 0x53434800 && /* "SCH\0" */ - read_32bitLE(skip + 0x00,streamFile) != 0x53434800) /* "SCH\0" (BE consoles) */ - goto fail; - - - /* chunked format (id+size, GC pads to 0x20 and uses BE/inverted ids): - * no other info so total subsongs would be count of usable chunks - * (offsets are probably in level .dat files) */ - big_endian = (read_32bitLE(skip + 0x00,streamFile) == 0x53434800); - if (big_endian) { - read_32bit = read_32bitBE; - chunk_padding = 0x18; - } - else { - read_32bit = read_32bitLE; - chunk_padding = 0; - } - - file_size = get_streamfile_size(streamFile); - if (read_32bit(skip + 0x04,streamFile) + skip + 0x08 + chunk_padding < file_size) /* sometimes padded */ - goto fail; - - if (target_subsong == 0) target_subsong = 1; - - chunk_offset = skip + 0x08 + chunk_padding; - - /* get all files*/ - while (chunk_offset < file_size) { - uint32_t chunk_id = read_32bitBE(chunk_offset + 0x00,streamFile); - uint32_t chunk_size = read_32bit(chunk_offset + 0x04,streamFile); - sch_type current_type = UNKNOWN; - - switch(chunk_id) { - case 0x494D5553: /* "IMUS" (TGE PC/Xbox only) */ - current_type = IMUS; - break; - - case 0x54534650: - case 0x50465354: /* "PFST" */ - current_type = PFST; - break; - - case 0x4D534650: - case 0x5046534D: /* "PFSM" */ - current_type = PFSM; - break; - - case 0x4B4E4142: - case 0x42414E4B: /* "BANK" */ - /* unknown format (variable size), maybe config for entry numbers */ - break; - case 0x424C4F4B: /* "BLOK" [Conflict: Desert Storm (Xbox)] */ - /* some ids or something? */ - break; - - default: - VGM_LOG("SCH: unknown chunk at %lx\n", chunk_offset); - goto fail; - } - - if (current_type != UNKNOWN) - total_subsongs++; - - if (total_subsongs == target_subsong && target_type == UNKNOWN) { - target_type = current_type; - target_offset = chunk_offset; - target_size = 0x08 + chunk_padding + chunk_size; - } - - chunk_offset += 0x08 + chunk_padding + chunk_size; - } - - if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; - if (target_size == 0) goto fail; - - header_offset = target_offset + 0x08 + chunk_padding; - - //;VGM_LOG("SCH: offset=%lx, size=%x\n",target_offset, target_size); - - switch(target_type) { - case IMUS: { /* external segmented track */ - STREAMFILE *psf_streamFile; - uint8_t name_size; - char name[255]; - - /* 0x00: config/size? - * 0x04: name size - * 0x05: segments - * 0x06: ? - * 0x08: relative path to .psf - * 0xNN: segment table (same as .psf) - */ - - name_size = read_8bit(header_offset + 0x04, streamFile); - read_string(name,name_size, header_offset + 0x08, streamFile); - - /* later games have name but actually use bigfile [Conflict: Global Storm (Xbox)] */ - if ((uint8_t)read_8bit(header_offset + 0x07, streamFile) == 0xCC) { - external_streamFile = open_streamfile_by_filename(streamFile, "Stream.swd"); - if (!external_streamFile) goto fail; - - subfile_offset = read_32bit(header_offset + 0x08 + name_size, streamFile); - subfile_size = get_streamfile_size(external_streamFile) - subfile_offset; /* not ok but meh */ - - temp_streamFile = setup_subfile_streamfile(external_streamFile, subfile_offset,subfile_size, "psf"); - if (!temp_streamFile) goto fail; - - psf_streamFile = temp_streamFile; - } - else { - external_streamFile = open_streamfile_by_filename(streamFile, name); - if (!external_streamFile) goto fail; - - psf_streamFile = external_streamFile; - } - - vgmstream = init_vgmstream_psf_segmented(psf_streamFile); - if (!vgmstream) { - vgmstream = init_vgmstream_psf_single(psf_streamFile); - if (!vgmstream) goto fail; - } - - snprintf(stream_name,sizeof(stream_name), "%s-%s" , "IMUS", name); - break; - } - - case PFST: { /* external track */ - STREAMFILE *psf_streamFile; - uint8_t name_size; - char name[255]; - - if (chunk_padding == 0 && target_size > 0x08 + 0x0c) { /* TGE PC/Xbox version */ - /* 0x00: -1/0 - * 0x04: config/size? - * 0x08: channel size - * 0x0c: sample rate? (differs vs PSF) - * 0x0e: 4? - * 0x0f: name size - * 0x10: name - */ - - /* later games have name but actually use bigfile [Conflict: Global Storm (Xbox)] */ - if ((read_32bitBE(header_offset + 0x14, streamFile) & 0x0000FFFF) == 0xCCCC) { - name_size = read_8bit(header_offset + 0x13, streamFile); - read_string(name,name_size, header_offset + 0x18, streamFile); - - external_streamFile = open_streamfile_by_filename(streamFile, "Stream.swd"); - if (!external_streamFile) goto fail; - - subfile_offset = read_32bit(header_offset + 0x0c, streamFile); - subfile_size = get_streamfile_size(external_streamFile) - subfile_offset; /* not ok but meh */ - - temp_streamFile = setup_subfile_streamfile(external_streamFile, subfile_offset,subfile_size, "psf"); - if (!temp_streamFile) goto fail; - - psf_streamFile = temp_streamFile; - } - else { - name_size = read_8bit(header_offset + 0x0f, streamFile); - read_string(name,name_size, header_offset + 0x10, streamFile); - - external_streamFile = open_streamfile_by_filename(streamFile, name); - if (!external_streamFile) goto fail; - - psf_streamFile = external_streamFile; - } - } - else if (chunk_padding) { - strcpy(name, "STREAM.SWD"); /* fixed */ - - /* 0x00: -1 - * 0x04: config/size? - * 0x08: .swd offset - */ - external_streamFile = open_streamfile_by_filename(streamFile, name); - if (!external_streamFile) goto fail; - - subfile_offset = read_32bit(header_offset + 0x24, streamFile); - subfile_size = get_streamfile_size(external_streamFile) - subfile_offset; /* not ok but meh */ - - temp_streamFile = setup_subfile_streamfile(external_streamFile, subfile_offset,subfile_size, "psf"); - if (!temp_streamFile) goto fail; - - psf_streamFile = temp_streamFile; - } - else { /* others */ - strcpy(name, "STREAM.SWD"); /* fixed */ - - /* 0x00: -1 - * 0x04: config/size? - * 0x08: .swd offset - */ - external_streamFile = open_streamfile_by_filename(streamFile, name); - if (!external_streamFile) goto fail; - - subfile_offset = read_32bit(header_offset + 0x08, streamFile); - subfile_size = get_streamfile_size(external_streamFile) - subfile_offset; /* not ok but meh */ - - temp_streamFile = setup_subfile_streamfile(external_streamFile, subfile_offset,subfile_size, "psf"); - if (!temp_streamFile) goto fail; - - psf_streamFile = temp_streamFile; - } - - vgmstream = init_vgmstream_psf_segmented(psf_streamFile); - if (!vgmstream) { - vgmstream = init_vgmstream_psf_single(psf_streamFile); - if (!vgmstream) goto fail; - } - - snprintf(stream_name,sizeof(stream_name), "%s-%s" , "PFST", name); - break; - } - - case PFSM: - /* internal sound */ - - temp_streamFile = setup_subfile_streamfile(streamFile, target_offset,target_size, NULL); - if (!temp_streamFile) goto fail; - - vgmstream = init_vgmstream_psf_pfsm(temp_streamFile); - if (!vgmstream) goto fail; - - snprintf(stream_name,sizeof(stream_name), "%s" , "PFSM"); - break; - - default: /* target not found */ - goto fail; - } - - vgmstream->num_streams = total_subsongs; - strcpy(vgmstream->stream_name, stream_name); - - close_streamfile(temp_streamFile); - close_streamfile(external_streamFile); - return vgmstream; - -fail: - close_streamfile(temp_streamFile); - close_streamfile(external_streamFile); - close_vgmstream(vgmstream); - return NULL; -} +#include "meta.h" +#include "../layout/layout.h" +#include "../coding/coding.h" + + +/* PSF single - Pivotal games single segment (external in some PC/Xbox or inside bigfiles) [The Great Escape, Conflict series] */ +VGMSTREAM* init_vgmstream_psf_single(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count, sample_rate, rate_value, interleave; + uint32_t psf_config; + uint8_t flags; + size_t data_size; + coding_t codec; + + + /* checks */ + if ((read_u32be(0x00,sf) & 0xFFFFFF00) != get_id32be("PSF\0")) + goto fail; + /* .psf: actual extension + * .swd: bigfile extension */ + if (!check_extensions(sf, "psf,swd")) + goto fail; + + flags = read_8bit(0x03,sf); + switch(flags) { + case 0xC0: /* [The Great Escape (PS2), Conflict: Desert Storm (PS2)] */ + case 0x40: /* [The Great Escape (PS2)] */ + case 0xA1: /* [Conflict: Desert Storm 2 (PS2)] */ + case 0x21: /* [Conflict: Desert Storm 2 (PS2), Conflict: Global Storm (PS2)] */ + //case 0x22: /* [Conflict: Vietman (PS2)] */ //todo weird size value, stereo, only one found + codec = coding_PSX; + interleave = 0x10; + + channel_count = 2; + if (flags == 0x21 || flags == 0x40) + channel_count = 1; + start_offset = 0x08; + break; + + case 0x80: /* [The Great Escape (PC/Xbox), Conflict: Desert Storm (Xbox/GC), Conflict: Desert Storm 2 (Xbox)] */ + case 0x81: /* [Conflict: Desert Storm 2 (Xbox), Conflict: Vietnam (Xbox)] */ + case 0x01: /* [Conflict: Global Storm (Xbox)] */ + codec = coding_PSX_pivotal; + interleave = 0x10; + + channel_count = 2; + if (flags == 0x01) + channel_count = 1; + start_offset = 0x08; + break; + + case 0xD1: /* [Conflict: Desert Storm 2 (GC)] */ + codec = coding_NGC_DSP; + interleave = 0x08; + + channel_count = 2; + start_offset = 0x08 + 0x60 * channel_count; + break; + + default: + goto fail; + } + + loop_flag = 0; + + psf_config = read_u32le(0x04, sf); + + /* pitch/cents? */ + rate_value = (psf_config >> 20) & 0xFFF; + switch(rate_value) { + case 3763: sample_rate = 44100; break; + case 1365: sample_rate = 16000; break; + case 940: sample_rate = 11050; break; + case 460: sample_rate = 5000; break; + default: + VGM_LOG("PSF: unknown rate value %x\n", rate_value); + sample_rate = rate_value * 11.72; /* not exact but works well enough */ + break; + } + + data_size = (psf_config & 0xFFFFF) * (interleave * channel_count); /* in blocks */ + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_PSF; + vgmstream->sample_rate = sample_rate; + + switch(codec) { + case coding_PSX: + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + + vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count); + break; + + case coding_PSX_pivotal: + vgmstream->coding_type = coding_PSX_pivotal; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + + vgmstream->num_samples = ps_cfg_bytes_to_samples(data_size, 0x10, channel_count); + break; + + case coding_NGC_DSP: + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + /* has standard DSP headers at 0x08 */ + dsp_read_coefs_be(vgmstream,sf,0x08+0x1c,0x60); + dsp_read_hist_be (vgmstream,sf,0x08+0x40,0x60); + + vgmstream->num_samples = read_32bitBE(0x08, sf);//dsp_bytes_to_samples(data_size, channel_count); + break; + + default: + goto fail; + } + + vgmstream->stream_size = data_size; + + if (!vgmstream_open_stream(vgmstream,sf,start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + + + +/* PSF segmented - Pivotal games multiple segments (external in some PC/Xbox or inside bigfiles) [The Great Escape, Conflict series] */ +VGMSTREAM* init_vgmstream_psf_segmented(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; + segmented_layout_data *data = NULL; + int i,j, sequence_count = 0, loop_flag = 0, loop_start = 0, loop_end = 0; + int sequence[512] = {0}; + off_t offsets[512] = {0}; + int total_subsongs = 0, target_subsong = sf->stream_index; + char stream_name[STREAM_NAME_SIZE] = {0}; + size_t stream_size = 0; + + + /* checks */ + if (!is_id32be(0x00,sf, "PSF\x60") && /* [The Great Escape (PC/Xbox/PS2), Conflict: Desert Storm (Xbox/GC)] */ + !is_id32be(0x00,sf, "PSF\x31")) /* [Conflict: Desert Storm 2 (Xbox/GC/PS2), Conflict: Global Terror (Xbox)] */ + goto fail; + + /* .psf: actual extension + * .swd: bigfile extension */ + if (!check_extensions(sf, "psf,swd")) + goto fail; + + + + /* transition table info: + * 0x00: offset + * 0x04: 0x02*4 next segment points (one per track) + * (xN segments) + * + * There are 4 possible tracks, like: normal, tension, action, high action. Segment 0 has tracks' + * entry segments (where 1=first, right after segment 0), and each segment has a link point to next + * (or starting) segment of any of other tracks. Thus, follow point 1/2/3/4 to playtrack 1/2/3/4 + * (points also loop back). It's designed to go smoothly between any tracks (1>3, 4>1, etc), + * so sometimes "step" segments (that aren't normally played) are used. + * If a track doesn't exist it may keep repeating silent segments, but still defines points. + * + * ex. sequence could go like this: + * (read segment 0 track1 entry): 1 + * - track0: 1>2>3>4>5>6>7>8>1>2>3, then to track2 goes 3>15>9 + * - track1: 9>10>11>12>13>14>9>10, then to track4 goes 10>33 + * - track2: 33>34>35>36>30>31>32>33, then to track1 goes 33>3 + * - track3: 3>4>5>6... (etc) + * + * Well make a sequence based on target subsong: + * - 1: tracks mixed with transitions, looping back to track1 (only first is used in .sch so we want all) + * - 2~5: track1~4 looping back to themselves + * - 6+: single segment (where 6=first segment) to allow free mixes + */ + { + int track[4][255] = {0}; + int count[4] = {0}; + int current_track, current_point, next_point, repeat_point; + int transition_count = read_s32le(0x04, sf); + + total_subsongs = 1 + 4 + (transition_count - 1); + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + + + if (target_subsong == 1) { + current_track = 0; /* start from first track, will move to others automatically */ + + snprintf(stream_name,sizeof(stream_name), "full"); + } + else if (target_subsong <= 1+4) { + current_track = (target_subsong-1) - 1; /* where 0 = first track */ + + snprintf(stream_name,sizeof(stream_name), "track%i", (current_track+1)); + } + else { + int segment = target_subsong - 1 - 4; /* where 1 = first segment */ + + sequence[0] = segment; + sequence_count = 1; + current_track = -1; + + /* show transitions to help with ordering */ + track[0][0] = read_16bitLE(0x08 + 0x0c*segment + 0x04, sf); + track[1][0] = read_16bitLE(0x08 + 0x0c*segment + 0x06, sf); + track[2][0] = read_16bitLE(0x08 + 0x0c*segment + 0x08, sf); + track[3][0] = read_16bitLE(0x08 + 0x0c*segment + 0x0a, sf); + snprintf(stream_name,sizeof(stream_name), "segment%03i to %03i/%03i/%03i/%03i", segment,track[0][0],track[1][0],track[2][0],track[3][0]); + } + + /* find target sequence */ + current_point = 0; /* segment 0 has track entry points */ + while (sequence_count < 512 && current_track >= 0) { + + next_point = read_16bitLE(0x08 + 0x0c*current_point + 0x04 + 0x02*current_track, sf); + + /* find if next point repeats in our current track */ + repeat_point = -1; + for (i = 0; i < count[current_track]; i++) { + + if (track[current_track][i] == next_point) { + repeat_point = i; + break; + } + } + + /* set loops and end sequence */ + if (repeat_point >= 0) { + if (target_subsong == 1) { + /* move to next track and change transition to next track too */ + current_track++; + + /* to loop properly we set loop end in track3 and loop start in track0 + * when track3 ends and move to track0 could have a transition segment + * before actually looping track0, so we do this in 2 steps */ + + if (loop_flag) { /* 2nd time repeat is found = loop start in track0 */ + loop_start = repeat_point; + break; /* sequence fully done */ + } + + if (current_track > 3) { /* 1st time repeat is found = loop end in track3 */ + current_track = 0; + loop_flag = 1; + } + + next_point = read_16bitLE(0x08 + 0x0c*current_point + 0x04 + 0x02*current_track, sf); + + if (loop_flag) { + loop_end = sequence_count; /* this points to the next_point that will be added below */ + } + } + else { + /* end track N */ + loop_flag = 1; + loop_start = repeat_point; + loop_end = sequence_count - 1; + break; + } + } + + + /* separate track info to find repeated points (since some transitions are common for all tracks) */ + track[current_track][count[current_track]] = next_point; + count[current_track]++; + + sequence[sequence_count] = next_point; + sequence_count++; + + current_point = next_point; + } + + if (sequence_count >= 512 || count[current_track] >= 512) + goto fail; + } + + + /* build segments */ + data = init_layout_segmented(sequence_count); + if (!data) goto fail; + + for (i = 0; i < sequence_count; i++) { + off_t psf_offset; + size_t psf_size; + int old_psf = -1; + + psf_offset = read_u32le(0x08 + sequence[i]*0x0c + 0x00, sf); + psf_size = get_streamfile_size(sf) - psf_offset; /* not ok but meh */ + + /* find repeated sections (sequences often repeat PSFs) */ + offsets[i] = psf_offset; + for (j = 0; j < i; j++) { + if (offsets[j] == psf_offset) { + old_psf = j; + break; + } + } + + /* reuse repeated VGMSTREAMs to improve memory and bitrate calcs a bit */ + if (old_psf >= 0) { + data->segments[i] = data->segments[old_psf]; + } + else { + temp_sf = setup_subfile_streamfile(sf, psf_offset, psf_size, "psf"); + if (!temp_sf) goto fail; + + data->segments[i] = init_vgmstream_psf_single(temp_sf); + if (!data->segments[i]) goto fail; + + stream_size += data->segments[i]->stream_size; /* only non-repeats */ + } + } + + /* setup VGMSTREAMs */ + if (!setup_layout_segmented(data)) + goto fail; + vgmstream = allocate_segmented_vgmstream(data,loop_flag, loop_start, loop_end); + if (!vgmstream) goto fail; + + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; + strcpy(vgmstream->stream_name, stream_name); + + return vgmstream; +fail: + if (!vgmstream) free_layout_segmented(data); + close_streamfile(temp_sf); + close_vgmstream(vgmstream); + return NULL; +} + +/* ***************************************************** */ + +static VGMSTREAM* init_vgmstream_psf_pfsm(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count, sample_rate = 0, rate_value = 0, interleave, big_endian; + size_t data_size; + coding_t codec; + int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; + int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; + + /* standard: + * 0x00: -1/number (lang?) + * 0x04: config/size? + * 0x08: channel size? only ok for PSX-pivotal + * 0x0c: sample rate or rate_value + * 0x0e: 0x4=PSX-pivotal or 0xFF=PSX + * 0x0f: name size (0xCC/FF=null) + * 0x10: data + * + * GC is similar with 0x20-align between some fields + */ + + /* checks */ + if (!is_id32be(0x00,sf, "PFSM") && + !is_id32le(0x00,sf, "PFSM")) + goto fail; + //if (!check_extensions(sf, "psf")) + // goto fail; + + big_endian = is_id32le(0x00,sf, "PFSM"); + if (big_endian) { + read_32bit = read_32bitBE; + read_16bit = read_16bitBE; + } + else { + read_32bit = read_32bitLE; + read_16bit = read_16bitLE; + } + + loop_flag = 0; + + + if (big_endian && read_32bit(0x50, sf) != 0) { /* GC */ + codec = coding_NGC_DSP; + interleave = 0x08; + channel_count = 1; + rate_value = (uint16_t)read_16bit(0x48, sf); + + start_offset = 0x60 + 0x60 * channel_count; + } + else if (big_endian) { /* GC */ + codec = coding_PCM16BE; + interleave = 0x02; + channel_count = 1; + rate_value = (uint16_t)read_16bit(0x48, sf); + + start_offset = 0x60; + } + else if ((uint8_t)read_8bit(0x16, sf) == 0xFF) { /* PS2 */ + codec = coding_PSX; + interleave = 0x10; + rate_value = (uint16_t)read_16bit(0x14, sf); + channel_count = 1; + + start_offset = 0x18; + } + else { /* PC/Xbox, some PS2/GC */ + codec = coding_PSX_pivotal; + interleave = 0x10; + sample_rate = (uint16_t)read_16bit(0x14, sf); + channel_count = 1; + + start_offset = 0x18; + } + + data_size = get_streamfile_size(sf) - start_offset; + + /* pitch/cents? */ + if (sample_rate == 0) { + /* pitch/cents? */ + switch(rate_value) { + case 3763: sample_rate = 44100; break; + case 1365: sample_rate = 16000; break; + case 940: sample_rate = 11050; break; + case 460: sample_rate = 5000; break; + default: + VGM_LOG("PSF: unknown rate value %x\n", rate_value); + sample_rate = rate_value * 11.72; /* not exact but works well enough */ + break; + } + } + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_PSF; + vgmstream->sample_rate = sample_rate; + + switch(codec) { + case coding_PCM16BE: + vgmstream->coding_type = coding_PCM16BE; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + + vgmstream->num_samples = pcm_bytes_to_samples(data_size, channel_count, 16); + break; + + case coding_PSX: + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + + vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count); + break; + + case coding_PSX_pivotal: + vgmstream->coding_type = coding_PSX_pivotal; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + + vgmstream->num_samples = ps_cfg_bytes_to_samples(data_size, 0x10, channel_count); + break; + + case coding_NGC_DSP: + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + /* has standard DSP headers at 0x08 */ + dsp_read_coefs_be(vgmstream,sf,0x60+0x1c,0x60); + dsp_read_hist_be (vgmstream,sf,0x60+0x40,0x60); + + vgmstream->num_samples = read_32bitBE(0x60, sf);//dsp_bytes_to_samples(data_size, channel_count); + break; + + default: + goto fail; + } + + if (!vgmstream_open_stream(vgmstream,sf,start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + + + +typedef enum { UNKNOWN, IMUS, PFST, PFSM } sch_type; +#define SCH_STREAM "Stream.swd" + + +/* SCH - Pivotal games multi-audio container [The Great Escape, Conflict series] */ +VGMSTREAM* init_vgmstream_sch(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* external_sf = NULL; + STREAMFILE* temp_sf = NULL; + off_t skip = 0, chunk_offset, target_offset = 0, header_offset, subfile_offset = 0; + size_t file_size, chunk_padding, target_size = 0, subfile_size = 0; + int big_endian; + int total_subsongs = 0, target_subsong = sf->stream_index; + int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; + sch_type target_type = UNKNOWN; + char stream_name[STREAM_NAME_SIZE] ={0}; + + + /* checks */ + if (is_id32be(0x00,sf, "HDRS")) /* "HDRSND" (found on later games) */ + skip = 0x0E; + if (!is_id32be(skip + 0x00,sf, "SCH\0") && + !is_id32le(skip + 0x00,sf, "SCH\0")) /* (BE consoles) */ + goto fail; + + if (!check_extensions(sf, "sch")) + goto fail; + + + /* chunked format (id+size, GC pads to 0x20 and uses BE/inverted ids): + * no other info so total subsongs would be count of usable chunks + * (offsets are probably in level .dat files) */ + big_endian = (is_id32le(skip + 0x00,sf, "SCH\0")); + if (big_endian) { + read_32bit = read_32bitBE; + chunk_padding = 0x18; + } + else { + read_32bit = read_32bitLE; + chunk_padding = 0; + } + + file_size = get_streamfile_size(sf); + if (read_32bit(skip + 0x04,sf) + skip + 0x08 + chunk_padding < file_size) /* sometimes padded */ + goto fail; + + if (target_subsong == 0) target_subsong = 1; + + chunk_offset = skip + 0x08 + chunk_padding; + + /* get all files*/ + while (chunk_offset < file_size) { + uint32_t chunk_id = read_32bitBE(chunk_offset + 0x00,sf); + uint32_t chunk_size = read_32bit(chunk_offset + 0x04,sf); + sch_type current_type = UNKNOWN; + + switch(chunk_id) { + case 0x494D5553: /* "IMUS" (TGE PC/Xbox only) */ + current_type = IMUS; + break; + + case 0x54534650: + case 0x50465354: /* "PFST" */ + current_type = PFST; + break; + + case 0x4D534650: + case 0x5046534D: /* "PFSM" */ + current_type = PFSM; + break; + + case 0x4B4E4142: + case 0x42414E4B: /* "BANK" */ + /* unknown format (variable size), maybe config for entry numbers */ + break; + case 0x424C4F4B: /* "BLOK" [Conflict: Desert Storm (Xbox)] */ + /* some ids or something? */ + break; + + default: + VGM_LOG("SCH: unknown chunk at %lx\n", chunk_offset); + goto fail; + } + + if (current_type != UNKNOWN) + total_subsongs++; + + if (total_subsongs == target_subsong && target_type == UNKNOWN) { + target_type = current_type; + target_offset = chunk_offset; + target_size = 0x08 + chunk_padding + chunk_size; + } + + chunk_offset += 0x08 + chunk_padding + chunk_size; + } + + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + if (target_size == 0) goto fail; + + header_offset = target_offset + 0x08 + chunk_padding; + + //;VGM_LOG("SCH: offset=%lx, size=%x\n",target_offset, target_size); + + switch(target_type) { + case IMUS: { /* external segmented track */ + STREAMFILE *psf_sf; + uint8_t name_size; + char name[255]; + + /* 0x00: config/size? + * 0x04: name size + * 0x05: segments + * 0x06: ? + * 0x08: relative path to .psf + * 0xNN: segment table (same as .psf) + */ + + name_size = read_u8(header_offset + 0x04, sf); + read_string(name,name_size, header_offset + 0x08, sf); + + /* later games have name but actually use bigfile [Conflict: Global Storm (Xbox)] */ + if (read_u8(header_offset + 0x07, sf) == 0xCC) { + external_sf = open_streamfile_by_filename(sf, SCH_STREAM); + if (!external_sf) { + vgm_logi("SCH: external file '%s' not found (put together)\n", SCH_STREAM); + goto fail; + } + + subfile_offset = read_32bit(header_offset + 0x08 + name_size, sf); + subfile_size = get_streamfile_size(external_sf) - subfile_offset; /* not ok but meh */ + + temp_sf = setup_subfile_streamfile(external_sf, subfile_offset,subfile_size, "psf"); + if (!temp_sf) goto fail; + + psf_sf = temp_sf; + } + else { + external_sf = open_streamfile_by_filename(sf, name); + if (!external_sf) { + vgm_logi("SCH: external file '%s' not found (put together)\n", name); + goto fail; + } + + psf_sf = external_sf; + } + + vgmstream = init_vgmstream_psf_segmented(psf_sf); + if (!vgmstream) { + vgmstream = init_vgmstream_psf_single(psf_sf); + if (!vgmstream) goto fail; + } + + snprintf(stream_name,sizeof(stream_name), "%s-%s" , "IMUS", name); + break; + } + + case PFST: { /* external track */ + STREAMFILE *psf_sf; + uint8_t name_size; + char name[255]; + + if (chunk_padding == 0 && target_size > 0x08 + 0x0c) { /* TGE PC/Xbox version */ + /* 0x00: -1/0 + * 0x04: config/size? + * 0x08: channel size + * 0x0c: sample rate? (differs vs PSF) + * 0x0e: 4? + * 0x0f: name size + * 0x10: name + */ + + /* later games have name but actually use bigfile [Conflict: Global Storm (Xbox)] */ + if ((read_32bitBE(header_offset + 0x14, sf) & 0x0000FFFF) == 0xCCCC) { + name_size = read_8bit(header_offset + 0x13, sf); + read_string(name,name_size, header_offset + 0x18, sf); + + external_sf = open_streamfile_by_filename(sf, SCH_STREAM); + if (!external_sf) { + vgm_logi("SCH: external file '%s' not found (put together)\n", SCH_STREAM); + goto fail; + } + + subfile_offset = read_32bit(header_offset + 0x0c, sf); + subfile_size = get_streamfile_size(external_sf) - subfile_offset; /* not ok but meh */ + + temp_sf = setup_subfile_streamfile(external_sf, subfile_offset,subfile_size, "psf"); + if (!temp_sf) goto fail; + + psf_sf = temp_sf; + } + else { + name_size = read_8bit(header_offset + 0x0f, sf); + read_string(name,name_size, header_offset + 0x10, sf); + + external_sf = open_streamfile_by_filename(sf, name); + if (!external_sf) { + vgm_logi("SCH: external file '%s' not found (put together)\n", name); + goto fail; + } + + psf_sf = external_sf; + } + } + else if (chunk_padding) { + strcpy(name, "STREAM.SWD"); /* fixed */ + + /* 0x00: -1 + * 0x04: config/size? + * 0x08: .swd offset + */ + external_sf = open_streamfile_by_filename(sf, name); + if (!external_sf) goto fail; + + subfile_offset = read_32bit(header_offset + 0x24, sf); + subfile_size = get_streamfile_size(external_sf) - subfile_offset; /* not ok but meh */ + + temp_sf = setup_subfile_streamfile(external_sf, subfile_offset,subfile_size, "psf"); + if (!temp_sf) goto fail; + + psf_sf = temp_sf; + } + else { /* others */ + strcpy(name, "STREAM.SWD"); /* fixed */ + + /* 0x00: -1 + * 0x04: config/size? + * 0x08: .swd offset + */ + external_sf = open_streamfile_by_filename(sf, name); + if (!external_sf) goto fail; + + subfile_offset = read_32bit(header_offset + 0x08, sf); + subfile_size = get_streamfile_size(external_sf) - subfile_offset; /* not ok but meh */ + + temp_sf = setup_subfile_streamfile(external_sf, subfile_offset,subfile_size, "psf"); + if (!temp_sf) goto fail; + + psf_sf = temp_sf; + } + + vgmstream = init_vgmstream_psf_segmented(psf_sf); + if (!vgmstream) { + vgmstream = init_vgmstream_psf_single(psf_sf); + if (!vgmstream) goto fail; + } + + snprintf(stream_name,sizeof(stream_name), "%s-%s" , "PFST", name); + break; + } + + case PFSM: + /* internal sound */ + + temp_sf = setup_subfile_streamfile(sf, target_offset,target_size, NULL); + if (!temp_sf) goto fail; + + vgmstream = init_vgmstream_psf_pfsm(temp_sf); + if (!vgmstream) goto fail; + + snprintf(stream_name,sizeof(stream_name), "%s" , "PFSM"); + break; + + default: /* target not found */ + goto fail; + } + + vgmstream->num_streams = total_subsongs; + strcpy(vgmstream->stream_name, stream_name); + + close_streamfile(temp_sf); + close_streamfile(external_sf); + return vgmstream; + +fail: + close_streamfile(temp_sf); + close_streamfile(external_sf); + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/riff.c b/Frameworks/vgmstream/vgmstream/src/meta/riff.c index 86232ed31..6ec580ae3 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/riff.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/riff.c @@ -131,6 +131,9 @@ static int read_fmt(int big_endian, STREAMFILE* sf, off_t offset, riff_fmt_chunk } } + if (!fmt->channels) + goto fail; + switch (fmt->codec) { case 0x00: /* Yamaha AICA ADPCM [Headhunter (DC), Bomber hehhe (DC), Rayman 2 (DC)] (unofficial) */ if (fmt->bps != 4) goto fail; @@ -142,17 +145,19 @@ static int read_fmt(int big_endian, STREAMFILE* sf, off_t offset, riff_fmt_chunk case 0x01: /* PCM */ switch (fmt->bps) { + case 24: /* Omori (PC) */ + fmt->coding_type = coding_PCM24LE; + break; case 16: fmt->coding_type = big_endian ? coding_PCM16BE : coding_PCM16LE; - fmt->interleave = 0x02; break; case 8: fmt->coding_type = coding_PCM8_U; - fmt->interleave = 0x01; break; default: goto fail; } + fmt->interleave = fmt->block_size / fmt->channels; break; case 0x02: /* MSADPCM */ @@ -354,8 +359,9 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) { * .mwv: Level-5 games [Dragon Quest VIII (PS2), Rogue Galaxy (PS2)] * .ima: Baja: Edge of Control (PS3/X360) * .nsa: Studio Ring games that uses NScripter [Hajimete no Otetsudai (PC)] + * .pcm: Silent Hill Arcade (PC) */ - if ( check_extensions(sf, "wav,lwav,xwav,da,dax,cd,med,snd,adx,adp,xss,xsew,adpcm,adw,wd,,sbv,wvx,str,at3,rws,aud,at9,saf,ima,nsa") ) { + if ( check_extensions(sf, "wav,lwav,xwav,da,dax,cd,med,snd,adx,adp,xss,xsew,adpcm,adw,wd,,sbv,wvx,str,at3,rws,aud,at9,saf,ima,nsa,pcm") ) { ; } else if ( check_extensions(sf, "mwv") ) { @@ -666,12 +672,10 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) { /* samples, codec init (after setting coding to ensure proper close on failure) */ switch (fmt.coding_type) { + case coding_PCM24LE: case coding_PCM16LE: - vgmstream->num_samples = pcm_bytes_to_samples(data_size, fmt.channels, 16); - break; - case coding_PCM8_U: - vgmstream->num_samples = pcm_bytes_to_samples(data_size, vgmstream->channels, 8); + vgmstream->num_samples = pcm_bytes_to_samples(data_size, fmt.channels, fmt.bps); break; case coding_L5_555: @@ -1068,10 +1072,8 @@ VGMSTREAM* init_vgmstream_rifx(STREAMFILE* sf) { /* init, samples */ switch (fmt.coding_type) { case coding_PCM16BE: - vgmstream->num_samples = pcm_bytes_to_samples(data_size, vgmstream->channels, 16); - break; case coding_PCM8_U: - vgmstream->num_samples = pcm_bytes_to_samples(data_size, vgmstream->channels, 8); + vgmstream->num_samples = pcm_bytes_to_samples(data_size, vgmstream->channels, fmt.bps); break; default: goto fail; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c b/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c index 83be3944f..272f109eb 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c @@ -110,7 +110,7 @@ VGMSTREAM* init_vgmstream_sqex_scd(STREAMFILE* sf) { /** stream header **/ stream_size = read_32bit(meta_offset+0x00,sf); - channels = read_32bit(meta_offset+0x04,sf); + channels = read_32bit(meta_offset+0x04,sf); sample_rate = read_32bit(meta_offset+0x08,sf); codec = read_32bit(meta_offset+0x0c,sf); @@ -270,6 +270,7 @@ VGMSTREAM* init_vgmstream_sqex_scd(STREAMFILE* sf) { } #endif case 0x0C: /* MS ADPCM [Final Fantasy XIV (PC) sfx] */ + case 0x17: /* MS ADPCM [Dragon Quest X (Switch)] (no diffs except frame size?) */ vgmstream->coding_type = coding_MSADPCM; vgmstream->layout_type = layout_none; vgmstream->frame_size = read_16bit(extradata_offset + 0x0c, sf); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/wwise.c b/Frameworks/vgmstream/vgmstream/src/meta/wwise.c index b906d3e66..945677fdc 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/wwise.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/wwise.c @@ -15,8 +15,9 @@ typedef enum { PCM, IMA, VORBIS, DSP, XMA2, XWMA, AAC, HEVAG, ATRAC9, OPUSNX, OP typedef struct { int big_endian; size_t file_size; - int truncated; + int prefetch; int is_wem; + int is_bnk; /* chunks references */ off_t fmt_offset; @@ -57,10 +58,13 @@ typedef struct { static int parse_wwise(STREAMFILE* sf, wwise_header* ww); static int is_dsp_full_interleave(STREAMFILE* sf, wwise_header* ww, off_t coef_offset); - - /* Wwise - Audiokinetic Wwise (WaveWorks Interactive Sound Engine) middleware */ VGMSTREAM* init_vgmstream_wwise(STREAMFILE* sf) { + return init_vgmstream_wwise_bnk(sf, NULL); +} + +/* used in .bnk */ +VGMSTREAM* init_vgmstream_wwise_bnk(STREAMFILE* sf, int* p_prefetch) { VGMSTREAM* vgmstream = NULL; wwise_header ww = {0}; off_t start_offset; @@ -83,9 +87,13 @@ VGMSTREAM* init_vgmstream_wwise(STREAMFILE* sf) { if (!check_extensions(sf,"wem,wav,lwav,ogg,logg,xma,bnk")) goto fail; + ww.is_bnk = (p_prefetch != NULL); if (!parse_wwise(sf, &ww)) goto fail; + if (p_prefetch) + *p_prefetch = ww.prefetch; + read_u32 = ww.big_endian ? read_u32be : read_u32le; read_s32 = ww.big_endian ? read_s32be : read_s32le; read_u16 = ww.big_endian ? read_u16be : read_u16le; @@ -114,14 +122,14 @@ VGMSTREAM* init_vgmstream_wwise(STREAMFILE* sf) { vgmstream->layout_type = ww.channels > 1 ? layout_interleave : layout_none; vgmstream->interleave_block_size = 0x02; - if (ww.truncated) { + if (ww.prefetch) { ww.data_size = ww.file_size - ww.data_offset; } vgmstream->num_samples = pcm_bytes_to_samples(ww.data_size, ww.channels, ww.bits_per_sample); - /* truncated .bnk RIFFs that only have header and no data is possible [Metal Gear Solid V (PC)] */ - if (ww.truncated && !vgmstream->num_samples) + /* prefetch .bnk RIFFs that only have header and no data is possible [Metal Gear Solid V (PC)] */ + if (ww.prefetch && !vgmstream->num_samples) vgmstream->num_samples = 1; /* force something to avoid broken subsongs */ break; @@ -147,7 +155,7 @@ VGMSTREAM* init_vgmstream_wwise(STREAMFILE* sf) { vgmstream->interleave_block_size = 0; } - if (ww.truncated) { + if (ww.prefetch) { ww.data_size = ww.file_size - ww.data_offset; } @@ -299,7 +307,7 @@ VGMSTREAM* init_vgmstream_wwise(STREAMFILE* sf) { start_offset += audio_offset; /* Vorbis is VBR so this is very approximate percent, meh */ - if (ww.truncated) { + if (ww.prefetch) { vgmstream->num_samples = (int32_t)(vgmstream->num_samples * (double)(ww.file_size - start_offset) / (double)ww.data_size); } @@ -333,7 +341,7 @@ VGMSTREAM* init_vgmstream_wwise(STREAMFILE* sf) { goto fail; } - if (ww.truncated) { + if (ww.prefetch) { ww.data_size = ww.file_size - ww.data_offset; vgmstream->num_samples = dsp_bytes_to_samples(ww.data_size, ww.channels); } @@ -381,7 +389,7 @@ VGMSTREAM* init_vgmstream_wwise(STREAMFILE* sf) { xma_fix_raw_samples(vgmstream, sf, ww.data_offset, ww.data_size, ww.xma2_offset ? ww.xma2_offset : ww.fmt_offset, 1,0); /* XMA is VBR so this is very approximate percent, meh */ - if (ww.truncated) { + if (ww.prefetch) { vgmstream->num_samples = (int32_t)(vgmstream->num_samples * (double)(ww.file_size - start_offset) / (double)ww.data_size); //todo data size, call function @@ -449,7 +457,7 @@ VGMSTREAM* init_vgmstream_wwise(STREAMFILE* sf) { } /* OPUS is VBR so this is very approximate percent, meh */ - if (ww.truncated) { + if (ww.prefetch) { vgmstream->num_samples = (int32_t)(vgmstream->num_samples * (double)(ww.file_size - start_offset) / (double)ww.data_size); ww.data_size = ww.file_size - start_offset; @@ -471,7 +479,7 @@ VGMSTREAM* init_vgmstream_wwise(STREAMFILE* sf) { /* 0x20: full samples (without encoder delay) */ /* OPUS is VBR so this is very approximate percent, meh */ - if (ww.truncated) { + if (ww.prefetch) { vgmstream->num_samples = (int32_t)(vgmstream->num_samples * (double)(ww.file_size - start_offset) / (double)ww.data_size); ww.data_size = ww.file_size - start_offset; @@ -532,7 +540,7 @@ VGMSTREAM* init_vgmstream_wwise(STREAMFILE* sf) { goto fail; /* OPUS is VBR so this is very approximate percent, meh */ - if (ww.truncated) { + if (ww.prefetch) { vgmstream->num_samples = (int32_t)(vgmstream->num_samples * (double)(ww.file_size - start_offset) / (double)ww.data_size); ww.data_size = ww.file_size - start_offset; @@ -641,7 +649,7 @@ VGMSTREAM* init_vgmstream_wwise(STREAMFILE* sf) { vgmstream->interleave_block_size = ww.block_size / ww.channels; //vgmstream->codec_endian = ww.big_endian; //? - if (ww.truncated) { + if (ww.prefetch) { ww.data_size = ww.file_size - ww.data_offset; } @@ -665,20 +673,20 @@ fail: static int is_dsp_full_interleave(STREAMFILE* sf, wwise_header* ww, off_t coef_offset) { /* older (only?) Wwise use full interleave for memory (in .bnk) files, but * detection from the .wem side is problematic [Punch Out!! (Wii)] - * - truncated point to streams = normal + * - prefetch point to streams = normal * - .bnk would be memory banks = full * - otherwise small-ish sizes, stereo, with initial predictors for the * second channel matching half size = full * some files aren't detectable like this though, when predictors are 0 * (but since memory wem aren't that used this shouldn't be too common) */ - if (ww->truncated) + if (ww->prefetch) return 0; if (ww->channels == 1) return 0; - if (check_extensions(sf,"bnk")) + if (ww->is_bnk) return 1; if (ww->data_size > 0x30000) @@ -910,9 +918,9 @@ static int parse_wwise(STREAMFILE* sf, wwise_header* ww) { if (ww->codec == PCM || ww->codec == IMA || ww->codec == VORBIS || ww->codec == DSP || ww->codec == XMA2 || ww->codec == OPUSNX || ww->codec == OPUS || ww->codec == OPUSWW || ww->codec == PTADPCM) { - ww->truncated = 1; /* only seen those, probably all exist (XWMA, AAC, HEVAG, ATRAC9?) */ + ww->prefetch = 1; /* only seen those, probably all exist (XWMA, AAC, HEVAG, ATRAC9?) */ } else { - vgm_logi("WWISE: wrong expected size, maybe truncated (report)\n"); + vgm_logi("WWISE: wrong expected size, maybe prefetch (report)\n"); goto fail; } } @@ -921,7 +929,7 @@ static int parse_wwise(STREAMFILE* sf, wwise_header* ww) { /* Cyberpunk 2077 has some mutant .wem, with proper Wwise header and PCMEX but data is standard OPUS. * From init bank and CAkSound's sources, those may be piped through their plugins. They come in * .opuspak (no names), have wrong riff/data sizes and only seem used for sfx (other audio is Vorbis). */ - if (ww->format == 0xFFFE && ww->truncated) { + if (ww->format == 0xFFFE && ww->prefetch) { if (read_u32be(ww->data_offset + 0x00, sf) == 0x4F676753) { ww->codec = OPUSCPR; }