Merge remote-tracking branch 'upstream/master' into ea-mpf

# Conflicts:
#	src/meta/ea_schl.c
This commit is contained in:
NicknineTheEagle 2019-11-15 22:36:29 +03:00
commit b4cc120e4c
31 changed files with 9543 additions and 8973 deletions

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="Windows-1252"?>
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="9.00"

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">

View File

@ -1248,6 +1248,7 @@ static const meta_info meta_info_list[] = {
{meta_UBI_HX, "Ubisoft HXx header"},
{meta_BMP_KONAMI, "Konami BMP header"},
{meta_ISB, "Creative ISACT header"},
{meta_XSSB, "Artoon XSSB header"},
};

View File

@ -43,6 +43,7 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) {
block_samples = 0; /* layout ignores this */
}
#ifdef VGM_USE_MPEG
/* "SCHl" start block, when decoding multi files pasted together */
if (block_id == 0x5343486C) {
switch(vgmstream->coding_type) {
@ -58,7 +59,7 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) {
break;
}
}
#endif
/* padding between "SCEl" and next "SCHl" (when subfiles exist) */
if (block_id == 0x00000000)
block_size = 0x04;

View File

@ -2,7 +2,7 @@
#include "../vgmstream.h"
#include "../mixing.h"
#define VGMSTREAM_MAX_SEGMENTS 512
#define VGMSTREAM_MAX_SEGMENTS 1024
#define VGMSTREAM_SEGMENT_SAMPLE_BUFFER 8192

View File

@ -260,6 +260,10 @@
RelativePath=".\meta\ea_eaac_streamfile.h"
>
</File>
<File
RelativePath=".\meta\ea_eaac_opus_streamfile.h"
>
</File>
<File
RelativePath=".\meta\ea_schl_streamfile.h"
>
@ -1782,6 +1786,10 @@
RelativePath=".\meta\xss.c"
>
</File>
<File
RelativePath=".\meta\xssb.c"
>
</File>
<File
RelativePath=".\meta\xvag.c"
>

View File

@ -107,6 +107,7 @@
<ClInclude Include="meta\bar_streamfile.h" />
<ClInclude Include="meta\bgw_streamfile.h" />
<ClInclude Include="meta\ea_eaac_streamfile.h" />
<ClInclude Include="meta\ea_eaac_opus_streamfile.h" />
<ClInclude Include="meta\ea_schl_streamfile.h" />
<ClInclude Include="meta\fsb_interleave_streamfile.h" />
<ClInclude Include="meta\fsb5_interleave_streamfile.h" />
@ -523,6 +524,7 @@
<ClCompile Include="meta\xpcm.c" />
<ClCompile Include="meta\xps.c" />
<ClCompile Include="meta\xss.c" />
<ClCompile Include="meta\xssb.c" />
<ClCompile Include="meta\xvag.c" />
<ClCompile Include="meta\xmd.c" />
<ClCompile Include="meta\xwb.c" />

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Header Files">
@ -92,6 +92,9 @@
<ClInclude Include="meta\ea_eaac_streamfile.h">
<Filter>meta\Header Files</Filter>
</ClInclude>
<ClInclude Include="meta\ea_eaac_opus_streamfile.h">
<Filter>meta\Header Files</Filter>
</ClInclude>
<ClInclude Include="meta\ea_schl_streamfile.h">
<Filter>meta\Header Files</Filter>
</ClInclude>
@ -1087,6 +1090,9 @@
<ClCompile Include="meta\xss.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\xssb.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\xvag.c">
<Filter>meta\Source Files</Filter>
</ClCompile>

View File

@ -1475,19 +1475,18 @@ static layered_layout_data* build_layered_eaaudiocore(STREAMFILE *sf_data, eaac_
size_t data_size;
/* We'll remove EA blocks and pass raw data to FFmpeg Opus decoder */
temp_sf = setup_eaac_streamfile(sf_data, eaac->version, eaac->codec, eaac->streamed,0,0, eaac->stream_offset);
temp_sf = setup_eaac_streamfile(sf_data, eaac->version, eaac->codec, eaac->streamed,i,layers, eaac->stream_offset);
if (!temp_sf) goto fail;
//TODO: setup opus frame deinterleave (1 frame per 2ch stream, only seen 4ch and 6ch tho)
skip = ea_opus_get_encoder_delay(0x00, temp_sf);
data_size = get_streamfile_size(temp_sf);
data->layers[i]->codec_data = init_ffmpeg_ea_opus(temp_sf, 0x00,data_size, eaac->channels, skip, eaac->sample_rate);
data->layers[i]->codec_data = init_ffmpeg_ea_opus(temp_sf, 0x00,data_size, layer_channels, skip, eaac->sample_rate);
if (!data->layers[i]->codec_data) goto fail;
data->layers[i]->coding_type = coding_FFmpeg;
data->layers[i]->layout_type = layout_none;
//TODO: 6ch channel layout seems L C R BL BR LFE, not sure about other EAAC
break;
}

View File

