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
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
*Installation*: drop the ```in_vgmstream.dll``` in your Winamp plugins directory,
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),
* 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

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

View File

@ -379,7 +379,7 @@
<ClCompile Include="meta\opus_ppp.c" />
<ClCompile Include="meta\otm.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\sab.c" />
<ClCompile Include="meta\xa_xa30.c" />

View File

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

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

@ -612,7 +612,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 */