diff --git a/src/meta/ubi_sb.c b/src/meta/ubi_sb.c index b9bef55e..14baee78 100644 --- a/src/meta/ubi_sb.c +++ b/src/meta/ubi_sb.c @@ -2,14 +2,13 @@ #include "../coding/coding.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 } ubi_sb_codec; +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_PC, UBI_PS2, UBI_XBOX, UBI_GC, UBI_X360, UBI_PSP, UBI_PS3, UBI_WII, UBI_3DS } ubi_sb_platform; typedef struct { ubi_sb_platform platform; int big_endian; int total_streams; int is_external; - int autodetect_external; ubi_sb_codec codec; /* map base header info */ @@ -24,24 +23,37 @@ typedef struct { char map_name[255]; uint32_t map_unknown; - /* SB main/fixed info */ - uint32_t version; - uint32_t version_empty; + /* 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 */ + /* folders? events? (often share header_id/type with some descriptors, + * but may exists without headers or header exist without this) */ 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 */ size_t section3_num; + size_t section3_offset; + /* section with sounds in some map versions */ size_t section4_num; + size_t section4_offset; + /* extra table, config for certain types (DSP coefs, external resources, layer headers, etc) */ size_t sectionX_size; + size_t sectionX_offset; + /* unknown, usually -1 but can be others (0/1/2/etc) */ int flag1; int flag2; + /* where sound data starts, derived */ + size_t sounds_offset; - /* maps data config */ - int is_map; - int map_version; /* header/stream info config */ - /* audio header varies slightly per game but not enough parse case by case, + /* 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) */ size_t section1_entry_size; size_t section2_entry_size; size_t section3_entry_size; @@ -49,43 +61,38 @@ typedef struct { off_t cfg_stream_size; off_t cfg_stream_offset; off_t cfg_extra_offset; - off_t cfg_stream_id; + off_t cfg_group_id; off_t cfg_stream_type; - off_t cfg_external_flag; /* if the song is internal or external */ - off_t cfg_samples_flag; /* some headers have two possible locations for num_samples */ - off_t cfg_samples_bitflag; - off_t cfg_num_samples; - off_t cfg_num_samples2; + off_t cfg_external_flag; /* stream is external */ + off_t cfg_loop_flag; /* stream loops */ + off_t cfg_num_samples; /* num_samples/loop start */ + off_t cfg_num_samples2; /* num_samples/loop end (if loop set) */ off_t cfg_sample_rate; off_t cfg_channels; off_t cfg_stream_name; /* where the resource name is within the header */ off_t cfg_extra_name; /* where the resource name is within sectionX */ off_t cfg_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 some cases */ - int has_extra_name_flag; /* if cfg_extra_name is set (since often extra_name = -1 is 'not set' and >= 0 is offset) */ - int has_rotating_ids; /* stream id isn't set but is assigned using sequential rotation of sorts */ - //todo see if has_extra_name_flag can be removed - - /* derived */ - size_t section1_offset; - size_t section2_offset; - size_t section3_offset; - size_t section4_offset; - size_t sectionX_offset; - size_t sounds_offset; + int has_internal_names; /* resource name doubles as internal name in earlier games, or may contain garbage */ /* header/stream info */ - uint32_t header_id; /* 16b+16b group+sound id identifier (should be unique within sbX, but not smX) */ + 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 */ off_t extra_offset; /* offset within sectionX to extra data */ - uint32_t stream_id; /* internal resource id needed to locate info */ + uint32_t group_id; /* internal id to reference in section3 */ uint32_t stream_type; /* rough codec value */ - int num_samples; /* usually only for external resources */ + int loop_flag; + int loop_start; /* loop starts that aren't 0 do exist but are very rare (ex. Beowulf PSP #33407) */ + 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 */ @@ -103,7 +110,8 @@ static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile, int targe 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 sound engine ("DARE" / "UbiSound Driver") games in ~2000-2008+ */ + +/* .SBx - banks from Ubisoft's DARE (Digital Audio Rendering Engine) engine games in ~2000-2008+ */ VGMSTREAM * init_vgmstream_ubi_sb(STREAMFILE *streamFile) { STREAMFILE *streamTest = NULL; int32_t(*read_32bit)(off_t, STREAMFILE*) = NULL; @@ -139,13 +147,13 @@ VGMSTREAM * init_vgmstream_ubi_sb(STREAMFILE *streamFile) { /* SB HEADER */ /* SBx layout: base header, section1, section2, extra section, section3, data (all except base header can be null) */ sb.is_map = 0; - sb.version = read_32bit(0x00, streamFile); /* 16b+16b major/minor version */ - sb.section1_num = read_32bit(0x04, streamFile); /* group headers? */ - sb.section2_num = read_32bit(0x08, streamFile); /* streams headers (internal or external) */ - sb.section3_num = read_32bit(0x0c, streamFile); /* internal streams table */ - sb.sectionX_size = read_32bit(0x10, streamFile); /* extra table, unknown (config for non-audio types) except with DSP = coefs */ - sb.flag1 = read_32bit(0x14, streamFile); /* unknown, usually -1 but can be others (0/1/2/etc) */ - sb.flag2 = read_32bit(0x18, streamFile); /* unknown, usually -1 but can be others */ + sb.version = read_32bit(0x00, streamFile); + sb.section1_num = read_32bit(0x04, streamFile); + sb.section2_num = read_32bit(0x08, streamFile); + sb.section3_num = read_32bit(0x0c, streamFile); + sb.sectionX_size = read_32bit(0x10, streamFile); + sb.flag1 = read_32bit(0x14, streamFile); + sb.flag2 = read_32bit(0x18, streamFile); ok = config_sb_version(&sb, streamFile); if (!ok) goto fail; @@ -287,24 +295,10 @@ static VGMSTREAM * init_vgmstream_ubi_sb_main(ubi_sb_header *sb, STREAMFILE *str VGMSTREAM * vgmstream = NULL; STREAMFILE *streamData = NULL; off_t start_offset; - int loop_flag = 0; + /* open external stream if needed */ - if (sb->autodetect_external) { /* works most of the time but could give false positives */ - VGM_LOG("UBI SB: autodetecting external stream '%s'\n", sb->resource_name); - - streamData = open_streamfile_by_filename(streamFile,sb->resource_name); - if (!streamData) { - streamData = streamFile; /* assume internal */ - if (sb->stream_size > get_streamfile_size(streamData)) { - VGM_LOG("UBI SB: expected external stream\n"); - goto fail; - } - } else { - sb->is_external = 1; - } - } - else if (sb->is_external) { + 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); @@ -314,59 +308,62 @@ static VGMSTREAM * init_vgmstream_ubi_sb_main(ubi_sb_header *sb, STREAMFILE *str 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); + ;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,loop_flag); + vgmstream = allocate_vgmstream(sb->channels,sb->loop_flag); if (!vgmstream) goto fail; + vgmstream->meta_type = meta_UBI_SB; vgmstream->sample_rate = sb->sample_rate; vgmstream->num_streams = sb->total_streams; vgmstream->stream_size = sb->stream_size; - vgmstream->meta_type = meta_UBI_SB; + + vgmstream->num_samples = sb->num_samples; + vgmstream->loop_start_sample = sb->loop_start; + vgmstream->loop_end_sample = sb->num_samples; switch(sb->codec) { case UBI_ADPCM: vgmstream->coding_type = coding_UBI_IMA; vgmstream->layout_type = layout_none; - vgmstream->num_samples = ubi_ima_bytes_to_samples(sb->stream_size, sb->channels, streamData, start_offset); break; case RAW_PCM: vgmstream->coding_type = coding_PCM16LE; /* always LE even on Wii */ vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x02; - vgmstream->num_samples = pcm_bytes_to_samples(sb->stream_size, sb->channels, 16); break; case RAW_PSX: vgmstream->coding_type = coding_PSX; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = (sb->stream_type == 0x00) ? sb->stream_size / sb->channels : 0x10; /* TODO: needs testing */ - vgmstream->num_samples = ps_bytes_to_samples(sb->stream_size, sb->channels) ; + if (vgmstream->num_samples == 0) { /* early PS2 games may not set it for a few internal streams */ + vgmstream->num_samples = ps_bytes_to_samples(sb->stream_size, sb->channels) ; + vgmstream->loop_end_sample = vgmstream->num_samples; + } break; case RAW_XBOX: vgmstream->coding_type = coding_XBOX_IMA; vgmstream->layout_type = layout_none; - vgmstream->num_samples = xbox_ima_bytes_to_samples(sb->stream_size, sb->channels); break; case RAW_DSP: vgmstream->coding_type = coding_NGC_DSP; vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = sb->stream_size / sb->channels; - vgmstream->num_samples = dsp_bytes_to_samples(sb->stream_size, sb->channels); + vgmstream->interleave_block_size = align_size_to_block(sb->stream_size / sb->channels, 0x04); /* DSP extra info entry size is 0x40 (first/last 0x10 = unknown), per channel */ dsp_read_coefs_be(vgmstream,streamFile,sb->extra_offset + 0x10, 0x40); break; case FMT_VAG: - /* skip VAG header (some sb4 use VAG and others raw PSX) */ + /* skip VAG header (some sb4 use VAG and others raw PSX) */ //todo remove if (read_32bitBE(start_offset, streamData) == 0x56414770) { /* "VAGp" */ start_offset += 0x30; sb->stream_size -= 0x30; @@ -375,8 +372,6 @@ static VGMSTREAM * init_vgmstream_ubi_sb_main(ubi_sb_header *sb, STREAMFILE *str vgmstream->coding_type = coding_PSX; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = sb->stream_size / sb->channels; - vgmstream->num_samples = ps_bytes_to_samples(sb->stream_size, sb->channels); - break; #ifdef VGM_USE_FFMPEG @@ -395,15 +390,6 @@ 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; - - if (sb->num_samples == 0) /* sometimes not known */ - sb->num_samples = ffmpeg_data->totalSamples; - vgmstream->num_samples = sb->num_samples; - if (sb->num_samples != ffmpeg_data->totalSamples) { - VGM_LOG("UBI SB: header samples differ (%i vs %i)\n", sb->num_samples, (size_t)ffmpeg_data->totalSamples); - goto fail; - } - if (ffmpeg_data->skipSamples <= 0) /* in case FFmpeg didn't get them */ ffmpeg_set_skip_samples(ffmpeg_data, riff_get_fact_skip_samples(streamData, start_offset)); break; @@ -422,7 +408,6 @@ static VGMSTREAM * init_vgmstream_ubi_sb_main(ubi_sb_header *sb, STREAMFILE *str 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->num_samples = sb->num_samples; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; break; @@ -447,10 +432,7 @@ static VGMSTREAM * init_vgmstream_ubi_sb_main(ubi_sb_header *sb, STREAMFILE *str num_frames = sec2_num; bits_per_frame = 4; - if (flag == 0x04) { - sec1_num--; - sec2_num += 4; - } else if (flag == 0x02) { + if (flag == 0x02 || flag == 0x04) { bits_per_frame = 2; } @@ -468,7 +450,6 @@ 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->num_samples = sb->num_samples; vgmstream->stream_size = data_size; break; } @@ -478,7 +459,7 @@ static VGMSTREAM * init_vgmstream_ubi_sb_main(ubi_sb_header *sb, STREAMFILE *str uint8_t buf[0x100]; size_t bytes, chunk_size; off_t header_offset; - + VGM_ASSERT(sb->is_external, "Ubi SB: Raw XMA used for external sound\n"); /* get XMA header from extra section */ @@ -491,7 +472,6 @@ 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->num_samples = sb->num_samples; break; } @@ -503,13 +483,19 @@ 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->num_samples = sb->num_samples; /* ffmpeg_data->totalSamples */ - VGM_ASSERT(sb->num_samples != ffmpeg_data->totalSamples, "UBI SB: header samples differ\n"); break; } - #endif + case FMT_CWAV: + if (sb->channels > 1) goto fail; //todo test + 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); + start_offset += 0xe0; /* skip CWAV header */ + break; + default: VGM_LOG("UBI SB: unknown codec\n"); goto fail; @@ -560,12 +546,14 @@ static void parse_header_type(ubi_sb_header * sb, uint32_t header_type, off_t of break; //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); + //;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); /* layer info for later * some values may be flags/config as multiple 0x06 can point to the same layer, with different 'flags' */ + /* headers/layers/blocks also respect endianness (codec is separate, ex. Ubi IMA header is always LE) */ + /* 0x0d layer [Splinter Cell] */ /* - type header: * 0x18: stream offset? @@ -578,7 +566,7 @@ static void parse_header_type(ubi_sb_header * sb, uint32_t header_type, off_t of * 0x78/7c: codec? * * - layer header at stream_offset: - * 0x00: version? (0x02000000) + * 0x00: version? (0x00000002) * 0x04: layers * 0x08: stream size (not including padding) * 0x0c: size? @@ -592,7 +580,7 @@ static void parse_header_type(ubi_sb_header * sb, uint32_t header_type, off_t of * 0x00: layer data size (varies between blocks, and one layer may have more than other, even the header) */ - /* Rainbow Six 3 */ //todo also check layer 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): @@ -606,7 +594,7 @@ static void parse_header_type(ubi_sb_header * sb, uint32_t header_type, off_t of * 0x64: number of samples * * - layer header at stream_offset (BE on GC): - * 0x00: version? (0x04) + * 0x00: version? (0x00000004) * 0x04: layers * 0x08: stream size (not including padding) * 0x0c: blocks count @@ -634,24 +622,24 @@ static void parse_header_type(ubi_sb_header * sb, uint32_t header_type, off_t of * 0x28: external flag? * 0x2c: external flag? * 0x30: stream name - * 0x5c: config? + * 0x5c: sample rate * layers * 0x60: stream size * 0x64: stream offset? * - * - in header extra offset + * - in header extra offset (style 0x10) * 0x00: sample rate * 0x04: 16? * 0x08: channels? * 0x0c: codec? * * - layer header at stream_offset: - * 0x00: version? (0x07000000) - * 0x04: 0x03? - * 0x08: layers/channels? + * 0x00: version (0x00000007) + * 0x04: config? + * 0x08: layers * 0x0c: stream size * 0x10: blocks count * 0x14: block header size - * 0x18: block size + * 0x18: next block size * - per layer: * 0x00: approximate layer data size per block * - per layer @@ -665,45 +653,81 @@ static void parse_header_type(ubi_sb_header * sb, uint32_t header_type, off_t of * 0x00: layer data size (varies between blocks, and one layer may have more than other) */ + //todo a few XMA streams aren't working (ex. #13072) + /* 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) + */ - /* 0x06 layer [TMNT (PS2)-bank] */ + /* 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/channels? - * 0x24: layers/channels? - * 0x28: config? same for all + * 0x20: layers + * 0x28: sample rate * layers * 0x2c: stream size * 0x30: stream offset * 0x38: name extra offset * - * - in header extra offset + * - in header extra offset (style 0x0c) * 0x00: sample rate - * 0x04: channels? - * 0x08: codec? + * 0x04: channels + * 0x08: codec * * - layer header at stream_offset: - * 0x00: version? (0x08000B00) - * 0x04: config? (0x0e/0x0b/etc) - * 0x08: layers/channels? + * 0x00: version? (0x000B0008) + * 0x04: config? (0x00/0x0e/0x0b/etc) + * 0x08: layers * 0x0c: blocks count * 0x10: block header size - * 0x14: size of header sizes + codec headers - * 0x18: block size + * 0x14: size of header sizes + * 0x18: next block size * - per layer: * 0x00: layer header size - * - per layer - * 0x00~0x20: standard Ubi IMA header (version 0x05) + * - per layer (if layer header size > 0) + * 0x00~0x20: standard Ubi IMA header (version 0x05), PCM data * * - blocked data: * 0x00: always 0x03 - * 0x04: block size + * 0x04: next block size * - per layer: * 0x00: layer data size (varies between blocks, and one layer may have more than other) */ - /* Splinter Cell 3D (2011)(3DS)-map 0x00130001 */ //todo - /* Open Season (2005)(PS2)-map 0x00180003 */ /* Rainbow Six Vegas (2007)(PSP)-bank 0x00180006 */ /* Star Wars - Lethal Alliance (2006)(PSP)-map 0x00180007 */ @@ -715,14 +739,10 @@ static void parse_header_type(ubi_sb_header * sb, uint32_t header_type, off_t of * 0x38: name extra offset * * - in header extra offset - * 0x00: sample rate - * 0x04: 16? - * 0x08: channels? - * 0x0c: codec (03=Ubi, 01=PCM16LE in SW:LA) - * + * style 0x10 (codec 03=Ubi, 01=PCM16LE in SW:LA/RS:V) * - layer header at stream_offset: * - blocked data: - * same as TMNT PSP-map + * version 0x000B0008 */ /* TMNT (2007)(PSP)-map 0x00190001 */ @@ -730,22 +750,36 @@ static void parse_header_type(ubi_sb_header * sb, uint32_t header_type, off_t of * 0x0c: header extra offset * 0x20: layers * 0x24: total channels? - * 0x28: config? + * 0x28: sample rate * layers? * 0x2c: stream size * 0x30: stream offset * 0x38: name extra offset * * - in header extra offset - * 0x00: sample rate - * 0x04: channels? - * 0x08: codec? - * + * style 0x0c * - layer header at stream_offset: * - blocked data: - * same as TMNT PS2-bank, but codec header size is 0 + * version 0x000B0008, but codec header size is 0 */ - //todo Surf's Up (PC?)-map + /* 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: @@ -756,13 +790,10 @@ static void parse_header_type(ubi_sb_header * sb, uint32_t header_type, off_t of * 0x54: name extra offset * * - in header extra offset - * 0x00: sample rate - * 0x04: channels? - * 0x08: codec? - * + * style 0x0c * - layer header at stream_offset: * - blocked data: - * same as TMNT PS2-bank, but codec header size is 0 + * version 0x000B0008, but codec header size is 0 */ } @@ -770,7 +801,13 @@ static void parse_header_type(ubi_sb_header * sb, uint32_t header_type, off_t of static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile, int target_stream) { int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; - int i, j, k, current_type = -1, current_id = -1, bank_streams = 0, prev_streams; + int i, j, k, bank_streams = 0, prev_streams; + + + ;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); + if (sb->big_endian) { read_32bit = read_32bitBE; @@ -794,55 +831,13 @@ static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile, int targe if (header_type != 0x01) continue; - /* weird case when there is no internal substream ID and just seem to rotate every time type changes, joy */ - if (sb->has_rotating_ids) { /* assumes certain configs can't happen in this case */ - int current_is_external = 0; - int type = read_32bit(offset + sb->cfg_stream_type, streamFile); - - if (sb->cfg_external_flag) { - current_is_external = read_32bit(offset + sb->cfg_external_flag, streamFile); - } else if (sb->has_extra_name_flag && read_32bit(offset + sb->cfg_extra_name, streamFile) != 0xFFFFFFFF) { - current_is_external = 1; /* -1 in extra_name means internal */ - } - - if (!current_is_external) { - if (sb->is_map) { - if (current_type == -1) - current_type = type; - if (current_id == -1) /* they seem to always start with 0 in maps */ - current_id = 0x00; - - if (!current_is_external) { - if (current_type != type) { - current_type = type; - current_id++; /* rotate */ - /* we'll warp it around later when parsing section 3 */ - } - } - } else { - if (current_type == -1) - current_type = type; - if (current_id == -1) /* use first ID in section3 */ - current_id = read_32bit(sb->section3_offset + 0x00, streamFile); - - if (!current_is_external) { - if (current_type != type) { - current_type = type; - current_id++; /* rotate */ - if (current_id >= sb->section3_num) - current_id = 0; /* reset */ - } - } - } - } - } - /* update streams (total_stream also doubles as current) */ sb->total_streams++; bank_streams++; if (sb->total_streams != target_stream) continue; + /* parse audio entry based on config */ sb->header_index = i; sb->header_offset = offset; @@ -858,20 +853,31 @@ static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile, int targe sb->sample_rate = read_32bit(offset + sb->cfg_sample_rate, streamFile); sb->stream_type = read_32bit(offset + sb->cfg_stream_type, streamFile); - /* some games may store number of samples at different locations */ - if (sb->cfg_samples_flag && read_32bit(offset + sb->cfg_samples_flag, streamFile) != 0) { - sb->num_samples = read_32bit(offset + sb->cfg_num_samples2, streamFile); - } else if (sb->cfg_samples_bitflag && (read_32bit(offset + sb->cfg_samples_bitflag, streamFile) & 0x10)) { - sb->num_samples = read_32bit(offset + sb->cfg_num_samples2, streamFile); - VGM_ASSERT(sb->num_samples == 0, "UBI SB: bad bitflag found\n"); - } else if (sb->cfg_num_samples) { + if (sb->cfg_loop_flag) { + sb->loop_flag = (read_32bit(offset + sb->cfg_loop_flag, streamFile) & sb->and_loop_flag); + } + + if (sb->loop_flag) { + sb->loop_start = read_32bit(offset + sb->cfg_num_samples, streamFile); + sb->num_samples = read_32bit(offset + sb->cfg_num_samples2, streamFile) + sb->loop_start; + if (sb->has_full_loop) { /* 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) */ + } else { sb->num_samples = read_32bit(offset + sb->cfg_num_samples, streamFile); } - if (sb->has_rotating_ids) { - sb->stream_id = current_id; - } else if (sb->cfg_stream_id) { - sb->stream_id = read_32bit(offset + sb->cfg_stream_id, streamFile); + if (sb->cfg_group_id) { + sb->group_id = read_32bit(offset + sb->cfg_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->cfg_external_flag) { + sb->is_external = (read_32bit(offset + sb->cfg_external_flag, streamFile) & sb->and_external_flag); } /* external stream name can be found in the header (first versions) or the sectionX table (later versions) */ @@ -879,41 +885,24 @@ static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile, int targe read_string(sb->resource_name, sb->resource_name_size, offset + sb->cfg_stream_name, streamFile); } else { sb->cfg_stream_name = read_32bit(offset + sb->cfg_extra_name, streamFile); - read_string(sb->resource_name, sb->resource_name_size, sb->sectionX_offset + sb->cfg_stream_name, streamFile); + if (sb->cfg_stream_name != 0xFFFFFFFF) + read_string(sb->resource_name, sb->resource_name_size, sb->sectionX_offset + sb->cfg_stream_name, streamFile); } - /* external flag is not always set and must be derived */ - if (sb->cfg_external_flag) { - sb->is_external = read_32bit(offset + sb->cfg_external_flag, streamFile); - } else if (sb->has_extra_name_flag && read_32bit(offset + sb->cfg_extra_name, streamFile) != 0xFFFFFFFF) { - sb->is_external = 1; /* -1 in extra_name means internal */ - } else if (sb->section3_num == 0) { - sb->is_external = 1; - } else { - sb->autodetect_external = 1; /* let the parser guess later */ - - if (sb->resource_name[0] == '\0') - sb->autodetect_external = 0; /* no name */ - if (sb->sectionX_size > 0 && sb->cfg_stream_name > sb->sectionX_size) - sb->autodetect_external = 0; /* name outside extra table == is internal */ - } - - //todo check sb->has_internal_names in case of garbage - /* build a full name for stream */ + /* build a usable name */ if (sb->is_map) { - if (sb->resource_name[0]) { + if ((sb->is_external || sb->has_internal_names) && sb->resource_name[0]) { snprintf(sb->readable_name, sizeof(sb->readable_name), "%s/%d/%08x/%s", sb->map_name, bank_streams, sb->header_id, sb->resource_name); } else { snprintf(sb->readable_name, sizeof(sb->readable_name), "%s/%d/%08x", sb->map_name, bank_streams, sb->header_id); } } else { - if (sb->resource_name[0]) { + if ((sb->is_external || sb->has_internal_names) && sb->resource_name[0]) { snprintf(sb->readable_name, sizeof(sb->readable_name), "%08x/%s", sb->header_id, sb->resource_name); } else { - snprintf(sb->readable_name, sizeof(sb->readable_name), "%s", sb->resource_name); + snprintf(sb->readable_name, sizeof(sb->readable_name), "%08x", sb->header_id); } } - } //;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"); @@ -932,28 +921,31 @@ static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile, int targe VGM_LOG("UBI SB: wrong target stream (target=%i, total=%i)\n", target_stream, sb->total_streams); goto fail; } + + VGM_ASSERT(sb->section3_num > 2, "UBI SB: section3 > 2 found\n"); } - if (!(sb->cfg_stream_id || sb->has_rotating_ids || sb->is_map) && sb->section3_num > 1) { + if (!(sb->cfg_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; } - //;VGM_LOG("UBI SB: target at %x (cfg %x), extra=%x, name=%s\n", (uint32_t)sb->header_offset, sb->section2_entry_size, (uint32_t)sb->extra_offset, sb->resource_name); + ;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); - /* happens in some versions */ + /* happens in a few internal sounds from early Xbox games */ if (sb->stream_type > 0xFF) { VGM_LOG("UBI SB: garbage in stream_type\n"); sb->stream_type = 0; } + /* guess codec */ switch (sb->stream_type) { case 0x00: /* platform default (rarely external) */ switch (sb->platform) { case UBI_PC: - case UBI_3DS: sb->codec = RAW_PCM; break; @@ -987,14 +979,25 @@ static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile, int targe 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: /* PCM (Wii, rarely used) */ - sb->codec = RAW_PCM; + case 0x01: /* DSP (early games) or PCM (rarely used, ex. Wii/PSP/3DS) */ + switch (sb->version) { + case 0x00000003: /* Donald Duck: Goin' Quackers */ + sb->codec = RAW_DSP; + break; + default: + sb->codec = RAW_PCM; + break; + } break; case 0x02: /* PS ADPCM (PS3) */ @@ -1005,8 +1008,15 @@ static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile, int targe sb->codec = UBI_ADPCM; break; - case 0x04: /* Ogg (later PC games) */ - sb->codec = FMT_OGG; + case 0x04: /* Ubi IMA v3 (early games) or Ogg (later PC games) */ + switch (sb->version) { + case 0x00000007: /* Splinter Cell */ + sb->codec = UBI_ADPCM; + break; + default: + sb->codec = FMT_OGG; + break; + } break; case 0x05: /* AT3 (PSP, PS3) or XMA1 (X360) */ @@ -1032,8 +1042,15 @@ static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile, int targe sb->codec = RAW_AT3; break; - case 0x08: - sb->codec = FMT_AT3; + case 0x08: /* Ubi IMA v2 (early games) or ATRAC3 */ + switch (sb->version) { + case 0x00000003: /* Donald Duck: Goin' Quackers */ + sb->codec = UBI_ADPCM; + break; + default: + sb->codec = FMT_AT3; + break; + } break; default: @@ -1046,20 +1063,20 @@ static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile, int targe sb->xma_header_offset = read_32bit(sb->header_offset + sb->cfg_xma_offset, streamFile) + sb->sectionX_offset; } - /* uncommon but possible */ - //VGM_ASSERT(sb->is_external && sb->section3_num != 0, "UBI SS: mixed external and internal streams\n"); - - /* seems that can be safely ignored */ - //VGM_ASSERT(sb->is_external && sb->cfg_stream_id && sb->stream_id > 0, "UBI SB: unexpected external stream with stream id\n"); /* 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. */ + if (sb->is_map) { - /* maps store internal sounds offsets in a separate table, find the matching entry */ + /* 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 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); @@ -1067,15 +1084,11 @@ static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile, int targe int index = read_32bit(table_offset + 0x08 * j + 0x00, streamFile) & 0x0000FFFF; if (index == sb->header_index) { - if (!(sb->cfg_stream_id || sb->has_rotating_ids) && table2_num > 1) { - VGM_LOG("UBI SB: unexpected number of internal stream map groups %i\n", table2_num); + if (!sb->cfg_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->has_rotating_ids) { - sb->stream_id %= table2_num; - } - sb->stream_offset = read_32bit(table_offset + 0x08 * j + 0x04, streamFile); for (k = 0; k < table2_num; k++) { /* entry layout: @@ -1084,7 +1097,7 @@ static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile, int targe * 0x08 - size without padding * 0x0c - absolute offset */ uint32_t id = read_32bit(table2_offset + 0x10 * k + 0x00, streamFile); - if (id == sb->stream_id) { + if (id == sb->group_id) { sb->stream_offset += read_32bit(table2_offset + 0x10 * k + 0x0c, streamFile); break; } @@ -1099,14 +1112,14 @@ static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile, int targe } else { sb->stream_offset += sb->sounds_offset; - if ((sb->cfg_stream_id || sb->has_rotating_ids) && sb->section3_num > 1) { - /* internal sounds are split into groups based on their type with their offsets being relative to group start - * this table contains sizes of each group, adjust offset based on group ID of our sound */ + /* banks store internal sounds offsets in table: group id + group size, find the matching entry */ + + if (sb->cfg_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->stream_id) + if (read_32bit(offset + 0x00, streamFile) == sb->group_id) break; sb->stream_offset += read_32bit(offset + 0x04, streamFile); } @@ -1191,22 +1204,29 @@ fail: static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { int is_biadd_psp = 0; - /* The type 1 audio header varies with almost every game + platform (some kind of class serialization?), - * support is done case-by-case as offsets move slightly. Basic layout: - * - fixed part (0x00..0x14) - * - garbage, flags - * - external stream number of samples / internal garbage - * - external stream size (also in the common part) / internal garbage - * - garbage - * - external original sample rate / internal garbage - * - sample rate, pcm bits?, channels - * - stream type and external filename (internal filename too on some platforms) - * - end flags/garbage - * Garbage looks like uninitialized variables (may be null, contain part of strings, etc). - * Later version don't have filename per header but in the extra section. + /* 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: + * - 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 + * - external filename or internal filename on some platforms (earlier versions) + * - external filename offset in the extra table (later versions) + * - 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). + * + * 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. */ - //todo set samples flag where "varies" /* common */ sb->section3_entry_size = 0x08; @@ -1216,18 +1236,23 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { sb->cfg_extra_offset = 0x0c; sb->cfg_stream_offset = 0x10; + sb->and_external_flag = 0x01; + sb->and_loop_flag = 0x01; + + //todo: create generic configs to simplify parsing, since many share header but change entry sizes /* Batman: Vengeance (2001)(PS2)-map 0x00000003 */ - /* Batman: Vengeance (2001)(GC)-map 0x00000003 */ - /* Disney's Tarzan: Untamed (2001)(GC)-map 0x00000003 */ + /* Tom Clancy's Rainbow Six - Vegas 2 (2008)(PC)-? */ + /* Myst III (2008)(PS2)-? */ -#if 0 + + //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) + /* 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) { - /* Stream types: - * 0x01: Nintendo DSP ADPCM - * 0x08: unsupported codec, looks like standard IMA with custom 0x29 bytes header - */ sb->section1_entry_size = 0x40; sb->section2_entry_size = 0x6c; @@ -1237,18 +1262,22 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { sb->cfg_extra_offset = 0x10; sb->cfg_stream_offset = 0x14; + sb->cfg_group_id = 0x2c; sb->cfg_external_flag = 0x30; - sb->cfg_stream_id = 0x34; + sb->cfg_loop_flag = 0x34; sb->cfg_num_samples = 0x48; + sb->cfg_num_samples2 = 0x48; /* full loop */ sb->cfg_sample_rate = 0x50; sb->cfg_channels = 0x56; sb->cfg_stream_type = 0x58; sb->cfg_stream_name = 0x5c; sb->has_short_channels = 1; + sb->has_full_loop = 1; + //has layer 0d return 1; } -#endif + #if 0 /* Splinter Cell (2002)(PC)-map */ /* Splinter Cell: Pandora Tomorrow (2004)(PC)-map */ @@ -1267,9 +1296,11 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { sb->cfg_extra_offset = 0x10; sb->cfg_stream_offset = 0x14; + //sb->cfg_loop_flag = 0x24; //? sb->cfg_external_flag = 0x28; - sb->cfg_stream_id = 0x2c; + sb->cfg_group_id = 0x2c; sb->cfg_num_samples = 0x30; + //sb->cfg_num_samples2 = 0x38; sb->cfg_sample_rate = 0x44; sb->cfg_channels = 0x4a; sb->cfg_stream_type = 0x4c; @@ -1277,18 +1308,54 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { sb->has_short_channels = 1; sb->has_internal_names = 1; + //has layer 0d return 1; } #endif +#if 0 + /* Splinter Cell (2002)(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; + sb->map_version = 1; + + sb->cfg_stream_size = 0x0c; + sb->cfg_extra_offset = 0x10; + sb->cfg_stream_offset = 0x14; + + sb->cfg_group_id = 0x24? 0x2c; + sb->cfg_external_flag = 0x28; + sb->cfg_loop_flag = 0x2c? 0x24? + sb->cfg_num_samples = 0x30; + sb->cfg_num_samples2 = 0x38; + sb->cfg_sample_rate = 0x44; + sb->cfg_channels = 0x4a; + sb->cfg_stream_type = 0x4c; + sb->cfg_stream_name = 0x50; + + sb->has_short_channels = 1; + sb->has_internal_names = 1; + //has layer 0d + 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->cfg_external_flag = 0x24; /* maybe 0x28 */ + sb->cfg_external_flag = 0x24; + sb->cfg_loop_flag = 0x28; + sb->cfg_group_id = 0x2c; sb->cfg_num_samples = 0x30; + sb->cfg_num_samples2 = 0x38; sb->cfg_sample_rate = 0x44; sb->cfg_channels = 0x4a; sb->cfg_stream_type = 0x4c; @@ -1306,13 +1373,20 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { sb->section1_entry_size = 0x48; sb->section2_entry_size = 0x6c; - sb->cfg_external_flag = 0; /* no apparent flag */ + sb->cfg_external_flag = 0x18; + sb->cfg_loop_flag = 0x18; + sb->cfg_group_id = 0x18; sb->cfg_channels = 0x20; sb->cfg_sample_rate = 0x24; - sb->cfg_num_samples = 0x30; + sb->cfg_num_samples = 0x30; /* may be null */ + sb->cfg_num_samples2 = 0x38; /* may be null */ sb->cfg_stream_name = 0x40; sb->cfg_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 (main game) return 1; } @@ -1323,8 +1397,11 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { sb->section1_entry_size = 0x64; sb->section2_entry_size = 0x78; - sb->cfg_external_flag = 0x24; /* maybe 0x28 */ + sb->cfg_external_flag = 0x24; + sb->cfg_group_id = 0x28; + sb->cfg_loop_flag = 0x2c; sb->cfg_num_samples = 0x30; + sb->cfg_num_samples2 = 0x38; sb->cfg_sample_rate = 0x44; sb->cfg_channels = 0x4a; sb->cfg_stream_type = 0x4c; /* may contain garbage */ @@ -1336,10 +1413,9 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { return 1; } - - //todo Batman offsets are slightly off for some subsongs, or maybe get external wrong (ex. 222~226) - /* Batman: Rise of Sin Tzu (2003)(GC)-map - 0x000A0002 */ - /* Prince of Persia: Sands of Time (2003)(GC)-bank */ + // 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; @@ -1347,8 +1423,11 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { sb->map_version = 2; - sb->cfg_external_flag = 0x20; /* maybe 0x24 */ + sb->cfg_external_flag = 0x20; + sb->cfg_group_id = 0x24; + sb->cfg_loop_flag = 0x28; sb->cfg_num_samples = 0x2c; + sb->cfg_num_samples2 = 0x34; sb->cfg_sample_rate = 0x40; sb->cfg_channels = 0x46; sb->cfg_stream_type = 0x48; @@ -1364,24 +1443,57 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { sb->section1_entry_size = 0x48; sb->section2_entry_size = 0x6c; - sb->cfg_external_flag = 0; /* no apparent flag */ + sb->cfg_external_flag = 0x18; + sb->cfg_loop_flag = 0x18; + sb->cfg_group_id = 0x18; sb->cfg_channels = 0x20; sb->cfg_sample_rate = 0x24; - sb->cfg_num_samples = 0x30; + sb->cfg_num_samples = 0x30; /* may be null */ + sb->cfg_num_samples2 = 0x38; /* may be null */ sb->cfg_stream_name = 0x40; sb->cfg_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->cfg_external_flag = 0x18; + sb->cfg_loop_flag = 0x18; + sb->cfg_group_id = 0x18; + sb->cfg_channels = 0x20; + sb->cfg_sample_rate = 0x24; + sb->cfg_num_samples = 0x30; /* may be null */ + sb->cfg_num_samples2 = 0x38; /* may be null */ + sb->cfg_stream_name = 0x40; + sb->cfg_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? + return 1; + } + /* Myst IV Demo (2004)(PC)-bank */ if (sb->version == 0x00100000 && sb->platform == UBI_PC) { /* final game is different */ sb->section1_entry_size = 0x68; sb->section2_entry_size = 0xa4; sb->cfg_external_flag = 0x24; - sb->cfg_num_samples = 0x34; + sb->cfg_loop_flag = 0x28; + sb->cfg_group_id = 0x2c; + sb->cfg_num_samples = 0x30; + sb->cfg_num_samples2 = 0x38; sb->cfg_sample_rate = 0x44; sb->cfg_channels = 0x4c; sb->cfg_stream_type = 0x50; @@ -1397,7 +1509,10 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { sb->section2_entry_size = 0x84; sb->cfg_external_flag = 0x24; + sb->cfg_loop_flag = 0x28; + sb->cfg_group_id = 0x2c; sb->cfg_num_samples = 0x30; + sb->cfg_num_samples2 = 0x38; sb->cfg_sample_rate = 0x44; sb->cfg_channels = 0x4c; sb->cfg_stream_type = 0x50; @@ -1413,13 +1528,20 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { sb->section1_entry_size = 0x48; sb->section2_entry_size = 0x6c; - sb->cfg_external_flag = 0; /* no apparent flag */ + sb->cfg_external_flag = 0x18; + sb->cfg_loop_flag = 0x18; + sb->cfg_group_id = 0x18; sb->cfg_channels = 0x20; sb->cfg_sample_rate = 0x24; - sb->cfg_num_samples = 0x30; + sb->cfg_num_samples = 0x30; /* may be null */ + sb->cfg_num_samples2 = 0x38; /* may be null */ sb->cfg_stream_name = 0x40; sb->cfg_stream_type = 0x68; + sb->and_external_flag = 0x04; + sb->and_loop_flag = 0x10; + sb->and_group_id = 0x08; + sb->shr_group_id = 3; //no layers return 1; } @@ -1430,7 +1552,10 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { sb->section2_entry_size = 0x90; sb->cfg_external_flag = 0x24; + sb->cfg_group_id = 0x28; + sb->cfg_loop_flag = 0x40; sb->cfg_num_samples = 0x44; + sb->cfg_num_samples2 = 0x4c; sb->cfg_sample_rate = 0x58; sb->cfg_channels = 0x60; sb->cfg_stream_type = 0x64; /* may contain garbage */ @@ -1447,7 +1572,10 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { sb->section2_entry_size = 0x78; sb->cfg_external_flag = 0x20; + sb->cfg_group_id = 0x24; + sb->cfg_loop_flag = 0x28; sb->cfg_num_samples = 0x2c; + sb->cfg_num_samples2 = 0x34; sb->cfg_sample_rate = 0x40; sb->cfg_channels = 0x48; sb->cfg_stream_type = 0x4c; @@ -1457,7 +1585,7 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { return 1; } - /* two configs with same id; use project file as identifier */ + /* two configs with same id and both sb4/sm4; use project file as identifier */ if (sb->version == 0x0012000C && sb->platform == UBI_PSP) { STREAMFILE * streamTest = open_streamfile_by_filename(streamFile, "BIAAUDIO.SP4"); if (streamTest) { @@ -1468,7 +1596,7 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { /* Prince of Persia: Revelations (2005)(PSP)-bank */ /* Splinter Cell: Essentials (2006)(PSP)-map */ - /* Beowulf - The Game (2007)(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; @@ -1476,7 +1604,10 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { sb->map_version = 2; sb->cfg_external_flag = 0x24; + sb->cfg_loop_flag = 0x28; + sb->cfg_group_id = 0x2c; sb->cfg_num_samples = 0x30; + sb->cfg_num_samples2 = 0x38; sb->cfg_sample_rate = 0x44; sb->cfg_channels = 0x4c; sb->cfg_stream_type = 0x50; @@ -1484,20 +1615,18 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { sb->has_internal_names = 1; //has layers 06 (SC:E only) - - //todo Beowulf needs rotating ids, but fails in some subsong offsets - // (ex. subsong 33415 should point to AT3 but offset is a VAGp) return 1; } + //todo check, bad external stream offsets? /* 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; sb->cfg_external_flag = 0x24; - sb->cfg_samples_flag = 0x28; - sb->cfg_stream_id = 0x2c; + sb->cfg_loop_flag = 0x28; + sb->cfg_group_id = 0x2c; sb->cfg_num_samples = 0x30; sb->cfg_num_samples2 = 0x38; sb->cfg_sample_rate = 0x44; @@ -1509,7 +1638,6 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { return 1; } -#if 0 /* Splinter Cell 3D (2011)(3DS)-map */ if (sb->version == 0x00130001 && sb->platform == UBI_3DS) { sb->section1_entry_size = 0x48; @@ -1517,25 +1645,23 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { sb->map_version = 2; - sb->cfg_external_flag = 0; - //sb->cfg_stream_id = 0; + sb->cfg_external_flag = 0x18; + sb->cfg_loop_flag = 0x18; + sb->cfg_group_id = 0x18; sb->cfg_num_samples = 0x1c; + sb->cfg_num_samples2 = 0x24; sb->cfg_sample_rate = 0x30; sb->cfg_channels = 0x38; sb->cfg_stream_type = 0x3c; sb->cfg_extra_name = 0x40; - sb->has_extra_name_flag = 1; - sb->has_rotating_ids = 1; //todo? + sb->and_external_flag = 0x04; + sb->and_loop_flag = 0x10; + sb->and_group_id = 0x08; + sb->shr_group_id = 3; //has layer 06 - - //todo SC3DS codecs: - // 00: RAW_PCM - // 01: FMT_CWAV - // 03: Ubi IMA return 1; } -#endif /* Prince of Persia: The Two Thrones (2005)(PC)-bank */ if (sb->version == 0x00150000 && sb->platform == UBI_PC) { @@ -1543,14 +1669,15 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { sb->section2_entry_size = 0x78; sb->cfg_external_flag = 0x2c; - sb->cfg_stream_id = 0x34; + sb->cfg_loop_flag = 0x30; + sb->cfg_group_id = 0x34; sb->cfg_num_samples = 0x40; + sb->cfg_num_samples2 = 0x48; sb->cfg_sample_rate = 0x54; sb->cfg_channels = 0x5c; sb->cfg_stream_type = 0x60; sb->cfg_extra_name = 0x64; - sb->has_extra_name_flag = 1; //no layers return 1; } @@ -1560,14 +1687,20 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { sb->section1_entry_size = 0x48; sb->section2_entry_size = 0x5c; - sb->cfg_external_flag = 0; + sb->cfg_external_flag = 0x20; + sb->cfg_loop_flag = 0x20; + sb->cfg_group_id = 0x20; sb->cfg_channels = 0x2c; sb->cfg_sample_rate = 0x30; sb->cfg_num_samples = 0x3c; + sb->cfg_num_samples2 = 0x44; sb->cfg_extra_name = 0x4c; sb->cfg_stream_type = 0x50; - sb->has_extra_name_flag = 1; + sb->and_external_flag = 0x04; + sb->and_loop_flag = 0x10; + sb->and_group_id = 0x08; + sb->shr_group_id = 3; //no layers return 1; } @@ -1577,16 +1710,20 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { sb->section1_entry_size = 0x48; sb->section2_entry_size = 0x58; - sb->cfg_external_flag = 0; - sb->cfg_stream_id = 0; + sb->cfg_external_flag = 0x20; + sb->cfg_loop_flag = 0x20; + sb->cfg_group_id = 0x20; sb->cfg_num_samples = 0x28; + sb->cfg_num_samples2 = 0x30; sb->cfg_sample_rate = 0x3c; sb->cfg_channels = 0x44; sb->cfg_stream_type = 0x48; sb->cfg_extra_name = 0x4c; - sb->has_extra_name_flag = 1; - sb->has_rotating_ids = 1; + sb->and_external_flag = 0x0008; + sb->and_loop_flag = 0x0400; + sb->and_group_id = 0x0010; + sb->shr_group_id = 4; //no layers return 1; } @@ -1596,8 +1733,11 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { sb->section1_entry_size = 0x68; sb->section2_entry_size = 0x6c; - sb->cfg_external_flag = 0x28; /* maybe 0x2c */ + sb->cfg_external_flag = 0x28; + //sb->cfg_group_id = 0x2c; /* assumed */ + sb->cfg_loop_flag = 0x30; sb->cfg_num_samples = 0x3c; + sb->cfg_num_samples2 = 0x44; sb->cfg_sample_rate = 0x50; sb->cfg_channels = 0x58; sb->cfg_stream_type = 0x5c; @@ -1615,7 +1755,10 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { sb->map_version = 2; sb->cfg_external_flag = 0x24; + //sb->cfg_group_id = 0x28; + //sb->cfg_loop_flag = 0x2c; //todo test sb->cfg_num_samples = 0x30; + sb->cfg_num_samples2 = 0x38; sb->cfg_sample_rate = 0x44; sb->cfg_channels = 0x4c; sb->cfg_stream_type = 0x50; @@ -1631,16 +1774,21 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { sb->map_version = 2; - sb->cfg_external_flag = 0; - sb->cfg_stream_id = 0; - sb->cfg_num_samples = 0x18; + sb->cfg_external_flag = 0x18; + sb->cfg_group_id = 0x18; + sb->cfg_loop_flag = 0x18; + sb->cfg_num_samples = 0x1c; + sb->cfg_num_samples2 = 0x24; sb->cfg_sample_rate = 0x30; sb->cfg_channels = 0x38; sb->cfg_stream_type = 0x3c; sb->cfg_extra_name = 0x40; - sb->has_extra_name_flag = 1; - sb->has_rotating_ids = 1; + sb->and_external_flag = 0x0008; + sb->and_loop_flag = 0x0400; + sb->and_group_id = 0x0010; + sb->shr_group_id = 4; + //no layers return 1; } @@ -1650,25 +1798,31 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { sb->section1_entry_size = 0x48; sb->section2_entry_size = 0x6c; - sb->cfg_external_flag = 0; - sb->cfg_stream_id = 0; + sb->cfg_external_flag = 0x20?; + sb->cfg_group_id = 0x20?; + sb->cfg_loop_flag = 0x20?; sb->cfg_num_samples = 0x28; + sb->cfg_num_samples2 = 0x30?; sb->cfg_sample_rate = 0x3c; sb->cfg_channels = 0x44; sb->cfg_stream_type = 0x48; sb->cfg_extra_name = 0x58; + ? return 1; } #endif /* Red Steel (2006)(Wii)-bank */ - if (sb->version == 0x00180006 && sb->platform == UBI_WII) { /* same as 0x00150000 */ + if (sb->version == 0x00180006 && sb->platform == UBI_WII) { sb->section1_entry_size = 0x68; sb->section2_entry_size = 0x6c; - sb->cfg_external_flag = 0x28; /* maybe 0x2c */ + sb->cfg_external_flag = 0x28; + //sb->cfg_group_id = 0x2c; /* assumed */ + sb->cfg_loop_flag = 0x30; sb->cfg_num_samples = 0x3c; + sb->cfg_num_samples2 = 0x44; sb->cfg_sample_rate = 0x50; sb->cfg_channels = 0x58; sb->cfg_stream_type = 0x5c; @@ -1685,7 +1839,8 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { sb->map_version = 3; sb->cfg_external_flag = 0x2c; - sb->cfg_stream_id = 0x34; + //sb->cfg_loop_flag = 0x30; //todo test + sb->cfg_group_id = 0x34; sb->cfg_channels = 0x5c; sb->cfg_sample_rate = 0x54; sb->cfg_num_samples = 0x40; @@ -1704,8 +1859,8 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { sb->map_version = 3; sb->cfg_external_flag = 0x2c; - sb->cfg_stream_id = 0x30; - sb->cfg_samples_flag = 0x34; + sb->cfg_group_id = 0x30; + sb->cfg_loop_flag = 0x34; sb->cfg_channels = 0x5c; sb->cfg_sample_rate = 0x54; sb->cfg_num_samples = 0x40; @@ -1714,6 +1869,7 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { sb->cfg_extra_name = 0x64; sb->cfg_xma_offset = 0x70; + //has layer 06 return 1; } @@ -1724,16 +1880,21 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { sb->map_version = 3; - sb->cfg_external_flag = 0; + sb->cfg_external_flag = 0x20; + sb->cfg_group_id = 0x20; + sb->cfg_loop_flag = 0x20; sb->cfg_num_samples = 0x28; - sb->cfg_stream_id = 0; + sb->cfg_num_samples2 = 0x30; sb->cfg_sample_rate = 0x3c; sb->cfg_channels = 0x44; sb->cfg_stream_type = 0x48; sb->cfg_extra_name = 0x4c; - sb->has_extra_name_flag = 1; - sb->has_rotating_ids = 1; + sb->and_external_flag = 0x0008; + sb->and_loop_flag = 0x0400; + sb->and_group_id = 0x0010; + sb->shr_group_id = 4; + //no layers return 1; } @@ -1745,8 +1906,10 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { sb->map_version = 3; sb->cfg_external_flag = 0x28; - sb->cfg_stream_id = 0x2c; + sb->cfg_group_id = 0x2c; + sb->cfg_loop_flag = 0x30; sb->cfg_num_samples = 0x3c; + sb->cfg_num_samples2 = 0x44; sb->cfg_sample_rate = 0x50; sb->cfg_channels = 0x58; sb->cfg_stream_type = 0x5c; @@ -1755,75 +1918,95 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { return 1; } - /* Open Season (2005)(PS2)-map - 0x00180003 */ - /* Open Season (2005)(PSP)-map - 0x00180003 */ - /* Star Wars - Lethal Alliance (2006)(PSP)-map - 0x00180007 */ + /* 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->cfg_external_flag = 0x20; + sb->cfg_loop_flag = 0x20; + sb->cfg_group_id = 0x20; + sb->cfg_channels = 0x28; + sb->cfg_sample_rate = 0x2c; + sb->cfg_num_samples = 0x34; + sb->cfg_num_samples2 = 0x38; + sb->cfg_extra_name = 0x44; + sb->cfg_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; + } + + /* 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) || (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; sb->map_version = 3; - sb->cfg_external_flag = 0; - sb->cfg_samples_bitflag = 0x20; + sb->cfg_external_flag = 0x20; + sb->cfg_loop_flag = 0x20; + sb->cfg_group_id = 0x20; sb->cfg_channels = 0x28; sb->cfg_sample_rate = 0x2c; sb->cfg_num_samples = 0x34; - sb->cfg_num_samples = 0x3c; + sb->cfg_num_samples2 = 0x3c; sb->cfg_extra_name = 0x44; sb->cfg_stream_type = 0x48; - sb->has_extra_name_flag = 1; + 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; } - /* Prince of Persia: Rival Swords (2007)(PSP)-bank */ - if (sb->version == 0x00180005 && sb->platform == UBI_PSP) { - sb->section1_entry_size = 0x48; - sb->section2_entry_size = 0x54; - - sb->cfg_external_flag = 0; - sb->cfg_channels = 0x28; - sb->cfg_sample_rate = 0x2c; - //sb->cfg_num_samples = 0x34 or 0x3c /* varies */ - sb->cfg_extra_name = 0x44; - sb->cfg_stream_type = 0x48; - - sb->has_extra_name_flag = 1; - return 1; - } - -#if 0 //todo join with other 0x0018000x configs? - /* Rainbow Six Vegas (2007)(PSP)-bank */ - if (sb->version == 0x00180006 && sb->platform == UBI_PSP) { - sb->section1_entry_size = 0x48; - sb->section2_entry_size = 0x54; - - sb->cfg_external_flag = 0; - sb->cfg_channels = 0x28; - sb->cfg_sample_rate = 0x2c; - //sb->cfg_num_samples = 0x34 or 0x3c /* varies */ - sb->cfg_extra_name = 0x44; - sb->cfg_stream_type = 0x48; - - sb->has_extra_name_flag = 1; - sb->has_rotating_ids = 1; //??? - return 1; - } -#endif - /* 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; - sb->cfg_external_flag = 0x28; /* maybe 0x2c */ + sb->cfg_external_flag = 0x28; + //sb->cfg_group_id = 0x2c; /* assumed */ + sb->cfg_loop_flag = 0x30; sb->cfg_channels = 0x3c; sb->cfg_sample_rate = 0x40; sb->cfg_num_samples = 0x48; + sb->cfg_num_samples2 = 0x50; + sb->cfg_extra_name = 0x58; + sb->cfg_stream_type = 0x5c; + + //has layer 06 (TMNT) + return 1; + } + + /* TMNT (2007)(GC)-bank */ + if (sb->version == 0x00190002 && sb->platform == UBI_GC) { + sb->section1_entry_size = 0x68; + sb->section2_entry_size = 0x6c; + + sb->cfg_external_flag = 0x28; + //sb->cfg_group_id = 0x2c; /* assumed */ + sb->cfg_loop_flag = 0x30; + sb->cfg_channels = 0x3c; + sb->cfg_sample_rate = 0x40; + sb->cfg_num_samples = 0x48; + sb->cfg_num_samples2 = 0x50; sb->cfg_extra_name = 0x58; sb->cfg_stream_type = 0x5c; @@ -1836,12 +2019,14 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { sb->section2_entry_size = 0x74; sb->cfg_external_flag = 0x28; - sb->cfg_stream_id = 0x2c; + sb->cfg_group_id = 0x2c; + sb->cfg_loop_flag = 0x30; sb->cfg_channels = 0x3c; sb->cfg_sample_rate = 0x40; sb->cfg_num_samples = 0x48; - sb->cfg_stream_type = 0x5c; + sb->cfg_num_samples2 = 0x50; sb->cfg_extra_name = 0x58; + sb->cfg_stream_type = 0x5c; return 1; } @@ -1851,40 +2036,30 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { sb->section1_entry_size = 0x48; sb->section2_entry_size = 0x5c; - sb->cfg_external_flag = 0; + sb->cfg_external_flag = 0x20; + sb->cfg_loop_flag = 0x20; + //sb->cfg_group_id = 0x20; /* assumed */ sb->cfg_channels = 0x28; sb->cfg_sample_rate = 0x2c; - //sb->cfg_num_samples = 0x34 or 0x3c /* varies */ + sb->cfg_num_samples = 0x34; + sb->cfg_num_samples2 = 0x3c; sb->cfg_extra_name = 0x44; sb->cfg_stream_type = 0x48; - sb->has_extra_name_flag = 1; + sb->and_external_flag = 0x04; + sb->and_loop_flag = 0x10; //has layer 06 return 1; } - /* TMNT (2007)(GC)-bank */ - if (sb->version == 0x00190002 && sb->platform == UBI_GC) { /* same as 0x00190003 */ - sb->section1_entry_size = 0x68; - sb->section2_entry_size = 0x6c; - - sb->cfg_external_flag = 0x28; /* maybe 0x2c */ - sb->cfg_channels = 0x3c; - sb->cfg_sample_rate = 0x40; - sb->cfg_num_samples = 0x48; - sb->cfg_stream_type = 0x5c; - sb->cfg_extra_name = 0x58; - - return 1; - } - - /* TMNT (2007)(X360)-bank */ + /* TMNT (2007)(X360)-bank */ if (sb->version == 0x00190002 && sb->platform == UBI_X360) { sb->section1_entry_size = 0x68; sb->section2_entry_size = 0x70; sb->cfg_external_flag = 0x28; - sb->cfg_samples_flag = 0x30; + //sb->cfg_group_id = 0x2c; /* assumed */ + sb->cfg_loop_flag = 0x30; sb->cfg_channels = 0x3c; sb->cfg_sample_rate = 0x40; sb->cfg_num_samples = 0x48; @@ -1903,14 +2078,18 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { sb->map_version = 3; - sb->cfg_external_flag = 0; + sb->cfg_external_flag = 0x20; + sb->cfg_loop_flag = 0x20; + //sb->cfg_group_id = 0x20; /* assumed */ sb->cfg_channels = 0x28; sb->cfg_sample_rate = 0x2c; sb->cfg_num_samples = 0x34; + sb->cfg_num_samples2 = 0x3c; sb->cfg_stream_type = 0x48; sb->cfg_extra_name = 0x44; - sb->has_extra_name_flag = 1; + sb->and_external_flag = 0x04; + sb->and_loop_flag = 0x10; //has layer 06 return 1; } @@ -1920,12 +2099,15 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { sb->section1_entry_size = 0x68; sb->section2_entry_size = 0x74; - sb->cfg_external_flag = 0x28; /* maybe 0x2c */ + sb->cfg_external_flag = 0x28; + //sb->cfg_group_id = 0x2c; /* assumed */ + sb->cfg_loop_flag = 0x30; sb->cfg_channels = 0x3c; sb->cfg_sample_rate = 0x40; sb->cfg_num_samples = 0x48; - sb->cfg_stream_type = 0x5c; + sb->cfg_num_samples2 = 0x50; sb->cfg_extra_name = 0x58; + sb->cfg_stream_type = 0x5c; return 1; } @@ -1939,12 +2121,14 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { sb->map_version = 3; sb->cfg_external_flag = 0x28; - sb->cfg_stream_id = 0x2c; + sb->cfg_group_id = 0x2c; + sb->cfg_loop_flag = 0x30; sb->cfg_channels = 0x3c; sb->cfg_sample_rate = 0x40; sb->cfg_num_samples = 0x48; - sb->cfg_stream_type = 0x5c; + sb->cfg_num_samples2 = 0x50; sb->cfg_extra_name = 0x58; + sb->cfg_stream_type = 0x5c; return 1; } @@ -1955,7 +2139,8 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { sb->section2_entry_size = 0x70; sb->cfg_external_flag = 0x28; - sb->cfg_samples_flag = 0x30; + //sb->cfg_group_id = 0x2c; /* assumed */ + sb->cfg_loop_flag = 0x30; sb->cfg_channels = 0x3c; sb->cfg_sample_rate = 0x40; sb->cfg_num_samples = 0x48; @@ -1974,14 +2159,18 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { sb->map_version = 3; - sb->cfg_external_flag = 0; + sb->cfg_external_flag = 0x20; + sb->cfg_loop_flag = 0x20; + //sb->cfg_group_id = 0x20; /* assumed */ sb->cfg_channels = 0x28; sb->cfg_sample_rate = 0x2c; sb->cfg_num_samples = 0x34; - sb->cfg_stream_type = 0x48; + sb->cfg_num_samples2 = 0x3c; sb->cfg_extra_name = 0x44; + sb->cfg_stream_type = 0x48; - sb->has_extra_name_flag = 1; + sb->and_external_flag = 0x04; + sb->and_loop_flag = 0x10; //no layers return 1; } @@ -1993,14 +2182,18 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { sb->map_version = 3; - sb->cfg_external_flag = 0; + sb->cfg_external_flag = 0x20; + sb->cfg_loop_flag = 0x20; + //sb->cfg_group_id = 0x20; /* assumed */ sb->cfg_channels = 0x28; sb->cfg_sample_rate = 0x30; sb->cfg_num_samples = 0x38; + sb->cfg_num_samples2 = 0x40; sb->cfg_extra_name = 0x48; - sb->cfg_stream_type = 0x4c; /* 06|08 */ + sb->cfg_stream_type = 0x4c; - sb->has_extra_name_flag = 1; + sb->and_external_flag = 0x04; + sb->and_loop_flag = 0x20; //no layers return 1; } @@ -2013,14 +2206,14 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { sb->map_version = 3; sb->cfg_external_flag = 0x28; - sb->cfg_stream_id = 0x30; - sb->cfg_samples_flag = 0x34; + sb->cfg_group_id = 0x30; + sb->cfg_loop_flag = 0x34; sb->cfg_channels = 0x44; sb->cfg_sample_rate = 0x4c; sb->cfg_num_samples = 0x54; sb->cfg_num_samples2 = 0x5c; - sb->cfg_stream_type = 0x68; sb->cfg_extra_name = 0x64; + sb->cfg_stream_type = 0x68; //has layer 06 return 1;