@ -0,0 +1,242 @@
#ifndef _EA_EAAC_OPUS_STREAMFILE_H_
#define _EA_EAAC_OPUS_STREAMFILE_H_
#include "../streamfile.h"
typedef struct deblock_config_t deblock_config_t;
typedef struct deblock_io_data deblock_io_data;
struct deblock_config_t {
/* config (all optional) */
size_t logical_size; /* pre-calculated size for performance (otherwise has to read the whole thing) */
off_t stream_start; /* data start */
size_t stream_size; /* data max */
size_t chunk_size; /* some size like a constant interleave */
size_t skip_size; /* same */
int codec; /* codec or type variations */
int channels;
int big_endian;
uint32_t config; /* some non-standard config value */
/* read=blocks from out stream to read) and "steps" (blocks from other streams to skip) */
int step_start; /* initial step_count at stream start (often 0) */
int step_count; /* number of blocks to step over from other streams */
int read_count; /* number of blocks to read from this stream, after steps */
size_t track_size;
int track_number;
int track_count;
size_t interleave_count;
size_t interleave_last_count;
/* callback that setups deblock_io_data state, normally block_size and data_size */
void (*block_callback)(STREAMFILE *sf, off_t offset, deblock_io_data *data);
} ;
struct deblock_io_data{
/* initial config */
deblock_config_t cfg;
/* state */
off_t logical_offset; /* fake deblocked offset */
off_t physical_offset; /* actual file offset */
off_t block_size; /* current block (added to physical offset) */
off_t skip_size; /* data to skip from block start to reach data (like a header) */
off_t data_size; /* usable data in a block (added to logical offset) */
//todo head/foot?
int step_count; /* number of blocks to step over */
int read_count; /* number of blocks to read */
size_t logical_size;
size_t physical_size;
off_t physical_end;
} ;
static void block_callback_default(STREAMFILE *sf, off_t offset, deblock_io_data *data) {
data->block_size = data->cfg.chunk_size;
data->skip_size = data->cfg.skip_size;
data->data_size = data->block_size - data->skip_size;
}
static size_t deblock_io_read(STREAMFILE *sf, uint8_t *dest, off_t offset, size_t length, deblock_io_data* data) {
size_t total_read = 0;
/* re-start when previous offset (can't map logical<>physical offsets) */
if (data->logical_offset < 0 || offset < data->logical_offset) {
;VGM_LOG("DEBLOCK: restart offset=%lx + %x, po=%lx, lo=%lx\n", offset, length, data->physical_offset, data->logical_offset);
data->physical_offset = data->cfg.stream_start;
data->logical_offset = 0x00;
data->block_size = 0;
data->data_size = 0;
data->skip_size = 0;
data->step_count = data->cfg.step_start;
data->read_count = data->cfg.read_count;
}
/* read blocks */
while (length > 0) {
/* ignore EOF */
if (offset < 0 ||
(data->physical_offset >= data->cfg.stream_start + data->physical_size) ||
(data->logical_size > 0 && offset > data->logical_size)) {
break;
}
/* process new block */
if (data->data_size <= 0) {
data->cfg.block_callback(sf, offset, data);
if (data->block_size <= 0) {
VGM_LOG("DEBLOCK: block size not set at %lx\n", data->physical_offset);
break;
}
}
#if 1
if (data->step_count > 0) {
data->step_count--;
data->physical_offset += data->block_size;
data->data_size = 0;
continue;
}
#else
/* handle blocks from multiple streams */
{
if (data->step_count > 0) {
data->step_count--;
data->data_size = 0; /* step over this block */
}
else if (data->read_count) {//must detect when blocks has been read
data->read_count--; /* read this block */
/* reset */
if (data->step_count == 0 && data->read_count == 0) {
data->step_count = data->cfg.step_count;
data->read_count = data->cfg.read_count;
}
}
}
#endif
/* move to next block */
if (data->data_size == 0 || offset >= data->logical_offset + data->data_size) {
data->physical_offset += data->block_size;
data->logical_offset += data->data_size;
data->data_size = 0;
data->step_count = data->cfg.step_count;
//VGM_LOG("ignore at %lx + %lx, skips=%i, reads=%i\n", data->physical_offset, data->block_size, data->step_count, data->read_count);
continue;
}
//VGM_LOG("accept at %lx + %lx, skips=%i, reads=%i\n", data->physical_offset, data->block_size, data->step_count, data->read_count);
/* read block data */
{
size_t bytes_consumed, bytes_done, to_read;
bytes_consumed = offset - data->logical_offset;
to_read = data->data_size - bytes_consumed;
if (to_read > length)
to_read = length;
bytes_done = read_streamfile(dest, data->physical_offset + data->skip_size + bytes_consumed, to_read, sf);
total_read += bytes_done;
dest += bytes_done;
offset += bytes_done;
length -= bytes_done;
if (bytes_done != to_read || bytes_done == 0) {
break; /* error/EOF */
}
}
}
return total_read;
}
static size_t deblock_io_size(STREAMFILE *streamfile, deblock_io_data* data) {
uint8_t buf[0x04];
if (data->logical_size)
return data->logical_size;
if (data->cfg.logical_size) {
data->logical_size = data->cfg.logical_size;
return data->logical_size;
}
/* force a fake read at max offset, to get max logical_offset (will be reset next read) */
deblock_io_read(streamfile, buf, 0x7FFFFFFF, 1, data);
data->logical_size = data->logical_offset;
return data->logical_size;
}
/* generic "de-blocker" helper for streams divided in blocks that have weird interleaves, their
* decoder can't easily use blocked layout, or some other weird feature. Must pass a
* deblock_config_t with setup and a callback that sets sizes of a single block. */
static STREAMFILE* open_io_deblocker_streamfile_f(STREAMFILE *sf, deblock_config_t *cfg) {
STREAMFILE *new_sf = NULL;
deblock_io_data io_data = {0};
/* prepare data */
io_data.cfg = *cfg; /* memcpy */
if (io_data.cfg.block_callback == NULL)
io_data.cfg.block_callback = block_callback_default;
if (io_data.cfg.stream_start < 0)
goto fail;
if (io_data.cfg.step_start < 0 || io_data.cfg.step_count < 0)
goto fail;
if (io_data.cfg.read_count == 0)
io_data.cfg.read_count = 1;
io_data.physical_size = io_data.cfg.stream_size;
if (io_data.physical_size > get_streamfile_size(sf) + io_data.cfg.stream_start || io_data.physical_size == 0)
io_data.physical_size = get_streamfile_size(sf) - io_data.cfg.stream_start;
io_data.physical_end = io_data.cfg.stream_start + io_data.physical_size;
VGM_LOG("ps=%x, pe=%lx\n", io_data.physical_size, io_data.physical_end);
io_data.logical_offset = -1; /* read reset */
//TODO: other validations
/* setup subfile */
new_sf = open_io_streamfile_f(sf, &io_data, sizeof(deblock_io_data), deblock_io_read, deblock_io_size);
return new_sf;
fail:
VGM_LOG("DEBLOCK: bad init\n");
close_streamfile(sf);
return NULL;
}
/*****************************************************/
static void block_callback(STREAMFILE *sf, off_t offset, deblock_io_data *data) {
/* read the whole block, will be skipped for unwanted sub-streams */
data->block_size = 0x02 + read_u16be(data->physical_offset, sf);
data->data_size = data->block_size;
//VGM_LOG("read at %lx + %lx, skips=%i, reads=%i\n", data->physical_offset, data->block_size, data->step_count, data->read_count);
}
static STREAMFILE* open_io_eaac_opus_streamfile_f(STREAMFILE *new_sf, int stream_number, int stream_count) {
deblock_config_t cfg = {0};
cfg.step_start = stream_number;
cfg.step_count = stream_count - 1;
cfg.block_callback = block_callback;
/* starts from 0 since new_sf is pre-deblocked */
/* setup subfile */
//new_sf = open_wrap_streamfile(sf); /* to be used with others */
new_sf = open_io_deblocker_streamfile_f(new_sf, &cfg);
return new_sf;
}
#endif /* _EA_EAAC_OPUS_STREAMFILE_H_ */

View File

@ -1,6 +1,7 @@
#ifndef _EA_EAAC_STREAMFILE_H_
#define _EA_EAAC_STREAMFILE_H_
#include "../streamfile.h"
#include "ea_eaac_opus_streamfile.h"
#define XMA_FRAME_SIZE 0x800
@ -261,6 +262,8 @@ static STREAMFILE* setup_eaac_streamfile(STREAMFILE *sf, int version, int codec,
new_sf = open_wrap_streamfile(sf);
new_sf = open_io_streamfile_f(new_sf, &io_data, sizeof(eaac_io_data), eaac_io_read, eaac_io_size);
new_sf = open_buffer_streamfile_f(new_sf, 0); /* EA-XMA and multichannel EALayer3 benefit from this */
if (codec == 0x0c && stream_count > 1) /* multichannel opus */
new_sf = open_io_eaac_opus_streamfile_f(new_sf, stream_number, stream_count);
return new_sf;
}

View File

