From 0c40610f7fc8673807d734a1c68c09f22f26f2e7 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 19 Jan 2019 20:01:34 +0100 Subject: [PATCH 01/10] Fix KOEI .mic loops [Dynasty Tactics 2 (PS2)] --- src/formats.c | 2 +- src/meta/ps2_mic.c | 81 ++++++++++++++++------------------------------ 2 files changed, 28 insertions(+), 55 deletions(-) diff --git a/src/formats.c b/src/formats.c index bddd4db8..40367c6d 100644 --- a/src/formats.c +++ b/src/formats.c @@ -769,7 +769,7 @@ static const meta_info meta_info_list[] = { {meta_PS_HEADERLESS, "Headerless PS-ADPCM raw header"}, {meta_PS2_MIB_MIH, "Sony MultiStream MIH+MIB header"}, {meta_DSP_MPDSP, "Single DSP header stereo by .mpdsp extension"}, - {meta_PS2_MIC, "assume KOEI MIC file by .mic extension"}, + {meta_PS2_MIC, "KOEI .MIC header"}, {meta_DSP_JETTERS, "Double DSP header stereo by _lr.dsp extension"}, {meta_DSP_MSS, "Double DSP header stereo by .mss extension"}, {meta_DSP_GCM, "Double DSP header stereo by .gcm extension"}, diff --git a/src/meta/ps2_mic.c b/src/meta/ps2_mic.c index ee7c917d..14850d20 100644 --- a/src/meta/ps2_mic.c +++ b/src/meta/ps2_mic.c @@ -1,75 +1,48 @@ #include "meta.h" -#include "../util.h" - -/* MIC - - PS2 MIC format is an interleaved format found in most of KOEI Games - The header always start the long value 0x800 which is the start - of the BGM datas. - - 2008-05-15 - Fastelbja : First version ... -*/ +#include "../coding/coding.h" +/* .MIC - from KOEI games [Crimson Sea 2 (PS2), Dynasty Tactics 2 (PS2)] */ VGMSTREAM * init_vgmstream_ps2_mic(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; + off_t start_offset; + int loop_flag, channel_count, loop_start, loop_end, sample_rate; + size_t interleave, block_size; - int loop_flag=0; - int channel_count; - int i; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("mic",filename_extension(filename))) goto fail; - - /* check Header */ - if (read_32bitLE(0x00,streamFile) != 0x800) + /* checks */ + if (!check_extensions(streamFile, "mic")) goto fail; - /* check loop */ - loop_flag = (read_32bitLE(0x14,streamFile)!=1); - - channel_count=read_32bitLE(0x08,streamFile); + start_offset = read_32bitLE(0x00,streamFile); + if (start_offset != 0x800) goto fail; + sample_rate = read_32bitLE(0x04,streamFile); + channel_count = read_32bitLE(0x08,streamFile); + interleave = read_32bitLE(0x0c,streamFile); + loop_end = read_32bitLE(0x10,streamFile); + loop_start = read_32bitLE(0x14,streamFile); + loop_flag = (loop_start != 1); + block_size = interleave * channel_count; /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); + vgmstream = allocate_vgmstream(channel_count, loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitLE(0x04,streamFile); - - /* Compression Scheme */ - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = read_32bitLE(0x10,streamFile)*14*channel_count; - - /* Get loop point values */ - if(vgmstream->loop_flag) { - vgmstream->loop_start_sample = read_32bitLE(0x14,streamFile)*14*channel_count; - vgmstream->loop_end_sample = read_32bitLE(0x10,streamFile)*14*channel_count; - } - - vgmstream->interleave_block_size = read_32bitLE(0x0C,streamFile); - vgmstream->layout_type = layout_interleave; vgmstream->meta_type = meta_PS2_MIC; + vgmstream->sample_rate = sample_rate; - /* open the file for reading by each channel */ - { - for (i=0;ich[i].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); + vgmstream->coding_type = coding_PSX; + vgmstream->interleave_block_size = interleave; + vgmstream->layout_type = layout_interleave; - if (!vgmstream->ch[i].streamfile) goto fail; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset= - (off_t)(0x800+vgmstream->interleave_block_size*i); - } - } + vgmstream->num_samples = ps_bytes_to_samples(loop_end * block_size, channel_count); + vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start * block_size, channel_count); + vgmstream->loop_end_sample = vgmstream->num_samples; + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; return vgmstream; - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } From 26e464ef2c2836bf07c50fcc5c9bbc72b6de7a21 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 19 Jan 2019 20:03:48 +0100 Subject: [PATCH 02/10] Add Ubi .sb/smX layers and sequences, more games --- src/libvgmstream.vcproj | 4 + src/libvgmstream.vcxproj | 1 + src/libvgmstream.vcxproj.filters | 3 + src/meta/ubi_sb.c | 2760 +++++++++++++----------------- src/meta/ubi_sb_streamfile.h | 366 ++++ 5 files changed, 1599 insertions(+), 1535 deletions(-) create mode 100644 src/meta/ubi_sb_streamfile.h diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index 104fec5b..d3f27ba8 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -272,6 +272,10 @@ RelativePath=".\meta\sqex_scd_streamfile.h" > + + diff --git a/src/libvgmstream.vcxproj b/src/libvgmstream.vcxproj index 22c7e683..bca602da 100644 --- a/src/libvgmstream.vcxproj +++ b/src/libvgmstream.vcxproj @@ -110,6 +110,7 @@ + diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index dbceedc8..f7b49f4d 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -104,6 +104,9 @@ meta\Header Files + + meta\Header Files + meta\Header Files diff --git a/src/meta/ubi_sb.c b/src/meta/ubi_sb.c index 76d15615..d8df1551 100644 --- a/src/meta/ubi_sb.c +++ b/src/meta/ubi_sb.c @@ -1,15 +1,19 @@ #include "meta.h" +#include "../layout/layout.h" #include "../coding/coding.h" +#include "ubi_sb_streamfile.h" -typedef enum { UBI_ADPCM, RAW_PCM, RAW_PSX, RAW_DSP, RAW_XBOX, FMT_VAG, FMT_AT3, RAW_AT3, FMT_XMA1, RAW_XMA1, FMT_OGG, FMT_CWAV } ubi_sb_codec; +typedef enum { UBI_IMA, UBI_UNK, RAW_PCM, RAW_PSX, RAW_DSP, RAW_XBOX, FMT_VAG, FMT_AT3, RAW_AT3, FMT_XMA1, RAW_XMA1, FMT_OGG, FMT_CWAV } ubi_sb_codec; typedef enum { UBI_PC, UBI_PS2, UBI_XBOX, UBI_GC, UBI_X360, UBI_PSP, UBI_PS3, UBI_WII, UBI_3DS } ubi_sb_platform; +typedef enum { UBI_NONE = 0, UBI_AUDIO, UBI_LAYER, UBI_SEQUENCE } ubi_sb_type; + typedef struct { ubi_sb_platform platform; + int is_psp_old; int big_endian; int total_subsongs; - int is_external; - ubi_sb_codec codec; + int bank_subsongs; /* map base header info */ off_t map_start; @@ -20,21 +24,21 @@ typedef struct { uint32_t map_zero; off_t map_offset; off_t map_size; - char map_name[255]; + char map_name[0x24]; uint32_t map_unknown; /* SB info (some values are derived depending if it's standard sbX or map sbX) */ int is_map; uint32_t version; /* 16b+16b major+minor version */ uint32_t version_empty; /* map sbX versions are empty */ - /* events? (often share header_id/type with some descriptors, - * but may exists without headers or header exist without this) */ + /* events (often share header_id/type with some descriptors, + * but may exist without headers or header exist without them) */ size_t section1_num; size_t section1_offset; /* descriptors, audio header or other config types */ size_t section2_num; size_t section2_offset; - /* internal streams table (id and offset), referenced by each header */ + /* internal streams table, referenced by each header */ size_t section3_num; size_t section3_offset; /* section with sounds in some map versions */ @@ -46,95 +50,113 @@ typedef struct { /* unknown, usually -1 but can be others (0/1/2/etc) */ int flag1; int flag2; - /* where sound data starts, derived */ - size_t sounds_offset; - /* header/stream info config */ /* audio header varies slightly per game/version but not enough parse case by case, * instead we configure sizes and offsets to where each variable is */ - int map_version; /* represents map style (1=first, 2=mid, 3=latest) */ + int map_version; size_t section1_entry_size; size_t section2_entry_size; size_t section3_entry_size; size_t resource_name_size; - /* type 0x01 (sample) config */ - off_t cfga_stream_size; - off_t cfga_stream_offset; - off_t cfga_extra_offset; - off_t cfga_group_id; - off_t cfga_stream_type; - - off_t cfga_external_flag; /* stream is external */ - off_t cfga_loop_flag; /* stream loops */ - off_t cfga_num_samples; /* num_samples/loop start */ - off_t cfga_num_samples2; /* num_samples/loop end (if loop set) */ - off_t cfga_sample_rate; - off_t cfga_channels; - off_t cfga_stream_name; /* where the resource name is within the header */ - off_t cfga_extra_name; /* where the resource name is within sectionX */ - off_t cfga_xma_offset; - int and_external_flag; /* value for some flags can be int or bitflags */ - int and_loop_flag; - int and_group_id; - int shr_group_id; - int has_full_loop; /* loop flag means full loop */ - int has_short_channels; /* channels value can be 16b or 32b */ - int has_internal_names; /* resource name doubles as internal name in earlier games, or may contain garbage */ - /* type 0x05/0c (sequence?) config */ + /* type 0x01 (audio) config */ + off_t cfga_extra_offset; + off_t cfga_stream_size; + off_t cfga_stream_offset; + off_t cfga_stream_type; + off_t cfga_group_id; + off_t cfga_external_flag; + off_t cfga_loop_flag; + off_t cfga_num_samples; + off_t cfga_num_samples2; + off_t cfga_sample_rate; + off_t cfga_channels; + off_t cfga_stream_name; + off_t cfga_extra_name; + off_t cfga_xma_offset; + int cfga_and_external_flag; + int cfga_and_loop_flag; + int cfga_and_group_id; + int cfga_has_internal_names; + /* type 0x05/0c (sequence) config */ off_t cfgs_extra_offset; + off_t cfgs_intro_flag; + off_t cfgs_outro_flag; off_t cfgs_sequence_count; - /* type 0x06/0d (multilayer) config */ - //off_t cfgl_extra_offset; - + off_t cfgs_entry_number; + size_t sequence_entry_size; + int cfgs_and_intro_flag; + int cfgs_and_outro_flag; + /* type 0x06/0d (layer) config */ + off_t cfgl_extra_offset; + off_t cfgl_layer_count; + off_t cfgl_stream_size; + off_t cfgl_stream_offset; + off_t cfgl_stream_name; + off_t cfgl_extra_name; + off_t cfgl_sample_rate; + off_t cfgl_channels; + off_t cfgl_stream_type; + off_t cfgl_num_samples; + size_t layer_entry_size; /* header/stream info */ - uint32_t header_id; /* 16b+16b group+sound id identifier (unique within a sbX, but not smX) */ - uint32_t header_type; /* audio type (we only need 'standard audio' or 'layered audio') */ - size_t stream_size; /* size of the audio data */ - off_t stream_offset; /* offset within the data section (internal) or absolute (external) to the audio */ + ubi_sb_type type; /* unified type */ + ubi_sb_codec codec; /* unified codec */ + int header_index; /* entry number within section2 */ + off_t header_offset; /* entry offset within section2 */ + uint32_t header_id; /* 16b+16b group+sound identifier (unique within a sbX, but not smX), may start from 0 */ + uint32_t header_type; /* parsed type (we only need audio types) */ off_t extra_offset; /* offset within sectionX to extra data */ + off_t stream_offset; /* offset within the data section (internal) or absolute (external) to the audio */ + size_t stream_size; /* size of the audio data */ uint32_t stream_type; /* rough codec value */ - uint32_t group_id; /* internal id to reference in section3 */ - //int sequence_count; /* number of segments in a sequence type */ - int loop_flag; - int loop_start; /* loop starts that aren't 0 do exist but are very rare (ex. Beowulf PSP #33407) */ + int loop_flag; /* stream loops (normally internal sfx, but also external music) */ + int loop_start; /* usually 0 */ int num_samples; /* should match manually calculated samples */ int sample_rate; int channels; - char resource_name[255]; /* filename to the external stream, or internal stream info for some games */ - char readable_name[255]; /* constructed name to show externally */ + off_t xma_header_offset; /* some XMA have extra header stuff */ - int header_index; /* position within section2 (considering all possible header types) */ - off_t header_offset; /* offset of parsed audio header */ - off_t xma_header_offset; /* XMA has some extra header stuff*/ + int layer_count; /* number of layers in a layer type */ + int sequence_count; /* number of segments in a sequence type */ + int sequence_chain[64]; /* sequence of entry numbers */ + int has_intro; /* seems to mean something like this */ + int has_outro; /* some games use this flag for segments that join with others too */ - int types[16]; /* counts for each possible header types, for debugging */ + int is_external; /* stream is in a external file */ + char resource_name[0x24]; /* filename to the external stream, or internal stream info for some games */ + + char readable_name[255]; /* final subsong name */ + int types[16]; /* counts each header types, for debugging */ + int allowed_types[16]; } ubi_sb_header; -static VGMSTREAM * init_vgmstream_ubi_sb_main(ubi_sb_header *sb, STREAMFILE *streamFile); +static VGMSTREAM * init_vgmstream_ubi_sb_header(ubi_sb_header *sb, STREAMFILE* streamTest, STREAMFILE *streamFile); static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile, int target_subsong); +static int parse_header(ubi_sb_header * sb, STREAMFILE *streamFile, off_t offset, int index); static int config_sb_platform(ubi_sb_header * sb, STREAMFILE *streamFile); static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile); /* .SBx - banks from Ubisoft's DARE (Digital Audio Rendering Engine) engine games in ~2000-2008+ */ VGMSTREAM * init_vgmstream_ubi_sb(STREAMFILE *streamFile) { + VGMSTREAM* vgmstream = NULL; STREAMFILE *streamTest = NULL; int32_t(*read_32bit)(off_t, STREAMFILE*) = NULL; - ubi_sb_header sb = { 0 }; - int ok; + ubi_sb_header sb = {0}; int target_subsong = streamFile->stream_index; - /* check extension (number represents the platform, see later) */ + + /* checks (number represents the platform, see later) */ if (!check_extensions(streamFile, "sb0,sb1,sb2,sb3,sb4,sb5,sb6,sb7")) goto fail; /* .sbX (sound bank) is a small multisong format (loaded in memory?) that contains SFX data * but can also reference .ss0/ls0 (sound stream) external files for longer streams. - * A companion .sp0 (sound project) describes files and if it uses BANKs (.sb0) or MAPs (.sm0). */ + * A companion .sp0 (sound project) describes files and if it uses BANKs (.sbX) or MAPs (.smX). */ /* PLATFORM DETECTION */ @@ -146,15 +168,15 @@ VGMSTREAM * init_vgmstream_ubi_sb(STREAMFILE *streamFile) { read_32bit = read_32bitLE; } - if (target_subsong == 0) target_subsong = 1; + if (target_subsong <= 0) target_subsong = 1; - /* use smaller I/O buffer for performance, as this read lots of small headers all over the place */ + /* use smaller header buffer for performance */ streamTest = reopen_streamfile(streamFile, 0x100); if (!streamTest) goto fail; /* SB HEADER */ - /* SBx layout: base header, section1, section2, extra section, section3, data (all except base header can be null) */ + /* SBx layout: header, section1, section2, extra section, section3, data (all except header can be null) */ sb.is_map = 0; sb.version = read_32bit(0x00, streamFile); sb.section1_num = read_32bit(0x04, streamFile); @@ -164,22 +186,21 @@ VGMSTREAM * init_vgmstream_ubi_sb(STREAMFILE *streamFile) { sb.flag1 = read_32bit(0x14, streamFile); sb.flag2 = read_32bit(0x18, streamFile); - ok = config_sb_version(&sb, streamFile); - if (!ok) goto fail; + if (!config_sb_version(&sb, streamFile)) + goto fail; sb.section1_offset = 0x1c; sb.section2_offset = sb.section1_offset + sb.section1_entry_size * sb.section1_num; sb.sectionX_offset = sb.section2_offset + sb.section2_entry_size * sb.section2_num; sb.section3_offset = sb.sectionX_offset + sb.sectionX_size; - sb.sounds_offset = sb.section3_offset + sb.section3_entry_size * sb.section3_num; if (!parse_sb_header(&sb, streamTest, target_subsong)) goto fail; - close_streamfile(streamTest); - /* CREATE VGMSTREAM */ - return init_vgmstream_ubi_sb_main(&sb, streamFile); + vgmstream = init_vgmstream_ubi_sb_header(&sb, streamTest, streamFile); + close_streamfile(streamTest); + return vgmstream; fail: close_streamfile(streamTest); @@ -188,20 +209,20 @@ fail: /* .SMx - maps (sets of custom SBx files) also from Ubisoft's sound engine games in ~2000-2008+ */ VGMSTREAM * init_vgmstream_ubi_sm(STREAMFILE *streamFile) { + VGMSTREAM* vgmstream = NULL; STREAMFILE *streamTest = NULL; int32_t(*read_32bit)(off_t, STREAMFILE*) = NULL; - //int16_t(*read_16bit)(off_t, STREAMFILE*) = NULL; - ubi_sb_header sb = { 0 }; - int ok, i; + ubi_sb_header sb = {0}, target_sb = {0}; int target_subsong = streamFile->stream_index; + int i; - /* check extension (number represents the platform, see later) */ + /* checks (number represents platform, lmX are localized variations) */ if (!check_extensions(streamFile, "sm0,sm1,sm2,sm3,sm4,sm5,sm6,sm7,lm0,lm1,lm2,lm3,lm4,lm5,lm6,lm7")) goto fail; /* .smX (sound map) is a set of slightly different sbX files, compiled into one "map" file. - * Map has a sbX per named area (example: menu, level1, boss1, level2...). + * Map has a sbX (called "submap") per named area (example: menu, level1, boss1, level2...). * This counts subsongs from all sbX, so totals can be massive, but there are splitters into mini-smX. */ @@ -214,39 +235,40 @@ VGMSTREAM * init_vgmstream_ubi_sm(STREAMFILE *streamFile) { read_32bit = read_32bitLE; } - if (target_subsong == 0) target_subsong = 1; + if (target_subsong <= 0) target_subsong = 1; - /* use smaller I/O buffer for performance, as this read lots of small headers all over the place */ + /* use smaller header buffer for performance */ streamTest = reopen_streamfile(streamFile, 0x100); if (!streamTest) goto fail; /* SM BASE HEADER */ - /* SMx layout: base header with N map area offset/sizes (some? offsets within a SBx are relative) */ - /* SBx layout: base header, section1, section2, section4, extra section, section3, data (all except base header can be null?) */ + /* SMx layout: header with N map area offset/sizes + custom SBx with relative offsets */ sb.is_map = 1; sb.version = read_32bit(0x00, streamFile); sb.map_start = read_32bit(0x04, streamFile); sb.map_num = read_32bit(0x08, streamFile); - ok = config_sb_version(&sb, streamFile); - if (!ok || sb.map_version == 0) goto fail; + if (!config_sb_version(&sb, streamFile)) + goto fail; - sb.map_entry_size = (sb.map_version < 2) ? 0x30 : 0x34; for (i = 0; i < sb.map_num; i++) { off_t offset = sb.map_start + i * sb.map_entry_size; - /* SM AREA HEADER */ + sb.type = UBI_NONE; /* reset possible parsed subsong */ + + /* SUBMAP HEADER */ sb.map_type = read_32bit(offset + 0x00, streamFile); /* usually 0/1=first, 0=rest */ sb.map_zero = read_32bit(offset + 0x04, streamFile); sb.map_offset = read_32bit(offset + 0x08, streamFile); sb.map_size = read_32bit(offset + 0x0c, streamFile); /* includes sbX header, but not internal streams */ - read_string(sb.map_name, 0x20+1, offset + 0x10, streamFile); /* null-terminated and may contain garbage after null */ + read_string(sb.map_name, sizeof(sb.map_name), offset + 0x10, streamFile); /* null-terminated and may contain garbage after null */ if (sb.map_version >= 3) sb.map_unknown = read_32bit(offset + 0x30, streamFile); /* uncommon, id/config? longer name? mem garbage? */ /* SB HEADER */ + /* SBx layout: base header, section1, section2, section4, extra section, section3, data (all except header can be null?) */ sb.version_empty = read_32bit(sb.map_offset + 0x00, streamFile); /* sbX in maps don't set version */ sb.section1_offset = read_32bit(sb.map_offset + 0x04, streamFile) + sb.map_offset; sb.section1_num = read_32bit(sb.map_offset + 0x08, streamFile); @@ -271,58 +293,41 @@ VGMSTREAM * init_vgmstream_ubi_sm(STREAMFILE *streamFile) { sb.sectionX_offset += sb.section4_offset; /* for some reason, this is relative to section 4 here */ } - VGM_ASSERT(sb.map_type != 0 && sb.map_type != 1, "UBI SM: unknown map_type %x\n", (uint32_t)offset); - VGM_ASSERT(sb.map_zero != 0, "UBI SM: unknown map_zero %x\n", (uint32_t)offset); + VGM_ASSERT(sb.map_type != 0 && sb.map_type != 1, "UBI SM: unknown map_type at %x\n", (uint32_t)offset); + VGM_ASSERT(sb.map_zero != 0, "UBI SM: unknown map_zero at %x\n", (uint32_t)offset); //;VGM_ASSERT(sb.map_unknown != 0, "UBI SM: unknown map_unknown at %x\n", (uint32_t)offset); - VGM_ASSERT(sb.version_empty != 0, "UBI SM: unknown version_empty %x\n", (uint32_t)offset); + VGM_ASSERT(sb.version_empty != 0, "UBI SM: unknown version_empty at %x\n", (uint32_t)offset); if (!parse_sb_header(&sb, streamTest, target_subsong)) goto fail; + + /* snapshot of current sb if subsong was found + * (it gets rewritten and we need exact values for sequences and stuff) */ + if (sb.type != UBI_NONE) { + target_sb = sb; /* memcpy */ + } } - if (sb.total_subsongs == 0) { - VGM_LOG("UBI SB: no subsongs\n"); - goto fail; - } - - if (target_subsong < 0 || target_subsong > sb.total_subsongs) { - goto fail; - } - - close_streamfile(streamTest); + target_sb.total_subsongs = sb.total_subsongs; /* CREATE VGMSTREAM */ - return init_vgmstream_ubi_sb_main(&sb, streamFile); + vgmstream = init_vgmstream_ubi_sb_header(&target_sb, streamTest, streamFile); + close_streamfile(streamTest); + return vgmstream; fail: close_streamfile(streamTest); return NULL; } -static VGMSTREAM * init_vgmstream_ubi_sb_main(ubi_sb_header *sb, STREAMFILE *streamFile) { +/* ************************************************************************* */ + +static VGMSTREAM * init_vgmstream_ubi_sb_base(ubi_sb_header *sb, STREAMFILE *streamHead, STREAMFILE *streamData, off_t start_offset) { VGMSTREAM * vgmstream = NULL; - STREAMFILE *streamData = NULL; - off_t start_offset; - - - /* open external stream if needed */ - if (sb->is_external) { - streamData = open_streamfile_by_filename(streamFile,sb->resource_name); - if (!streamData) { - VGM_LOG("UBI SB: external stream '%s' not found\n", sb->resource_name); - goto fail; - } - } - else { - streamData = streamFile; - } - ;VGM_LOG("UBI SB: stream offset=%x, size=%x, external=%i\n", (uint32_t)sb->stream_offset, sb->stream_size, sb->is_external); - - start_offset = sb->stream_offset; /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(sb->channels,sb->loop_flag); + vgmstream = allocate_vgmstream(sb->channels, sb->loop_flag); if (!vgmstream) goto fail; vgmstream->meta_type = meta_UBI_SB; @@ -335,11 +340,17 @@ static VGMSTREAM * init_vgmstream_ubi_sb_main(ubi_sb_header *sb, STREAMFILE *str vgmstream->loop_end_sample = sb->num_samples; switch(sb->codec) { - case UBI_ADPCM: + case UBI_IMA: vgmstream->coding_type = coding_UBI_IMA; vgmstream->layout_type = layout_none; break; + case UBI_UNK: + // todo some kind of blocked layout + Ubi 4/6-bit ADPCM? + // used in Splinter Cell and some Myst IV banks (ex. puzzle_si_splintercell.sb2) + VGM_LOG("UBI SB: unsupported Ubi ADPCM found\n"); + goto fail; + case RAW_PCM: vgmstream->coding_type = coding_PCM16LE; /* always LE even on Wii */ vgmstream->layout_type = layout_interleave; @@ -364,10 +375,10 @@ static VGMSTREAM * init_vgmstream_ubi_sb_main(ubi_sb_header *sb, STREAMFILE *str case RAW_DSP: vgmstream->coding_type = coding_NGC_DSP; vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = align_size_to_block(sb->stream_size / sb->channels, 0x04); + vgmstream->interleave_block_size = align_size_to_block(sb->stream_size / sb->channels, 0x08); /* frame-aligned */ /* DSP extra info entry size is 0x40 (first/last 0x10 = unknown), per channel */ - dsp_read_coefs_be(vgmstream,streamFile,sb->extra_offset + 0x10, 0x40); + dsp_read_coefs_be(vgmstream,streamHead,sb->extra_offset + 0x10, 0x40); break; case FMT_VAG: @@ -386,7 +397,7 @@ static VGMSTREAM * init_vgmstream_ubi_sb_main(ubi_sb_header *sb, STREAMFILE *str case FMT_AT3: { ffmpeg_codec_data *ffmpeg_data; - /* skip weird value (3, 4) in Brothers in Arms: D-Day (PSP) */ + /* skip weird value (3, 4) in Brothers in Arms: D-Day (PSP) */ //todo remove if (read_32bitBE(start_offset+0x04,streamData) == 0x52494646) { VGM_LOG("UBI SB: skipping unknown value 0x%x before RIFF\n", read_32bitBE(start_offset+0x00,streamData)); start_offset += 0x04; @@ -421,6 +432,7 @@ static VGMSTREAM * init_vgmstream_ubi_sb_main(ubi_sb_header *sb, STREAMFILE *str break; } + //todo: some XMA1 decode a bit strangely at certain positions (FFmpeg bug?) case FMT_XMA1: { ffmpeg_codec_data *ffmpeg_data; uint8_t buf[0x100]; @@ -438,15 +450,16 @@ static VGMSTREAM * init_vgmstream_ubi_sb_main(ubi_sb_header *sb, STREAMFILE *str sec1_num = read_32bitBE(header_offset + 0x28, streamData); sec3_num = read_32bitBE(header_offset + 0x2c, streamData); num_frames = sec2_num; - bits_per_frame = 4; - if (flag == 0x02 || flag == 0x04) { + bits_per_frame = 4; + if (flag == 0x02 || flag == 0x04) bits_per_frame = 2; - } + else if (flag == 0x08) + bits_per_frame = 1; header_size = 0x30; header_size += sec1_num * 0x04; - header_size += align_size_to_block(sec2_num * bits_per_frame, 32) / 8; /* bitstream with 4 or 2 bits for each frame */ + header_size += align_size_to_block(sec2_num * bits_per_frame, 32) / 8; /* bitstream seek table? */ header_size += sec3_num * 0x08; start_offset += header_size; data_size = num_frames * 0x800; @@ -458,7 +471,8 @@ static VGMSTREAM * init_vgmstream_ubi_sb_main(ubi_sb_header *sb, STREAMFILE *str vgmstream->codec_data = ffmpeg_data; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - vgmstream->stream_size = data_size; + + xma_fix_raw_samples_ch(vgmstream, streamData, start_offset, data_size, sb->channels, 0, 0); break; } @@ -473,13 +487,15 @@ static VGMSTREAM * init_vgmstream_ubi_sb_main(ubi_sb_header *sb, STREAMFILE *str /* get XMA header from extra section */ chunk_size = 0x20; header_offset = sb->xma_header_offset; - bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf, 0x100, header_offset, chunk_size, sb->stream_size, streamFile, 1); + bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf, 0x100, header_offset, chunk_size, sb->stream_size, streamHead, 1); ffmpeg_data = init_ffmpeg_header_offset(streamData, buf, bytes, start_offset, sb->stream_size); if (!ffmpeg_data) goto fail; vgmstream->codec_data = ffmpeg_data; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; + + xma_fix_raw_samples_ch(vgmstream, streamData, start_offset, sb->stream_size, sb->channels, 0, 0); break; } @@ -495,12 +511,12 @@ static VGMSTREAM * init_vgmstream_ubi_sb_main(ubi_sb_header *sb, STREAMFILE *str } #endif case FMT_CWAV: - if (sb->channels > 1) goto fail; //todo test + if (sb->channels > 1) goto fail; /* unknown layout */ vgmstream->coding_type = coding_NGC_DSP; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x08; - dsp_read_coefs_le(vgmstream,streamFile,start_offset + 0x7c, 0x40); + dsp_read_coefs_le(vgmstream,streamData,start_offset + 0x7c, 0x40); start_offset += 0xe0; /* skip CWAV header */ break; @@ -509,11 +525,37 @@ static VGMSTREAM * init_vgmstream_ubi_sb_main(ubi_sb_header *sb, STREAMFILE *str goto fail; } - strcpy(vgmstream->stream_name, sb->readable_name); - /* open the actual for decoding (streamData can be an internal or external stream) */ if ( !vgmstream_open_stream(vgmstream, streamData, start_offset) ) goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + +static VGMSTREAM * init_vgmstream_ubi_sb_audio(ubi_sb_header *sb, STREAMFILE *streamTest, STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + STREAMFILE *streamData = NULL; + + /* open external stream if needed */ + if (sb->is_external) { + streamData = open_streamfile_by_filename(streamFile,sb->resource_name); + if (streamData == NULL) { + VGM_LOG("UBI SB: external stream '%s' not found\n", sb->resource_name); + goto fail; + } + } + else { + streamData = streamFile; + } + + + /* init actual VGMSTREAM */ + vgmstream = init_vgmstream_ubi_sb_base(sb, streamTest, streamData, sb->stream_offset); + if (!vgmstream) goto fail; + if (sb->is_external && streamData) close_streamfile(streamData); return vgmstream; @@ -524,8 +566,184 @@ fail: return NULL; } +static VGMSTREAM * init_vgmstream_ubi_sb_layer(ubi_sb_header *sb, STREAMFILE *streamTest, STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + layered_layout_data* data = NULL; + STREAMFILE* temp_streamFile = NULL; + STREAMFILE *streamData = NULL; + int i; -static void build_readable_name(ubi_sb_header * sb, int bank_streams) { + /* open external stream if needed */ + if (sb->is_external) { + streamData = open_streamfile_by_filename(streamFile,sb->resource_name); + if (streamData == NULL) { + VGM_LOG("UBI SB: external stream '%s' not found\n", sb->resource_name); + goto fail; + } + } + else { + streamData = streamFile; + } + + /* init layout */ + data = init_layout_layered(sb->layer_count); + if (!data) goto fail; + + /* open all layers and mix */ + for (i = 0; i < sb->layer_count; i++) { + /* prepare streamfile from a single layer section */ + temp_streamFile = setup_ubi_sb_streamfile(streamData, sb->stream_offset, sb->stream_size, i, sb->layer_count, sb->big_endian); + if (!temp_streamFile) goto fail; + + /* build the layer VGMSTREAM (standard sb with custom streamfile) */ + data->layers[i] = init_vgmstream_ubi_sb_base(sb, streamTest, temp_streamFile, 0x00); + if (!data->layers[i]) goto fail; + + close_streamfile(temp_streamFile); + } + + if (!setup_layout_layered(data)) + goto fail; + + /* build the base VGMSTREAM */ + vgmstream = allocate_vgmstream(sb->channels * sb->layer_count, sb->loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_UBI_SB; + vgmstream->sample_rate = sb->sample_rate; + vgmstream->num_streams = sb->total_subsongs; + vgmstream->stream_size = sb->stream_size; + + vgmstream->num_samples = sb->num_samples; + vgmstream->loop_start_sample = sb->loop_start; + vgmstream->loop_end_sample = sb->num_samples; + + vgmstream->coding_type = data->layers[0]->coding_type; + vgmstream->layout_type = layout_layered; + vgmstream->layout_data = data; + + + if (sb->is_external && streamData) close_streamfile(streamData); + + return vgmstream; +fail: + close_streamfile(temp_streamFile); + if (sb->is_external && streamData) close_streamfile(streamData); + if (vgmstream) + close_vgmstream(vgmstream); + else + free_layout_layered(data); + return NULL; +} + +static VGMSTREAM * init_vgmstream_ubi_sb_sequence(ubi_sb_header *sb, STREAMFILE *streamTest, STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + segmented_layout_data* data = NULL; + int i; + + /* init layout */ + data = init_layout_segmented(sb->sequence_count); + if (!data) goto fail; + + sb->num_samples = 0; + + /* open all segments and mix */ + for (i = 0; i < sb->sequence_count; i++) { + ubi_sb_header temp_sb = *sb; /* memcpy'ed */ + int entry_index = sb->sequence_chain[i]; + off_t entry_offset = sb->section2_offset + sb->section2_entry_size * entry_index; + + ;VGM_LOG("UBI SB: index=%x, offset=%lx\n", entry_index, entry_offset); + + /* parse expected entry */ + if (!parse_header(&temp_sb, streamTest, entry_offset, entry_index)) + goto fail; + + if (temp_sb.type != UBI_AUDIO && temp_sb.type != UBI_LAYER) { + VGM_LOG("UBI SB: unexpected sequence entry type at %x\n", (uint32_t)entry_offset); + goto fail; /* technically ok but too much recursiveness? */ + } + + /* build the layer VGMSTREAM (current sb entry config) */ + data->segments[i] = init_vgmstream_ubi_sb_header(&temp_sb, streamTest, streamFile); + if (!data->segments[i]) goto fail; + + sb->num_samples += data->segments[i]->num_samples; + } + + if (!setup_layout_segmented(data)) + goto fail; + + /* build the base VGMSTREAM */ + vgmstream = allocate_vgmstream(data->segments[0]->channels, data->segments[0]->loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_UBI_SB; + vgmstream->sample_rate = data->segments[0]->sample_rate; + vgmstream->num_streams = sb->total_subsongs; + //vgmstream->stream_size = sb->stream_size; /* auto when getting avg br */ + + vgmstream->num_samples = sb->num_samples; + vgmstream->loop_start_sample = sb->loop_start; + vgmstream->loop_end_sample = sb->num_samples; + + vgmstream->coding_type = data->segments[0]->coding_type; + vgmstream->layout_type = layout_segmented; + vgmstream->layout_data = data; + + return vgmstream; +fail: + if (vgmstream) + close_vgmstream(vgmstream); + else + free_layout_segmented(data); + return NULL; +} + +static VGMSTREAM * init_vgmstream_ubi_sb_header(ubi_sb_header *sb, STREAMFILE* streamTest, STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + + if (sb->total_subsongs == 0) { + VGM_LOG("UBI SB: no subsongs\n"); + goto fail; + } + + ;VGM_LOG("UBI SB: target at %x + %x, extra=%x, name=%s, id=%i, t=%i\n", + (uint32_t)sb->header_offset, sb->section2_entry_size, (uint32_t)sb->extra_offset, sb->resource_name, sb->group_id, sb->stream_type); + + ;VGM_LOG("UBI SB: stream offset=%x, size=%x, name=%s\n", (uint32_t)sb->stream_offset, sb->stream_size, sb->is_external ? sb->resource_name : "internal" ); + + switch(sb->type) { + case UBI_AUDIO: + vgmstream = init_vgmstream_ubi_sb_audio(sb, streamTest, streamFile); + break; + + case UBI_LAYER: + vgmstream = init_vgmstream_ubi_sb_layer(sb, streamTest, streamFile); + break; + + case UBI_SEQUENCE: + vgmstream = init_vgmstream_ubi_sb_sequence(sb, streamTest, streamFile); + break; + + case UBI_NONE: + default: + VGM_LOG("UBI SB: subsong not found/parsed\n"); + goto fail; + } + + if (!vgmstream) goto fail; + + strcpy(vgmstream->stream_name, sb->readable_name); + return vgmstream; +fail: + close_vgmstream(vgmstream); + return NULL; +} + +/* ************************************************************************* */ + +static void build_readable_name(char * buf, size_t buf_size, ubi_sb_header * sb) { const char *grp_name; const char *res_name; uint32_t id; @@ -540,82 +758,111 @@ static void build_readable_name(ubi_sb_header * sb, int bank_streams) { id = sb->header_id; type = sb->header_type; if (sb->is_map) - index = sb->header_index; //bank_streams; + index = sb->header_index; //sb->bank_subsongs; else index = sb->header_index; //-1 - res_name = sb->resource_name; + + if (sb->type == UBI_SEQUENCE) { + if (sb->has_intro && sb->has_outro) /* uncommon but exists */ + res_name = sb->sequence_count == 1 ? "start+end" : "start+loop+end"; + else if (sb->has_intro) + res_name = sb->sequence_count == 1 ? "start" : "start+loop"; + else if (sb->has_outro) + res_name = sb->sequence_count == 1 ? "end" : "loop+end"; + else + res_name = "loop"; + } + else { + if (sb->is_external || sb->cfga_has_internal_names) + res_name = sb->resource_name; + else + res_name = NULL; + } + /* create name */ if (grp_name) { - if ((sb->is_external || sb->has_internal_names) && res_name[0]) { + if (res_name && res_name[0]) { if (index >= 0) - snprintf(sb->readable_name, sizeof(sb->readable_name), "%s/%04d/%02x-%08x/%s", grp_name, index, type, id, res_name); + snprintf(buf,buf_size, "%s/%04d/%02x-%08x/%s", grp_name, index, type, id, res_name); else - snprintf(sb->readable_name, sizeof(sb->readable_name), "%s/%02x-%08x/%s", grp_name, type, id, res_name); + snprintf(buf,buf_size, "%s/%02x-%08x/%s", grp_name, type, id, res_name); } else { if (index >= 0) - snprintf(sb->readable_name, sizeof(sb->readable_name), "%s/%04d/%02x-%08x", grp_name, index, type, id); + snprintf(buf,buf_size, "%s/%04d/%02x-%08x", grp_name, index, type, id); else - snprintf(sb->readable_name, sizeof(sb->readable_name), "%s/%02x-%08x", grp_name, type, id); + snprintf(buf,buf_size, "%s/%02x-%08x", grp_name, type, id); } } else { - if ((sb->is_external || sb->has_internal_names) && res_name[0]) { + if (res_name && res_name[0]) { if (index >= 0) - snprintf(sb->readable_name, sizeof(sb->readable_name), "%04d/%02x-%08x/%s", index, type, id, res_name); + snprintf(buf,buf_size, "%04d/%02x-%08x/%s", index, type, id, res_name); else - snprintf(sb->readable_name, sizeof(sb->readable_name), "%02x-%08x/%s", type, id, res_name); + snprintf(buf,buf_size, "%02x-%08x/%s", type, id, res_name); } else { if (index >= 0) - snprintf(sb->readable_name, sizeof(sb->readable_name), "%04d/%02x-%08x", index, type, id); + snprintf(buf,buf_size, "%04d/%02x-%08x", index, type, id); else - snprintf(sb->readable_name, sizeof(sb->readable_name), "%02x-%08x", type, id); + snprintf(buf,buf_size, "%02x-%08x", type, id); } } } -static int parse_header_type_audio(ubi_sb_header * sb, off_t offset, STREAMFILE* streamFile) { +static int parse_type_audio(ubi_sb_header * sb, off_t offset, STREAMFILE* streamFile) { int32_t (*read_32bit)(off_t,STREAMFILE*) = sb->big_endian ? read_32bitBE : read_32bitLE; int16_t (*read_16bit)(off_t,STREAMFILE*) = sb->big_endian ? read_16bitBE : read_16bitLE; - /* Single audio header, external or internal. Can be part of a sequence or separate - * (some games don't use sequences at all). */ + /* audio header */ + sb->type = UBI_AUDIO; - sb->stream_size = read_32bit(offset + sb->cfga_stream_size, streamFile); sb->extra_offset = read_32bit(offset + sb->cfga_extra_offset, streamFile) + sb->sectionX_offset; + sb->stream_size = read_32bit(offset + sb->cfga_stream_size, streamFile); sb->stream_offset = read_32bit(offset + sb->cfga_stream_offset, streamFile); - sb->channels = (sb->has_short_channels) ? + sb->channels = (sb->cfga_channels % 4) ? /* non-aligned offset is always 16b */ (uint16_t)read_16bit(offset + sb->cfga_channels, streamFile) : (uint32_t)read_32bit(offset + sb->cfga_channels, streamFile); sb->sample_rate = read_32bit(offset + sb->cfga_sample_rate, streamFile); sb->stream_type = read_32bit(offset + sb->cfga_stream_type, streamFile); + if (sb->stream_size == 0) { + VGM_LOG("UBI SB: bad stream size\n"); + goto fail; + } + if (sb->cfga_loop_flag) { - sb->loop_flag = (read_32bit(offset + sb->cfga_loop_flag, streamFile) & sb->and_loop_flag); + sb->loop_flag = (read_32bit(offset + sb->cfga_loop_flag, streamFile) & sb->cfga_and_loop_flag); } if (sb->loop_flag) { sb->loop_start = read_32bit(offset + sb->cfga_num_samples, streamFile); sb->num_samples = read_32bit(offset + sb->cfga_num_samples2, streamFile) + sb->loop_start; - if (sb->has_full_loop) { /* early games just repeat and don't set loop start */ + if (sb->cfga_num_samples == sb->cfga_num_samples2) { /* early games just repeat and don't set loop start */ sb->num_samples = sb->loop_start; sb->loop_start = 0; } - /* loop starts that aren't 0 do exist but are very rare (ex. Beowulf PSP #33407) - * also rare are looping external streams (ex. Surf's Up PSP #1462) */ + /* loop starts that aren't 0 do exist but are very rare (ex. Beowulf PSP sb 82, index 575) */ } else { sb->num_samples = read_32bit(offset + sb->cfga_num_samples, streamFile); } if (sb->cfga_group_id) { sb->group_id = read_32bit(offset + sb->cfga_group_id, streamFile); - if (sb->and_group_id) sb->group_id &= sb->and_group_id; - if (sb->shr_group_id) sb->group_id >>= sb->shr_group_id; + if (sb->cfga_and_group_id) sb->group_id &= sb->cfga_and_group_id; + + /* normalize bitflag, known groups are only id 0/1 (if needed could calculate + * (shift-right value here, based on cfga_and_group_id first 1-bit) */ + if (sb->group_id > 1) + sb->group_id = 1; } if (sb->cfga_external_flag) { - sb->is_external = (read_32bit(offset + sb->cfga_external_flag, streamFile) & sb->and_external_flag); + sb->is_external = (read_32bit(offset + sb->cfga_external_flag, streamFile) & sb->cfga_and_external_flag); + } + + if (sb->resource_name_size > sizeof(sb->resource_name)) { + goto fail; } /* external stream name can be found in the header (first versions) or the sectionX table (later versions) */ @@ -623,212 +870,146 @@ static int parse_header_type_audio(ubi_sb_header * sb, off_t offset, STREAMFILE* read_string(sb->resource_name, sb->resource_name_size, offset + sb->cfga_stream_name, streamFile); } else { sb->cfga_stream_name = read_32bit(offset + sb->cfga_extra_name, streamFile); - if (sb->cfga_stream_name != 0xFFFFFFFF) + if (sb->cfgl_stream_name != 0xFFFFFFFF) read_string(sb->resource_name, sb->resource_name_size, sb->sectionX_offset + sb->cfga_stream_name, streamFile); } - return 1; -//fail: -// return 0; -} - -static int parse_header_type_sequence(ubi_sb_header * sb, off_t offset, STREAMFILE* streamFile) { -// int32_t (*read_32bit)(off_t,STREAMFILE*) = sb->big_endian ? read_32bitBE : read_32bitLE; - - /* A "sequence" that includes N audio segments, defined as a chain of section2 entries. - * - * They don't include lead-in/outs and look loopable. Several sequences can reuse - * audio segments (variations of the same songs), or can be single entries (pointing - * to a full song or a lead-out). - * - * Sequences seem to include only music or dialogues, so even single entries may be useful to parse. */ - - /* - rough format: */ - /* extra table offset (references id?) at ~0x0c */ - /* flags? */ - /* id? possibly related to sequence lead-out? */ - /* id? possibly related to sequence lead-in? */ - /* sequence count at ~0x28/2c */ - - /* - in the extra table, per sequence count: - * 0x00: section2 entry number (points to audio types) - * 0x04+ size varies (0x10-0x14 are common) - * at the end: some kind of ID? - */ - -#if 0 - if (!sb->cfgs_sequence_count) { - VGM_LOG("UBI SB: segment found but not configured at %lx\n", offset); - goto fail; + /* points at XMA1 header in the extra section (only for RAW_XMA1, garbage ignored otherwise) */ + if (sb->cfga_xma_offset) { + sb->xma_header_offset = read_32bit(offset + sb->cfga_xma_offset, streamFile) + sb->sectionX_offset; } return 1; fail: -#endif return 0; } -static int parse_header_type_layer(ubi_sb_header * sb, off_t offset, STREAMFILE* streamFile) { -// int32_t (*read_32bit)(off_t,STREAMFILE*) = sb->big_endian ? read_32bitBE : read_32bitLE; -// int16_t (*read_16bit)(off_t,STREAMFILE*) = sb->big_endian ? read_16bitBE : read_16bitLE; - -// goto fail; - - /* some values may be flags/config as multiple 0x06 can point to the same layer, with different 'flags'? */ - - return 1; -//fail: -// return 0; -} - - -static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile, int target_subsong) { +static int parse_type_sequence(ubi_sb_header * sb, off_t offset, STREAMFILE* streamFile) { int32_t (*read_32bit)(off_t,STREAMFILE*) = sb->big_endian ? read_32bitBE : read_32bitLE; - int i, j, k, bank_streams = 0, prev_streams; + off_t table_offset; + int i; + /* sequence chain */ + sb->type = UBI_SEQUENCE; - ;VGM_LOG("UBI SB: s1=%x (%x*%x), s2=%x (%x*%x), s3=%x (%x*%x), sX=%x (%x)\n", - sb->section1_offset,sb->section1_entry_size,sb->section1_num,sb->section2_offset,sb->section2_entry_size,sb->section2_num, - sb->section3_offset,sb->section3_entry_size,sb->section3_num,sb->sectionX_offset,sb->sectionX_size); + sb->extra_offset = read_32bit(offset + sb->cfgs_extra_offset, streamFile) + sb->sectionX_offset; + sb->has_intro = read_32bit(offset + sb->cfgs_intro_flag, streamFile) & sb->cfgs_and_intro_flag; + sb->has_outro = read_32bit(offset + sb->cfgs_outro_flag, streamFile) & sb->cfgs_and_outro_flag; + sb->sequence_count = read_32bit(offset + sb->cfgs_sequence_count, streamFile); - prev_streams = sb->total_subsongs; - - /* find target stream info in section2 */ - for (i = 0; i < sb->section2_num; i++) { - off_t offset = sb->section2_offset + sb->section2_entry_size*i; - uint32_t header_id, header_type; - - /* parse base header (possibly called "resource" or "object") */ - header_id = read_32bit(offset + 0x00, streamFile); - header_type = read_32bit(offset + 0x04, streamFile); - - if (header_type <= 0x00 || header_type >= 0x10 || header_type == 0x09 || header_type == 0x0b) { - VGM_LOG("UBI SB: unknown type %x at %x size %x\n", header_type, (uint32_t)offset, sb->section2_entry_size); - goto fail; - } - - //;VGM_ASSERT(header_type == 0x06 || header_type == 0x0d, - // "UBI SB: type %x at %x size %x\n", header_type, (uint32_t)offset, sb->section2_entry_size); - - sb->types[header_type]++; - - /* ignore non-audio entries */ - if (header_type != 0x01) - continue; - - /* update streams (total_stream also doubles as current) */ - bank_streams++; - sb->total_subsongs++; - if (sb->total_subsongs != target_subsong) - continue; - - /* parse target entry */ - sb->header_index = i; - sb->header_offset = offset; - - sb->header_id = header_id; - sb->header_type = header_type; - - switch(header_type) { - case 0x01: /* old and new */ - if (!parse_header_type_audio(sb, offset, streamFile)) - goto fail; - break; - case 0x02: - /* A group, possibly to play with config. (ex: 0x08 (float 0.3) + 0x01) */ - goto fail; - case 0x03: - //case 0x09? - /* A group, other way to play things? (ex: 0x03 + 0x04) */ - goto fail; - case 0x04: /* newer/older */ - case 0x0a: /* older */ - /* A group of N audio/sequences, seemingly 'random' type to play one in the group - * (usually includes voice/sfx like death screams, but may include sequences). - * Header is similar to sequences (count in header, points to extra table's N entries in section2) */ - goto fail; - case 0x05: /* newer */ - case 0x0c: /* older */ - if (!parse_header_type_sequence(sb, offset, streamFile)) - goto fail; - break; - case 0x06: /* newer */ - case 0x0d: /* older */ - if (!parse_header_type_layer(sb, offset, streamFile)) - goto fail; - break; - case 0x07: - //case 0x0e? - /* Another group of something (single entry?), rare. */ - goto fail; - case 0x08: /* newer (also in older with 0x0f) */ - case 0x0f: /* older */ - /* Audio config? (almost all fields 0 except sometimes 1.0 float in the middle). - * In older games may also point to the extra table and look different, maybe equivalent to another type. */ - goto fail; - - default: - /* debug strings reference: - * - TYPE_SAMPLE: should be 0x01 (also "sound resource") - * - TYPE_MULTITRACK: should be 0x06/0x0d (also "multilayer resource") - * - TYPE_SILENCE: ? - * sequences may be "theme resource" - * - * possible type names from .bnm (.sb's predecessor): - * 0: TYPE_INVALID - * 1: TYPE_SAMPLE - * 2: TYPE_MIDI - * 3: TYPE_CDAUDIO - * 4: TYPE_SEQUENCE - * 5: TYPE_SWITCH_OLD - * 6: TYPE_SPLIT - * 7: TYPE_THEME_OLD - * 8: TYPE_SWITCH - * 9: TYPE_THEME_OLD2 - * A: TYPE_RANDOM - * B: TYPE_THEME0 - */ - - /* All types may contain memory garbage, making it harder to identify fields (platforms - * and games are affected differently by this). Often types contain memory from the previous - * type header unless overwritten, random memory, or default initialization garbage. - * So if some non-audio type looks like audio it's probably repeating old data. - * This even happens for common fields (ex. type 06 at 0x08 has prev garbage, not stream size). */ - goto fail; - } - - - /* maps can contain +10000 subsongs, we need something helpful */ - build_readable_name(sb, bank_streams); + if (sb->sequence_entry_size == 0) { + VGM_LOG("Ubi SB: sequence entry size not configured at %x\n", (uint32_t)offset); + goto fail; } - - ;VGM_LOG("UBI SB: types "); for (int i = 0; i < 16; i++) { VGM_ASSERT(sb->types[i], "%02x=%i ",i,sb->types[i]); } VGM_LOG("\n"); - - - if (sb->is_map) { - if (bank_streams == 0 || target_subsong <= prev_streams || target_subsong > sb->total_subsongs) - return 1; /* Target stream is not in this map */ - } else { - if (sb->total_subsongs == 0) { - VGM_LOG("UBI SB: no subsongs\n"); - goto fail; - } - - if (target_subsong < 0 || target_subsong > sb->total_subsongs) { - goto fail; - } - - VGM_ASSERT(sb->section3_num > 2, "UBI SB: section3 > 2 found\n"); - } - - if (!(sb->cfga_group_id || sb->is_map) && sb->section3_num > 1) { - VGM_LOG("UBI SB: unexpected number of internal stream groups %i\n", sb->section3_num); + if (sb->sequence_count > sizeof(sb->sequence_chain)) { /* arbitrary max */ + VGM_LOG("Ubi SB: incorrect layer count\n"); goto fail; } - ;VGM_LOG("UBI SB: target at %x (cfg %x), extra=%x, name=%s, id=%i, t=%i\n", - (uint32_t)sb->header_offset, sb->section2_entry_size, (uint32_t)sb->extra_offset, sb->resource_name, sb->group_id, sb->stream_type); + /* get chain in extra table */ + table_offset = sb->extra_offset; + for (i = 0; i < sb->sequence_count; i++) { + uint32_t entry_number = (uint32_t)read_32bit(table_offset+sb->cfgs_entry_number, streamFile); + /* some sequences have an upper bit for some reason */ + ;VGM_ASSERT_ONCE(entry_number & 0x80000000, "UBI SB: sequence bit entry found at %x\n", (uint32_t)sb->extra_offset); + + entry_number = entry_number & 0x7FFFFFFF; + if (entry_number > sb->section2_num) { + VGM_LOG("UBI SB: chain with wrong entry %i vs %i at %x\n", entry_number, sb->section2_num, (uint32_t)sb->extra_offset); + goto fail; + } + + sb->sequence_chain[i] = entry_number; + + table_offset += sb->sequence_entry_size; + } + + return 1; +fail: + return 0; +} + +static int parse_type_layer(ubi_sb_header * sb, off_t offset, STREAMFILE* streamFile) { + int32_t (*read_32bit)(off_t,STREAMFILE*) = sb->big_endian ? read_32bitBE : read_32bitLE; + int16_t (*read_16bit)(off_t,STREAMFILE*) = sb->big_endian ? read_16bitBE : read_16bitLE; + off_t table_offset; + int i; + + /* layer header */ + sb->type = UBI_LAYER; + + sb->extra_offset = read_32bit(offset + sb->cfgl_extra_offset, streamFile) + sb->sectionX_offset; + sb->layer_count = read_32bit(offset + sb->cfgl_layer_count, streamFile); + sb->stream_size = read_32bit(offset + sb->cfgl_stream_size, streamFile); + sb->stream_offset = read_32bit(offset + sb->cfgl_stream_offset, streamFile); + + if (sb->stream_size == 0) { + VGM_LOG("UBI SB: bad stream size\n"); + goto fail; + } + + if (sb->layer_entry_size == 0) { + VGM_LOG("Ubi SB: layer entry size not configured at %x\n", (uint32_t)offset); + goto fail; + } + if (sb->layer_count > 16) { /* arbitrary max */ + VGM_LOG("Ubi SB: incorrect layer count\n"); + goto fail; + } + + /* get 1st layer header in extra table and validate all headers match */ + table_offset = sb->extra_offset; + sb->channels = (sb->cfgl_channels % 4) ? /* non-aligned offset is always 16b */ + (uint16_t)read_16bit(table_offset + sb->cfgl_channels, streamFile) : + (uint32_t)read_32bit(table_offset + sb->cfgl_channels, streamFile); + sb->sample_rate = read_32bit(table_offset + sb->cfgl_sample_rate, streamFile); + sb->stream_type = read_32bit(table_offset + sb->cfgl_stream_type, streamFile); + sb->num_samples = read_32bit(table_offset + sb->cfgl_num_samples, streamFile); + + for (i = 0; i < sb->layer_count; i++) { + int channels = (sb->cfgl_channels % 4) ? /* non-aligned offset is always 16b */ + (uint16_t)read_16bit(table_offset + sb->cfgl_channels, streamFile) : + (uint32_t)read_32bit(table_offset + sb->cfgl_channels, streamFile); + int sample_rate = read_32bit(table_offset + sb->cfgl_sample_rate, streamFile); + int stream_type = read_32bit(table_offset + sb->cfgl_stream_type, streamFile); + int num_samples = read_32bit(table_offset + sb->cfgl_num_samples, streamFile); + if (sb->channels != channels || sb->sample_rate != sample_rate || sb->stream_type != stream_type) { + VGM_LOG("Ubi SB: layer headers don't match at %x\n", (uint32_t)table_offset); + goto fail; + } + + /* can be +-1 */ + if (sb->num_samples != num_samples && sb->num_samples + 1 == num_samples) { + sb->num_samples -= 1; + } + + table_offset += sb->layer_entry_size; + } + + /* all layers seem external */ + sb->is_external = 1; + + /* external stream name can be found in the header (first versions) or the sectionX table (later versions) */ + if (sb->cfgl_stream_name) { + read_string(sb->resource_name, sb->resource_name_size, offset + sb->cfgl_stream_name, streamFile); + } else { + sb->cfgl_stream_name = read_32bit(offset + sb->cfgl_extra_name, streamFile); + if (sb->cfgl_stream_name != 0xFFFFFFFF) + read_string(sb->resource_name, sb->resource_name_size, sb->sectionX_offset + sb->cfgl_stream_name, streamFile); + } + + /* layers seem to include XMA header */ + + return 1; +fail: + return 0; +} + +static int parse_stream_type(ubi_sb_header * sb) { + + if (sb->type == UBI_SEQUENCE) + return 1; /* happens in a few internal sounds from early Xbox games */ if (sb->stream_type > 0xFF) { @@ -836,7 +1017,6 @@ static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile, int targe sb->stream_type = 0; } - /* guess codec */ switch (sb->stream_type) { case 0x00: /* platform default (rarely external) */ @@ -844,78 +1024,78 @@ static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile, int targe case UBI_PC: sb->codec = RAW_PCM; break; - case UBI_PS2: sb->codec = RAW_PSX; break; - case UBI_PSP: - if (check_extensions(streamFile, "sb4,sm4")) { + if (sb->is_psp_old) sb->codec = FMT_VAG; - } else { + else sb->codec = RAW_PSX; - } break; - case UBI_XBOX: sb->codec = RAW_XBOX; break; - case UBI_GC: case UBI_WII: sb->codec = RAW_DSP; break; - case UBI_X360: sb->codec = RAW_XMA1; break; #if 0 - case UBI_PS3: - /* Need to confirm */ + case UBI_PS3: /* assumed, but not games seem to use it */ sb->codec = RAW_AT3; break; #endif case UBI_3DS: sb->codec = FMT_CWAV; break; - default: VGM_LOG("UBI SB: unknown internal format\n"); goto fail; } break; - case 0x01: /* DSP (early games) or PCM (rarely used, ex. Wii/PSP/3DS) */ + case 0x01: switch (sb->version) { case 0x00000003: /* Donald Duck: Goin' Quackers */ sb->codec = RAW_DSP; break; default: - sb->codec = RAW_PCM; + sb->codec = RAW_PCM; /* uncommon, ex. Wii/PSP/3DS */ break; } break; - case 0x02: /* PS ADPCM (PS3) */ - sb->codec = RAW_PSX; - break; - - case 0x03: /* Ubi ADPCM (main external stream codec, has subtypes) */ - sb->codec = UBI_ADPCM; - break; - - case 0x04: /* Ubi IMA v3 (early games) or Ogg (later PC games) */ + case 0x02: switch (sb->version) { - case 0x00000007: /* Splinter Cell */ - sb->codec = UBI_ADPCM; + case 0x00000007: /* Splinter Cell, Splinter Cell: Pandora Tomorrow */ + case 0x00120012: /* Myst IV: Exile */ + sb->codec = UBI_UNK; break; default: - sb->codec = FMT_OGG; + sb->codec = RAW_PSX; /* PS3 */ break; } break; - case 0x05: /* AT3 (PSP, PS3) or XMA1 (X360) */ + case 0x03: + sb->codec = UBI_IMA; /* Ubi IMA v3+ (versions handled in decoder) */ + break; + + case 0x04: + switch (sb->version) { + case 0x00000007: /* Splinter Cell, Splinter Cell: Pandora Tomorrow */ + sb->codec = UBI_IMA; + break; + default: + sb->codec = FMT_OGG; /* later PC games */ + break; + } + break; + + case 0x05: switch (sb->platform) { case UBI_X360: sb->codec = FMT_XMA1; @@ -930,19 +1110,19 @@ static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile, int targe } break; - case 0x06: /* PS ADPCM (later PSP and PS3(?) games) */ - sb->codec = RAW_PSX; + case 0x06: + sb->codec = RAW_PSX; /* later PSP and PS3(?) games */ break; case 0x07: sb->codec = RAW_AT3; break; - case 0x08: /* Ubi IMA v2/v3 (early games) or ATRAC3 */ + case 0x08: switch (sb->version) { case 0x00000003: /* Donald Duck: Goin' Quackers */ case 0x00000004: /* Myst III: Exile */ - sb->codec = UBI_ADPCM; + sb->codec = UBI_IMA; /* Ubi IMA v2/v3 */ break; default: sb->codec = FMT_AT3; @@ -955,72 +1135,87 @@ static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile, int targe goto fail; } - if (sb->codec == RAW_XMA1) { - /* this field is only seen in X360 games, points at XMA1 header in extra section */ - sb->xma_header_offset = read_32bit(sb->header_offset + sb->cfga_xma_offset, streamFile) + sb->sectionX_offset; + return 1; +fail: + return 0; +} + +static int parse_internal_offset(ubi_sb_header * sb, STREAMFILE *streamFile) { + int32_t (*read_32bit)(off_t,STREAMFILE*) = sb->big_endian ? read_32bitBE : read_32bitLE; + int i, j, k; + + if (sb->type == UBI_SEQUENCE) + return 1; + + VGM_ASSERT(!sb->is_map && sb->section3_num > 2, "UBI SB: section3 > 2 found\n"); + + if (!(sb->cfga_group_id || sb->is_map) && sb->section3_num > 1) { + VGM_LOG("UBI SB: unexpected number of internal stream groups %i\n", sb->section3_num); + goto fail; } + if (sb->is_external) + return 1; - /* section 3: internal stream info */ - if (!sb->is_external) { - /* Internal sounds are split into codec groups, with their offsets being relative to group start. - * A table contains sizes of each group, so we adjust offsets based on the group ID of our sound. - * Headers normally only use 0 or 1, and section3 may only define id1 (which the internal sound would use). - * May exist even for external streams only, and they often use id 1 too. */ + /* Internal sounds are split into codec groups, with their offsets being relative to group start. + * A table contains sizes of each group, so we adjust offsets based on the group ID of our sound. + * Headers normally only use 0 or 1, and section3 may only define id1 (which the internal sound would use). + * May exist even for external streams only, and they often use id 1 too. */ - if (sb->is_map) { - /* maps store internal sounds offsets in a separate subtable, find the matching entry */ - for (i = 0; i < sb->section3_num; i++) { - off_t offset = sb->section3_offset + 0x14 * i; - off_t table_offset = read_32bit(offset + 0x04, streamFile) + sb->section3_offset; - uint32_t table_num = read_32bit(offset + 0x08, streamFile); - off_t table2_offset = read_32bit(offset + 0x0c, streamFile) + sb->section3_offset; - uint32_t table2_num = read_32bit(offset + 0x10, streamFile); + if (sb->is_map) { + /* maps store internal sounds offsets in a separate subtable, find the matching entry */ + for (i = 0; i < sb->section3_num; i++) { + off_t offset = sb->section3_offset + 0x14 * i; + off_t table_offset = read_32bit(offset + 0x04, streamFile) + sb->section3_offset; + uint32_t table_num = read_32bit(offset + 0x08, streamFile); + off_t table2_offset = read_32bit(offset + 0x0c, streamFile) + sb->section3_offset; + uint32_t table2_num = read_32bit(offset + 0x10, streamFile); - for (j = 0; j < table_num; j++) { - int index = read_32bit(table_offset + 0x08 * j + 0x00, streamFile) & 0x0000FFFF; + for (j = 0; j < table_num; j++) { + int index = read_32bit(table_offset + 0x08 * j + 0x00, streamFile) & 0x0000FFFF; - if (index == sb->header_index) { - if (!sb->cfga_group_id && table2_num > 1) { - VGM_LOG("UBI SB: unexpected number of internal stream map groups %i at %x\n", table2_num, (uint32_t)table2_offset); - goto fail; - } - - sb->stream_offset = read_32bit(table_offset + 0x08 * j + 0x04, streamFile); - for (k = 0; k < table2_num; k++) { - /* entry layout: - * 0x00 - group ID - * 0x04 - size with padding included - * 0x08 - size without padding - * 0x0c - absolute offset */ - uint32_t id = read_32bit(table2_offset + 0x10 * k + 0x00, streamFile); - if (id == sb->group_id) { - sb->stream_offset += read_32bit(table2_offset + 0x10 * k + 0x0c, streamFile); - break; - } - } - break; + if (index == sb->header_index) { + if (!sb->cfga_group_id && table2_num > 1) { + VGM_LOG("UBI SB: unexpected number of internal stream map groups %i at %x\n", table2_num, (uint32_t)table2_offset); + goto fail; } - } - if (sb->stream_offset) + sb->stream_offset = read_32bit(table_offset + 0x08 * j + 0x04, streamFile); + for (k = 0; k < table2_num; k++) { + /* entry layout: + * 0x00 - group ID + * 0x04 - size with padding included + * 0x08 - size without padding + * 0x0c - absolute offset */ + uint32_t id = read_32bit(table2_offset + 0x10 * k + 0x00, streamFile); + if (id == sb->group_id) { + sb->stream_offset += read_32bit(table2_offset + 0x10 * k + 0x0c, streamFile); + break; + } + } break; - } - } else { - sb->stream_offset += sb->sounds_offset; - - /* banks store internal sounds offsets in table: group id + group size, find the matching entry */ - - if (sb->cfga_group_id && sb->section3_num > 1) { - for (i = 0; i < sb->section3_num; i++) { - off_t offset = sb->section3_offset + sb->section3_entry_size * i; - - /* table has unordered ids+size, so if our id doesn't match current data offset must be beyond */ - if (read_32bit(offset + 0x00, streamFile) == sb->group_id) - break; - sb->stream_offset += read_32bit(offset + 0x04, streamFile); } } + + if (sb->stream_offset) + break; + } + } + else { + /* banks store internal sounds after all headers and adjusted by the group table, find the matching entry */ + + off_t sounds_offset = sb->section3_offset + sb->section3_entry_size*sb->section3_num; + sb->stream_offset = sounds_offset + sb->stream_offset; + + if (sb->cfga_group_id && sb->section3_num > 1) { /* maybe should always test this? */ + for (i = 0; i < sb->section3_num; i++) { + off_t offset = sb->section3_offset + sb->section3_entry_size * i; + + /* table has unordered ids+size, so if our id doesn't match current data offset must be beyond */ + if (read_32bit(offset + 0x00, streamFile) == sb->group_id) + break; + sb->stream_offset += read_32bit(offset + 0x04, streamFile); + } } } @@ -1029,16 +1224,115 @@ fail: return 0; } +/* parse a header resource at offset */ +static int parse_header(ubi_sb_header * sb, STREAMFILE *streamFile, off_t offset, int index) { + int32_t (*read_32bit)(off_t,STREAMFILE*) = sb->big_endian ? read_32bitBE : read_32bitLE; + + /* parse known headers (see config_sb for info) */ + sb->header_index = index; + sb->header_offset = offset; + + sb->header_id = read_32bit(offset + 0x00, streamFile); + sb->header_type = read_32bit(offset + 0x04, streamFile); + + switch(sb->header_type) { + case 0x01: + if (!parse_type_audio(sb, offset, streamFile)) + goto fail; + break; + case 0x05: + case 0x0c: + if (!parse_type_sequence(sb, offset, streamFile)) + goto fail; + break; + case 0x06: + case 0x0d: + if (!parse_type_layer(sb, offset, streamFile)) + goto fail; + break; + default: + VGM_LOG("UBI SB: unknown header type at %x\n", (uint32_t)offset); + goto fail; + } + + /* find actual codec (as different games' stream_type can overlap) */ + if (!parse_stream_type(sb)) + goto fail; + + /* find actual stream offset in section3 */ + if (!parse_internal_offset(sb, streamFile)) + goto fail; + + return 1; +fail: + return 0; +} + +/* parse a bank and its possible audio headers (resources). */ +static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile, int target_subsong) { + int32_t (*read_32bit)(off_t,STREAMFILE*) = sb->big_endian ? read_32bitBE : read_32bitLE; + int i; + + ;VGM_LOG("UBI SB: s1=%x (%x*%x), s2=%x (%x*%x), sX=%x (%x), s3=%x (%x*%x)\n", + sb->section1_offset,sb->section1_entry_size,sb->section1_num,sb->section2_offset,sb->section2_entry_size,sb->section2_num, + sb->sectionX_offset,sb->sectionX_size,sb->section3_offset,sb->section3_entry_size,sb->section3_num); + + /* find target subsong info in section2 */ + sb->bank_subsongs = 0; + for (i = 0; i < sb->section2_num; i++) { + off_t offset = sb->section2_offset + sb->section2_entry_size*i; + uint32_t header_type; + + /* check candidate types */ + /*header_id =*/ read_32bit(offset + 0x00, streamFile); /* forces buffer read */ + header_type = read_32bit(offset + 0x04, streamFile); + + if (header_type <= 0x00 || header_type >= 0x10 || header_type == 0x09 || header_type == 0x0b) { + VGM_LOG("UBI SB: unknown type %x at %x\n", header_type, (uint32_t)offset); + goto fail; + } + + sb->types[header_type]++; + + if (!sb->allowed_types[header_type]) + continue; + + /* update subsongs (keep counting even after found) */ + sb->bank_subsongs++; + sb->total_subsongs++; + if (sb->total_subsongs != target_subsong) + continue; + + /* parse target subsong */ + if (!parse_header(sb, streamFile, offset, i)) + goto fail; + + /* maps can contain +10000 subsongs, we need something helpful + * (best here right after subsong detection, since some sequence re-parse types) */ + build_readable_name(sb->readable_name, sizeof(sb->readable_name), sb); + } + + /* either target subsong found or it's in another bank (in case of maps), both handled externally */ + + ;VGM_LOG("UBI SB: types "); {int i; for (i=0;i<16;i++){ VGM_ASSERT(sb->types[i],"%02x=%i ",i,sb->types[i]); }} VGM_LOG("\n"); + + return 1; +fail: + return 0; +} + +/* ************************************************************************* */ + static int config_sb_platform(ubi_sb_header * sb, STREAMFILE *streamFile) { char filename[PATH_LIMIT]; int filename_len; char platform_char; uint32_t version; - /* to find out hijacking platforms */ + /* to find out hijacking (LE) platforms */ version = read_32bitLE(0x00, streamFile); - /* get X from .sbX/smX */ + /* get X from .sbX/smX/lmX */ get_streamfile_name(streamFile,filename,sizeof(filename)); filename_len = strlen(filename); platform_char = filename[filename_len - 1]; @@ -1060,6 +1354,7 @@ static int config_sb_platform(ubi_sb_header * sb, STREAMFILE *streamFile) { switch(version) { /* early PSP clashes with X360 */ case 0x0012000C: /* multiple games use this ID and all are sb4/sm4 */ sb->platform = UBI_PSP; + sb->is_psp_old = 1; break; default: sb->platform = UBI_X360; @@ -1092,134 +1387,272 @@ static int config_sb_platform(ubi_sb_header * sb, STREAMFILE *streamFile) { sb->platform == UBI_X360 || sb->platform == UBI_WII; - return 1; fail: return 0; } -#if 0 -static int config_sb_version_layer2(ubi_sb_header * sb) { - sb->cfgc_sequence_count = 0x28; //POP - sb->cfg5_sequence_count = 0x28; //POP WW - sb->cfg5_sequence_count = 0x2c; //POP TT, TMNT - - sb->cfgd_layer_rate = 0x1c; - sb->cfgd_layer_count = 0x20; - /* 0x2c: external flag? */ - sb->cfgd_stream_name = 0x30; - sb->cfgd_stream_offset = 0x58; - /* 0x5c: original layer rate? */ - sb->cfgd_stream_size = 0x60; - sb->cfgd_num_samples = 0x64; +static void config_sb_entry(ubi_sb_header * sb, size_t section1_size_entry, size_t section2_size_entry) { + sb->section1_entry_size = section1_size_entry; + sb->section2_entry_size = section2_size_entry; + sb->section3_entry_size = 0x08; } -#endif +static void config_sb_audio_fs(ubi_sb_header * sb, off_t external_flag, off_t group_id, off_t loop_flag) { + /* audio header with standard flags */ + sb->cfga_external_flag = external_flag; + sb->cfga_group_id = group_id; + sb->cfga_loop_flag = loop_flag; + sb->cfga_and_external_flag = 1; + sb->cfga_and_group_id = 1; + sb->cfga_and_loop_flag = 1; +} +static void config_sb_audio_fb(ubi_sb_header * sb, off_t flag_bits, int external_and, int group_and, int loop_and) { + /* audio header with bit flags */ + sb->cfga_external_flag = flag_bits; + sb->cfga_group_id = flag_bits; + sb->cfga_loop_flag = flag_bits; + sb->cfga_and_external_flag = external_and; + sb->cfga_and_group_id = group_and; + sb->cfga_and_loop_flag = loop_and; +} +static void config_sb_audio_hs(ubi_sb_header * sb, off_t channels, off_t sample_rate, off_t num_samples, off_t num_samples2, off_t stream_name, off_t stream_type) { + /* audio header with stream name */ + sb->cfga_channels = channels; + sb->cfga_sample_rate = sample_rate; + sb->cfga_num_samples = num_samples; + sb->cfga_num_samples2 = num_samples2; + sb->cfga_stream_name = stream_name; + sb->cfga_stream_type = stream_type; +} +static void config_sb_audio_he(ubi_sb_header * sb, off_t channels, off_t sample_rate, off_t num_samples, off_t num_samples2, off_t extra_name, off_t stream_type) { + /* audio header with extra name */ + sb->cfga_channels = channels; + sb->cfga_sample_rate = sample_rate; + sb->cfga_num_samples = num_samples; + sb->cfga_num_samples2 = num_samples2; + sb->cfga_extra_name = extra_name; + sb->cfga_stream_type = stream_type; +} +static void config_sb_sequence(ubi_sb_header * sb, off_t sequence_count, off_t entry_size) { + /* sequence header and chain table */ + sb->cfgs_intro_flag = sequence_count - 0x10; + sb->cfgs_outro_flag = sequence_count - 0x0c; + sb->cfgs_sequence_count = sequence_count; + sb->sequence_entry_size = entry_size; + sb->cfgs_entry_number = 0x00; + sb->cfgs_and_intro_flag = 1; + sb->cfgs_and_outro_flag = 1; +} +static void config_sb_layer_hs(ubi_sb_header * sb, off_t layer_count, off_t stream_size, off_t stream_offset, off_t stream_name) { + /* layer headers with stream name */ + sb->cfgl_layer_count = layer_count; + sb->cfgl_stream_size = stream_size; + sb->cfgl_stream_offset = stream_offset; + sb->cfgl_stream_name = stream_name; +} +static void config_sb_layer_he(ubi_sb_header * sb, off_t layer_count, off_t stream_size, off_t stream_offset, off_t extra_name) { + /* layer headers with extra name */ + sb->cfgl_layer_count = layer_count; + sb->cfgl_stream_size = stream_size; + sb->cfgl_stream_offset = stream_offset; + sb->cfgl_extra_name = extra_name; +} +static void config_sb_layer_sh(ubi_sb_header * sb, off_t entry_size, off_t sample_rate, off_t channels, off_t stream_type, off_t num_samples) { + /* layer sub-headers in extra table */ + sb->layer_entry_size = entry_size; + sb->cfgl_sample_rate = sample_rate; + sb->cfgl_channels = channels; + sb->cfgl_stream_type = stream_type; + sb->cfgl_num_samples = num_samples; +} + static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { int is_biadd_psp = 0; - /* Type 1 audio header varies with almost every game + platform (some kind of struct serialization?) - * Support is done case-by-case as offsets/order/fields change slightly. Header usually contains: + /* Most of the format varies with almost every game + platform (struct serialization?). + * Support is configured case-by-case as offsets/order/fields only change slightly, + * and later games may remove fields. We only configure those actually needed. + * + * Various type use "chains" of entry numbers (defined in the extra table). + * Its format also depends on type. + */ + + /* Header types found in section2 (possibly called "resource headers"): + * + * Type 01 (old/new): + * Single audio header, external or internal, part of a chain or single. Format: * - fixed part (id, type, stream size, extra offset, stream offset) - * - flags - * - stream samples (early games may or may not set it for internal streams, even in the same game) - * - stream size (same as the fixed part) - * - bitrate / original sample rate - * - sample rate - * - pcm bits? - * - channels - * - stream type + * - flags (as bitflags or in separate fields, around ~6 observed flags) + * - samples+samples (loop+total) + size+size (roughly equal to stream size) + * - bitrate (total sample rate) + * - base info (sample rate, pcm bits?, channels, codec) * - external filename or internal filename on some platforms (earlier versions) * - external filename offset in the extra table (later versions) - * - end flags + * - end flags? * - * In between all those there are may be unused fields contain uninitialized memory garbage - * (null, part of strings, data from previous header, etc). + * Type 02 (old?/new): + * Chain, possibly to play with config (ex: type 08 (float 0.3) + 01) * - * Earlier games or those with bigger header size have most fields, while later remove - * or change around some, or sometimes only use fields for external streams. - * We only configure offsets for fields actually needed. + * Type 03 (new), 09? (old): + * Chain, other way to play things? (ex: type 03 + 04) + * + * Type 04 (old/new), 0a (old): + * Chain of N types, possibly to play one as random (usually N voice/sfx like + * like death screams, but may include sequences). + * + * Type 05 (new), 0c (old): sequences + * N audio segment, normally with lead-in but not lead-outs. Sequences can reuse + * segments (internal loops), or can be single entries (full song or leads). + * Sequences seem to include only music or cutscenes, so even single entries can be + * useful to parse, since the readable name would make them stand out. Format: + * - extra offset to chain + * - intro flag + * - outro flag + * - sequence count + * - ID-like fields in the header and sequence table may point to other chains? + * + * Type 06 (new), 0d (old): + * Layer header, stream divided into N equal parts in a blocked format. Format: + * - fixed part (id, type) + * - extra offset to layer info (sample rate, pcm bits?, channels, codec, samples) + * - layer count + * - sometimes total channels, bitrate, etc + * - flags? + * - stream size + stream offset + * - external filename, or filename offset in the extra table + * Layer blocks are handled separatedly as the format doesn't depend on sb's version/platform. + * Some values may be flags/config as multiple 0x06 can point to the same layer, with different 'flags'? + * + * Type 07 (new), 0e (old): + * - another chain of something (single entry?), rare. + * + * Type 08 (new), 0f (old): + * - audio config? (almost all fields 0 except sometimes 1.0 float in the middle). + * in older games may also point to the extra table, maybe equivalent to 02. */ + /* debug strings reference: + * - TYPE_SAMPLE: should be 0x01 (also "sound resource") + * - TYPE_MULTITRACK: should be 0x06/0x0d (also "multilayer resource") + * - TYPE_SILENCE: ? + * sequences may be "theme resource" + * "class descryptor" is referenced, + * + * Possible type names from .bnm (.sb's predecessor): + * 0: TYPE_INVALID + * 1: TYPE_SAMPLE + * 2: TYPE_MIDI + * 3: TYPE_CDAUDIO + * 4: TYPE_SEQUENCE + * 5: TYPE_SWITCH_OLD + * 6: TYPE_SPLIT + * 7: TYPE_THEME_OLD + * 8: TYPE_SWITCH + * 9: TYPE_THEME_OLD2 + * A: TYPE_RANDOM + * B: TYPE_THEME0 + */ + + /* All types may contain memory garbage, making it harder to identify fields (platforms + * and games are affected differently by this). Often types contain memory from the previous + * type header unless overwritten, random memory, or default initialization garbage. + * So if some non-audio type looks like audio it's probably repeating old data. + * This even happens for common fields (ex. type 6 at 0x08 has prev garbage, not stream size). */ + /* common */ - sb->section3_entry_size = 0x08; - sb->resource_name_size = 0x24; /* maybe 0x28 or 0x20 for some but ok enough (null terminated) */ - /* this is same in all games since ~2003 */ - sb->cfga_stream_size = 0x08; - sb->cfga_extra_offset = 0x0c; - sb->cfga_stream_offset = 0x10; - sb->cfgs_extra_offset = 0x0c; + sb->resource_name_size = 0x24; /* maybe 0x20/0x28 for some but ok enough (null terminated) */ - sb->and_external_flag = 0x01; - sb->and_loop_flag = 0x01; + /* represents map style (1=first, 2=mid, 3=latest) */ + if (sb->version <= 0x00000007) + sb->map_version = 1; + else if (sb->version < 0x00150000) + sb->map_version = 2; + else + sb->map_version = 3; + sb->map_entry_size = (sb->map_version < 2) ? 0x30 : 0x34; - /* Batman: Vengeance (2001)(PS2)-map 0x00000003 */ - /* Tom Clancy's Rainbow Six - Vegas 2 (2008)(PC)-? */ - /* Myst III (2008)(PS2)-? */ + if (sb->version <= 0x00000007) { + sb->cfga_stream_size = 0x0c; + sb->cfga_extra_offset = 0x10; + sb->cfga_stream_offset = 0x14; + sb->cfgs_extra_offset = 0x10; + + sb->cfgl_extra_offset = 0x10; + } + else { + sb->cfga_stream_size = 0x08; + sb->cfga_extra_offset = 0x0c; + sb->cfga_stream_offset = 0x10; + + sb->cfgs_extra_offset = 0x0c; + + sb->cfgl_extra_offset = 0x0c; + } + + sb->allowed_types[0x01] = 1; + sb->allowed_types[0x05] = 1; + sb->allowed_types[0x0c] = 1; + sb->allowed_types[0x06] = 1; + sb->allowed_types[0x0d] = 1; + +#if 0 + { + STREAMFILE * streamTest; + streamTest= open_streamfile_by_filename(streamFile, ".no_audio.sbx"); + if (streamTest) { sb->allowed_types[0x01] = 0; close_streamfile(streamTest); } + + streamTest= open_streamfile_by_filename(streamFile, ".no_sequence.sbx"); + if (streamTest) { sb->allowed_types[0x05] = sb->allowed_types[0x0c] = 0; close_streamfile(streamTest); } + + streamTest= open_streamfile_by_filename(streamFile, ".no_layer.sbx"); + if (streamTest) { sb->allowed_types[0x06] = sb->allowed_types[0x0d] = 0; close_streamfile(streamTest); } + } +#endif //todo some dsp offsets have problems, wrong id? //todo uses Ubi IMA v2 has has some deviation in the right channel + clicks? //todo has some sample rate / loop configs problems? (ex Batman #5451) + //todo buggy reads in layers? /* Disney's Tarzan: Untamed (2001)(GC)-map */ /* Batman: Vengeance (2001)(GC)-map */ /* Donald Duck: Goin' Quackers (2002)(GC)-map */ if (sb->version == 0x00000003 && sb->platform == UBI_GC) { - sb->section1_entry_size = 0x40; - sb->section2_entry_size = 0x6c; + config_sb_entry(sb, 0x40, 0x6c); - sb->map_version = 1; + config_sb_audio_fs(sb, 0x30, 0x2c, 0x34); + config_sb_audio_hs(sb, 0x56, 0x50, 0x48, 0x48, 0x5c, 0x58); - sb->cfga_stream_size = 0x0c; - sb->cfga_extra_offset = 0x10; - sb->cfga_stream_offset = 0x14; + config_sb_sequence(sb, 0x2c, 0x1c); - sb->cfga_group_id = 0x2c; - sb->cfga_external_flag = 0x30; - sb->cfga_loop_flag = 0x34; - sb->cfga_num_samples = 0x48; - sb->cfga_num_samples2 = 0x48; /* full loop */ - sb->cfga_sample_rate = 0x50; - sb->cfga_channels = 0x56; - sb->cfga_stream_type = 0x58; - sb->cfga_stream_name = 0x5c; - - sb->has_short_channels = 1; - sb->has_full_loop = 1; - - sb->cfgs_extra_offset = 0x10; - sb->cfgs_sequence_count = 0x2c; - //has layer 0d + config_sb_layer_hs(sb, 0x20, 0x4c, 0x44, 0x34); + config_sb_layer_sh(sb, 0x1c, 0x04, 0x0a, 0x0c, 0x18); return 1; } + #if 0 /* Batman: Vengeance (2001)(PS2)-map */ + /* Disney's Tarzan: Untamed (2001)(PS2)-map */ if (sb->version == 0x00000003 && sb->platform == UBI_PS2) { - sb->section1_entry_size = 0x30; - sb->section2_entry_size = 0x3c; + config_sb_entry(sb, 0x30, 0x3c); - sb->map_version = 1; + config_sb_audio_fb(sb, 0x20, (1 << 2), (1 << 3), (1 << 4));//? + sb->cfga_group_id = 0x1c?; + sb->cfga_external_flag = 0x1c?; + sb->cfga_loop_flag = 0x1c?; + sb->cfga_num_samples = 0x28; + sb->cfga_num_samples2 = 0x28; + sb->cfga_sample_rate = 0x24; + sb->cfga_channels = 0x2a? + sb->cfga_stream_type = 0x34? 0x38; + sb->cfga_stream_name = -1; /* implicit STRM.SM1 */ - sb->cfga_stream_size = 0x0c; - sb->cfga_extra_offset = 0x10; - sb->cfga_stream_offset = 0x14; + config_sb_sequence(sb, 0x2c, 0x10); - sb->cfga_group_id = 0x1c?; - sb->cfga_external_flag = 0x1c?; - sb->cfga_loop_flag = 0x1c?; - sb->cfga_num_samples = 0x20? 28? 2c? - sb->cfga_num_samples2 = 0x20? 28? 2c? - sb->cfga_sample_rate = 0x24; - sb->cfga_channels = 0x2a? - sb->cfga_stream_type = 0x34; - sb->cfga_stream_name = -1; //has implicit stream name - - sb->has_short_channels = 1; - sb->has_full_loop = 1; - - //has layer 0d + //layer format ??? return 1; } #endif @@ -1228,358 +1661,175 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { //todo offsets seems to work differently (stream offset is always 0) /* Myst III: Exile (2001)(PS2)-map */ if (sb->version == 0x00000004 && sb->platform == UBI_PS2) { - sb->section1_entry_size = 0x34; - sb->section2_entry_size = 0x70; + config_sb_entry(sb, 0x34, 0x70); - sb->map_version = 1; + config_sb_audio_fb(sb, 0x1c, (1 << 2), (1 << 3), (1 << 4)); + config_sb_audio_hs(sb, 0x24, 0x28, 0x2c, 0x34, 0x44, 0x6c); - sb->cfga_stream_size = 0x0c; - sb->cfga_extra_offset = 0x10; - sb->cfga_stream_offset = 0x14; + //todo sequences - sb->cfga_group_id = 0x1c; - sb->cfga_external_flag = 0x1c; - sb->cfga_loop_flag = 0x1c; - sb->cfga_channels = 0x24; - sb->cfga_sample_rate = 0x28; - sb->cfga_num_samples = 0x2c; - sb->cfga_num_samples2 = 0x34; - sb->cfga_stream_name = 0x44; - sb->cfga_stream_type = 0x6c; - - sb->and_external_flag = 0x04; - sb->and_loop_flag = 0x10; - sb->and_group_id = 0x08; - sb->shr_group_id = 3; - - sb->cfgs_extra_offset = 0x10; - sb->cfgs_sequence_count = 0x2c; + //todo layers return 1; } #endif - #if 0 + //todo uses codec 02 /* Splinter Cell (2002)(PC)-map */ /* Splinter Cell: Pandora Tomorrow (2004)(PC)-map */ if (sb->version == 0x00000007 && sb->platform == UBI_PC) { - /* Stream types: - * 0x01: PCM - * 0x02: unsupported codec, appears to be Ubi IMA in a blocked layout - * 0x04: Ubi IMA v3 (not Vorbis) - */ - sb->section1_entry_size = 0x58; - sb->section2_entry_size = 0x80; + config_sb_entry(sb, 0x58, 0x80); - sb->map_version = 1; + config_sb_audio_fs(sb, 0x28, 0x2c, 0x24?); + config_sb_audio_hs(sb, 0x4a, 0x44, 0x30, 0x38, 0x50, 0x4c); + sb->cfga_has_internal_names = 1; - sb->cfga_stream_size = 0x0c; - sb->cfga_extra_offset = 0x10; - sb->cfga_stream_offset = 0x14; + //todo sequences - //sb->cfga_loop_flag = 0x24; //? - sb->cfga_external_flag = 0x28; - sb->cfga_group_id = 0x2c; - sb->cfga_num_samples = 0x30; - //sb->cfga_num_samples2 = 0x38; - sb->cfga_sample_rate = 0x44; - sb->cfga_channels = 0x4a; - sb->cfga_stream_type = 0x4c; - sb->cfga_stream_name = 0x50; - - sb->has_short_channels = 1; - sb->has_internal_names = 1; - //has layer 0d + config_sb_layer_hs(sb, 0x20, 0x64, 0x5c, 0x34); + config_sb_layer_sh(sb, 0x18, 0x00, 0x06, 0x08, 0x14); return 1; } #endif + #if 0 /* Splinter Cell (2002)(Xbox)-map */ /* Splinter Cell: Pandora Tomorrow (2004)(Xbox)-map */ if (sb->version == 0x00000007 && sb->platform == UBI_XBOX) { - /* Stream types: - * 0x01: PCM - * 0x02: unsupported codec, appears to be Ubi IMA in a blocked layout - * 0x04: Ubi IMA v3 (not Vorbis) - */ - sb->section1_entry_size = 0x58; - sb->section2_entry_size = 0x78; + config_sb_entry(sb, 0x58, 0x78); - sb->map_version = 1; + config_sb_audio_fs(sb, 0x28, 0x24? 0x2c?, 0x2c? 0x24?); + config_sb_audio_hs(sb, 0x4a, 0x44, 0x30, 0x38, 0x50, 0x4c); + sb->cfga_has_internal_names = 1; - sb->cfga_stream_size = 0x0c; - sb->cfga_extra_offset = 0x10; - sb->cfga_stream_offset = 0x14; + //todo sequences - sb->cfga_group_id = 0x24? 0x2c; - sb->cfga_external_flag = 0x28; - sb->cfga_loop_flag = 0x2c? 0x24? - sb->cfga_num_samples = 0x30; - sb->cfga_num_samples2 = 0x38; - sb->cfga_sample_rate = 0x44; - sb->cfga_channels = 0x4a; - sb->cfga_stream_type = 0x4c; - sb->cfga_stream_name = 0x50; - - sb->has_short_channels = 1; - sb->has_internal_names = 1; - //has layer 0d + //todo layers return 1; } #endif - /* Prince of Persia: Sands of Time (2003)(PC)-bank */ - if ((sb->version == 0x000A0002 && sb->platform == UBI_PC) || /* (not sure if exists, just in case) */ - (sb->version == 0x000A0004 && sb->platform == UBI_PC)) { /* main game */ - sb->section1_entry_size = 0x64; - sb->section2_entry_size = 0x80; - sb->cfga_external_flag = 0x24; - sb->cfga_loop_flag = 0x28; - sb->cfga_group_id = 0x2c; - sb->cfga_num_samples = 0x30; - sb->cfga_num_samples2 = 0x38; - sb->cfga_sample_rate = 0x44; - sb->cfga_channels = 0x4a; - sb->cfga_stream_type = 0x4c; - sb->cfga_stream_name = 0x50; + /* Prince of Persia: Sands of Time (2003)(PC)-bank 0x000A0004 / 0x000A0002 (just in case) */ + if ((sb->version == 0x000A0002 && sb->platform == UBI_PC) || + (sb->version == 0x000A0004 && sb->platform == UBI_PC)) { + config_sb_entry(sb, 0x64, 0x80); - sb->has_short_channels = 1; - sb->has_internal_names = 1; + config_sb_audio_fs(sb, 0x24, 0x2c, 0x28); + config_sb_audio_hs(sb, 0x4a, 0x44, 0x30, 0x38, 0x50, 0x4c); + sb->cfga_has_internal_names = 1; - sb->cfgs_sequence_count = 0x28; - //has layer 0d (main game) + config_sb_sequence(sb, 0x28, 0x14); + + config_sb_layer_hs(sb, 0x20, 0x60, 0x58, 0x30); + config_sb_layer_sh(sb, 0x14, 0x00, 0x06, 0x08, 0x10); return 1; } - /* Prince of Persia: Sands of Time (2003)(PS2)-bank */ - if ((sb->version == 0x000A0002 && sb->platform == UBI_PS2) || /* Prince of Persia 1 port */ - (sb->version == 0x000A0004 && sb->platform == UBI_PS2)) { /* main game */ - sb->section1_entry_size = 0x48; - sb->section2_entry_size = 0x6c; + /* Prince of Persia: Sands of Time (2003)(PS2)-bank 0x000A0004 / 0x000A0002 (POP1 port) */ + /* Tom Clancy's Rainbow Six 3 (2003)(PS2)-bank 0x000A0007 */ + /* Splinter Cell: Pandora Tomorrow(?) (2006)(PS2)-bank 0x000A0008 */ + /* Prince of Persia: Warrior Within (2004)(PS2)-bank 0x00120009 */ + if ((sb->version == 0x000A0002 && sb->platform == UBI_PS2) || + (sb->version == 0x000A0004 && sb->platform == UBI_PS2) || + (sb->version == 0x000A0007 && sb->platform == UBI_PS2) || + (sb->version == 0x000A0008 && sb->platform == UBI_PS2) || + (sb->version == 0x00120009 && sb->platform == UBI_PS2)) { + config_sb_entry(sb, 0x48, 0x6c); - sb->cfga_external_flag = 0x18; - sb->cfga_loop_flag = 0x18; - sb->cfga_group_id = 0x18; - sb->cfga_channels = 0x20; - sb->cfga_sample_rate = 0x24; - sb->cfga_num_samples = 0x30; /* may be null */ - sb->cfga_num_samples2 = 0x38; /* may be null */ - sb->cfga_stream_name = 0x40; - sb->cfga_stream_type = 0x68; + config_sb_audio_fb(sb, 0x18, (1 << 2), (1 << 3), (1 << 4)); + config_sb_audio_hs(sb, 0x20, 0x24, 0x30, 0x38, 0x40, 0x68); /* num_samples may be null */ - sb->and_external_flag = 0x04; - sb->and_loop_flag = 0x10; - sb->and_group_id = 0x08; - sb->shr_group_id = 3; + config_sb_sequence(sb, 0x28, 0x10); - sb->cfgs_sequence_count = 0x28; - //has layer 0d (main game) + config_sb_layer_hs(sb, 0x20, 0x60, 0x58, 0x30); + config_sb_layer_sh(sb, 0x14, 0x00, 0x06, 0x08, 0x10); return 1; } - /* Prince of Persia: Sands of Time (2003)(Xbox)-bank */ - if ((sb->version == 0x000A0002 && sb->platform == UBI_XBOX) || /* Prince of Persia 1 port */ - (sb->version == 0x000A0004 && sb->platform == UBI_XBOX)) { /* main game */ - sb->section1_entry_size = 0x64; - sb->section2_entry_size = 0x78; + /* Prince of Persia: Sands of Time (2003)(Xbox)-bank 0x000A0004 / 0x000A0002 (POP1 port) */ + if ((sb->version == 0x000A0002 && sb->platform == UBI_XBOX) || + (sb->version == 0x000A0004 && sb->platform == UBI_XBOX)) { + config_sb_entry(sb, 0x64, 0x78); - sb->cfga_external_flag = 0x24; - sb->cfga_group_id = 0x28; - sb->cfga_loop_flag = 0x2c; - sb->cfga_num_samples = 0x30; - sb->cfga_num_samples2 = 0x38; - sb->cfga_sample_rate = 0x44; - sb->cfga_channels = 0x4a; - sb->cfga_stream_type = 0x4c; /* may contain garbage */ - sb->cfga_stream_name = 0x50; + config_sb_audio_fs(sb, 0x24, 0x28, 0x2c); + config_sb_audio_hs(sb, 0x4a, 0x44, 0x30, 0x38, 0x50, 0x4c); /* stream_type may contain garbage */ + sb->cfga_has_internal_names = 1; - sb->has_short_channels = 1; - sb->has_internal_names = 1; + config_sb_sequence(sb, 0x28, 0x14); - sb->cfgs_sequence_count = 0x28; - //has layer 0d (main game) + config_sb_layer_hs(sb, 0x20, 0x60, 0x58, 0x30); + config_sb_layer_sh(sb, 0x14, 0x00, 0x06, 0x08, 0x10); return 1; } - // todo fix batman interleave (ex. #22, #134, #222) - /* Batman: Rise of Sin Tzu (2003)(GC)-map [0x000A0002] */ - /* Prince of Persia: Sands of Time (2003)(GC)-bank [0x000A0002/0x000A0004] */ - if ((sb->version == 0x000A0002 && sb->platform == UBI_GC) || /* Prince of Persia 1 port */ - (sb->version == 0x000A0004 && sb->platform == UBI_GC)) { /* main game */ - sb->section1_entry_size = 0x64; - sb->section2_entry_size = 0x74; + /* Batman: Rise of Sin Tzu (2003)(GC)-map 0x000A0002 */ + /* Prince of Persia: Sands of Time (2003)(GC)-bank 0x000A0004 / 0x000A0002 (POP1 port)*/ + if ((sb->version == 0x000A0002 && sb->platform == UBI_GC) || + (sb->version == 0x000A0004 && sb->platform == UBI_GC)) { + config_sb_entry(sb, 0x64, 0x74); - sb->map_version = 2; + config_sb_audio_fs(sb, 0x20, 0x24, 0x28); + config_sb_audio_hs(sb, 0x46, 0x40, 0x2c, 0x34, 0x4c, 0x48); - sb->cfga_external_flag = 0x20; - sb->cfga_group_id = 0x24; - sb->cfga_loop_flag = 0x28; - sb->cfga_num_samples = 0x2c; - sb->cfga_num_samples2 = 0x34; - sb->cfga_sample_rate = 0x40; - sb->cfga_channels = 0x46; - sb->cfga_stream_type = 0x48; - sb->cfga_stream_name = 0x4c; + config_sb_sequence(sb, 0x28, 0x14); - sb->has_short_channels = 1; - //has layer 0d (POP:SOT main game, Batman) - - sb->cfgs_sequence_count = 0x28; - return 1; - } - - /* Tom Clancy's Rainbow Six 3 (2003)(PS2)-bank */ - if (sb->version == 0x000A0007 && sb->platform == UBI_PS2) { - sb->section1_entry_size = 0x48; - sb->section2_entry_size = 0x6c; - - sb->cfga_external_flag = 0x18; - sb->cfga_loop_flag = 0x18; - sb->cfga_group_id = 0x18; - sb->cfga_channels = 0x20; - sb->cfga_sample_rate = 0x24; - sb->cfga_num_samples = 0x30; /* may be null */ - sb->cfga_num_samples2 = 0x38; /* may be null */ - sb->cfga_stream_name = 0x40; - sb->cfga_stream_type = 0x68; - - sb->and_external_flag = 0x04; - sb->and_loop_flag = 0x10; - sb->and_group_id = 0x08; - sb->shr_group_id = 3; - - //has layer 0d - return 1; - } - - /* Splincer Cell: Pandora Tomorrow(?) (2006)(PS2)-bank */ - if (sb->version == 0x000A0008 && sb->platform == UBI_PS2) { - sb->section1_entry_size = 0x48; - sb->section2_entry_size = 0x6c; - - sb->cfga_external_flag = 0x18; - sb->cfga_loop_flag = 0x18; - sb->cfga_group_id = 0x18; - sb->cfga_channels = 0x20; - sb->cfga_sample_rate = 0x24; - sb->cfga_num_samples = 0x30; /* may be null */ - sb->cfga_num_samples2 = 0x38; /* may be null */ - sb->cfga_stream_name = 0x40; - sb->cfga_stream_type = 0x68; - - sb->and_external_flag = 0x04; - sb->and_loop_flag = 0x10; - sb->and_group_id = 0x08; - sb->shr_group_id = 3; - //has layer? + config_sb_layer_hs(sb, 0x20, 0x60, 0x58, 0x30); + config_sb_layer_sh(sb, 0x14, 0x00, 0x06, 0x08, 0x10); return 1; } /* Myst IV Demo (2004)(PC)-bank */ if (sb->version == 0x00100000 && sb->platform == UBI_PC) { - sb->section1_entry_size = 0x68; - sb->section2_entry_size = 0xa4; + config_sb_entry(sb, 0x68, 0xa4); - sb->cfga_external_flag = 0x24; - sb->cfga_loop_flag = 0x28; - sb->cfga_group_id = 0x2c; - sb->cfga_num_samples = 0x30; - sb->cfga_num_samples2 = 0x38; - sb->cfga_sample_rate = 0x44; - sb->cfga_channels = 0x4c; - sb->cfga_stream_type = 0x50; - sb->cfga_stream_name = 0x54; + config_sb_audio_fs(sb, 0x24, 0x2c, 0x28); + config_sb_audio_hs(sb, 0x4c, 0x44, 0x30, 0x38, 0x54, 0x50); + sb->cfga_has_internal_names = 1; - sb->has_internal_names = 1; + /* no sequences */ + + /* no layers */ return 1; } /* Prince of Persia: Warrior Within (2004)(PC)-bank */ if (sb->version == 0x00120009 && sb->platform == UBI_PC) { - sb->section1_entry_size = 0x6c; - sb->section2_entry_size = 0x84; + config_sb_entry(sb, 0x6c, 0x84); - sb->cfga_external_flag = 0x24; - sb->cfga_loop_flag = 0x28; - sb->cfga_group_id = 0x2c; - sb->cfga_num_samples = 0x30; - sb->cfga_num_samples2 = 0x38; - sb->cfga_sample_rate = 0x44; - sb->cfga_channels = 0x4c; - sb->cfga_stream_type = 0x50; - sb->cfga_stream_name = 0x54; + config_sb_audio_fs(sb, 0x24, 0x2c, 0x28); + config_sb_audio_hs(sb, 0x4c, 0x44, 0x30, 0x38, 0x54, 0x50); + sb->cfga_has_internal_names = 1; - sb->has_internal_names = 1; + config_sb_sequence(sb, 0x28, 0x14); - sb->cfgs_sequence_count = 0x28; - //no layers - return 1; - } - - /* Prince of Persia: Warrior Within (2004)(PS2)-bank */ - if (sb->version == 0x00120009 && sb->platform == UBI_PS2) { - sb->section1_entry_size = 0x48; - sb->section2_entry_size = 0x6c; - - sb->cfga_external_flag = 0x18; - sb->cfga_loop_flag = 0x18; - sb->cfga_group_id = 0x18; - sb->cfga_channels = 0x20; - sb->cfga_sample_rate = 0x24; - sb->cfga_num_samples = 0x30; /* may be null */ - sb->cfga_num_samples2 = 0x38; /* may be null */ - sb->cfga_stream_name = 0x40; - sb->cfga_stream_type = 0x68; - - sb->and_external_flag = 0x04; - sb->and_loop_flag = 0x10; - sb->and_group_id = 0x08; - sb->shr_group_id = 3; - - sb->cfgs_sequence_count = 0x28; - //no layers + /* no layers */ return 1; } /* Prince of Persia: Warrior Within (2004)(Xbox)-bank */ if (sb->version == 0x00120009 && sb->platform == UBI_XBOX) { - sb->section1_entry_size = 0x6c; - sb->section2_entry_size = 0x90; + config_sb_entry(sb, 0x6c, 0x90); - sb->cfga_external_flag = 0x24; - sb->cfga_group_id = 0x28; - sb->cfga_loop_flag = 0x40; - sb->cfga_num_samples = 0x44; - sb->cfga_num_samples2 = 0x4c; - sb->cfga_sample_rate = 0x58; - sb->cfga_channels = 0x60; - sb->cfga_stream_type = 0x64; /* may contain garbage */ - sb->cfga_stream_name = 0x68; + config_sb_audio_fs(sb, 0x24, 0x28, 0x40); + config_sb_audio_hs(sb, 0x60, 0x58, 0x44, 0x4c, 0x68, 0x64); /* stream_type may contain garbage */ + sb->cfga_has_internal_names = 1; - sb->has_internal_names = 1; + config_sb_sequence(sb, 0x28, 0x14); - sb->cfgs_sequence_count = 0x28; - //no layers + /* no layers */ return 1; } /* Prince of Persia: Warrior Within (2004)(GC)-bank */ if (sb->version == 0x00120009 && sb->platform == UBI_GC) { - sb->section1_entry_size = 0x6c; - sb->section2_entry_size = 0x78; + config_sb_entry(sb, 0x6c, 0x78); - sb->cfga_external_flag = 0x20; - sb->cfga_group_id = 0x24; - sb->cfga_loop_flag = 0x28; - sb->cfga_num_samples = 0x2c; - sb->cfga_num_samples2 = 0x34; - sb->cfga_sample_rate = 0x40; - sb->cfga_channels = 0x48; - sb->cfga_stream_type = 0x4c; - sb->cfga_stream_name = 0x50; + config_sb_audio_fs(sb, 0x20, 0x24, 0x28); + config_sb_audio_hs(sb, 0x48, 0x40, 0x2c, 0x34, 0x50, 0x4c); - sb->cfgs_sequence_count = 0x28; - //no layers + config_sb_sequence(sb, 0x28, 0x14); + + /* no layers */ return 1; } @@ -1596,894 +1846,334 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { /* Splinter Cell: Essentials (2006)(PSP)-map */ /* Beowulf: The Game (2007)(PSP)-map */ if (sb->version == 0x0012000C && sb->platform == UBI_PSP && !is_biadd_psp) { - sb->section1_entry_size = 0x68; - sb->section2_entry_size = 0x84; + config_sb_entry(sb, 0x68, 0x84); - sb->map_version = 2; + config_sb_audio_fs(sb, 0x24, 0x2c, 0x28); + config_sb_audio_hs(sb, 0x4c, 0x44, 0x30, 0x38, 0x54, 0x50); + sb->cfga_has_internal_names = 1; - sb->cfga_external_flag = 0x24; - sb->cfga_loop_flag = 0x28; - sb->cfga_group_id = 0x2c; - sb->cfga_num_samples = 0x30; - sb->cfga_num_samples2 = 0x38; - sb->cfga_sample_rate = 0x44; - sb->cfga_channels = 0x4c; - sb->cfga_stream_type = 0x50; - sb->cfga_stream_name = 0x54; + config_sb_sequence(sb, 0x28, 0x14); - sb->has_internal_names = 1; - //has layers 06 (SC:E only) + config_sb_layer_hs(sb, 0x1c, 0x60, 0x64, 0x30); + config_sb_layer_sh(sb, 0x18, 0x00, 0x08, 0x0c, 0x14); return 1; } - //todo some .sb have bad external stream offsets (but not all, maybe unused garbage?) - /* Brothers in Arms - D-Day (2006)(PSP)-bank */ + //todo some .sbX have bad external stream offsets, but not all (ex. offset 0xE3641 but should be 0x0A26) + /* Brothers in Arms: D-Day (2006)(PSP)-bank */ if (sb->version == 0x0012000C && sb->platform == UBI_PSP && is_biadd_psp) { - sb->section1_entry_size = 0x80; - sb->section2_entry_size = 0x94; + config_sb_entry(sb, 0x80, 0x94); - sb->cfga_external_flag = 0x24; - sb->cfga_loop_flag = 0x28; - sb->cfga_group_id = 0x2c; - sb->cfga_num_samples = 0x30; - sb->cfga_num_samples2 = 0x38; - sb->cfga_sample_rate = 0x44; - sb->cfga_channels = 0x4c; - sb->cfga_stream_type = 0x50; - sb->cfga_stream_name = 0x54; + config_sb_audio_fs(sb, 0x24, 0x2c, 0x28); + config_sb_audio_hs(sb, 0x4c, 0x44, 0x30, 0x38, 0x54, 0x50); + sb->cfga_has_internal_names = 1; - sb->has_internal_names = 1; + /* no sequences */ + + /* no layers */ return 1; } /* Splinter Cell: Chaos Theory (2005)(PC)-map */ if (sb->version == 0x00120012 && sb->platform == UBI_PC) { - sb->section1_entry_size = 0x68; - sb->section2_entry_size = 0x60; + config_sb_entry(sb, 0x68, 0x60); - sb->map_version = 2; + config_sb_audio_fs(sb, 0x24, 0x2c, 0x28); + config_sb_audio_he(sb, 0x4c, 0x44, 0x30, 0x38, 0x54, 0x50); - sb->cfga_external_flag = 0x24; - //sb->cfga_group_id = 0x28; - //sb->cfga_loop_flag = 0x2c; //todo test - sb->cfga_num_samples = 0x30; - sb->cfga_num_samples2 = 0x38; - sb->cfga_sample_rate = 0x44; - sb->cfga_channels = 0x4c; - sb->cfga_stream_type = 0x50; - sb->cfga_extra_name = 0x54; + config_sb_sequence(sb, 0x28, 0x14); + /* no layers */ return 1; } /* Myst IV: Revelation (2005)(PC)-bank */ /* Splinter Cell: Chaos Theory (2005)(Xbox)-map */ if (sb->version == 0x00120012 && sb->platform == UBI_XBOX) { - sb->section1_entry_size = 0x48; - sb->section2_entry_size = 0x4c; + config_sb_entry(sb, 0x48, 0x4c); - sb->map_version = 2; + config_sb_audio_fb(sb, 0x18, (1 << 3), (1 << 4), (1 << 10)); + config_sb_audio_he(sb, 0x38, 0x30, 0x1c, 0x24, 0x40, 0x3c); - sb->cfga_external_flag = 0x18; - sb->cfga_group_id = 0x18; - sb->cfga_loop_flag = 0x18; - sb->cfga_num_samples = 0x1c; - sb->cfga_num_samples2 = 0x24; - sb->cfga_sample_rate = 0x30; - sb->cfga_channels = 0x38; - sb->cfga_stream_type = 0x3c; - sb->cfga_extra_name = 0x40; + config_sb_sequence(sb, 0x28, 0x10); - sb->and_external_flag = 0x0008; - sb->and_loop_flag = 0x0400; - sb->and_group_id = 0x0010; - sb->shr_group_id = 4; - //no layers + /* no layers */ return 1; } /* Splinter Cell 3D (2011)(3DS)-map */ if (sb->version == 0x00130001 && sb->platform == UBI_3DS) { - sb->section1_entry_size = 0x48; - sb->section2_entry_size = 0x4c; + config_sb_entry(sb, 0x48, 0x4c); - sb->map_version = 2; + config_sb_audio_fb(sb, 0x18, (1 << 2), (1 << 3), (1 << 4)); + config_sb_audio_he(sb, 0x38, 0x30, 0x1c, 0x24, 0x40, 0x3c); - sb->cfga_external_flag = 0x18; - sb->cfga_loop_flag = 0x18; - sb->cfga_group_id = 0x18; - sb->cfga_num_samples = 0x1c; - sb->cfga_num_samples2 = 0x24; - sb->cfga_sample_rate = 0x30; - sb->cfga_channels = 0x38; - sb->cfga_stream_type = 0x3c; - sb->cfga_extra_name = 0x40; + config_sb_sequence(sb, 0x28, 0x10); - sb->and_external_flag = 0x04; - sb->and_loop_flag = 0x10; - sb->and_group_id = 0x08; - sb->shr_group_id = 3; - //has layer 06 + config_sb_layer_he(sb, 0x1c, 0x28, 0x30, 0x34); + config_sb_layer_sh(sb, 0x18, 0x00, 0x08, 0x0c, 0x14); return 1; } /* Prince of Persia: The Two Thrones (2005)(PC)-bank */ if (sb->version == 0x00150000 && sb->platform == UBI_PC) { - sb->section1_entry_size = 0x68; - sb->section2_entry_size = 0x78; + config_sb_entry(sb, 0x68, 0x78); - sb->cfga_external_flag = 0x2c; - sb->cfga_loop_flag = 0x30; - sb->cfga_group_id = 0x34; - sb->cfga_num_samples = 0x40; - sb->cfga_num_samples2 = 0x48; - sb->cfga_sample_rate = 0x54; - sb->cfga_channels = 0x5c; - sb->cfga_stream_type = 0x60; - sb->cfga_extra_name = 0x64; + config_sb_audio_fs(sb, 0x2c, 0x34, 0x30); + config_sb_audio_he(sb, 0x5c, 0x54, 0x40, 0x48, 0x64, 0x60); - sb->cfgs_sequence_count = 0x2c; - //no layers + config_sb_sequence(sb, 0x2c, 0x14); + + /* no layers */ return 1; } /* Prince of Persia: The Two Thrones (2005)(PS2)-bank */ if (sb->version == 0x00150000 && sb->platform == UBI_PS2) { - sb->section1_entry_size = 0x48; - sb->section2_entry_size = 0x5c; + config_sb_entry(sb, 0x48, 0x5c); - sb->cfga_external_flag = 0x20; - sb->cfga_loop_flag = 0x20; - sb->cfga_group_id = 0x20; - sb->cfga_channels = 0x2c; - sb->cfga_sample_rate = 0x30; - sb->cfga_num_samples = 0x3c; - sb->cfga_num_samples2 = 0x44; - sb->cfga_extra_name = 0x4c; - sb->cfga_stream_type = 0x50; + config_sb_audio_fb(sb, 0x20, (1 << 2), (1 << 3), (1 << 4)); + config_sb_audio_he(sb, 0x2c, 0x30, 0x3c, 0x44, 0x4c, 0x50); - sb->and_external_flag = 0x04; - sb->and_loop_flag = 0x10; - sb->and_group_id = 0x08; - sb->shr_group_id = 3; + config_sb_sequence(sb, 0x2c, 0x10); - sb->cfgs_sequence_count = 0x2c; - //no layers + /* no layers */ return 1; } - /* Prince of Persia: The Two Thrones (2005)(Xbox)-bank */ - /* Far Cry Instincts (2005)(Xbox)-bank */ - if (sb->version == 0x00150000 && sb->platform == UBI_XBOX) { - sb->section1_entry_size = 0x48; - sb->section2_entry_size = 0x58; + /* Prince of Persia: The Two Thrones (2005)(Xbox)-bank 0x00150000 */ + /* Far Cry Instincts (2005)(Xbox)-bank 0x00150000 */ + /* Splinter Cell: Double Agent (2006)(Xbox)-map 0x00160002 */ + /* Far cry Instincts: Evolution (2006)(Xbox)-bank 0x00170000 */ + if ((sb->version == 0x00150000 && sb->platform == UBI_XBOX) || + (sb->version == 0x00160002 && sb->platform == UBI_XBOX) || + (sb->version == 0x00170000 && sb->platform == UBI_XBOX)) { + config_sb_entry(sb, 0x48, 0x58); - sb->cfga_external_flag = 0x20; - sb->cfga_loop_flag = 0x20; - sb->cfga_group_id = 0x20; - sb->cfga_num_samples = 0x28; - sb->cfga_num_samples2 = 0x30; - sb->cfga_sample_rate = 0x3c; - sb->cfga_channels = 0x44; - sb->cfga_stream_type = 0x48; - sb->cfga_extra_name = 0x4c; + config_sb_audio_fb(sb, 0x20, (1 << 3), (1 << 4), (1 << 10)); + config_sb_audio_he(sb, 0x44, 0x3c, 0x28, 0x30, 0x4c, 0x48); - sb->and_external_flag = 0x0008; - sb->and_loop_flag = 0x0400; - sb->and_group_id = 0x0010; - sb->shr_group_id = 4; + config_sb_sequence(sb, 0x2c, 0x10); - sb->cfgs_sequence_count = 0x2c; - //no layers + config_sb_layer_he(sb, 0x20, 0x2c, 0x34, 0x3c); + config_sb_layer_sh(sb, 0x30, 0x00, 0x08, 0x0c, 0x14); return 1; } - /* Prince of Persia: The Two Thrones (2005)(GC)-bank */ - if (sb->version == 0x00150000 && sb->platform == UBI_GC) { - sb->section1_entry_size = 0x68; - sb->section2_entry_size = 0x6c; + /* Prince of Persia: The Two Thrones (2005)(GC)-bank 0x00150000 */ + /* Splinter Cell: Double Agent (2006)(GC)-map 0x00160002 */ + if ((sb->version == 0x00150000 && sb->platform == UBI_GC) || + (sb->version == 0x00160002 && sb->platform == UBI_GC)) { + config_sb_entry(sb, 0x68, 0x6c); - sb->cfga_external_flag = 0x28; - //sb->cfga_group_id = 0x2c; /* assumed */ - sb->cfga_loop_flag = 0x30; - sb->cfga_num_samples = 0x3c; - sb->cfga_num_samples2 = 0x44; - sb->cfga_sample_rate = 0x50; - sb->cfga_channels = 0x58; - sb->cfga_stream_type = 0x5c; - sb->cfga_extra_name = 0x60; + config_sb_audio_fs(sb, 0x28, 0x2c, 0x30); + config_sb_audio_he(sb, 0x58, 0x50, 0x3c, 0x44, 0x60, 0x5c); - sb->cfgs_sequence_count = 0x2c; - //no layers + config_sb_sequence(sb, 0x2c, 0x14); + + config_sb_layer_he(sb, 0x20, 0x38, 0x40, 0x48); + config_sb_layer_sh(sb, 0x30, 0x00, 0x08, 0x0c, 0x14); return 1; } - /* Splinter Cell: Double Agent (2006)(Xbox)-map */ - if (sb->version == 0x00160002 && sb->platform == UBI_XBOX) { - sb->section1_entry_size = 0x48; - sb->section2_entry_size = 0x58; - - sb->map_version = 3; - - sb->cfga_external_flag = 0x20; - sb->cfga_group_id = 0x20; - sb->cfga_loop_flag = 0x20; - sb->cfga_num_samples = 0x28; - sb->cfga_num_samples2 = 0x30; - sb->cfga_sample_rate = 0x3c; - sb->cfga_channels = 0x44; - sb->cfga_stream_type = 0x48; - sb->cfga_extra_name = 0x4c; - - sb->and_external_flag = 0x0008; - sb->and_loop_flag = 0x0400; - sb->and_group_id = 0x0010; - sb->shr_group_id = 4; - //no layers - return 1; - } - - /* Splinter Cell: Double Agent (2006)(GC)-map */ - if (sb->version == 0x00160002 && sb->platform == UBI_GC) { - sb->section1_entry_size = 0x68; - sb->section2_entry_size = 0x6c; - - sb->map_version = 3; - - sb->cfga_external_flag = 0x28; - sb->cfga_group_id = 0x2c; - sb->cfga_loop_flag = 0x30; - sb->cfga_num_samples = 0x3c; - sb->cfga_num_samples2 = 0x44; - sb->cfga_sample_rate = 0x50; - sb->cfga_channels = 0x58; - sb->cfga_stream_type = 0x5c; - sb->cfga_extra_name = 0x60; - - return 1; - } - - /* Splinter Cell: Double Agent (2006)(PS2)-map */ - if (sb->version == 0x00160002 && sb->platform == UBI_PS2) { - sb->section1_entry_size = 0x48; - sb->section2_entry_size = 0x54; - - sb->map_version = 3; - - sb->cfga_external_flag = 0x20; - sb->cfga_loop_flag = 0x20; - sb->cfga_group_id = 0x20; - sb->cfga_channels = 0x28; - sb->cfga_sample_rate = 0x2c; - sb->cfga_num_samples = 0x34; - sb->cfga_num_samples2 = 0x38; - sb->cfga_extra_name = 0x44; - sb->cfga_stream_type = 0x48; - - sb->and_external_flag = 0x04; - sb->and_loop_flag = 0x10; - sb->and_group_id = 0x08; - sb->shr_group_id = 3; - - return 1; - } - - /* Far cry Instincts: Evolution (2006)(Xbox)-bank */ - if (sb->version == 0x00170000 && sb->platform == UBI_XBOX) { - sb->section1_entry_size = 0x48; - sb->section2_entry_size = 0x58; - - sb->cfga_external_flag = 0x20; - sb->cfga_group_id = 0x20; - sb->cfga_loop_flag = 0x20; - sb->cfga_num_samples = 0x28; - sb->cfga_num_samples2 = 0x30; - sb->cfga_sample_rate = 0x3c; - sb->cfga_channels = 0x44; - sb->cfga_stream_type = 0x48; - sb->cfga_extra_name = 0x4c; - - sb->and_external_flag = 0x0008; - sb->and_loop_flag = 0x0400; - sb->and_group_id = 0x0010; - sb->shr_group_id = 4; - return 1; - } - - /* Open Season (2005)(PS2)-map [0x00180003] */ - /* Open Season (2005)(PSP)-map [0x00180003] */ - /* Prince of Persia: Rival Swords (2007)(PSP)-bank [0x00180005] */ - /* Rainbow Six Vegas (2007)(PSP)-bank [0x00180006] */ - /* Star Wars: Lethal Alliance (2006)(PSP)-map [0x00180007] */ - if ((sb->version == 0x00180003 && sb->platform == UBI_PS2) || + //todo Open Season (PSP) uses sequence with type 0x08 (silence?) + //todo Rainbow Six Vegas (PSP) has layers with different sample rates (but 2nd layer is silent, can be ignored) + /* Splinter Cell: Double Agent (2006)(PS2)-map 0x00160002 */ + /* Open Season (2005)(PS2)-map 0x00180003 */ + /* Open Season (2005)(PSP)-map 0x00180003 */ + /* Shaun White Snowboarding (2008)(PS2)-map 0x00180003 */ + /* Prince of Persia: Rival Swords (2007)(PSP)-bank 0x00180005 */ + /* Rainbow Six Vegas (2007)(PSP)-bank 0x00180006 */ + /* Star Wars: Lethal Alliance (2006)(PSP)-map 0x00180007 */ + if ((sb->version == 0x00160002 && sb->platform == UBI_PS2) || + (sb->version == 0x00180003 && sb->platform == UBI_PS2) || (sb->version == 0x00180003 && sb->platform == UBI_PSP) || (sb->version == 0x00180005 && sb->platform == UBI_PSP) || (sb->version == 0x00180006 && sb->platform == UBI_PSP) || (sb->version == 0x00180007 && sb->platform == UBI_PSP)) { - sb->section1_entry_size = 0x48; - sb->section2_entry_size = 0x54; + config_sb_entry(sb, 0x48, 0x54); - sb->map_version = 3; + config_sb_audio_fb(sb, 0x20, (1 << 2), (1 << 3), (1 << 4)); + config_sb_audio_he(sb, 0x28, 0x2c, 0x34, 0x3c, 0x44, 0x48); - sb->cfga_external_flag = 0x20; - sb->cfga_loop_flag = 0x20; - sb->cfga_group_id = 0x20; - sb->cfga_channels = 0x28; - sb->cfga_sample_rate = 0x2c; - sb->cfga_num_samples = 0x34; - sb->cfga_num_samples2 = 0x3c; - sb->cfga_extra_name = 0x44; - sb->cfga_stream_type = 0x48; - - sb->and_external_flag = 0x04; - sb->and_loop_flag = 0x10; - sb->and_group_id = 0x08; - sb->shr_group_id = 3; - //has layer 06 - return 1; - } - - /* Red Steel (2006)(Wii)-bank */ - if (sb->version == 0x00180006 && sb->platform == UBI_WII) { - sb->section1_entry_size = 0x68; - sb->section2_entry_size = 0x6c; - - sb->cfga_external_flag = 0x28; - //sb->cfga_group_id = 0x2c; /* assumed */ - sb->cfga_loop_flag = 0x30; - sb->cfga_num_samples = 0x3c; - sb->cfga_num_samples2 = 0x44; - sb->cfga_sample_rate = 0x50; - sb->cfga_channels = 0x58; - sb->cfga_stream_type = 0x5c; - sb->cfga_extra_name = 0x60; + config_sb_sequence(sb, 0x2c, 0x10); + config_sb_layer_he(sb, 0x20, 0x2c, 0x30, 0x38); + config_sb_layer_sh(sb, 0x34, 0x00, 0x08, 0x0c, 0x14); return 1; } /* Splinter Cell: Double Agent (2006)(PC)-map */ if (sb->version == 0x00180006 && sb->platform == UBI_PC) { - sb->section1_entry_size = 0x68; - sb->section2_entry_size = 0x7c; + config_sb_entry(sb, 0x68, 0x7c); - sb->map_version = 3; + config_sb_audio_fs(sb, 0x2c, 0x34, 0x30); + config_sb_audio_he(sb, 0x5c, 0x54, 0x40, 0x48, 0x64, 0x60); - sb->cfga_external_flag = 0x2c; - //sb->cfga_loop_flag = 0x30; //todo test - sb->cfga_group_id = 0x34; - sb->cfga_channels = 0x5c; - sb->cfga_sample_rate = 0x54; - sb->cfga_num_samples = 0x40; - sb->cfga_num_samples2 = 0x48; - sb->cfga_stream_type = 0x60; - sb->cfga_extra_name = 0x64; + /* no sequences */ + config_sb_layer_he(sb, 0x20, 0x38, 0x3c, 0x44); + config_sb_layer_sh(sb, 0x34, 0x00, 0x08, 0x0c, 0x14); return 1; } /* Splinter Cell: Double Agent (2006)(X360)-map */ if (sb->version == 0x00180006 && sb->platform == UBI_X360) { - sb->section1_entry_size = 0x68; - sb->section2_entry_size = 0x78; + config_sb_entry(sb, 0x68, 0x78); - sb->map_version = 3; + config_sb_audio_fs(sb, 0x2c, 0x30, 0x34); + config_sb_audio_he(sb, 0x5c, 0x54, 0x40, 0x48, 0x64, 0x60); + sb->cfga_xma_offset = 0x70; - sb->cfga_external_flag = 0x2c; - sb->cfga_group_id = 0x30; - sb->cfga_loop_flag = 0x34; - sb->cfga_channels = 0x5c; - sb->cfga_sample_rate = 0x54; - sb->cfga_num_samples = 0x40; - sb->cfga_num_samples2 = 0x48; - sb->cfga_stream_type = 0x60; - sb->cfga_extra_name = 0x64; - sb->cfga_xma_offset = 0x70; + /* no sequences */ - //has layer 06 + config_sb_layer_he(sb, 0x20, 0x38, 0x3c, 0x44); + config_sb_layer_sh(sb, 0x34, 0x00, 0x08, 0x0c, 0x14); return 1; } - /* TMNT (2007)(PSP)-map */ - if (sb->version == 0x00190001 && sb->platform == UBI_PSP) { - sb->section1_entry_size = 0x48; - sb->section2_entry_size = 0x58; + /* Red Steel (2006)(Wii)-bank 0x00180006 */ + if (sb->version == 0x00180006 && sb->platform == UBI_WII) { + config_sb_entry(sb, 0x68, 0x6c); - sb->map_version = 3; + config_sb_audio_fs(sb, 0x28, 0x2c, 0x30); + config_sb_audio_he(sb, 0x58, 0x50, 0x3c, 0x44, 0x60, 0x5c); - sb->cfga_external_flag = 0x20; - sb->cfga_loop_flag = 0x20; - //sb->cfga_group_id = 0x20; /* assumed */ - sb->cfga_channels = 0x28; - sb->cfga_sample_rate = 0x2c; - sb->cfga_num_samples = 0x34; - sb->cfga_num_samples2 = 0x3c; - sb->cfga_stream_type = 0x48; - sb->cfga_extra_name = 0x44; + config_sb_sequence(sb, 0x2c, 0x14); - sb->and_external_flag = 0x04; - sb->and_loop_flag = 0x10; - //has layer 06 + config_sb_layer_he(sb, 0x20, 0x38, 0x3c, 0x44); + config_sb_layer_sh(sb, 0x34, 0x00, 0x08, 0x0c, 0x14); return 1; } + /* TMNT (2007)(PSP)-map 0x00190001 */ + /* Surf's Up (2007)(PSP)-map 0x00190005 */ + if ((sb->version == 0x00190001 && sb->platform == UBI_PSP) || + (sb->version == 0x00190005 && sb->platform == UBI_PSP)) { + config_sb_entry(sb, 0x48, 0x58); + + config_sb_audio_fb(sb, 0x20, (1 << 2), (1 << 3), (1 << 4)); /* assumed group_flag */ + config_sb_audio_he(sb, 0x28, 0x2c, 0x34, 0x3c, 0x44, 0x48); + + config_sb_sequence(sb, 0x2c, 0x10); + + config_sb_layer_he(sb, 0x20, 0x2c, 0x30, 0x38); + config_sb_layer_sh(sb, 0x30, 0x00, 0x04, 0x08, 0x10); + return 1; + } + + //todo one sequence is using type 0x08 (only) with 5.0: maybe type_silence? /* TMNT (2007)(GC)-bank */ if (sb->version == 0x00190002 && sb->platform == UBI_GC) { - sb->section1_entry_size = 0x68; - sb->section2_entry_size = 0x6c; + config_sb_entry(sb, 0x68, 0x6c); - sb->cfga_external_flag = 0x28; - //sb->cfga_group_id = 0x2c; /* assumed */ - sb->cfga_loop_flag = 0x30; - sb->cfga_channels = 0x3c; - sb->cfga_sample_rate = 0x40; - sb->cfga_num_samples = 0x48; - sb->cfga_num_samples2 = 0x50; - sb->cfga_extra_name = 0x58; - sb->cfga_stream_type = 0x5c; + config_sb_audio_fs(sb, 0x28, 0x2c, 0x30); /* assumed groud_id */ + config_sb_audio_he(sb, 0x3c, 0x40, 0x48, 0x50, 0x58, 0x5c); - return 1; - } - - /* TMNT (2007)(PC)-bank */ - if (sb->version == 0x00190002 && sb->platform == UBI_PC) { - sb->section1_entry_size = 0x68; - sb->section2_entry_size = 0x74; - - sb->cfga_external_flag = 0x28; - sb->cfga_group_id = 0x2c; - sb->cfga_loop_flag = 0x30; - sb->cfga_channels = 0x3c; - sb->cfga_sample_rate = 0x40; - sb->cfga_num_samples = 0x48; - sb->cfga_num_samples2 = 0x50; - sb->cfga_extra_name = 0x58; - sb->cfga_stream_type = 0x5c; + config_sb_sequence(sb, 0x2c, 0x14); + config_sb_layer_he(sb, 0x20, 0x34, 0x38, 0x40); + config_sb_layer_sh(sb, 0x30, 0x00, 0x04, 0x08, 0x10); return 1; } + //todo one sequence is using type 0x08 (only) with 5.0: maybe type_silence? /* TMNT (2007)(PS2)-bank */ if (sb->version == 0x00190002 && sb->platform == UBI_PS2) { - sb->section1_entry_size = 0x48; - sb->section2_entry_size = 0x5c; + config_sb_entry(sb, 0x48, 0x5c); - sb->cfga_external_flag = 0x20; - sb->cfga_loop_flag = 0x20; - //sb->cfga_group_id = 0x20; /* assumed */ - sb->cfga_channels = 0x28; - sb->cfga_sample_rate = 0x2c; - sb->cfga_num_samples = 0x34; - sb->cfga_num_samples2 = 0x3c; - sb->cfga_extra_name = 0x44; - sb->cfga_stream_type = 0x48; + config_sb_audio_fb(sb, 0x20, (1 << 2), (1 << 3), (1 << 4)); /* assumed group_flag */ + config_sb_audio_he(sb, 0x28, 0x2c, 0x34, 0x3c, 0x44, 0x48); - sb->and_external_flag = 0x04; - sb->and_loop_flag = 0x10; - //has layer 06 + config_sb_sequence(sb, 0x2c, 0x10); + + config_sb_layer_he(sb, 0x20, 0x2c, 0x30, 0x38); + config_sb_layer_sh(sb, 0x30, 0x00, 0x04, 0x08, 0x10); return 1; } - /* TMNT (2007)(X360)-bank */ - if (sb->version == 0x00190002 && sb->platform == UBI_X360) { - sb->section1_entry_size = 0x68; - sb->section2_entry_size = 0x70; + /* TMNT (2007)(X360)-bank 0x00190002 */ + /* Prince of Persia: Rival Swords (2007)(Wii)-bank 0x00190003 */ + /* Rainbow Six Vegas (2007)(PS3)-bank 0x00190005 */ + /* Surf's Up (2007)(PS3)-bank 0x00190005 */ + /* Surf's Up (2007)(X360)-bank 0x00190005 */ + /* Splinter Cell: Double Agent (2007)(PS3)-map 0x00190005 */ + if ((sb->version == 0x00190002 && sb->platform == UBI_X360) || + (sb->version == 0x00190003 && sb->platform == UBI_WII) || + (sb->version == 0x00190005 && sb->platform == UBI_PS3) || + (sb->version == 0x00190005 && sb->platform == UBI_X360)) { + config_sb_entry(sb, 0x68, 0x70); - sb->cfga_external_flag = 0x28; - //sb->cfga_group_id = 0x2c; /* assumed */ - sb->cfga_loop_flag = 0x30; - sb->cfga_channels = 0x3c; - sb->cfga_sample_rate = 0x40; - sb->cfga_num_samples = 0x48; - sb->cfga_num_samples2 = 0x50; - sb->cfga_extra_name = 0x58; - sb->cfga_stream_type = 0x5c; - sb->cfga_xma_offset = 0x6c; + config_sb_audio_fs(sb, 0x28, 0x2c, 0x30); + config_sb_audio_he(sb, 0x3c, 0x40, 0x48, 0x50, 0x58, 0x5c); + sb->cfga_xma_offset = 0x6c; + config_sb_sequence(sb, 0x2c, 0x14); + + config_sb_layer_he(sb, 0x20, 0x34, 0x38, 0x40); + config_sb_layer_sh(sb, 0x30, 0x00, 0x04, 0x08, 0x10); return 1; } - /* Prince of Persia: Rival Swords (2007)(Wii)-bank */ - if (sb->version == 0x00190003 && sb->platform == UBI_WII) { - sb->section1_entry_size = 0x68; - sb->section2_entry_size = 0x70; + /* TMNT (2007)(PC)-bank 0x00190002 */ + /* Surf's Up (2007)(PC)-bank 0x00190005 */ + if ((sb->version == 0x00190002 && sb->platform == UBI_PC) || + (sb->version == 0x00190005 && sb->platform == UBI_PC)) { + config_sb_entry(sb, 0x68, 0x74); - sb->cfga_external_flag = 0x28; - //sb->cfga_group_id = 0x2c; /* assumed */ - sb->cfga_loop_flag = 0x30; - sb->cfga_channels = 0x3c; - sb->cfga_sample_rate = 0x40; - sb->cfga_num_samples = 0x48; - sb->cfga_num_samples2 = 0x50; - sb->cfga_extra_name = 0x58; - sb->cfga_stream_type = 0x5c; + config_sb_audio_fs(sb, 0x28, 0x2c, 0x30); + config_sb_audio_he(sb, 0x3c, 0x40, 0x48, 0x50, 0x58, 0x5c); - //has layer 06 (TMNT) + config_sb_sequence(sb, 0x2c, 0x14); + + config_sb_layer_he(sb, 0x20, 0x34, 0x38, 0x40); + config_sb_layer_sh(sb, 0x30, 0x00, 0x04, 0x08, 0x10); return 1; } - /* Surf's Up (2007)(PC)-bank */ - if (sb->version == 0x00190005 && sb->platform == UBI_PC) { - sb->section1_entry_size = 0x68; - sb->section2_entry_size = 0x74; + /* Rainbow Six Vegas 2 (2008)(PS3)-map */ + if (sb->version == 0x001c0000 && sb->platform == UBI_PS3) { + config_sb_entry(sb, 0x64, 0x7c); - sb->cfga_external_flag = 0x28; - //sb->cfga_group_id = 0x2c; /* assumed */ - sb->cfga_loop_flag = 0x30; - sb->cfga_channels = 0x3c; - sb->cfga_sample_rate = 0x40; - sb->cfga_num_samples = 0x48; - sb->cfga_num_samples2 = 0x50; - sb->cfga_extra_name = 0x58; - sb->cfga_stream_type = 0x5c; + config_sb_audio_fs(sb, 0x28, 0x30, 0x34); + config_sb_audio_he(sb, 0x44, 0x48, 0x50, 0x58, 0x60, 0x64); - return 1; - } + /* no sequences */ - /* Surf's Up (2007)(PS3)-bank */ - /* Splinter Cell: Double Agent (2007)(PS3)-map */ - if (sb->version == 0x00190005 && sb->platform == UBI_PS3) { - sb->section1_entry_size = 0x68; - sb->section2_entry_size = 0x70; - - sb->map_version = 3; - - sb->cfga_external_flag = 0x28; - sb->cfga_group_id = 0x2c; - sb->cfga_loop_flag = 0x30; - sb->cfga_channels = 0x3c; - sb->cfga_sample_rate = 0x40; - sb->cfga_num_samples = 0x48; - sb->cfga_num_samples2 = 0x50; - sb->cfga_extra_name = 0x58; - sb->cfga_stream_type = 0x5c; - - return 1; - } - - /* Surf's Up (2007)(X360)-bank */ - if (sb->version == 0x00190005 && sb->platform == UBI_X360) { - sb->section1_entry_size = 0x68; - sb->section2_entry_size = 0x70; - - sb->cfga_external_flag = 0x28; - //sb->cfga_group_id = 0x2c; /* assumed */ - sb->cfga_loop_flag = 0x30; - sb->cfga_channels = 0x3c; - sb->cfga_sample_rate = 0x40; - sb->cfga_num_samples = 0x48; - sb->cfga_num_samples2 = 0x50; - sb->cfga_stream_type = 0x5c; - sb->cfga_extra_name = 0x58; - sb->cfga_xma_offset = 0x6c; - - return 1; - } - - /* Surf's Up (2007)(PSP)-map */ - if (sb->version == 0x00190005 && sb->platform == UBI_PSP) { - sb->section1_entry_size = 0x48; - sb->section2_entry_size = 0x58; - - sb->map_version = 3; - - sb->cfga_external_flag = 0x20; - sb->cfga_loop_flag = 0x20; - //sb->cfga_group_id = 0x20; /* assumed */ - sb->cfga_channels = 0x28; - sb->cfga_sample_rate = 0x2c; - sb->cfga_num_samples = 0x34; - sb->cfga_num_samples2 = 0x3c; - sb->cfga_extra_name = 0x44; - sb->cfga_stream_type = 0x48; - - sb->and_external_flag = 0x04; - sb->and_loop_flag = 0x10; - //no layers + config_sb_layer_he(sb, 0x20, 0x44, 0x48, 0x54); + config_sb_layer_sh(sb, 0x30, 0x00, 0x04, 0x08, 0x10); return 1; } /* Michael Jackson: The Experience (2010)(PSP)-map */ if (sb->version == 0x001d0000 && sb->platform == UBI_PSP) { - sb->section1_entry_size = 0x40; - sb->section2_entry_size = 0x60; + config_sb_entry(sb, 0x40, 0x60); - sb->map_version = 3; + config_sb_audio_fb(sb, 0x20, (1 << 2), (1 << 3), (1 << 5)); /* assumed group_flag */ + config_sb_audio_he(sb, 0x28, 0x30, 0x38, 0x40, 0x48, 0x4c); - sb->cfga_external_flag = 0x20; - sb->cfga_loop_flag = 0x20; - //sb->cfga_group_id = 0x20; /* assumed */ - sb->cfga_channels = 0x28; - sb->cfga_sample_rate = 0x30; - sb->cfga_num_samples = 0x38; - sb->cfga_num_samples2 = 0x40; - sb->cfga_extra_name = 0x48; - sb->cfga_stream_type = 0x4c; + /* no sequences */ - sb->and_external_flag = 0x04; - sb->and_loop_flag = 0x20; - //no layers + /* no layers */ return 1; } /* Splinter Cell Classic Trilogy HD (2011)(PS3)-map */ if (sb->version == 0x001d0000 && sb->platform == UBI_PS3) { - sb->section1_entry_size = 0x5c; - sb->section2_entry_size = 0x80; + config_sb_entry(sb, 0x5c, 0x80); - sb->map_version = 3; + config_sb_audio_fs(sb, 0x28, 0x30, 0x34); + config_sb_audio_he(sb, 0x44, 0x4c, 0x54, 0x5c, 0x64, 0x68); + + config_sb_sequence(sb, 0x2c, 0x14); + + config_sb_layer_he(sb, 0x20, 0x44, 0x48, 0x54); + config_sb_layer_sh(sb, 0x38, 0x00, 0x04, 0x08, 0x10); - sb->cfga_external_flag = 0x28; - sb->cfga_group_id = 0x30; - sb->cfga_loop_flag = 0x34; - sb->cfga_channels = 0x44; - sb->cfga_sample_rate = 0x4c; - sb->cfga_num_samples = 0x54; - sb->cfga_num_samples2 = 0x5c; - sb->cfga_extra_name = 0x64; - sb->cfga_stream_type = 0x68; - //has layer 06 return 1; } - VGM_LOG("UBI SB: unknown SB/SM version+platform for %08x\n", sb->version); + VGM_LOG("UBI SB: unknown SB/SM version+platform %08x\n", sb->version); return 0; } - -/* Donald Duck: Goin' Quackers (2002)(GC)-map */ -/* - type header: - * 0x1c: sample rate * layers - * 0x20: layers - * 0x30: external flag? - * 0x34: external name - * 0x44: stream offset - * 0x48: original rate * layers? - * 0x4c: stream size (not including padding) - * no samples - * - * - layer header - * - blocked data - * 0x02 see below (Ubi IMA v2 though) - */ - -/* 0x0d layer [Splinter Cell] */ -/* - type header: - * 0x18: stream offset? - * 0x20: (sample rate * layers) + 1? - * 0x24: layers/channels? - * 0x30: external flag - * 0x34: external name - * 0x5C: stream offset - * 0x64: stream size (not including padding) - * 0x78/7c: codec? - * - * - layer header at stream_offset: - * 0x00: version? (0x00000002) - * 0x04: layers - * 0x08: stream size (not including padding) - * 0x0c: size? - * 0x10: count? - * 0x14: min block size? - * - * - blocked data (unlike other layers, first block data is standard Ubi IMA headers, v3): - * 0x00: block number (from 0x01 to block_count) - * 0x04: current offset (within stream_offset) - * - per layer: - * 0x00: layer data size (varies between blocks, and one layer may have more than other, even the header) - */ - - -/* Rainbow Six 3 */ -/* Prince of Persia: Sands of Time (all) 0x000A0004 */ -/* Batman: Rise of Sin Tzu (2003)(GC)-map - 0x000A0002 */ -/* - type header (bizarrely but thankfully doesn't change between platforms): - * 0x1c: sample rate * layers - * 0x20: layers/channels? - * 0x2c: external flag? - * 0x30: external name - * 0x58: stream offset - * 0x5c: original rate * layers? - * 0x60: stream size (not including padding) - * 0x64: number of samples - * - * - layer header at stream_offset (BE on GC): - * 0x00: version? (0x00000004) - * 0x04: layers - * 0x08: stream size (not including padding) - * 0x0c: blocks count - * 0x10: block header size - * 0x14: block size - * 0x18: ? - * 0x1c: size of next section - * - per layer: - * 0x00: layer header size - * codec header per layer - * 0x00~0x20: standard Ubi IMA header (version 0x05, LE) - * - * - blocked data: - * 0x00: block number (from 0x01 to block_count) - * 0x04: current offset (within stream_offset) - * 0x08: always 0x03 - * - per layer: - * 0x00: layer data size (varies between blocks, and one layer may have more than other) - */ - -/* Splinter Cell: Essentials (PSP)-map 0x0012000C */ -/* - type header: - * 0x08: header extra offset - * 0x1c: layers - * 0x28: external flag? - * 0x2c: external flag? - * 0x30: stream name - * 0x5c: sample rate * layers - * 0x60: stream size - * 0x64: stream offset? - * - * - in header extra offset (style 0x10) - * 0x00: sample rate - * 0x04: 16? - * 0x08: channels? - * 0x0c: codec? - * - * - layer header at stream_offset: - * 0x00: version (0x00000007) - * 0x04: config? - * 0x08: layers - * 0x0c: stream size - * 0x10: blocks count - * 0x14: block header size - * 0x18: next block size - * - per layer: - * 0x00: approximate layer data size per block - * - per layer - * 0x00~0x0c: weird header thing? -1/0/0c... - * - * - blocked data: - * 0x00: block number (from 0x01 to block_count) - * 0x04: current offset (within stream_offset) - * 0x08: always 0x03 - * - per layer: - * 0x00: layer data size (varies between blocks, and one layer may have more than other) - */ - -/* Splinter Cell: Double Agent (2006)(X360)-map 0x00180006 */ -/* - type header: - * 0x08: header extra offset - * 0x20: layers - * 0x28: external flag? - * 0x30: external flag? - * 0x34: sample rate * layers - * 0x38: stream size - * 0x3c: stream offset - * 0x44: name extra offset - * - * - in header extra offset - * style 0x10 (codec 05 XMA), possible total size 0x34 - * - * - layer header at stream_offset: - * version 0x000C0008 same as 0x000B0008, but codec header size is 0 - * - * - blocked data: - * version same as 0x000B0008 (including mini XMA header + weird tables) - */ - -/* Splinter Cell 3D (2011)(3DS)-map 0x00130001 */ -/* - type header: - * 0x08: header extra offset - * 0x10: layers - * 0x28: stream size - * 0x30: stream offset - * 0x34: name extra offset - * - * - in header extra offset - * style 0x10 (size 0x18?) - * - * - layer header at stream_offset: - * same as 0x00000007 but weird header thing is 0x00~0x08, has header sizes after that - * - * - blocked data: - * same as 0x00000007 - */ - -/* TMNT (2007)(PS2)-bank 0x00190002 */ -/* - type header: - * 0x08: header extra offset - * 0x1c: external flag? - * 0x20: layers - * 0x28: sample rate * layers - * 0x2c: stream size - * 0x30: stream offset - * 0x38: name extra offset - * - * - in header extra offset (style 0x0c) - * 0x00: sample rate - * 0x04: channels - * 0x08: codec - * - * - layer header at stream_offset: - * 0x00: version? (0x000B0008) - * 0x04: config? (0x00/0x0e/0x0b/etc) - * 0x08: layers - * 0x0c: blocks count - * 0x10: block header size - * 0x14: size of header sizes - * 0x18: next block size - * - per layer: - * 0x00: layer header size - * - per layer (if layer header size > 0) - * 0x00~0x20: standard Ubi IMA header (version 0x05), PCM data - * - * - blocked data: - * 0x00: always 0x03 - * 0x04: next block size - * - per layer: - * 0x00: layer data size (varies between blocks, and one layer may have more than other) - */ - -/* Open Season (2005)(PS2)-map 0x00180003 */ -/* Rainbow Six Vegas (2007)(PSP)-bank 0x00180006 */ -/* Star Wars - Lethal Alliance (2006)(PSP)-map 0x00180007 */ -/* - type header: - * 0x0c: header extra offset - * 0x20: layers - * 0x2c: stream size - * 0x30: stream offset - * 0x38: name extra offset - * - * - in header extra offset - * style 0x10 (codec 03=Ubi, 01=PCM16LE in SW:LA/RS:V) - * - layer header at stream_offset: - * - blocked data: - * version 0x000B0008 - */ - -/* TMNT (2007)(PSP)-map 0x00190001 */ -/* - type header: - * 0x0c: header extra offset - * 0x20: layers - * 0x24: total channels? - * 0x28: sample rate * layers? - * 0x2c: stream size - * 0x30: stream offset - * 0x38: name extra offset - * - * - in header extra offset - * style 0x0c - * - layer header at stream_offset: - * - blocked data: - * version 0x000B0008, but codec header size is 0 - */ - -/* TMNT (2007)(GC)-bank 0x00190002 */ -/* Surf's Up (PS3)-bank 0x00190005 */ -/* - type header: - * 0x08: header extra offset - * 0x1c: external flag? - * 0x20: layers - * 0x2c: external flag? (would match header 01) - * 0x30: sample rate * layers - * 0x34: stream size - * 0x38: stream offset - * 0x40: name extra offset - * - * - in header extra offset - * style 0x0c - * - layer header at stream_offset: - * - blocked data: - * version 0x000B0008 - */ - -/* Splinter Cell Classic Trilogy HD (2011)(PS3)-map 0x001d0000 */ -/* - type header: - * 0x0c: header extra offset - * 0x20: layers - * 0x44: stream size - * 0x48: stream offset - * 0x54: name extra offset - * - * - in header extra offset - * style 0x0c - * - layer header at stream_offset: - * - blocked data: - * version 0x000B0008, but codec header size is 0 - */ diff --git a/src/meta/ubi_sb_streamfile.h b/src/meta/ubi_sb_streamfile.h new file mode 100644 index 00000000..fbaf05c1 --- /dev/null +++ b/src/meta/ubi_sb_streamfile.h @@ -0,0 +1,366 @@ +#ifndef _UBI_SB_STREAMFILE_H_ +#define _UBI_SB_STREAMFILE_H_ +#include "../streamfile.h" + + +typedef struct { + /* config */ + off_t stream_offset; + off_t stream_size; + int layer_number; + int layer_count; + int layer_max; + int big_endian; + + /* internal config */ + off_t header_next_start; /* offset to header field */ + off_t header_sizes_start; /* offset to header table */ + off_t header_data_start; /* offset to header data */ + off_t block_next_start; /* offset to block field */ + off_t block_sizes_start; /* offset to block table */ + off_t block_data_start; /* offset to block data */ + size_t header_size; /* derived */ + + /* state */ + off_t logical_offset; /* fake offset */ + off_t physical_offset; /* actual offset */ + size_t block_size; /* current size */ + size_t next_block_size; /* next size */ + size_t skip_size; /* size from block start to reach data */ + size_t data_size; /* usable size in a block */ + + size_t logical_size; +} ubi_sb_io_data; + + +static size_t ubi_sb_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, ubi_sb_io_data* data) { + int32_t(*read_32bit)(off_t, STREAMFILE*) = data->big_endian ? read_32bitBE : read_32bitLE; + size_t total_read = 0; + int i; + + + /* re-start when previous offset (can't map logical<>physical offsets) */ + if (data->logical_offset < 0 || offset < data->logical_offset) { + data->physical_offset = data->stream_offset; + data->logical_offset = 0x00; + data->data_size = 0; + + /* process header block (slightly different and data size may be 0) */ + { + data->block_size = data->header_size; + data->next_block_size = read_32bit(data->physical_offset + data->header_next_start, streamfile); + + if (data->header_sizes_start) { + data->skip_size = data->header_data_start; + for (i = 0; i < data->layer_number; i++) { + data->skip_size += read_32bit(data->physical_offset + data->header_sizes_start + i*0x04, streamfile); + } + data->data_size = read_32bit(data->physical_offset + data->header_sizes_start + data->layer_number*0x04, streamfile); + } + + if (data->data_size == 0) { + data->physical_offset += data->block_size; + } +//VGM_LOG("h=%lx, b=%x, n=%x\n", data->physical_offset, data->block_size, data->next_block_size); + } + } + + + /* read blocks */ + while (length > 0) { + + /* ignore EOF */ + if (offset < 0 || data->physical_offset >= data->stream_offset + data->stream_size) { + break; + } + + /* process new block */ + if (data->data_size == 0) { + data->block_size = data->next_block_size; + if (data->block_next_start) /* not set when fixed block size */ + data->next_block_size = read_32bit(data->physical_offset + data->block_next_start, streamfile); + + data->skip_size = data->block_data_start; + for (i = 0; i < data->layer_number; i++) { + data->skip_size += read_32bit(data->physical_offset + data->block_sizes_start + i*0x04, streamfile); + } + data->data_size = read_32bit(data->physical_offset + data->block_sizes_start + data->layer_number*0x04, streamfile); + +//VGM_LOG("o=%lx, b=%x, n=%x\n", data->physical_offset, data->block_size, data->next_block_size); + } + + /* move to next block */ + if (offset >= data->logical_offset + data->data_size) { + if (data->block_size == 0 || data->block_size == 0xFFFFFFFF) + break; + data->physical_offset += data->block_size; + data->logical_offset += data->data_size; + data->data_size = 0; + continue; + } + + /* read 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, streamfile); + + 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 ubi_sb_io_size(STREAMFILE *streamfile, ubi_sb_io_data* data) { + uint8_t buf[1]; + + if (data->logical_size) + return data->logical_size; + + /* force a fake read at max offset, to get max logical_offset (will be reset next read) */ + ubi_sb_io_read(streamfile, buf, 0x7FFFFFFF, 1, data); + data->logical_size = data->logical_offset; + + return data->logical_size; +} + + +static int ubi_sb_io_init(STREAMFILE *streamfile, ubi_sb_io_data* data) { + int32_t (*read_32bit)(off_t,STREAMFILE*) = data->big_endian ? read_32bitBE : read_32bitLE; + off_t offset = data->stream_offset; + uint32_t version; + int i; + + if (data->stream_offset + data->stream_size > get_streamfile_size(streamfile)) { + VGM_LOG("UBI SB: bad size\n"); + goto fail; + } + + /* Layers have a main header, then headered blocks with data. + * We configure stuff to unify parsing of all variations. */ + version = (uint32_t)read_32bit(offset+0x00, streamfile); + switch(version) { + case 0x00000002: /* Splinter Cell */ + /* - layer header + * 0x04: layer count + * 0x08: stream size + * 0x0c: block header size + * 0x10: block size (fixed) + * 0x14: min layer size? + * - block header + * 0x00: block number + * 0x04: block offset + * 0x08+(04*N): layer size per layer + * 0xNN: layer data per layer */ + data->layer_max = read_32bit(offset+0x04, streamfile); + + data->header_next_start = 0x10; + data->header_sizes_start = 0; + data->header_data_start = 0x18; + + data->block_next_start = 0; + data->block_sizes_start = 0x08; + data->block_data_start = 0x08 + data->layer_max*0x04; + break; + + case 0x00000004: /* Prince of Persia: Sands of Time, Batman: Rise of Sin Tzu */ + /* - layer header + * 0x04: layer count + * 0x08: stream size + * 0x0c: block count + * 0x10: block header size + * 0x14: block size (fixed) + * 0x18: min layer data? + * 0x1c: size of header sizes + * 0x20+(04*N): header size per layer + * - block header + * 0x00: block number + * 0x04: block offset + * 0x08: always 0x03 + * 0x0c+(04*N): layer size per layer + * 0xNN: layer data per layer */ + data->layer_max = read_32bit(offset+0x04, streamfile); + + data->header_next_start = 0x14; + data->header_sizes_start = 0x20; + data->header_data_start = 0x20 + data->layer_max*0x04; + + data->block_next_start = 0; + data->block_sizes_start = 0x0c; + data->block_data_start = 0x0c + data->layer_max*0x04; + break; + + case 0x00000007: /* Splinter Cell: Essentials, Splinter Cell 3D */ + /* - layer header + * 0x04: config? + * 0x08: layer count + * 0x0c: stream size + * 0x10: block count + * 0x14: block header size + * 0x18: block size (fixed) + * 0x1c+(04*8): min layer data? for 8 layers (-1 after layer count) + * 0x3c: size of header sizes + * 0x40+(04*N): header size per layer + * 0xNN: header data per layer + * - block header + * 0x00: block number + * 0x04: block offset + * 0x08: always 0x03 + * 0x0c+(04*N): layer size per layer + * 0xNN: layer data per layer */ + data->layer_max = read_32bit(offset+0x08, streamfile); + + data->header_next_start = 0x18; + data->header_sizes_start = 0x40; + data->header_data_start = 0x40 + data->layer_max*0x04; + + data->block_next_start = 0; + data->block_sizes_start = 0x0c; + data->block_data_start = 0x0c + data->layer_max*0x04; + break; + + case 0x00040008: /* Assassin's Creed */ + case 0x000B0008: /* Open Season, Surf's Up, TMNT, Splinter Cell HD */ + case 0x000C0008: /* Splinter Cell: Double Agent */ + case 0x00100008: /* Rainbow Six 2 */ + /* - layer header + * 0x04: config? + * 0x08: layer count + * 0x0c: blocks count + * 0x10: block header size + * 0x14: size of header sizes/data + * 0x18: next block size + * 0x1c+(04*N): layer header size + * 0xNN: header data per layer + * - block header: + * 0x00: always 0x03 + * 0x04: next block size + * 0x08+(04*N): layer size per layer + * 0xNN: layer data per layer */ + data->layer_max = read_32bit(offset+0x08, streamfile); + + data->header_next_start = 0x18; + data->header_sizes_start = 0x1c; + data->header_data_start = 0x1c + data->layer_max*0x04; + + data->block_next_start = 0x04; + data->block_sizes_start = 0x08; + data->block_data_start = 0x08 + data->layer_max*0x04; + break; + + case 0x00100009: /* Splinter Cell: Pandora Tomorrow HD, Prince of Persia 2008, Scott Pilgrim */ + /* - layer header + * 0x04: config? + * 0x08: layer count + * 0x0c: blocks count + * 0x10: block header size + * 0x14: size of header sizes/data + * 0x18: next block size + * 0x1c+(04*10): usable size per layer + * 0x5c+(04*N): layer header size + * 0xNN: header data per layer + * - block header: + * 0x00: always 0x03 + * 0x04: next block size + * 0x08+(04*N): layer size per layer + * 0xNN: layer data per layer */ + data->layer_max = read_32bit(offset+0x08, streamfile); + + data->header_next_start = 0x18; + data->header_sizes_start = 0x5c; + data->header_data_start = 0x5c + data->layer_max*0x04; + + data->block_next_start = 0x04; + data->block_sizes_start = 0x08; + data->block_data_start = 0x08 + data->layer_max*0x04; + break; + + default: + VGM_LOG("UBI SB: unknown layer header %08x\n", version); + goto fail; + } + + /* get base size to simplify later parsing */ + data->header_size = data->header_data_start; + if (data->header_sizes_start) { + for (i = 0; i < data->layer_max; i++) { + data->header_size += read_32bit(offset + data->header_sizes_start + i*0x04, streamfile); + } + } + + /* force read header block */ + data->logical_offset = -1; + + /* just in case some headers may use less layers that stream has */ + VGM_ASSERT(data->layer_count != data->layer_max, "UBI SB: non-matching layer counts\n"); + if (data->layer_count > data->layer_max) { + VGM_LOG("UBI SB: layer count bigger than layer max\n"); + goto fail; + } + + /* Common layer quirks: + * - layer format depends on its own version and not on platform or DARE engine version + * - codec header may be in the layer header, or in the first block + * - stream size doesn't include padding + * - block number goes from 1 to block_count + * - block offset is relative to layer start + * - blocks data size varies between blocks and between layers in the same block + * - "config?" is a small value that varies between streams of the same game + * - next block size is 0 at last block + * - both Ubi SB and Ubi BAO use same-version layers + */ + + return 1; +fail: + return 0; +} + + +/* Handles deinterleaving of Ubisoft's headered+blocked 'multitrack' streams */ +static STREAMFILE* setup_ubi_sb_streamfile(STREAMFILE *streamFile, off_t stream_offset, size_t stream_size, int layer_number, int layer_count, int big_endian) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + ubi_sb_io_data io_data = {0}; + size_t io_data_size = sizeof(ubi_sb_io_data); + + io_data.stream_offset = stream_offset; + io_data.stream_size = stream_size; + io_data.layer_number = layer_number; + io_data.layer_count = layer_count; + io_data.big_endian = big_endian; + + if (!ubi_sb_io_init(streamFile, &io_data)) + goto fail; + + /* 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, ubi_sb_io_read,ubi_sb_io_size); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_buffer_streamfile(new_streamFile,0); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} + +#endif /* _UBI_SB_STREAMFILE_H_ */ From da9f9a5572dcff5d0f0e4169102d9f595b14f26a Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 19 Jan 2019 20:04:38 +0100 Subject: [PATCH 03/10] Fix non-initialized var --- src/meta/ea_eaac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/meta/ea_eaac.c b/src/meta/ea_eaac.c index 350968f4..958846de 100644 --- a/src/meta/ea_eaac.c +++ b/src/meta/ea_eaac.c @@ -577,7 +577,7 @@ VGMSTREAM * init_vgmstream_ea_sbr_harmony(STREAMFILE *streamFile) { off_t data_offset, table_offset, dset_offset, base_offset, sound_table_offset, sound_offset, header_offset, start_offset; STREAMFILE *sbsFile = NULL, *streamData = NULL; VGMSTREAM *vgmstream = NULL; - int target_stream = streamFile->stream_index, total_sounds, local_target, is_streamed; + int target_stream = streamFile->stream_index, total_sounds, local_target, is_streamed = 0; int32_t(*read_32bit)(off_t, STREAMFILE*); int16_t(*read_16bit)(off_t, STREAMFILE*); From 8f1ec86bda42d8b8f0f262ffc188130b19c06e13 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 19 Jan 2019 23:08:26 +0100 Subject: [PATCH 04/10] Fix UE4 MSADPCM .adpcm [Heroes of Ark (iOS), Angels in the Sky (iOS)] --- src/formats.c | 1 + src/meta/riff.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++-- src/vgmstream.c | 20 +++++++++------ src/vgmstream.h | 1 + 4 files changed, 78 insertions(+), 9 deletions(-) diff --git a/src/formats.c b/src/formats.c index 40367c6d..63813897 100644 --- a/src/formats.c +++ b/src/formats.c @@ -620,6 +620,7 @@ static const coding_info coding_info_list[] = { {coding_H4M_IMA, "Hudson HVQM4 4-bit IMA ADPCM"}, {coding_MSADPCM, "Microsoft 4-bit ADPCM"}, + {coding_MSADPCM_int, "Microsoft 4-bit ADPCM (mono/interleave)"}, {coding_MSADPCM_ck, "Microsoft 4-bit ADPCM (Cricket Audio)"}, {coding_WS, "Westwood Studios VBR ADPCM"}, {coding_AICA, "Yamaha AICA 4-bit ADPCM"}, diff --git a/src/meta/riff.c b/src/meta/riff.c index c0fc8683..f4f0d588 100644 --- a/src/meta/riff.c +++ b/src/meta/riff.c @@ -10,6 +10,7 @@ static VGMSTREAM *parse_riff_ogg(STREAMFILE * streamFile, off_t start_offset, size_t data_size); #endif + /* return milliseconds */ static long parse_adtl_marker(unsigned char * marker) { long hh,mm,ss,ms; @@ -236,6 +237,9 @@ fail: return -1; } +static int is_ue4_msadpcm(VGMSTREAM* vgmstream, STREAMFILE* streamFile, riff_fmt_chunk* fmt, int fact_sample_count, off_t start_offset); + + VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; riff_fmt_chunk fmt = {0}; @@ -682,17 +686,74 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { vgmstream->meta_type = meta_RIFF_WAVE_MWV; } - if ( !vgmstream_open_stream(vgmstream,streamFile,start_offset) ) goto fail; - return vgmstream; + /* UE4 uses half-interleave mono MSADPCM, try to autodetect without breaking normal MSADPCM */ + if (fmt.coding_type == coding_MSADPCM && is_ue4_msadpcm(vgmstream, streamFile, &fmt, fact_sample_count, start_offset)) { + int ch; + size_t half_interleave = data_size / vgmstream->channels; + + vgmstream->coding_type = coding_MSADPCM_int; + + /* only works with half-interleave as frame_size and interleave are merged ATM*/ + for (ch = 0; ch < vgmstream->channels; ch++) { + vgmstream->ch[ch].channel_start_offset = + vgmstream->ch[ch].offset = start_offset + half_interleave*ch; + } + } + + return vgmstream; fail: close_vgmstream(vgmstream); return NULL; } +/* UE4 MSADPCM is quite normal but has a few minor quirks we can use to detect it */ +static int is_ue4_msadpcm(VGMSTREAM* vgmstream, STREAMFILE* streamFile, riff_fmt_chunk* fmt, int fact_sample_count, off_t start_offset) { + + /* stereo only */ + if (fmt->channel_count != 2) + goto fail; + + /* UE4 class is "ADPCM", assume it's the extension too */ + if (!check_extensions(streamFile, "adpcm")) + goto fail; + + /* UE4 encoder doesn't add "fact" */ + if (fact_sample_count != 0) + goto fail; + + /* fixed block size */ + if (fmt->block_size != 0x200) + goto fail; + + /* later UE4 versions use 0x36 (at 0x32 may be fact_samples?) */ + if (fmt->size != 0x32 && fmt->size != 0x36) + goto fail; + + /* size 0x32 in older UE4 matches standard MSADPCM, so add extra detection */ + if (fmt->size == 0x32) { + off_t offset = start_offset; + off_t max_offset = 5 * fmt->block_size; /* try N blocks */ + if (max_offset > get_streamfile_size(streamFile)) + max_offset = get_streamfile_size(streamFile); + + /* their encoder doesn't calculate optimal coefs and uses fixed values every frame + * (could do it for fmt size 0x36 too but maybe they'll fix it in the future) */ + while (offset <= max_offset) { + if (read_8bit(offset+0x00, streamFile) != 0 || read_16bitLE(offset+0x01, streamFile) != 0x00E6) + goto fail; + offset += fmt->block_size; + } + } + + return 1; +fail: + return 0; +} + VGMSTREAM * init_vgmstream_rifx(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; riff_fmt_chunk fmt = {0}; diff --git a/src/vgmstream.c b/src/vgmstream.c index d8da509a..fedcd8f6 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -1199,6 +1199,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { case coding_MSADPCM: return (vgmstream->interleave_block_size - 0x07*vgmstream->channels)*2 / vgmstream->channels + 2; + case coding_MSADPCM_int: case coding_MSADPCM_ck: return (vgmstream->interleave_block_size - 0x07)*2 + 2; case coding_WS: /* only works if output sample size is 8 bit, which always is for WS ADPCM */ @@ -1383,6 +1384,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { return 0x4c*vgmstream->channels; case coding_MSADPCM: + case coding_MSADPCM_int: case coding_MSADPCM_ck: return vgmstream->interleave_block_size; case coding_WS: @@ -1950,14 +1952,17 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to buffer+samples_written*vgmstream->channels, samples_to_do); break; case coding_MSADPCM: - if (vgmstream->channels == 2) { + case coding_MSADPCM_int: + if (vgmstream->channels == 1 || vgmstream->coding_type == coding_MSADPCM_int) { + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_msadpcm_mono(vgmstream,buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block, samples_to_do, ch); + } + } + else if (vgmstream->channels == 2) { decode_msadpcm_stereo(vgmstream,buffer+samples_written*vgmstream->channels, vgmstream->samples_into_block,samples_to_do); } - else if (vgmstream->channels == 1) { - decode_msadpcm_mono(vgmstream,buffer+samples_written*vgmstream->channels, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, 0); - } break; case coding_MSADPCM_ck: for (ch = 0; ch < vgmstream->channels; ch++) { @@ -2342,6 +2347,7 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { if (vgmstream->layout_type == layout_none && vgmstream->interleave_block_size > 0) { switch (vgmstream->coding_type) { case coding_MSADPCM: + case coding_MSADPCM_int: case coding_MSADPCM_ck: case coding_MS_IMA: case coding_MC3: @@ -2756,14 +2762,13 @@ int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t s if (!file) goto fail; } - for (ch=0; ch < vgmstream->channels; ch++) { + for (ch = 0; ch < vgmstream->channels; ch++) { off_t offset; if (use_same_offset_per_channel) { offset = start_offset; } else if (is_stereo_codec) { int ch_mod = (ch & 1) ? ch - 1 : ch; /* adjust odd channels (ch 0,1,2,3,4,5 > ch 0,0,2,2,4,4) */ offset = start_offset + vgmstream->interleave_block_size*ch_mod; - //VGM_LOG("ch%i offset=%lx\n", ch,offset); } else { offset = start_offset + vgmstream->interleave_block_size*ch; } @@ -2774,6 +2779,7 @@ int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t s if (!file) goto fail; } + VGM_LOG("ch%i offset=%lx\n", ch,offset); vgmstream->ch[ch].streamfile = file; vgmstream->ch[ch].channel_start_offset = vgmstream->ch[ch].offset = offset; diff --git a/src/vgmstream.h b/src/vgmstream.h index dfc1477d..f793130b 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -137,6 +137,7 @@ typedef enum { coding_H4M_IMA, /* H4M IMA ADPCM (stereo or mono, high nibble first) */ coding_MSADPCM, /* Microsoft ADPCM (stereo/mono) */ + coding_MSADPCM_int, /* Microsoft ADPCM (mono) */ coding_MSADPCM_ck, /* Microsoft ADPCM (Cricket Audio variation) */ coding_WS, /* Westwood Studios VBR ADPCM */ coding_AICA, /* Yamaha AICA ADPCM (stereo) */ From 29bed4b1e0c043f397f358cf7787fc9ac7eb93b4 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sun, 20 Jan 2019 00:17:06 +0100 Subject: [PATCH 05/10] Add TXTH/GENH interleave_last option --- doc/TXTH.md | 12 ++++++++++-- src/meta/genh.c | 13 ++++++++++--- src/meta/txth.c | 16 ++++++++++++++++ 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/doc/TXTH.md b/doc/TXTH.md index 776bda70..482c8508 100644 --- a/doc/TXTH.md +++ b/doc/TXTH.md @@ -10,7 +10,7 @@ When an unsupported file is loaded (for instance "bgm01.snd"), vgmstream tries t If found and parsed correctly (the TXTH may be rejected if incorrect commands are found) vgmstream will try to play the file as described. Extension must be accepted/added to vgmstream (plugins like foobar2000 only load extensions from a whitelist in formats.c), or one could rename to any supported extension (like .vgmstream), or leave the file extensionless. You can also use ".(sub).(ext).txth" (if the file is "filename.sub.ext"), to allow mixing slightly different files in the same folder. The "sub" part doesn't need to be an extension, for example: -- 001.1ch.str, 001.1ch.str may use .1ch.txth +- 001.1ch.str, 001.1ch.str may use .1ch.txth - 003.2ch.str, 003.2ch.str may use .2ch.txth - etc @@ -51,7 +51,7 @@ A text file with the above commands must be saved as ".vag.txth" or ".txth", not # * $1|2|3|4: value has size of 8/16/24/32 bit (optional, defaults to 4) # Examples: @0x10:BE$2 (get big endian 16b value at 0x10) # - (field): uses current value of a field. Accepted strings: -# - interleave, channels, sample_rate +# - interleave, interleave_last, channels, sample_rate # - start_offset, data_size # - num_samples, loop_start_sample, loop_end_sample # - subsong_count, subsong_offset @@ -116,6 +116,14 @@ value_sub|value_- = (number)|(offset)|(field) # Interleave 0 means "stereo mode" for some codecs (IMA, AICA, etc). interleave = (number)|(offset)|(field)|half_size +# Interleave in the last block [OPTIONAL] +# - auto: calculate based on channels, interleave and data_size/start_offset +# In some files with interleaved data the last block is smaller than interleave, +# so interleave must be smaller in the last block. This fixes decoding glitches +# for those files. Note that this doesn't affect files with padding data in the +# last block (as the interleave itself is constant). +interleave_last = (number)|(auto) + # Validate that id_value matches value at id_offset [OPTIONAL] # Can be redefined several times, it's checked whenever a new id_offset is found. id_value = (number)|(offset)|(field) diff --git a/src/meta/genh.c b/src/meta/genh.c index e6f3ae3a..c3bdfad9 100644 --- a/src/meta/genh.c +++ b/src/meta/genh.c @@ -39,8 +39,9 @@ typedef enum { typedef struct { genh_type codec; int codec_mode; - size_t interleave; + size_t interleave; + size_t interleave_last; int channels; int32_t sample_rate; @@ -153,6 +154,7 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) { case coding_AICA: case coding_APPLE_IMA4: vgmstream->interleave_block_size = genh.interleave; + vgmstream->interleave_last_block_size = genh.interleave_last; if (vgmstream->channels > 1) { if (coding == coding_SDX2) { @@ -202,6 +204,7 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) { case coding_PCFX: vgmstream->interleave_block_size = genh.interleave; vgmstream->layout_type = layout_interleave; + vgmstream->interleave_last_block_size = genh.interleave_last; if (genh.codec_mode >= 0 && genh.codec_mode <= 3) vgmstream->codec_config = genh.codec_mode; break; @@ -227,11 +230,13 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) { if (genh.codec_mode == 1) { /* mono interleave */ coding = coding_XBOX_IMA_int; vgmstream->layout_type = layout_interleave; + vgmstream->interleave_last_block_size = genh.interleave_last; vgmstream->interleave_block_size = genh.interleave; } else { /* 1ch mono, or stereo interleave */ vgmstream->layout_type = genh.interleave ? layout_interleave : layout_none; vgmstream->interleave_block_size = genh.interleave; + vgmstream->interleave_last_block_size = genh.interleave_last; if (vgmstream->channels > 2 && vgmstream->channels % 2 != 0) goto fail; /* only 2ch+..+2ch layout is known */ } @@ -245,6 +250,7 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) { if (!genh.interleave) goto fail; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = genh.interleave; + vgmstream->interleave_last_block_size = genh.interleave_last; } else if (genh.coef_interleave_type == 1) { if (!genh.interleave) goto fail; coding = coding_NGC_DSP_subint; @@ -419,8 +425,8 @@ static int parse_genh(STREAMFILE * streamFile, genh_header * genh) { genh->coef_split_spacing = read_32bitLE(0x38,streamFile); } - /* extended fields */ - if (header_size >= 0x54) { + /* extended + reserved fields */ + if (header_size >= 0x100) { genh->num_samples = read_32bitLE(0x40,streamFile); genh->skip_samples = read_32bitLE(0x44,streamFile); /* for FFmpeg based codecs */ genh->skip_samples_mode = read_8bit(0x48,streamFile); /* 0=autodetect, 1=force manual value @ 0x44 */ @@ -430,6 +436,7 @@ static int parse_genh(STREAMFILE * streamFile, genh_header * genh) { if ((genh->codec == XMA1 || genh->codec == XMA2) && genh->codec_mode==0) genh->codec_mode = read_8bit(0x4a,streamFile); genh->data_size = read_32bitLE(0x50,streamFile); + genh->interleave_last = read_32bitLE(0x54,streamFile); } if (genh->data_size == 0) diff --git a/src/meta/txth.c b/src/meta/txth.c index 375cf021..6bee9bb5 100644 --- a/src/meta/txth.c +++ b/src/meta/txth.c @@ -49,6 +49,7 @@ typedef struct { uint32_t id_offset; uint32_t interleave; + uint32_t interleave_last; uint32_t channels; uint32_t sample_rate; @@ -228,6 +229,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { case coding_AICA: case coding_APPLE_IMA4: vgmstream->interleave_block_size = txth.interleave; + vgmstream->interleave_last_block_size = txth.interleave_last; if (vgmstream->channels > 1) { if (coding == coding_SDX2) { @@ -277,6 +279,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { case coding_PCFX: vgmstream->interleave_block_size = txth.interleave; + vgmstream->interleave_last_block_size = txth.interleave_last; vgmstream->layout_type = layout_interleave; if (txth.codec_mode >= 0 && txth.codec_mode <= 3) vgmstream->codec_config = txth.codec_mode; @@ -304,10 +307,12 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { coding = coding_XBOX_IMA_int; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = txth.interleave; + vgmstream->interleave_last_block_size = txth.interleave_last; } else { /* 1ch mono, or stereo interleave */ vgmstream->layout_type = txth.interleave ? layout_interleave : layout_none; vgmstream->interleave_block_size = txth.interleave; + vgmstream->interleave_last_block_size = txth.interleave_last; if (vgmstream->channels > 2 && vgmstream->channels % 2 != 0) goto fail; /* only 2ch+..+2ch layout is known */ } @@ -320,6 +325,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { if (txth.channels > 1 && txth.codec_mode == 0) { if (!txth.interleave) goto fail; vgmstream->layout_type = layout_interleave; + vgmstream->interleave_last_block_size = txth.interleave_last; vgmstream->interleave_block_size = txth.interleave; } else if (txth.channels > 1 && txth.codec_mode == 1) { if (!txth.interleave) goto fail; @@ -655,6 +661,15 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char if (!parse_num(txth->streamHead,txth,val, &txth->interleave)) goto fail; } } + else if (0==strcmp(key,"interleave_last")) { + if (0==strcmp(val,"auto")) { + if (txth->channels > 0 && txth->interleave > 0) + txth->interleave_last = (txth->data_size % (txth->interleave * txth->channels)) / txth->channels; + } + else { + if (!parse_num(txth->streamHead,txth,val, &txth->interleave_last)) goto fail; + } + } else if (0==strcmp(key,"channels")) { if (!parse_num(txth->streamHead,txth,val, &txth->channels)) goto fail; } @@ -901,6 +916,7 @@ static int parse_num(STREAMFILE * streamFile, txth_header * txth, const char * v } else { /* known field */ if (0==strcmp(val,"interleave")) *out_value = txth->interleave; + if (0==strcmp(val,"interleave_last")) *out_value = txth->interleave_last; else if (0==strcmp(val,"channels")) *out_value = txth->channels; else if (0==strcmp(val,"sample_rate")) *out_value = txth->sample_rate; else if (0==strcmp(val,"start_offset")) *out_value = txth->start_offset; From 37df2cb18cf150a951e6e74ee7ed572e5403cea4 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sun, 20 Jan 2019 00:39:32 +0100 Subject: [PATCH 06/10] Show layers/segments in description --- src/vgmstream.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/vgmstream.c b/src/vgmstream.c index fedcd8f6..d26a9db5 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -2315,11 +2315,18 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { snprintf(temp,TEMPSIZE, "\nlayout: "); concatn(length,desc,temp); + + description = get_vgmstream_layout_description(vgmstream->layout_type); + if (!description) + description = "INCONCEIVABLE"; switch (vgmstream->layout_type) { + case layout_layered: + snprintf(temp,TEMPSIZE,"%s (%i layers)",description, ((layered_layout_data*)vgmstream->layout_data)->layer_count); + break; + case layout_segmented: + snprintf(temp,TEMPSIZE,"%s (%i segments)",description, ((segmented_layout_data*)vgmstream->layout_data)->segment_count); + break; default: - description = get_vgmstream_layout_description(vgmstream->layout_type); - if (!description) - description = "INCONCEIVABLE"; snprintf(temp,TEMPSIZE,"%s",description); break; } From 3a9273538fe9098d2555b2d9a7870498f29c4a40 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sun, 20 Jan 2019 00:42:05 +0100 Subject: [PATCH 07/10] Fix type 04 .xnb [Scare Me (XBLIG)] --- src/meta/xnb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/meta/xnb.c b/src/meta/xnb.c index 3e2191f0..2ca78366 100644 --- a/src/meta/xnb.c +++ b/src/meta/xnb.c @@ -22,7 +22,8 @@ VGMSTREAM * init_vgmstream_xnb(STREAMFILE *streamFile) { platform = read_8bit(0x03,streamFile); big_endian = (platform == 'x'); - if (read_8bit(0x04,streamFile) != 0x05) /* XNA 4.0 version only */ + if (read_8bit(0x04,streamFile) != 0x04 && /* XNA 3.0? found on Scare Me (XBLIG), no notable diffs */ + read_8bit(0x04,streamFile) != 0x05) /* XNA 4.0 version */ goto fail; flags = read_8bit(0x05,streamFile); From fb8d453c947ccf8498079f3e9a1f4dff974ff5b7 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sun, 20 Jan 2019 00:50:39 +0100 Subject: [PATCH 08/10] Add .lasf (.asf) for consistency --- README.md | 2 +- src/formats.c | 1 + src/meta/asf.c | 4 +++- src/meta/ea_1snh.c | 5 +++-- src/meta/ea_schl.c | 3 ++- src/meta/ea_schl_fixed.c | 6 ++++-- 6 files changed, 14 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index c4ea73a7..d8c456f6 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,7 @@ them playable through vgmstream. - .aac to .laac (tri-Ace games) - .ac3 to .lac3 (standard AC3) - .aif to .aiffl or .aifcl (standard Mac AIF) -- .asf to .sng (EA games) +- .asf to .lasf (EA games, Argonaut ASF) - .flac to .lflac (standard FLAC) - .mp2 to .lmp2 (standard MP2) - .mp3 to .lmp3 (standard MP3) diff --git a/src/formats.c b/src/formats.c index 63813897..7f35538b 100644 --- a/src/formats.c +++ b/src/formats.c @@ -206,6 +206,7 @@ static const char* extension_list[] = { "l", "laac", //fake extension for .aac (tri-Ace) "lac3", //fake extension for .ac3, FFmpeg/not parsed + "lasf", //fake extension for .asf (various) "leg", "lflac", //fake extension for .flac, FFmpeg/not parsed "lin", diff --git a/src/meta/asf.c b/src/meta/asf.c index fc094d36..57ecfae8 100644 --- a/src/meta/asf.c +++ b/src/meta/asf.c @@ -9,7 +9,9 @@ VGMSTREAM * init_vgmstream_asf(STREAMFILE *streamFile) { /* checks */ - if (!check_extensions(streamFile, "asf")) + /* .asf: original + * .lasf: fake for plugins */ + if (!check_extensions(streamFile, "asf,lasf")) goto fail; if (read_32bitBE(0x00,streamFile) != 0x41534600) /* "ASF\0" */ diff --git a/src/meta/ea_1snh.c b/src/meta/ea_1snh.c index 3091383d..2ec1da29 100644 --- a/src/meta/ea_1snh.c +++ b/src/meta/ea_1snh.c @@ -40,11 +40,12 @@ VGMSTREAM * init_vgmstream_ea_1snh(STREAMFILE *streamFile) { /* checks */ - /* .asf/as4: common + /* .asf/as4: common, + * .lasf: fake for plugins * .cnk: some PS games * .sng: fake for plugins (to mimic EA SCHl's common extension) * .uv/tgq: some SAT games (video only?) */ - if (!check_extensions(streamFile,"asf,as4,cnk,sng,uv,tgq")) + if (!check_extensions(streamFile,"asf,lasf,as4,cnk,sng,uv,tgq")) goto fail; if (read_32bitBE(0x00,streamFile) != 0x31534E68 && /* "1SNh" */ diff --git a/src/meta/ea_schl.c b/src/meta/ea_schl.c index c39266a3..fbe12d03 100644 --- a/src/meta/ea_schl.c +++ b/src/meta/ea_schl.c @@ -116,6 +116,7 @@ VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile) { /* check extension */ /* they don't seem enforced by EA's tools but usually: * .asf: ~early (audio stream file?) [ex. Need for Speed (PC)] + * .lasf: fake for plugins * .str: ~early [ex. FIFA 2002 (PS1)] * .eam: ~mid (fake?) * .exa: ~mid [ex. 007 - From Russia with Love] @@ -129,7 +130,7 @@ VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile) { * .gsf: 007 - Everything or Nothing (GC) * .mus: map/mpf+mus only? * (extensionless): SSX (PS2) (inside .big) */ - if (!check_extensions(streamFile,"asf,str,eam,exa,sng,aud,sx,xa,strm,stm,hab,xsf,gsf,mus,")) + if (!check_extensions(streamFile,"asf,lasf,str,eam,exa,sng,aud,sx,xa,strm,stm,hab,xsf,gsf,mus,")) goto fail; /* check header */ diff --git a/src/meta/ea_schl_fixed.c b/src/meta/ea_schl_fixed.c index c374f874..eb5f4873 100644 --- a/src/meta/ea_schl_fixed.c +++ b/src/meta/ea_schl_fixed.c @@ -29,8 +29,10 @@ VGMSTREAM * init_vgmstream_ea_schl_fixed(STREAMFILE *streamFile) { ea_header ea = {0}; - /* check extension */ - if (!check_extensions(streamFile,"asf")) + /* checks */ + /* .asf: original + * .lasf: fake for plugins */ + if (!check_extensions(streamFile,"asf,lasf")) goto fail; /* check header (see ea_schl.c for more info about blocks) */ From 4c24b039519e4890f09143221dcfa961de180508 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sun, 20 Jan 2019 00:57:09 +0100 Subject: [PATCH 09/10] Remove debug crap --- src/meta/ubi_sb_streamfile.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/meta/ubi_sb_streamfile.h b/src/meta/ubi_sb_streamfile.h index fbaf05c1..88c3b17f 100644 --- a/src/meta/ubi_sb_streamfile.h +++ b/src/meta/ubi_sb_streamfile.h @@ -61,7 +61,6 @@ static size_t ubi_sb_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset if (data->data_size == 0) { data->physical_offset += data->block_size; } -//VGM_LOG("h=%lx, b=%x, n=%x\n", data->physical_offset, data->block_size, data->next_block_size); } } @@ -85,8 +84,6 @@ static size_t ubi_sb_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset data->skip_size += read_32bit(data->physical_offset + data->block_sizes_start + i*0x04, streamfile); } data->data_size = read_32bit(data->physical_offset + data->block_sizes_start + data->layer_number*0x04, streamfile); - -//VGM_LOG("o=%lx, b=%x, n=%x\n", data->physical_offset, data->block_size, data->next_block_size); } /* move to next block */ From b3867b02605da7b4eede5cc04573513882b496ca Mon Sep 17 00:00:00 2001 From: bnnm Date: Sun, 20 Jan 2019 17:16:44 +0100 Subject: [PATCH 10/10] Handle Ubi SB sequence loops properly --- src/meta/ubi_sb.c | 48 ++++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/src/meta/ubi_sb.c b/src/meta/ubi_sb.c index d8df1551..242e642a 100644 --- a/src/meta/ubi_sb.c +++ b/src/meta/ubi_sb.c @@ -80,13 +80,11 @@ typedef struct { int cfga_has_internal_names; /* type 0x05/0c (sequence) config */ off_t cfgs_extra_offset; - off_t cfgs_intro_flag; - off_t cfgs_outro_flag; + off_t cfgs_sequence_loop; + off_t cfgs_sequence_single; off_t cfgs_sequence_count; off_t cfgs_entry_number; size_t sequence_entry_size; - int cfgs_and_intro_flag; - int cfgs_and_outro_flag; /* type 0x06/0d (layer) config */ off_t cfgl_extra_offset; off_t cfgl_layer_count; @@ -123,8 +121,8 @@ typedef struct { int layer_count; /* number of layers in a layer type */ int sequence_count; /* number of segments in a sequence type */ int sequence_chain[64]; /* sequence of entry numbers */ - int has_intro; /* seems to mean something like this */ - int has_outro; /* some games use this flag for segments that join with others too */ + int sequence_loop; /* chain index to loop */ + int sequence_single; /* if que sequence plays once (loops by default) */ int is_external; /* stream is in a external file */ char resource_name[0x24]; /* filename to the external stream, or internal stream info for some games */ @@ -256,8 +254,6 @@ VGMSTREAM * init_vgmstream_ubi_sm(STREAMFILE *streamFile) { for (i = 0; i < sb.map_num; i++) { off_t offset = sb.map_start + i * sb.map_entry_size; - sb.type = UBI_NONE; /* reset possible parsed subsong */ - /* SUBMAP HEADER */ sb.map_type = read_32bit(offset + 0x00, streamFile); /* usually 0/1=first, 0=rest */ sb.map_zero = read_32bit(offset + 0x04, streamFile); @@ -305,6 +301,7 @@ VGMSTREAM * init_vgmstream_ubi_sm(STREAMFILE *streamFile) { * (it gets rewritten and we need exact values for sequences and stuff) */ if (sb.type != UBI_NONE) { target_sb = sb; /* memcpy */ + sb.type = UBI_NONE; /* reset parsed flag */ } } @@ -668,14 +665,17 @@ static VGMSTREAM * init_vgmstream_ubi_sb_sequence(ubi_sb_header *sb, STREAMFILE data->segments[i] = init_vgmstream_ubi_sb_header(&temp_sb, streamTest, streamFile); if (!data->segments[i]) goto fail; + if (i == sb->sequence_loop) + sb->loop_start = sb->num_samples; sb->num_samples += data->segments[i]->num_samples; } + if (!setup_layout_segmented(data)) goto fail; /* build the base VGMSTREAM */ - vgmstream = allocate_vgmstream(data->segments[0]->channels, data->segments[0]->loop_flag); + vgmstream = allocate_vgmstream(data->segments[0]->channels, !sb->sequence_single); if (!vgmstream) goto fail; vgmstream->meta_type = meta_UBI_SB; @@ -763,14 +763,18 @@ static void build_readable_name(char * buf, size_t buf_size, ubi_sb_header * sb) index = sb->header_index; //-1 if (sb->type == UBI_SEQUENCE) { - if (sb->has_intro && sb->has_outro) /* uncommon but exists */ - res_name = sb->sequence_count == 1 ? "start+end" : "start+loop+end"; - else if (sb->has_intro) - res_name = sb->sequence_count == 1 ? "start" : "start+loop"; - else if (sb->has_outro) - res_name = sb->sequence_count == 1 ? "end" : "loop+end"; - else - res_name = "loop"; + if (sb->sequence_single) { + if (sb->sequence_count == 1) + res_name = "single"; + else + res_name = "multi"; + } + else { + if (sb->sequence_count == 1) + res_name = "single-loop"; + else + res_name = (sb->sequence_loop == 0) ? "multi-loop" : "intro-loop"; + } } else { if (sb->is_external || sb->cfga_has_internal_names) @@ -893,8 +897,8 @@ static int parse_type_sequence(ubi_sb_header * sb, off_t offset, STREAMFILE* str sb->type = UBI_SEQUENCE; sb->extra_offset = read_32bit(offset + sb->cfgs_extra_offset, streamFile) + sb->sectionX_offset; - sb->has_intro = read_32bit(offset + sb->cfgs_intro_flag, streamFile) & sb->cfgs_and_intro_flag; - sb->has_outro = read_32bit(offset + sb->cfgs_outro_flag, streamFile) & sb->cfgs_and_outro_flag; + sb->sequence_loop = read_32bit(offset + sb->cfgs_sequence_loop, streamFile); + sb->sequence_single = read_32bit(offset + sb->cfgs_sequence_single, streamFile); sb->sequence_count = read_32bit(offset + sb->cfgs_sequence_count, streamFile); if (sb->sequence_entry_size == 0) { @@ -1436,13 +1440,11 @@ static void config_sb_audio_he(ubi_sb_header * sb, off_t channels, off_t sample_ } static void config_sb_sequence(ubi_sb_header * sb, off_t sequence_count, off_t entry_size) { /* sequence header and chain table */ - sb->cfgs_intro_flag = sequence_count - 0x10; - sb->cfgs_outro_flag = sequence_count - 0x0c; + sb->cfgs_sequence_loop = sequence_count - 0x10; + sb->cfgs_sequence_single = sequence_count - 0x0c; sb->cfgs_sequence_count = sequence_count; sb->sequence_entry_size = entry_size; sb->cfgs_entry_number = 0x00; - sb->cfgs_and_intro_flag = 1; - sb->cfgs_and_outro_flag = 1; } static void config_sb_layer_hs(ubi_sb_header * sb, off_t layer_count, off_t stream_size, off_t stream_offset, off_t stream_name) { /* layer headers with stream name */