This commit is contained in:
bnnm 2020-05-23 23:23:17 +02:00
parent 71bee18a06
commit 3e52b1dd0b
4 changed files with 764 additions and 764 deletions

File diff suppressed because it is too large Load Diff

View File

@ -33,45 +33,45 @@ typedef struct {
/* ********************************************************************************** */
static layered_layout_data* build_layered_fsb5_celt(STREAMFILE *streamFile, fsb5_header* fsb5);
static layered_layout_data* build_layered_fsb5_atrac9(STREAMFILE *streamFile, fsb5_header* fsb5, off_t configs_offset, size_t configs_size);
static layered_layout_data* build_layered_fsb5_celt(STREAMFILE* sf, fsb5_header* fsb5);
static layered_layout_data* build_layered_fsb5_atrac9(STREAMFILE* sf, fsb5_header* fsb5, off_t configs_offset, size_t configs_size);
/* FSB5 - FMOD Studio multiplatform format */
VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
fsb5_header fsb5 = {0};
int target_subsong = streamFile->stream_index;
int target_subsong = sf->stream_index;
int i;
/* checks */
/* .fsb: standard
* .snd: Alchemy engine (also Unity) */
if (!check_extensions(streamFile,"fsb,snd"))
if (!check_extensions(sf,"fsb,snd"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x46534235) /* "FSB5" */
if (read_32bitBE(0x00,sf) != 0x46534235) /* "FSB5" */
goto fail;
/* 0x00 is rare (seen in Tales from Space Vita) */
fsb5.version = read_32bitLE(0x04,streamFile);
fsb5.version = read_32bitLE(0x04,sf);
if (fsb5.version != 0x00 && fsb5.version != 0x01) goto fail;
fsb5.total_subsongs = read_32bitLE(0x08,streamFile);
fsb5.sample_header_size = read_32bitLE(0x0C,streamFile);
fsb5.name_table_size = read_32bitLE(0x10,streamFile);
fsb5.sample_data_size = read_32bitLE(0x14,streamFile);
fsb5.codec = read_32bitLE(0x18,streamFile);
fsb5.total_subsongs = read_32bitLE(0x08,sf);
fsb5.sample_header_size = read_32bitLE(0x0C,sf);
fsb5.name_table_size = read_32bitLE(0x10,sf);
fsb5.sample_data_size = read_32bitLE(0x14,sf);
fsb5.codec = read_32bitLE(0x18,sf);
/* version 0x01 - 0x1c(4): zero, 0x24(16): hash, 0x34(8): unk
* version 0x00 has an extra field (always 0?) at 0x1c */
if (fsb5.version == 0x01) {
/* found by tests and assumed to be flags, no games known */
fsb5.flags = read_32bitLE(0x20,streamFile);
fsb5.flags = read_32bitLE(0x20,sf);
}
fsb5.base_header_size = (fsb5.version==0x00) ? 0x40 : 0x3C;
if ((fsb5.sample_header_size + fsb5.name_table_size + fsb5.sample_data_size + fsb5.base_header_size) != get_streamfile_size(streamFile)) {
VGM_LOG("FSB5: bad size (%x + %x + %x + %x != %x)\n", fsb5.sample_header_size, fsb5.name_table_size, fsb5.sample_data_size, fsb5.base_header_size, get_streamfile_size(streamFile));
if ((fsb5.sample_header_size + fsb5.name_table_size + fsb5.sample_data_size + fsb5.base_header_size) != get_streamfile_size(sf)) {
VGM_LOG("FSB5: bad size (%x + %x + %x + %x != %x)\n", fsb5.sample_header_size, fsb5.name_table_size, fsb5.sample_data_size, fsb5.base_header_size, get_streamfile_size(sf));
goto fail;
}
@ -87,8 +87,8 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
off_t data_offset = 0;
uint32_t sample_mode1, sample_mode2; /* maybe one uint64? */
sample_mode1 = (uint32_t)read_32bitLE(fsb5.sample_header_offset+0x00,streamFile);
sample_mode2 = (uint32_t)read_32bitLE(fsb5.sample_header_offset+0x04,streamFile);
sample_mode1 = (uint32_t)read_32bitLE(fsb5.sample_header_offset+0x00,sf);
sample_mode2 = (uint32_t)read_32bitLE(fsb5.sample_header_offset+0x04,sf);
stream_header_size += 0x08;
/* get samples */
@ -133,7 +133,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
uint32_t extraflag, extraflag_type, extraflag_size, extraflag_end;
do {
extraflag = read_32bitLE(extraflag_offset,streamFile);
extraflag = read_32bitLE(extraflag_offset,sf);
extraflag_type = (extraflag >> 25) & 0x7F; /* bits 32..26 (7) */
extraflag_size = (extraflag >> 1) & 0xFFFFFF; /* bits 25..1 (24)*/
extraflag_end = (extraflag & 0x01); /* bit 0 (1) */
@ -142,15 +142,15 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
if (i + 1 == target_subsong) {
switch(extraflag_type) {
case 0x01: /* channels */
fsb5.channels = read_8bit(extraflag_offset+0x04,streamFile);
fsb5.channels = read_8bit(extraflag_offset+0x04,sf);
break;
case 0x02: /* sample rate */
fsb5.sample_rate = read_32bitLE(extraflag_offset+0x04,streamFile);
fsb5.sample_rate = read_32bitLE(extraflag_offset+0x04,sf);
break;
case 0x03: /* loop info */
fsb5.loop_start = read_32bitLE(extraflag_offset+0x04,streamFile);
fsb5.loop_start = read_32bitLE(extraflag_offset+0x04,sf);
if (extraflag_size > 0x04) { /* probably not needed */
fsb5.loop_end = read_32bitLE(extraflag_offset+0x08,streamFile);
fsb5.loop_end = read_32bitLE(extraflag_offset+0x08,sf);
fsb5.loop_end += 1; /* correct compared to FMOD's tools */
}
//;VGM_LOG("FSB5: stream %i loop start=%i, loop end=%i, samples=%i\n", i, fsb5.loop_start, fsb5.loop_end, fsb5.num_samples);
@ -183,7 +183,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
case 0x05: /* unknown 32b */
/* rare, found in Tearaway (Vita) with value 0 in first stream and
* Shantae and the Seven Sirens (Mobile) with value 0x0003bd72 BE in #44 (Arena Town) */
VGM_LOG("FSB5: stream %i flag %x with value %08x\n", i, extraflag_type, read_32bitLE(extraflag_offset+0x04,streamFile));
VGM_LOG("FSB5: stream %i flag %x with value %08x\n", i, extraflag_type, read_32bitLE(extraflag_offset+0x04,sf));
break;
case 0x06: /* XMA seek table */
/* no need for it */
@ -209,7 +209,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
break;
case 0x0d: /* unknown 32b (config? usually 0x3fnnnn00 BE and sometimes 0x3dnnnn00 BE) */
/* found in some XMA2/Vorbis/FADPCM */
VGM_LOG("FSB5: stream %i flag %x with value %08x\n", i, extraflag_type, read_32bitLE(extraflag_offset+0x04,streamFile));
VGM_LOG("FSB5: stream %i flag %x with value %08x\n", i, extraflag_type, read_32bitLE(extraflag_offset+0x04,sf));
break;
default:
VGM_LOG("FSB5: stream %i unknown flag 0x%x at %x + 0x04 (size 0x%x)\n", i, extraflag_type, (uint32_t)extraflag_offset, extraflag_size);
@ -233,8 +233,8 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
else {
off_t next_data_offset;
uint32_t next_sample_mode1, next_sample_mode2;
next_sample_mode1 = (uint32_t)read_32bitLE(fsb5.sample_header_offset+stream_header_size+0x00,streamFile);
next_sample_mode2 = (uint32_t)read_32bitLE(fsb5.sample_header_offset+stream_header_size+0x04,streamFile);
next_sample_mode1 = (uint32_t)read_32bitLE(fsb5.sample_header_offset+stream_header_size+0x00,sf);
next_sample_mode2 = (uint32_t)read_32bitLE(fsb5.sample_header_offset+stream_header_size+0x04,sf);
next_data_offset = (((next_sample_mode2 & 0x03) << 25) | ((next_sample_mode1 >> 7) & 0x1FFFFFF)) << 5;
fsb5.stream_size = next_data_offset - data_offset;
@ -252,7 +252,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
/* get stream name */
if (fsb5.name_table_size) {
off_t name_suboffset = fsb5.base_header_size + fsb5.sample_header_size + 0x04*(target_subsong-1);
fsb5.name_offset = fsb5.base_header_size + fsb5.sample_header_size + read_32bitLE(name_suboffset,streamFile);
fsb5.name_offset = fsb5.base_header_size + fsb5.sample_header_size + read_32bitLE(name_suboffset,sf);
}
@ -270,7 +270,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
vgmstream->stream_size = fsb5.stream_size;
vgmstream->meta_type = meta_FSB5;
if (fsb5.name_offset)
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, fsb5.name_offset,streamFile);
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, fsb5.name_offset,sf);
switch (fsb5.codec) {
case 0x00: /* FMOD_SOUND_FORMAT_NONE */
@ -313,7 +313,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size = 0x02;
}
dsp_read_coefs_be(vgmstream,streamFile,fsb5.extradata_offset,0x2E);
dsp_read_coefs_be(vgmstream,sf,fsb5.extradata_offset,0x2E);
break;
case 0x07: /* FMOD_SOUND_FORMAT_IMAADPCM [Skylanders] */
@ -347,12 +347,12 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
block_count = fsb5.stream_size / block_size + (fsb5.stream_size % block_size ? 1 : 0);
bytes = ffmpeg_make_riff_xma2(buf, 0x100, vgmstream->num_samples, fsb5.stream_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size);
vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, fsb5.stream_offset,fsb5.stream_size);
vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, fsb5.stream_offset,fsb5.stream_size);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
xma_fix_raw_samples(vgmstream, streamFile, fsb5.stream_offset,fsb5.stream_size, 0, 0,0); /* samples look ok */
xma_fix_raw_samples(vgmstream, sf, fsb5.stream_offset,fsb5.stream_size, 0, 0,0); /* samples look ok */
break;
}
#endif
@ -363,7 +363,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
cfg.fsb_padding = (vgmstream->channels > 2 ? 16 : 4); /* observed default */
vgmstream->codec_data = init_mpeg_custom(streamFile, fsb5.stream_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_FSB, &cfg);
vgmstream->codec_data = init_mpeg_custom(sf, fsb5.stream_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_FSB, &cfg);
if (!vgmstream->codec_data) goto fail;
vgmstream->layout_type = layout_none;
break;
@ -375,7 +375,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
int is_multistream = fsb5.channels > 2;
if (is_multistream) {
vgmstream->layout_data = build_layered_fsb5_celt(streamFile, &fsb5);
vgmstream->layout_data = build_layered_fsb5_celt(sf, &fsb5);
if (!vgmstream->layout_data) goto fail;
vgmstream->coding_type = coding_CELT_FSB;
vgmstream->layout_type = layout_layered;
@ -398,7 +398,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
/* skip frame size in newer FSBs [Day of the Tentacle Remastered (Vita), Tearaway Unfolded (PS4)] */
if (configs_size >= 0x08 && (uint8_t)read_8bit(configs_offset, streamFile) != 0xFE) { /* ATRAC9 sync */
if (configs_size >= 0x08 && (uint8_t)read_8bit(configs_offset, sf) != 0xFE) { /* ATRAC9 sync */
configs_offset += 0x04;
configs_size -= 0x04;
}
@ -407,7 +407,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
if (is_multistream) {
/* multichannel made of various streams [Little Big Planet (Vita)] */
vgmstream->layout_data = build_layered_fsb5_atrac9(streamFile, &fsb5, configs_offset, configs_size);
vgmstream->layout_data = build_layered_fsb5_atrac9(sf, &fsb5, configs_offset, configs_size);
if (!vgmstream->layout_data) goto fail;
vgmstream->coding_type = coding_ATRAC9;
vgmstream->layout_type = layout_layered;
@ -417,7 +417,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
atrac9_config cfg = {0};
cfg.channels = vgmstream->channels;
cfg.config_data = read_32bitBE(configs_offset,streamFile);
cfg.config_data = read_32bitBE(configs_offset,sf);
//cfg.encoder_delay = 0x100; //todo not used? num_samples seems to count all data
vgmstream->codec_data = init_atrac9(&cfg);
@ -434,14 +434,14 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
uint8_t buf[0x100];
int bytes, format, average_bps, block_align;
format = read_16bitBE(fsb5.extradata_offset+0x00,streamFile);
block_align = (uint16_t)read_16bitBE(fsb5.extradata_offset+0x02,streamFile);
average_bps = (uint32_t)read_32bitBE(fsb5.extradata_offset+0x04,streamFile);
format = read_16bitBE(fsb5.extradata_offset+0x00,sf);
block_align = (uint16_t)read_16bitBE(fsb5.extradata_offset+0x02,sf);
average_bps = (uint32_t)read_32bitBE(fsb5.extradata_offset+0x04,sf);
/* rest: seek entries + mini seek table? */
/* XWMA encoder only does up to 6ch (doesn't use FSB multistreams for more) */
bytes = ffmpeg_make_riff_xwma(buf,0x100, format, fsb5.stream_size, vgmstream->channels, vgmstream->sample_rate, average_bps, block_align);
vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, fsb5.stream_offset,fsb5.stream_size);
vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, fsb5.stream_offset,fsb5.stream_size);
if ( !vgmstream->codec_data ) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
@ -455,11 +455,11 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
cfg.channels = vgmstream->channels;
cfg.sample_rate = vgmstream->sample_rate;
cfg.setup_id = read_32bitLE(fsb5.extradata_offset,streamFile);
cfg.setup_id = read_32bitLE(fsb5.extradata_offset,sf);
vgmstream->layout_type = layout_none;
vgmstream->coding_type = coding_VORBIS_custom;
vgmstream->codec_data = init_vorbis_custom(streamFile, fsb5.stream_offset, VORBIS_FSB, &cfg);
vgmstream->codec_data = init_vorbis_custom(sf, fsb5.stream_offset, VORBIS_FSB, &cfg);
if (!vgmstream->codec_data) goto fail;
break;
@ -477,7 +477,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
goto fail;
}
if (!vgmstream_open_stream(vgmstream,streamFile,fsb5.stream_offset))
if (!vgmstream_open_stream(vgmstream,sf,fsb5.stream_offset))
goto fail;
return vgmstream;
@ -488,15 +488,15 @@ fail:
}
static layered_layout_data* build_layered_fsb5_celt(STREAMFILE *streamFile, fsb5_header* fsb5) {
static layered_layout_data* build_layered_fsb5_celt(STREAMFILE* sf, fsb5_header* fsb5) {
layered_layout_data* data = NULL;
STREAMFILE* temp_streamFile = NULL;
STREAMFILE* temp_sf = NULL;
int i, layers = (fsb5->channels+1) / 2;
size_t interleave;
if (read_32bitBE(fsb5->stream_offset+0x00,streamFile) != 0x17C30DF3) /* FSB CELT frame ID */
if (read_32bitBE(fsb5->stream_offset+0x00,sf) != 0x17C30DF3) /* FSB CELT frame ID */
goto fail;
interleave = 0x04+0x04+read_32bitLE(fsb5->stream_offset+0x04,streamFile); /* frame size */
interleave = 0x04+0x04+read_32bitLE(fsb5->stream_offset+0x04,sf); /* frame size */
//todo unknown interleave for max quality odd channel streams (found in test files)
/* FSB5 odd channels use 2ch+2ch...+1ch streams, and the last only goes up to 0x17a, and other
@ -533,29 +533,29 @@ static layered_layout_data* build_layered_fsb5_celt(STREAMFILE *streamFile, fsb5
goto fail;
#endif
temp_streamFile = setup_fsb5_streamfile(streamFile, fsb5->stream_offset, fsb5->stream_size, layers, i, interleave);
if (!temp_streamFile) goto fail;
temp_sf = setup_fsb5_streamfile(sf, fsb5->stream_offset, fsb5->stream_size, layers, i, interleave);
if (!temp_sf) goto fail;
if (!vgmstream_open_stream(data->layers[i], temp_streamFile, 0x00))
if (!vgmstream_open_stream(data->layers[i], temp_sf, 0x00))
goto fail;
}
/* setup layered VGMSTREAMs */
if (!setup_layout_layered(data))
goto fail;
close_streamfile(temp_streamFile);
close_streamfile(temp_sf);
return data;
fail:
close_streamfile(temp_streamFile);
close_streamfile(temp_sf);
free_layout_layered(data);
return NULL;
}
static layered_layout_data* build_layered_fsb5_atrac9(STREAMFILE *streamFile, fsb5_header* fsb5, off_t configs_offset, size_t configs_size) {
static layered_layout_data* build_layered_fsb5_atrac9(STREAMFILE* sf, fsb5_header* fsb5, off_t configs_offset, size_t configs_size) {
layered_layout_data* data = NULL;
STREAMFILE* temp_streamFile = NULL;
STREAMFILE* temp_sf = NULL;
int i, layers = (configs_size / 0x04);
size_t interleave = 0;
@ -566,7 +566,7 @@ static layered_layout_data* build_layered_fsb5_atrac9(STREAMFILE *streamFile, fs
/* open each layer subfile (2ch+2ch..+1/2ch) */
for (i = 0; i < layers; i++) {
uint32_t config = read_32bitBE(configs_offset + 0x04*i, streamFile);
uint32_t config = read_32bitBE(configs_offset + 0x04*i, sf);
int channel_index, layer_channels;
size_t frame_size;
@ -609,21 +609,21 @@ static layered_layout_data* build_layered_fsb5_atrac9(STREAMFILE *streamFile, fs
goto fail;
#endif
temp_streamFile = setup_fsb5_streamfile(streamFile, fsb5->stream_offset, fsb5->stream_size, layers, i, interleave);
if (!temp_streamFile) goto fail;
temp_sf = setup_fsb5_streamfile(sf, fsb5->stream_offset, fsb5->stream_size, layers, i, interleave);
if (!temp_sf) goto fail;
if (!vgmstream_open_stream(data->layers[i], temp_streamFile, 0x00))
if (!vgmstream_open_stream(data->layers[i], temp_sf, 0x00))
goto fail;
}
/* setup layered VGMSTREAMs */
if (!setup_layout_layered(data))
goto fail;
close_streamfile(temp_streamFile);
close_streamfile(temp_sf);
return data;
fail:
close_streamfile(temp_streamFile);
close_streamfile(temp_sf);
free_layout_layered(data);
return NULL;
}

View File

@ -1,54 +1,54 @@
#include "meta.h"
#include "../coding/coding.h"
/* FEV+FSB5 container [Just Cause 3 (PC), Shantae: Half-Genie Hero (Switch)] */
VGMSTREAM * init_vgmstream_fsb5_fev_bank(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
STREAMFILE *temp_streamFile = NULL;
off_t subfile_offset, chunk_offset, first_offset = 0x0c;
size_t subfile_size, chunk_size;
/* checks */
if (!check_extensions(streamFile, "bank"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x52494646) /* "RIFF" */
goto fail;
if (read_32bitBE(0x08,streamFile) != 0x46455620) /* "FEV " */
goto fail;
/* .fev is an event format referencing various external .fsb, but FMOD can bake .fev and .fsb to
* form a .bank, which is the format we support here (regular .fev is complex and not very interesting).
* Format is RIFF with FMT (main), LIST (config) and SND (FSB5 data), we want the FSB5 offset inside LIST */
if (!find_chunk_le(streamFile, 0x4C495354,first_offset,0, &chunk_offset,NULL)) /* "LIST" */
goto fail;
if (read_32bitBE(chunk_offset+0x00,streamFile) != 0x50524F4A || /* "PROJ" */
read_32bitBE(chunk_offset+0x04,streamFile) != 0x424E4B49) /* "BNKI" */
goto fail; /* event .fev has "OBCT" instead of "BNKI" */
/* inside BNKI is a bunch of LIST each with event subchunks and finally the fsb offset */
first_offset = chunk_offset + 0x04;
if (!find_chunk_le(streamFile, 0x534E4448,first_offset,0, &chunk_offset,&chunk_size)) /* "SNDH" */
goto fail;
if (chunk_size != 0x0c)
goto fail; /* assuming only one FSB5 is possible */
subfile_offset = read_32bitLE(chunk_offset+0x04,streamFile);
subfile_size = read_32bitLE(chunk_offset+0x08,streamFile);
temp_streamFile = setup_subfile_streamfile(streamFile, subfile_offset,subfile_size, "fsb");
if (!temp_streamFile) goto fail;
vgmstream = init_vgmstream_fsb5(temp_streamFile);
close_streamfile(temp_streamFile);
return vgmstream;
fail:
close_streamfile(temp_streamFile);
close_vgmstream(vgmstream);
return NULL;
}
#include "meta.h"
#include "../coding/coding.h"
/* FEV+FSB5 container [Just Cause 3 (PC), Shantae: Half-Genie Hero (Switch)] */
VGMSTREAM* init_vgmstream_fsb5_fev_bank(STREAMFILE* sf) {
VGMSTREAM * vgmstream = NULL;
STREAMFILE *temp_sf = NULL;
off_t subfile_offset, chunk_offset, first_offset = 0x0c;
size_t subfile_size, chunk_size;
/* checks */
if (!check_extensions(sf, "bank"))
goto fail;
if (read_32bitBE(0x00,sf) != 0x52494646) /* "RIFF" */
goto fail;
if (read_32bitBE(0x08,sf) != 0x46455620) /* "FEV " */
goto fail;
/* .fev is an event format referencing various external .fsb, but FMOD can bake .fev and .fsb to
* form a .bank, which is the format we support here (regular .fev is complex and not very interesting).
* Format is RIFF with FMT (main), LIST (config) and SND (FSB5 data), we want the FSB5 offset inside LIST */
if (!find_chunk_le(sf, 0x4C495354,first_offset,0, &chunk_offset,NULL)) /* "LIST" */
goto fail;
if (read_32bitBE(chunk_offset+0x00,sf) != 0x50524F4A || /* "PROJ" */
read_32bitBE(chunk_offset+0x04,sf) != 0x424E4B49) /* "BNKI" */
goto fail; /* event .fev has "OBCT" instead of "BNKI" */
/* inside BNKI is a bunch of LIST each with event subchunks and finally the fsb offset */
first_offset = chunk_offset + 0x04;
if (!find_chunk_le(sf, 0x534E4448,first_offset,0, &chunk_offset,&chunk_size)) /* "SNDH" */
goto fail;
if (chunk_size != 0x0c)
goto fail; /* assuming only one FSB5 is possible */
subfile_offset = read_32bitLE(chunk_offset+0x04,sf);
subfile_size = read_32bitLE(chunk_offset+0x08,sf);
temp_sf = setup_subfile_streamfile(sf, subfile_offset,subfile_size, "fsb");
if (!temp_sf) goto fail;
vgmstream = init_vgmstream_fsb5(temp_sf);
close_streamfile(temp_sf);
return vgmstream;
fail:
close_streamfile(temp_sf);
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -24,37 +24,37 @@ typedef struct {
off_t stream_offset;
} xvag_header;
static int init_xvag_atrac9(STREAMFILE *streamFile, VGMSTREAM* vgmstream, xvag_header * xvag, off_t chunk_offset);
static layered_layout_data* build_layered_xvag(STREAMFILE *streamFile, xvag_header * xvag, off_t chunk_offset, off_t start_offset);
static int init_xvag_atrac9(STREAMFILE* sf, VGMSTREAM* vgmstream, xvag_header * xvag, off_t chunk_offset);
static layered_layout_data* build_layered_xvag(STREAMFILE* sf, xvag_header * xvag, off_t chunk_offset, off_t start_offset);
/* XVAG - Sony's Scream Tool/Stream Creator format (God of War III, Ratchet & Clank Future, The Last of Us, Uncharted) */
VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
STREAMFILE* temp_streamFile = NULL;
VGMSTREAM* init_vgmstream_xvag(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
STREAMFILE* temp_sf = NULL;
xvag_header xvag = {0};
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
off_t start_offset, chunk_offset, first_offset = 0x20;
size_t chunk_size;
int total_subsongs = 0, target_subsong = streamFile->stream_index;
int total_subsongs = 0, target_subsong = sf->stream_index;
/* checks */
/* .xvag: standard
* (extensionless): The Last Of Us (PS3) speech files */
if (!check_extensions(streamFile,"xvag,"))
if (!check_extensions(sf,"xvag,"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x58564147) /* "XVAG" */
if (read_32bitBE(0x00,sf) != 0x58564147) /* "XVAG" */
goto fail;
/* endian flag (XVAGs of the same game can use BE or LE, usually when reusing from other platforms) */
xvag.big_endian = read_8bit(0x08,streamFile) & 0x01;
xvag.big_endian = read_8bit(0x08,sf) & 0x01;
if (xvag.big_endian) {
read_32bit = read_32bitBE;
} else {
read_32bit = read_32bitLE;
}
start_offset = read_32bit(0x04,streamFile);
start_offset = read_32bit(0x04,sf);
/* 0x08: flags? (&0x01=big endian, 0x02=?, 0x06=full RIFF AT9?)
* 0x09: flags2? (0x00/0x01/0x04, speaker mode?)
* 0x0a: always 0?
@ -62,24 +62,24 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) {
/* "fmat": base format (always first) */
if (!find_chunk(streamFile, 0x666D6174,first_offset,0, &chunk_offset,&chunk_size, xvag.big_endian, 1)) /*"fmat"*/
if (!find_chunk(sf, 0x666D6174,first_offset,0, &chunk_offset,&chunk_size, xvag.big_endian, 1)) /*"fmat"*/
goto fail;
xvag.channels = read_32bit(chunk_offset+0x00,streamFile);
xvag.codec = read_32bit(chunk_offset+0x04,streamFile);
xvag.num_samples = read_32bit(chunk_offset+0x08,streamFile);
xvag.channels = read_32bit(chunk_offset+0x00,sf);
xvag.codec = read_32bit(chunk_offset+0x04,sf);
xvag.num_samples = read_32bit(chunk_offset+0x08,sf);
/* 0x0c: samples again? */
VGM_ASSERT(xvag.num_samples != read_32bit(chunk_offset+0x0c,streamFile), "XVAG: num_samples values don't match\n");
VGM_ASSERT(xvag.num_samples != read_32bit(chunk_offset+0x0c,sf), "XVAG: num_samples values don't match\n");
xvag.factor = read_32bit(chunk_offset+0x10,streamFile); /* for interleave */
xvag.sample_rate = read_32bit(chunk_offset+0x14,streamFile);
xvag.data_size = read_32bit(chunk_offset+0x18,streamFile); /* not always accurate */
xvag.factor = read_32bit(chunk_offset+0x10,sf); /* for interleave */
xvag.sample_rate = read_32bit(chunk_offset+0x14,sf);
xvag.data_size = read_32bit(chunk_offset+0x18,sf); /* not always accurate */
/* extra data, seen in versions 0x61+ */
if (chunk_size > 0x1c) {
/* number of interleaved subsongs */
xvag.subsongs = read_32bit(chunk_offset+0x1c,streamFile);
xvag.subsongs = read_32bit(chunk_offset+0x1c,sf);
/* number of interleaved layers (layers * channels_per_layer = channels) */
xvag.layers = read_32bit(chunk_offset+0x20,streamFile);
xvag.layers = read_32bit(chunk_offset+0x20,sf);
}
else {
xvag.subsongs = 1;
@ -99,9 +99,9 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) {
/* XVAG has no looping, but some PS3 PS-ADPCM seems to do full loops (without data flags) */
if (xvag.codec == 0x06 && xvag.subsongs == 1) {
size_t file_size = get_streamfile_size(streamFile);
size_t file_size = get_streamfile_size(sf);
/* simply test if last frame is not empty = may loop */
xvag.loop_flag = (read_8bit(file_size - 0x01, streamFile) != 0);
xvag.loop_flag = (read_8bit(file_size - 0x01, sf) != 0);
xvag.loop_start = 0;
xvag.loop_end = ps_bytes_to_samples(file_size - start_offset, xvag.channels);
}
@ -162,7 +162,7 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) {
if (xvag.layers > 1 && !(xvag.layers*1 == vgmstream->channels || xvag.layers*2 == vgmstream->channels)) goto fail;
/* "mpin": mpeg info */
if (!find_chunk(streamFile, 0x6D70696E,first_offset,0, &chunk_offset,NULL, xvag.big_endian, 1)) /*"mpin"*/
if (!find_chunk(sf, 0x6D70696E,first_offset,0, &chunk_offset,NULL, xvag.big_endian, 1)) /*"mpin"*/
goto fail;
/* all layers/subsongs share the same config; not very useful but for posterity:
@ -182,18 +182,18 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) {
* - 0x34: data size
* (rest is padding)
* */
cfg.chunk_size = read_32bit(chunk_offset+0x1c,streamFile);
cfg.skip_samples = read_32bit(chunk_offset+0x20,streamFile);
cfg.chunk_size = read_32bit(chunk_offset+0x1c,sf);
cfg.skip_samples = read_32bit(chunk_offset+0x20,sf);
cfg.interleave = cfg.chunk_size * xvag.factor;
vgmstream->codec_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_XVAG, &cfg);
vgmstream->codec_data = init_mpeg_custom(sf, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_XVAG, &cfg);
if (!vgmstream->codec_data) goto fail;
vgmstream->layout_type = layout_none;
/* interleaved subsongs, rarely [Sly Cooper: Thieves in Time (PS3)] */
if (xvag.subsongs > 1) {
temp_streamFile = setup_xvag_streamfile(streamFile, start_offset, cfg.interleave,cfg.chunk_size, (target_subsong-1), total_subsongs);
if (!temp_streamFile) goto fail;
temp_sf = setup_xvag_streamfile(sf, start_offset, cfg.interleave,cfg.chunk_size, (target_subsong-1), total_subsongs);
if (!temp_sf) goto fail;
start_offset = 0;
}
@ -207,13 +207,13 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) {
/* "a9in": ATRAC9 info */
/* 0x00: frame size, 0x04: samples per frame, 0x0c: fact num_samples (no change), 0x10: encoder delay1 */
if (!find_chunk(streamFile, 0x6139696E,first_offset,0, &chunk_offset,NULL, xvag.big_endian, 1)) /*"a9in"*/
if (!find_chunk(sf, 0x6139696E,first_offset,0, &chunk_offset,NULL, xvag.big_endian, 1)) /*"a9in"*/
goto fail;
if (xvag.layers > 1) {
/* some Vita/PS4 multichannel [flower (Vita), Uncharted Collection (PS4)]. PS4 ATRAC9 also
* does single-stream >2ch, but this can do configs ATRAC9 can't, like 5ch/14ch/etc */
vgmstream->layout_data = build_layered_xvag(streamFile, &xvag, chunk_offset, start_offset);
vgmstream->layout_data = build_layered_xvag(sf, &xvag, chunk_offset, start_offset);
if (!vgmstream->layout_data) goto fail;
vgmstream->coding_type = coding_ATRAC9;
vgmstream->layout_type = layout_layered;
@ -222,12 +222,12 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) {
}
else {
/* interleaved subsongs (section layers) */
size_t frame_size = read_32bit(chunk_offset+0x00,streamFile);
size_t frame_size = read_32bit(chunk_offset+0x00,sf);
if (!init_xvag_atrac9(streamFile, vgmstream, &xvag, chunk_offset))
if (!init_xvag_atrac9(sf, vgmstream, &xvag, chunk_offset))
goto fail;
temp_streamFile = setup_xvag_streamfile(streamFile, start_offset, frame_size*xvag.factor,frame_size, (target_subsong-1), total_subsongs);
if (!temp_streamFile) goto fail;
temp_sf = setup_xvag_streamfile(sf, start_offset, frame_size*xvag.factor,frame_size, (target_subsong-1), total_subsongs);
if (!temp_sf) goto fail;
start_offset = 0;
}
@ -240,25 +240,25 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) {
}
if (!vgmstream_open_stream(vgmstream,temp_streamFile ? temp_streamFile : streamFile,start_offset))
if (!vgmstream_open_stream(vgmstream,temp_sf ? temp_sf : sf,start_offset))
goto fail;
close_streamfile(temp_streamFile);
close_streamfile(temp_sf);
return vgmstream;
fail:
close_streamfile(temp_streamFile);
close_streamfile(temp_sf);
close_vgmstream(vgmstream);
return NULL;
}
#ifdef VGM_USE_ATRAC9
static int init_xvag_atrac9(STREAMFILE *streamFile, VGMSTREAM* vgmstream, xvag_header * xvag, off_t chunk_offset) {
static int init_xvag_atrac9(STREAMFILE* sf, VGMSTREAM* vgmstream, xvag_header * xvag, off_t chunk_offset) {
int32_t (*read_32bit)(off_t,STREAMFILE*) = xvag->big_endian ? read_32bitBE : read_32bitLE;
atrac9_config cfg = {0};
cfg.channels = vgmstream->channels;
cfg.config_data = read_32bitBE(chunk_offset+0x08,streamFile);
cfg.encoder_delay = read_32bit(chunk_offset+0x14,streamFile);
cfg.config_data = read_32bitBE(chunk_offset+0x08,sf);
cfg.encoder_delay = read_32bit(chunk_offset+0x14,sf);
vgmstream->codec_data = init_atrac9(&cfg);
if (!vgmstream->codec_data) goto fail;
@ -271,9 +271,9 @@ fail:
}
#endif
static layered_layout_data* build_layered_xvag(STREAMFILE *streamFile, xvag_header * xvag, off_t chunk_offset, off_t start_offset) {
static layered_layout_data* build_layered_xvag(STREAMFILE* sf, xvag_header * xvag, off_t chunk_offset, off_t start_offset) {
layered_layout_data* data = NULL;
STREAMFILE* temp_streamFile = NULL;
STREAMFILE* temp_sf = NULL;
int32_t (*read_32bit)(off_t,STREAMFILE*) = xvag->big_endian ? read_32bitBE : read_32bitLE;
int i, layers = xvag->layers;
@ -296,12 +296,12 @@ static layered_layout_data* build_layered_xvag(STREAMFILE *streamFile, xvag_head
switch(xvag->codec) {
#ifdef VGM_USE_ATRAC9
case 0x09: {
size_t frame_size = read_32bit(chunk_offset+0x00,streamFile);
size_t frame_size = read_32bit(chunk_offset+0x00,sf);
if (!init_xvag_atrac9(streamFile, data->layers[i], xvag, chunk_offset))
if (!init_xvag_atrac9(sf, data->layers[i], xvag, chunk_offset))
goto fail;
temp_streamFile = setup_xvag_streamfile(streamFile, start_offset, frame_size*xvag->factor,frame_size, i, layers);
if (!temp_streamFile) goto fail;
temp_sf = setup_xvag_streamfile(sf, start_offset, frame_size*xvag->factor,frame_size, i, layers);
if (!temp_sf) goto fail;
break;
}
#endif
@ -309,9 +309,9 @@ static layered_layout_data* build_layered_xvag(STREAMFILE *streamFile, xvag_head
goto fail;
}
if ( !vgmstream_open_stream(data->layers[i], temp_streamFile, 0x00) )
if ( !vgmstream_open_stream(data->layers[i], temp_sf, 0x00) )
goto fail;
close_streamfile(temp_streamFile);
close_streamfile(temp_sf);
}
/* setup layered VGMSTREAMs */
@ -320,7 +320,7 @@ static layered_layout_data* build_layered_xvag(STREAMFILE *streamFile, xvag_head
return data;
fail:
close_streamfile(temp_streamFile);
close_streamfile(temp_sf);
free_layout_layered(data);
return NULL;
}