mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-29 19:37:30 +01:00
commit
ebdfed9927
@ -222,7 +222,6 @@ static int ffmpeg_read(void *opaque, uint8_t *buf, int buf_size) {
|
||||
switch(data->config.type) {
|
||||
case FFMPEG_EA_XMA: ret = ffmpeg_custom_read_eaxma(data, buf, buf_size); break;
|
||||
case FFMPEG_SWITCH_OPUS: ret = ffmpeg_custom_read_switch_opus(data, buf, buf_size); break;
|
||||
case FFMPEG_BGW_ATRAC3: ret = ffmpeg_custom_read_bgw_atrac3(data, buf, buf_size); break;
|
||||
//case FFMPEG_EA_SCHL: ret = ffmpeg_custom_read_ea_schl(data, buf, buf_size); break;
|
||||
//case FFMPEG_SFH: ret = ffmpeg_custom_read_sfh(data, buf, buf_size); break;
|
||||
default: ret = ffmpeg_custom_read_standard(data, buf, buf_size); break;
|
||||
@ -291,7 +290,6 @@ static int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence) {
|
||||
switch(data->config.type) {
|
||||
case FFMPEG_EA_XMA: offset = ffmpeg_custom_seek_eaxma(data, offset); break;
|
||||
case FFMPEG_SWITCH_OPUS: offset = ffmpeg_custom_seek_switch_opus(data, offset); break;
|
||||
case FFMPEG_BGW_ATRAC3: offset = ffmpeg_custom_seek_bgw_atrac3(data, offset); break;
|
||||
//case FFMPEG_EA_SCHL: offset = ffmpeg_custom_seek_ea_schl(data, offset); break;
|
||||
//case FFMPEG_SFH: offset = ffmpeg_custom_seek_sfh(data, offset); break;
|
||||
default: offset = ffmpeg_custom_seek_standard(data, offset); break;
|
||||
@ -309,7 +307,6 @@ static int64_t ffmpeg_size(ffmpeg_codec_data * data) {
|
||||
switch(data->config.type) {
|
||||
case FFMPEG_EA_XMA: bytes = ffmpeg_custom_size_eaxma(data); break;
|
||||
case FFMPEG_SWITCH_OPUS: bytes = ffmpeg_custom_size_switch_opus(data); break;
|
||||
case FFMPEG_BGW_ATRAC3: bytes = ffmpeg_custom_size_bgw_atrac3(data); break;
|
||||
//case FFMPEG_EA_SCHL: bytes = ffmpeg_custom_size_ea_schl(data); break;
|
||||
//case FFMPEG_SFH: bytes = ffmpeg_custom_size_sfh(data); break;
|
||||
default: bytes = ffmpeg_custom_size_standard(data); break;
|
||||
@ -806,9 +803,6 @@ void free_ffmpeg(ffmpeg_codec_data *data) {
|
||||
close_streamfile(data->streamfile);
|
||||
data->streamfile = NULL;
|
||||
}
|
||||
if (data->config.key) {
|
||||
free(data->config.key);
|
||||
}
|
||||
free(data);
|
||||
}
|
||||
|
||||
|
@ -31,10 +31,6 @@ int ffmpeg_custom_read_switch_opus(ffmpeg_codec_data *data, uint8_t *buf, int bu
|
||||
int64_t ffmpeg_custom_seek_switch_opus(ffmpeg_codec_data *data, int64_t virtual_offset);
|
||||
int64_t ffmpeg_custom_size_switch_opus(ffmpeg_codec_data *data);
|
||||
|
||||
int ffmpeg_custom_read_bgw_atrac3(ffmpeg_codec_data *data, uint8_t *buf, int buf_size);
|
||||
int64_t ffmpeg_custom_seek_bgw_atrac3(ffmpeg_codec_data *data, int64_t virtual_offset);
|
||||
int64_t ffmpeg_custom_size_bgw_atrac3(ffmpeg_codec_data *data);
|
||||
|
||||
//int ffmpeg_custom_read_ea_schl(ffmpeg_codec_data *data, uint8_t *buf, int buf_size);
|
||||
//int64_t ffmpeg_custom_seek_ea_schl(ffmpeg_codec_data *data, int64_t virtual_offset);
|
||||
//int64_t ffmpeg_custom_size_ea_schl(ffmpeg_codec_data *data);
|
||||
|
@ -1,58 +0,0 @@
|
||||
#if 1
|
||||
#include "coding.h"
|
||||
#include "ffmpeg_decoder_utils.h"
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
|
||||
#define BGM_ATRAC3_FRAME_SIZE 0xC0
|
||||
|
||||
/**
|
||||
* Encrypted ATRAC3 used in BGW (Final Fantasy XI PC).
|
||||
* Info from Moogle Toolbox: https://sourceforge.net/projects/mogbox/
|
||||
*/
|
||||
|
||||
int ffmpeg_custom_read_bgw_atrac3(ffmpeg_codec_data *data, uint8_t *buf, int buf_size) {
|
||||
int i, ch;
|
||||
size_t bytes;
|
||||
size_t block_align = BGM_ATRAC3_FRAME_SIZE * data->config.channels;
|
||||
|
||||
|
||||
/* init key: first frame + modified channel header */
|
||||
if (data->config.key == NULL) {
|
||||
data->config.key = malloc(block_align);
|
||||
if (!data->config.key) return 0;
|
||||
|
||||
read_streamfile(data->config.key, data->real_start, block_align, data->streamfile);
|
||||
for (ch = 0; ch < data->config.channels; ch++) {
|
||||
uint32_t xor = get_32bitBE(data->config.key + ch*BGM_ATRAC3_FRAME_SIZE);
|
||||
put_32bitBE(data->config.key + ch*BGM_ATRAC3_FRAME_SIZE, xor ^ 0xA0024E9F);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* read normally and unXOR the data */
|
||||
bytes = read_streamfile(buf, data->real_offset, buf_size, data->streamfile);
|
||||
for (i = 0; i < bytes; i++) {
|
||||
int key_pos = (data->real_offset - data->real_start + i) % block_align;
|
||||
buf[i] = buf[i] ^ data->config.key[key_pos];
|
||||
}
|
||||
|
||||
|
||||
data->real_offset += bytes;
|
||||
return bytes;
|
||||
}
|
||||
|
||||
int64_t ffmpeg_custom_seek_bgw_atrac3(ffmpeg_codec_data *data, int64_t virtual_offset) {
|
||||
int64_t seek_virtual_offset = virtual_offset - data->header_size;
|
||||
|
||||
data->real_offset = data->real_start + seek_virtual_offset;
|
||||
return virtual_offset;
|
||||
}
|
||||
|
||||
int64_t ffmpeg_custom_size_bgw_atrac3(ffmpeg_codec_data *data) {
|
||||
return data->real_size + data->header_size;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
#endif
|
@ -47,7 +47,7 @@ int vorbis_custom_setup_init_wwise(STREAMFILE *streamFile, off_t start_offset, v
|
||||
size_t header_size, packet_size;
|
||||
vorbis_custom_config cfg = data->config;
|
||||
|
||||
if (cfg.setup_type == HEADER_TRIAD) {
|
||||
if (cfg.setup_type == WWV_HEADER_TRIAD) {
|
||||
/* read 3 Wwise packets with triad (id/comment/setup), each with a Wwise header */
|
||||
off_t offset = start_offset;
|
||||
|
||||
@ -132,17 +132,17 @@ static int get_packet_header(STREAMFILE *streamFile, off_t offset, wwise_header_
|
||||
|
||||
/* packet size doesn't include header size */
|
||||
switch(header_type) {
|
||||
case TYPE_8: /* size 4+4 */
|
||||
case WWV_TYPE_8: /* size 4+4 */
|
||||
*packet_size = (uint32_t)read_32bit(offset, streamFile);
|
||||
*granulepos = read_32bit(offset+4, streamFile);
|
||||
return 8;
|
||||
|
||||
case TYPE_6: /* size 4+2 */
|
||||
case WWV_TYPE_6: /* size 4+2 */
|
||||
*packet_size = (uint16_t)read_16bit(offset, streamFile);
|
||||
*granulepos = read_32bit(offset+2, streamFile);
|
||||
return 6;
|
||||
|
||||
case TYPE_2: /* size 2 */
|
||||
case WWV_TYPE_2: /* size 2 */
|
||||
*packet_size = (uint16_t)read_16bit(offset, streamFile);
|
||||
*granulepos = 0; /* granule is an arbitrary unit so we could use offset instead; libvorbis has no actually need it actually */
|
||||
return 2;
|
||||
@ -306,7 +306,7 @@ static int ww2ogg_generate_vorbis_packet(vgm_bitstream * ow, vgm_bitstream * iw,
|
||||
//VGM_ASSERT(granule < 0, "Wwise Vorbis: negative granule %i @ 0x%lx\n", granule, offset);
|
||||
|
||||
|
||||
if (data->config.packet_type == MODIFIED) {
|
||||
if (data->config.packet_type == WWV_MODIFIED) {
|
||||
/* rebuild first bits of packet type and window info (for the i-MDCT) */
|
||||
uint32_t packet_type = 0, mode_number = 0, remainder = 0;
|
||||
|
||||
@ -423,13 +423,13 @@ static int ww2ogg_generate_vorbis_setup(vgm_bitstream * ow, vgm_bitstream * iw,
|
||||
w_bits(ow, 8, codebook_count_less1);
|
||||
codebook_count = codebook_count_less1 + 1;
|
||||
|
||||
if (data->config.setup_type == FULL_SETUP) {
|
||||
if (data->config.setup_type == WWV_FULL_SETUP) {
|
||||
/* rebuild Wwise codebooks: untouched */
|
||||
for (i = 0; i < codebook_count; i++) {
|
||||
if(!ww2ogg_codebook_library_copy(ow, iw)) goto fail;
|
||||
}
|
||||
}
|
||||
else if (data->config.setup_type == INLINE_CODEBOOKS) {
|
||||
else if (data->config.setup_type == WWV_INLINE_CODEBOOKS) {
|
||||
/* rebuild Wwise codebooks: inline in simplified format */
|
||||
for (i = 0; i < codebook_count; i++) {
|
||||
if(!ww2ogg_codebook_library_rebuild(ow, iw, 0, streamFile)) goto fail;
|
||||
@ -456,7 +456,7 @@ static int ww2ogg_generate_vorbis_setup(vgm_bitstream * ow, vgm_bitstream * iw,
|
||||
w_bits(ow, 16, dummy_time_value);
|
||||
|
||||
|
||||
if (data->config.setup_type == FULL_SETUP) {
|
||||
if (data->config.setup_type == WWV_FULL_SETUP) {
|
||||
/* rest of setup is untouched, copy bits */
|
||||
uint32_t bitly = 0;
|
||||
uint32_t total_bits_read = iw->b_off;
|
||||
@ -1174,11 +1174,11 @@ static int load_wvc_array(uint8_t * buf, size_t bufsize, uint32_t codebook_id, w
|
||||
const wvc_info * wvc_list;
|
||||
|
||||
switch (setup_type) {
|
||||
case EXTERNAL_CODEBOOKS:
|
||||
case WWV_EXTERNAL_CODEBOOKS:
|
||||
wvc_list = wvc_list_standard;
|
||||
list_length = sizeof(wvc_list_standard) / sizeof(wvc_info);
|
||||
break;
|
||||
case AOTUV603_CODEBOOKS:
|
||||
case WWV_AOTUV603_CODEBOOKS:
|
||||
wvc_list = wvc_list_aotuv603;
|
||||
list_length = sizeof(wvc_list_standard) / sizeof(wvc_info);
|
||||
break;
|
||||
|
@ -43,6 +43,8 @@ static const char* extension_list[] = {
|
||||
"ast",
|
||||
"at3",
|
||||
"at9",
|
||||
"atsl3",
|
||||
"atx",
|
||||
"aud",
|
||||
"aus",
|
||||
"awc",
|
||||
@ -173,6 +175,7 @@ static const char* extension_list[] = {
|
||||
"lstm", //fake extension, for STMs
|
||||
"lwav", //fake extension, for WAVs
|
||||
|
||||
"mab",
|
||||
"matx",
|
||||
"mc3",
|
||||
"mca",
|
||||
@ -263,6 +266,7 @@ static const char* extension_list[] = {
|
||||
"sb5",
|
||||
"sb6",
|
||||
"sb7",
|
||||
"sbin",
|
||||
"sc",
|
||||
"scd",
|
||||
"sck",
|
||||
@ -377,6 +381,7 @@ static const char* extension_list[] = {
|
||||
"xvas",
|
||||
"xwav",
|
||||
"xwb",
|
||||
"xwc",
|
||||
"xwm", //FFmpeg, not parsed (XWMA)
|
||||
"xwma", //FFmpeg, not parsed (XWMA)
|
||||
"xws",
|
||||
@ -474,7 +479,7 @@ static const coding_info coding_info_list[] = {
|
||||
{coding_APPLE_IMA4, "Apple Quicktime 4-bit IMA ADPCM"},
|
||||
{coding_SNDS_IMA, "Heavy Iron .snds 4-bit IMA ADPCM"},
|
||||
{coding_OTNS_IMA, "Omikron: The Nomad Soul 4-bit IMA ADPCM"},
|
||||
{coding_FSB_IMA, "FSB multichannel 4-bit IMA ADPCM"},
|
||||
{coding_FSB_IMA, "FSB 4-bit IMA ADPCM"},
|
||||
{coding_WWISE_IMA, "Audiokinetic Wwise 4-bit IMA ADPCM"},
|
||||
{coding_REF_IMA, "Reflections 4-bit IMA ADPCM"},
|
||||
{coding_AWC_IMA, "Rockstar AWC 4-bit IMA ADPCM"},
|
||||
@ -947,6 +952,9 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_OGG_SNGW, "Ogg Vorbis (Capcom)"},
|
||||
{meta_OGG_ISD, "Ogg Vorbis (ISD)"},
|
||||
{meta_KMA9, "Koei Tecmo KMA9 header"},
|
||||
{meta_XWC, "Starbreeze XWC header"},
|
||||
{meta_SQEX_SAB, "Square-Enix SAB header"},
|
||||
{meta_SQEX_MAB, "Square-Enix MAB header"},
|
||||
|
||||
#ifdef VGM_USE_MP4V2
|
||||
{meta_MP4, "AAC header"},
|
||||
|
@ -95,6 +95,7 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) {
|
||||
|
||||
/* id, size, samples, hists-per-channel, stereo/interleaved data */
|
||||
case coding_EA_XA:
|
||||
//case coding_EA_XA_V2: /* handled in default */
|
||||
case coding_EA_XA_int:
|
||||
for (i = 0; i < vgmstream->channels; i++) {
|
||||
int is_interleaved = vgmstream->coding_type == coding_EA_XA_int;
|
||||
@ -106,7 +107,9 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) {
|
||||
|
||||
/* the block can have padding so find the channel size from num_samples */
|
||||
interleave = is_interleaved ? (block_samples / 28 * 0x0f) : 0;
|
||||
vgmstream->ch[i].offset = block_offset + 0x0c + vgmstream->channels*0x04 + i*interleave;
|
||||
|
||||
/* NOT channels*0x04, as seen in Superbike 2000 (PC) EA-XA v1 mono vids */
|
||||
vgmstream->ch[i].offset = block_offset + 0x0c + 2*0x04 + i*interleave;
|
||||
}
|
||||
|
||||
break;
|
||||
@ -146,6 +149,7 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) {
|
||||
for (i = 0; i < vgmstream->channels; i++) {
|
||||
off_t channel_start = read_32bit(block_offset + 0x0C + (0x04*i),streamFile);
|
||||
vgmstream->ch[i].offset = block_offset + 0x0C + (0x04*vgmstream->channels) + channel_start;
|
||||
VGM_LOG("ch=%x, off=%lx\n", i, vgmstream->ch[i].offset);
|
||||
}
|
||||
|
||||
/* read ADPCM history before each channel if needed (not actually read in sx.exe) */
|
||||
|
@ -276,6 +276,14 @@
|
||||
RelativePath=".\meta\ast.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\atsl3.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\atx.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\awc.c"
|
||||
>
|
||||
@ -1178,6 +1186,10 @@
|
||||
RelativePath=".\meta\sli.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\sps_n1.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\spt_spd.c"
|
||||
>
|
||||
@ -1186,6 +1198,10 @@
|
||||
RelativePath=".\meta\sqex_scd.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\sqex_sead.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\stm.c"
|
||||
>
|
||||
@ -1374,6 +1390,10 @@
|
||||
RelativePath=".\meta\xwb.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\xwc.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\ydsp.c"
|
||||
>
|
||||
@ -1470,10 +1490,6 @@
|
||||
RelativePath=".\coding\ffmpeg_decoder_utils_ea_schl.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\ffmpeg_decoder_utils_bgw_atrac3.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\ffmpeg_decoder_utils_ea_xma.c"
|
||||
>
|
||||
|
@ -112,7 +112,6 @@
|
||||
<ClCompile Include="coding\atrac9_decoder.c" />
|
||||
<ClCompile Include="coding\coding_utils.c" />
|
||||
<ClCompile Include="coding\ffmpeg_decoder.c" />
|
||||
<ClCompile Include="coding\ffmpeg_decoder_utils_bgw_atrac3.c" />
|
||||
<ClCompile Include="coding\ffmpeg_decoder_utils_ea_xma.c" />
|
||||
<ClCompile Include="coding\ffmpeg_decoder_utils_switch_opus.c" />
|
||||
<ClCompile Include="coding\ffmpeg_decoder_utils.c" />
|
||||
@ -163,6 +162,7 @@
|
||||
<ClCompile Include="meta\vawx.c" />
|
||||
<ClCompile Include="meta\seg.c" />
|
||||
<ClCompile Include="meta\sqex_scd.c" />
|
||||
<ClCompile Include="meta\sqex_sead.c" />
|
||||
<ClCompile Include="meta\tun.c" />
|
||||
<ClCompile Include="meta\txth.c" />
|
||||
<ClCompile Include="meta\wii_ras.c" />
|
||||
@ -186,6 +186,8 @@
|
||||
<ClCompile Include="meta\aix.c" />
|
||||
<ClCompile Include="meta\apple_caff.c" />
|
||||
<ClCompile Include="meta\ast.c" />
|
||||
<ClCompile Include="meta\atsl3.c" />
|
||||
<ClCompile Include="meta\atx.c" />
|
||||
<ClCompile Include="meta\baf.c" />
|
||||
<ClCompile Include="meta\bgw.c" />
|
||||
<ClCompile Include="meta\bik.c" />
|
||||
@ -376,6 +378,7 @@
|
||||
<ClCompile Include="meta\sdt.c" />
|
||||
<ClCompile Include="meta\sfl.c" />
|
||||
<ClCompile Include="meta\sli.c" />
|
||||
<ClCompile Include="meta\sps_n1.c" />
|
||||
<ClCompile Include="meta\spt_spd.c" />
|
||||
<ClCompile Include="meta\stm.c" />
|
||||
<ClCompile Include="meta\str_asr.c" />
|
||||
@ -417,6 +420,7 @@
|
||||
<ClCompile Include="meta\xss.c" />
|
||||
<ClCompile Include="meta\xvag.c" />
|
||||
<ClCompile Include="meta\xwb.c" />
|
||||
<ClCompile Include="meta\xwc.c" />
|
||||
<ClCompile Include="meta\ydsp.c" />
|
||||
<ClCompile Include="meta\zsd.c" />
|
||||
<ClCompile Include="meta\zwdsp.c" />
|
||||
|
@ -169,6 +169,12 @@
|
||||
<ClCompile Include="meta\ast.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\atsl3.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\atx.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\baf.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -718,6 +724,9 @@
|
||||
<ClCompile Include="meta\sli.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\sps_n1.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\spt_spd.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -835,6 +844,9 @@
|
||||
<ClCompile Include="meta\xwb.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\xwc.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\ydsp.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -1117,6 +1129,9 @@
|
||||
<ClCompile Include="meta\sqex_scd.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\sqex_sead.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\seg.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -1213,9 +1228,6 @@
|
||||
<ClCompile Include="coding\ffmpeg_decoder.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\ffmpeg_decoder_utils_bgw_atrac3.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\ffmpeg_decoder_utils_ea_xma.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
@ -177,7 +177,7 @@ static const adxkey_info adxkey8_list[] = {
|
||||
{0x4133,0x5a01,0x5723, NULL,0}, // ?
|
||||
|
||||
/* Shounen Onmyouji: Tsubasa yo Ima, Sora e Kaere [PS2] */
|
||||
{0x55d9,0x46d3,0x5b01, NULL,0}, // ?
|
||||
{0x55d9,0x46d3,0x5b01, "SONMYOJI",0},
|
||||
|
||||
};
|
||||
|
||||
|
76
src/meta/atsl3.c
Normal file
76
src/meta/atsl3.c
Normal file
@ -0,0 +1,76 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
static STREAMFILE* setup_atsl3_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size);
|
||||
|
||||
/* .ATSL3 - Koei Tecmo container of multiple .AT3 [One Piece Pirate Warriors (PS3)] */
|
||||
VGMSTREAM * init_vgmstream_atsl3(STREAMFILE *streamFile) {
|
||||
VGMSTREAM *vgmstream = NULL;
|
||||
STREAMFILE *temp_streamFile = NULL;
|
||||
int total_subsongs, target_subsong = streamFile->stream_index;
|
||||
off_t subfile_offset;
|
||||
size_t subfile_size, header_size, entry_size;
|
||||
|
||||
/* check extensions */
|
||||
if ( !check_extensions(streamFile,"atsl3"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00,streamFile) != 0x4154534C) /* "ATSL" */
|
||||
goto fail;
|
||||
|
||||
/* main header (LE) */
|
||||
header_size = read_32bitLE(0x04,streamFile);
|
||||
/* 0x08/0c: flags?, 0x10: some size? */
|
||||
total_subsongs = read_32bitLE(0x14,streamFile);
|
||||
entry_size = read_32bitLE(0x18,streamFile);
|
||||
/* 0x1c: null, 0x20: subheader size, 0x24/28: null */
|
||||
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
if (target_subsong > total_subsongs || total_subsongs <= 0) goto fail;
|
||||
|
||||
/* entry header (BE) */
|
||||
/* 0x00: id */
|
||||
subfile_offset = read_32bitBE(header_size + (target_subsong-1)*entry_size + 0x04,streamFile);
|
||||
subfile_size = read_32bitBE(header_size + (target_subsong-1)*entry_size + 0x08,streamFile);
|
||||
/* 0x08+: sample rate/num_samples/loop_start/etc, matching subfile header */
|
||||
/* some kind of seek/switch table follows */
|
||||
|
||||
temp_streamFile = setup_atsl3_streamfile(streamFile, subfile_offset,subfile_size);
|
||||
if (!temp_streamFile) goto fail;
|
||||
|
||||
/* init the VGMSTREAM */
|
||||
vgmstream = init_vgmstream_riff(temp_streamFile);
|
||||
if (!vgmstream) goto fail;
|
||||
vgmstream->num_streams = total_subsongs;
|
||||
|
||||
close_streamfile(temp_streamFile);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_streamFile);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static STREAMFILE* setup_atsl3_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size) {
|
||||
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
|
||||
|
||||
/* setup subfile */
|
||||
new_streamFile = open_wrap_streamfile(streamFile);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,"at3");
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
return temp_streamFile;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_streamFile);
|
||||
return NULL;
|
||||
}
|
109
src/meta/atx.c
Normal file
109
src/meta/atx.c
Normal file
@ -0,0 +1,109 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
#define ATX_MAX_SEGMENTS 2
|
||||
|
||||
static STREAMFILE* setup_atx_streamfile(STREAMFILE *streamFile);
|
||||
|
||||
/* .ATX - Media.Vision's segmented RIFF AT3 wrapper [Senjo no Valkyria 3 (PSP), Shining Blade (PSP)] */
|
||||
VGMSTREAM * init_vgmstream_atx(STREAMFILE *streamFile) {
|
||||
VGMSTREAM *vgmstream = NULL;
|
||||
STREAMFILE *temp_streamFile = NULL;
|
||||
|
||||
|
||||
/* check extensions */
|
||||
if ( !check_extensions(streamFile,"atx"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00,streamFile) != 0x41504133) /* "APA3" */
|
||||
goto fail;
|
||||
|
||||
/* .ATX is made of subfile segments, handled by the streamFile.
|
||||
* Each segment has a header/footer, and part of the whole data
|
||||
* (i.e. ATRAC3 data ends in a subfile and continues in the next) */
|
||||
temp_streamFile = setup_atx_streamfile(streamFile);
|
||||
if (!temp_streamFile) goto fail;
|
||||
|
||||
vgmstream = init_vgmstream_riff(temp_streamFile);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
close_streamfile(temp_streamFile);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_streamFile);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static STREAMFILE* setup_atx_streamfile(STREAMFILE *streamFile) {
|
||||
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
|
||||
STREAMFILE *segment_streamFiles[ATX_MAX_SEGMENTS] = {0};
|
||||
char filename[PATH_LIMIT];
|
||||
size_t filename_len;
|
||||
int i, num_segments = 0;
|
||||
size_t riff_size;
|
||||
VGM_LOG("1\n");
|
||||
|
||||
if (read_16bitLE(0x1c,streamFile) != 0) goto fail; /* this must be first segment */
|
||||
if (read_16bitLE(0x1e,streamFile) < 1 || read_16bitLE(0x1e,streamFile) > ATX_MAX_SEGMENTS) goto fail;
|
||||
num_segments = read_16bitLE(0x1e,streamFile);
|
||||
|
||||
/* expected segment name: X_XXX_XXX_0n.ATX, starting from n=1 */
|
||||
get_streamfile_name(streamFile, filename,PATH_LIMIT);
|
||||
filename_len = strlen(filename);
|
||||
if (filename_len < 7 || filename[filename_len - 5] != '1') goto fail;
|
||||
|
||||
/* setup segments (could avoid reopening first segment but meh) */
|
||||
for (i = 0; i < num_segments; i++) {
|
||||
off_t subfile_offset;
|
||||
size_t subfile_size;
|
||||
VGM_LOG("loop\n");
|
||||
filename[filename_len - 5] = ('0'+i+1); /* ghetto digit conversion */
|
||||
new_streamFile = open_stream_name(streamFile, filename);
|
||||
if (!new_streamFile) goto fail;
|
||||
segment_streamFiles[i] = new_streamFile;
|
||||
|
||||
if (read_32bitBE(0x00,segment_streamFiles[i]) != 0x41504133) /* "APA3" */
|
||||
goto fail;
|
||||
|
||||
/* parse block/segment header (other Media.Vision's files use it too) */
|
||||
subfile_offset = read_32bitLE(0x08,segment_streamFiles[i]); /* header size */
|
||||
subfile_size = read_32bitLE(0x14,segment_streamFiles[i]); /* can be 0 in other containers */
|
||||
VGM_LOG("subfile: %lx, %x\n", subfile_offset, subfile_size);
|
||||
if (read_16bitLE(0x1c,segment_streamFiles[i]) != i)
|
||||
goto fail; /* segment sequence */
|
||||
/* 0x04: block size (should match subfile_size in .ATX) */
|
||||
/* 0x0c: flags? also in other files, 0x10/18: null, 0x1e: segments */
|
||||
|
||||
/* clamp to ignore header/footer during next reads */
|
||||
new_streamFile = open_clamp_streamfile(segment_streamFiles[i], subfile_offset,subfile_size);
|
||||
if (!new_streamFile) goto fail;
|
||||
segment_streamFiles[i] = new_streamFile;
|
||||
}
|
||||
|
||||
/* setup with all segments and clamp further using riff_size (last segment has padding) */
|
||||
riff_size = read_32bitLE(read_32bitLE(0x08,streamFile) + 0x04,streamFile) + 0x08;
|
||||
|
||||
new_streamFile = open_multifile_streamfile(segment_streamFiles, num_segments);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
new_streamFile = open_clamp_streamfile(temp_streamFile, 0,riff_size);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
new_streamFile = open_fakename_streamfile(temp_streamFile, NULL, "at3");
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
return temp_streamFile;
|
||||
|
||||
fail:
|
||||
if (!temp_streamFile) {
|
||||
for (i = 0; i < num_segments; i++)
|
||||
close_streamfile(segment_streamFiles[i]);
|
||||
} else {
|
||||
close_streamfile(temp_streamFile); /* closes all segments */
|
||||
}
|
||||
return NULL;
|
||||
}
|
@ -7,7 +7,7 @@ typedef struct {
|
||||
int is_encrypted;
|
||||
int is_music;
|
||||
|
||||
int total_streams;
|
||||
int total_subsongs;
|
||||
|
||||
int channel_count;
|
||||
int sample_rate;
|
||||
@ -47,7 +47,8 @@ VGMSTREAM * init_vgmstream_awc(STREAMFILE *streamFile) {
|
||||
|
||||
vgmstream->sample_rate = awc.sample_rate;
|
||||
vgmstream->num_samples = awc.num_samples;
|
||||
vgmstream->num_streams = awc.total_streams;
|
||||
vgmstream->num_streams = awc.total_subsongs;
|
||||
vgmstream->stream_size = awc.stream_size;
|
||||
vgmstream->meta_type = meta_AWC;
|
||||
|
||||
|
||||
@ -113,7 +114,7 @@ static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc) {
|
||||
int i, ch, entries;
|
||||
uint32_t flags, info_header, tag_count = 0, tags_skip = 0;
|
||||
off_t off;
|
||||
int target_stream = streamFile->stream_index;
|
||||
int target_subsong = streamFile->stream_index;
|
||||
|
||||
memset(awc,0,sizeof(awc_header));
|
||||
|
||||
@ -161,13 +162,13 @@ static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc) {
|
||||
* Music seems layered (N-1/2 stereo pairs), maybe set with events? */
|
||||
awc->is_music = (read_32bit(off + 0x00,streamFile) & 0x1FFFFFFF) == 0x00000000;
|
||||
if (awc->is_music) { /* all streams except id 0 is a channel */
|
||||
awc->total_streams = 1;
|
||||
target_stream = 1; /* we only need id 0, though channels may have its own tags/chunks */
|
||||
awc->total_subsongs = 1;
|
||||
target_subsong = 1; /* we only need id 0, though channels may have its own tags/chunks */
|
||||
}
|
||||
else { /* each stream is a single sound */
|
||||
awc->total_streams = entries;
|
||||
if (target_stream == 0) target_stream = 1;
|
||||
if (target_stream < 0 || target_stream > awc->total_streams || awc->total_streams < 1) goto fail;
|
||||
awc->total_subsongs = entries;
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
if (target_subsong < 0 || target_subsong > awc->total_subsongs || awc->total_subsongs < 1) goto fail;
|
||||
}
|
||||
|
||||
|
||||
@ -176,7 +177,7 @@ static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc) {
|
||||
info_header = read_32bit(off + 0x04*i, streamFile);
|
||||
tag_count = (info_header >> 29) & 0x7; /* 3b */
|
||||
//id = (info_header >> 0) & 0x1FFFFFFF; /* 29b */
|
||||
if (target_stream-1 == i)
|
||||
if (target_subsong-1 == i)
|
||||
break;
|
||||
tags_skip += tag_count; /* tags to skip to reach target's tags, in the next header */
|
||||
}
|
||||
|
103
src/meta/bgw.c
103
src/meta/bgw.c
@ -1,9 +1,14 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
static STREAMFILE* setup_bgw_atrac3_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, size_t frame_size, int channels);
|
||||
|
||||
|
||||
/* BGW - from Final Fantasy XI (PC) music files */
|
||||
VGMSTREAM * init_vgmstream_bgw(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
STREAMFILE *temp_streamFile = NULL;
|
||||
uint32_t codec, file_size, block_size, sample_rate, block_align;
|
||||
int32_t loop_start;
|
||||
off_t start_offset;
|
||||
@ -32,18 +37,15 @@ VGMSTREAM * init_vgmstream_bgw(STREAMFILE *streamFile) {
|
||||
channel_count = read_8bit(0x2e,streamFile);
|
||||
block_align = read_8bit(0x2f,streamFile);
|
||||
|
||||
|
||||
/* check file size with header value */
|
||||
if (file_size != get_streamfile_size(streamFile))
|
||||
goto fail;
|
||||
|
||||
loop_flag = (loop_start > 0);
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
/* fill in the vital statistics */
|
||||
vgmstream->meta_type = meta_FFXI_BGW;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
|
||||
@ -65,7 +67,7 @@ VGMSTREAM * init_vgmstream_bgw(STREAMFILE *streamFile) {
|
||||
case 3: { /* ATRAC3 (encrypted) */
|
||||
uint8_t buf[0x100];
|
||||
int bytes, joint_stereo, skip_samples;
|
||||
ffmpeg_custom_config cfg;
|
||||
size_t data_size = file_size - start_offset;
|
||||
|
||||
vgmstream->num_samples = block_size; /* atrac3_bytes_to_samples gives the same value */
|
||||
if (loop_flag) {
|
||||
@ -77,18 +79,18 @@ VGMSTREAM * init_vgmstream_bgw(STREAMFILE *streamFile) {
|
||||
joint_stereo = 0;
|
||||
skip_samples = 0;
|
||||
|
||||
bytes = ffmpeg_make_riff_atrac3(buf, 0x100, vgmstream->num_samples, file_size - start_offset, vgmstream->channels, vgmstream->sample_rate, block_align, joint_stereo, skip_samples);
|
||||
bytes = ffmpeg_make_riff_atrac3(buf, 0x100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_align, joint_stereo, skip_samples);
|
||||
if (bytes <= 0) goto fail;
|
||||
|
||||
memset(&cfg, 0, sizeof(ffmpeg_custom_config));
|
||||
cfg.type = FFMPEG_BGW_ATRAC3;
|
||||
cfg.channels = vgmstream->channels;
|
||||
temp_streamFile = setup_bgw_atrac3_streamfile(streamFile, start_offset,data_size, 0xC0,channel_count);
|
||||
if (!temp_streamFile) goto fail;
|
||||
|
||||
vgmstream->codec_data = init_ffmpeg_config(streamFile, buf,bytes, start_offset,file_size - start_offset, &cfg);
|
||||
vgmstream->codec_data = init_ffmpeg_header_offset(temp_streamFile, buf,bytes, 0,data_size);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
close_streamfile(temp_streamFile);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
@ -98,17 +100,17 @@ VGMSTREAM * init_vgmstream_bgw(STREAMFILE *streamFile) {
|
||||
}
|
||||
|
||||
|
||||
/* open the file for reading */
|
||||
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
|
||||
goto fail;
|
||||
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_streamFile);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* SPW (SEWave) - from PlayOnline viewer for Final Fantasy XI (PC) */
|
||||
VGMSTREAM * init_vgmstream_spw(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
@ -118,8 +120,8 @@ VGMSTREAM * init_vgmstream_spw(STREAMFILE *streamFile) {
|
||||
|
||||
int channel_count, loop_flag = 0;
|
||||
|
||||
/* check extensions */
|
||||
if ( !check_extensions(streamFile, "spw") )
|
||||
/* check extensions */
|
||||
if ( !check_extensions(streamFile, "spw") )
|
||||
goto fail;
|
||||
|
||||
/* check header */
|
||||
@ -127,10 +129,6 @@ VGMSTREAM * init_vgmstream_spw(STREAMFILE *streamFile) {
|
||||
read_32bitBE(4,streamFile) != 0x76650000) /* "ve\0\0" */
|
||||
goto fail;
|
||||
|
||||
/* check file size with header value */
|
||||
if (read_32bitLE(0x8,streamFile) != get_streamfile_size(streamFile))
|
||||
goto fail;
|
||||
|
||||
file_size = read_32bitLE(0x08,streamFile);
|
||||
codec = read_32bitLE(0x0c,streamFile);
|
||||
/*file_id = read_32bitLE(0x10,streamFile);*/
|
||||
@ -144,17 +142,15 @@ VGMSTREAM * init_vgmstream_spw(STREAMFILE *streamFile) {
|
||||
block_align = read_8bit(0x2b,streamFile);
|
||||
/*0x2c: unk (0x01 when PCM, 0x10 when VAG?) */
|
||||
|
||||
/* check file size with header value */
|
||||
if (file_size != get_streamfile_size(streamFile))
|
||||
goto fail;
|
||||
|
||||
loop_flag = (loop_start > 0);
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
/* fill in the vital statistics */
|
||||
vgmstream->meta_type = meta_FFXI_SPW;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
|
||||
@ -171,7 +167,7 @@ VGMSTREAM * init_vgmstream_spw(STREAMFILE *streamFile) {
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
|
||||
case 1: /* PCM */
|
||||
vgmstream->coding_type = coding_PCM16LE;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
@ -182,8 +178,9 @@ VGMSTREAM * init_vgmstream_spw(STREAMFILE *streamFile) {
|
||||
vgmstream->loop_start_sample = (loop_start-1);
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
@ -199,3 +196,61 @@ fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
#define BGW_KEY_MAX (0xC0*2)
|
||||
|
||||
typedef struct {
|
||||
uint8_t key[BGW_KEY_MAX];
|
||||
size_t key_size;
|
||||
} bgw_decryption_data;
|
||||
|
||||
/* Encrypted ATRAC3 info from Moogle Toolbox (https://sourceforge.net/projects/mogbox/) */
|
||||
static size_t bgw_decryption_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, bgw_decryption_data* data) {
|
||||
size_t bytes_read;
|
||||
int i;
|
||||
|
||||
bytes_read = streamfile->read(streamfile, dest, offset, length);
|
||||
|
||||
/* decrypt data (xor) */
|
||||
for (i = 0; i < bytes_read; i++) {
|
||||
dest[i] ^= data->key[(offset + i) % data->key_size];
|
||||
}
|
||||
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
static STREAMFILE* setup_bgw_atrac3_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, size_t frame_size, int channels) {
|
||||
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
|
||||
bgw_decryption_data io_data = {0};
|
||||
size_t io_data_size = sizeof(bgw_decryption_data);
|
||||
int ch;
|
||||
|
||||
/* setup decryption with key (first frame + modified channel header) */
|
||||
if (frame_size*channels == 0 || frame_size*channels > BGW_KEY_MAX) goto fail;
|
||||
|
||||
io_data.key_size = read_streamfile(io_data.key, subfile_offset, frame_size*channels, streamFile);
|
||||
for (ch = 0; ch < channels; ch++) {
|
||||
uint32_t xor = get_32bitBE(io_data.key + frame_size*ch);
|
||||
put_32bitBE(io_data.key + frame_size*ch, xor ^ 0xA0024E9F);
|
||||
}
|
||||
|
||||
/* setup subfile */
|
||||
new_streamFile = open_wrap_streamfile(streamFile);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, bgw_decryption_read);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
return temp_streamFile;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_streamFile);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -2,13 +2,14 @@
|
||||
#include "../coding/coding.h"
|
||||
#include "../util.h"
|
||||
|
||||
static int bink_get_info(STREAMFILE *streamFile, int * out_total_streams, int * out_channel_count, int * out_sample_rate, int * out_num_samples);
|
||||
static int bink_get_info(STREAMFILE *streamFile, int * out_total_streams, size_t *out_stream_size, int * out_channel_count, int * out_sample_rate, int * out_num_samples);
|
||||
|
||||
/* BINK 1/2 - RAD Game Tools movies (audio/video format) */
|
||||
VGMSTREAM * init_vgmstream_bik(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
int channel_count = 0, loop_flag = 0, sample_rate = 0, num_samples = 0, total_streams = 0;
|
||||
int stream_index = streamFile->stream_index;
|
||||
int channel_count = 0, loop_flag = 0, sample_rate = 0, num_samples = 0;
|
||||
int total_subsongs = 0, stream_index = streamFile->stream_index;
|
||||
size_t stream_size;
|
||||
|
||||
|
||||
/* check extension, case insensitive (bika = manually demuxed audio) */
|
||||
@ -19,7 +20,7 @@ VGMSTREAM * init_vgmstream_bik(STREAMFILE *streamFile) {
|
||||
(read_32bitBE(0x00,streamFile) & 0xffffff00) != 0x4B423200 ) goto fail;
|
||||
|
||||
/* find target stream info and samples */
|
||||
if (!bink_get_info(streamFile, &total_streams, &channel_count, &sample_rate, &num_samples))
|
||||
if (!bink_get_info(streamFile, &total_subsongs, &stream_size, &channel_count, &sample_rate, &num_samples))
|
||||
goto fail;
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
@ -29,7 +30,8 @@ VGMSTREAM * init_vgmstream_bik(STREAMFILE *streamFile) {
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = num_samples;
|
||||
vgmstream->num_streams = total_streams;
|
||||
vgmstream->num_streams = total_subsongs;
|
||||
vgmstream->stream_size = stream_size;
|
||||
vgmstream->meta_type = meta_BINK;
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
@ -58,12 +60,13 @@ fail:
|
||||
* as they are not in the main header. The header for BINK1 and 2 is the same.
|
||||
* (a ~3 min movie needs ~6000-7000 frames = fseeks, should be fast enough)
|
||||
*/
|
||||
static int bink_get_info(STREAMFILE *streamFile, int * out_total_streams, int * out_channel_count, int * out_sample_rate, int * out_num_samples) {
|
||||
static int bink_get_info(STREAMFILE *streamFile, int * out_total_subsongs, size_t * out_stream_size, int * out_channel_count, int * out_sample_rate, int * out_num_samples) {
|
||||
uint32_t *offsets = NULL;
|
||||
uint32_t num_frames, num_samples_b = 0;
|
||||
off_t cur_offset;
|
||||
int i, j, sample_rate, channel_count;
|
||||
int total_streams, target_stream = streamFile->stream_index;
|
||||
int total_subsongs, target_subsong = streamFile->stream_index;
|
||||
size_t stream_size = 0;
|
||||
|
||||
size_t filesize = get_streamfile_size(streamFile);
|
||||
uint32_t signature = (read_32bitBE(0x00,streamFile) & 0xffffff00);
|
||||
@ -76,20 +79,20 @@ static int bink_get_info(STREAMFILE *streamFile, int * out_total_streams, int *
|
||||
if (num_frames == 0 || num_frames > 0x100000) goto fail; /* something must be off (avoids big allocs below) */
|
||||
|
||||
/* multichannel/multilanguage audio is usually N streams of stereo/mono, no way to know channel layout */
|
||||
total_streams = read_32bitLE(0x28,streamFile);
|
||||
if (target_stream == 0) target_stream = 1;
|
||||
if (target_stream < 0 || target_stream > total_streams || total_streams < 1 || total_streams > 255) goto fail;
|
||||
total_subsongs = read_32bitLE(0x28,streamFile);
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1 || total_subsongs > 255) goto fail;
|
||||
|
||||
/* find stream info and position in offset table */
|
||||
cur_offset = 0x2c;
|
||||
if ((signature == 0x42494B00 && (revision == 0x6b)) || /* k */
|
||||
(signature == 0x4B423200 && (revision == 0x69 || revision == 0x6a || revision == 0x6b))) /* i,j,k */
|
||||
cur_offset += 0x04; /* unknown v2 header field */
|
||||
cur_offset += 0x04*total_streams; /* skip streams max packet bytes */
|
||||
sample_rate = (uint16_t)read_16bitLE(cur_offset+0x04*(target_stream-1)+0x00,streamFile);
|
||||
channel_count = (uint16_t)read_16bitLE(cur_offset+0x04*(target_stream-1)+0x02,streamFile) & 0x2000 ? 2 : 1; /* stereo flag */
|
||||
cur_offset += 0x04*total_streams; /* skip streams info */
|
||||
cur_offset += 0x04*total_streams; /* skip streams ids */
|
||||
cur_offset += 0x04*total_subsongs; /* skip streams max packet bytes */
|
||||
sample_rate = (uint16_t)read_16bitLE(cur_offset+0x04*(target_subsong-1)+0x00,streamFile);
|
||||
channel_count = (uint16_t)read_16bitLE(cur_offset+0x04*(target_subsong-1)+0x02,streamFile) & 0x2000 ? 2 : 1; /* stereo flag */
|
||||
cur_offset += 0x04*total_subsongs; /* skip streams info */
|
||||
cur_offset += 0x04*total_subsongs; /* skip streams ids */
|
||||
|
||||
|
||||
/* read frame offsets in a buffer, to avoid fseeking to the table back and forth */
|
||||
@ -111,10 +114,11 @@ static int bink_get_info(STREAMFILE *streamFile, int * out_total_streams, int *
|
||||
cur_offset = offsets[i];
|
||||
|
||||
/* read audio packet headers per stream */
|
||||
for (j=0; j < total_streams; j++) {
|
||||
for (j=0; j < total_subsongs; j++) {
|
||||
uint32_t ap_size = read_32bitLE(cur_offset+0x00,streamFile); /* not counting this int */
|
||||
|
||||
if (j == target_stream-1) {
|
||||
if (j == target_subsong-1) {
|
||||
stream_size += 0x04 + ap_size;
|
||||
if (ap_size > 0)
|
||||
num_samples_b += read_32bitLE(cur_offset+0x04,streamFile); /* decoded samples in bytes */
|
||||
break; /* next frame */
|
||||
@ -128,7 +132,8 @@ static int bink_get_info(STREAMFILE *streamFile, int * out_total_streams, int *
|
||||
free(offsets);
|
||||
|
||||
|
||||
if (out_total_streams) *out_total_streams = total_streams;
|
||||
if (out_total_subsongs) *out_total_subsongs = total_subsongs;
|
||||
if (out_stream_size) *out_stream_size = stream_size;
|
||||
if (out_sample_rate) *out_sample_rate = sample_rate;
|
||||
if (out_channel_count) *out_channel_count = channel_count;
|
||||
//todo returns a few more samples (~48) than binkconv.exe?
|
||||
|
@ -7,7 +7,8 @@ VGMSTREAM * init_vgmstream_flx(STREAMFILE *streamFile) {
|
||||
off_t start_offset, stream_offset = 0;
|
||||
size_t data_size;
|
||||
int loop_flag, channel_count, codec;
|
||||
int total_streams = 0, target_stream = streamFile->stream_index;
|
||||
int total_subsongs = 0, target_subsong = streamFile->stream_index;
|
||||
size_t stream_size = 0;
|
||||
|
||||
|
||||
/* check extensions (.flx: name of archive, files inside don't have extensions) */
|
||||
@ -24,23 +25,26 @@ VGMSTREAM * init_vgmstream_flx(STREAMFILE *streamFile) {
|
||||
|| read_32bitLE(0x58,streamFile) != get_streamfile_size(streamFile))
|
||||
goto fail;
|
||||
|
||||
if (target_stream == 0) target_stream = 1;
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
|
||||
for (i = 0; i < entries; i++) {
|
||||
off_t entry_offset = read_32bitLE(offset + 0x00, streamFile);
|
||||
/* 0x04: stream size */
|
||||
size_t entry_size = read_32bitLE(offset + 0x04, streamFile);
|
||||
offset += 0x08;
|
||||
|
||||
if (entry_offset != 0x00)
|
||||
total_streams++; /* many entries are empty */
|
||||
if (total_streams == target_stream && stream_offset == 0)
|
||||
total_subsongs++; /* many entries are empty */
|
||||
if (total_subsongs == target_subsong && stream_offset == 0) {
|
||||
stream_offset = entry_offset; /* found but let's keep adding total_streams */
|
||||
stream_size = entry_size;
|
||||
}
|
||||
}
|
||||
if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail;
|
||||
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
|
||||
if (stream_offset == 0x00) goto fail;
|
||||
}
|
||||
else {
|
||||
stream_offset = 0x00;
|
||||
stream_size = get_streamfile_size(streamFile);
|
||||
}
|
||||
|
||||
if (read_32bitLE(stream_offset + 0x30,streamFile) != 0x10)
|
||||
@ -57,7 +61,8 @@ VGMSTREAM * init_vgmstream_flx(STREAMFILE *streamFile) {
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = read_32bitLE(stream_offset + 0x2c,streamFile);
|
||||
vgmstream->num_streams = total_streams;
|
||||
vgmstream->num_streams = total_subsongs;
|
||||
vgmstream->stream_size = stream_size;
|
||||
vgmstream->meta_type = meta_PC_FLX;
|
||||
|
||||
switch(codec) {
|
||||
|
@ -100,12 +100,12 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) {
|
||||
off_t start_offset;
|
||||
size_t custom_data_offset;
|
||||
int loop_flag = 0;
|
||||
int target_stream = streamFile->stream_index;
|
||||
int target_subsong = streamFile->stream_index;
|
||||
fsb_header fsb = {0};
|
||||
|
||||
|
||||
/* check extensions (.wii: fsb4_wav? .bnk = Hard Corps Uprising PS3) */
|
||||
if ( !check_extensions(streamFile, "fsb,wii,bnk") )
|
||||
/* check extensions (.bnk = Hard Corps Uprising PS3) */
|
||||
if ( !check_extensions(streamFile, "fsb,bnk") )
|
||||
goto fail;
|
||||
|
||||
/* check header */
|
||||
@ -185,8 +185,8 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) {
|
||||
}
|
||||
|
||||
if (fsb.sample_header_size < fsb.sample_header_min) goto fail;
|
||||
if (target_stream == 0) target_stream = 1;
|
||||
if (target_stream < 0 || target_stream > fsb.total_subsongs || fsb.total_subsongs < 1) goto fail;
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
if (target_subsong < 0 || target_subsong > fsb.total_subsongs || fsb.total_subsongs < 1) goto fail;
|
||||
|
||||
/* sample header (N-stream) */
|
||||
{
|
||||
@ -210,7 +210,7 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) {
|
||||
/* FSB3.1/4: 0x40:mindistance 0x44:maxdistance 0x48:varfreq/size_32bits 0x4c:varvol 0x4e:fsb.varpan */
|
||||
/* FSB3/4: 0x50:extended_data size_32bits (not always given) */
|
||||
|
||||
if (i+1 == target_stream) /* d_off found */
|
||||
if (i+1 == target_subsong) /* d_off found */
|
||||
break;
|
||||
|
||||
s_off += stream_header_size;
|
||||
@ -259,6 +259,7 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) {
|
||||
vgmstream->loop_start_sample = fsb.loop_start;
|
||||
vgmstream->loop_end_sample = fsb.loop_end;
|
||||
vgmstream->num_streams = fsb.total_subsongs;
|
||||
vgmstream->stream_size = fsb.stream_size;
|
||||
vgmstream->meta_type = fsb.meta_type;
|
||||
if (fsb.name_offset)
|
||||
read_string(vgmstream->stream_name,fsb.name_size+1, fsb.name_offset,streamFile);
|
||||
@ -289,8 +290,9 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) {
|
||||
|
||||
vgmstream->coding_type = coding_XBOX;
|
||||
vgmstream->layout_type = layout_none;
|
||||
/* "interleaved header" IMA, only used with >2ch (ex. Blade Kitten 5.1) */
|
||||
if (vgmstream->channels > 2)
|
||||
/* "interleaved header" IMA, only used with >2ch (ex. Blade Kitten 6ch)
|
||||
* or (seemingly) when flag is used (ex. Dead to Rights 2 (Xbox) 2ch in FSB3.1 */
|
||||
if (vgmstream->channels > 2 || (fsb.mode & FSOUND_MULTICHANNEL))
|
||||
vgmstream->coding_type = coding_FSB_IMA;
|
||||
}
|
||||
else if (fsb.mode & FSOUND_VAG) { /* FSB1: Jurassic Park Operation Genesis (PS2), FSB4: Spider Man Web of Shadows (PSP) */
|
||||
@ -363,13 +365,15 @@ fail:
|
||||
}
|
||||
|
||||
|
||||
static STREAMFILE* setup_fsb4_wav_streamfile(STREAMFILE *streamfile, off_t subfile_offset, size_t subfile_size);
|
||||
|
||||
/* FSB4 with "\0WAV" Header, found in Deadly Creatures (Wii).
|
||||
* Has a 0x10 BE header that holds the filesize (unsure if this is from a proper rip). */
|
||||
VGMSTREAM * init_vgmstream_fsb4_wav(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
STREAMFILE *custom_streamFile = NULL;
|
||||
off_t custom_start = 0x10;
|
||||
size_t custom_size = get_streamfile_size(streamFile) - 0x10 - 0x10; //todo
|
||||
STREAMFILE *test_streamFile = NULL;
|
||||
off_t subfile_start = 0x10;
|
||||
size_t subfile_size = get_streamfile_size(streamFile) - 0x10 - 0x10; //todo
|
||||
|
||||
/* check extensions */
|
||||
if ( !check_extensions(streamFile, "fsb,wii") )
|
||||
@ -379,17 +383,41 @@ VGMSTREAM * init_vgmstream_fsb4_wav(STREAMFILE *streamFile) {
|
||||
goto fail;
|
||||
|
||||
/* parse FSB subfile */
|
||||
custom_streamFile = open_clamp_streamfile(open_wrap_streamfile(streamFile), custom_start,custom_size);
|
||||
if (!custom_streamFile) goto fail;
|
||||
test_streamFile = setup_fsb4_wav_streamfile(streamFile, subfile_start,subfile_size);
|
||||
if (!test_streamFile) goto fail;
|
||||
|
||||
vgmstream = init_vgmstream_fsb(custom_streamFile);
|
||||
vgmstream = init_vgmstream_fsb(test_streamFile);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
close_streamfile(custom_streamFile);
|
||||
/* init the VGMSTREAM */
|
||||
close_streamfile(test_streamFile);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(custom_streamFile);
|
||||
close_streamfile(test_streamFile);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static STREAMFILE* setup_fsb4_wav_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size) {
|
||||
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
|
||||
|
||||
/* setup subfile */
|
||||
new_streamFile = open_wrap_streamfile(streamFile);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,"fsb");
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
return temp_streamFile;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_streamFile);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
|
||||
|
||||
uint32_t NumSamples = 0, LoopStart = 0, LoopEnd = 0;
|
||||
int LoopFlag = 0, ChannelCount = 0, Version, SampleRate = 0, CodingID;
|
||||
int TotalStreams, TargetStream = streamFile->stream_index;
|
||||
int TotalSubsongs, TargetSubsong = streamFile->stream_index;
|
||||
int i;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
@ -25,7 +25,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
|
||||
Version = read_32bitLE(0x04,streamFile);
|
||||
if (Version != 0x00 && Version != 0x01) goto fail;
|
||||
|
||||
TotalStreams = read_32bitLE(0x08,streamFile);
|
||||
TotalSubsongs = read_32bitLE(0x08,streamFile);
|
||||
SampleHeaderLength = read_32bitLE(0x0C,streamFile);
|
||||
NameTableLength = read_32bitLE(0x10,streamFile);
|
||||
SampleDataLength = read_32bitLE(0x14,streamFile);
|
||||
@ -37,14 +37,14 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
|
||||
if ((SampleHeaderLength + NameTableLength + SampleDataLength + BaseHeaderLength) != get_streamfile_size(streamFile))
|
||||
goto fail;
|
||||
|
||||
if (TargetStream == 0) TargetStream = 1; /* default to 1 */
|
||||
if (TargetStream > TotalStreams || TotalStreams <= 0) goto fail;
|
||||
if (TargetSubsong == 0) TargetSubsong = 1; /* default to 1 */
|
||||
if (TargetSubsong > TotalSubsongs || TotalSubsongs <= 0) goto fail;
|
||||
|
||||
SampleHeaderStart = BaseHeaderLength;
|
||||
|
||||
/* find target stream header and data offset, and read all needed values for later use
|
||||
* (reads one by one as the size of a single stream header is variable) */
|
||||
for (i = 1; i <= TotalStreams; i++) {
|
||||
for (i = 1; i <= TotalSubsongs; i++) {
|
||||
off_t DataStart = 0;
|
||||
size_t StreamHeaderLength = 0;
|
||||
uint32_t SampleMode1, SampleMode2;
|
||||
@ -156,11 +156,11 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
|
||||
}
|
||||
|
||||
/* stream found */
|
||||
if (i == TargetStream) {
|
||||
if (i == TargetSubsong) {
|
||||
StartOffset = BaseHeaderLength + SampleHeaderLength + NameTableLength + DataStart;
|
||||
|
||||
/* get stream size from next stream or datasize if there is only one */
|
||||
if (i == TotalStreams) {
|
||||
if (i == TotalSubsongs) {
|
||||
StreamSize = SampleDataLength - DataStart;
|
||||
} else {
|
||||
uint32_t NextSampleMode = (uint32_t)read_32bitLE(SampleHeaderStart+StreamHeaderLength+0x00,streamFile);
|
||||
@ -178,7 +178,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
|
||||
|
||||
/* get stream name */
|
||||
if (NameTableLength) {
|
||||
NameOffset = BaseHeaderLength + SampleHeaderLength + read_32bitLE(BaseHeaderLength + SampleHeaderLength + 0x04*(TargetStream-1),streamFile);
|
||||
NameOffset = BaseHeaderLength + SampleHeaderLength + read_32bitLE(BaseHeaderLength + SampleHeaderLength + 0x04*(TargetSubsong-1),streamFile);
|
||||
}
|
||||
|
||||
|
||||
@ -187,12 +187,13 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = SampleRate;
|
||||
vgmstream->num_streams = TotalStreams;
|
||||
vgmstream->num_samples = NumSamples;
|
||||
if (LoopFlag) {
|
||||
vgmstream->loop_start_sample = LoopStart;
|
||||
vgmstream->loop_end_sample = LoopEnd;
|
||||
}
|
||||
vgmstream->num_streams = TotalSubsongs;
|
||||
vgmstream->stream_size = StreamSize;
|
||||
vgmstream->meta_type = meta_FSB5;
|
||||
if (NameOffset)
|
||||
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, NameOffset,streamFile);
|
||||
|
@ -3,58 +3,12 @@
|
||||
|
||||
#define FSB_KEY_MAX 128 /* probably 32 */
|
||||
|
||||
typedef struct {
|
||||
uint8_t fsbkey[FSB_KEY_MAX];
|
||||
size_t fsbkey_size;
|
||||
int is_alt;
|
||||
} fsb_decryption_data;
|
||||
|
||||
static size_t fsb_decryption_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, fsb_decryption_data* data) {
|
||||
static const unsigned char reverse_bits_table[] = { /* LUT to simplify, could use some bitswap function */
|
||||
0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
|
||||
0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
|
||||
0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
|
||||
0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
|
||||
0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
|
||||
0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
|
||||
0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
|
||||
0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
|
||||
0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
|
||||
0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
|
||||
0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
|
||||
0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
|
||||
0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
|
||||
0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
|
||||
0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
|
||||
0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
|
||||
};
|
||||
size_t bytes_read;
|
||||
int i;
|
||||
|
||||
bytes_read = streamfile->read(streamfile, dest, offset, length);
|
||||
|
||||
/* decrypt data (inverted bits and xor) */
|
||||
for (i = 0; i < bytes_read; i++) {
|
||||
uint8_t xor = data->fsbkey[(offset + i) % data->fsbkey_size];
|
||||
uint8_t val = dest[i];
|
||||
if (data->is_alt) {
|
||||
dest[i] = reverse_bits_table[val ^ xor];
|
||||
}
|
||||
else {
|
||||
dest[i] = reverse_bits_table[val] ^ xor;
|
||||
}
|
||||
}
|
||||
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
static STREAMFILE* setup_fsb_streamfile(STREAMFILE *streamFile, const uint8_t * key, size_t key_size, int is_alt);
|
||||
|
||||
|
||||
/* fully encrypted FSBs */
|
||||
VGMSTREAM * init_vgmstream_fsb_encrypted(STREAMFILE * streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
fsb_decryption_data io_data = {0};
|
||||
size_t io_data_size = sizeof(fsb_decryption_data);
|
||||
|
||||
/* check extensions */
|
||||
if ( !check_extensions(streamFile, "fsb") )
|
||||
@ -67,35 +21,29 @@ VGMSTREAM * init_vgmstream_fsb_encrypted(STREAMFILE * streamFile) {
|
||||
|
||||
/* try fsbkey + all combinations of FSB4/5 and decryption algorithms */
|
||||
{
|
||||
io_data.fsbkey_size = read_key_file(io_data.fsbkey, FSB_KEY_MAX, streamFile);
|
||||
if (io_data.fsbkey_size) {
|
||||
STREAMFILE *temp_streamFile = NULL;
|
||||
uint8_t key[FSB_KEY_MAX];
|
||||
size_t key_size = read_key_file(key, FSB_KEY_MAX, streamFile);
|
||||
|
||||
if (key_size) {
|
||||
{
|
||||
STREAMFILE *custom_streamFile = NULL;
|
||||
temp_streamFile = setup_fsb_streamfile(streamFile, key,key_size, 0);
|
||||
if (!temp_streamFile) goto fail;
|
||||
|
||||
io_data.is_alt = 0;
|
||||
custom_streamFile = open_io_streamfile(open_wrap_streamfile(streamFile), &io_data,io_data_size, fsb_decryption_read);
|
||||
if (!custom_streamFile) goto fail;
|
||||
if (!vgmstream) vgmstream = init_vgmstream_fsb(temp_streamFile);
|
||||
if (!vgmstream) vgmstream = init_vgmstream_fsb5(temp_streamFile);
|
||||
|
||||
if (!vgmstream) vgmstream = init_vgmstream_fsb(custom_streamFile);
|
||||
if (!vgmstream) vgmstream = init_vgmstream_fsb5(custom_streamFile);
|
||||
|
||||
close_streamfile(custom_streamFile);
|
||||
close_streamfile(temp_streamFile);
|
||||
}
|
||||
|
||||
|
||||
if (!vgmstream) {
|
||||
STREAMFILE *custom_streamFile = NULL;
|
||||
temp_streamFile = setup_fsb_streamfile(streamFile, key,key_size, 1);
|
||||
if (!temp_streamFile) goto fail;
|
||||
|
||||
io_data.is_alt = 1;
|
||||
custom_streamFile = open_io_streamfile(open_wrap_streamfile(streamFile), &io_data,io_data_size, fsb_decryption_read);
|
||||
if (!custom_streamFile) goto fail;
|
||||
if (!vgmstream) vgmstream = init_vgmstream_fsb(temp_streamFile);
|
||||
if (!vgmstream) vgmstream = init_vgmstream_fsb5(temp_streamFile);
|
||||
|
||||
vgmstream = init_vgmstream_fsb(custom_streamFile);
|
||||
if (!vgmstream)
|
||||
vgmstream = init_vgmstream_fsb5(custom_streamFile);
|
||||
|
||||
close_streamfile(custom_streamFile);
|
||||
close_streamfile(temp_streamFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -104,26 +52,22 @@ VGMSTREAM * init_vgmstream_fsb_encrypted(STREAMFILE * streamFile) {
|
||||
/* try all keys until one works */
|
||||
if (!vgmstream) {
|
||||
int i;
|
||||
STREAMFILE *custom_streamFile = NULL;
|
||||
STREAMFILE *temp_streamFile = NULL;
|
||||
|
||||
for (i = 0; i < fsbkey_list_count; i++) {
|
||||
if (!fsbkey_list[i].fsbkey_size || fsbkey_list[i].fsbkey_size > FSB_KEY_MAX) goto fail;
|
||||
fsbkey_info entry = fsbkey_list[i];
|
||||
;VGM_LOG("fsbkey: size=%i, is_fsb5=%i, is_alt=%i\n", entry.fsbkey_size,entry.is_fsb5, entry.is_alt);
|
||||
|
||||
memcpy(io_data.fsbkey, fsbkey_list[i].fsbkey, fsbkey_list[i].fsbkey_size);
|
||||
io_data.fsbkey_size = fsbkey_list[i].fsbkey_size;
|
||||
io_data.is_alt = fsbkey_list[i].is_alt;
|
||||
//;VGM_LOG("fsbkey: size=%i, is_fsb5=%i, is_alt=%i\n", fsbkey_list[i].fsbkey_size,fsbkey_list[i].is_fsb5, fsbkey_list[i].is_alt);
|
||||
|
||||
custom_streamFile = open_io_streamfile(open_wrap_streamfile(streamFile), &io_data,io_data_size, fsb_decryption_read);
|
||||
if (!custom_streamFile) goto fail;
|
||||
temp_streamFile = setup_fsb_streamfile(streamFile, entry.fsbkey, entry.fsbkey_size, entry.is_alt);
|
||||
if (!temp_streamFile) goto fail;
|
||||
|
||||
if (fsbkey_list[i].is_fsb5) {
|
||||
vgmstream = init_vgmstream_fsb5(custom_streamFile);
|
||||
vgmstream = init_vgmstream_fsb5(temp_streamFile);
|
||||
} else {
|
||||
vgmstream = init_vgmstream_fsb(custom_streamFile);
|
||||
vgmstream = init_vgmstream_fsb(temp_streamFile);
|
||||
}
|
||||
|
||||
close_streamfile(custom_streamFile);
|
||||
close_streamfile(temp_streamFile);
|
||||
if (vgmstream) break;
|
||||
}
|
||||
}
|
||||
@ -137,3 +81,78 @@ fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint8_t key[FSB_KEY_MAX];
|
||||
size_t key_size;
|
||||
int is_alt;
|
||||
} fsb_decryption_data;
|
||||
|
||||
/* Encrypted FSB info from guessfsb and fsbext */
|
||||
static size_t fsb_decryption_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, fsb_decryption_data* data) {
|
||||
static const unsigned char reverse_bits_table[] = { /* LUT to simplify, could use some bitswap function */
|
||||
0x00,0x80,0x40,0xC0,0x20,0xA0,0x60,0xE0,0x10,0x90,0x50,0xD0,0x30,0xB0,0x70,0xF0,
|
||||
0x08,0x88,0x48,0xC8,0x28,0xA8,0x68,0xE8,0x18,0x98,0x58,0xD8,0x38,0xB8,0x78,0xF8,
|
||||
0x04,0x84,0x44,0xC4,0x24,0xA4,0x64,0xE4,0x14,0x94,0x54,0xD4,0x34,0xB4,0x74,0xF4,
|
||||
0x0C,0x8C,0x4C,0xCC,0x2C,0xAC,0x6C,0xEC,0x1C,0x9C,0x5C,0xDC,0x3C,0xBC,0x7C,0xFC,
|
||||
0x02,0x82,0x42,0xC2,0x22,0xA2,0x62,0xE2,0x12,0x92,0x52,0xD2,0x32,0xB2,0x72,0xF2,
|
||||
0x0A,0x8A,0x4A,0xCA,0x2A,0xAA,0x6A,0xEA,0x1A,0x9A,0x5A,0xDA,0x3A,0xBA,0x7A,0xFA,
|
||||
0x06,0x86,0x46,0xC6,0x26,0xA6,0x66,0xE6,0x16,0x96,0x56,0xD6,0x36,0xB6,0x76,0xF6,
|
||||
0x0E,0x8E,0x4E,0xCE,0x2E,0xAE,0x6E,0xEE,0x1E,0x9E,0x5E,0xDE,0x3E,0xBE,0x7E,0xFE,
|
||||
0x01,0x81,0x41,0xC1,0x21,0xA1,0x61,0xE1,0x11,0x91,0x51,0xD1,0x31,0xB1,0x71,0xF1,
|
||||
0x09,0x89,0x49,0xC9,0x29,0xA9,0x69,0xE9,0x19,0x99,0x59,0xD9,0x39,0xB9,0x79,0xF9,
|
||||
0x05,0x85,0x45,0xC5,0x25,0xA5,0x65,0xE5,0x15,0x95,0x55,0xD5,0x35,0xB5,0x75,0xF5,
|
||||
0x0D,0x8D,0x4D,0xCD,0x2D,0xAD,0x6D,0xED,0x1D,0x9D,0x5D,0xDD,0x3D,0xBD,0x7D,0xFD,
|
||||
0x03,0x83,0x43,0xC3,0x23,0xA3,0x63,0xE3,0x13,0x93,0x53,0xD3,0x33,0xB3,0x73,0xF3,
|
||||
0x0B,0x8B,0x4B,0xCB,0x2B,0xAB,0x6B,0xEB,0x1B,0x9B,0x5B,0xDB,0x3B,0xBB,0x7B,0xFB,
|
||||
0x07,0x87,0x47,0xC7,0x27,0xA7,0x67,0xE7,0x17,0x97,0x57,0xD7,0x37,0xB7,0x77,0xF7,
|
||||
0x0F,0x8F,0x4F,0xCF,0x2F,0xAF,0x6F,0xEF,0x1F,0x9F,0x5F,0xDF,0x3F,0xBF,0x7F,0xFF
|
||||
};
|
||||
size_t bytes_read;
|
||||
int i;
|
||||
|
||||
bytes_read = streamfile->read(streamfile, dest, offset, length);
|
||||
|
||||
/* decrypt data (inverted bits and xor) */
|
||||
for (i = 0; i < bytes_read; i++) {
|
||||
uint8_t xor = data->key[(offset + i) % data->key_size];
|
||||
uint8_t val = dest[i];
|
||||
if (data->is_alt) {
|
||||
dest[i] = reverse_bits_table[val ^ xor];
|
||||
}
|
||||
else {
|
||||
dest[i] = reverse_bits_table[val] ^ xor;
|
||||
}
|
||||
}
|
||||
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
static STREAMFILE* setup_fsb_streamfile(STREAMFILE *streamFile, const uint8_t * key, size_t key_size, int is_alt) {
|
||||
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
|
||||
fsb_decryption_data io_data = {0};
|
||||
size_t io_data_size = sizeof(fsb_decryption_data);
|
||||
|
||||
/* setup decryption with key (external) */
|
||||
if (!key_size || key_size > FSB_KEY_MAX) goto fail;
|
||||
|
||||
memcpy(io_data.key, key, key_size);
|
||||
io_data.key_size = key_size;
|
||||
io_data.is_alt = is_alt;
|
||||
|
||||
/* setup subfile */
|
||||
new_streamFile = open_wrap_streamfile(streamFile);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, fsb_decryption_read);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
return temp_streamFile;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_streamFile);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ static const uint8_t key_gfs[] = { 0x25,0x6C,0x41,0x6E,0x32,0x7B,0x50,0x69,0x2A,
|
||||
/* RevHeadz Engine Sounds (Mobile) */ //"1^7%82#&5$~/8sz"
|
||||
static const uint8_t key_rev[] = { 0x31,0x5E,0x37,0x25,0x38,0x32,0x23,0x26,0x35,0x24,0x7E,0x2F,0x38,0x73,0x7A };
|
||||
|
||||
/* Dark Souls 3 (PC) */ //"FDPrVuT4fAFvdHJYAgyMzRF4EcBAnKg" //todo ps4?
|
||||
/* Dark Souls 3 (PC) */ //"FDPrVuT4fAFvdHJYAgyMzRF4EcBAnKg"
|
||||
static const uint8_t key_ds3[] = { 0x46,0x44,0x50,0x72,0x56,0x75,0x54,0x34,0x66,0x41,0x46,0x76,0x64,0x48,0x4A,0x59,0x41,0x67,0x79,0x4D,0x7A,0x52,0x46,0x34,0x45,0x63,0x42,0x41,0x6E,0x4B,0x67 };
|
||||
|
||||
/* Mortal Kombat X */
|
||||
|
@ -225,6 +225,9 @@ static const hcakey_info hcakey_list[] = {
|
||||
// Kai-ri-Sei Million Arthur (Vita)
|
||||
{1782351729464341796}, // 18BC2F7463867524
|
||||
|
||||
// Dx2 Shin Megami Tensei Liberation (iOS/Android)
|
||||
{118714477}, // 000000000713706D
|
||||
|
||||
};
|
||||
|
||||
#endif/*_HCA_KEYS_H_*/
|
||||
|
@ -5,6 +5,7 @@
|
||||
VGMSTREAM * init_vgmstream_kma9(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
size_t stream_size;
|
||||
int loop_flag, channel_count;
|
||||
int total_subsongs = 0, target_subsong = streamFile->stream_index;
|
||||
|
||||
@ -25,7 +26,7 @@ VGMSTREAM * init_vgmstream_kma9(STREAMFILE *streamFile) {
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
|
||||
/* 0x0c: unknown */
|
||||
/* 0x14: data size of each subsong */
|
||||
stream_size = read_32bitLE(0x14,streamFile); /* per subsong */
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
@ -37,7 +38,7 @@ VGMSTREAM * init_vgmstream_kma9(STREAMFILE *streamFile) {
|
||||
vgmstream->loop_start_sample = read_32bitLE(0x24,streamFile); /* with skip_samples? */
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples; /* 0x28 looks like end samples but isn't, no idea */
|
||||
vgmstream->num_streams = total_subsongs;
|
||||
|
||||
vgmstream->stream_size = stream_size;
|
||||
vgmstream->meta_type = meta_KMA9;
|
||||
|
||||
#ifdef VGM_USE_ATRAC9
|
||||
|
@ -697,4 +697,13 @@ VGMSTREAM * init_vgmstream_kma9(STREAMFILE * streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_fsb_encrypted(STREAMFILE * streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_xwc(STREAMFILE *streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_atsl3(STREAMFILE *streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_sps_n1(STREAMFILE *streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_atx(STREAMFILE *streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_sqex_sead(STREAMFILE * streamFile);
|
||||
#endif /*_META_H*/
|
||||
|
@ -274,6 +274,9 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, const ch
|
||||
int32_t loop_length = vgm_inf->loop_length;
|
||||
int loop_end_found = vgm_inf->loop_end_found;
|
||||
int32_t loop_end = vgm_inf->loop_end;
|
||||
size_t stream_size = vgm_inf->stream_size ?
|
||||
vgm_inf->stream_size :
|
||||
get_streamfile_size(streamFile) - start;
|
||||
|
||||
ov_callbacks default_callbacks;
|
||||
|
||||
@ -295,9 +298,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, const ch
|
||||
|
||||
temp_streamfile.start = start;
|
||||
temp_streamfile.offset = 0;
|
||||
temp_streamfile.size = vgm_inf->stream_size ?
|
||||
vgm_inf->stream_size :
|
||||
get_streamfile_size(temp_streamfile.streamfile) - start;
|
||||
temp_streamfile.size = stream_size;
|
||||
|
||||
temp_streamfile.decryption_callback = vgm_inf->decryption_callback;
|
||||
temp_streamfile.scd_xor = vgm_inf->scd_xor;
|
||||
@ -324,9 +325,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, const ch
|
||||
|
||||
data->ov_streamfile.start = start;
|
||||
data->ov_streamfile.offset = 0;
|
||||
data->ov_streamfile.size = vgm_inf->stream_size ?
|
||||
vgm_inf->stream_size :
|
||||
get_streamfile_size(data->ov_streamfile.streamfile) - start;
|
||||
data->ov_streamfile.size = stream_size;
|
||||
|
||||
data->ov_streamfile.decryption_callback = vgm_inf->decryption_callback;
|
||||
data->ov_streamfile.scd_xor = vgm_inf->scd_xor;
|
||||
@ -412,6 +411,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, const ch
|
||||
vgmstream->channels = vi->channels;
|
||||
vgmstream->sample_rate = vi->rate;
|
||||
vgmstream->num_streams = vgm_inf->total_subsongs;
|
||||
vgmstream->stream_size = stream_size;
|
||||
|
||||
vgmstream->num_samples = ov_pcm_total(ovf,-1); /* let libvorbisfile find total samples */
|
||||
if (loop_flag) {
|
||||
|
@ -7,10 +7,10 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
STREAMFILE * streamHeader = NULL;
|
||||
off_t start_offset, chunk_offset, name_offset = 0;
|
||||
size_t data_size, chunk_size;
|
||||
size_t stream_size, chunk_size;
|
||||
int loop_flag = 0, channel_count, is_separate = 0, type, sample_rate;
|
||||
int32_t loop_start, loop_end;
|
||||
int total_streams, target_stream = streamFile->stream_index;
|
||||
int total_subsongs, target_subsong = streamFile->stream_index;
|
||||
|
||||
/* check extensions */
|
||||
/* .xws: header and data, .xwh+xwb: header + data (.bin+dat are also found in Wild Arms 4/5) */
|
||||
@ -46,14 +46,14 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) {
|
||||
|
||||
|
||||
/* check multi-streams */
|
||||
total_streams = read_32bitLE(chunk_offset+0x00,streamHeader);
|
||||
if (target_stream == 0) target_stream = 1;
|
||||
if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail;
|
||||
total_subsongs = read_32bitLE(chunk_offset+0x00,streamHeader);
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
|
||||
|
||||
|
||||
/* read stream header */
|
||||
{
|
||||
off_t header_offset = chunk_offset + 0x4 + 0x1c * (target_stream-1); /* position in FORM */
|
||||
off_t header_offset = chunk_offset + 0x4 + 0x1c * (target_subsong-1); /* position in FORM */
|
||||
off_t stream_offset, next_stream_offset, data_offset = 0;
|
||||
|
||||
type = read_8bit(header_offset+0x00, streamHeader);
|
||||
@ -83,22 +83,22 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) {
|
||||
if (!data_offset) goto fail;
|
||||
}
|
||||
|
||||
if (target_stream == total_streams) {
|
||||
if (target_subsong == total_subsongs) {
|
||||
next_stream_offset = data_offset + get_streamfile_size(is_separate ? streamFile : streamHeader);
|
||||
} else {
|
||||
off_t next_header_offset = chunk_offset + 0x4 + 0x1c * (target_stream);
|
||||
off_t next_header_offset = chunk_offset + 0x4 + 0x1c * (target_subsong);
|
||||
next_stream_offset = read_32bitLE(next_header_offset+0x10,streamHeader);
|
||||
}
|
||||
|
||||
data_size = next_stream_offset - stream_offset;
|
||||
stream_size = next_stream_offset - stream_offset;
|
||||
start_offset = data_offset + stream_offset;
|
||||
}
|
||||
|
||||
/* get stream name (always follows FORM) */
|
||||
if (read_32bitBE(0x10+0x10 + chunk_size,streamHeader) == 0x46545854) { /* "FTXT" */
|
||||
chunk_offset = 0x10+0x10 + chunk_size + 0x10;
|
||||
if (read_32bitLE(chunk_offset+0x00,streamHeader) == total_streams) {
|
||||
name_offset = chunk_offset + read_32bitLE(chunk_offset+0x04 + (target_stream-1)*0x04,streamHeader);
|
||||
if (read_32bitLE(chunk_offset+0x00,streamHeader) == total_subsongs) {
|
||||
name_offset = chunk_offset + read_32bitLE(chunk_offset+0x04 + (target_subsong-1)*0x04,streamHeader);
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,7 +108,8 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) {
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_streams = total_streams;
|
||||
vgmstream->num_streams = total_subsongs;
|
||||
vgmstream->stream_size = stream_size;
|
||||
vgmstream->meta_type = meta_PS2_RXWS;
|
||||
if (name_offset)
|
||||
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamHeader);
|
||||
@ -143,10 +144,10 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) {
|
||||
joint_stereo = 0;
|
||||
encoder_delay = 0x0;
|
||||
|
||||
bytes = ffmpeg_make_riff_atrac3(buf, 0x100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_size, joint_stereo, encoder_delay);
|
||||
bytes = ffmpeg_make_riff_atrac3(buf, 0x100, vgmstream->num_samples, stream_size, vgmstream->channels, vgmstream->sample_rate, block_size, joint_stereo, encoder_delay);
|
||||
if (bytes <= 0) goto fail;
|
||||
|
||||
vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size);
|
||||
vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,stream_size);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
@ -39,20 +39,27 @@ VGMSTREAM * init_vgmstream_cdxa(STREAMFILE *streamFile) {
|
||||
}
|
||||
}
|
||||
|
||||
/* test first block (except when RIFF) */
|
||||
/* test some blocks (except when RIFF) since other .XA/STR may start blank */
|
||||
if (start_offset == 0) {
|
||||
int i, j;
|
||||
int i, j, block;
|
||||
off_t test_offset = start_offset;
|
||||
size_t sector_size = (is_blocked ? 0x900 : 0x800);
|
||||
size_t block_size = 0x80;
|
||||
|
||||
/* 0x80 frames for 1 sector (max ~0x800 for ISO mode) */
|
||||
for (i = 0; i < (0x800/0x80); i++) {
|
||||
off_t test_offset = start_offset + (is_blocked ? 0x18 : 0x00) + 0x80*i;
|
||||
for (block = 0; block < 3; block++) {
|
||||
test_offset += (is_blocked ? 0x18 : 0x00); /* header */
|
||||
|
||||
/* ADPCM predictors should be 0..3 index */
|
||||
for (j = 0; j < 16; j++) {
|
||||
uint8_t header = read_8bit(test_offset + i, streamFile);
|
||||
if (((header >> 4) & 0xF) > 3)
|
||||
goto fail;
|
||||
for (i = 0; i < (sector_size/block_size); i++) {
|
||||
/* first 0x10 ADPCM predictors should be 0..3 index */
|
||||
for (j = 0; j < 16; j++) {
|
||||
uint8_t header = read_8bit(test_offset + i, streamFile);
|
||||
if (((header >> 4) & 0xF) > 3)
|
||||
goto fail;
|
||||
}
|
||||
test_offset += 0x80;
|
||||
}
|
||||
|
||||
test_offset += (is_blocked ? 0x18 : 0x00); /* footer */
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,7 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
|
||||
size_t block_size = 0, block_size_total = 0;
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
|
||||
int i, total_segments;
|
||||
int total_streams, target_stream = streamFile->stream_index;
|
||||
int total_subsongs, target_subsong = streamFile->stream_index;
|
||||
|
||||
|
||||
if (!check_extensions(streamFile,"rws"))
|
||||
@ -49,7 +49,7 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
|
||||
* 0x1c: null? 0x30: 0x800?, 0x34: block_size_total?, 0x38: data offset, 0x3c: 0?, 0x40-50: file uuid */
|
||||
read_32bit = (read_32bitLE(off+0x00,streamFile) > header_size) ? read_32bitBE : read_32bitLE; /* GC/Wii/X360 = BE */
|
||||
total_segments = read_32bit(off+0x20,streamFile);
|
||||
total_streams = read_32bit(off+0x28,streamFile);
|
||||
total_subsongs = read_32bit(off+0x28,streamFile);
|
||||
|
||||
/* skip audio file name */
|
||||
off += 0x50 + get_rws_string_size(off+0x50, streamFile);
|
||||
@ -58,8 +58,8 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
|
||||
/* Data is divided into "segments" (cues/divisions within data, ex. intro+main, voice1+2+..N) and "streams"
|
||||
* of interleaved blocks (for multichannel?). last stream (only?) has padding. Segments divide all streams.
|
||||
* ex.- 0x1800 data + 0 pad of stream_0 2ch, 0x1800 data + 0x200 pad of stream1 2ch (xN). */
|
||||
if (target_stream == 0) target_stream = 1;
|
||||
if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail;
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
|
||||
|
||||
/* get segment info, for all streams */
|
||||
/* 0x00/04/0c: command?, 0x18: full segment size (including all streams), 0x1c: offset, others: ?) */
|
||||
@ -70,9 +70,9 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
|
||||
|
||||
/* get usable segment sizes (usually ok but sometimes > stream_size), per stream */
|
||||
for (i = 0; i < total_segments; i++) { /* sum usable segment sizes (no padding) */
|
||||
stream_size += read_32bit(off + 0x04*i + 0x04*total_segments*(target_stream-1),streamFile);
|
||||
stream_size += read_32bit(off + 0x04*i + 0x04*total_segments*(target_subsong-1),streamFile);
|
||||
}
|
||||
off += 0x04 * (total_segments * total_streams);
|
||||
off += 0x04 * (total_segments * total_subsongs);
|
||||
|
||||
/* skip segment uuids */
|
||||
off += 0x10 * total_segments;
|
||||
@ -85,21 +85,21 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
|
||||
/* get stream layout */
|
||||
/* 0x00/04/14: command?, 0x08: null? 0x0c: spf related? (XADPCM=07, VAG=1C, DSP=0E, PCM=01)
|
||||
* 0x24: offset within data chunk, 0x1c: codec related?, others: ?) */
|
||||
for (i = 0; i < total_streams; i++) { /* get block_sizes */
|
||||
for (i = 0; i < total_subsongs; i++) { /* get block_sizes */
|
||||
block_size_total += read_32bit(off + 0x10 + 0x28*i, streamFile); /* for all streeams, to skip during decode */
|
||||
if (i+1 == target_stream) {
|
||||
if (i+1 == target_subsong) {
|
||||
//block_size_full = read_32bit(off + 0x10 + 0x28*i, streamFile); /* with padding, can be different per stream */
|
||||
block_size = read_32bit(off + 0x20 + 0x28*i, streamFile); /* without padding */
|
||||
stream_offset = read_32bit(off + 0x24 + 0x28*i, streamFile); /* within data */
|
||||
}
|
||||
}
|
||||
off += 0x28 * total_streams;
|
||||
off += 0x28 * total_subsongs;
|
||||
|
||||
/* get stream config */
|
||||
/* 0x04: command?, 0x0c(1): bits per sample, others: null? */
|
||||
for (i = 0; i < total_streams; i++) { /* size depends on codec so we must parse it */
|
||||
for (i = 0; i < total_subsongs; i++) { /* size depends on codec so we must parse it */
|
||||
int prev_codec = 0;
|
||||
if (i+1 == target_stream) {
|
||||
if (i+1 == target_subsong) {
|
||||
sample_rate = read_32bit(off+0x00, streamFile);
|
||||
//unk_size = read_32bit(off+0x08, streamFile); /* segment size again? loop-related? */
|
||||
channel_count = read_8bit(off+0x0d, streamFile);
|
||||
@ -110,7 +110,7 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
|
||||
|
||||
if (prev_codec == 0xF86215B0) { /* if codec is DSP there is an extra field per stream */
|
||||
/* 0x00: approx num samples? 0x04: approx size/loop related? (can be 0) */
|
||||
if (i+1 == target_stream) {
|
||||
if (i+1 == target_subsong) {
|
||||
coefs_offset = off + 0x1c;
|
||||
}
|
||||
off += 0x60;
|
||||
@ -120,11 +120,11 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
|
||||
}
|
||||
|
||||
/* skip stream uuids */
|
||||
off += 0x10 * total_streams;
|
||||
off += 0x10 * total_subsongs;
|
||||
|
||||
/* get stream name */
|
||||
for (i = 0; i < total_streams; i++) {
|
||||
if (i+1 == target_stream) {
|
||||
for (i = 0; i < total_subsongs; i++) {
|
||||
if (i+1 == target_subsong) {
|
||||
name_offset = off;
|
||||
}
|
||||
off += get_rws_string_size(off, streamFile);
|
||||
@ -137,7 +137,7 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
|
||||
start_offset = 0x0c + 0x0c + header_size + 0x0c + stream_offset;
|
||||
|
||||
/* sometimes it's wrong for no apparent reason (probably a bug in RWS) */
|
||||
stream_size_expected = (stream_size_full / block_size_total) * (block_size * total_streams) / total_streams;
|
||||
stream_size_expected = (stream_size_full / block_size_total) * (block_size * total_subsongs) / total_subsongs;
|
||||
if (stream_size > stream_size_expected) {
|
||||
VGM_LOG("RWS: readjusting wrong stream size %x vs expected %x\n", stream_size, stream_size_expected);
|
||||
stream_size = stream_size_expected;
|
||||
@ -149,7 +149,8 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_streams = total_streams;
|
||||
vgmstream->num_streams = total_subsongs;
|
||||
vgmstream->stream_size = stream_size;
|
||||
vgmstream->meta_type = meta_RWS;
|
||||
if (name_offset)
|
||||
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamFile);
|
||||
|
@ -8,8 +8,8 @@ static void get_stream_name(char * stream_name, STREAMFILE *streamFile, int targ
|
||||
VGMSTREAM * init_vgmstream_sab(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag, channel_count = 0, is_stream, align, codec, sample_rate, data_size, loop_start, loop_end;
|
||||
int total_streams, target_stream = streamFile->stream_index;
|
||||
int loop_flag, channel_count = 0, is_stream, align, codec, sample_rate, stream_size, loop_start, loop_end;
|
||||
int total_subsongs, target_subsong = streamFile->stream_index;
|
||||
|
||||
/* .sab: main, .sob: config/names */
|
||||
if (!check_extensions(streamFile,"sab"))
|
||||
@ -20,29 +20,29 @@ VGMSTREAM * init_vgmstream_sab(STREAMFILE *streamFile) {
|
||||
goto fail;
|
||||
|
||||
is_stream = read_32bitLE(0x04,streamFile) & 0x04; /* other flags don't seem to matter */
|
||||
total_streams = is_stream ? 1 : read_32bitLE(0x08,streamFile);
|
||||
if (target_stream == 0) target_stream = 1;
|
||||
if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail;
|
||||
total_subsongs = is_stream ? 1 : read_32bitLE(0x08,streamFile);
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
|
||||
|
||||
align = read_32bitLE(0x0c,streamFile); /* doubles as interleave */
|
||||
|
||||
/* stream config */
|
||||
codec = read_32bitLE(0x18 + 0x1c*(target_stream-1) + 0x00,streamFile);
|
||||
channel_count = read_32bitLE(0x18 + 0x1c*(target_stream-1) + 0x04,streamFile);
|
||||
sample_rate = read_32bitLE(0x18 + 0x1c*(target_stream-1) + 0x08,streamFile);
|
||||
data_size = read_32bitLE(0x18 + 0x1c*(target_stream-1) + 0x0c,streamFile);
|
||||
loop_start = read_32bitLE(0x18 + 0x1c*(target_stream-1) + 0x10,streamFile);
|
||||
loop_end = read_32bitLE(0x18 + 0x1c*(target_stream-1) + 0x14,streamFile);
|
||||
codec = read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x00,streamFile);
|
||||
channel_count = read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x04,streamFile);
|
||||
sample_rate = read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x08,streamFile);
|
||||
stream_size = read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x0c,streamFile);
|
||||
loop_start = read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x10,streamFile);
|
||||
loop_end = read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x14,streamFile);
|
||||
loop_flag = (loop_end > 0);
|
||||
|
||||
start_offset = 0x18 + 0x1c*total_streams;
|
||||
start_offset = 0x18 + 0x1c*total_subsongs;
|
||||
if (start_offset % align)
|
||||
start_offset += align - (start_offset % align);
|
||||
start_offset += read_32bitLE(0x18 + 0x1c*(target_stream-1) + 0x18,streamFile);
|
||||
start_offset += read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x18,streamFile);
|
||||
|
||||
if (is_stream) {
|
||||
channel_count = read_32bitLE(0x08,streamFile); /* uncommon, but non-stream stereo exists */
|
||||
data_size *= channel_count;
|
||||
stream_size *= channel_count;
|
||||
}
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
@ -50,8 +50,8 @@ VGMSTREAM * init_vgmstream_sab(STREAMFILE *streamFile) {
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
|
||||
vgmstream->num_streams = total_streams;
|
||||
vgmstream->num_streams = total_subsongs;
|
||||
vgmstream->stream_size = stream_size;
|
||||
vgmstream->meta_type = meta_SAB;
|
||||
|
||||
switch(codec) {
|
||||
@ -60,7 +60,7 @@ VGMSTREAM * init_vgmstream_sab(STREAMFILE *streamFile) {
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = is_stream ? align : 0x02;
|
||||
|
||||
vgmstream->num_samples = pcm_bytes_to_samples(data_size, vgmstream->channels, 16);
|
||||
vgmstream->num_samples = pcm_bytes_to_samples(stream_size, vgmstream->channels, 16);
|
||||
vgmstream->loop_start_sample = pcm_bytes_to_samples(loop_start, vgmstream->channels, 16);
|
||||
vgmstream->loop_end_sample = pcm_bytes_to_samples(loop_end, vgmstream->channels, 16);
|
||||
|
||||
@ -71,7 +71,7 @@ VGMSTREAM * init_vgmstream_sab(STREAMFILE *streamFile) {
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = is_stream ? align : 0x10;
|
||||
|
||||
vgmstream->num_samples = ps_bytes_to_samples(data_size, vgmstream->channels);
|
||||
vgmstream->num_samples = ps_bytes_to_samples(stream_size, vgmstream->channels);
|
||||
vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start, vgmstream->channels);
|
||||
vgmstream->loop_end_sample = ps_bytes_to_samples(loop_end, vgmstream->channels);
|
||||
break;
|
||||
@ -81,7 +81,7 @@ VGMSTREAM * init_vgmstream_sab(STREAMFILE *streamFile) {
|
||||
vgmstream->layout_type = is_stream ? layout_interleave : layout_none;
|
||||
vgmstream->interleave_block_size = is_stream ? align : 0x00;
|
||||
|
||||
vgmstream->num_samples = ms_ima_bytes_to_samples(data_size, 0x24*vgmstream->channels, vgmstream->channels);
|
||||
vgmstream->num_samples = ms_ima_bytes_to_samples(stream_size, 0x24*vgmstream->channels, vgmstream->channels);
|
||||
vgmstream->loop_start_sample = ms_ima_bytes_to_samples(loop_start, 0x24*vgmstream->channels, vgmstream->channels);
|
||||
vgmstream->loop_end_sample = ms_ima_bytes_to_samples(loop_end, 0x24*vgmstream->channels, vgmstream->channels);
|
||||
break;
|
||||
@ -91,7 +91,7 @@ VGMSTREAM * init_vgmstream_sab(STREAMFILE *streamFile) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
get_stream_name(vgmstream->stream_name, streamFile, target_stream);
|
||||
get_stream_name(vgmstream->stream_name, streamFile, target_subsong);
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
goto fail;
|
||||
|
@ -2,19 +2,17 @@
|
||||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
/* SGXD - Sony/SCEI's format (SGB+SGH / SGD / SGX), found in:
|
||||
* PS3: Genji, Folklore, Afrika (Short VAG), Tokyo Jungle
|
||||
* PSP: Brave Story, Sarugetchu Sarusaru Daisakusen, Kurohyo 1/2, Pathwork Heroes */
|
||||
/* SGXD - Sony/SCEI's format (SGB+SGH / SGD / SGX) */
|
||||
VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
STREAMFILE * streamHeader = NULL;
|
||||
off_t start_offset, data_offset, chunk_offset, name_offset = 0;
|
||||
size_t data_size;
|
||||
size_t stream_size;
|
||||
|
||||
int is_sgx, is_sgb = 0;
|
||||
int loop_flag, channels, type;
|
||||
int sample_rate, num_samples, loop_start_sample, loop_end_sample;
|
||||
int total_streams, target_stream = streamFile->stream_index;
|
||||
int total_subsongs, target_subsong = streamFile->stream_index;
|
||||
|
||||
|
||||
/* check extension, case insensitive */
|
||||
@ -59,14 +57,14 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
|
||||
/* 0x04 SGX: unknown; SGD/SGH: chunk length, 0x08 null */
|
||||
|
||||
/* check multi-streams (usually only SE containers; Puppeteer) */
|
||||
total_streams = read_32bitLE(chunk_offset+0x04,streamHeader);
|
||||
if (target_stream == 0) target_stream = 1;
|
||||
if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail;
|
||||
total_subsongs = read_32bitLE(chunk_offset+0x04,streamHeader);
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
|
||||
|
||||
/* read stream header */
|
||||
{
|
||||
off_t stream_offset;
|
||||
chunk_offset += 0x08 + 0x38 * (target_stream-1); /* position in target header*/
|
||||
chunk_offset += 0x08 + 0x38 * (target_subsong-1); /* position in target header*/
|
||||
|
||||
/* 0x00 ? (00/01/02) */
|
||||
if (!is_sgx) /* meaning unknown in .sgx; offset 0 = not a stream (a RGND sample) */
|
||||
@ -85,7 +83,7 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
|
||||
num_samples = read_32bitLE(chunk_offset+0x20,streamHeader);
|
||||
loop_start_sample = read_32bitLE(chunk_offset+0x24,streamHeader);
|
||||
loop_end_sample = read_32bitLE(chunk_offset+0x28,streamHeader);
|
||||
data_size = read_32bitLE(chunk_offset+0x2c,streamHeader); /* stream size (without padding) / interleave (for type3) */
|
||||
stream_size = read_32bitLE(chunk_offset+0x2c,streamHeader); /* stream size (without padding) / interleave (for type3) */
|
||||
|
||||
if (is_sgx) {
|
||||
stream_offset = 0x0;
|
||||
@ -107,7 +105,8 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
|
||||
vgmstream->num_samples = num_samples;
|
||||
vgmstream->loop_start_sample = loop_start_sample;
|
||||
vgmstream->loop_end_sample = loop_end_sample;
|
||||
vgmstream->num_streams = total_streams;
|
||||
vgmstream->num_streams = total_subsongs;
|
||||
vgmstream->stream_size = stream_size;
|
||||
vgmstream->meta_type = meta_SGXD;
|
||||
if (name_offset)
|
||||
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamHeader);
|
||||
@ -118,23 +117,23 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
|
||||
vgmstream->loop_end_sample -= 1;
|
||||
|
||||
switch (type) {
|
||||
case 0x03: /* PS-ADPCM */
|
||||
case 0x03: /* PS-ADPCM [Genji (PS3), Ape Escape Move (PS3)]*/
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
if (is_sgx || is_sgb) {
|
||||
vgmstream->interleave_block_size = 0x10;
|
||||
} else { /* this only seems to happen with SFX */
|
||||
vgmstream->interleave_block_size = data_size;
|
||||
vgmstream->interleave_block_size = stream_size;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case 0x04: { /* ATRAC3plus */
|
||||
case 0x04: { /* ATRAC3plus [Kurohyo 1/2 (PSP), BraveStory (PSP)] */
|
||||
ffmpeg_codec_data *ffmpeg_data;
|
||||
|
||||
/* internally has a RIFF header; but the SGXD header / sample rate has priority over it (may not match) */
|
||||
ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, data_size);
|
||||
ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, stream_size);
|
||||
if ( !ffmpeg_data ) goto fail;
|
||||
vgmstream->codec_data = ffmpeg_data;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
@ -158,7 +157,7 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case 0x05: /* Short PS-ADPCM */
|
||||
case 0x05: /* Short PS-ADPCM [Afrika (PS3)] */
|
||||
vgmstream->coding_type = coding_PSX_cfg;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x4;
|
||||
@ -166,10 +165,10 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
|
||||
break;
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case 0x06: { /* AC3 */
|
||||
case 0x06: { /* AC3 [Tokyo Jungle (PS3), Afrika (PS3)] */
|
||||
ffmpeg_codec_data *ffmpeg_data;
|
||||
|
||||
ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, data_size);
|
||||
ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, stream_size);
|
||||
if ( !ffmpeg_data ) goto fail;
|
||||
vgmstream->codec_data = ffmpeg_data;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
|
82
src/meta/sps_n1.c
Normal file
82
src/meta/sps_n1.c
Normal file
@ -0,0 +1,82 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
static STREAMFILE* setup_sps_streamfile(STREAMFILE *streamfile, off_t subfile_offset, size_t subfile_size, char* extension);
|
||||
|
||||
/* .SPS - Nippon Ichi's RIFF AT3 wrapper [ClaDun (PSP)] */
|
||||
VGMSTREAM * init_vgmstream_sps_n1(STREAMFILE *streamFile) {
|
||||
VGMSTREAM *vgmstream = NULL;
|
||||
STREAMFILE *temp_streamFile = NULL;
|
||||
int type, sample_rate;
|
||||
off_t subfile_offset;
|
||||
size_t subfile_size;
|
||||
|
||||
/* check extensions */
|
||||
if ( !check_extensions(streamFile,"sps"))
|
||||
goto fail;
|
||||
|
||||
/* mini header */
|
||||
type = read_32bitLE(0x00,streamFile); //todo channels? all known VAG are mono and AT3 stereo
|
||||
subfile_size = read_32bitLE(0x04,streamFile);
|
||||
sample_rate = (uint16_t)read_16bitLE(0x08,streamFile);
|
||||
/* 0x0a/0b: stereo+loop flags? */
|
||||
//num_samples = read_32bitLE(0x0c,streamFile);
|
||||
subfile_offset = 0x10;
|
||||
|
||||
/* init the VGMSTREAM */
|
||||
switch(type) {
|
||||
case 1: /* .vag */
|
||||
temp_streamFile = setup_sps_streamfile(streamFile, subfile_offset, subfile_size, "vag");
|
||||
if (!temp_streamFile) goto fail;
|
||||
|
||||
vgmstream = init_vgmstream_ps2_vag(temp_streamFile);
|
||||
if (!vgmstream) goto fail;
|
||||
break;
|
||||
|
||||
case 2: /* .at3 */
|
||||
temp_streamFile = setup_sps_streamfile(streamFile, subfile_offset, subfile_size, "at3");
|
||||
if (!temp_streamFile) goto fail;
|
||||
|
||||
vgmstream = init_vgmstream_riff(temp_streamFile);
|
||||
if (!vgmstream) goto fail;
|
||||
break;
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
//VGM_LOG(vgmstream->num_samples != num_samples,
|
||||
// "SPS: sps num_samples and subfile num_samples don't match\n");
|
||||
//vgmstream->num_samples = num_samples; //todo adjusted for MAIATRAC3
|
||||
vgmstream->sample_rate = sample_rate; /* .vag header doesn't match */
|
||||
|
||||
close_streamfile(temp_streamFile);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_streamFile);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static STREAMFILE* setup_sps_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, char* extension) {
|
||||
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
|
||||
|
||||
/* setup subfile */
|
||||
new_streamFile = open_wrap_streamfile(streamFile);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,extension);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
return temp_streamFile;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_streamFile);
|
||||
return NULL;
|
||||
}
|
@ -185,10 +185,9 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
/* fill in the vital statistics */
|
||||
vgmstream->channels = channel_count;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_streams = total_subsongs;
|
||||
vgmstream->stream_size = stream_size;
|
||||
vgmstream->meta_type = meta_SQEX_SCD;
|
||||
|
||||
switch (codec) {
|
||||
|
273
src/meta/sqex_sead.c
Normal file
273
src/meta/sqex_sead.c
Normal file
@ -0,0 +1,273 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
static STREAMFILE* setup_sead_hca_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size);
|
||||
|
||||
/* SABF/MABF - Square Enix's "Sead" audio games [Dragon Quest Builders (PS3), Dissidia Opera Omnia (mobile), FF XV (PS4)] */
|
||||
VGMSTREAM * init_vgmstream_sqex_sead(STREAMFILE * streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset, mtrl_offset, meta_offset, post_meta_offset; //, info_offset, name_offset = 0;
|
||||
size_t stream_size, subheader_size; //, name_size = 0;
|
||||
|
||||
int loop_flag = 0, channel_count, codec, sample_rate, loop_start, loop_end;
|
||||
int is_sab = 0, is_mab = 0;
|
||||
int total_subsongs, target_subsong = streamFile->stream_index;
|
||||
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
|
||||
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
|
||||
|
||||
|
||||
/* check extensions (.sab: sound/bgm, .mab: music, .sbin: Dissidia Opera Omnia .sab) */
|
||||
if ( !check_extensions(streamFile,"sab,mab,sbin"))
|
||||
goto fail;
|
||||
|
||||
|
||||
/** main header **/
|
||||
if (read_32bitBE(0x00,streamFile) == 0x73616266) { /* "sabf" */
|
||||
is_sab = 1;
|
||||
} else if (read_32bitBE(0x00,streamFile) == 0x6D616266) { /* "mabf" */
|
||||
is_mab = 1;
|
||||
} else {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
//if (read_8bit(0x04,streamFile) != 0x02) /* version? */
|
||||
// goto fail;
|
||||
/* 0x04(1): version? (usually 0x02, rarely 0x01, ex FF XV title) */
|
||||
/* 0x05(1): 0x00/01? */
|
||||
/* 0x06(2): chunk size? (usually 0x10, rarely 0x20) */
|
||||
if (read_16bitBE(0x06,streamFile) < 0x100) { /* use size as no apparent flag */
|
||||
read_32bit = read_32bitBE;
|
||||
read_16bit = read_16bitBE;
|
||||
} else {
|
||||
read_32bit = read_32bitLE;
|
||||
read_16bit = read_16bitLE;
|
||||
}
|
||||
/* 0x08(1): ?, 0x09(1): ?, 0x0a(2): ? */
|
||||
if (read_32bit(0x0c,streamFile) != get_streamfile_size(streamFile))
|
||||
goto fail;
|
||||
/* 0x10(10): file descriptor ("BGM", "Music", "SE", etc) */
|
||||
|
||||
|
||||
/** offset tables **/
|
||||
if (is_sab) {
|
||||
if (read_32bitBE(0x20,streamFile) != 0x736E6420) goto fail; /* "snd " (info) */
|
||||
if (read_32bitBE(0x30,streamFile) != 0x73657120) goto fail; /* "seq " (unknown) */
|
||||
if (read_32bitBE(0x40,streamFile) != 0x74726B20) goto fail; /* "trk " (unknown) */
|
||||
if (read_32bitBE(0x50,streamFile) != 0x6D74726C) goto fail; /* "mtrl" (headers/streams) */
|
||||
//info_offset = read_32bit(0x28,streamFile);
|
||||
//seq_offset = read_32bit(0x38,streamFile);
|
||||
//trk_offset = read_32bit(0x48,streamFile);
|
||||
mtrl_offset = read_32bit(0x58,streamFile);
|
||||
}
|
||||
else if (is_mab) {
|
||||
if (read_32bitBE(0x20,streamFile) != 0x6D757363) goto fail; /* "musc" (info) */
|
||||
if (read_32bitBE(0x30,streamFile) != 0x696E7374) goto fail; /* "inst" (unknown) */
|
||||
if (read_32bitBE(0x40,streamFile) != 0x6D74726C) goto fail; /* "mtrl" (headers/streams) */
|
||||
//info_offset = read_32bit(0x28,streamFile);
|
||||
//inst_offset = read_32bit(0x38,streamFile);
|
||||
mtrl_offset = read_32bit(0x48,streamFile);
|
||||
}
|
||||
else {
|
||||
goto fail;
|
||||
}
|
||||
/* each section starts with:
|
||||
* 0x00(2): 0x00/01?, 0x02: size? (0x10), 0x04(2): entries, 0x06+: padded to 0x10
|
||||
* 0x10+0x04*entry: offset from section start, also padded to 0x10 at the end */
|
||||
|
||||
/* find meta_offset in mtrl and total subsongs */
|
||||
{
|
||||
int i;
|
||||
int entries = read_16bit(mtrl_offset+0x04,streamFile);
|
||||
off_t entries_offset = mtrl_offset + 0x10;
|
||||
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
total_subsongs = 0;
|
||||
meta_offset = 0;
|
||||
|
||||
/* manually find subsongs as entries can be dummy (ex. sfx banks in Dissidia Opera Omnia) */
|
||||
for (i = 0; i < entries; i++) {
|
||||
off_t entry_offset = mtrl_offset + read_32bit(entries_offset + i*0x04,streamFile);
|
||||
|
||||
if (read_8bit(entry_offset+0x05,streamFile) == 0)
|
||||
continue; /* codec 0 when dummy */
|
||||
|
||||
total_subsongs++;
|
||||
if (!meta_offset && total_subsongs == target_subsong)
|
||||
meta_offset = entry_offset;
|
||||
}
|
||||
if (meta_offset == 0) goto fail;
|
||||
/* SAB can contain 0 entries too */
|
||||
}
|
||||
|
||||
|
||||
/** stream header **/
|
||||
/* 0x00(2): 0x00/01? */
|
||||
/* 0x02(2): base entry size? (0x20) */
|
||||
channel_count = read_8bit(meta_offset+0x04,streamFile);
|
||||
codec = read_8bit(meta_offset+0x05,streamFile);
|
||||
//entry_id = read_16bit(meta_offset+0x06,streamFile);
|
||||
sample_rate = read_32bit(meta_offset+0x08,streamFile);
|
||||
loop_start = read_32bit(meta_offset+0x0c,streamFile); /* in samples but usually ignored */
|
||||
|
||||
loop_end = read_32bit(meta_offset+0x10,streamFile);
|
||||
subheader_size = read_32bit(meta_offset+0x14,streamFile); /* including subfile header */
|
||||
stream_size = read_32bit(meta_offset+0x18,streamFile); /* not including subfile header */
|
||||
/* 0x1c: null? */
|
||||
|
||||
loop_flag = (loop_end > 0);
|
||||
post_meta_offset = meta_offset + 0x20;
|
||||
|
||||
|
||||
/** info section (get stream name) **/
|
||||
//if (is_sab) { //todo load name based on entry id
|
||||
/* "snd ": unknown flags/sizes and name */
|
||||
/* 0x08(2): file number within descriptor */
|
||||
/* 0x1a(2): base_entry size (-0x10?) */
|
||||
//name_size = read_32bit(snd_offset+0x20,streamFile);
|
||||
//name_offset = snd_offset+0x70;
|
||||
/* 0x24(4): unique id? (referenced in "seq" section) */
|
||||
//}
|
||||
//else if (is_mab) {
|
||||
/* "musc": unknown flags sizes and names, another format */
|
||||
//}
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_streams = total_subsongs;
|
||||
vgmstream->stream_size = stream_size;
|
||||
vgmstream->meta_type = is_sab ? meta_SQEX_SAB : meta_SQEX_MAB;
|
||||
|
||||
switch(codec) {
|
||||
|
||||
case 0x02: { /* MSADPCM [Dragon Quest Builders (Vita) sfx] */
|
||||
start_offset = post_meta_offset + subheader_size;
|
||||
|
||||
/* 0x00 (2): null?, 0x02(2): entry size? */
|
||||
vgmstream->coding_type = coding_MSADPCM;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->interleave_block_size = read_16bit(post_meta_offset+0x04,streamFile);
|
||||
|
||||
/* much like AKBs, there are slightly different loop values here, probably more accurate
|
||||
* (if no loop, loop_end doubles as num_samples) */
|
||||
vgmstream->num_samples = msadpcm_bytes_to_samples(stream_size, vgmstream->interleave_block_size, vgmstream->channels);
|
||||
vgmstream->loop_start_sample = read_32bit(post_meta_offset+0x08, streamFile); //loop_start
|
||||
vgmstream->loop_end_sample = read_32bit(post_meta_offset+0x0c, streamFile); //loop_end
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef VGM_USE_ATRAC9
|
||||
case 0x04: { /* ATRAC9 [Dragon Quest Builders (Vita), Final Fantaxy XV (PS4)] */
|
||||
atrac9_config cfg = {0};
|
||||
|
||||
start_offset = post_meta_offset + subheader_size;
|
||||
/* post header has various typical ATRAC9 values */
|
||||
cfg.channels = vgmstream->channels;
|
||||
cfg.config_data = read_32bit(post_meta_offset+0x0c,streamFile);
|
||||
cfg.encoder_delay = read_32bit(post_meta_offset+0x18,streamFile);
|
||||
VGM_LOG("1\n");
|
||||
vgmstream->codec_data = init_atrac9(&cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_ATRAC9;
|
||||
vgmstream->layout_type = layout_none;
|
||||
VGM_LOG("2\n");
|
||||
vgmstream->sample_rate = read_32bit(post_meta_offset+0x1c,streamFile); /* SAB's sample rate can be different but it's ignored */
|
||||
vgmstream->num_samples = read_32bit(post_meta_offset+0x10,streamFile); /* loop values above are also weird and ignored */
|
||||
vgmstream->loop_start_sample = read_32bit(post_meta_offset+0x20, streamFile) - (loop_flag ? cfg.encoder_delay : 0); //loop_start
|
||||
vgmstream->loop_end_sample = read_32bit(post_meta_offset+0x24, streamFile) - (loop_flag ? cfg.encoder_delay : 0); //loop_end
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
case 0x06: { /* MSF subfile (MPEG mode) [Dragon Quest Builders (PS3)] */
|
||||
mpeg_codec_data *mpeg_data = NULL;
|
||||
mpeg_custom_config cfg = {0};
|
||||
|
||||
start_offset = post_meta_offset + subheader_size;
|
||||
/* post header is a proper MSF, but sample rate/loops are ignored in favor of SAB's */
|
||||
|
||||
mpeg_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg);
|
||||
if (!mpeg_data) goto fail;
|
||||
vgmstream->codec_data = mpeg_data;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->num_samples = mpeg_bytes_to_samples(stream_size, mpeg_data);
|
||||
vgmstream->loop_start_sample = loop_start;
|
||||
vgmstream->loop_end_sample = loop_end;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
case 0x07: { /* HCA subfile [Dissidia Opera Omnia (Mobile), Final Fantaxy XV (PS4)] */
|
||||
//todo there is no easy way to use the HCA decoder; try subfile hack for now
|
||||
VGMSTREAM *temp_vgmstream = NULL;
|
||||
STREAMFILE *temp_streamFile = NULL;
|
||||
off_t subfile_offset = post_meta_offset + 0x10;
|
||||
size_t subfile_size = stream_size + subheader_size - 0x10;
|
||||
/* post header has 0x10 unknown + HCA header */
|
||||
|
||||
|
||||
temp_streamFile = setup_sead_hca_streamfile(streamFile, subfile_offset, subfile_size);
|
||||
if (!temp_streamFile) goto fail;
|
||||
|
||||
temp_vgmstream = init_vgmstream_hca(temp_streamFile);
|
||||
if (temp_vgmstream) {
|
||||
/* loops can be slightly different (~1000 samples) but probably HCA's are more accurate */
|
||||
temp_vgmstream->num_streams = vgmstream->num_streams;
|
||||
temp_vgmstream->stream_size = vgmstream->stream_size;
|
||||
temp_vgmstream->meta_type = vgmstream->meta_type;
|
||||
|
||||
close_streamfile(temp_streamFile);
|
||||
close_vgmstream(vgmstream);
|
||||
return temp_vgmstream;
|
||||
}
|
||||
else {
|
||||
close_streamfile(temp_streamFile);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
case 0x00: /* dummy entry */
|
||||
default:
|
||||
VGM_LOG("SQEX SEAD: unknown codec %x\n", codec);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* open the file for reading */
|
||||
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static STREAMFILE* setup_sead_hca_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size) {
|
||||
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
|
||||
|
||||
/* setup subfile */
|
||||
new_streamFile = open_wrap_streamfile(streamFile);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,"hca");
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
return temp_streamFile;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_streamFile);
|
||||
return NULL;
|
||||
}
|
@ -7,13 +7,13 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
STREAMFILE * streamHeader = NULL;
|
||||
off_t start_offset, chunk_offset, first_offset = 0x60, name_offset = 0;
|
||||
size_t chunk_size;
|
||||
size_t chunk_size, stream_size = 0;
|
||||
|
||||
int is_separate;
|
||||
int loop_flag, channels, codec;
|
||||
int sample_rate, num_samples, loop_start_sample, loop_end_sample;
|
||||
uint32_t at9_config_data = 0;
|
||||
int total_streams, target_stream = streamFile->stream_index;
|
||||
int total_subsongs, target_subsong = streamFile->stream_index;
|
||||
|
||||
|
||||
/* check extension, case insensitive */
|
||||
@ -38,16 +38,16 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
|
||||
if (!find_chunk_le(streamHeader, 0x57415645,first_offset,0, &chunk_offset,&chunk_size)) goto fail; /* "WAVE" */
|
||||
|
||||
/* check multi-streams (usually only in SFX containers) */
|
||||
total_streams = read_32bitLE(chunk_offset+0x04,streamHeader);
|
||||
if (target_stream == 0) target_stream = 1;
|
||||
if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail;
|
||||
total_subsongs = read_32bitLE(chunk_offset+0x04,streamHeader);
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
|
||||
|
||||
/* read stream header */
|
||||
{
|
||||
off_t table_offset, header_offset, stream_offset;
|
||||
|
||||
/* get target offset using table of relative offsets within WAVE */
|
||||
table_offset = chunk_offset + 0x08 + 4*(target_stream-1);
|
||||
table_offset = chunk_offset + 0x08 + 4*(target_subsong-1);
|
||||
header_offset = table_offset + read_32bitLE(table_offset,streamHeader);
|
||||
|
||||
/* 0x00(4): type/location? (00/01=sxd/RAM?, 02/03=sxd2/stream?) */
|
||||
@ -59,7 +59,7 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
|
||||
num_samples = read_32bitLE(header_offset+0x14,streamHeader);
|
||||
loop_start_sample = read_32bitLE(header_offset+0x18,streamHeader);
|
||||
loop_end_sample = read_32bitLE(header_offset+0x1c,streamHeader);
|
||||
/* 0x20(4): data size */
|
||||
stream_size = read_32bitLE(header_offset+0x20,streamHeader);
|
||||
stream_offset = read_32bitLE(header_offset+0x24,streamHeader);
|
||||
|
||||
/* Extra data, variable sized and uses some kind of TLVs (HEVAG's is optional and much smaller).
|
||||
@ -100,7 +100,7 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
|
||||
int num_entries = read_16bitLE(chunk_offset+0x04,streamHeader); /* can be bigger than streams */
|
||||
for (i = 0; i < num_entries; i++) {
|
||||
uint32_t index = (uint32_t)read_32bitLE(chunk_offset+0x08 + 0x08 + i*0x0c,streamHeader);
|
||||
if (index+1 == target_stream) {
|
||||
if (index+1 == target_subsong) {
|
||||
name_offset = chunk_offset+0x08 + 0x00 + i*0x0c + read_32bitLE(chunk_offset+0x08 + 0x00 + i*0x0c,streamHeader);
|
||||
break;
|
||||
}
|
||||
@ -116,7 +116,8 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
|
||||
vgmstream->num_samples = num_samples;
|
||||
vgmstream->loop_start_sample = loop_start_sample;
|
||||
vgmstream->loop_end_sample = loop_end_sample;
|
||||
vgmstream->num_streams = total_streams;
|
||||
vgmstream->num_streams = total_subsongs;
|
||||
vgmstream->stream_size = stream_size;
|
||||
vgmstream->meta_type = meta_SXD;
|
||||
if (name_offset)
|
||||
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamHeader);
|
||||
@ -148,7 +149,7 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
//case 0x28: /* dummy codec? (found with 0 samples) [Hot Shots Golf: World Invitational (Vita) sfx] */
|
||||
default:
|
||||
VGM_LOG("SXD: unknown codec 0x%x\n", codec);
|
||||
goto fail;
|
||||
|
@ -127,6 +127,7 @@ VGMSTREAM * init_vgmstream_ubi_sb(STREAMFILE *streamFile) {
|
||||
|
||||
vgmstream->sample_rate = sb.sample_rate;
|
||||
vgmstream->num_streams = sb.total_streams;
|
||||
vgmstream->stream_size = sb.stream_size;
|
||||
vgmstream->meta_type = meta_UBI_SB;
|
||||
|
||||
|
||||
|
@ -7,7 +7,7 @@ VGMSTREAM * init_vgmstream_vxn(STREAMFILE *streamFile) {
|
||||
int loop_flag = 0, channel_count, codec, sample_rate, block_align, bits, num_samples;
|
||||
off_t start_offset, stream_offset, chunk_offset, first_offset = 0x00;
|
||||
size_t stream_size;
|
||||
int total_streams, target_stream = streamFile->stream_index;
|
||||
int total_subsongs, target_subsong = streamFile->stream_index;
|
||||
|
||||
/* check extensions */
|
||||
if (!check_extensions(streamFile,"vxn"))
|
||||
@ -31,13 +31,13 @@ VGMSTREAM * init_vgmstream_vxn(STREAMFILE *streamFile) {
|
||||
* (the "Plst" and "Rule" chunks may have order info) */
|
||||
if (!find_chunk_le(streamFile, 0x5365676D,first_offset,0, &chunk_offset,NULL)) /* "Segm" */
|
||||
goto fail;
|
||||
total_streams = read_32bitLE(chunk_offset+0x00, streamFile);
|
||||
if (target_stream == 0) target_stream = 1;
|
||||
if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail;
|
||||
total_subsongs = read_32bitLE(chunk_offset+0x00, streamFile);
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
|
||||
|
||||
stream_offset = read_32bitLE(chunk_offset+0x04 + (target_stream-1)*0x18 + 0x00, streamFile);
|
||||
stream_size = read_32bitLE(chunk_offset+0x04 + (target_stream-1)*0x18 + 0x04, streamFile);
|
||||
num_samples = read_32bitLE(chunk_offset+0x04 + (target_stream-1)*0x18 + 0x08, streamFile);
|
||||
stream_offset = read_32bitLE(chunk_offset+0x04 + (target_subsong-1)*0x18 + 0x00, streamFile);
|
||||
stream_size = read_32bitLE(chunk_offset+0x04 + (target_subsong-1)*0x18 + 0x04, streamFile);
|
||||
num_samples = read_32bitLE(chunk_offset+0x04 + (target_subsong-1)*0x18 + 0x08, streamFile);
|
||||
|
||||
if (!find_chunk_le(streamFile, 0x44617461,first_offset,0, &chunk_offset,NULL)) /* "Data" */
|
||||
goto fail;
|
||||
@ -50,8 +50,8 @@ VGMSTREAM * init_vgmstream_vxn(STREAMFILE *streamFile) {
|
||||
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = num_samples;
|
||||
vgmstream->num_streams = total_streams;
|
||||
|
||||
vgmstream->num_streams = total_subsongs;
|
||||
vgmstream->stream_size = stream_size;
|
||||
vgmstream->meta_type = meta_VXN;
|
||||
|
||||
switch (codec) {
|
||||
|
@ -233,25 +233,25 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
||||
case 0x28: /* early (~2009), ex. The Lord of the Rings: Conquest PC */
|
||||
data_offsets = 0x18;
|
||||
block_offsets = 0; /* no need, full headers are present */
|
||||
cfg.header_type = TYPE_8;
|
||||
cfg.packet_type = STANDARD;
|
||||
cfg.setup_type = HEADER_TRIAD;
|
||||
cfg.header_type = WWV_TYPE_8;
|
||||
cfg.packet_type = WWV_STANDARD;
|
||||
cfg.setup_type = WWV_HEADER_TRIAD;
|
||||
break;
|
||||
|
||||
//case 0x32: /* ? */
|
||||
case 0x34: /* common (2010~2011) */
|
||||
data_offsets = 0x18;
|
||||
block_offsets = 0x30;
|
||||
cfg.header_type = TYPE_6;
|
||||
cfg.packet_type = STANDARD;
|
||||
cfg.setup_type = EXTERNAL_CODEBOOKS; /* setup_type will be corrected later */
|
||||
cfg.header_type = WWV_TYPE_6;
|
||||
cfg.packet_type = WWV_STANDARD;
|
||||
cfg.setup_type = WWV_EXTERNAL_CODEBOOKS; /* setup_type will be corrected later */
|
||||
break;
|
||||
case 0x2a: /* uncommon (mid 2011), ex. infamous 2 PS3 */
|
||||
data_offsets = 0x10;
|
||||
block_offsets = 0x28;
|
||||
cfg.header_type = TYPE_2;
|
||||
cfg.packet_type = MODIFIED;
|
||||
cfg.setup_type = EXTERNAL_CODEBOOKS;
|
||||
cfg.header_type = WWV_TYPE_2;
|
||||
cfg.packet_type = WWV_MODIFIED;
|
||||
cfg.setup_type = WWV_EXTERNAL_CODEBOOKS;
|
||||
break;
|
||||
default:
|
||||
VGM_LOG("WWISE: unknown vorb size 0x%x\n", vorb_size);
|
||||
@ -277,11 +277,11 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
||||
|
||||
/* if the setup after header starts with "(data)BCV" it's an inline codebook) */
|
||||
if ((id & 0x00FFFFFF) == 0x00424356) { /* 0"BCV" */
|
||||
cfg.setup_type = FULL_SETUP;
|
||||
cfg.setup_type = WWV_FULL_SETUP;
|
||||
}
|
||||
/* if the setup is suspiciously big it's probably trimmed inline codebooks */
|
||||
else if (setup_size > 0x200) { /* an external setup it's ~0x100 max + some threshold */
|
||||
cfg.setup_type = INLINE_CODEBOOKS;
|
||||
cfg.setup_type = WWV_INLINE_CODEBOOKS;
|
||||
}
|
||||
}
|
||||
|
||||
@ -297,13 +297,13 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
||||
case 0x30:
|
||||
data_offsets = 0x10;
|
||||
block_offsets = 0x28;
|
||||
cfg.header_type = TYPE_2;
|
||||
cfg.packet_type = MODIFIED;
|
||||
cfg.header_type = WWV_TYPE_2;
|
||||
cfg.packet_type = WWV_MODIFIED;
|
||||
|
||||
/* setup not detectable by header, so we'll try both; hopefully libvorbis will reject wrong codebooks
|
||||
* - standard: early (<2012), ex. The King of Fighters XIII X360 (2011/11), .ogg (cbs are from aoTuV, too)
|
||||
* - aoTuV603: later (>2012), ex. Sonic & All-Stars Racing Transformed PC (2012/11), .wem */
|
||||
cfg.setup_type = is_wem ? AOTUV603_CODEBOOKS : EXTERNAL_CODEBOOKS; /* aoTuV came along .wem */
|
||||
cfg.setup_type = is_wem ? WWV_AOTUV603_CODEBOOKS : WWV_EXTERNAL_CODEBOOKS; /* aoTuV came along .wem */
|
||||
break;
|
||||
|
||||
//case 0x2a: /* Rocksmith 2011 X360? */
|
||||
@ -326,14 +326,14 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
||||
/* all blocksizes I've seen are 0x08+0x0B except Oddworld PSV, that uses 0x09+0x09
|
||||
* (maybe lower spec machines = needs simpler packets) */
|
||||
if (cfg.blocksize_0_exp == cfg.blocksize_1_exp)
|
||||
cfg.packet_type = STANDARD;
|
||||
cfg.packet_type = WWV_STANDARD;
|
||||
}
|
||||
|
||||
/* try with the selected codebooks */
|
||||
vgmstream->codec_data = init_vorbis_custom(streamFile, start_offset + setup_offset, VORBIS_WWISE, &cfg);
|
||||
if (!vgmstream->codec_data) {
|
||||
/* codebooks failed: try again with the other type */
|
||||
cfg.setup_type = is_wem ? EXTERNAL_CODEBOOKS : AOTUV603_CODEBOOKS;
|
||||
cfg.setup_type = is_wem ? WWV_EXTERNAL_CODEBOOKS : WWV_AOTUV603_CODEBOOKS;
|
||||
vgmstream->codec_data = init_vorbis_custom(streamFile, start_offset + setup_offset, VORBIS_WWISE, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) {
|
||||
|
||||
off_t start_offset, loop_start = 0, loop_end = 0, chunk_offset;
|
||||
off_t first_offset = 0x20;
|
||||
size_t chunk_size;
|
||||
size_t chunk_size, stream_size;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
if (!check_extensions(streamFile,"xvag"))
|
||||
@ -51,7 +51,7 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) {
|
||||
|
||||
interleave_factor = read_32bit(chunk_offset+0x10,streamFile);
|
||||
sample_rate = read_32bit(chunk_offset+0x14,streamFile);
|
||||
/* 0x18: datasize */
|
||||
stream_size = read_32bit(chunk_offset+0x18,streamFile);
|
||||
|
||||
/* extra data, seen in versions 0x61+ */
|
||||
if (chunk_size > 0x1c) {
|
||||
@ -86,6 +86,7 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) {
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = num_samples;
|
||||
vgmstream->num_streams = total_subsongs;
|
||||
vgmstream->stream_size = (stream_size / total_subsongs);
|
||||
vgmstream->meta_type = meta_XVAG;
|
||||
|
||||
switch (codec) {
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
/* the x.x version is just to make it clearer, MS only classifies XACT as 1/2/3 */
|
||||
#define XACT1_0_MAX 1 /* Project Gotham Racing 2 (v1), Silent Hill 4 (v1) */
|
||||
#define XACT1_1_MAX 3 /* The King of Fighters 2003 (v3) */
|
||||
#define XACT1_1_MAX 3 /* Unreal Championship (v2), The King of Fighters 2003 (v3) */
|
||||
#define XACT2_0_MAX 34 /* Dead or Alive 4 (v17), Kameo (v23), Table Tennis (v34) */ // v35/36/37 too?
|
||||
#define XACT2_1_MAX 38 /* Prey (v38) */ // v39 too?
|
||||
#define XACT2_2_MAX 41 /* Blue Dragon (v40) */
|
||||
@ -354,47 +354,45 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) {
|
||||
vgmstream->loop_start_sample = xwb.loop_start_sample;
|
||||
vgmstream->loop_end_sample = xwb.loop_end_sample;
|
||||
vgmstream->num_streams = xwb.streams;
|
||||
vgmstream->stream_size = xwb.stream_size;
|
||||
vgmstream->meta_type = meta_XWB;
|
||||
get_xsb_name(vgmstream->stream_name,STREAM_NAME_SIZE, target_stream, &xwb, streamFile);
|
||||
|
||||
switch(xwb.codec) {
|
||||
case PCM:
|
||||
vgmstream->coding_type = xwb.bits_per_sample == 0 ? coding_PCM8 :
|
||||
case PCM: /* Unreal Championship (Xbox)[PCM8], KOF2003 (Xbox)[PCM16LE], Otomedius (X360)[PCM16BE] */
|
||||
vgmstream->coding_type = xwb.bits_per_sample == 0 ? coding_PCM8_U :
|
||||
(xwb.little_endian ? coding_PCM16LE : coding_PCM16BE);
|
||||
vgmstream->layout_type = xwb.channels > 1 ? layout_interleave : layout_none;
|
||||
vgmstream->interleave_block_size = xwb.bits_per_sample == 0 ? 0x01 : 0x02;
|
||||
break;
|
||||
|
||||
case XBOX_ADPCM:
|
||||
case XBOX_ADPCM: /* Silent Hill 4 (Xbox) */
|
||||
vgmstream->coding_type = coding_XBOX;
|
||||
vgmstream->layout_type = layout_none;
|
||||
break;
|
||||
|
||||
case MS_ADPCM:
|
||||
case MS_ADPCM: /* Persona 4 Ultimax (AC) */
|
||||
vgmstream->coding_type = coding_MSADPCM;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->interleave_block_size = (xwb.block_align + 22) * xwb.channels; /*22=CONVERSION_OFFSET (?)*/
|
||||
break;
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case XMA1: {
|
||||
ffmpeg_codec_data *ffmpeg_data = NULL;
|
||||
case XMA1: { /* Kameo (X360) */
|
||||
uint8_t buf[100];
|
||||
int bytes;
|
||||
|
||||
bytes = ffmpeg_make_riff_xma1(buf, 100, vgmstream->num_samples, xwb.stream_size, vgmstream->channels, vgmstream->sample_rate, 0);
|
||||
if (bytes <= 0) goto fail;
|
||||
|
||||
ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size);
|
||||
if ( !ffmpeg_data ) goto fail;
|
||||
vgmstream->codec_data = ffmpeg_data;
|
||||
vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
break;
|
||||
}
|
||||
|
||||
case XMA2: {
|
||||
ffmpeg_codec_data *ffmpeg_data = NULL;
|
||||
case XMA2: { /* Blue Dragon (X360) */
|
||||
uint8_t buf[100];
|
||||
int bytes, block_size, block_count;
|
||||
|
||||
@ -404,15 +402,14 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) {
|
||||
bytes = ffmpeg_make_riff_xma2(buf, 100, vgmstream->num_samples, xwb.stream_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size);
|
||||
if (bytes <= 0) goto fail;
|
||||
|
||||
ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size);
|
||||
if ( !ffmpeg_data ) goto fail;
|
||||
vgmstream->codec_data = ffmpeg_data;
|
||||
vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
break;
|
||||
}
|
||||
|
||||
case WMA: { /* WMAudio1 (WMA v1) */
|
||||
case WMA: { /* WMAudio1 (WMA v1): Prince of Persia 2 port (Xbox) */
|
||||
ffmpeg_codec_data *ffmpeg_data = NULL;
|
||||
|
||||
ffmpeg_data = init_ffmpeg_offset(streamFile, xwb.stream_offset,xwb.stream_size);
|
||||
@ -427,8 +424,7 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) {
|
||||
break;
|
||||
}
|
||||
|
||||
case XWMA: { /* WMAudio2 (WMA v2), WMAudio3 (WMA Pro) */
|
||||
ffmpeg_codec_data *ffmpeg_data = NULL;
|
||||
case XWMA: { /* WMAudio2 (WMA v2): BlazBlue (X360), WMAudio3 (WMA Pro): ? */
|
||||
uint8_t buf[100];
|
||||
int bytes, bps_index, block_align, block_index, avg_bps, wma_codec;
|
||||
|
||||
@ -444,15 +440,14 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) {
|
||||
bytes = ffmpeg_make_riff_xwma(buf, 100, wma_codec, xwb.stream_size, vgmstream->channels, vgmstream->sample_rate, avg_bps, block_align);
|
||||
if (bytes <= 0) goto fail;
|
||||
|
||||
ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size);
|
||||
if ( !ffmpeg_data ) goto fail;
|
||||
vgmstream->codec_data = ffmpeg_data;
|
||||
vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
break;
|
||||
}
|
||||
|
||||
case ATRAC3: { /* Techland PS3 extension */
|
||||
case ATRAC3: { /* Techland PS3 extension: Sniper Ghost Warrior (PS3) */
|
||||
uint8_t buf[200];
|
||||
int bytes;
|
||||
|
||||
|
108
src/meta/xwc.c
Normal file
108
src/meta/xwc.c
Normal file
@ -0,0 +1,108 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* .XWC - Starbreeze games [Chronicles of Riddick: Assault on Dark Athena, Syndicate] */
|
||||
VGMSTREAM * init_vgmstream_xwc(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
size_t data_size;
|
||||
int loop_flag, channel_count, codec;
|
||||
|
||||
/* check extensions (.xwc is the extension of the bigfile, individual files don't have one) */
|
||||
if ( !check_extensions(streamFile,"xwc"))
|
||||
goto fail;
|
||||
|
||||
if(read_32bitBE(0x00,streamFile) != 0x00040000 && /* version? */
|
||||
read_32bitBE(0x04,streamFile) != 0x00900000)
|
||||
goto fail;
|
||||
|
||||
data_size = read_32bitLE(0x08, streamFile); /* including subheader */
|
||||
channel_count = read_32bitLE(0x0c, streamFile);
|
||||
/* 0x10: num_samples */
|
||||
/* 0x14: 0x8000? */
|
||||
codec = read_32bitBE(0x24, streamFile);
|
||||
/* 0x28: num_samples */
|
||||
/* 0x2c: config data? (first nibble: 0x4=mono, 0x8=stereo) */
|
||||
/* 0x30+: codec dependant */
|
||||
loop_flag = 0; /* seemingly not in the file */
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->num_samples = read_32bitLE(0x28, streamFile);
|
||||
vgmstream->meta_type = meta_XWC;
|
||||
|
||||
switch(codec) {
|
||||
#ifdef VGM_USE_MPEG
|
||||
case 0x4D504547: { /* "MPEG" (PS3) */
|
||||
mpeg_custom_config cfg = {0};
|
||||
|
||||
start_offset = 0x800;
|
||||
vgmstream->num_samples = read_32bitLE(0x30, streamFile); /* with encoder delay */ //todo improve
|
||||
cfg.data_size = read_32bitLE(0x34, streamFile); //data_size - 0x28;
|
||||
|
||||
vgmstream->codec_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->sample_rate = ((mpeg_codec_data*)vgmstream->codec_data)->sample_rate_per_frame;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case 0x584D4100: { /* "XMA\0" (X360) */
|
||||
uint8_t buf[0x100];
|
||||
int32_t bytes, seek_size, block_size, block_count, sample_rate;
|
||||
|
||||
seek_size = read_32bitLE(0x30, streamFile);
|
||||
start_offset = 0x34 + seek_size + read_32bitLE(0x34+seek_size, streamFile) + 0x08;
|
||||
start_offset += (start_offset % 0x800) ? 0x800 - (start_offset % 0x800) : 0; /* padded */
|
||||
|
||||
sample_rate = read_32bitBE(0x34+seek_size+0x10, streamFile);
|
||||
block_size = read_32bitBE(0x34+seek_size+0x1c, streamFile);
|
||||
block_count = read_32bitBE(0x34+seek_size+0x28, streamFile);
|
||||
/* others: scrambled RIFF fmt BE values */
|
||||
|
||||
bytes = ffmpeg_make_riff_xma2(buf,0x100, vgmstream->num_samples, data_size, vgmstream->channels, sample_rate, block_count, block_size);
|
||||
if (bytes <= 0) goto fail;
|
||||
|
||||
vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size - start_offset - 0x28);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x564F5242: { /* "VORB" (PC) */
|
||||
start_offset = 0x30;
|
||||
|
||||
vgmstream->codec_data = init_ffmpeg_offset(streamFile, start_offset, data_size - start_offset - 0x28);
|
||||
if ( !vgmstream->codec_data ) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->sample_rate = read_32bitLE(start_offset + 0x28, streamFile);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (vgmstream->sample_rate != 48000) { /* get from config data instead of codecs? */
|
||||
VGM_LOG("XWC: unexpected sample rate %i\n",vgmstream->sample_rate);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
253
src/streamfile.c
253
src/streamfile.c
@ -250,6 +250,8 @@ STREAMFILE * open_stdio_streamfile_by_file(FILE * file, const char * filename) {
|
||||
/* **************************************************** */
|
||||
|
||||
//todo stream_index: copy? pass? funtion? external?
|
||||
//todo use realnames on reopen? simplify?
|
||||
//todo use safe string ops, this ain't easy
|
||||
|
||||
typedef struct {
|
||||
STREAMFILE sf;
|
||||
@ -331,8 +333,18 @@ static void clamp_get_realname(CLAMP_STREAMFILE *streamfile, char *buffer, size_
|
||||
streamfile->inner_sf->get_realname(streamfile->inner_sf, buffer, length); /* default */
|
||||
}
|
||||
static STREAMFILE *clamp_open(CLAMP_STREAMFILE *streamfile, const char * const filename, size_t buffersize) {
|
||||
STREAMFILE *new_inner_sf = streamfile->inner_sf->open(streamfile->inner_sf,filename,buffersize);
|
||||
return open_clamp_streamfile(new_inner_sf, streamfile->start, streamfile->size);
|
||||
char original_filename[PATH_LIMIT];
|
||||
STREAMFILE *new_inner_sf;
|
||||
|
||||
new_inner_sf = streamfile->inner_sf->open(streamfile->inner_sf,filename,buffersize);
|
||||
streamfile->inner_sf->get_name(streamfile->inner_sf, original_filename, PATH_LIMIT);
|
||||
|
||||
/* detect re-opening the file */
|
||||
if (strcmp(filename, original_filename) == 0) {
|
||||
return open_clamp_streamfile(new_inner_sf, streamfile->start, streamfile->size); /* clamp again */
|
||||
} else {
|
||||
return new_inner_sf; /**/
|
||||
}
|
||||
}
|
||||
static void clamp_close(CLAMP_STREAMFILE *streamfile) {
|
||||
streamfile->inner_sf->close(streamfile->inner_sf);
|
||||
@ -342,7 +354,8 @@ static void clamp_close(CLAMP_STREAMFILE *streamfile) {
|
||||
STREAMFILE *open_clamp_streamfile(STREAMFILE *streamfile, off_t start, size_t size) {
|
||||
CLAMP_STREAMFILE *this_sf;
|
||||
|
||||
if (!streamfile || !size || start > size) return NULL;
|
||||
if (!streamfile || !size) return NULL;
|
||||
if (start + size > get_streamfile_size(streamfile)) return NULL;
|
||||
|
||||
this_sf = calloc(1,sizeof(CLAMP_STREAMFILE));
|
||||
if (!this_sf) return NULL;
|
||||
@ -391,6 +404,7 @@ static void io_get_realname(IO_STREAMFILE *streamfile, char *buffer, size_t leng
|
||||
streamfile->inner_sf->get_realname(streamfile->inner_sf, buffer, length); /* default */
|
||||
}
|
||||
static STREAMFILE *io_open(IO_STREAMFILE *streamfile, const char * const filename, size_t buffersize) {
|
||||
//todo should have some flag to decide if opening other files with IO
|
||||
STREAMFILE *new_inner_sf = streamfile->inner_sf->open(streamfile->inner_sf,filename,buffersize);
|
||||
return open_io_streamfile(new_inner_sf, streamfile->data, streamfile->data_size, streamfile->read_callback);
|
||||
}
|
||||
@ -436,6 +450,239 @@ STREAMFILE *open_io_streamfile(STREAMFILE *streamfile, void* data, size_t data_s
|
||||
|
||||
/* **************************************************** */
|
||||
|
||||
typedef struct {
|
||||
STREAMFILE sf;
|
||||
|
||||
STREAMFILE *inner_sf;
|
||||
char fakename[PATH_LIMIT];
|
||||
} FAKENAME_STREAMFILE;
|
||||
|
||||
static size_t fakename_read(FAKENAME_STREAMFILE *streamfile, uint8_t * dest, off_t offset, size_t length) {
|
||||
return streamfile->inner_sf->read(streamfile->inner_sf, dest, offset, length); /* default */
|
||||
}
|
||||
static size_t fakename_get_size(FAKENAME_STREAMFILE * streamfile) {
|
||||
return streamfile->inner_sf->get_size(streamfile->inner_sf); /* default */
|
||||
}
|
||||
static size_t fakename_get_offset(FAKENAME_STREAMFILE * streamfile) {
|
||||
return streamfile->inner_sf->get_offset(streamfile->inner_sf); /* default */
|
||||
}
|
||||
static void fakename_get_name(FAKENAME_STREAMFILE *streamfile, char *buffer, size_t length) {
|
||||
strncpy(buffer,streamfile->fakename,length);
|
||||
buffer[length-1]='\0';
|
||||
}
|
||||
static void fakename_get_realname(FAKENAME_STREAMFILE *streamfile, char *buffer, size_t length) {
|
||||
fakename_get_name(streamfile, buffer, length);
|
||||
}
|
||||
static STREAMFILE *fakename_open(FAKENAME_STREAMFILE *streamfile, const char * const filename, size_t buffersize) {
|
||||
/* detect re-opening the file */
|
||||
if (strcmp(filename, streamfile->fakename) == 0) {
|
||||
STREAMFILE *new_inner_sf;
|
||||
char original_filename[PATH_LIMIT];
|
||||
|
||||
streamfile->inner_sf->get_name(streamfile->inner_sf, original_filename, PATH_LIMIT);
|
||||
new_inner_sf = streamfile->inner_sf->open(streamfile->inner_sf, original_filename, buffersize);
|
||||
return open_fakename_streamfile(new_inner_sf, streamfile->fakename, NULL);
|
||||
}
|
||||
else {
|
||||
return streamfile->inner_sf->open(streamfile->inner_sf, filename, buffersize);
|
||||
}
|
||||
}
|
||||
static void fakename_close(FAKENAME_STREAMFILE *streamfile) {
|
||||
streamfile->inner_sf->close(streamfile->inner_sf);
|
||||
free(streamfile);
|
||||
}
|
||||
|
||||
STREAMFILE *open_fakename_streamfile(STREAMFILE *streamfile, char * fakename, char* fakeext) {
|
||||
FAKENAME_STREAMFILE *this_sf;
|
||||
|
||||
if (!streamfile || (!fakename && !fakeext)) return NULL;
|
||||
|
||||
this_sf = calloc(1,sizeof(FAKENAME_STREAMFILE));
|
||||
if (!this_sf) return NULL;
|
||||
|
||||
/* set callbacks and internals */
|
||||
this_sf->sf.read = (void*)fakename_read;
|
||||
this_sf->sf.get_size = (void*)fakename_get_size;
|
||||
this_sf->sf.get_offset = (void*)fakename_get_offset;
|
||||
this_sf->sf.get_name = (void*)fakename_get_name;
|
||||
this_sf->sf.get_realname = (void*)fakename_get_realname;
|
||||
this_sf->sf.open = (void*)fakename_open;
|
||||
this_sf->sf.close = (void*)fakename_close;
|
||||
this_sf->sf.stream_index = streamfile->stream_index;
|
||||
|
||||
this_sf->inner_sf = streamfile;
|
||||
|
||||
/* copy passed name or retain current, and swap extension if expected */
|
||||
if (fakename) {
|
||||
strcpy(this_sf->fakename,fakename);
|
||||
} else {
|
||||
streamfile->get_name(streamfile, this_sf->fakename, PATH_LIMIT);
|
||||
}
|
||||
if (fakeext) {
|
||||
char * ext = strrchr(this_sf->fakename,'.');
|
||||
if (ext != NULL)
|
||||
ext[1] = '\0'; /* truncate past dot */
|
||||
strcat(this_sf->fakename, fakeext);
|
||||
}
|
||||
|
||||
return &this_sf->sf;
|
||||
}
|
||||
|
||||
/* **************************************************** */
|
||||
|
||||
|
||||
typedef struct {
|
||||
STREAMFILE sf;
|
||||
|
||||
STREAMFILE **inner_sfs;
|
||||
size_t inner_sfs_size;
|
||||
size_t *sizes;
|
||||
off_t size;
|
||||
off_t offset;
|
||||
} MULTIFILE_STREAMFILE;
|
||||
|
||||
static size_t multifile_read(MULTIFILE_STREAMFILE *streamfile, uint8_t * dest, off_t offset, size_t length) {
|
||||
int i, segment = 0;
|
||||
off_t segment_offset = 0;
|
||||
size_t done = 0;
|
||||
|
||||
if (offset > streamfile->size) {
|
||||
streamfile->offset = streamfile->size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* map external offset to multifile offset */
|
||||
for (i = 0; i < streamfile->inner_sfs_size; i++) {
|
||||
size_t segment_size = streamfile->sizes[i];
|
||||
/* check if offset falls in this segment */
|
||||
if (offset >= segment_offset && offset < segment_offset + segment_size) {
|
||||
segment = i;
|
||||
segment_offset = offset - segment_offset;
|
||||
break;
|
||||
}
|
||||
|
||||
segment_offset += segment_size;
|
||||
}
|
||||
|
||||
/* reads can span multiple segments */
|
||||
while(done < length) {
|
||||
if (segment >= streamfile->inner_sfs_size) /* over last segment, not fully done */
|
||||
break;
|
||||
/* reads over segment size are ok, will return smaller value and continue next segment */
|
||||
done += streamfile->inner_sfs[segment]->read(streamfile->inner_sfs[segment], dest+done, segment_offset, length - done);
|
||||
segment++;
|
||||
segment_offset = 0;
|
||||
}
|
||||
|
||||
streamfile->offset = offset + done;
|
||||
return done;
|
||||
}
|
||||
static size_t multifile_get_size(MULTIFILE_STREAMFILE *streamfile) {
|
||||
return streamfile->size;
|
||||
}
|
||||
static size_t multifile_get_offset(MULTIFILE_STREAMFILE * streamfile) {
|
||||
return streamfile->offset;
|
||||
}
|
||||
static void multifile_get_name(MULTIFILE_STREAMFILE *streamfile, char *buffer, size_t length) {
|
||||
streamfile->inner_sfs[0]->get_name(streamfile->inner_sfs[0], buffer, length);
|
||||
}
|
||||
static void multifile_get_realname(MULTIFILE_STREAMFILE *streamfile, char *buffer, size_t length) {
|
||||
multifile_get_name(streamfile, buffer, length);
|
||||
}
|
||||
static STREAMFILE *multifile_open(MULTIFILE_STREAMFILE *streamfile, const char * const filename, size_t buffersize) {
|
||||
char original_filename[PATH_LIMIT];
|
||||
STREAMFILE *new_sf = NULL;
|
||||
STREAMFILE **new_inner_sfs = NULL;
|
||||
int i;
|
||||
|
||||
streamfile->inner_sfs[0]->get_name(streamfile->inner_sfs[0], original_filename, PATH_LIMIT);
|
||||
|
||||
/* detect re-opening the file */
|
||||
if (strcmp(filename, original_filename) == 0) { /* same multifile */
|
||||
new_inner_sfs = calloc(streamfile->inner_sfs_size, sizeof(STREAMFILE*));
|
||||
if (!new_inner_sfs) goto fail;
|
||||
|
||||
for (i = 0; i < streamfile->inner_sfs_size; i++) {
|
||||
streamfile->inner_sfs[i]->get_name(streamfile->inner_sfs[i], original_filename, PATH_LIMIT);
|
||||
new_inner_sfs[i] = streamfile->inner_sfs[i]->open(streamfile->inner_sfs[i], original_filename, buffersize);
|
||||
if (!new_inner_sfs[i]) goto fail;
|
||||
}
|
||||
|
||||
new_sf = open_multifile_streamfile(new_inner_sfs, streamfile->inner_sfs_size);
|
||||
if (!new_sf) goto fail;
|
||||
|
||||
return new_sf;
|
||||
}
|
||||
else {
|
||||
return streamfile->inner_sfs[0]->open(streamfile->inner_sfs[0], filename, buffersize); /* regular file */
|
||||
}
|
||||
|
||||
fail:
|
||||
if (new_inner_sfs) {
|
||||
for (i = 0; i < streamfile->inner_sfs_size; i++)
|
||||
close_streamfile(new_inner_sfs[i]);
|
||||
}
|
||||
free(new_inner_sfs);
|
||||
return NULL;
|
||||
}
|
||||
static void multifile_close(MULTIFILE_STREAMFILE *streamfile) {
|
||||
int i;
|
||||
for (i = 0; i < streamfile->inner_sfs_size; i++) {
|
||||
for (i = 0; i < streamfile->inner_sfs_size; i++)
|
||||
close_streamfile(streamfile->inner_sfs[i]);
|
||||
}
|
||||
free(streamfile->inner_sfs);
|
||||
free(streamfile->sizes);
|
||||
free(streamfile);
|
||||
}
|
||||
|
||||
STREAMFILE *open_multifile_streamfile(STREAMFILE **streamfiles, size_t streamfiles_size) {
|
||||
MULTIFILE_STREAMFILE *this_sf;
|
||||
int i;
|
||||
|
||||
if (!streamfiles || !streamfiles_size) return NULL;
|
||||
for (i = 0; i < streamfiles_size; i++) {
|
||||
if (!streamfiles[i]) return NULL;
|
||||
}
|
||||
|
||||
this_sf = calloc(1,sizeof(MULTIFILE_STREAMFILE));
|
||||
if (!this_sf) goto fail;
|
||||
|
||||
/* set callbacks and internals */
|
||||
this_sf->sf.read = (void*)multifile_read;
|
||||
this_sf->sf.get_size = (void*)multifile_get_size;
|
||||
this_sf->sf.get_offset = (void*)multifile_get_offset;
|
||||
this_sf->sf.get_name = (void*)multifile_get_name;
|
||||
this_sf->sf.get_realname = (void*)multifile_get_realname;
|
||||
this_sf->sf.open = (void*)multifile_open;
|
||||
this_sf->sf.close = (void*)multifile_close;
|
||||
this_sf->sf.stream_index = streamfiles[0]->stream_index;
|
||||
|
||||
this_sf->inner_sfs_size = streamfiles_size;
|
||||
this_sf->inner_sfs = calloc(streamfiles_size, sizeof(STREAMFILE*));
|
||||
if (!this_sf->inner_sfs) goto fail;
|
||||
this_sf->sizes = calloc(streamfiles_size, sizeof(size_t));
|
||||
if (!this_sf->sizes) goto fail;
|
||||
|
||||
for (i = 0; i < this_sf->inner_sfs_size; i++) {
|
||||
this_sf->inner_sfs[i] = streamfiles[i];
|
||||
this_sf->sizes[i] = streamfiles[i]->get_size(streamfiles[i]);
|
||||
this_sf->size += this_sf->sizes[i];
|
||||
}
|
||||
|
||||
return &this_sf->sf;
|
||||
|
||||
fail:
|
||||
if (this_sf) {
|
||||
free(this_sf->inner_sfs);
|
||||
free(this_sf->sizes);
|
||||
}
|
||||
free(this_sf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* **************************************************** */
|
||||
|
||||
/* Read a line into dst. The source files are lines separated by CRLF (Windows) / LF (Unux) / CR (Mac).
|
||||
* The line will be null-terminated and CR/LF removed if found.
|
||||
*
|
||||
|
@ -81,6 +81,17 @@ STREAMFILE *open_clamp_streamfile(STREAMFILE *streamfile, off_t start, size_t si
|
||||
* Can be used with subfiles inside a bigger file, so it looks standard to a meta. */
|
||||
STREAMFILE *open_io_streamfile(STREAMFILE *streamfile, void* data, size_t data_size, void* read_callback);//void* size_callback, void* seek_callback);
|
||||
|
||||
/* A STREAMFILE that reports a fake name, but still re-opens itself properly.
|
||||
* Can be used to trick a meta's extension check (to call from another, with a modified SF).
|
||||
* When fakename isn't supplied it's read from the streamfile, and the extension swapped with fakeext.
|
||||
* If the fakename is an existing file, open won't work on it as it'll reopen the fake-named streamfile. */
|
||||
STREAMFILE *open_fakename_streamfile(STREAMFILE *streamfile, char * fakename, char * fakeext);
|
||||
|
||||
/* A streamfile formed from multiple streamfiles, their data joined during reads.
|
||||
* Can be used when data is segmented in multiple separate files.
|
||||
* The first streamfile is used to get names, stream index and so on. */
|
||||
STREAMFILE *open_multifile_streamfile(STREAMFILE **streamfiles, size_t streamfiles_size);
|
||||
|
||||
|
||||
/* close a file, destroy the STREAMFILE object */
|
||||
static inline void close_streamfile(STREAMFILE * streamfile) {
|
||||
|
@ -375,6 +375,11 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
|
||||
init_vgmstream_mogg,
|
||||
init_vgmstream_kma9,
|
||||
init_vgmstream_fsb_encrypted,
|
||||
init_vgmstream_xwc,
|
||||
init_vgmstream_atsl3,
|
||||
init_vgmstream_sps_n1,
|
||||
init_vgmstream_atx,
|
||||
init_vgmstream_sqex_sead,
|
||||
|
||||
init_vgmstream_txth, /* should go at the end (lower priority) */
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
@ -2195,6 +2200,11 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
|
||||
}
|
||||
concatn(length,desc,temp);
|
||||
|
||||
snprintf(temp,TEMPSIZE,
|
||||
"\nbitrate: %d kbps",
|
||||
get_vgmstream_average_bitrate(vgmstream) / 1000);
|
||||
concatn(length,desc,temp);
|
||||
|
||||
/* only interesting if more than one */
|
||||
if (vgmstream->num_streams > 1) {
|
||||
snprintf(temp,TEMPSIZE,
|
||||
@ -2443,9 +2453,11 @@ static STREAMFILE * get_vgmstream_average_bitrate_channel_streamfile(VGMSTREAM *
|
||||
return vgmstream->ch[channel].streamfile;
|
||||
}
|
||||
|
||||
static int get_vgmstream_average_bitrate_from_size(size_t size, int sample_rate, int length_samples) {
|
||||
return (int)((int64_t)size * 8 * sample_rate / length_samples);
|
||||
}
|
||||
static int get_vgmstream_average_bitrate_from_streamfile(STREAMFILE * streamfile, int sample_rate, int length_samples) {
|
||||
// todo: not correct in subsongs or formats which only use part of the data
|
||||
return (int)((int64_t)get_streamfile_size(streamfile) * 8 * sample_rate / length_samples);
|
||||
return get_vgmstream_average_bitrate_from_size(get_streamfile_size(streamfile), sample_rate, length_samples);
|
||||
}
|
||||
|
||||
/* Return the average bitrate in bps of all unique files contained within this stream. */
|
||||
@ -2463,6 +2475,11 @@ int get_vgmstream_average_bitrate(VGMSTREAM * vgmstream) {
|
||||
if (!sample_rate || !channels || !length_samples)
|
||||
return 0;
|
||||
|
||||
/* subsongs need to report this to properly calculate */
|
||||
if (vgmstream->stream_size) {
|
||||
return get_vgmstream_average_bitrate_from_size(vgmstream->stream_size, sample_rate, length_samples);
|
||||
}
|
||||
|
||||
if (channels >= 1) {
|
||||
streamFile = get_vgmstream_average_bitrate_channel_streamfile(vgmstream, 0);
|
||||
if (streamFile) {
|
||||
|
@ -650,12 +650,15 @@ typedef enum {
|
||||
meta_OGG_SLI, /* Ogg Vorbis file w/ companion .sli for looping */
|
||||
meta_OGG_SLI2, /* Ogg Vorbis file w/ different styled .sli for looping */
|
||||
meta_OGG_SFL, /* Ogg Vorbis file w/ .sfl (RIFF SFPL) for looping */
|
||||
meta_OGG_UM3, /* Ogg Vorbis with optional first 0x800 bytes XOR 0xFF */
|
||||
meta_OGG_KOVS, /* Ogg Vorbis with extra header and 0x100 bytes XOR */
|
||||
meta_OGG_PSYCHIC, /* Ogg Vorbis with all bytes -0x23 */
|
||||
meta_OGG_SNGW, /* Ogg Vorbis with optional key XOR + nibble swap (Capcom PC games) */
|
||||
meta_OGG_ISD, /* Ogg Vorbis with key XOR (Azure Striker Gunvolt PC) */
|
||||
meta_OGG_UM3, /* Ogg Vorbis with optional encryption */
|
||||
meta_OGG_KOVS, /* Ogg Vorbis with encryption (Koei Tecmo Games) */
|
||||
meta_OGG_PSYCHIC, /* Ogg Vorbis with encryption */
|
||||
meta_OGG_SNGW, /* Ogg Vorbis with optional encryption (Capcom PC games) */
|
||||
meta_OGG_ISD, /* Ogg Vorbis with encryption (Azure Striker Gunvolt PC) */
|
||||
meta_KMA9, /* Koei Tecmo [Nobunaga no Yabou - Souzou (Vita)] */
|
||||
meta_XWC, /* Starbreeze games */
|
||||
meta_SQEX_SAB, /* Square-Enix newest middleware (sound) */
|
||||
meta_SQEX_MAB, /* Square-Enix newest middleware (music) */
|
||||
|
||||
#ifdef VGM_USE_MP4V2
|
||||
meta_MP4, /* AAC (iOS) */
|
||||
@ -734,6 +737,7 @@ typedef struct {
|
||||
int num_streams; /* for multi-stream formats (0=not set/one stream, 1=one stream) */
|
||||
int stream_index; /* selected stream (also 1-based) */
|
||||
char stream_name[STREAM_NAME_SIZE]; /* name of the current stream (info), if the file stores it and it's filled */
|
||||
size_t stream_size; /* info to properly calculate bitrate */
|
||||
|
||||
/* looping */
|
||||
int loop_flag; /* is this stream looped? */
|
||||
@ -827,9 +831,9 @@ typedef enum {
|
||||
} vorbis_custom_t;
|
||||
|
||||
/* config for Wwise Vorbis (3 types for flexibility though not all combinations exist) */
|
||||
typedef enum { HEADER_TRIAD, FULL_SETUP, INLINE_CODEBOOKS, EXTERNAL_CODEBOOKS, AOTUV603_CODEBOOKS } wwise_setup_t; /* Vorbis setup style */
|
||||
typedef enum { TYPE_8, TYPE_6, TYPE_2 } wwise_header_t; /* size of packet headers */
|
||||
typedef enum { STANDARD, MODIFIED } wwise_packet_t; /* type of Vorbis packets */
|
||||
typedef enum { WWV_HEADER_TRIAD, WWV_FULL_SETUP, WWV_INLINE_CODEBOOKS, WWV_EXTERNAL_CODEBOOKS, WWV_AOTUV603_CODEBOOKS } wwise_setup_t;
|
||||
typedef enum { WWV_TYPE_8, WWV_TYPE_6, WWV_TYPE_2 } wwise_header_t;
|
||||
typedef enum { WWV_STANDARD, WWV_MODIFIED } wwise_packet_t;
|
||||
|
||||
typedef struct {
|
||||
/* to reconstruct init packets */
|
||||
@ -1097,7 +1101,6 @@ typedef enum {
|
||||
FFMPEG_STANDARD, /* default FFmpeg */
|
||||
FFMPEG_SWITCH_OPUS, /* Opus without Ogg layer */
|
||||
FFMPEG_EA_XMA, /* XMA with padding removed and custom streams in SNS blocks */
|
||||
FFMPEG_BGW_ATRAC3, /* Encrypted raw ATRAC3 */
|
||||
//FFMPEG_EA_SCHL, /* Normal header+data (ex. ATRAC3) in SCxx blocks */
|
||||
//FFMPEG_SFH, /* ATRAC3plus header+data in SFH blocks */
|
||||
//FFMPEG_AWC_XMA, /* XMA data in AWC blocks, 1 streams per channel */
|
||||
@ -1115,7 +1118,6 @@ typedef struct {
|
||||
/* internal sequences, when needed */
|
||||
int sequence;
|
||||
int samples_done;
|
||||
uint8_t * key;
|
||||
} ffmpeg_custom_config;
|
||||
|
||||
typedef struct {
|
||||
|
Loading…
x
Reference in New Issue
Block a user