mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-03-02 08:21:20 +01:00
commit
14dc85664d
8
Makefile
8
Makefile
@ -6,6 +6,8 @@
|
|||||||
# currently aimed to WIN32 builds but vgmstream_cli should work for others (or use autotools instead)
|
# currently aimed to WIN32 builds but vgmstream_cli should work for others (or use autotools instead)
|
||||||
export TARGET_OS = $(OS)
|
export TARGET_OS = $(OS)
|
||||||
|
|
||||||
|
#for Win builds with vgmstream123
|
||||||
|
LIBAO_DLL_PATH = ../libao/bin
|
||||||
|
|
||||||
### tools
|
### tools
|
||||||
RMF = rm -f
|
RMF = rm -f
|
||||||
@ -49,6 +51,8 @@ export RMF SHELL CC AR STRIP WINDRES DLLTOOL
|
|||||||
|
|
||||||
buildrelease: clean bin
|
buildrelease: clean bin
|
||||||
|
|
||||||
|
buildrelease-ex: clean bin-ex
|
||||||
|
|
||||||
buildfullrelease: clean sourceball bin
|
buildfullrelease: clean sourceball bin
|
||||||
|
|
||||||
sourceball:
|
sourceball:
|
||||||
@ -63,6 +67,10 @@ sourceball:
|
|||||||
bin mingwbin: vgmstream_cli winamp xmplay
|
bin mingwbin: vgmstream_cli winamp xmplay
|
||||||
zip -FS -j "vgmstream-`./version.sh`-test.zip" COPYING README.md cli/test.exe winamp/in_vgmstream.dll xmplay/xmp-vgmstream.dll ext_libs/*.dll
|
zip -FS -j "vgmstream-`./version.sh`-test.zip" COPYING README.md cli/test.exe winamp/in_vgmstream.dll xmplay/xmp-vgmstream.dll ext_libs/*.dll
|
||||||
|
|
||||||
|
#separate since vgmstream123 is kinda untested
|
||||||
|
bin-ex mingwbin-ex: vgmstream_cli winamp xmplay vgmstream123
|
||||||
|
zip -FS -j "vgmstream-`./version.sh`-test.zip" COPYING README.md cli/test.exe cli/vgmstream123.exe winamp/in_vgmstream.dll xmplay/xmp-vgmstream.dll ext_libs/*.dll $(LIBAO_DLL_PATH)/*.dll
|
||||||
|
|
||||||
vgmstream_cli mingw_test:
|
vgmstream_cli mingw_test:
|
||||||
$(MAKE) -C cli vgmstream_cli
|
$(MAKE) -C cli vgmstream_cli
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
#include <math.h>
|
||||||
#include "coding.h"
|
#include "coding.h"
|
||||||
|
|
||||||
/* Relic Codec decoder, a fairly simple mono-interleave DCT-based codec.
|
/* Relic Codec decoder, a fairly simple mono-interleave DCT-based codec.
|
||||||
|
@ -6,10 +6,10 @@
|
|||||||
VGMSTREAM* init_vgmstream_bkhd(STREAMFILE* sf) {
|
VGMSTREAM* init_vgmstream_bkhd(STREAMFILE* sf) {
|
||||||
VGMSTREAM* vgmstream = NULL;
|
VGMSTREAM* vgmstream = NULL;
|
||||||
STREAMFILE* temp_sf = NULL;
|
STREAMFILE* temp_sf = NULL;
|
||||||
off_t subfile_offset, didx_offset, data_offset, offset;
|
off_t subfile_offset, base_offset = 0;
|
||||||
size_t subfile_size, didx_size;
|
size_t subfile_size;
|
||||||
uint32_t subfile_id;
|
uint32_t subfile_id, header_id;
|
||||||
int big_endian;
|
int big_endian, version, is_dummy = 0;
|
||||||
uint32_t (*read_u32)(off_t,STREAMFILE*);
|
uint32_t (*read_u32)(off_t,STREAMFILE*);
|
||||||
int total_subsongs, target_subsong = sf->stream_index;
|
int total_subsongs, target_subsong = sf->stream_index;
|
||||||
|
|
||||||
@ -17,17 +17,65 @@ VGMSTREAM* init_vgmstream_bkhd(STREAMFILE* sf) {
|
|||||||
/* checks */
|
/* checks */
|
||||||
if (!check_extensions(sf,"bnk"))
|
if (!check_extensions(sf,"bnk"))
|
||||||
goto fail;
|
goto fail;
|
||||||
if (read_u32be(0x00, sf) != 0x424B4844) /* "BKHD" */
|
|
||||||
|
if (read_u32be(0x00, sf) == 0x414B424B) /* "AKBK" [Shadowrun (X360)] */
|
||||||
|
base_offset = 0x0c;
|
||||||
|
if (read_u32be(base_offset + 0x00, sf) != 0x424B4844) /* "BKHD" */
|
||||||
goto fail;
|
goto fail;
|
||||||
big_endian = guess_endianness32bit(0x04, sf);
|
big_endian = guess_endianness32bit(base_offset + 0x04, sf);
|
||||||
read_u32 = big_endian ? read_u32be : read_u32le;
|
read_u32 = big_endian ? read_u32be : read_u32le;
|
||||||
|
|
||||||
/* Wwise banks have event/track/sequence/etc info in the HIRC chunk, as well
|
|
||||||
* as other chunks, and may have a DIDX index to memory .wem in DATA.
|
|
||||||
* We support the internal .wem mainly for quick tests, as the HIRC is
|
|
||||||
* complex and better handled with TXTP (some info from Nicknine's script) */
|
|
||||||
|
|
||||||
/* unlike RIFF first chunk follows chunk rules */
|
/* Wwise banks have event/track/sequence/etc info in the HIRC chunk, as well
|
||||||
|
* as other chunks, and may have a DATA/DIDX index to memory .wem in DATA.
|
||||||
|
* We support the internal .wem mainly for quick tests, as the HIRC is
|
||||||
|
* complex and better handled with TXTP (some info from Nicknine's script).
|
||||||
|
* unlike RIFF, first chunk follows chunk rules */
|
||||||
|
|
||||||
|
version = read_u32(base_offset + 0x08, sf);
|
||||||
|
if (version == 0 || version == 1) { /* early games */
|
||||||
|
version = read_u32(base_offset + 0x10, sf);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version <= 26) {
|
||||||
|
off_t data_offset, data_start, offset;
|
||||||
|
if (!find_chunk(sf, 0x44415441, base_offset, 0, &data_offset, NULL, big_endian, 0)) /* "DATA" */
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
/* index:
|
||||||
|
* 00: entries
|
||||||
|
* 04: null
|
||||||
|
* 08: entries size
|
||||||
|
* 0c: padding size after entries
|
||||||
|
* 10: data size
|
||||||
|
* 14: size?
|
||||||
|
* 18: data start
|
||||||
|
* 1c: data size
|
||||||
|
* per entry:
|
||||||
|
* 00: always -1
|
||||||
|
* 04: always 0
|
||||||
|
* 08: index number or -1
|
||||||
|
* 0c: 5 or -1?
|
||||||
|
* 0c: 5 or -1?
|
||||||
|
* 0c: 5 or -1?
|
||||||
|
* 10: stream offset (from data start) or -1 if none
|
||||||
|
* 14: stream size or 0 if none
|
||||||
|
*/
|
||||||
|
|
||||||
|
total_subsongs = read_u32(data_offset + 0x00, sf);
|
||||||
|
if (target_subsong == 0) target_subsong = 1;
|
||||||
|
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
|
||||||
|
|
||||||
|
data_start = read_u32(data_offset + 0x18, sf);
|
||||||
|
offset = data_offset + 0x20 + (target_subsong - 1) * 0x18;
|
||||||
|
|
||||||
|
subfile_id = read_u32(offset + 0x08, sf);
|
||||||
|
subfile_offset = read_u32(offset + 0x10, sf) + data_offset + 0x20 + data_start;
|
||||||
|
subfile_size = read_u32(offset + 0x14, sf);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
off_t didx_offset, data_offset, offset;
|
||||||
|
size_t didx_size;
|
||||||
if (!find_chunk(sf, 0x44494458, 0x00,0, &didx_offset, &didx_size, big_endian, 0)) /* "DIDX" */
|
if (!find_chunk(sf, 0x44494458, 0x00,0, &didx_offset, &didx_size, big_endian, 0)) /* "DIDX" */
|
||||||
goto fail;
|
goto fail;
|
||||||
if (!find_chunk(sf, 0x44415441, 0x00,0, &data_offset, NULL, big_endian, 0)) /* "DATA" */
|
if (!find_chunk(sf, 0x44415441, 0x00,0, &data_offset, NULL, big_endian, 0)) /* "DATA" */
|
||||||
@ -41,13 +89,27 @@ VGMSTREAM* init_vgmstream_bkhd(STREAMFILE* sf) {
|
|||||||
subfile_id = read_u32(offset + 0x00, sf);
|
subfile_id = read_u32(offset + 0x00, sf);
|
||||||
subfile_offset = read_u32(offset + 0x04, sf) + data_offset;
|
subfile_offset = read_u32(offset + 0x04, sf) + data_offset;
|
||||||
subfile_size = read_u32(offset + 0x08, sf);
|
subfile_size = read_u32(offset + 0x08, sf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* some indexes don't have that, but for now leave a dummy song for easier HIRC mapping */
|
||||||
|
if (subfile_offset <= 0 || subfile_size <= 0) {
|
||||||
|
//;VGM_LOG("BKHD: dummy entry");
|
||||||
|
temp_sf = setup_subfile_streamfile(sf, 0x00, 0x10, "raw");
|
||||||
|
if (!temp_sf) goto fail;
|
||||||
|
|
||||||
|
//todo make some better silent entry
|
||||||
|
vgmstream = init_vgmstream_raw_pcm(temp_sf);
|
||||||
|
if (!vgmstream) goto fail;
|
||||||
|
|
||||||
|
is_dummy = 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
//;VGM_LOG("BKHD: %lx, %x\n", subfile_offset, subfile_size);
|
//;VGM_LOG("BKHD: %lx, %x\n", subfile_offset, subfile_size);
|
||||||
temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, "wem");
|
temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, "wem");
|
||||||
if (!temp_sf) goto fail;
|
if (!temp_sf) goto fail;
|
||||||
|
|
||||||
subfile_id = read_u32(0x00, temp_sf);
|
header_id = read_u32be(0x00, temp_sf);
|
||||||
if (subfile_id == 0x52494646 || subfile_id == 0x52494658) { /* "RIFF" / "RIFX" */
|
if (header_id == 0x52494646 || header_id == 0x52494658) { /* "RIFF" / "RIFX" */
|
||||||
vgmstream = init_vgmstream_wwise(temp_sf);
|
vgmstream = init_vgmstream_wwise(temp_sf);
|
||||||
if (!vgmstream) goto fail;
|
if (!vgmstream) goto fail;
|
||||||
}
|
}
|
||||||
@ -55,8 +117,14 @@ VGMSTREAM* init_vgmstream_bkhd(STREAMFILE* sf) {
|
|||||||
vgmstream = init_vgmstream_bkhd_fx(temp_sf);
|
vgmstream = init_vgmstream_bkhd_fx(temp_sf);
|
||||||
if (!vgmstream) goto fail;
|
if (!vgmstream) goto fail;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
vgmstream->num_streams = total_subsongs;
|
vgmstream->num_streams = total_subsongs;
|
||||||
|
|
||||||
|
if (is_dummy)
|
||||||
|
snprintf(vgmstream->stream_name, STREAM_NAME_SIZE, "%s", "dummy");
|
||||||
|
else if (subfile_id != 0xFFFFFFFF)
|
||||||
snprintf(vgmstream->stream_name, STREAM_NAME_SIZE, "%u", subfile_id);
|
snprintf(vgmstream->stream_name, STREAM_NAME_SIZE, "%u", subfile_id);
|
||||||
|
|
||||||
close_streamfile(temp_sf);
|
close_streamfile(temp_sf);
|
||||||
@ -69,7 +137,7 @@ fail:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* BKHD mini format, probably from a generator plugin [Borderlands 2 (X360)] */
|
/* BKHD mini format, probably from a FX generator plugin [Borderlands 2 (X360)] */
|
||||||
VGMSTREAM* init_vgmstream_bkhd_fx(STREAMFILE* sf) {
|
VGMSTREAM* init_vgmstream_bkhd_fx(STREAMFILE* sf) {
|
||||||
VGMSTREAM* vgmstream = NULL;
|
VGMSTREAM* vgmstream = NULL;
|
||||||
off_t start_offset, data_size;
|
off_t start_offset, data_size;
|
||||||
@ -93,8 +161,7 @@ VGMSTREAM* init_vgmstream_bkhd_fx(STREAMFILE* sf) {
|
|||||||
/* 0x14/18: some float? */
|
/* 0x14/18: some float? */
|
||||||
entries = read_u32(0x1c, sf);
|
entries = read_u32(0x1c, sf);
|
||||||
/* 0x20 data size / 0x10 */
|
/* 0x20 data size / 0x10 */
|
||||||
if (read_u8(0x24, sf) != 4) /* bps */
|
/* 0x24 usually 4, sometimes higher values? */
|
||||||
goto fail;
|
|
||||||
/* 0x30: unknown table of 16b that goes up and down */
|
/* 0x30: unknown table of 16b that goes up and down */
|
||||||
|
|
||||||
start_offset = 0x30 + align_size_to_block(entries * 0x02, 0x10);
|
start_offset = 0x30 + align_size_to_block(entries * 0x02, 0x10);
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
#ifdef 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);
|
static void bruteforce_hca_key(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* out_keycode, uint16_t subkey);
|
||||||
#endif
|
#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, uint64_t* p_keycode, uint16_t subkey);
|
||||||
|
|
||||||
|
|
||||||
/* CRI HCA - streamed audio from CRI ADX2/Atom middleware */
|
/* CRI HCA - streamed audio from CRI ADX2/Atom middleware */
|
||||||
@ -17,7 +17,6 @@ VGMSTREAM * init_vgmstream_hca(STREAMFILE *streamFile) {
|
|||||||
VGMSTREAM * init_vgmstream_hca_subkey(STREAMFILE *streamFile, uint16_t subkey) {
|
VGMSTREAM * init_vgmstream_hca_subkey(STREAMFILE *streamFile, uint16_t subkey) {
|
||||||
VGMSTREAM * vgmstream = NULL;
|
VGMSTREAM * vgmstream = NULL;
|
||||||
hca_codec_data * hca_data = NULL;
|
hca_codec_data * hca_data = NULL;
|
||||||
unsigned long long keycode = 0;
|
|
||||||
|
|
||||||
|
|
||||||
/* checks */
|
/* checks */
|
||||||
@ -32,6 +31,7 @@ VGMSTREAM * init_vgmstream_hca_subkey(STREAMFILE *streamFile, uint16_t subkey) {
|
|||||||
|
|
||||||
/* find decryption key in external file or preloaded list */
|
/* find decryption key in external file or preloaded list */
|
||||||
if (hca_data->info.encryptionEnabled) {
|
if (hca_data->info.encryptionEnabled) {
|
||||||
|
uint64_t keycode = 0;
|
||||||
uint8_t keybuf[0x08+0x02];
|
uint8_t keybuf[0x08+0x02];
|
||||||
size_t keysize;
|
size_t keysize;
|
||||||
|
|
||||||
@ -56,7 +56,7 @@ VGMSTREAM * init_vgmstream_hca_subkey(STREAMFILE *streamFile, uint16_t subkey) {
|
|||||||
find_hca_key(hca_data, &keycode, subkey);
|
find_hca_key(hca_data, &keycode, subkey);
|
||||||
}
|
}
|
||||||
|
|
||||||
clHCA_SetKey(hca_data->handle, keycode); //maybe should be done through hca_decoder.c?
|
clHCA_SetKey(hca_data->handle, (unsigned long long)keycode); //maybe should be done through hca_decoder.c?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -139,25 +139,25 @@ 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, uint64_t* p_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 */
|
*p_keycode = 0xCC55463930DBE1AB; /* defaults to PSO2 key, most common */
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
test_key(hca_data, key, subkey, &best_score, out_keycode);
|
test_key(hca_data, key, subkey, &best_score, p_keycode);
|
||||||
if (best_score == 1)
|
if (best_score == 1)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
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, p_keycode);
|
||||||
if (best_score == 1)
|
if (best_score == 1)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
@ -166,7 +166,7 @@ static void find_hca_key(hca_codec_data* hca_data, unsigned long long* out_keyco
|
|||||||
|
|
||||||
done:
|
done:
|
||||||
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)((*p_keycode >> 32) & 0xFFFFFFFF), (uint32_t)(*p_keycode & 0xFFFFFFFF), best_score);
|
||||||
VGM_ASSERT(best_score < 0, "HCA: key not found\n");
|
VGM_ASSERT(best_score < 0, "HCA: key not found\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ VGMSTREAM * init_vgmstream_rsd(STREAMFILE *streamFile) {
|
|||||||
|
|
||||||
loop_flag = 0;
|
loop_flag = 0;
|
||||||
|
|
||||||
codec = read_32bitBE(0x04,streamFile);
|
codec = (uint32_t)read_32bitBE(0x04,streamFile);
|
||||||
channel_count = read_32bitLE(0x08, streamFile);
|
channel_count = read_32bitLE(0x08, streamFile);
|
||||||
/* 0x0c: always 16? */
|
/* 0x0c: always 16? */
|
||||||
sample_rate = read_32bitLE(0x10, streamFile);
|
sample_rate = read_32bitLE(0x10, streamFile);
|
||||||
|
@ -47,18 +47,20 @@ VGMSTREAM * init_vgmstream_wwise(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;
|
||||||
|
|
||||||
/* basic checks */
|
/* checks */
|
||||||
/* .wem (Wwise Encoded Media) is "newer Wwise", used after the 2011.2 SDK (~july)
|
/* .wem: newer "Wwise Encoded Media" used after the 2011.2 SDK (~july 2011)
|
||||||
* .wav (ex. Shadowrun X360) and .ogg (ex. KOF XII X360), .xma (ex. Tron Evolution X360) are used in older Wwise */
|
* .wav: older ADPCM files [Punch Out!! (Wii)]
|
||||||
if (!check_extensions(streamFile,"wem,wav,lwav,ogg,logg,xma")) goto fail;
|
* .xma: older XMA files [Too Human (X360), Tron Evolution (X360)]
|
||||||
|
* .ogg: older Vorbis files [The King of Fighters XII (X360)] */
|
||||||
if ((read_32bitBE(0x00,streamFile) != 0x52494646) && /* "RIFF" (LE) */
|
if (!check_extensions(streamFile,"wem,wav,lwav,ogg,logg,xma"))
|
||||||
(read_32bitBE(0x00,streamFile) != 0x52494658)) /* "RIFX" (BE) */
|
|
||||||
goto fail;
|
|
||||||
if ((read_32bitBE(0x08,streamFile) != 0x57415645) && /* "WAVE" */
|
|
||||||
(read_32bitBE(0x08,streamFile) != 0x58574D41)) /* "XWMA" */
|
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
|
if (read_32bitBE(0x00,streamFile) != 0x52494646 && /* "RIFF" (LE) */
|
||||||
|
read_32bitBE(0x00,streamFile) != 0x52494658) /* "RIFX" (BE) */
|
||||||
|
goto fail;
|
||||||
|
if (read_32bitBE(0x08,streamFile) != 0x57415645 && /* "WAVE" */
|
||||||
|
read_32bitBE(0x08,streamFile) != 0x58574D41) /* "XWMA" */
|
||||||
|
goto fail;
|
||||||
|
|
||||||
ww.big_endian = read_32bitBE(0x00,streamFile) == 0x52494658; /* RIFX */
|
ww.big_endian = read_32bitBE(0x00,streamFile) == 0x52494658; /* RIFX */
|
||||||
if (ww.big_endian) { /* Wwise honors machine's endianness (PC=RIFF, X360=RIFX --unlike XMA) */
|
if (ww.big_endian) { /* Wwise honors machine's endianness (PC=RIFF, X360=RIFX --unlike XMA) */
|
||||||
@ -94,10 +96,9 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
|||||||
if (find_chunk(streamFile, 0x66616374,first_offset,0, &fact_offset,&fact_size, 0, 0)) { /* "fact" */
|
if (find_chunk(streamFile, 0x66616374,first_offset,0, &fact_offset,&fact_size, 0, 0)) { /* "fact" */
|
||||||
if (fact_size == 0x10 && read_32bitBE(fact_offset+0x04, streamFile) == 0x4C794E20) /* "LyN " */
|
if (fact_size == 0x10 && read_32bitBE(fact_offset+0x04, streamFile) == 0x4C794E20) /* "LyN " */
|
||||||
goto fail; /* parsed elsewhere */
|
goto fail; /* parsed elsewhere */
|
||||||
|
}
|
||||||
/* Wwise doesn't use "fact", though */
|
/* Wwise doesn't use "fact", though */
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* parse format (roughly spec-compliant but some massaging is needed) */
|
/* parse format (roughly spec-compliant but some massaging is needed) */
|
||||||
{
|
{
|
||||||
@ -105,19 +106,26 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
|||||||
size_t loop_size;
|
size_t loop_size;
|
||||||
|
|
||||||
/* find basic chunks */
|
/* find basic chunks */
|
||||||
if (!find_chunk(streamFile, 0x666d7420,first_offset,0, &ww.fmt_offset,&ww.fmt_size, ww.big_endian, 0)) goto fail; /*"fmt "*/
|
if (read_32bitBE(0x0c, streamFile) == 0x584D4132) { /* "XMA2" with no "fmt" [Too Human (X360)] */
|
||||||
if (!find_chunk(streamFile, 0x64617461,first_offset,0, &ww.data_offset,&ww.data_size, ww.big_endian, 0)) goto fail; /*"data"*/
|
ww.format = 0x0165; /* signal for below */
|
||||||
|
}
|
||||||
/* base fmt */
|
else {
|
||||||
if (ww.fmt_size < 0x12) goto fail;
|
if (!find_chunk(streamFile, 0x666d7420,first_offset,0, &ww.fmt_offset,&ww.fmt_size, ww.big_endian, 0)) /* "fmt " */
|
||||||
|
goto fail;
|
||||||
|
if (ww.fmt_size < 0x12)
|
||||||
|
goto fail;
|
||||||
ww.format = (uint16_t)read_16bit(ww.fmt_offset+0x00,streamFile);
|
ww.format = (uint16_t)read_16bit(ww.fmt_offset+0x00,streamFile);
|
||||||
|
}
|
||||||
|
|
||||||
if (ww.format == 0x0165) { /* pseudo-XMA2WAVEFORMAT (always "fmt"+"XMA2", unlike .xma that may only have "XMA2") */
|
|
||||||
if (!find_chunk(streamFile, 0x584D4132,first_offset,0, &ww.chunk_offset,NULL, ww.big_endian, 0))
|
if (ww.format == 0x0165) {
|
||||||
|
/* pseudo-XMA2WAVEFORMAT ("fmt"+"XMA2" or just "XMA2) */
|
||||||
|
if (!find_chunk(streamFile, 0x584D4132,first_offset,0, &ww.chunk_offset,NULL, ww.big_endian, 0)) /* "XMA2" */
|
||||||
goto fail;
|
goto fail;
|
||||||
xma2_parse_xma2_chunk(streamFile, ww.chunk_offset,&ww.channels,&ww.sample_rate, &ww.loop_flag, &ww.num_samples, &ww.loop_start_sample, &ww.loop_end_sample);
|
xma2_parse_xma2_chunk(streamFile, ww.chunk_offset,&ww.channels,&ww.sample_rate, &ww.loop_flag, &ww.num_samples, &ww.loop_start_sample, &ww.loop_end_sample);
|
||||||
}
|
}
|
||||||
else { /* pseudo-WAVEFORMATEX */
|
else {
|
||||||
|
/* pseudo-WAVEFORMATEX */
|
||||||
ww.channels = read_16bit(ww.fmt_offset+0x02,streamFile);
|
ww.channels = read_16bit(ww.fmt_offset+0x02,streamFile);
|
||||||
ww.sample_rate = read_32bit(ww.fmt_offset+0x04,streamFile);
|
ww.sample_rate = read_32bit(ww.fmt_offset+0x04,streamFile);
|
||||||
ww.average_bps = read_32bit(ww.fmt_offset+0x08,streamFile);/* bytes per sec */
|
ww.average_bps = read_32bit(ww.fmt_offset+0x08,streamFile);/* bytes per sec */
|
||||||
@ -128,7 +136,7 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
|||||||
if (ww.extra_size >= 0x06) { /* always present (actual RIFFs only have it in WAVEFORMATEXTENSIBLE) */
|
if (ww.extra_size >= 0x06) { /* always present (actual RIFFs only have it in WAVEFORMATEXTENSIBLE) */
|
||||||
/* mostly WAVEFORMATEXTENSIBLE's bitmask (see AkSpeakerConfig.h) */
|
/* mostly WAVEFORMATEXTENSIBLE's bitmask (see AkSpeakerConfig.h) */
|
||||||
ww.channel_layout = read_32bit(ww.fmt_offset+0x14,streamFile);
|
ww.channel_layout = read_32bit(ww.fmt_offset+0x14,streamFile);
|
||||||
/* latest games have a pseudo-format instead to handle more cases:
|
/* later games (+2018?) have a pseudo-format instead to handle more cases:
|
||||||
* - 8b: uNumChannels
|
* - 8b: uNumChannels
|
||||||
* - 4b: eConfigType (0=none, 1=standard, 2=ambisonic)
|
* - 4b: eConfigType (0=none, 1=standard, 2=ambisonic)
|
||||||
* - 19b: uChannelMask */
|
* - 19b: uChannelMask */
|
||||||
@ -138,8 +146,8 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* find loop info */
|
/* find loop info ("XMA2" chunks already read them) */
|
||||||
if (ww.format == 0x0166) { /* XMA2WAVEFORMATEX */
|
if (ww.format == 0x0166) { /* XMA2WAVEFORMATEX in fmt */
|
||||||
ww.chunk_offset = ww.fmt_offset;
|
ww.chunk_offset = ww.fmt_offset;
|
||||||
xma2_parse_fmt_chunk_extra(streamFile, ww.chunk_offset, &ww.loop_flag, &ww.num_samples, &ww.loop_start_sample, &ww.loop_end_sample, ww.big_endian);
|
xma2_parse_fmt_chunk_extra(streamFile, ww.chunk_offset, &ww.loop_flag, &ww.num_samples, &ww.loop_start_sample, &ww.loop_end_sample, ww.big_endian);
|
||||||
}
|
}
|
||||||
@ -149,29 +157,31 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
|||||||
&& read_32bit(loop_offset+0x24+4, streamFile)==0) {
|
&& read_32bit(loop_offset+0x24+4, streamFile)==0) {
|
||||||
ww.loop_flag = 1;
|
ww.loop_flag = 1;
|
||||||
ww.loop_start_sample = read_32bit(loop_offset+0x24+0x8, streamFile);
|
ww.loop_start_sample = read_32bit(loop_offset+0x24+0x8, streamFile);
|
||||||
ww.loop_end_sample = read_32bit(loop_offset+0x24+0xc,streamFile);
|
ww.loop_end_sample = read_32bit(loop_offset+0x24+0xc, streamFile) + 1; /* like standard RIFF */
|
||||||
//todo fix repeat looping
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//else if (find_chunk(streamFile, 0x4C495354,first_offset,0, &loop_offset,&loop_size, ww.big_endian, 0)) { /*"LIST", common */
|
//else if (find_chunk(streamFile, 0x4C495354,first_offset,0, &loop_offset,&loop_size, ww.big_endian, 0)) { /*"LIST", common */
|
||||||
// /* usually contains "cue"s with sample positions for events (ex. Platinum Games) but no real looping info */
|
// /* usually contains "cue"s with sample positions for events (ex. Platinum Games) but no real looping info */
|
||||||
//}
|
//}
|
||||||
|
|
||||||
/* other Wwise specific: */
|
/* other Wwise specific chunks:
|
||||||
//"JUNK": optional padding for aligment (0-size JUNK exists too)
|
* "JUNK": optional padding for aligment (0-size JUNK exists too)
|
||||||
//"akd ": seem to store extra info for Wwise editor (wave peaks/loudness/HDR envelope?)
|
* "akd ": seem to store extra info for Wwise editor (wave peaks/loudness/HDR envelope?)
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!find_chunk(streamFile, 0x64617461,first_offset,0, &ww.data_offset,&ww.data_size, ww.big_endian, 0)) /* "data" */
|
||||||
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* format to codec */
|
/* format to codec */
|
||||||
switch(ww.format) {
|
switch(ww.format) {
|
||||||
case 0x0001: ww.codec = PCM; break; /* older Wwise */
|
case 0x0001: ww.codec = PCM; break; /* older Wwise */
|
||||||
case 0x0002: ww.codec = IMA; break; /* newer Wwise (conflicts with MSADPCM, probably means "platform's ADPCM") */
|
case 0x0002: ww.codec = IMA; break; /* newer Wwise (conflicts with MSADPCM, probably means "platform's ADPCM") */
|
||||||
//case 0x0011: ww.codec = IMA; break; /* older Wwise (used?) */
|
case 0x0069: ww.codec = IMA; break; /* older Wwise [Spiderman Web of Shadows (X360), LotR Conquest (PC)] */
|
||||||
case 0x0069: ww.codec = IMA; break; /* older Wwise (Spiderman Web of Shadows X360, LotR Conquest PC) */
|
|
||||||
case 0x0161: ww.codec = XWMA; break; /* WMAv2 */
|
case 0x0161: ww.codec = XWMA; break; /* WMAv2 */
|
||||||
case 0x0162: ww.codec = XWMA; break; /* WMAPro */
|
case 0x0162: ww.codec = XWMA; break; /* WMAPro */
|
||||||
case 0x0165: ww.codec = XMA2; break; /* always with the "XMA2" chunk, Wwise doesn't use XMA1 */
|
case 0x0165: ww.codec = XMA2; break; /* XMA2-chunk XMA (Wwise doesn't use XMA1) */
|
||||||
case 0x0166: ww.codec = XMA2; break;
|
case 0x0166: ww.codec = XMA2; break; /* fmt-chunk XMA */
|
||||||
case 0xAAC0: ww.codec = AAC; break;
|
case 0xAAC0: ww.codec = AAC; break;
|
||||||
case 0xFFF0: ww.codec = DSP; break;
|
case 0xFFF0: ww.codec = DSP; break;
|
||||||
case 0xFFFB: ww.codec = HEVAG; break;
|
case 0xFFFB: ww.codec = HEVAG; break;
|
||||||
@ -213,7 +223,7 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ww.codec == PCM || ww.codec == IMA || ww.codec == VORBIS || ww.codec == XMA2 || ww.codec == OPUSNX || ww.codec == OPUS) {
|
if (ww.codec == PCM || ww.codec == IMA || ww.codec == DSP || ww.codec == VORBIS || ww.codec == XMA2 || ww.codec == OPUSNX || ww.codec == OPUS) {
|
||||||
ww.truncated = 1; /* only seen those, probably all exist */
|
ww.truncated = 1; /* only seen those, probably all exist */
|
||||||
} else {
|
} else {
|
||||||
VGM_LOG("WWISE: wrong size, maybe truncated\n");
|
VGM_LOG("WWISE: wrong size, maybe truncated\n");
|
||||||
@ -264,7 +274,6 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
|||||||
vgmstream->interleave_block_size = ww.block_align / ww.channels;
|
vgmstream->interleave_block_size = ww.block_align / ww.channels;
|
||||||
vgmstream->codec_endian = ww.big_endian;
|
vgmstream->codec_endian = ww.big_endian;
|
||||||
|
|
||||||
/* enough to get real samples */
|
|
||||||
if (ww.truncated) {
|
if (ww.truncated) {
|
||||||
ww.data_size = ww.file_size - ww.data_offset;
|
ww.data_size = ww.file_size - ww.data_offset;
|
||||||
}
|
}
|
||||||
@ -290,8 +299,8 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
|||||||
/* older Wwise (~<2012) */
|
/* older Wwise (~<2012) */
|
||||||
|
|
||||||
switch(vorb_size) {
|
switch(vorb_size) {
|
||||||
case 0x2C: /* earliest (~2009), [UFC Undisputed 2009 (PS3), some EVE Online Apocrypha (PC)?] */
|
case 0x2C: /* earliest (~2009) [The Lord of the Rings: Conquest (PC)] */
|
||||||
case 0x28: /* early (~2009) [The Lord of the Rings: Conquest (PC)] */
|
case 0x28: /* early (~2009) [UFC Undisputed 2009 (PS3), some EVE Online Apocrypha (PC)] */
|
||||||
data_offsets = 0x18;
|
data_offsets = 0x18;
|
||||||
block_offsets = 0; /* no need, full headers are present */
|
block_offsets = 0; /* no need, full headers are present */
|
||||||
cfg.header_type = WWV_TYPE_8;
|
cfg.header_type = WWV_TYPE_8;
|
||||||
@ -299,8 +308,8 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
|||||||
cfg.setup_type = WWV_HEADER_TRIAD;
|
cfg.setup_type = WWV_HEADER_TRIAD;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x34: /* common (2010~2011) */
|
case 0x34: /* common (2010~2011) [The King of Fighters XII (PS3), Assassin's Creed II (X360)] */
|
||||||
case 0x32: /* very rare (mid 2011) [Saints Row the 3rd (PC)] */
|
case 0x32: /* rare (mid 2011) [Saints Row the 3rd (PC)] */
|
||||||
data_offsets = 0x18;
|
data_offsets = 0x18;
|
||||||
block_offsets = 0x30;
|
block_offsets = 0x30;
|
||||||
cfg.header_type = WWV_TYPE_6;
|
cfg.header_type = WWV_TYPE_6;
|
||||||
@ -377,8 +386,6 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
|||||||
cfg.setup_type = is_wem ? WWV_AOTUV603_CODEBOOKS : WWV_EXTERNAL_CODEBOOKS; /* aoTuV came along .wem */
|
cfg.setup_type = is_wem ? WWV_AOTUV603_CODEBOOKS : WWV_EXTERNAL_CODEBOOKS; /* aoTuV came along .wem */
|
||||||
break;
|
break;
|
||||||
|
|
||||||
//case 0x2a: /* Rocksmith 2011 (X360)? */
|
|
||||||
//non mod packets? TYPE_06? (possibly detectable by checking setup's granule, should be 0)
|
|
||||||
default:
|
default:
|
||||||
VGM_LOG("WWISE: unknown extra size 0x%x\n", vorb_size);
|
VGM_LOG("WWISE: unknown extra size 0x%x\n", vorb_size);
|
||||||
goto fail;
|
goto fail;
|
||||||
@ -449,6 +456,11 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ww.truncated) {
|
||||||
|
ww.data_size = ww.file_size - ww.data_offset;
|
||||||
|
vgmstream->num_samples = dsp_bytes_to_samples(ww.data_size, ww.channels);
|
||||||
|
}
|
||||||
|
|
||||||
/* for some reason all(?) DSP .wem do full loops (even mono/jingles/etc) but
|
/* for some reason all(?) DSP .wem do full loops (even mono/jingles/etc) but
|
||||||
* several tracks do loop like this, so disable it for short-ish tracks */
|
* several tracks do loop like this, so disable it for short-ish tracks */
|
||||||
if (ww.loop_flag && vgmstream->loop_start_sample == 0 &&
|
if (ww.loop_flag && vgmstream->loop_start_sample == 0 &&
|
||||||
@ -457,7 +469,6 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* get coefs and default history */
|
/* get coefs and default history */
|
||||||
dsp_read_coefs(vgmstream,streamFile,wiih_offset, 0x2e, ww.big_endian);
|
dsp_read_coefs(vgmstream,streamFile,wiih_offset, 0x2e, ww.big_endian);
|
||||||
for (i=0; i < ww.channels; i++) {
|
for (i=0; i < ww.channels; i++) {
|
||||||
|
@ -978,29 +978,46 @@ static void apply_config(VGMSTREAM * vgmstream, winamp_song_config *current) {
|
|||||||
static int winampGetExtendedFileInfo_common(in_char* filename, char *metadata, char* ret, int retlen);
|
static int winampGetExtendedFileInfo_common(in_char* filename, char *metadata, char* ret, int retlen);
|
||||||
|
|
||||||
static double get_album_gain_volume(const in_char *fn) {
|
static double get_album_gain_volume(const in_char *fn) {
|
||||||
char replaygain_gain[64], replaygain_peak[64];
|
char replaygain[64];
|
||||||
double gain = 0.0;
|
double gain = 0.0;
|
||||||
int had_replaygain = 0;
|
int had_replaygain = 0;
|
||||||
if (settings.gain_type != REPLAYGAIN_NONE) {
|
if (settings.gain_type == REPLAYGAIN_NONE)
|
||||||
if (settings.gain_type == REPLAYGAIN_ALBUM && winampGetExtendedFileInfo_common((in_char *)fn, "replaygain_album_gain", replaygain_gain, sizeof(replaygain_gain))) {
|
return 1.0;
|
||||||
gain = atof(replaygain_gain);
|
|
||||||
|
replaygain[0] = '\0'; /* reset each time to make sure we read actual tags */
|
||||||
|
if (settings.gain_type == REPLAYGAIN_ALBUM
|
||||||
|
&& winampGetExtendedFileInfo_common((in_char *)fn, "replaygain_album_gain", replaygain, sizeof(replaygain))
|
||||||
|
&& replaygain[0] != '\0') {
|
||||||
|
gain = atof(replaygain);
|
||||||
had_replaygain = 1;
|
had_replaygain = 1;
|
||||||
}
|
}
|
||||||
if (!had_replaygain && winampGetExtendedFileInfo_common((in_char *)fn, "replaygain_track_gain", replaygain_gain, sizeof(replaygain_gain))) {
|
|
||||||
gain = atof(replaygain_gain);
|
replaygain[0] = '\0';
|
||||||
|
if (!had_replaygain
|
||||||
|
&& winampGetExtendedFileInfo_common((in_char *)fn, "replaygain_track_gain", replaygain, sizeof(replaygain))
|
||||||
|
&& replaygain[0] != '\0') {
|
||||||
|
gain = atof(replaygain);
|
||||||
had_replaygain = 1;
|
had_replaygain = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (had_replaygain) {
|
if (had_replaygain) {
|
||||||
double vol = pow(10.0, gain / 20.0), peak = 1.0;
|
double vol = pow(10.0, gain / 20.0);
|
||||||
if (settings.clip_type == REPLAYGAIN_ALBUM && winampGetExtendedFileInfo_common((in_char *)fn, "replaygain_album_peak", replaygain_peak, sizeof(replaygain_peak))) {
|
double peak = 1.0;
|
||||||
peak = atof(replaygain_peak);
|
|
||||||
|
replaygain[0] = '\0';
|
||||||
|
if (settings.clip_type == REPLAYGAIN_ALBUM
|
||||||
|
&& winampGetExtendedFileInfo_common((in_char *)fn, "replaygain_album_peak", replaygain, sizeof(replaygain))
|
||||||
|
&& replaygain[0] != '\0') {
|
||||||
|
peak = atof(replaygain);
|
||||||
}
|
}
|
||||||
else if (settings.clip_type != REPLAYGAIN_NONE && winampGetExtendedFileInfo_common((in_char *)fn, "replaygain_track_peak", replaygain_peak, sizeof(replaygain_peak))) {
|
else if (settings.clip_type != REPLAYGAIN_NONE
|
||||||
peak = atof(replaygain_peak);
|
&& winampGetExtendedFileInfo_common((in_char *)fn, "replaygain_track_peak", replaygain, sizeof(replaygain))
|
||||||
|
&& replaygain[0] != '\0') {
|
||||||
|
peak = atof(replaygain);
|
||||||
}
|
}
|
||||||
return peak != 1.0 ? min(vol, 1.0 / peak) : vol;
|
return peak != 1.0 ? min(vol, 1.0 / peak) : vol;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return 1.0;
|
return 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1648,6 +1665,8 @@ static int winampGetExtendedFileInfo_common(in_char* filename, char *metadata, c
|
|||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
|
//TODO: is this always needed for Winamp to use replaygain?
|
||||||
|
//strcpy(ret, "1.0"); //should set some default value?
|
||||||
return strcasecmp(metadata, "replaygain_track_gain") == 0 ? 1 : 0;
|
return strcasecmp(metadata, "replaygain_track_gain") == 0 ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user