diff --git a/src/formats.c b/src/formats.c index 89727700..a2713aaf 100644 --- a/src/formats.c +++ b/src/formats.c @@ -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"}, }; diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index b34f4822..194547ee 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -1778,10 +1778,14 @@ RelativePath=".\meta\xps.c" > - - + + + + diff --git a/src/libvgmstream.vcxproj b/src/libvgmstream.vcxproj index 647c6c49..4809abd5 100644 --- a/src/libvgmstream.vcxproj +++ b/src/libvgmstream.vcxproj @@ -523,6 +523,7 @@ + diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index 7288b942..9ce72cf5 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -1087,6 +1087,9 @@ meta\Source Files + + meta\Source Files + meta\Source Files diff --git a/src/meta/meta.h b/src/meta/meta.h index e6f48516..81aae0fe 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -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*/ diff --git a/src/meta/xssb.c b/src/meta/xssb.c new file mode 100644 index 00000000..8f63c793 --- /dev/null +++ b/src/meta/xssb.c @@ -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; +} diff --git a/src/vgmstream.c b/src/vgmstream.c index f7f38359..8d27fb09 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -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 */ diff --git a/src/vgmstream.h b/src/vgmstream.h index 3b5b1244..5527dd1c 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -715,6 +715,7 @@ typedef enum { meta_UBI_HX, meta_BMP_KONAMI, meta_ISB, + meta_XSSB, } meta_t;