Updated VGMStream to r1050-2812-g24e9d177

CQTexperiment
Christopher Snowhill 2020-02-23 00:17:33 -08:00
parent fbe9283835
commit 004c53962a
20 changed files with 400 additions and 211 deletions

View File

@ -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 = "<group>"; };
831BA6221EAC61CB00CF89B0 /* coding_utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = coding_utils.c; sourceTree = "<group>"; };
831BD11F1EEE1CF200198540 /* ngc_ulw.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = ngc_ulw.c; sourceTree = "<group>"; };
8322ECE6240268BA009E9429 /* raw_al.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = raw_al.c; sourceTree = "<group>"; };
832389481D22419B00482226 /* clHCA.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = clHCA.c; sourceTree = "<group>"; };
832389491D22419B00482226 /* clHCA.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = clHCA.h; sourceTree = "<group>"; };
8323894F1D2246C300482226 /* hca.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hca.c; sourceTree = "<group>"; };
@ -805,7 +806,6 @@
832BF81921E0514A006F50F1 /* xopus.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xopus.c; sourceTree = "<group>"; };
832BF81A21E0514A006F50F1 /* hca_keys_awb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hca_keys_awb.h; sourceTree = "<group>"; };
832BF81B21E0514B006F50F1 /* nus3audio.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nus3audio.c; sourceTree = "<group>"; };
83345A4C1F8AEB2700B2EAA4 /* pc_al2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pc_al2.c; sourceTree = "<group>"; };
83345A4E1F8AEB2800B2EAA4 /* xvag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xvag.c; sourceTree = "<group>"; };
833A7A2D1ED11961003EC53E /* xau.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xau.c; sourceTree = "<group>"; };
8342469020C4D22F00926E48 /* h4m.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = h4m.c; sourceTree = "<group>"; };
@ -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 */,

View File

@ -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];

View File

@ -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];
} ;

View File

@ -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 */

View File

@ -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";

View File

@ -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]);

View File

@ -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

View File

@ -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_*/

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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 */

View File

@ -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 */