Merge pull request #561 from bnnm/bnsf-lrmd-sgdx

bnsf lrmd sgdx
This commit is contained in:
bnnm 2020-02-22 20:47:11 +01:00 committed by GitHub
commit 24e9d17735
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 408 additions and 210 deletions

View File

@ -68,6 +68,15 @@ There are multiple options that alter how the file is converted, for example:
Available commands are printed when run with no flags. Note that you can also Available commands are printed when run with no flags. Note that you can also
achieve similar results for other plugins using TXTP, described later. achieve similar results for other plugins using TXTP, described later.
With files multiple subsongs you need to specify manually subsong (by design, to avoid
massive data dumps since some formats have hundred of subsongs), but you could do
some command line tricks:
```
REM extracts from subsong 5 to 10 in file.fsb
for /L %A in (5,1,10) do test.exe -s %A -o file_%A.wav file.fsb
```
### in_vgmstream ### in_vgmstream
*Installation*: drop the ```in_vgmstream.dll``` in your Winamp plugins directory, *Installation*: drop the ```in_vgmstream.dll``` in your Winamp plugins directory,
and follow the above instructions for installing the other files needed. and follow the above instructions for installing the other files needed.

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), /* 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). * 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 */ * whatever, we'll just kill and reconstruct FFmpeg's config every time */
;VGM_LOG("1\n");
data->force_seek = 1; data->force_seek = 1;
reset_ffmpeg_internal(data); /* reset state from trying to seek */ reset_ffmpeg_internal(data); /* reset state from trying to seek */
//stream = data->formatCtx->streams[data->streamIndex]; //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..., ... /* 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?) */ * layout is: Td0a Td1a Td2a Td3a, Td0b Td0b Td1b Td2b, ... (better for CPU cache?) */
uint32_t tds[256*4]; uint32_t tds[256*4];
/* expanded roundkey, actual final key */ /* expanded roundkey, actual final key (192-bit keys only need up to 52 though) */
uint32_t rk[52]; 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 */ /* 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]; uint32_t data_u32[0x78/4 + 2];
int bitpos, expected_frame_size; int bitpos, expected_frame_size;
int power_categories[NUMBER_OF_REGIONS]; 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) */ /* 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 bits_left = 8 * expected_frame_size - bitpos;
int i; int i, endpos, test_bits;
if (bits_left > 0) { if (bits_left > 0) {
/* frame must be padded with 1s */ /* frame must be padded with 1s */
endpos = bitpos;
for (i = 0; i < bits_left; i++) { for (i = 0; i < bits_left; i++) {
int bit = (data_u32[bitpos >> 5] >> (31 - (bitpos & 0x1F))) & 1; int bit = (data_u32[endpos >> 5] >> (31 - (endpos & 0x1F))) & 1;
bitpos++; endpos++;
if (bit == 0) if (bit == 0)
return -1; 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 { else {
/* ? */ /* ? */
@ -1145,10 +1164,11 @@ fail:
int g7221_decode_frame(g7221_handle* handle, uint8_t* data, int16_t* out_samples) { int g7221_decode_frame(g7221_handle* handle, uint8_t* data, int16_t* out_samples) {
int res; int res;
int mag_shift; int mag_shift;
int encrypted = handle->aes != NULL;
/* first 0x10 bytes may be encrypted with AES. Original code also saves encrypted bytes, /* first 0x10 bytes may be encrypted with AES. Original code also saves encrypted bytes,
* then re-crypts after unpacking, presumably to guard against memdumps. */ * then re-crypts after unpacking, presumably to guard against memdumps. */
if (handle->aes != NULL) { if (encrypted) {
s14aes_decrypt(handle->aes, data); 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 */ * so we could avoid one extra buffer, but for clarity we'll leave as is */
/* unpack data into MLT spectrum coefs */ /* 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; if (res < 0) goto fail;
/* convert coefs to samples using reverse (inverse) MLT */ /* convert coefs to samples using reverse (inverse) MLT */

View File

@ -62,6 +62,7 @@ static const char* extension_list[] = {
"aiffl", //fake extension for .aif??? "aiffl", //fake extension for .aif???
"aix", "aix",
"akb", "akb",
"al",
"al2", "al2",
"amts", //fake extension/header id for .stm (renamed? to be removed?) "amts", //fake extension/header id for .stm (renamed? to be removed?)
"ao", "ao",
@ -1156,7 +1157,7 @@ static const meta_info meta_info_list[] = {
{meta_EA_SNU, "Electronic Arts SNU header"}, {meta_EA_SNU, "Electronic Arts SNU header"},
{meta_AWC, "Rockstar AWC header"}, {meta_AWC, "Rockstar AWC header"},
{meta_OPUS, "Nintendo Switch OPUS 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_PC_AST, "Capcom AST (PC) header"},
{meta_UBI_SB, "Ubisoft SBx header"}, {meta_UBI_SB, "Ubisoft SBx header"},
{meta_NAAC, "Namco NAAC 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; int i, list_length;
const char *description; const char *description;
/* we need to recurse down because of FFmpeg */ #ifdef VGM_USE_FFMPEG
if (vgmstream->layout_type == layout_layered) { if (vgmstream->coding_type == coding_FFmpeg) {
layered_layout_data* layout_data = vgmstream->layout_data; /* recurse down for FFmpeg, but metas should set prefered/main codec, or maybe print a list of codecs */
get_vgmstream_coding_description(layout_data->layers[0], out, out_size); if (vgmstream->layout_type == layout_layered) {
return; layered_layout_data* layout_data = vgmstream->layout_data;
} else if (vgmstream->layout_type == layout_segmented) { get_vgmstream_coding_description(layout_data->layers[0], out, out_size);
segmented_layout_data* layout_data = vgmstream->layout_data; return;
get_vgmstream_coding_description(layout_data->segments[0], out, out_size); } else if (vgmstream->layout_type == layout_segmented) {
return; segmented_layout_data* layout_data = vgmstream->layout_data;
get_vgmstream_coding_description(layout_data->segments[0], out, out_size);
return;
}
} }
#endif
description = "CANNOT DECODE"; description = "CANNOT DECODE";

View File

@ -1067,7 +1067,7 @@
> >
</File> </File>
<File <File
RelativePath=".\meta\pc_al2.c" RelativePath=".\meta\raw_al.c"
> >
</File> </File>
<File <File

View File

@ -379,7 +379,7 @@
<ClCompile Include="meta\opus_ppp.c" /> <ClCompile Include="meta\opus_ppp.c" />
<ClCompile Include="meta\otm.c" /> <ClCompile Include="meta\otm.c" />
<ClCompile Include="meta\p3d.c" /> <ClCompile Include="meta\p3d.c" />
<ClCompile Include="meta\pc_al2.c" /> <ClCompile Include="meta\raw_al.c" />
<ClCompile Include="meta\pc_mxst.c" /> <ClCompile Include="meta\pc_mxst.c" />
<ClCompile Include="meta\sab.c" /> <ClCompile Include="meta\sab.c" />
<ClCompile Include="meta\xa_xa30.c" /> <ClCompile Include="meta\xa_xa30.c" />

View File

@ -670,7 +670,7 @@
<ClCompile Include="meta\p3d.c"> <ClCompile Include="meta\p3d.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="meta\pc_al2.c"> <ClCompile Include="meta\raw_al.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="meta\pc_mxst.c"> <ClCompile Include="meta\pc_mxst.c">

View File

@ -231,6 +231,9 @@ static const adxkey_info adxkey9_list[] = {
/* Nogizaka46 Rhythm Festival (Android) */ /* Nogizaka46 Rhythm Festival (Android) */
{0x2378,0x5511,0x0201, NULL,5613126134333697}, // 0013F11BC5510101 {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]); static const int adxkey8_list_count = sizeof(adxkey8_list) / sizeof(adxkey8_list[0]);

View File

@ -4,7 +4,12 @@
#include "bnsf_keys.h" #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)] */ /* BNSF - Bandai Namco Sound Format/File [Tales of Graces (Wii), Tales of Berseria (PS4)] */
VGMSTREAM * init_vgmstream_bnsf(STREAMFILE *streamFile) { 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 */ uint8_t key[24] = {0}; /* keystring 0-padded to 192-bit */
keysize = read_key_file(key, sizeof(key), streamFile); 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)) { 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); set_key_g7221(vgmstream->codec_data, key);
} }
@ -117,40 +126,96 @@ fail:
return NULL; return NULL;
} }
static void find_bnsf_key(g7221_codec_data* data, off_t start, STREAMFILE* sf, uint8_t* best_key) { 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) {
const size_t keys_length = sizeof(s14key_list) / sizeof(bnsfkey_info);
int score, best_score = -1;
int i;
uint8_t tmpkey[24]; 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++) { for (i = 0; i < keys_length; i++) {
const char* key = s14key_list[i].key; const char* key = s14key_list[i].key;
int keylen = strlen(key); int keylen = strlen(key);
if (keylen > sizeof(tmpkey)) test_key(sf, start, data, key, keylen, &best_score, best_key);
continue; if (best_score == 1)
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) {
break; break;
}
} }
VGM_ASSERT(best_score > 0, "BNSF: best key=%.24s (score=%i)\n", best_key, best_score); 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; const char* key;
} bnsfkey_info; } bnsfkey_info;
/* Known keys, extracted from games' exe */ /* Known keys, extracted from games' exe/files */
static const bnsfkey_info s14key_list[] = { static const bnsfkey_info s14key_list[] = {
/* THE iDOLM@STER 2 (PS3/X360) */ /* THE iDOLM@STER 2 (PS3/X360) */
{"haruka17imas"}, {"haruka17imas"},
/* Tales of Zestiria (PS3) */
{"TO12_SPSLoc"},
/* Tales of Berseria (PS3) */
{"SPSLOC13"},
}; };
#endif/*_BNSF_KEYS_H_*/ #endif/*_BNSF_KEYS_H_*/

View File

@ -2,8 +2,14 @@
#include "hca_keys.h" #include "hca_keys.h"
#include "../coding/coding.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); 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) { VGMSTREAM * init_vgmstream_hca(STREAMFILE *streamFile) {
return init_vgmstream_hca_subkey(streamFile, 0x0000); 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); uint16_t file_sub = (uint16_t)get_16bitBE(keybuf+0x08);
keycode = file_key * ( ((uint64_t)file_sub << 16u) | ((uint16_t)~file_sub + 2u) ); 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 { else {
find_hca_key(hca_data, &keycode, subkey); 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. */ /* 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) { 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); const size_t keys_length = sizeof(hcakey_list) / sizeof(hcakey_info);
int best_score = -1; int best_score = -1;
int i,j; int i,j;
*out_keycode = 0xCC55463930DBE1AB; /* defaults to PSO2 key, most common */ *out_keycode = 0xCC55463930DBE1AB; /* defaults to PSO2 key, most common */
/* find a candidate key */
for (i = 0; i < keys_length; i++) { for (i = 0; i < keys_length; i++) {
uint64_t key = hcakey_list[i].key; uint64_t key = hcakey_list[i].key;
size_t subkeys_size = hcakey_list[i].subkeys_size; size_t subkeys_size = hcakey_list[i].subkeys_size;
const uint16_t *subkeys = hcakey_list[i].subkeys; 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); test_key(hca_data, key, subkey, &best_score, out_keycode);
if (best_score == 1) /* best possible score */ if (best_score == 1)
goto done; goto done;
/* try subkey list */
if (subkeys_size > 0 && subkey == 0) { if (subkeys_size > 0 && subkey == 0) {
for (j = 0; j < subkeys_size; j++) { for (j = 0; j < subkeys_size; j++) {
test_key(hca_data, key, subkeys[j], &best_score, out_keycode); test_key(hca_data, key, subkeys[j], &best_score, out_keycode);
if (best_score == 1) /* best possible score */ if (best_score == 1)
goto done; goto done;
} }
} }
} }
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", 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); (uint32_t)((*out_keycode >> 32) & 0xFFFFFFFF), (uint32_t)(*out_keycode & 0xFFFFFFFF), best_score);
VGM_ASSERT(best_score < 0, "HCA: key not found\n"); 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) */ /* Inazuma Eleven SD (Android) */
{0xC436E03737D55B5F}, // C436E03737D55B5F / 14138734607940803423 {0xC436E03737D55B5F}, // C436E03737D55B5F / 14138734607940803423
/* Detective Conan Runner / Case Closed Runner (Android) */
{1175268187653273344}, // 104f643098e3f700
/* Dragalia Lost (iOS/Android) */ /* Dragalia Lost (iOS/Android) */
{2967411924141, subkeys_dgl, sizeof(subkeys_dgl) / sizeof(subkeys_dgl[0]) }, // 000002B2E7889CAD {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; VGMSTREAM * vgmstream = NULL;
STREAMFILE * sf_h = NULL, *temp_sf = NULL; STREAMFILE * sf_h = NULL, *temp_sf = NULL;
off_t stream_offset, section1_offset, section2_offset, basename_offset, subname_offset; 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; int loop_flag, channel_count, sample_rate, layers;
int32_t num_samples, loop_start, loop_end; int32_t num_samples, loop_start, loop_end;
int total_subsongs, target_subsong = sf->stream_index; int total_subsongs, target_subsong = sf->stream_index;
@ -37,14 +37,14 @@ VGMSTREAM * init_vgmstream_lrmd(STREAMFILE *sf) {
VGM_LOG("LRMD: unknown value\n"); VGM_LOG("LRMD: unknown value\n");
goto fail; goto fail;
} }
layer_chunk = read_u16le(0x2a, sf_h); max_chunk = read_u16le(0x2a, sf_h);
num_samples = read_u32le(0x2c, sf_h); num_samples = read_u32le(0x2c, sf_h);
/* 0x30: null? */ /* 0x30: null? */
/* 0x34: data size for all layers */ /* 0x34: data size for all layers */
layers = read_u32le(0x38, sf_h); layers = read_u32le(0x38, sf_h);
section1_offset = read_u32le(0x3c, sf_h); section1_offset = read_u32le(0x3c, sf_h);
/* 0x40: seek/layer? table entries */ /* 0x40: lip table entries */
/* 0x44: seek/layer? table offset */ /* 0x44: lip table offset */
/* 0x48: section2 flag */ /* 0x48: section2 flag */
section2_offset = read_u32le(0x4c, sf_h); section2_offset = read_u32le(0x4c, sf_h);
/* 0x40: section3 flag */ /* 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 = 1;
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; 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 /* data is divided into N interleaved layers sharing config (channels may vary), and
* layered, but since they have names it's worth showing as subsongs */ * since they have names it's worth showing as subsongs */
/* section1: layer config */ /* section1: layer config */
section1_offset += (target_subsong - 1) * 0x18; {
/* 0x00: null */ int i;
subname_offset = read_u32le(section1_offset + 0x04, sf_h); int frame_size = max_chunk / layers / 2; /* even for songs with mono layers */
/* 0x08: unk */
/* 0x0c: flags? */ chunk_size = 0;
/* 0x10: null? */ for (i = 0; i < layers; i++) {
/* 0x14: null? */ off_t header_offset = section1_offset + i * 0x18;
sample_rate = 44100; int layer_channels;
channel_count = 2;
/* 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 */ /* section2: loops */
/* 0x00: offset to "loop" name */ /* 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 */ /* 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; if (!temp_sf) goto fail;
stream_offset = 0x00; stream_offset = 0x00;
@ -105,13 +127,11 @@ VGMSTREAM * init_vgmstream_lrmd(STREAMFILE *sf) {
#ifdef VGM_USE_FFMPEG #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->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; if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg; vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none; vgmstream->layout_type = layout_none;

View File

@ -2,14 +2,21 @@
#define _LRMD_STREAMFILE_H_ #define _LRMD_STREAMFILE_H_
#include "deblock_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 */ /* 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; STREAMFILE *new_sf = NULL;
deblock_config_t cfg = {0}; deblock_config_t cfg = {0};
cfg.chunk_size = interleave_size; cfg.frame_size = block_size;
cfg.step_start = stream_number; cfg.chunk_size = chunk_size;
cfg.step_count = stream_count; cfg.skip_size = chunk_start;
cfg.block_callback = block_callback;
new_sf = open_wrap_streamfile(sf); new_sf = open_wrap_streamfile(sf);
new_sf = open_io_deblock_streamfile_f(new_sf, &cfg); 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_opusnx(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_opus_sqex(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); VGMSTREAM * init_vgmstream_pc_ast(STREAMFILE * streamFile);

View File

@ -19,7 +19,9 @@ VGMSTREAM * init_vgmstream_nub(STREAMFILE *streamFile) {
/* checks */ /* checks */
if (!check_extensions(streamFile, "nub")) /* .nub: standard
* .nub2: rare [iDOLM@STER - Gravure For You (PS3)] */
if (!check_extensions(streamFile, "nub,nub2"))
goto fail; goto fail;
version = read_32bitBE(0x00,streamFile); version = read_32bitBE(0x00,streamFile);

View File

@ -1,7 +1,7 @@
#include "meta.h" #include "meta.h"
#include "../coding/coding.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 * init_vgmstream_ps2_svag(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
off_t start_offset; off_t start_offset;
@ -12,14 +12,17 @@ VGMSTREAM * init_vgmstream_ps2_svag(STREAMFILE *streamFile) {
/* checks */ /* checks */
if (!check_extensions(streamFile, "svag")) if (!check_extensions(streamFile, "svag"))
goto fail; goto fail;
if (read_32bitBE(0x00,streamFile) != 0x53766167) /* "Svag" */ if (read_32bitBE(0x00,streamFile) != 0x53766167) /* "Svag" */
goto fail; goto fail;
channel_count = read_16bitLE(0x0C,streamFile); /* always 2? ("S"tereo vag?) */ channel_count = read_16bitLE(0x0C,streamFile); /* always 2? ("S"tereo vag?) */
loop_flag = (read_32bitLE(0x14,streamFile)==1); 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); data_size = read_32bitLE(0x04,streamFile);

View File

@ -1,40 +1,42 @@
#include "meta.h" #include "meta.h"
#include "../coding/coding.h" #include "../coding/coding.h"
/* AL" - headerless a-law, found in Conquest of Elysium 3 (PC) */ /* AL/AL2 - headerless a-law, from Illwinter Game Design games */
VGMSTREAM * init_vgmstream_pc_al2(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_raw_al(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
off_t start_offset; off_t start_offset;
int loop_flag = 0, channel_count; int loop_flag = 0, channel_count;
/* checks */
if ( !check_extensions(streamFile,"al2")) /* .al: Dominions 3 - The Awakening (PC)
goto fail; * .al2: Conquest of Elysium 3 (PC) */
if ( !check_extensions(streamFile,"al,al2"))
channel_count = 2; goto fail;
/* build the VGMSTREAM */ channel_count = check_extensions(streamFile,"al") ? 1 : 2;
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail; /* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
vgmstream->sample_rate = 22050; if (!vgmstream) goto fail;
vgmstream->coding_type = coding_ALAW;
vgmstream->layout_type = layout_interleave; vgmstream->sample_rate = 22050;
vgmstream->interleave_block_size = 0x01; vgmstream->coding_type = coding_ALAW;
vgmstream->meta_type = meta_PC_AL2; vgmstream->layout_type = layout_interleave;
vgmstream->num_samples = pcm_bytes_to_samples(get_streamfile_size(streamFile), channel_count, 8); vgmstream->interleave_block_size = 0x01;
if (loop_flag) { vgmstream->meta_type = meta_RAW_AL;
vgmstream->loop_start_sample = 0; vgmstream->num_samples = pcm_bytes_to_samples(get_streamfile_size(streamFile), channel_count, 8);
vgmstream->loop_end_sample = vgmstream->num_samples; 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) ) start_offset = 0;
goto fail;
return vgmstream; if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
goto fail;
fail: return vgmstream;
close_vgmstream(vgmstream);
return NULL; fail:
} close_vgmstream(vgmstream);
return NULL;
}

View File

@ -47,7 +47,8 @@ static void read_rwav(struct rwav_data * rd)
/* little endian, version 2 */ /* little endian, version 2 */
if ((uint32_t)read_32bitBE(rd->offset+4,rd->streamFile)!=0xFFFE4000 || 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 (uint32_t)read_32bitBE(rd->offset+8,rd->streamFile)!=0x00010102
) )
) )
@ -98,7 +99,7 @@ VGMSTREAM * init_vgmstream_rwsd(STREAMFILE *streamFile) {
coding_t coding_type; coding_t coding_type;
size_t wave_length; size_t wave_length;
int codec_number; int codec;
int channel_count; int channel_count;
int loop_flag; int loop_flag;
int rwar = 0; int rwar = 0;
@ -111,8 +112,6 @@ VGMSTREAM * init_vgmstream_rwsd(STREAMFILE *streamFile) {
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
const char *ext;
rwav_data.version = -1; rwav_data.version = -1;
rwav_data.start_offset = 0; rwav_data.start_offset = 0;
rwav_data.info_chunk = -1; rwav_data.info_chunk = -1;
@ -121,58 +120,39 @@ VGMSTREAM * init_vgmstream_rwsd(STREAMFILE *streamFile) {
/* check extension, case insensitive */ /* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename)); streamFile->get_name(streamFile,filename,sizeof(filename));
ext = filename_extension(filename); if (check_extensions(streamFile, "rwsd")) {
;
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;
}
} }
else else if (check_extensions(streamFile, "rwar")) {
{ rwar = 1;
// match rwsd }
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_16bit = read_16bitBE;
read_32bit = read_32bitBE; read_32bit = read_32bitBE;
} }
else else {
{
read_16bit = read_16bitLE; read_16bit = read_16bitLE;
read_32bit = read_32bitLE; read_32bit = read_32bitLE;
} }
/* check header */ /* check header */
if (rwar || rwav) if (rwar || rwav) {
{
rwav_data.offset = 0; rwav_data.offset = 0;
rwav_data.streamFile = streamFile; rwav_data.streamFile = streamFile;
rwav_data.big_endian = big_endian; rwav_data.big_endian = big_endian;
@ -182,13 +162,11 @@ VGMSTREAM * init_vgmstream_rwsd(STREAMFILE *streamFile) {
if (rwav) read_rwav(&rwav_data); if (rwav) read_rwav(&rwav_data);
if (rwav_data.wave_offset < 0) goto fail; if (rwav_data.wave_offset < 0) goto fail;
} }
else else {
{
if ((uint32_t)read_32bitBE(0,streamFile)!=0x52575344) /* "RWSD" */ if ((uint32_t)read_32bitBE(0,streamFile)!=0x52575344) /* "RWSD" */
goto fail; goto fail;
switch (read_32bitBE(4,streamFile)) switch (read_32bitBE(4,streamFile)) {
{
case 0xFEFF0102: case 0xFEFF0102:
/* ideally we would look through the chunk list for a WAVE chunk, /* ideally we would look through the chunk list for a WAVE chunk,
* but it's always in the same order */ * but it's always in the same order */
@ -226,14 +204,14 @@ VGMSTREAM * init_vgmstream_rwsd(STREAMFILE *streamFile) {
} }
/* get type details */ /* 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); loop_flag = read_8bit(rwav_data.wave_offset+0x11,streamFile);
if (big_endian) if (big_endian)
channel_count = read_8bit(rwav_data.wave_offset+0x12,streamFile); channel_count = read_8bit(rwav_data.wave_offset+0x12,streamFile);
else else
channel_count = read_32bit(rwav_data.wave_offset+0x24,streamFile); channel_count = read_32bit(rwav_data.wave_offset+0x24,streamFile);
switch (codec_number) { switch (codec) {
case 0: case 0:
coding_type = coding_PCM8; coding_type = coding_PCM8;
break; break;
@ -256,18 +234,14 @@ VGMSTREAM * init_vgmstream_rwsd(STREAMFILE *streamFile) {
if (channel_count < 1) goto fail; if (channel_count < 1) goto fail;
/* build the VGMSTREAM */ /* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag); vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail; 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->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)); 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->num_samples = read_32bit(rwav_data.wave_offset+0x1c,streamFile);
vgmstream->loop_start_sample = read_32bit(rwav_data.wave_offset+0x18,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) if (rwar)
vgmstream->meta_type = meta_RWAR; vgmstream->meta_type = meta_RWAR;
else if (rwav) else if (rwav) {
{
if (big_endian) { if (big_endian) {
vgmstream->meta_type = meta_RWAV; 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) if (rwav_data.version == 2)
rwav_data.start_offset = read_32bit(8,streamFile); rwav_data.start_offset = read_32bit(8,streamFile);
} }

View File

@ -10,7 +10,7 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
size_t stream_size; size_t stream_size;
int is_sgx, is_sgb = 0; 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 sample_rate, num_samples, loop_start_sample, loop_end_sample;
int total_subsongs, target_subsong = streamFile->stream_index; int total_subsongs, target_subsong = streamFile->stream_index;
@ -69,7 +69,7 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
/* 0x00 ? (00/01/02) */ /* 0x00 ? (00/01/02) */
if (!is_sgx) /* meaning unknown in .sgx; offset 0 = not a stream (a RGND sample) */ if (!is_sgx) /* meaning unknown in .sgx; offset 0 = not a stream (a RGND sample) */
name_offset = read_32bitLE(chunk_offset+0x04,streamHeader); 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); channels = read_8bit(chunk_offset+0x09,streamHeader);
/* 0x0a null */ /* 0x0a null */
sample_rate = read_32bitLE(chunk_offset+0x0c,streamHeader); sample_rate = read_32bitLE(chunk_offset+0x0c,streamHeader);
@ -111,7 +111,14 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
if (name_offset) if (name_offset)
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamHeader); 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 #ifdef VGM_USE_VORBIS
case 0x02: /* Ogg Vorbis [Ni no Kuni: Wrath of the White Witch Remastered (PC)] (codec hijack?) */ 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); 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; 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; break;
#ifdef VGM_USE_FFMPEG #ifdef VGM_USE_FFMPEG
@ -173,6 +183,7 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
#endif #endif
default: default:
VGM_LOG("SGDX: unknown codec %i\n", codec);
goto fail; goto fail;
} }

View File

@ -346,7 +346,6 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_opus_nus3, init_vgmstream_opus_nus3,
init_vgmstream_opus_sps_n1, init_vgmstream_opus_sps_n1,
init_vgmstream_opus_nxa, init_vgmstream_opus_nxa,
init_vgmstream_pc_al2,
init_vgmstream_pc_ast, init_vgmstream_pc_ast,
init_vgmstream_naac, init_vgmstream_naac,
init_vgmstream_ubi_sb, init_vgmstream_ubi_sb,
@ -500,6 +499,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_raw_wavm, /* .wavm raw xbox */ init_vgmstream_raw_wavm, /* .wavm raw xbox */
init_vgmstream_raw_pcm, /* .raw raw PCM */ init_vgmstream_raw_pcm, /* .raw raw PCM */
init_vgmstream_s14_sss, /* .s14/sss raw siren14 */ init_vgmstream_s14_sss, /* .s14/sss raw siren14 */
init_vgmstream_raw_al, /* .al/al2 raw A-LAW */
#ifdef VGM_USE_FFMPEG #ifdef VGM_USE_FFMPEG
init_vgmstream_ffmpeg, /* may play anything incorrectly, since FFmpeg doesn't check extensions */ init_vgmstream_ffmpeg, /* may play anything incorrectly, since FFmpeg doesn't check extensions */
#endif #endif
@ -2496,7 +2496,8 @@ static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *strea
{".V0",".V1"}, /* Homura (PS2) */ {".V0",".V1"}, /* Homura (PS2) */
{".L",".R"}, /* Crash Nitro Racing (PS2), Gradius V (PS2) */ {".L",".R"}, /* Crash Nitro Racing (PS2), Gradius V (PS2) */
{"_0.dsp","_1.dsp"}, /* Wario World (GC) */ {"_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 new_filename[PATH_LIMIT];
char * extension; 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 */ 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); //;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) { 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); 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 */ 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); //;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) { 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 */ 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 */ 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) if (dfs_pair == -1)
goto fail; goto fail;
//;VGM_LOG("DFS: match %i filename=%s\n", dfs_pair, new_filename); //;VGM_LOG("DFS: match %i filename=%s\n", dfs_pair, new_filename);
/* try to init other channel (new_filename now has the opposite name) */ new_vgmstream = init_vgmstream_function(dual_streamFile); /* use the init function that just worked */
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 */
close_streamfile(dual_streamFile); close_streamfile(dual_streamFile);
/* see if we were able to open the file, and if everything matched nicely */ /* see if we were able to open the file, and if everything matched nicely */

View File

@ -612,7 +612,7 @@ typedef enum {
meta_EA_SNU, /* Electronic Arts SNU (Dead Space) */ meta_EA_SNU, /* Electronic Arts SNU (Dead Space) */
meta_AWC, /* Rockstar AWC (GTA5, RDR) */ meta_AWC, /* Rockstar AWC (GTA5, RDR) */
meta_OPUS, /* Nintendo Opus [Lego City Undercover (Switch)] */ 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_PC_AST, /* Dead Rising (PC) */
meta_NAAC, /* Namco AAC (3DS) */ meta_NAAC, /* Namco AAC (3DS) */
meta_UBI_SB, /* Ubisoft banks */ meta_UBI_SB, /* Ubisoft banks */