@ -82,7 +82,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
/* 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 <= fsb5.total_subsongs; i++) {
for (i = 0; i < fsb5.total_subsongs; i++) {
size_t stream_header_size = 0;
off_t data_offset = 0;
uint32_t sample_mode1, sample_mode2; /* maybe one uint64? */
@ -138,6 +138,8 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
extraflag_size = (extraflag >> 1) & 0xFFFFFF; /* bits 25..1 (24)*/
extraflag_end = (extraflag & 0x01); /* bit 0 (1) */
/* parse target only, as flags change between subsongs */
if (i + 1 == target_subsong) {
switch(extraflag_type) {
case 0x01: /* channels */
fsb5.channels = read_8bit(extraflag_offset+0x04,streamFile);
@ -151,34 +153,37 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
fsb5.loop_end = read_32bitLE(extraflag_offset+0x08,streamFile);
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);
/* autodetect unwanted loops */
{
/* like FSB4 jingles/sfx/music do full loops for no reason, but happens a lot less.
* Most songs loop normally now with proper values [ex. Shantae, FFX] */
int full_loop, ajurika_loops;
int full_loop, ajurika_loops, is_small;
/* could use the same checks as FSB4 but simplified (ex. Sonic Boom Fire & Ice jingles) */
full_loop = fsb5.loop_start != 0x00;
/* disable some jingles, it's even possible one jingle (StingerA Var1) to not have loops
* and next one (StingerA Var2) do [Sonic Boom Fire & Ice (3DS)] */
full_loop = fsb5.loop_start == 0 && fsb5.loop_end + 20 >= fsb5.num_samples; /* around ~15 samples less */
/* a few longer Sonic songs shouldn't repeat, may add if other games need it */
is_small = 0; //fsb5.num_samples < 20 * fsb5.sample_rate;
/* wrong values in some files [Pac-Man CE2 Plus (Switch) pce2p_bgm_ajurika_*.fsb] */
ajurika_loops = fsb5.loop_start == 0x3c && fsb5.loop_end == 0x007F007F &&
fsb5.num_samples > fsb5.loop_end + 100000; /* arbitrary limit */
//;VGM_LOG("FSB5: loop start=%i, loop end=%i, samples=%i\n", fsb5.loop_start, fsb5.loop_end, fsb5.num_samples);
fsb5.num_samples > fsb5.loop_end + 10000; /* arbitrary test in case some game does have those */
fsb5.loop_flag = 1;
if (!full_loop || ajurika_loops) {
VGM_LOG("FSB5: disabled unwanted loop\n");
if ((full_loop && is_small) || ajurika_loops) {
VGM_LOG("FSB5: stream %i disabled unwanted loop ls=%i, le=%i, ns=%i\n", i, fsb5.loop_start, fsb5.loop_end, fsb5.num_samples);
fsb5.loop_flag = 0;
}
}
break;
case 0x04: /* free comment, or maybe SFX info */
break;
case 0x05: /* unknown 32b */ //todo multistream marker?
/* found in Tearaway Vita, value 0, first stream only */
VGM_LOG("FSB5: flag %x with value %08x\n", extraflag_type, read_32bitLE(extraflag_offset+0x04,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));
break;
case 0x06: /* XMA seek table */
/* no need for it */
@ -202,26 +207,27 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
* (xN entries)
*/
break;
case 0x0d: /* unknown 32b (config? usually 0x3fnnnn00 BE) */
case 0x0d: /* unknown 32b (config? usually 0x3fnnnn00 BE and sometimes 0x3dnnnn00 BE) */
/* found in some XMA2/Vorbis/FADPCM */
VGM_LOG("FSB5: flag %x with value %08x\n", 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,streamFile));
break;
default:
VGM_LOG("FSB5: unknown extraflag 0x%x at %x + 0x04 (size 0x%x)\n", extraflag_type, (uint32_t)extraflag_offset, extraflag_size);
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);
break;
}
}
extraflag_offset += 0x04 + extraflag_size;
stream_header_size += 0x04 + extraflag_size;
} while (extraflag_end != 0x00);
}
/* stream found */
if (i == target_subsong) {
/* target found */
if (i + 1 == target_subsong) {
fsb5.stream_offset = fsb5.base_header_size + fsb5.sample_header_size + fsb5.name_table_size + data_offset;
/* get stream size from next stream offset or full size if there is only one */
if (i == fsb5.total_subsongs) {
if (i + 1 == fsb5.total_subsongs) {
fsb5.stream_size = fsb5.sample_data_size - data_offset;
}
else {
@ -237,7 +243,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
break;
}
/* continue searching */
/* continue searching target */
fsb5.sample_header_offset += stream_header_size;
}
/* target stream not found*/

View File

@ -870,4 +870,6 @@ VGMSTREAM * init_vgmstream_bmp_konami(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_isb(STREAMFILE * streamFile);
VGMSTREAM* init_vgmstream_xssb(STREAMFILE *sf);
#endif /*_META_H*/

140
src/meta/xssb.c Normal file
View File

@ -0,0 +1,140 @@
#include "meta.h"
#include "../coding/coding.h"
//todo test and rethink usefulness/viability of globally using this
/* generic helper with a usable fields to describe static header values */
typedef struct {
int codec;
int type;
int channels;
int sample_rate;
int loop_flag;
int loop_start;
int loop_end;
int total_subsongs;
int target_subsong;
size_t file_size;
off_t info_start;
off_t header_start;
size_t header_entry;
off_t header_offset;
off_t data_start;
size_t data_size;
off_t stream_start;
size_t stream_size;
} header_t;
/* XSSB - from Artoon games [Blinx (Xbox), Blinx 2 (Xbox)] */
VGMSTREAM* init_vgmstream_xssb(STREAMFILE *sf) {
VGMSTREAM *vgmstream = NULL;
//off_t start_offset, header_offset, data_start, info_start, header_start;
//size_t header_size, stream_size;
//int loop_flag, channel_count, sample_rate, codec, loop_start, loop_end;
//int total_subsongs, target_subsong = streamFile->stream_index;
header_t h;
/* checks */
/* .bin: from named files inside .ipk bigfiles */
if (!check_extensions(sf, "bin,lbin"))
goto fail;
if (read_u32be(0x00, sf) != 0x58535342) /* "XSSB" */
goto fail;
/* 0x04: null */
/* 0x08: date-version ('20011217' in hex) */
/* 0x0c: null */
h.info_start = read_s32le(0x10, sf);
h.header_start = read_s32le(0x14, sf);
h.data_start = read_s32le(0x18, sf);
/* 0x1c: null */
h.header_entry = read_s16le(h.info_start + 0x00, sf);
/* 0x02: always 127 */
/* get subsongs from header entries */
{
off_t offset = h.header_start;
h.total_subsongs = 0;
h.target_subsong = sf->stream_index <= 0 ? 1 : sf->stream_index;
h.header_offset = 0;
while (offset < h.data_start) {
/* headers are just pasted together and then padding */
if (read_u32be(offset, sf) == 0)
break;
h.total_subsongs++;
if (h.target_subsong == h.total_subsongs) {
h.header_offset = offset;
}
offset += h.header_entry;
}
if (h.header_offset == 0)
goto fail;
if (h.target_subsong > h.total_subsongs || h.total_subsongs < 1)
goto fail;
}
/* read header */
h.codec = read_s16le(h.header_offset + 0x00, sf);
h.channels = read_s16le(h.header_offset + 0x02, sf);
h.sample_rate = read_u16le(h.header_offset + 0x04, sf);
/* 0x08: bitrate */
/* 0x0c: block align/bps */
/* 0x10: 0=PCM, 2=XBOX-IMA? */
h.stream_start = read_s32le(h.header_offset + 0x14, sf) + h.data_start;
h.stream_size = read_s32le(h.header_offset + 0x18, sf);
h.loop_start = read_s32le(h.header_offset + 0x1c, sf);
h.loop_end = read_s32le(h.header_offset + 0x20, sf);
/* others: unknown and mostly fixed values */
h.loop_flag = (h.loop_end > 0);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(h.channels, h.loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_XSSB;
vgmstream->sample_rate = h.sample_rate;
vgmstream->loop_start_sample = h.loop_start;
vgmstream->loop_end_sample = h.loop_end;
vgmstream->num_streams = h.total_subsongs;
vgmstream->stream_size = h.stream_size;
switch(h.codec) {
case 0x01:
vgmstream->coding_type = coding_PCM16LE;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x01;
vgmstream->num_samples = pcm_bytes_to_samples(h.stream_size, h.channels, 16);
break;
case 0x69:
vgmstream->coding_type = coding_XBOX_IMA;
vgmstream->layout_type = layout_none;
vgmstream->num_samples = xbox_ima_bytes_to_samples(h.stream_size, h.channels);
break;
}
if (!vgmstream_open_stream(vgmstream, sf, h.stream_start))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -30,6 +30,9 @@ typedef struct {
int cue_names_size;
off_t cue_names_offset;
int index_size;
int entry_size;
/* output */
int parse_done;
char name[STREAM_NAME_SIZE];
@ -38,9 +41,16 @@ typedef struct {
} xsb_header;
static void xsb_check_stream(xsb_header * xsb, int stream_index, int wavebank_index, off_t name_offset, STREAMFILE *sf) {
static void xsb_check_stream(xsb_header *xsb, int stream_index, int wavebank_index, off_t name_offset, STREAMFILE *sf) {
if (xsb->parse_done)
return;
//;VGM_LOG("XSB old: found stream=%i vs %i, wavebank=%i vs %i, name_offset=%lx\n", stream_index, xsb->selected_stream, wavebank_index, xsb->selected_wavebank, name_offset);
if (stream_index < 0 || stream_index > 0xFFF || wavebank_index < 0 || wavebank_index > xsb->wavebanks_count) {
VGM_LOG("XSB old: bad stream=%i, wavebank=%i\n", stream_index, wavebank_index);
return;
}
/* multiple names may correspond to a stream (ex. Blue Dragon), so we concat all */
if (xsb->selected_stream == stream_index &&
@ -48,7 +58,7 @@ static void xsb_check_stream(xsb_header * xsb, int stream_index, int wavebank_in
char name[STREAM_NAME_SIZE];
size_t name_size;
name_size = read_string(name,sizeof(name), name_offset,sf); /* null-terminated */
name_size = read_string(name,sizeof(name), name_offset, sf); /* null-terminated */
if (xsb->name_len) {
const char *cat = "; ";
@ -68,95 +78,154 @@ static void xsb_check_stream(xsb_header * xsb, int stream_index, int wavebank_in
}
}
/* old XACT1 is a bit different and much of it is unknown but this seems to work.
* - after header is the simple(?) cues table then complex(?) cues table
* - simple cues point to complex cues by index
* - complex cues may have stream/wavebank or point again to a sound(?) with the stream/wavebank
*/
static int parse_xsb_cues_old(xsb_header * xsb, STREAMFILE *sf) {
static int parse_xsb_old_cue_entry(xsb_header *xsb, STREAMFILE *sf, off_t name_offset, int entry) {
int32_t (*read_s32)(off_t,STREAMFILE*) = xsb->big_endian ? read_s32be : read_s32le;
int16_t (*read_s16)(off_t,STREAMFILE*) = xsb->big_endian ? read_s16be : read_s16le;
uint8_t flags, subflags;
int cue_index, stream_index, wavebank_index = 0;
off_t offset, name_offset, cue_offset, sound_offset;
int i;
size_t simple_entry, complex_entry;
uint32_t sound_type, sound_size;
int stream_index, wavebank_index;
off_t offset, jump_offset, sound_offset, min_sections_offset, max_sections_offset;
int i, j, sound_count, table_count;
if (xsb->version <= XSB_XACT1_1_MAX) {
simple_entry = 0x10;
complex_entry = 0x14;
}
else if (xsb->version <= XSB_XACT1_2_MAX) {
simple_entry = 0x14;
complex_entry = 0x14;
}
else {
VGM_LOG("XSB: unknown old format for version %x\n", xsb->version);
if (entry < 0 || entry > xsb->complex_cues_count) {
VGM_LOG("XSB old: ignored bad cue entry %i\n", entry);
goto fail;
}
min_sections_offset = xsb->sounds_offset + xsb->simple_cues_count*xsb->index_size + xsb->complex_cues_offset*xsb->entry_size;
max_sections_offset = get_streamfile_size(sf);
offset = xsb->sounds_offset;
for (i = 0; i < xsb->simple_cues_count; i++) {
offset = xsb->sounds_offset + xsb->simple_cues_count*xsb->index_size + entry*xsb->entry_size;
/* *** simple sound *** */
/* 00(2): flags? */
cue_index = read_s16(offset + 0x02,sf);
name_offset = read_s32(offset + 0x04,sf);
/* 06-14: unknown */
//;VGM_LOG("XSB old simple at %lx: cue=%i, name_offset=%lx\n", offset, cue_index, name_offset);
offset += simple_entry;
/*** cue entry ***/
/* 0x00: offset or stream/wave */
/* others: mostly 1 byte fields, probably config for sfx/complex entries */
flags = read_u8(offset + 0x0b, sf);
//;VGM_LOG("XSB old entry %i at %lx: flags=%x\n", entry, offset, flags);
/* when cue_index is -1 @0x08 points to some offset (random sound type?) [ex. ATV 3 Lawless (Xbox)] */
if (cue_index < 0 && cue_index > xsb->complex_cues_count) {
VGM_LOG("XSB old: ignored cue index %i\n", cue_index);
continue;
if (flags & 0x10) { /* multi entry (found with lower bits but not with 8) */
jump_offset = read_s32(offset + 0x00, sf);
if (jump_offset < min_sections_offset || jump_offset > max_sections_offset) {
VGM_LOG("XSB old entry %i at %lx: bad multi jump offset=%lx\n", entry, offset, jump_offset);
goto fail;
}
/*** table to streams ***/
table_count = read_s8(jump_offset + 0x00, sf);
/* 0x01: null? */
/* 0x02: always count*2? */
//;VGM_LOG("XSB old multi stream table at %lx: count=%x\n", jump_offset, table_count);
/* *** complex sound *** */
cue_offset = xsb->sounds_offset + xsb->simple_cues_count*simple_entry + cue_index*complex_entry;
for (j = 0; j < table_count; j++) {
stream_index = read_s16(jump_offset + 0x04 + 0x08*j + 0x00, sf);
wavebank_index = read_s16(jump_offset + 0x04 + 0x08*j + 0x02, sf);
/* 0x04: config? */
/* most fields looks like more flags and optional offsets depending of flags */
flags = read_u8(cue_offset + 0x0b,sf);
if (flags & 8) { /* simple */
stream_index = read_s16(cue_offset + 0x00,sf);
wavebank_index = read_s16(cue_offset + 0x02,sf);
xsb_check_stream(xsb, stream_index, wavebank_index, name_offset, sf);
if (xsb->parse_done) return 1;
}
//else if (flags & 4) { /* unsure */
// VGM_LOG("XSB old complex at %lx: unknown flags=%x\n", cue_offset, flags);
// continue;
//}
else { /* complex (flags none/1/2) */
sound_offset = read_s32(cue_offset + 0x00,sf);
/* *** jump entry *** */
/* 00(1): flags? */
sound_offset = read_s32(sound_offset + 0x01,sf) & 0x00FFFFFF; /* 24b */
/* *** sound entry *** */
subflags = read_u8(sound_offset + 0x00,sf);
if (subflags == 0x00) { /* 0x0c entry */
stream_index = read_s16(sound_offset + 0x08,sf);
wavebank_index = read_s16(sound_offset + 0x0a,sf);
}
else if (subflags == 0x0a) { /* 0x20 entry */
stream_index = read_s16(sound_offset + 0x1c,sf);
wavebank_index = read_s16(sound_offset + 0x1e,sf);
else if (flags & 0x8) { /* simple entry (also found with lower bits) */
stream_index = read_s16(offset + 0x00, sf);
wavebank_index = read_s16(offset + 0x02, sf);
xsb_check_stream(xsb, stream_index, wavebank_index, name_offset, sf);
if (xsb->parse_done) return 1;
}
else { /* complex entry (lower flags) */
jump_offset = read_s32(offset + 0x00, sf);
if (jump_offset < min_sections_offset || jump_offset > max_sections_offset) {
VGM_LOG("XSB old entry %i at %lx: bad complex jump offset=%lx\n", entry, offset, jump_offset);
goto fail;
}
/*** sound table ***/
sound_count = read_s8 (jump_offset + 0x00, sf);
sound_offset = read_s32(jump_offset + 0x01, sf) & 0x00FFFFFF; /* 24b */
//;VGM_LOG("XSB old entry %i sound table at %lx: count=%x\n", entry, jump_offset, sound_count);
/* read all sounds (seems ordered higher types to lower) */
for (i = 0; i < sound_count; i++) {
/*** sound entry ***/
sound_type = read_u8(sound_offset + 0x00, sf);
/* 0x01: rarely set but possible */
/* 0x02: null? */
sound_size = read_u8(sound_offset + 0x04, sf);
//;VGM_LOG("XSB old entry sound %i at %lx: type=%x\n", i, sound_offset, sound_type);
switch(sound_type) {
case 0x12:
case 0x11:
case 0x10:
case 0x07:
case 0x05:
/* config? (doesn't seem they contain entries or offsets) */
break;
#if 0
case 0x0a /* used? (0x20 entry)? */
stream_index = read_s16(sound_offset + 0x1c, sf);
wavebank_index = read_s16(sound_offset + 0x1e, sf);
break;
#endif
case 0x01: /* has more fields, uses subflag 0x04 */
case 0x00: /* smaller, uses subflag 0x44 (rare) */
subflags = read_u8(sound_offset + 0x05, sf);
if (subflags == 0x00 || subflags == 0x40) {
stream_index = read_s16(sound_offset + 0x08, sf);
wavebank_index = read_s16(sound_offset + 0x0a, sf);
xsb_check_stream(xsb, stream_index, wavebank_index, name_offset, sf);
if (xsb->parse_done) return 1;
}
else if (subflags == 0x04 || subflags == 0x44) {
jump_offset = read_s32(sound_offset + 0x08, sf);
if (jump_offset < min_sections_offset || jump_offset > max_sections_offset) {
VGM_LOG("XSB old entry %i at %lx: bad complex multi jump offset=%lx at %lx\n", entry, offset, jump_offset, sound_offset);
break;
}
/*** table to streams ***/
table_count = read_s8(jump_offset + 0x00, sf);
/* 0x01: null? */
/* 0x02: always count*2? */
//;VGM_LOG("XSB old complex stream table at %lx: count=%x\n", jump_offset, table_count);
for (j = 0; j < table_count; j++) {
stream_index = read_s16(jump_offset + 0x04 + 0x08*j + 0x00, sf);
wavebank_index = read_s16(jump_offset + 0x04 + 0x08*j + 0x02, sf);
/* 0x04: config? */
xsb_check_stream(xsb, stream_index, wavebank_index, name_offset, sf);
if (xsb->parse_done) return 1;
}
}
else {
VGM_LOG("XSB old sound at %lx: unknown subflags=%x\n", sound_offset, subflags);
continue;
VGM_LOG("XSB old entry %i at %lx: bad complex multi flags at %lx\n", entry, offset, sound_offset);
}
break;
stream_index = read_s16(sound_offset + 0x08, sf);
wavebank_index = read_s16(sound_offset + 0x0a, sf);
xsb_check_stream(xsb, stream_index, wavebank_index, name_offset, sf);
if (xsb->parse_done) return 1;
break;
default:
VGM_LOG("XSB old entry %i at %lx: unknown sound type=%x at %lx\n", entry, offset, sound_type, sound_offset);
break;
}
//;VGM_LOG("XSB old complex at %lx: flags=%x, stream=%i, wavebank=%i, name_offset=%lx\n", cue_offset, flags, stream_index, wavebank_index, name_offset);
xsb_check_stream(xsb, stream_index, wavebank_index, name_offset,sf);
if (xsb->parse_done) return 1;
sound_offset += 0x04 + 0x04 + sound_size;
}
}
return 1;
@ -164,7 +233,83 @@ fail:
return 0;
}
static int parse_xsb_clip(xsb_header * xsb, off_t offset, off_t name_offset, STREAMFILE *sf) {
/* old XACT1 is a bit different and much of it is unknown but this seems ok:
* - after header is the cue index table then cue entry table
* - each cue index points to a cue entry by number
* - each cue entry have a stream/wavebank, directly or first pointing to a "sound"
* sound entries are more complex with multi-parts and subtables (mainly used for sfx,
* ex. ATV 3 Lawless (Xbox), Psychonauts (Xbox) have more complex types.
*
* Some streams may not be pointed at all as they don't have an apparent name, or have an
* entry in the sound table but no reference to it (ex. CommonMusic.xsb or BBFX.xsb in Psychonauts)
*
* Data is divided like:
* - header
* - cue indexes
* - cue entries
* - wavebank names
* - cue names
* - unknown table
* - sounds jump table
* - sounds entries
* - multi entry jump table
* - others
*/
static int parse_xsb_old_cues(xsb_header *xsb, STREAMFILE *sf) {
int32_t (*read_s32)(off_t,STREAMFILE*) = xsb->big_endian ? read_s32be : read_s32le;
int16_t (*read_s16)(off_t,STREAMFILE*) = xsb->big_endian ? read_s16be : read_s16le;
uint16_t flags;
int cue_entry;
off_t offset, name_offset, jump_offset;
int i, j, table_count;
//;VGM_LOG("XSB old: s.offset=%lx, index count=%i, entry count=%i\n", xsb->sounds_offset, xsb->simple_cues_count, xsb->complex_cues_count);
offset = xsb->sounds_offset;
for (i = 0; i < xsb->simple_cues_count; i++) {
/*** cue index ***/
flags = read_s16(offset + 0x00, sf); /* 0 is normal, 2 exists and 8 often goes with -1 (random) entry */
cue_entry = read_s16(offset + 0x02, sf);
name_offset = read_s32(offset + 0x04, sf);
/* 0x08: table offset, or -1 */
/* 0x0c: some low value or flag? */
/* 0x0e: some index? */
/* 0x10: 4 fields? (-1 or 7) */
//;VGM_LOG("XSB old index %i at %lx: flags=%x, entry=%i, name_offset=%lx\n", i, offset, flags, cue_entry, name_offset);
if (cue_entry < 0) {
jump_offset = read_s32(offset + 0x08, sf);
/* 0x0c/0e: some count? */
/* 0x10: offset to some empty-ish table */
/*** table (random?) to cue entry ***/
table_count = read_s8(jump_offset + 0x00, sf);
/* 0x01: often 0x60? */
/* 0x02: always count*2? */
//;VGM_LOG("XSB old entry table at %lx: count=%x\n", jump_offset, table_count);
for (j = 0; j < table_count; j++) {
cue_entry = read_s16(jump_offset + 0x04 + 0x08*j, sf);
/* 0x02: null? */
/* 0x04/6: related to randomness? */
parse_xsb_old_cue_entry(xsb, sf, name_offset, cue_entry);
if (xsb->parse_done) return 1;
}
}
else {
parse_xsb_old_cue_entry(xsb, sf, name_offset, cue_entry);
if (xsb->parse_done) return 1;
}
offset += xsb->index_size;
}
return 1;
}
static int parse_xsb_clip(xsb_header *xsb, off_t offset, off_t name_offset, STREAMFILE *sf) {
uint32_t (*read_u32)(off_t,STREAMFILE*) = xsb->big_endian ? read_u32be : read_u32le;
int16_t (*read_s16)(off_t,STREAMFILE*) = xsb->big_endian ? read_s16be : read_s16le;
@ -173,13 +318,13 @@ static int parse_xsb_clip(xsb_header * xsb, off_t offset, off_t name_offset, STR
int i, t, track_count, event_count;
event_count = read_s8(offset + 0x00,sf);
event_count = read_s8(offset + 0x00, sf);
//;VGM_LOG("XSB clip at %lx\n", offset);
offset += 0x01;
for (i = 0; i < event_count; i++) {
flags = read_u32(offset + 0x00,sf);
flags = read_u32(offset + 0x00, sf);
/* 04(2): random offset */
//;VGM_LOG("XSB clip event: %x at %lx\n", flags, offset);
@ -190,8 +335,8 @@ static int parse_xsb_clip(xsb_header * xsb, off_t offset, off_t name_offset, STR
case 0x01: /* playwave event */
/* 00(1): unknown */
/* 01(1): flags */
stream_index = read_s16(offset + 0x02,sf);
wavebank_index = read_s8 (offset + 0x04,sf);
stream_index = read_s16(offset + 0x02, sf);
wavebank_index = read_s8 (offset + 0x04, sf);
/* 05(1): loop count */
/* 06(2): pan angle */
/* 08(2): pan arc */
@ -199,7 +344,7 @@ static int parse_xsb_clip(xsb_header * xsb, off_t offset, off_t name_offset, STR
//;VGM_LOG("XSB clip event 1 at %lx: stream=%i, wavebank=%i\n", offset, stream_index, wavebank_index);
offset += 0x0a;
xsb_check_stream(xsb, stream_index, wavebank_index, name_offset,sf);
xsb_check_stream(xsb, stream_index, wavebank_index, name_offset, sf);
if (xsb->parse_done) return 1;
break;
@ -209,7 +354,7 @@ static int parse_xsb_clip(xsb_header * xsb, off_t offset, off_t name_offset, STR
/* 02(1): loop count */
/* 03(2): pan angle */
/* 05(2): pan arc */
track_count = read_s16(offset + 0x07,sf);
track_count = read_s16(offset + 0x07, sf);
/* 09(1): flags? */
/* 0a(5): unknown */
@ -217,15 +362,15 @@ static int parse_xsb_clip(xsb_header * xsb, off_t offset, off_t name_offset, STR
offset += 0x0F;
for (t = 0; t < track_count; t++) {
stream_index = read_s16(offset + 0x00,sf);
wavebank_index = read_s8 (offset + 0x02,sf);
stream_index = read_s16(offset + 0x00, sf);
wavebank_index = read_s8 (offset + 0x02, sf);
/* 03(1): min weight */
/* 04(1): min weight */
//;VGM_LOG("XSB clip event 3: track=%i, stream=%i, wavebank=%i\n", t, stream_index, wavebank_index);
offset += 0x05;
xsb_check_stream(xsb, stream_index, wavebank_index, name_offset,sf);
xsb_check_stream(xsb, stream_index, wavebank_index, name_offset, sf);
if (xsb->parse_done) return 1;
}
break;
@ -233,8 +378,8 @@ static int parse_xsb_clip(xsb_header * xsb, off_t offset, off_t name_offset, STR
case 0x04: /* playwave event */
/* 00(1): unknown */
/* 01(1): flags */
stream_index = read_s16(offset + 0x02,sf);
wavebank_index = read_s8 (offset + 0x04,sf);
stream_index = read_s16(offset + 0x02, sf);
wavebank_index = read_s8 (offset + 0x04, sf);
/* 05(1): loop count */
/* 06(2): pan angle */
/* 08(2): pan arc */
@ -252,7 +397,7 @@ static int parse_xsb_clip(xsb_header * xsb, off_t offset, off_t name_offset, STR
//;VGM_LOG("XSB clip event 4 at %lx: stream=%i, wavebank=%i\n", offset, stream_index, wavebank_index);
offset += 0x1c;
xsb_check_stream(xsb, stream_index, wavebank_index, name_offset,sf);
xsb_check_stream(xsb, stream_index, wavebank_index, name_offset, sf);
if (xsb->parse_done) return 1;
break;
@ -272,7 +417,7 @@ static int parse_xsb_clip(xsb_header * xsb, off_t offset, off_t name_offset, STR
/* 16(1): max Q */
/* 17(1): unknown */
/* 18(1): variation flags */
track_count = read_s16(offset + 0x19,sf);
track_count = read_s16(offset + 0x19, sf);
/* 1a(1): flags 2 */
/* 1b(5): unknown 2 */
@ -280,15 +425,15 @@ static int parse_xsb_clip(xsb_header * xsb, off_t offset, off_t name_offset, STR
offset += 0x20;
for (t = 0; t < track_count; t++) {
stream_index = read_s16(offset + 0x00,sf);
wavebank_index = read_s8 (offset + 0x02,sf);
stream_index = read_s16(offset + 0x00, sf);
wavebank_index = read_s8 (offset + 0x02, sf);
/* 03(1): min weight */
/* 04(1): min weight */
//;VGM_LOG("XSB clip event 6: track=%i, stream=%i, wavebank=%i at %lx\n", t, stream_index, wavebank_index, offset);
offset += 0x05;
xsb_check_stream(xsb, stream_index, wavebank_index, name_offset,sf);
xsb_check_stream(xsb, stream_index, wavebank_index, name_offset, sf);
if (xsb->parse_done) return 1;
}
break;
@ -318,7 +463,7 @@ fail:
return 0;
}
static int parse_xsb_sound(xsb_header * xsb, off_t offset, off_t name_offset, STREAMFILE *sf) {
static int parse_xsb_sound(xsb_header *xsb, off_t offset, off_t name_offset, STREAMFILE *sf) {
int32_t (*read_s32)(off_t,STREAMFILE*) = xsb->big_endian ? read_s32be : read_s32le;
int16_t (*read_s16)(off_t,STREAMFILE*) = xsb->big_endian ? read_s16be : read_s16le;
@ -327,7 +472,7 @@ static int parse_xsb_sound(xsb_header * xsb, off_t offset, off_t name_offset, ST
int i, clip_count = 0;
flags = read_u8 (offset + 0x00,sf);
flags = read_u8 (offset + 0x00, sf);
/* 0x01(2): category */
/* 0x03(1): decibels */
/* 0x04(2): pitch */
@ -338,24 +483,24 @@ static int parse_xsb_sound(xsb_header * xsb, off_t offset, off_t name_offset, ST
offset += 0x09;
if (flags & 0x01) { /* complex sound */
clip_count = read_u8 (offset + 0x00,sf);
clip_count = read_u8 (offset + 0x00, sf);
//;VGM_LOG("XSB sound: complex with clips=%i\n", clip_count);
offset += 0x01;
}
else {
stream_index = read_s16(offset + 0x00,sf);
wavebank_index = read_s8(offset + 0x02,sf);
stream_index = read_s16(offset + 0x00, sf);
wavebank_index = read_s8(offset + 0x02, sf);
//;VGM_LOG("XSB sound: simple with stream=%i, wavebank=%i\n", stream_index, wavebank_index);
offset += 0x03;
xsb_check_stream(xsb, stream_index, wavebank_index, name_offset,sf);
xsb_check_stream(xsb, stream_index, wavebank_index, name_offset, sf);
if (xsb->parse_done) return 1;
}
if (flags & 0x0E) { /* has RPCs */
size_t rpc_size = read_s16(offset + 0x00,sf);
size_t rpc_size = read_s16(offset + 0x00, sf);
/* 0x02(2): preset count */
/* 0x04(4*count): RPC indexes */
/* (presets per flag 2/4/8 flag) */
@ -363,7 +508,7 @@ static int parse_xsb_sound(xsb_header * xsb, off_t offset, off_t name_offset, ST
}
if (flags & 0x10) { /* has DSPs */
size_t dsp_size = read_s16(offset + 0x00,sf);
size_t dsp_size = read_s16(offset + 0x00, sf);
/* follows RPC format? */
offset += dsp_size;
}
@ -372,14 +517,14 @@ static int parse_xsb_sound(xsb_header * xsb, off_t offset, off_t name_offset, ST
off_t clip_offset;
for (i = 0; i < clip_count; i++) {
/* 00(1): decibels */
clip_offset = read_s32(offset + 0x01,sf);
clip_offset = read_s32(offset + 0x01, sf);
/* 05(2): filter config */
/* 07(2): filter frequency */
//;VGM_LOG("XSB sound clip %i at %lx\n", i, offset);
offset += 0x09;
parse_xsb_clip(xsb, clip_offset, name_offset,sf);
parse_xsb_clip(xsb, clip_offset, name_offset, sf);
if (xsb->parse_done) return 1;
}
}
@ -387,7 +532,7 @@ static int parse_xsb_sound(xsb_header * xsb, off_t offset, off_t name_offset, ST
return 0;
}
static int parse_xsb_variation(xsb_header * xsb, off_t offset, off_t name_offset, STREAMFILE *sf) {
static int parse_xsb_variation(xsb_header *xsb, off_t offset, off_t name_offset, STREAMFILE *sf) {
int32_t (*read_s32)(off_t,STREAMFILE*) = xsb->big_endian ? read_s32be : read_s32le;
uint16_t (*read_u16)(off_t,STREAMFILE*) = xsb->big_endian ? read_u16be : read_u16le;
int16_t (*read_s16)(off_t,STREAMFILE*) = xsb->big_endian ? read_s16be : read_s16le;
@ -397,8 +542,8 @@ static int parse_xsb_variation(xsb_header * xsb, off_t offset, off_t name_offset
int i, variation_count;
variation_count = read_s16(offset + 0x00,sf);
flags = read_u16(offset + 0x02,sf);
variation_count = read_s16(offset + 0x00, sf);
flags = read_u16(offset + 0x02, sf);
//;VGM_LOG("XSB variation at %lx\n", offset);
offset += 0x04;
@ -408,32 +553,32 @@ static int parse_xsb_variation(xsb_header * xsb, off_t offset, off_t name_offset
switch ((flags >> 3) & 0x7) {
case 0: /* wave */
stream_index = read_s16(offset + 0x00,sf);
wavebank_index = read_s8(offset + 0x02,sf);
stream_index = read_s16(offset + 0x00, sf);
wavebank_index = read_s8(offset + 0x02, sf);
/* 03(1): weight min */
/* 04(1): weight max */
//;VGM_LOG("XSB variation: type 0 with stream=%i, wavebank=%i\n", stream_index, wavebank_index);
offset += 0x05;
xsb_check_stream(xsb, stream_index, wavebank_index, name_offset,sf);
xsb_check_stream(xsb, stream_index, wavebank_index, name_offset, sf);
if (xsb->parse_done) return 1;
break;
case 1: /* sound */
sound_offset = read_s32(offset + 0x00,sf);
sound_offset = read_s32(offset + 0x00, sf);
/* 04(1): weight min */
/* 05(1): weight max */
//;VGM_LOG("XSB variation: type 1\n");
offset += 0x06;
parse_xsb_sound(xsb, sound_offset, name_offset,sf);
parse_xsb_sound(xsb, sound_offset, name_offset, sf);
if (xsb->parse_done) return 1;
break;
case 3: /* sound */
sound_offset = read_s32(offset + 0x00,sf);
sound_offset = read_s32(offset + 0x00, sf);
/* 04(4): weight min */
/* 08(4): weight max */
/* 0c(4): flags */
@ -441,18 +586,18 @@ static int parse_xsb_variation(xsb_header * xsb, off_t offset, off_t name_offset
//;VGM_LOG("XSB variation: type 3\n");
offset += 0x10;
parse_xsb_sound(xsb, sound_offset, name_offset,sf);
parse_xsb_sound(xsb, sound_offset, name_offset, sf);
if (xsb->parse_done) return 1;
break;
case 4: /* compact wave */
stream_index = read_s16(offset + 0x00,sf);
wavebank_index = read_s8(offset + 0x02,sf);
stream_index = read_s16(offset + 0x00, sf);
wavebank_index = read_s8(offset + 0x02, sf);
//;VGM_LOG("XSB variation: type 4 with stream=%i, wavebank=%i\n", stream_index, wavebank_index);
offset += 0x03;
xsb_check_stream(xsb, stream_index, wavebank_index, name_offset,sf);
xsb_check_stream(xsb, stream_index, wavebank_index, name_offset, sf);
if (xsb->parse_done) return 1;
break;
@ -474,7 +619,7 @@ fail:
}
static int parse_xsb_cues_new(xsb_header * xsb, STREAMFILE *sf) {
static int parse_xsb_cues(xsb_header *xsb, STREAMFILE *sf) {
int32_t (*read_s32)(off_t,STREAMFILE*) = xsb->big_endian ? read_s32be : read_s32le;
uint8_t flags;
@ -486,23 +631,23 @@ static int parse_xsb_cues_new(xsb_header * xsb, STREAMFILE *sf) {
offset = xsb->simple_cues_offset;
for (i = 0; i < xsb->simple_cues_count; i++) {
/* 00(1): flags */
sound_offset = read_s32(offset + 0x01,sf);
sound_offset = read_s32(offset + 0x01, sf);
//;VGM_LOG("XSB cues: simple %i at %lx\n", i, offset);
offset += 0x05;
name_offset = read_s32(names_offset + 0x00,sf);
name_offset = read_s32(names_offset + 0x00, sf);
/* 04(2): unknown (-1) */
names_offset += 0x06;
parse_xsb_sound(xsb, sound_offset, name_offset,sf);
parse_xsb_sound(xsb, sound_offset, name_offset, sf);
if (xsb->parse_done) break;
}
offset = xsb->complex_cues_offset;
for (i = 0; i < xsb->complex_cues_count; i++) {
flags = read_u8(offset + 0x00,sf);
sound_offset = read_s32(offset + 0x01,sf);
flags = read_u8(offset + 0x00, sf);
sound_offset = read_s32(offset + 0x01, sf);
/* 05(4): unknown (sound) / transition table offset (variation) */
/* 09(1): instance limit */
/* 0a(2): fade in sec */
@ -512,14 +657,14 @@ static int parse_xsb_cues_new(xsb_header * xsb, STREAMFILE *sf) {
//;VGM_LOG("XSB cues: complex %i at %lx\n", i, offset);
offset += 0x0f;
name_offset = read_s32(names_offset + 0x00,sf);
name_offset = read_s32(names_offset + 0x00, sf);
/* 04(2): unknown (-1) */
names_offset += 0x06;
if (flags & (1<<2))
parse_xsb_sound(xsb, sound_offset, name_offset,sf);
parse_xsb_sound(xsb, sound_offset, name_offset, sf);
else
parse_xsb_variation(xsb, sound_offset, name_offset,sf);
parse_xsb_variation(xsb, sound_offset, name_offset, sf);
if (xsb->parse_done) break;
}
@ -547,7 +692,7 @@ static int parse_xsb_cues_new(xsb_header * xsb, STREAMFILE *sf) {
* - https://github.com/MonoGame/MonoGame/blob/master/MonoGame.Framework/Audio/Xact/
* - https://github.com/espes/MacTerrariaWrapper/tree/master/xactxtract
*/
static int parse_xsb(xsb_header * xsb, STREAMFILE *sf, char *xwb_wavebank_name) {
static int parse_xsb(xsb_header *xsb, STREAMFILE *sf, char *xwb_wavebank_name) {
int32_t (*read_s32)(off_t,STREAMFILE*) = NULL;
int16_t (*read_s16)(off_t,STREAMFILE*) = NULL;
@ -563,77 +708,84 @@ static int parse_xsb(xsb_header * xsb, STREAMFILE *sf, char *xwb_wavebank_name)
/* parse sound bank header */
xsb->version = read_s16(0x04,sf); /* tool version */
xsb->version = read_s16(0x04, sf); /* tool version */
if (xsb->version <= XSB_XACT1_0_MAX) {
/* 06(2): crc */
xsb->wavebanks_offset = read_s32(0x08,sf);
xsb->wavebanks_offset = read_s32(0x08, sf);
/* 0c(4): unknown1 offset (entry: 0x04) */
/* 10(4): unknown2 offset */
/* 14(2): element count? */
/* 16(2): empty? */
/* 18(2): empty? */
xsb->complex_cues_count = read_s16(0x1a,sf);
xsb->simple_cues_count = read_s16(0x1c,sf);
xsb->wavebanks_count = read_s16(0x1e,sf);
xsb->complex_cues_count = read_s16(0x1a, sf);
xsb->simple_cues_count = read_s16(0x1c, sf);
xsb->wavebanks_count = read_s16(0x1e, sf);
/* 20(10): xsb name */
xsb->sounds_offset = 0x30;
xsb->wavebanks_name_size = 0x10;
xsb->index_size = 0x10;
xsb->entry_size = 0x14;
}
else if (xsb->version <= XSB_XACT1_1_MAX) {
/* 06(2): crc */
xsb->wavebanks_offset = read_s32(0x08,sf);
xsb->wavebanks_offset = read_s32(0x08, sf);
/* 0c(4): unknown1 offset (entry: 0x04) */
/* 10(4): unknown2 offset */
/* 14(4): unknown3 offset */
/* 18(2): empty? */
/* 1a(2): element count? */
xsb->complex_cues_count = read_s16(0x1c,sf);
xsb->simple_cues_count = read_s16(0x1e,sf);
xsb->complex_cues_count = read_s16(0x1c, sf);
xsb->simple_cues_count = read_s16(0x1e, sf);
/* 20(2): unknown count? (related to unknown2?) */
xsb->wavebanks_count = read_s16(0x22,sf);
xsb->wavebanks_count = read_s16(0x22, sf);
/* 24(10): xsb name */
xsb->sounds_offset = 0x34;
xsb->wavebanks_name_size = 0x10;
xsb->index_size = 0x10;
xsb->entry_size = 0x14;
}
else if (xsb->version <= XSB_XACT1_2_MAX) {
/* 06(2): crc */
xsb->wavebanks_offset = read_s32(0x08,sf);
xsb->wavebanks_offset = read_s32(0x08, sf);
/* 0c(4): unknown1 offset (entry: 0x14) */
/* 10(4): unknown2 offset (entry: variable) */
/* 14(4): unknown3 offset */
/* 18(2): empty? */
/* 1a(2): element count? */
xsb->complex_cues_count = read_s16(0x1c,sf);
xsb->simple_cues_count = read_s16(0x1e,sf);
xsb->complex_cues_count = read_s16(0x1c, sf);
xsb->simple_cues_count = read_s16(0x1e, sf);
/* 20(2): unknown count? (related to unknown2?) */
xsb->wavebanks_count = read_s16(0x22,sf);
xsb->wavebanks_count = read_s16(0x22, sf);
/* 24(4): null? */
/* 28(10): xsb name */
xsb->sounds_offset = 0x38;
xsb->wavebanks_name_size = 0x10;
xsb->index_size = 0x14;
xsb->entry_size = 0x14;
}
else if (xsb->version <= XSB_XACT2_MAX) {
/* 06(2): crc */
/* 08(1): platform? (3=X360) */
xsb->simple_cues_count = read_s16(0x09,sf);
xsb->complex_cues_count = read_s16(0x0B,sf);
xsb->wavebanks_count = read_s8 (0x11,sf);
xsb->sounds_count = read_s16(0x12,sf);
xsb->simple_cues_count = read_s16(0x09, sf);
xsb->complex_cues_count = read_s16(0x0B, sf);
xsb->wavebanks_count = read_s8 (0x11, sf);
xsb->sounds_count = read_s16(0x12, sf);
/* 14(2): unknown */
xsb->cue_names_size = read_s32(0x16,sf);
xsb->simple_cues_offset = read_s32(0x1a,sf);
xsb->complex_cues_offset = read_s32(0x1e,sf);
xsb->cue_names_offset = read_s32(0x22,sf);
xsb->cue_names_size = read_s32(0x16, sf);
xsb->simple_cues_offset = read_s32(0x1a, sf);
xsb->complex_cues_offset = read_s32(0x1e, sf);
xsb->cue_names_offset = read_s32(0x22, sf);
/* 26(4): unknown */
/* 2a(4): unknown */
/* 2e(4): unknown */
xsb->wavebanks_offset = read_s32(0x32,sf);
xsb->wavebanks_offset = read_s32(0x32, sf);
/* 36(4): cue name hash table offset? */
xsb->nameoffsets_offset = read_s32(0x3a,sf);
xsb->sounds_offset = read_s32(0x3e,sf);
xsb->nameoffsets_offset = read_s32(0x3a, sf);
xsb->sounds_offset = read_s32(0x3e, sf);
/* 42(4): unknown */
/* 46(4): unknown */
/* 4a(64): xsb name */
@ -646,23 +798,23 @@ static int parse_xsb(xsb_header * xsb, STREAMFILE *sf, char *xwb_wavebank_name)
/* 0a(4): last modified low */
/* 0e(4): last modified high */
/* 12(1): platform? (1=PC, 3=X360) */
xsb->simple_cues_count = read_s16(0x13,sf);
xsb->complex_cues_count = read_s16(0x15,sf);
xsb->simple_cues_count = read_s16(0x13, sf);
xsb->complex_cues_count = read_s16(0x15, sf);
/* 17(2): unknown count? */
/* 19(2): element count? (often simple+complex cues, but may be more) */
xsb->wavebanks_count = read_s8 (0x1b,sf);
xsb->sounds_count = read_s16(0x1c,sf);
xsb->cue_names_size = read_s32(0x1e,sf);
xsb->simple_cues_offset = read_s32(0x22,sf);
xsb->complex_cues_offset = read_s32(0x26,sf);
xsb->cue_names_offset = read_s32(0x2a,sf);
xsb->wavebanks_count = read_s8 (0x1b, sf);
xsb->sounds_count = read_s16(0x1c, sf);
xsb->cue_names_size = read_s32(0x1e, sf);
xsb->simple_cues_offset = read_s32(0x22, sf);
xsb->complex_cues_offset = read_s32(0x26, sf);
xsb->cue_names_offset = read_s32(0x2a, sf);
/* 0x2E(4): unknown offset */
/* 0x32(4): variation tables offset */
/* 0x36(4): unknown offset */
xsb->wavebanks_offset = read_s32(0x3a,sf);
xsb->wavebanks_offset = read_s32(0x3a, sf);
/* 0x3E(4): cue name hash table offset (16b each) */
xsb->nameoffsets_offset = read_s32(0x42,sf);
xsb->sounds_offset = read_s32(0x46,sf);
xsb->nameoffsets_offset = read_s32(0x42, sf);
xsb->sounds_offset = read_s32(0x46, sf);
/* 4a(64): xsb name */
xsb->wavebanks_name_size = 0x40;
@ -692,7 +844,7 @@ static int parse_xsb(xsb_header * xsb, STREAMFILE *sf, char *xwb_wavebank_name)
offset = xsb->wavebanks_offset;
for (i = 0; i < xsb->wavebanks_count; i++) {
read_string(xsb_wavebank_name,xsb->wavebanks_name_size, offset,sf);
read_string(xsb_wavebank_name,xsb->wavebanks_name_size, offset, sf);
//;VGM_LOG("XSB wavebanks: bank %i=%s\n", i, wavebank_name);
if (strcasecmp(xsb_wavebank_name, xwb_wavebank_name)==0) {
//;VGM_LOG("XSB banks: current xwb is wavebank %i=%s\n", i, xsb_wavebank_name);
@ -705,17 +857,17 @@ static int parse_xsb(xsb_header * xsb, STREAMFILE *sf, char *xwb_wavebank_name)
//;VGM_LOG("xsb: selected wavebank=%i\n", xsb->selected_wavebank);
if (xsb->selected_wavebank == -1) {
VGM_LOG("XSB: current wavebank not found, selecting first\n");
xsb->selected_wavebank = 0; //todo goto fail?
xsb->selected_wavebank = 0;
}
}
/* find cue pointing to stream */
if (xsb->version <= XSB_XACT1_2_MAX) {
parse_xsb_cues_old(xsb, sf);
parse_xsb_old_cues(xsb, sf);
}
else {
parse_xsb_cues_new(xsb, sf);
parse_xsb_cues(xsb, sf);
}
return 1;
@ -739,6 +891,12 @@ static STREAMFILE * open_xsb_filename_pair(STREAMFILE *streamXwb) {
{"StreamBank_*.xwb","SoundBank_*.xsb"}, /* Ginga Force (X360) */
{"WaveBank_*.xwb","SoundBank_*.xsb"}, /* Ginga Force (X360) */
{"*_WB.xwb","*_SB.xsb"}, /* Ninja Blade (X360) */
{"*_WB.xwb","*_SB.xsb"}, /* Ninja Blade (X360) */
{"CA_NightMusic.xwb","CAMusic.xsb"}, /* Psychonauts (Xbox) */
{"CAJAMusic.xwb","CAMusic.xsb"}, /* "" */
{"STFX.xwb","CommonMusic.xsb"}, /* "" */
{"CALI_NightFX.xwb","CAFX.xsb"}, /* "" */
/* Psychonauts has a bunch more pairs for sfx too, improve */
{"*.xwb","*.xsb"}, /* default */
};
int i;

View File

@ -482,6 +482,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_opus_opusnx,
init_vgmstream_opus_sqex,
init_vgmstream_isb,
init_vgmstream_xssb,
/* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */
init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */

View File

@ -715,6 +715,7 @@ typedef enum {
meta_UBI_HX,
meta_BMP_KONAMI,
meta_ISB,
meta_XSSB,
} meta_t;

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">