Merge pull request #185 from bnnm/master

XWC, SAB, subfiles, fixes
This commit is contained in:
Christopher Snowhill 2018-01-27 20:17:47 -08:00 committed by GitHub
commit ebdfed9927
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 1448 additions and 415 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -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"},

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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