diff --git a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj index 90cc56df3..184731c31 100644 --- a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj +++ b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj @@ -82,6 +82,7 @@ 831BA61F1EAC61A500CF89B0 /* x360_cxs.c in Sources */ = {isa = PBXBuildFile; fileRef = 831BA6151EAC61A500CF89B0 /* x360_cxs.c */; }; 831BA6211EAC61A500CF89B0 /* x360_pasx.c in Sources */ = {isa = PBXBuildFile; fileRef = 831BA6171EAC61A500CF89B0 /* x360_pasx.c */; }; 831BA6281EAC61CB00CF89B0 /* coding_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 831BA6221EAC61CB00CF89B0 /* coding_utils.c */; }; + 8322ECE7240268BB009E9429 /* raw_al.c in Sources */ = {isa = PBXBuildFile; fileRef = 8322ECE6240268BA009E9429 /* raw_al.c */; }; 8323894A1D22419B00482226 /* clHCA.c in Sources */ = {isa = PBXBuildFile; fileRef = 832389481D22419B00482226 /* clHCA.c */; }; 8323894B1D22419B00482226 /* clHCA.h in Headers */ = {isa = PBXBuildFile; fileRef = 832389491D22419B00482226 /* clHCA.h */; settings = {ATTRIBUTES = (Public, ); }; }; 832389501D2246C300482226 /* hca.c in Sources */ = {isa = PBXBuildFile; fileRef = 8323894F1D2246C300482226 /* hca.c */; }; @@ -115,7 +116,6 @@ 832BF82C21E0514B006F50F1 /* hca_keys_awb.h in Headers */ = {isa = PBXBuildFile; fileRef = 832BF81A21E0514A006F50F1 /* hca_keys_awb.h */; }; 832BF82D21E0514B006F50F1 /* nus3audio.c in Sources */ = {isa = PBXBuildFile; fileRef = 832BF81B21E0514B006F50F1 /* nus3audio.c */; }; 832C70BF1E9335E400BD7B4E /* Vorbis.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83F4128F1E932F9A002E37D0 /* Vorbis.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 83345A501F8AEB2800B2EAA4 /* pc_al2.c in Sources */ = {isa = PBXBuildFile; fileRef = 83345A4C1F8AEB2700B2EAA4 /* pc_al2.c */; }; 83345A521F8AEB2800B2EAA4 /* xvag.c in Sources */ = {isa = PBXBuildFile; fileRef = 83345A4E1F8AEB2800B2EAA4 /* xvag.c */; }; 833A7A2E1ED11961003EC53E /* xau.c in Sources */ = {isa = PBXBuildFile; fileRef = 833A7A2D1ED11961003EC53E /* xau.c */; }; 8342469420C4D23000926E48 /* h4m.c in Sources */ = {isa = PBXBuildFile; fileRef = 8342469020C4D22F00926E48 /* h4m.c */; }; @@ -773,6 +773,7 @@ 831BA6171EAC61A500CF89B0 /* x360_pasx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = x360_pasx.c; sourceTree = ""; }; 831BA6221EAC61CB00CF89B0 /* coding_utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = coding_utils.c; sourceTree = ""; }; 831BD11F1EEE1CF200198540 /* ngc_ulw.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = ngc_ulw.c; sourceTree = ""; }; + 8322ECE6240268BA009E9429 /* raw_al.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = raw_al.c; sourceTree = ""; }; 832389481D22419B00482226 /* clHCA.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = clHCA.c; sourceTree = ""; }; 832389491D22419B00482226 /* clHCA.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = clHCA.h; sourceTree = ""; }; 8323894F1D2246C300482226 /* hca.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hca.c; sourceTree = ""; }; @@ -805,7 +806,6 @@ 832BF81921E0514A006F50F1 /* xopus.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xopus.c; sourceTree = ""; }; 832BF81A21E0514A006F50F1 /* hca_keys_awb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hca_keys_awb.h; sourceTree = ""; }; 832BF81B21E0514B006F50F1 /* nus3audio.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nus3audio.c; sourceTree = ""; }; - 83345A4C1F8AEB2700B2EAA4 /* pc_al2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pc_al2.c; sourceTree = ""; }; 83345A4E1F8AEB2800B2EAA4 /* xvag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xvag.c; sourceTree = ""; }; 833A7A2D1ED11961003EC53E /* xau.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xau.c; sourceTree = ""; }; 8342469020C4D22F00926E48 /* h4m.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = h4m.c; sourceTree = ""; }; @@ -1771,7 +1771,6 @@ 836F6E8418BDC2180095E648 /* p3d.c */, 8349A8FE1FE6257F00E26435 /* pc_adp_otns.c */, 836F6E8518BDC2180095E648 /* pc_adp.c */, - 83345A4C1F8AEB2700B2EAA4 /* pc_al2.c */, 8349A8F01FE6257C00E26435 /* pc_ast.c */, 836F6E8618BDC2180095E648 /* pc_mxst.c */, 8306B0D12098458F000302D4 /* pcm_sre.c */, @@ -1842,6 +1841,7 @@ 836F6ED918BDC2190095E648 /* ps3_past.c */, 837CEAE823487F2B00E62A4A /* psf.c */, 83997F5722D9569E00633184 /* rad.c */, + 8322ECE6240268BA009E9429 /* raw_al.c */, 837CEAED23487F2C00E62A4A /* raw_int.c */, 837CEADD23487F2A00E62A4A /* raw_pcm.c */, 837CEAEA23487F2B00E62A4A /* raw_snds.c */, @@ -2308,6 +2308,7 @@ 836F6F9418BDC2190095E648 /* ivb.c in Sources */, 836F6F8D18BDC2190095E648 /* gsp_gsb.c in Sources */, 836F704518BDC2190095E648 /* wvs.c in Sources */, + 8322ECE7240268BB009E9429 /* raw_al.c in Sources */, 83EDE5D91A70951A005F5D84 /* btsnd.c in Sources */, 8306B0E420984590000302D4 /* wave.c in Sources */, 836F6FFC18BDC2190095E648 /* ps2_ster.c in Sources */, @@ -2485,7 +2486,6 @@ 836F702218BDC2190095E648 /* rsd.c in Sources */, 8349A90D1FE6258200E26435 /* ubi_sb.c in Sources */, 83C7281D22BC893D00678B4A /* ikm.c in Sources */, - 83345A501F8AEB2800B2EAA4 /* pc_al2.c in Sources */, 834FE0BB215C798C000A5D3D /* celt_fsb_decoder.c in Sources */, 836F702518BDC2190095E648 /* rwx.c in Sources */, 836F6F3A18BDC2190095E648 /* sdx2_decoder.c in Sources */, diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c index c038e2b24..13a3a02e1 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c @@ -936,7 +936,6 @@ void ffmpeg_set_force_seek(ffmpeg_codec_data * data) { /* some formats like Smacker are so buggy that any seeking is impossible (even on video players), * or MPC with an incorrectly parsed seek table (using as 0 some non-0 seek offset). * whatever, we'll just kill and reconstruct FFmpeg's config every time */ - ;VGM_LOG("1\n"); data->force_seek = 1; reset_ffmpeg_internal(data); /* reset state from trying to seek */ //stream = data->formatCtx->streams[data->streamIndex]; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/g7221_decoder_aes.c b/Frameworks/vgmstream/vgmstream/src/coding/g7221_decoder_aes.c index e7d250184..dfdfd4f06 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/g7221_decoder_aes.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/g7221_decoder_aes.c @@ -18,8 +18,8 @@ struct s14aes_handle { /* MixColumn(?) LUTs, unlike normal Rijndael which uses 4 tables: Td0a Td0b..., Td1a Td1b..., ... * layout is: Td0a Td1a Td2a Td3a, Td0b Td0b Td1b Td2b, ... (better for CPU cache?) */ uint32_t tds[256*4]; - /* expanded roundkey, actual final key */ - uint32_t rk[52]; + /* expanded roundkey, actual final key (192-bit keys only need up to 52 though) */ + uint32_t rk[64]; } ; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/g7221_decoder_lib.c b/Frameworks/vgmstream/vgmstream/src/coding/g7221_decoder_lib.c index b5e474145..1545bd85f 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/g7221_decoder_lib.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/g7221_decoder_lib.c @@ -941,7 +941,7 @@ static int decode_vector_quantized_mlt_indices(uint32_t* data_u32, int* p_bitpos } /* unpacks input buffer into MLT coefs */ -static int unpack_frame(int bit_rate, const uint8_t* data, int frame_size, /*int* p_frame_size, */ int* p_mag_shift, int16_t* mlt_coefs, uint32_t* p_random_value) { +static int unpack_frame(int bit_rate, const uint8_t* data, int frame_size, /*int* p_frame_size, */ int* p_mag_shift, int16_t* mlt_coefs, uint32_t* p_random_value, int test_errors) { uint32_t data_u32[0x78/4 + 2]; int bitpos, expected_frame_size; int power_categories[NUMBER_OF_REGIONS]; @@ -1071,19 +1071,38 @@ static int unpack_frame(int bit_rate, const uint8_t* data, int frame_size, /*int /* test for errors (in refdec but not Namco's, useful to detect decryption) */ - { + if (test_errors) { int bits_left = 8 * expected_frame_size - bitpos; - int i; + int i, endpos, test_bits; if (bits_left > 0) { + /* frame must be padded with 1s */ + endpos = bitpos; for (i = 0; i < bits_left; i++) { - int bit = (data_u32[bitpos >> 5] >> (31 - (bitpos & 0x1F))) & 1; - bitpos++; + int bit = (data_u32[endpos >> 5] >> (31 - (endpos & 0x1F))) & 1; + endpos++; if (bit == 0) return -1; } + + /* extra: test we aren't in the middle of padding (happens with bad keys) */ + endpos = bitpos; + test_bits = 8 * 0x04; + if (test_bits > bitpos) + test_bits = bitpos; + for (i = 0; i < test_bits; i++) { + int bit = (data_u32[endpos >> 5] >> (31 - (endpos & 0x1F))) & 1; + endpos--; + + if (bit != 1) + break; + } + /* so many 1s isn't very normal */ + if (i == test_bits) + return -8; + } else { /* ? */ @@ -1145,10 +1164,11 @@ fail: int g7221_decode_frame(g7221_handle* handle, uint8_t* data, int16_t* out_samples) { int res; int mag_shift; + int encrypted = handle->aes != NULL; /* first 0x10 bytes may be encrypted with AES. Original code also saves encrypted bytes, * then re-crypts after unpacking, presumably to guard against memdumps. */ - if (handle->aes != NULL) { + if (encrypted) { s14aes_decrypt(handle->aes, data); } @@ -1156,7 +1176,7 @@ int g7221_decode_frame(g7221_handle* handle, uint8_t* data, int16_t* out_samples * so we could avoid one extra buffer, but for clarity we'll leave as is */ /* unpack data into MLT spectrum coefs */ - res = unpack_frame(handle->bit_rate, data, handle->frame_size, &mag_shift, handle->mlt_coefs, &handle->random_value); + res = unpack_frame(handle->bit_rate, data, handle->frame_size, &mag_shift, handle->mlt_coefs, &handle->random_value, encrypted); if (res < 0) goto fail; /* convert coefs to samples using reverse (inverse) MLT */ diff --git a/Frameworks/vgmstream/vgmstream/src/formats.c b/Frameworks/vgmstream/vgmstream/src/formats.c index 543c066d0..02cb4c25a 100644 --- a/Frameworks/vgmstream/vgmstream/src/formats.c +++ b/Frameworks/vgmstream/vgmstream/src/formats.c @@ -62,6 +62,7 @@ static const char* extension_list[] = { "aiffl", //fake extension for .aif??? "aix", "akb", + "al", "al2", "amts", //fake extension/header id for .stm (renamed? to be removed?) "ao", @@ -1156,7 +1157,7 @@ static const meta_info meta_info_list[] = { {meta_EA_SNU, "Electronic Arts SNU header"}, {meta_AWC, "Rockstar AWC header"}, {meta_OPUS, "Nintendo Switch OPUS header"}, - {meta_PC_AL2, "Illwinter Game Design AL2 raw header"}, + {meta_RAW_AL, "Illwinter Game Design .AL raw header"}, {meta_PC_AST, "Capcom AST (PC) header"}, {meta_UBI_SB, "Ubisoft SBx header"}, {meta_NAAC, "Namco NAAC header"}, @@ -1275,16 +1276,20 @@ void get_vgmstream_coding_description(VGMSTREAM *vgmstream, char *out, size_t ou int i, list_length; const char *description; - /* we need to recurse down because of FFmpeg */ - if (vgmstream->layout_type == layout_layered) { - layered_layout_data* layout_data = vgmstream->layout_data; - get_vgmstream_coding_description(layout_data->layers[0], out, out_size); - return; - } else if (vgmstream->layout_type == layout_segmented) { - segmented_layout_data* layout_data = vgmstream->layout_data; - get_vgmstream_coding_description(layout_data->segments[0], out, out_size); - return; +#ifdef VGM_USE_FFMPEG + if (vgmstream->coding_type == coding_FFmpeg) { + /* recurse down for FFmpeg, but metas should set prefered/main codec, or maybe print a list of codecs */ + if (vgmstream->layout_type == layout_layered) { + layered_layout_data* layout_data = vgmstream->layout_data; + get_vgmstream_coding_description(layout_data->layers[0], out, out_size); + return; + } else if (vgmstream->layout_type == layout_segmented) { + segmented_layout_data* layout_data = vgmstream->layout_data; + get_vgmstream_coding_description(layout_data->segments[0], out, out_size); + return; + } } +#endif description = "CANNOT DECODE"; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h index 027f0b068..e691f0ba4 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h @@ -231,6 +231,9 @@ static const adxkey_info adxkey9_list[] = { /* Nogizaka46 Rhythm Festival (Android) */ {0x2378,0x5511,0x0201, NULL,5613126134333697}, // 0013F11BC5510101 + /* Detective Conan Runner / Case Closed Runner (Android) */ + {0x0613,0x0e3d,0x6dff, NULL,1175268187653273344}, // 104f643098e3f700 + }; static const int adxkey8_list_count = sizeof(adxkey8_list) / sizeof(adxkey8_list[0]); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bnsf.c b/Frameworks/vgmstream/vgmstream/src/meta/bnsf.c index b66dbc5d7..3e0d1c0d6 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/bnsf.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/bnsf.c @@ -4,7 +4,12 @@ #include "bnsf_keys.h" -static void find_bnsf_key(g7221_codec_data *data, off_t start, STREAMFILE *sf, uint8_t *best_key); +//#define BNSF_BRUTEFORCE +#ifdef BNSF_BRUTEFORCE +static void bruteforce_bnsf_key(STREAMFILE* sf, off_t start, g7221_codec_data* data, uint8_t* best_key); +#endif +static void find_bnsf_key(STREAMFILE *sf, off_t start, g7221_codec_data *data, uint8_t *best_key); + /* BNSF - Bandai Namco Sound Format/File [Tales of Graces (Wii), Tales of Berseria (PS4)] */ VGMSTREAM * init_vgmstream_bnsf(STREAMFILE *streamFile) { @@ -81,10 +86,14 @@ VGMSTREAM * init_vgmstream_bnsf(STREAMFILE *streamFile) { uint8_t key[24] = {0}; /* keystring 0-padded to 192-bit */ keysize = read_key_file(key, sizeof(key), streamFile); +#ifdef BNSF_BRUTEFORCE + if (1) { + bruteforce_bnsf_key(streamFile, start_offset, vgmstream->codec_data, key); + } else +#endif if (keysize <= 0 || keysize > sizeof(key)) { - find_bnsf_key(vgmstream->codec_data, start_offset, streamFile, key); + find_bnsf_key(streamFile, start_offset, vgmstream->codec_data, key); } - set_key_g7221(vgmstream->codec_data, key); } @@ -117,40 +126,96 @@ fail: return NULL; } -static void find_bnsf_key(g7221_codec_data* data, off_t start, STREAMFILE* sf, uint8_t* best_key) { - const size_t keys_length = sizeof(s14key_list) / sizeof(bnsfkey_info); - int score, best_score = -1; - int i; +static inline void test_key(STREAMFILE* sf, off_t start, g7221_codec_data* data, const char* key, int keylen, int* p_best_score, uint8_t* p_best_key) { uint8_t tmpkey[24]; + int score; + + if (keylen > sizeof(tmpkey)) + return; + memcpy(tmpkey, key, keylen); + memset(tmpkey + keylen, 0, sizeof(tmpkey) - keylen); + + //;VGM_LOG("BNSF: test key=%.24s\n", tmpkey); + set_key_g7221(data, tmpkey); + + score = test_key_g7221(data, start, sf); + if (score < 0) return; + + if (*p_best_score <= 0 || (score < *p_best_score && score > 0)) { + *p_best_score = score; + memcpy(p_best_key, key, keylen); + memset(p_best_key + keylen, 0, sizeof(tmpkey) - keylen); + } +} + +static void find_bnsf_key(STREAMFILE* sf, off_t start, g7221_codec_data* data, uint8_t* best_key) { + const size_t keys_length = sizeof(s14key_list) / sizeof(bnsfkey_info); + int best_score = -1; + int i; for (i = 0; i < keys_length; i++) { const char* key = s14key_list[i].key; int keylen = strlen(key); - if (keylen > sizeof(tmpkey)) - continue; - memcpy(tmpkey, key, keylen); - memset(tmpkey + keylen, 0, sizeof(tmpkey) - keylen); - - //;VGM_LOG("BNSF: test key=%.24s\n", tmpkey); - set_key_g7221(data, tmpkey); - - score = test_key_g7221(data, start, sf); - if (score < 0) continue; - - if (best_score <= 0 || (score < best_score && score > 0)) { - best_score = score; - memcpy(best_key, key, keylen); - memset(best_key + keylen, 0, sizeof(tmpkey) - keylen); - } - - if (best_score == 1) { + test_key(sf, start, data, key, keylen, &best_score, best_key); + if (best_score == 1) break; - } - } VGM_ASSERT(best_score > 0, "BNSF: best key=%.24s (score=%i)\n", best_key, best_score); - VGM_ASSERT(best_score < 0, "BNSF: key not found\n"); /* defaults to all 0s */ + VGM_ASSERT(best_score < 0, "BNSF: key not found\n"); } + +#define BNSF_MIN_KEY_LEN 3 + +#ifdef BNSF_BRUTEFORCE +/* bruteforce keys in a string list extracted from executables or files near sound data, trying variations. */ +static void bruteforce_bnsf_key(STREAMFILE* sf, off_t start, g7221_codec_data* data, uint8_t* best_key) { + STREAMFILE* sf_keys = NULL; + int best_score = -1; + int i, j; + char line[1024]; + int bytes, line_ok; + off_t offset; + size_t keys_size; + + + VGM_LOG("BNSF: test keys\n"); + + sf_keys = open_streamfile_by_filename(sf, "keys.txt"); + if (!sf_keys) goto done; + + keys_size = get_streamfile_size(sf_keys); + + offset = 0x00; + while (offset < keys_size) { + int line_len; + + bytes = read_line(line, sizeof(line), offset, sf_keys, &line_ok); + if (!line_ok) break; + + offset += bytes; + + line_len = strlen(line); + for (i = 0; i < line_len - BNSF_MIN_KEY_LEN; i++) { + for (j = i + BNSF_MIN_KEY_LEN; j <= line_len; j++) { + int keylen = j - i; + const char* key = &line[i]; + + test_key(sf, start, data, key, keylen, &best_score, best_key); + if (best_score == 1) { + VGM_ASSERT(best_score > 0, "BNSF: good key=%.24s (score=%i)\n", best_key, best_score); + //goto done; + } + } + } + } + +//done: + VGM_ASSERT(best_score > 0, "BNSF: best key=%.24s (score=%i)\n", best_key, best_score); + VGM_ASSERT(best_score < 0, "BNSF: key not found\n"); + + close_streamfile(sf_keys); +} +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bnsf_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/bnsf_keys.h index 3e36b38a4..b415402c8 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/bnsf_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/bnsf_keys.h @@ -5,12 +5,18 @@ typedef struct { const char* key; } bnsfkey_info; -/* Known keys, extracted from games' exe */ +/* Known keys, extracted from games' exe/files */ static const bnsfkey_info s14key_list[] = { /* THE iDOLM@STER 2 (PS3/X360) */ {"haruka17imas"}, + /* Tales of Zestiria (PS3) */ + {"TO12_SPSLoc"}, + + /* Tales of Berseria (PS3) */ + {"SPSLOC13"}, + }; #endif/*_BNSF_KEYS_H_*/ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/hca.c b/Frameworks/vgmstream/vgmstream/src/meta/hca.c index 42c56ec5c..50080a538 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/hca.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/hca.c @@ -2,8 +2,14 @@ #include "hca_keys.h" #include "../coding/coding.h" +//#define HCA_BRUTEFORCE +#ifdef HCA_BRUTEFORCE +static void bruteforce_hca_key(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* out_keycode, uint16_t subkey); +#endif static void find_hca_key(hca_codec_data * hca_data, unsigned long long * out_keycode, uint16_t subkey); + +/* CRI HCA - streamed audio from CRI ADX2/Atom middleware */ VGMSTREAM * init_vgmstream_hca(STREAMFILE *streamFile) { return init_vgmstream_hca_subkey(streamFile, 0x0000); } @@ -41,6 +47,11 @@ VGMSTREAM * init_vgmstream_hca_subkey(STREAMFILE *streamFile, uint16_t subkey) { uint16_t file_sub = (uint16_t)get_16bitBE(keybuf+0x08); keycode = file_key * ( ((uint64_t)file_sub << 16u) | ((uint16_t)~file_sub + 2u) ); } +#ifdef HCA_BRUTEFORCE + else if (1) { + bruteforce_hca_key(streamFile, hca_data, &keycode, subkey); + } +#endif else { find_hca_key(hca_data, &keycode, subkey); } @@ -127,41 +138,94 @@ static inline void test_key(hca_codec_data * hca_data, uint64_t key, uint16_t su } } -/* Try to find the decryption key from a list. */ -static void find_hca_key(hca_codec_data * hca_data, unsigned long long * out_keycode, uint16_t subkey) { +/* try to find the decryption key from a list. */ +static void find_hca_key(hca_codec_data* hca_data, unsigned long long* out_keycode, uint16_t subkey) { const size_t keys_length = sizeof(hcakey_list) / sizeof(hcakey_info); int best_score = -1; int i,j; *out_keycode = 0xCC55463930DBE1AB; /* defaults to PSO2 key, most common */ - /* find a candidate key */ for (i = 0; i < keys_length; i++) { uint64_t key = hcakey_list[i].key; size_t subkeys_size = hcakey_list[i].subkeys_size; const uint16_t *subkeys = hcakey_list[i].subkeys; - /* try once with external subkey, if any */ test_key(hca_data, key, subkey, &best_score, out_keycode); - if (best_score == 1) /* best possible score */ + if (best_score == 1) goto done; - /* try subkey list */ if (subkeys_size > 0 && subkey == 0) { for (j = 0; j < subkeys_size; j++) { test_key(hca_data, key, subkeys[j], &best_score, out_keycode); - if (best_score == 1) /* best possible score */ + if (best_score == 1) goto done; } } } done: - //;VGM_LOG("HCA: best key=%08x%08x (score=%i)\n", - // (uint32_t)((*out_keycode >> 32) & 0xFFFFFFFF), (uint32_t)(*out_keycode & 0xFFFFFFFF), best_score); - VGM_ASSERT(best_score > 1, "HCA: best key=%08x%08x (score=%i)\n", (uint32_t)((*out_keycode >> 32) & 0xFFFFFFFF), (uint32_t)(*out_keycode & 0xFFFFFFFF), best_score); - VGM_ASSERT(best_score < 0, "HCA: key not found\n"); } + +#ifdef HCA_BRUTEFORCE +/* Bruteforce binary keys in executables and similar files, mainly for some mobile games. + * Kinda slow but acceptable for ~20MB exes, not very optimized. Unity usually has keys + * in plaintext (inside levelX or other base files) instead though. */ +static void bruteforce_hca_key(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* out_keycode, uint16_t subkey) { + STREAMFILE* sf_keys = NULL; + uint8_t* buf = NULL; + int best_score = -1; + off_t keys_size, bytes; + int i, pos; + + + VGM_LOG("HCA: test keys\n"); + + *out_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) goto done; + + keys_size = get_streamfile_size(sf_keys); + + buf = malloc(keys_size); + if (!buf) goto done; + + bytes = read_streamfile(buf, 0, keys_size, sf_keys); + if (bytes != keys_size) goto done; + + pos = 0; + while (pos < keys_size - 4) { + uint64_t key; + + /* keys are usually u32le lower, u32le upper (u64le) but other orders may exist */ + key = ((uint64_t)get_u32le(buf + pos + 0x00) << 0 ) | ((uint64_t)get_u32le(buf + pos + 0x04) << 32); + //key = ((uint64_t)get_u32le(buf + pos + 0x00) << 32) | ((uint64_t)get_u32le(buf + pos + 0x04) << 0); + //key = ((uint64_t)get_u32be(buf + pos + 0x00) << 0 ) | ((uint64_t)get_u32be(buf + pos + 0x04) << 32); + //key = ((uint64_t)get_u32be(buf + pos + 0x00) << 32) | ((uint64_t)get_u32be(buf + pos + 0x04) << 0); + if (key == 0) + continue; + + test_key(hca_data, keys[i], subkey, &best_score, out_keycode); + if (best_score == 1) + goto done; + + VGM_ASSERT(pos % 0x100000 == 0, "HCA: pos %x...\n", pos); + + /* observed files have aligned keys in the .text section, change if needed */ + pos += 0x04; //pos++; + } + +done: + VGM_ASSERT(best_score > 0, "HCA: best key=%08x%08x (score=%i)\n", + (uint32_t)((*out_keycode >> 32) & 0xFFFFFFFF), (uint32_t)(*out_keycode & 0xFFFFFFFF), best_score); + VGM_ASSERT(best_score < 0, "HCA: key not found\n"); + + close_streamfile(sf_keys); + free(buf); +} +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h index 3f3ff76ff..e77da08e2 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h @@ -330,6 +330,9 @@ static const hcakey_info hcakey_list[] = { /* Inazuma Eleven SD (Android) */ {0xC436E03737D55B5F}, // C436E03737D55B5F / 14138734607940803423 + /* Detective Conan Runner / Case Closed Runner (Android) */ + {1175268187653273344}, // 104f643098e3f700 + /* Dragalia Lost (iOS/Android) */ {2967411924141, subkeys_dgl, sizeof(subkeys_dgl) / sizeof(subkeys_dgl[0]) }, // 000002B2E7889CAD diff --git a/Frameworks/vgmstream/vgmstream/src/meta/lrmd.c b/Frameworks/vgmstream/vgmstream/src/meta/lrmd.c index e53e9bfed..4792884ec 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/lrmd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/lrmd.c @@ -7,7 +7,7 @@ VGMSTREAM * init_vgmstream_lrmd(STREAMFILE *sf) { VGMSTREAM * vgmstream = NULL; STREAMFILE * sf_h = NULL, *temp_sf = NULL; off_t stream_offset, section1_offset, section2_offset, basename_offset, subname_offset; - size_t stream_size, layer_chunk; + size_t stream_size, max_chunk, block_size = 0, chunk_start, chunk_size; int loop_flag, channel_count, sample_rate, layers; int32_t num_samples, loop_start, loop_end; int total_subsongs, target_subsong = sf->stream_index; @@ -37,14 +37,14 @@ VGMSTREAM * init_vgmstream_lrmd(STREAMFILE *sf) { VGM_LOG("LRMD: unknown value\n"); goto fail; } - layer_chunk = read_u16le(0x2a, sf_h); + max_chunk = read_u16le(0x2a, sf_h); num_samples = read_u32le(0x2c, sf_h); /* 0x30: null? */ /* 0x34: data size for all layers */ layers = read_u32le(0x38, sf_h); section1_offset = read_u32le(0x3c, sf_h); - /* 0x40: seek/layer? table entries */ - /* 0x44: seek/layer? table offset */ + /* 0x40: lip table entries */ + /* 0x44: lip table offset */ /* 0x48: section2 flag */ section2_offset = read_u32le(0x4c, sf_h); /* 0x40: section3 flag */ @@ -54,19 +54,42 @@ VGMSTREAM * init_vgmstream_lrmd(STREAMFILE *sf) { if (target_subsong == 0) target_subsong = 1; if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; - /* data is divided into N interleaved layers sharing config, so it could be implemented as - * layered, but since they have names it's worth showing as subsongs */ + /* data is divided into N interleaved layers sharing config (channels may vary), and + * since they have names it's worth showing as subsongs */ /* section1: layer config */ - section1_offset += (target_subsong - 1) * 0x18; - /* 0x00: null */ - subname_offset = read_u32le(section1_offset + 0x04, sf_h); - /* 0x08: unk */ - /* 0x0c: flags? */ - /* 0x10: null? */ - /* 0x14: null? */ - sample_rate = 44100; - channel_count = 2; + { + int i; + int frame_size = max_chunk / layers / 2; /* even for songs with mono layers */ + + chunk_size = 0; + for (i = 0; i < layers; i++) { + off_t header_offset = section1_offset + i * 0x18; + int layer_channels; + + /* not too sure but needed for LR2's muihouse last 3 layers */ + layer_channels = read_u8(header_offset + 0x0d, sf_h) != 0 ? 1 : 2; + + if (i + 1 == target_subsong) { + /* 0x00: null */ + subname_offset = read_u32le(header_offset + 0x04, sf_h); + /* 0x08: unk */ + /* 0x0c: flags? */ + /* 0x10: null? */ + /* 0x14: null? */ + + chunk_start = chunk_size; + block_size = frame_size * layer_channels; + + channel_count = layer_channels; + sample_rate = 44100; + } + + chunk_size += frame_size * layer_channels; + } + if (block_size == 0) + goto fail; + } /* section2: loops */ /* 0x00: offset to "loop" name */ @@ -82,9 +105,8 @@ VGMSTREAM * init_vgmstream_lrmd(STREAMFILE *sf) { } - //TODO: LR2's muihouse has buggy 7-layer interleave /* data de-interleave */ - temp_sf = setup_lrmd_streamfile(sf, layer_chunk / layers, (target_subsong-1), total_subsongs); + temp_sf = setup_lrmd_streamfile(sf, block_size, chunk_start, chunk_size); if (!temp_sf) goto fail; stream_offset = 0x00; @@ -105,13 +127,11 @@ VGMSTREAM * init_vgmstream_lrmd(STREAMFILE *sf) { #ifdef VGM_USE_FFMPEG { - int block_align, encoder_delay; + int encoder_delay = 1024; /* assumed */ - block_align = layer_chunk / layers; - encoder_delay = 1024; /* assumed */ vgmstream->num_samples -= encoder_delay; - vgmstream->codec_data = init_ffmpeg_atrac3_raw(temp_sf, stream_offset, stream_size, vgmstream->num_samples, vgmstream->channels, vgmstream->sample_rate, block_align, encoder_delay); + vgmstream->codec_data = init_ffmpeg_atrac3_raw(temp_sf, stream_offset, stream_size, vgmstream->num_samples, vgmstream->channels, vgmstream->sample_rate, block_size, encoder_delay); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/lrmd_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/lrmd_streamfile.h index f46ce4c46..b5fcd4d45 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/lrmd_streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/lrmd_streamfile.h @@ -2,14 +2,21 @@ #define _LRMD_STREAMFILE_H_ #include "deblock_streamfile.h" +static void block_callback(STREAMFILE *sf, deblock_io_data *data) { + data->data_size = data->cfg.frame_size; + data->skip_size = data->cfg.skip_size; + data->block_size = data->cfg.chunk_size; +} + /* Deinterleaves LRMD streams */ -static STREAMFILE* setup_lrmd_streamfile(STREAMFILE *sf, size_t interleave_size, int stream_number, int stream_count) { +static STREAMFILE* setup_lrmd_streamfile(STREAMFILE *sf, size_t block_size, size_t chunk_start, size_t chunk_size) { STREAMFILE *new_sf = NULL; deblock_config_t cfg = {0}; - cfg.chunk_size = interleave_size; - cfg.step_start = stream_number; - cfg.step_count = stream_count; + cfg.frame_size = block_size; + cfg.chunk_size = chunk_size; + cfg.skip_size = chunk_start; + cfg.block_callback = block_callback; new_sf = open_wrap_streamfile(sf); new_sf = open_io_deblock_streamfile_f(new_sf, &cfg); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/meta.h b/Frameworks/vgmstream/vgmstream/src/meta/meta.h index a78e2feda..39ab32c6d 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/meta.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/meta.h @@ -646,7 +646,7 @@ VGMSTREAM * init_vgmstream_opus_prototype(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_opus_opusnx(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_opus_sqex(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_pc_al2(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_raw_al(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_pc_ast(STREAMFILE * streamFile); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/nub.c b/Frameworks/vgmstream/vgmstream/src/meta/nub.c index 04a7f651e..136c9b9fd 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/nub.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/nub.c @@ -19,7 +19,9 @@ VGMSTREAM * init_vgmstream_nub(STREAMFILE *streamFile) { /* checks */ - if (!check_extensions(streamFile, "nub")) + /* .nub: standard + * .nub2: rare [iDOLM@STER - Gravure For You (PS3)] */ + if (!check_extensions(streamFile, "nub,nub2")) goto fail; version = read_32bitBE(0x00,streamFile); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_svag.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_svag.c index beae5882e..4b9bc17d2 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_svag.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_svag.c @@ -1,7 +1,7 @@ #include "meta.h" #include "../coding/coding.h" -/* SVAG - from Konami Tokyo games [OZ (PS2), Neo Contra (PS2)]] */ +/* SVAG - from Konami Tokyo games [OZ (PS2), Neo Contra (PS2), Silent Hill 2 (PS2)] */ VGMSTREAM * init_vgmstream_ps2_svag(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset; @@ -12,14 +12,17 @@ VGMSTREAM * init_vgmstream_ps2_svag(STREAMFILE *streamFile) { /* checks */ if (!check_extensions(streamFile, "svag")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x53766167) /* "Svag" */ goto fail; channel_count = read_16bitLE(0x0C,streamFile); /* always 2? ("S"tereo vag?) */ loop_flag = (read_32bitLE(0x14,streamFile)==1); - start_offset = 0x800; /* header repeated at 0x400 too */ + /* header repeated at 0x400 presumably for stereo */ + if (channel_count > 1 && read_32bitBE(0x400,streamFile) != 0x53766167) /* "Svag" */ + goto fail; + + start_offset = 0x800; data_size = read_32bitLE(0x04,streamFile); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/pc_al2.c b/Frameworks/vgmstream/vgmstream/src/meta/raw_al.c similarity index 67% rename from Frameworks/vgmstream/vgmstream/src/meta/pc_al2.c rename to Frameworks/vgmstream/vgmstream/src/meta/raw_al.c index 33f01c9d9..717940df1 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/pc_al2.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/raw_al.c @@ -1,40 +1,42 @@ -#include "meta.h" -#include "../coding/coding.h" - -/* AL" - headerless a-law, found in Conquest of Elysium 3 (PC) */ -VGMSTREAM * init_vgmstream_pc_al2(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - int loop_flag = 0, channel_count; - - - if ( !check_extensions(streamFile,"al2")) - goto fail; - - channel_count = 2; - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - vgmstream->sample_rate = 22050; - vgmstream->coding_type = coding_ALAW; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x01; - vgmstream->meta_type = meta_PC_AL2; - vgmstream->num_samples = pcm_bytes_to_samples(get_streamfile_size(streamFile), channel_count, 8); - if (loop_flag) { - vgmstream->loop_start_sample = 0; - vgmstream->loop_end_sample = vgmstream->num_samples; - } - - start_offset = 0; - - if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) - goto fail; - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} +#include "meta.h" +#include "../coding/coding.h" + +/* AL/AL2 - headerless a-law, from Illwinter Game Design games */ +VGMSTREAM * init_vgmstream_raw_al(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag = 0, channel_count; + + /* checks */ + /* .al: Dominions 3 - The Awakening (PC) + * .al2: Conquest of Elysium 3 (PC) */ + if ( !check_extensions(streamFile,"al,al2")) + goto fail; + + channel_count = check_extensions(streamFile,"al") ? 1 : 2; + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = 22050; + vgmstream->coding_type = coding_ALAW; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x01; + vgmstream->meta_type = meta_RAW_AL; + vgmstream->num_samples = pcm_bytes_to_samples(get_streamfile_size(streamFile), channel_count, 8); + if (loop_flag) { + vgmstream->loop_start_sample = 0; + vgmstream->loop_end_sample = vgmstream->num_samples; + } + + start_offset = 0; + + if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/rwsd.c b/Frameworks/vgmstream/vgmstream/src/meta/rwsd.c index 7c7b29b0c..673143fe8 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/rwsd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/rwsd.c @@ -47,7 +47,8 @@ static void read_rwav(struct rwav_data * rd) /* little endian, version 2 */ if ((uint32_t)read_32bitBE(rd->offset+4,rd->streamFile)!=0xFFFE4000 || ( - (uint32_t)read_32bitBE(rd->offset+8,rd->streamFile)!=0x00000102 && + (uint32_t)read_32bitBE(rd->offset+8,rd->streamFile)!=0x00000002 && /* Kirby's Adventure */ + (uint32_t)read_32bitBE(rd->offset+8,rd->streamFile)!=0x00000102 && /* common */ (uint32_t)read_32bitBE(rd->offset+8,rd->streamFile)!=0x00010102 ) ) @@ -98,7 +99,7 @@ VGMSTREAM * init_vgmstream_rwsd(STREAMFILE *streamFile) { coding_t coding_type; size_t wave_length; - int codec_number; + int codec; int channel_count; int loop_flag; int rwar = 0; @@ -111,8 +112,6 @@ VGMSTREAM * init_vgmstream_rwsd(STREAMFILE *streamFile) { int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; - const char *ext; - rwav_data.version = -1; rwav_data.start_offset = 0; rwav_data.info_chunk = -1; @@ -121,58 +120,39 @@ VGMSTREAM * init_vgmstream_rwsd(STREAMFILE *streamFile) { /* check extension, case insensitive */ streamFile->get_name(streamFile,filename,sizeof(filename)); - ext = filename_extension(filename); - - if (strcasecmp("rwsd",ext)) - { - if (strcasecmp("rwar",ext)) - { - if (strcasecmp("rwav",ext)) - { - /* .bcwav: standard - * .bms: ? - * .sfx: Wizdom (3DS) - * .str: Pac-Man and the Ghostly Adventures 2 (3DS) - * .zic: Wizdom (3DS) */ - if (check_extensions(streamFile, "bcwav,bms,sfx,str,zic")) { - rwav = 1; // cwav, similar to little endian rwav - big_endian = 0; - } - else { - goto fail; - } - } - else - { - // matched rwav - rwav = 1; - } - } - else - { - // matched rwar - rwar = 1; - } + if (check_extensions(streamFile, "rwsd")) { + ; } - else - { - // match rwsd + else if (check_extensions(streamFile, "rwar")) { + rwar = 1; + } + else if (check_extensions(streamFile, "rwav")) { + rwav = 1; + } + /* .bcwav: standard 3DS + * .bms: 3D Classics Kirby's Adventure (3DS) + * .sfx: Wizdom (3DS) + * .str: Pac-Man and the Ghostly Adventures 2 (3DS) + * .zic: Wizdom (3DS) */ + else if (check_extensions(streamFile, "bcwav,bms,sfx,str,zic")) { + rwav = 1; // cwav, similar to little endian rwav + big_endian = 0; + } + else { + goto fail; } - if (big_endian) - { + if (big_endian) { read_16bit = read_16bitBE; read_32bit = read_32bitBE; } - else - { + else { read_16bit = read_16bitLE; read_32bit = read_32bitLE; } /* check header */ - if (rwar || rwav) - { + if (rwar || rwav) { rwav_data.offset = 0; rwav_data.streamFile = streamFile; rwav_data.big_endian = big_endian; @@ -182,13 +162,11 @@ VGMSTREAM * init_vgmstream_rwsd(STREAMFILE *streamFile) { if (rwav) read_rwav(&rwav_data); if (rwav_data.wave_offset < 0) goto fail; } - else - { + else { if ((uint32_t)read_32bitBE(0,streamFile)!=0x52575344) /* "RWSD" */ goto fail; - switch (read_32bitBE(4,streamFile)) - { + switch (read_32bitBE(4,streamFile)) { case 0xFEFF0102: /* ideally we would look through the chunk list for a WAVE chunk, * but it's always in the same order */ @@ -226,14 +204,14 @@ VGMSTREAM * init_vgmstream_rwsd(STREAMFILE *streamFile) { } /* get type details */ - codec_number = read_8bit(rwav_data.wave_offset+0x10,streamFile); + codec = read_8bit(rwav_data.wave_offset+0x10,streamFile); loop_flag = read_8bit(rwav_data.wave_offset+0x11,streamFile); if (big_endian) channel_count = read_8bit(rwav_data.wave_offset+0x12,streamFile); else channel_count = read_32bit(rwav_data.wave_offset+0x24,streamFile); - switch (codec_number) { + switch (codec) { case 0: coding_type = coding_PCM8; break; @@ -256,18 +234,14 @@ VGMSTREAM * init_vgmstream_rwsd(STREAMFILE *streamFile) { if (channel_count < 1) goto fail; /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - if (big_endian) - { + if (big_endian) { vgmstream->num_samples = dsp_nibbles_to_samples(read_32bit(rwav_data.wave_offset+0x1c,streamFile)); vgmstream->loop_start_sample = dsp_nibbles_to_samples(read_32bit(rwav_data.wave_offset+0x18,streamFile)); } - else - { + else { vgmstream->num_samples = read_32bit(rwav_data.wave_offset+0x1c,streamFile); vgmstream->loop_start_sample = read_32bit(rwav_data.wave_offset+0x18,streamFile); } @@ -280,8 +254,7 @@ VGMSTREAM * init_vgmstream_rwsd(STREAMFILE *streamFile) { if (rwar) vgmstream->meta_type = meta_RWAR; - else if (rwav) - { + else if (rwav) { if (big_endian) { vgmstream->meta_type = meta_RWAV; } @@ -355,12 +328,10 @@ VGMSTREAM * init_vgmstream_rwsd(STREAMFILE *streamFile) { } } - if (rwar || rwav) - { + if (rwar || rwav) { /* */ } - else - { + else { if (rwav_data.version == 2) rwav_data.start_offset = read_32bit(8,streamFile); } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c b/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c index b37680a5a..ab86c0a1e 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c @@ -10,7 +10,7 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { size_t stream_size; int is_sgx, is_sgb = 0; - int loop_flag, channels, type; + int loop_flag, channels, codec; int sample_rate, num_samples, loop_start_sample, loop_end_sample; int total_subsongs, target_subsong = streamFile->stream_index; @@ -69,7 +69,7 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { /* 0x00 ? (00/01/02) */ if (!is_sgx) /* meaning unknown in .sgx; offset 0 = not a stream (a RGND sample) */ name_offset = read_32bitLE(chunk_offset+0x04,streamHeader); - type = read_8bit(chunk_offset+0x08,streamHeader); + codec = read_8bit(chunk_offset+0x08,streamHeader); channels = read_8bit(chunk_offset+0x09,streamHeader); /* 0x0a null */ sample_rate = read_32bitLE(chunk_offset+0x0c,streamHeader); @@ -111,7 +111,14 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { if (name_offset) read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamHeader); - switch (type) { + switch (codec) { + + case 0x01: /* PCM [LocoRoco Cocoreccho! (PS3)] (rare, locoloco_psn#279) */ + vgmstream->coding_type = coding_PCM16BE; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x02; + break; + #ifdef VGM_USE_VORBIS case 0x02: /* Ogg Vorbis [Ni no Kuni: Wrath of the White Witch Remastered (PC)] (codec hijack?) */ vgmstream->codec_data = init_ogg_vorbis(streamFile, start_offset, stream_size, NULL); @@ -129,6 +136,9 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { vgmstream->interleave_block_size = stream_size; } + /* a few files in LocoRoco set 0 stream size/samples, use an empty file for now */ + if (vgmstream->num_samples == 0) + vgmstream->num_samples = 28; break; #ifdef VGM_USE_FFMPEG @@ -173,6 +183,7 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { #endif default: + VGM_LOG("SGDX: unknown codec %i\n", codec); goto fail; } diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.c b/Frameworks/vgmstream/vgmstream/src/vgmstream.c index 9d54ec843..e8634ccff 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.c +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.c @@ -346,7 +346,6 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_opus_nus3, init_vgmstream_opus_sps_n1, init_vgmstream_opus_nxa, - init_vgmstream_pc_al2, init_vgmstream_pc_ast, init_vgmstream_naac, init_vgmstream_ubi_sb, @@ -500,6 +499,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_raw_wavm, /* .wavm raw xbox */ init_vgmstream_raw_pcm, /* .raw raw PCM */ init_vgmstream_s14_sss, /* .s14/sss raw siren14 */ + init_vgmstream_raw_al, /* .al/al2 raw A-LAW */ #ifdef VGM_USE_FFMPEG init_vgmstream_ffmpeg, /* may play anything incorrectly, since FFmpeg doesn't check extensions */ #endif @@ -2496,7 +2496,8 @@ static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *strea {".V0",".V1"}, /* Homura (PS2) */ {".L",".R"}, /* Crash Nitro Racing (PS2), Gradius V (PS2) */ {"_0.dsp","_1.dsp"}, /* Wario World (GC) */ - {".adpcm","_NxEncoderOut_.adpcm"}, /* Kill la Kill: IF (Switch) */ //todo can't match R>L + {".adpcm","_NxEncoderOut_.adpcm"}, /* Kill la Kill: IF (Switch) */ + {".adpcm","_2.adpcm"}, /* Desire: Remaster Version (Switch) */ }; char new_filename[PATH_LIMIT]; char * extension; @@ -2541,31 +2542,38 @@ static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *strea if (filename_len > this_suffix_len && strchr(this_suffix, '.') != NULL) { /* same suffix with extension */ //;VGM_LOG("DFS: suf+ext %s vs %s len %i\n", new_filename, this_suffix, this_suffix_len); if (memcmp(new_filename + (filename_len - this_suffix_len), this_suffix, this_suffix_len) == 0) { - dfs_pair = j; memcpy (new_filename + (filename_len - this_suffix_len), that_suffix,that_suffix_len+1); + dfs_pair = j; } } else if (filename_len - extension_len > this_suffix_len) { /* same suffix without extension */ //;VGM_LOG("DFS: suf-ext %s vs %s len %i\n", extension - this_suffix_len, this_suffix, this_suffix_len); if (memcmp(extension - this_suffix_len, this_suffix,this_suffix_len) == 0) { - dfs_pair = j; memmove(extension + that_suffix_len - this_suffix_len, extension,extension_len+1); /* move old extension to end */ memcpy (extension - this_suffix_len, that_suffix,that_suffix_len); /* overwrite with new suffix */ + dfs_pair = j; + } + } + + if (dfs_pair != -1) { + //VGM_LOG("DFS: try %i: %s\n", dfs_pair, new_filename); + /* try to init other channel (new_filename now has the opposite name) */ + dual_streamFile = open_streamfile(streamFile, new_filename); + if (!dual_streamFile) { + /* restore filename and keep trying (if found it'll break and init) */ + dfs_pair = -1; + get_streamfile_name(streamFile, new_filename, sizeof(new_filename)); } } } } - /* see if the filename had a suitable L/R-pair name */ + /* filename didn't have a suitable L/R-pair name */ if (dfs_pair == -1) goto fail; //;VGM_LOG("DFS: match %i filename=%s\n", dfs_pair, new_filename); - /* try to init other channel (new_filename now has the opposite name) */ - dual_streamFile = open_streamfile(streamFile, new_filename); - if (!dual_streamFile) goto fail; - - new_vgmstream = init_vgmstream_function(dual_streamFile); /* use the init that just worked, no other should work */ + new_vgmstream = init_vgmstream_function(dual_streamFile); /* use the init function that just worked */ close_streamfile(dual_streamFile); /* see if we were able to open the file, and if everything matched nicely */ diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.h b/Frameworks/vgmstream/vgmstream/src/vgmstream.h index 36ed47d03..2a24b25b7 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.h +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.h @@ -621,7 +621,7 @@ typedef enum { meta_EA_SNU, /* Electronic Arts SNU (Dead Space) */ meta_AWC, /* Rockstar AWC (GTA5, RDR) */ meta_OPUS, /* Nintendo Opus [Lego City Undercover (Switch)] */ - meta_PC_AL2, /* Conquest of Elysium 3 (PC) */ + meta_RAW_AL, meta_PC_AST, /* Dead Rising (PC) */ meta_NAAC, /* Namco AAC (3DS) */ meta_UBI_SB, /* Ubisoft banks */