From adea00edab082912bf3b2d836b5dcb4507bfe41e Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 5 Jan 2019 04:02:14 +0100 Subject: [PATCH] Tweak Ubi sb/xmX for clarity and add more games --- src/meta/ubi_sb.c | 1493 +++++++++++++++++++++++++-------------------- 1 file changed, 835 insertions(+), 658 deletions(-) diff --git a/src/meta/ubi_sb.c b/src/meta/ubi_sb.c index 3183310d..1da0bd9b 100644 --- a/src/meta/ubi_sb.c +++ b/src/meta/ubi_sb.c @@ -12,13 +12,26 @@ typedef struct { int autodetect_external; ubi_sb_codec codec; - /* main/fixed info */ + /* map base header info */ + off_t map_start; + off_t map_num; + size_t map_entry_size; + + uint32_t map_type; + uint32_t map_zero; + off_t map_offset; + off_t map_size; + char map_name[255]; + uint32_t map_unknown; + + /* SB main/fixed info */ uint32_t version; + uint32_t version_empty; size_t section1_num; size_t section2_num; size_t section3_num; size_t section4_num; - size_t extra_size; + size_t sectionX_size; int flag1; int flag2; @@ -26,74 +39,72 @@ typedef struct { int is_map; int map_version; - /* stream info config (format varies slightly per game) */ + /* header/stream info config */ + /* audio header varies slightly per game but not enough parse case by case, + * instead we configure sizes and offsets to where each variable is */ size_t section1_entry_size; size_t section2_entry_size; size_t section3_entry_size; - off_t stream_size_offset; - off_t extra_pointer_offset; - off_t stream_pointer_offset; - off_t external_flag_offset; - off_t samples_flag_offset; - off_t num_samples_offset; - off_t num_samples_offset2; - off_t sample_rate_offset; - off_t channels_offset; - off_t stream_type_offset; - off_t stream_name_offset; - off_t extra_name_offset; - size_t stream_name_size; - off_t stream_id_offset; - off_t xma_pointer_offset; - int has_short_channels; - int has_internal_names; - int has_extra_name_flag; - int has_rotating_ids; + size_t resource_name_size; + off_t cfg_stream_size; + off_t cfg_stream_offset; + off_t cfg_extra_offset; + off_t cfg_stream_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_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 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 */ /* derived */ size_t section1_offset; size_t section2_offset; size_t section3_offset; size_t section4_offset; - size_t extra_section_offset; + size_t sectionX_offset; size_t sounds_offset; - /* map info */ - off_t map_header_offset; - off_t map_num; - off_t map_offset; - char map_name[255]; + /* header/stream info */ + uint32_t header_id; /* 16b+16b group+sound id identifier (should be unique within 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 */ - /* stream info */ - uint32_t header_id; - uint32_t header_type; - size_t stream_size; - off_t extra_offset; - off_t stream_offset; - uint32_t stream_id; - off_t xma_header_offset; - - int stream_samples; /* usually only for external resources */ + uint32_t stream_id; /* internal resource id needed to locate info */ + uint32_t stream_type; /* rough codec value */ + int num_samples; /* usually only for external resources */ int sample_rate; int channels; - uint32_t stream_type; - char stream_name[255]; - char extra_name[255]; - int header_idx; - off_t header_offset; + 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 */ - int subtypes[16]; + 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 types[16]; /* counts for each possible header types, for debugging */ } ubi_sb_header; static VGMSTREAM * init_vgmstream_ubi_sb_main(ubi_sb_header *sb, STREAMFILE *streamFile); static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile, int target_stream); -static int config_sb_header_version(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 sound engine ("DARE" / "UbiSound Driver") games in ~2000-2008+ */ VGMSTREAM * init_vgmstream_ubi_sb(STREAMFILE *streamFile) { 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; int target_stream = streamFile->stream_index; @@ -102,12 +113,12 @@ VGMSTREAM * init_vgmstream_ubi_sb(STREAMFILE *streamFile) { if (!check_extensions(streamFile, "sb0,sb1,sb2,sb3,sb4,sb5,sb6,sb7")) goto fail; - if (target_stream == 0) target_stream = 1; - - /* .sb0 (sound bank) is a small multisong format (loaded in memory?) that contains SFX data + /* .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). */ + + /* PLATFORM DETECTION */ /* sigh... PSP hijacks not one but *two* platform indexes */ /* please add any PSP game versions under sb4 and sb5 sections so we can properly identify platform */ sb.version = read_32bitLE(0x00, streamFile); @@ -147,50 +158,50 @@ VGMSTREAM * init_vgmstream_ubi_sb(STREAMFILE *streamFile) { goto fail; } + /* CONFIG */ sb.big_endian = (sb.platform == UBI_GC || sb.platform == UBI_PS3 || sb.platform == UBI_X360 || sb.platform == UBI_WII); if (sb.big_endian) { read_32bit = read_32bitBE; - //read_16bit = read_16bitBE; } else { read_32bit = read_32bitLE; - //read_16bit = read_16bitLE; } - /* file layout is: base header, section1, section2, extra section, section3, data (all except base header can be null) */ - - 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.extra_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 */ - - ok = config_sb_header_version(&sb, streamFile); - if (!ok) { - VGM_LOG("UBI SB: unknown SB version+platform\n"); - goto fail; - } - - sb.section1_offset = 0x1c; - sb.section2_offset = sb.section1_offset + sb.section1_entry_size * sb.section1_num; - sb.extra_section_offset = sb.section2_offset + sb.section2_entry_size * sb.section2_num; - sb.section3_offset = sb.extra_section_offset + sb.extra_size; - sb.sounds_offset = sb.section3_offset + sb.section3_entry_size * sb.section3_num; - sb.is_map = 0; + if (target_stream == 0) target_stream = 1; /* use smaller I/O buffer for performance, as this read lots of small headers all over the place */ streamTest = reopen_streamfile(streamFile, 0x100); if (!streamTest) goto fail; - /* main parse */ + + /* 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 */ + + ok = config_sb_version(&sb, streamFile); + if (!ok) 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_stream)) goto fail; close_streamfile(streamTest); + + /* CREATE VGMSTREAM */ return init_vgmstream_ubi_sb_main(&sb, streamFile); fail: @@ -198,22 +209,26 @@ fail: return NULL; } -/* .SMx - essentially a set of SBx files, one per map, compiled into one file */ +/* .SMx - maps (sets of custom SBx files) also from Ubisoft's sound engine games in ~2000-2008+ */ VGMSTREAM * init_vgmstream_ubi_sm(STREAMFILE *streamFile) { STREAMFILE *streamTest = NULL; int32_t(*read_32bit)(off_t, STREAMFILE*) = NULL; //int16_t(*read_16bit)(off_t, STREAMFILE*) = NULL; ubi_sb_header sb = { 0 }; - size_t map_entry_size; int ok, i; int target_stream = streamFile->stream_index; + /* check extension (number represents the platform, see later) */ if (!check_extensions(streamFile, "sm0,sm1,sm2,sm3,sm4,sm5,sm6,sm7,lm0,lm1,lm2,lm3,lm4,lm5,lm6,lm7")) goto fail; - if (target_stream == 0) target_stream = 1; + /* .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...). + * This counts subsongs from all sbX, so totals can be massive, but there are splitters into mini-smX. */ + + /* PLATFORM DETECTION */ /* sigh... PSP hijacks not one but *two* platform indexes */ /* please add any PSP game versions under sb4 and sb5 sections so we can properly identify platform */ sb.version = read_32bitLE(0x00, streamFile); @@ -237,8 +252,11 @@ VGMSTREAM * init_vgmstream_ubi_sm(STREAMFILE *streamFile) { } } else if (check_extensions(streamFile, "sm5,lm5")) { switch (sb.version) { + case 0x00180003: /* Open Season (2007) */ + case 0x00180007: /* Star Wars - Lethal Alliance (2007) */ case 0x00190001: /* TMNT (2007) */ case 0x00190005: /* Surf's Up (2007) */ + case 0x001D0000: /* Michael Jackson: The Experience (2010) */ sb.platform = UBI_PSP; break; default: @@ -253,74 +271,79 @@ VGMSTREAM * init_vgmstream_ubi_sm(STREAMFILE *streamFile) { goto fail; } + /* CONFIG */ sb.big_endian = (sb.platform == UBI_GC || sb.platform == UBI_PS3 || sb.platform == UBI_X360 || sb.platform == UBI_WII); if (sb.big_endian) { read_32bit = read_32bitBE; - //read_16bit = read_16bitBE; } else { read_32bit = read_32bitLE; - //read_16bit = read_16bitLE; } - sb.is_map = 1; - sb.version = read_32bit(0x00, streamFile); - sb.map_header_offset = read_32bit(0x04, streamFile); - sb.map_num = read_32bit(0x08, streamFile); - - ok = config_sb_header_version(&sb, streamFile); - if (!ok || sb.map_version == 0) { - VGM_LOG("UBI SB: unknown SM version+platform\n"); - goto fail; - } + if (target_stream == 0) target_stream = 1; /* use smaller I/O buffer for performance, as this read lots of small headers all over the place */ streamTest = reopen_streamfile(streamFile, 0x100); if (!streamTest) goto fail; - map_entry_size = (sb.map_version < 2) ? 0x30 : 0x34; + + /* 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?) */ + 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; + + sb.map_entry_size = (sb.map_version < 2) ? 0x30 : 0x34; for (i = 0; i < sb.map_num; i++) { - /* basic layout: - * 0x00 - map type - * 0x04 - zero - * 0x08 - map section offset - * 0x0c - map section size - * 0x10 - map name (20 byte) */ - off_t offset = sb.map_header_offset + i * map_entry_size; - sb.map_offset = read_32bit(offset + 0x08, streamFile); - read_string(sb.map_name, sizeof(sb.map_name), offset + 0x10, streamFile); - /* latest format has another unknown long here or maybe string buffer is 4 bytes bigger */ + off_t offset = sb.map_start + i * sb.map_entry_size; - /* parse map section header */ - sb.section1_offset = read_32bit(sb.map_offset + 0x04, streamFile) + sb.map_offset; - sb.section1_num = read_32bit(sb.map_offset + 0x08, streamFile); - sb.section2_offset = read_32bit(sb.map_offset + 0x0c, streamFile) + sb.map_offset; - sb.section2_num = read_32bit(sb.map_offset + 0x10, streamFile); + /* SM AREA 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 */ + if (sb.map_version >= 3) + sb.map_unknown = read_32bit(offset + 0x30, streamFile); /* uncommon, id/config? longer name? mem garbage? */ + + /* SB HEADER */ + 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); + sb.section2_offset = read_32bit(sb.map_offset + 0x0c, streamFile) + sb.map_offset; + sb.section2_num = read_32bit(sb.map_offset + 0x10, streamFile); if (sb.map_version < 3) { - sb.section3_offset = read_32bit(sb.map_offset + 0x14, streamFile) + sb.map_offset; - sb.section3_num = read_32bit(sb.map_offset + 0x18, streamFile); - sb.extra_section_offset = read_32bit(sb.map_offset + 0x1c, streamFile) + sb.map_offset; - sb.extra_size = read_32bit(sb.map_offset + 0x20, streamFile); + sb.section3_offset = read_32bit(sb.map_offset + 0x14, streamFile) + sb.map_offset; + sb.section3_num = read_32bit(sb.map_offset + 0x18, streamFile); + sb.sectionX_offset = read_32bit(sb.map_offset + 0x1c, streamFile) + sb.map_offset; + sb.sectionX_size = read_32bit(sb.map_offset + 0x20, streamFile); } else { + sb.section4_offset = read_32bit(sb.map_offset + 0x14, streamFile); + sb.section4_num = read_32bit(sb.map_offset + 0x18, streamFile); + sb.section3_offset = read_32bit(sb.map_offset + 0x1c, streamFile) + sb.map_offset; + sb.section3_num = read_32bit(sb.map_offset + 0x20, streamFile); + sb.sectionX_offset = read_32bit(sb.map_offset + 0x24, streamFile) + sb.map_offset; + sb.sectionX_size = read_32bit(sb.map_offset + 0x28, streamFile); + /* latest map format has another section with sounds after section 2 */ - sb.section4_offset = read_32bit(sb.map_offset + 0x14, streamFile); - sb.section4_num = read_32bit(sb.map_offset + 0x18, streamFile); - sb.section3_offset = read_32bit(sb.map_offset + 0x1c, streamFile) + sb.map_offset; - sb.section3_num = read_32bit(sb.map_offset + 0x20, streamFile); - sb.extra_section_offset = read_32bit(sb.map_offset + 0x24, streamFile) + sb.map_offset; - sb.extra_size = read_32bit(sb.map_offset + 0x28, streamFile); - - /* Let's just merge it with section 2 */ - sb.section2_num += sb.section4_num; - - /* for some reason, this is relative to section 4 here */ - sb.extra_section_offset += sb.section4_offset; + sb.section2_num += sb.section4_num; /* Let's just merge it with section 2 */ + 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_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); + if (!parse_sb_header(&sb, streamTest, target_stream)) goto fail; } @@ -336,6 +359,8 @@ VGMSTREAM * init_vgmstream_ubi_sm(STREAMFILE *streamFile) { } close_streamfile(streamTest); + + /* CREATE VGMSTREAM */ return init_vgmstream_ubi_sb_main(&sb, streamFile); fail: @@ -351,9 +376,9 @@ static VGMSTREAM * init_vgmstream_ubi_sb_main(ubi_sb_header *sb, STREAMFILE *str /* 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->extra_name); + VGM_LOG("UBI SB: autodetecting external stream '%s'\n", sb->resource_name); - streamData = open_streamfile_by_filename(streamFile,sb->extra_name); + streamData = open_streamfile_by_filename(streamFile,sb->resource_name); if (!streamData) { streamData = streamFile; /* assume internal */ if (sb->stream_size > get_streamfile_size(streamData)) { @@ -365,18 +390,19 @@ static VGMSTREAM * init_vgmstream_ubi_sb_main(ubi_sb_header *sb, STREAMFILE *str } } else if (sb->is_external) { - streamData = open_streamfile_by_filename(streamFile,sb->extra_name); + streamData = open_streamfile_by_filename(streamFile,sb->resource_name); if (!streamData) { - VGM_LOG("UBI SB: external stream '%s' not found\n", sb->extra_name); + 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; - //;VGM_LOG("start offset=%lx, external=%i\n", start_offset, sb->is_external); + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(sb->channels,loop_flag); @@ -387,7 +413,6 @@ static VGMSTREAM * init_vgmstream_ubi_sb_main(ubi_sb_header *sb, STREAMFILE *str vgmstream->stream_size = sb->stream_size; vgmstream->meta_type = meta_UBI_SB; - switch(sb->codec) { case UBI_ADPCM: vgmstream->coding_type = coding_UBI_IMA; @@ -421,12 +446,8 @@ static VGMSTREAM * init_vgmstream_ubi_sb_main(ubi_sb_header *sb, STREAMFILE *str vgmstream->interleave_block_size = sb->stream_size / sb->channels; vgmstream->num_samples = dsp_bytes_to_samples(sb->stream_size, sb->channels); - { - off_t coefs_offset = sb->extra_offset; - coefs_offset += 0x10; /* entry size is 0x40 (first/last 0x10 = unknown), per channel */ - - dsp_read_coefs_be(vgmstream,streamFile,coefs_offset, 0x40); - } + /* 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: @@ -449,6 +470,7 @@ static VGMSTREAM * init_vgmstream_ubi_sb_main(ubi_sb_header *sb, STREAMFILE *str /* skip weird value (3, 4) in Brothers in Arms: D-Day (PSP) */ 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; sb->stream_size -= 0x04; } @@ -459,11 +481,11 @@ static VGMSTREAM * init_vgmstream_ubi_sb_main(ubi_sb_header *sb, STREAMFILE *str vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - if (sb->stream_samples == 0) /* sometimes not known */ - sb->stream_samples = ffmpeg_data->totalSamples; - vgmstream->num_samples = sb->stream_samples; - if (sb->stream_samples != ffmpeg_data->totalSamples) { - VGM_LOG("UBI SB: header samples differ (%i vs %i)\n", sb->stream_samples, (size_t)ffmpeg_data->totalSamples); + 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; } @@ -481,11 +503,11 @@ static VGMSTREAM * init_vgmstream_ubi_sb_main(ubi_sb_header *sb, STREAMFILE *str joint_stereo = 0; encoder_delay = 0x00; /* TODO: this is incorrect */ - bytes = ffmpeg_make_riff_atrac3(buf, 0x100, sb->stream_samples, sb->stream_size, sb->channels, sb->sample_rate, block_size, joint_stereo, encoder_delay); + bytes = ffmpeg_make_riff_atrac3(buf, 0x100, sb->num_samples, sb->stream_size, sb->channels, sb->sample_rate, block_size, joint_stereo, encoder_delay); 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->stream_samples; + vgmstream->num_samples = sb->num_samples; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; break; @@ -531,7 +553,7 @@ 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->stream_samples; + vgmstream->num_samples = sb->num_samples; vgmstream->stream_size = data_size; break; } @@ -554,7 +576,7 @@ 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->stream_samples; + vgmstream->num_samples = sb->num_samples; break; } @@ -567,8 +589,8 @@ static VGMSTREAM * init_vgmstream_ubi_sb_main(ubi_sb_header *sb, STREAMFILE *str vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - vgmstream->num_samples = sb->stream_samples; /* ffmpeg_data->totalSamples */ - VGM_ASSERT(sb->stream_samples != ffmpeg_data->totalSamples, "UBI SB: header samples differ\n"); + vgmstream->num_samples = sb->num_samples; /* ffmpeg_data->totalSamples */ + VGM_ASSERT(sb->num_samples != ffmpeg_data->totalSamples, "UBI SB: header samples differ\n"); break; } @@ -578,9 +600,9 @@ static VGMSTREAM * init_vgmstream_ubi_sb_main(ubi_sb_header *sb, STREAMFILE *str goto fail; } - strcpy(vgmstream->stream_name, sb->stream_name); + strcpy(vgmstream->stream_name, sb->readable_name); - /* open the file for reading (can be an external stream, different from the current .sb0) */ + /* open the actual for decoding (streamData can be an internal or external stream) */ if ( !vgmstream_open_stream(vgmstream, streamData, start_offset) ) goto fail; @@ -591,111 +613,163 @@ fail: if (sb->is_external && streamData) close_streamfile(streamData); close_vgmstream(vgmstream); return NULL; - } /* debug stuff, for now */ -static void parse_descriptor_subtype(ubi_sb_header * sb, uint32_t descriptor_subtype, off_t offset) { +static void parse_header_type(ubi_sb_header * sb, uint32_t header_type, off_t offset) { - /* type may be flags? */ - /* all types may contain memory garbage, making it harder to identify */ - - switch(descriptor_subtype) { - /* standard audio */ - case 0x01: sb->subtypes[0x01]++; break; - - /* audio/garbage? [Splinter Cell: Chaos Theory] */ - /* audio/garbage? [Surf's Up-map] */ - /* audio/garbage? [TMNT-map] */ - /* config? [Brothers in Arms: D-Day] */ - /* config? [Surf's Up-bank] */ - case 0x02: sb->subtypes[0x02]++; break; - - /* audio/garbage? [Splinter Cell: Chaos Theory] */ - /* audio/garbage? [TMNT-map] */ - /* config? [Prince of Persia: The Two Thrones] */ - case 0x03: sb->subtypes[0x03]++; break; - - /* audio/garbage? [Splinter Cell: Chaos Theory] */ - /* audio/garbage? [TMNT-map] */ - /* config? [Rainbow Six 3] */ - /* config? [Prince of Persia: Warrior Within] */ - /* config? [Prince of Persia: The Two Thrones] */ - /* config? [TMNT-bank] */ - /* config? [Surf's Up-map] */ - /* audio/garbage? [Myst IV demo] */ - /* config? [Brothers in Arms: D-Day] */ - /* config? [Surf's Up-bank] */ - case 0x04: sb->subtypes[0x04]++; break; - - /* audio/garbage? [Splinter Cell: Chaos Theory] */ - /* audio/garbage? [TMNT-map] */ - /* config? [Prince of Persia: Warrior Within] */ - /* config? [Prince of Persia: The Two Thrones] */ - /* config? [Prince of Persia: Revelations] */ - /* config? [TMNT-bank] */ - case 0x05: sb->subtypes[0x05]++; break; - - /* layer? [Surf's Up-map] */ - /* layer [TMNT-bank] */ - /* layer [TMNT-map] */ - case 0x06: sb->subtypes[0x06]++; break; - - /* audio/garbage? [Splinter Cell: Chaos Theory] */ - /* config? [Brothers in Arms: D-Day] */ - /* config? [Surf's Up-bank] */ - case 0x07: sb->subtypes[0x07]++; break; - - /* audio/garbage? [Splinter Cell: Chaos Theory] */ - /* audio/garbage? [TMNT-map] */ - /* config? [Prince of Persia: Warrior Within] */ - /* config? [Prince of Persia: The Two Thrones] */ - /* config? [TMNT-bank] */ - /* config? [Surf's Up-map] */ - /* config? [Brothers in Arms: D-Day] */ - /* config? [Surf's Up-bank] */ - case 0x08: sb->subtypes[0x08]++; break; - - /* related to voices? [Prince of Persia: Sands of Time] */ - /* config? [Rainbow Six 3] */ - /* config? [Myst IV demo] */ - case 0x0a: sb->subtypes[0x0a]++; break; - - /* config? [Prince of Persia: Sands of Time] */ - /* config? [Rainbow Six 3] */ - case 0x0c: sb->subtypes[0x0c]++; break; - - /* layer [Prince of Persia: Sands of Time] */ - /* layer [Rainbow Six 3] */ - case 0x0d: sb->subtypes[0x0d]++; break; - - /* config? [Rainbow Six 3] */ - /* config? [Myst IV demo] */ - case 0x0f: sb->subtypes[0x0f]++; break; + /* all types may contain memory garbage, making it harder to identify + * usually next types can contain memory from the previous type header, + * 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) */ + switch(header_type) { + case 0x01: sb->types[0x01]++; break; /* audio (all games) */ + case 0x02: sb->types[0x02]++; break; /* config? (later games) */ + case 0x03: sb->types[0x03]++; break; /* config? (later games) */ + case 0x04: sb->types[0x04]++; break; /* config? (all games) [recheck: SC:CT] */ + case 0x05: sb->types[0x05]++; break; /* config? (all games) [recheck: SC:CT] */ + case 0x06: sb->types[0x06]++; break; /* layer (later games) */ + case 0x07: sb->types[0x07]++; break; /* config? (later games) [recheck: SC:CT] */ + case 0x08: sb->types[0x08]++; break; /* config? (all games) [recheck: SC:CT] */ + //case 0x09: sb->types[0x09]++; break; /* ? */ + case 0x0a: sb->types[0x0a]++; break; /* config? (early games) */ + //case 0x0b: sb->types[0x0b]++; break; /* ? */ + case 0x0c: sb->types[0x0c]++; break; /* config? (early games) */ + case 0x0d: sb->types[0x0d]++; break; /* layer (early games) */ + case 0x0e: sb->types[0x0e]++; break; /* config? (early games) */ + case 0x0f: sb->types[0x0f]++; break; /* config? (early games) */ default: - VGM_LOG("UBI SB: unknown subtype %x at %x size %x\n", descriptor_subtype, (uint32_t)offset, sb->section2_entry_size); + VGM_LOG("UBI SB: unknown type %x at %x size %x\n", header_type, (uint32_t)offset, sb->section2_entry_size); break; //goto fail; } - //;VGM_ASSERT(descriptor_subtype != 0x01, "UBI SB: subtype %x at %x size %x\n", descriptor_subtype, (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' */ + + /* 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? (0x02000000) + * 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 */ //todo also check layer header + /* 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? (0x04) + * 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: config? + * 0x60: stream size + * 0x64: stream offset? + * + * - in header extra offset + * 0x00: sample rate + * 0x04: 16? + * 0x08: channels? + * 0x0c: codec? + * + * - layer header at stream_offset: + * 0x00: version? (0x07000000) + * 0x04: 0x03? + * 0x08: layers/channels? + * 0x0c: stream size + * 0x10: blocks count + * 0x14: block header size + * 0x18: 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) + */ - /* 0x06 layer [TMNT-bank] */ - /* - subtype header: + /* 0x06 layer [TMNT (PS2)-bank] */ + /* - type header: + * 0x08: header extra offset * 0x1c: external flag? * 0x20: layers/channels? * 0x24: layers/channels? * 0x28: config? same for all * 0x2c: stream size * 0x30: stream offset - * 0x38: always 0x037c - * (todo sample rate?) + * 0x38: name extra offset + * + * - in header extra offset + * 0x00: sample rate + * 0x04: channels? + * 0x08: codec? * * - layer header at stream_offset: * 0x00: version? (0x08000B00) - * 0x04: 0x0e? + * 0x04: config? (0x0e/0x0b/etc) * 0x08: layers/channels? * 0x0c: blocks count * 0x10: block header size @@ -713,58 +787,67 @@ static void parse_descriptor_subtype(ubi_sb_header * sb, uint32_t descriptor_sub * 0x00: layer data size (varies between blocks, and one layer may have more than other) */ - /* 0x06 layer [TMNT-map] */ - /* - subtype header: - * 0x20: layers/channels? - * 0x24: layers/channels * 2? - * 0x28: config? same for all + /* 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 */ + /* - type header: + * 0x0c: header extra offset + * 0x20: layers * 0x2c: stream size * 0x30: stream offset - * 0x38: 0x01D0/0118/etc - * 0x40: external flag? - * 0x48: codec? - * 0x54: codec? - * (todo sample rate?) + * 0x38: name extra offset + * + * - in header extra offset + * 0x00: sample rate + * 0x04: 16? + * 0x08: channels? + * 0x0c: codec (03=Ubi, 01=PCM16LE in SW:LA) * * - layer header at stream_offset: * - blocked data: - * same as TMNT-bank, but codec header size is 0 + * same as TMNT PSP-map */ - //todo Surf's Up-map - - /* 0x0d layer [Prince of Persia: Sands of Time] */ - /* 0x0d layer [Rainbow Six 3] */ //todo also check layer header - /* - subtype 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 + /* TMNT (2007)(PSP)-map 0x00190001 */ + /* - type header: + * 0x0c: header extra offset + * 0x20: layers + * 0x24: total channels? + * 0x28: config? + * 0x2c: stream size + * 0x30: stream offset + * 0x38: name extra offset + * + * - in header extra offset + * 0x00: sample rate + * 0x04: channels? + * 0x08: codec? * * - layer header at stream_offset: - * 0x00: version? (0x04000000) - * 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) - * * - 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) + * same as TMNT PS2-bank, but codec header size is 0 + */ + + //todo Surf's Up (PC?)-map + + /* 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 + * 0x00: sample rate + * 0x04: channels? + * 0x08: codec? + * + * - layer header at stream_offset: + * - blocked data: + * same as TMNT PS2-bank, but codec header size is 0 */ } @@ -787,24 +870,23 @@ static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile, int targe /* 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 descriptor_subtype; + uint32_t header_type; - descriptor_subtype = read_32bit(offset + 0x04, streamFile); - parse_descriptor_subtype(sb, descriptor_subtype, offset); + header_type = read_32bit(offset + 0x04, streamFile); + parse_header_type(sb, header_type, offset); /* ignore non-audio entries */ - if (descriptor_subtype != 0x01) + if (header_type != 0x01) continue; - //;VGM_LOG("UBI SB: type at %lx\n", offset); /* 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->stream_type_offset, streamFile); + int type = read_32bit(offset + sb->cfg_stream_type, streamFile); - if (sb->external_flag_offset) { - current_is_external = read_32bit(offset + sb->external_flag_offset, streamFile); - } else if (sb->has_extra_name_flag && read_32bit(offset + sb->extra_name_offset, streamFile) != 0xFFFFFFFF) { + 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 */ } @@ -845,83 +927,82 @@ static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile, int targe bank_streams++; if (sb->total_streams != target_stream) continue; - //;VGM_LOG("target at offset=%lx (size=%x)\n", offset, sb->section2_entry_size); /* parse audio entry based on config */ - sb->header_offset = offset; - sb->header_idx = i; + sb->header_index = i; + sb->header_offset = offset; - sb->header_id = read_32bit(offset + 0x00, streamFile); /* 16b+16b group+sound id */ - sb->header_type = read_32bit(offset + 0x04, streamFile); - sb->stream_size = read_32bit(offset + sb->stream_size_offset, streamFile); - sb->extra_offset = read_32bit(offset + sb->extra_pointer_offset, streamFile) + sb->extra_section_offset; /* within the extra section */ - sb->stream_offset = read_32bit(offset + sb->stream_pointer_offset, streamFile); /* within the data section */ - sb->channels = (sb->has_short_channels) ? - (uint16_t)read_16bit(offset + sb->channels_offset, streamFile) : - (uint32_t)read_32bit(offset + sb->channels_offset, streamFile); - sb->sample_rate = read_32bit(offset + sb->sample_rate_offset, streamFile); - sb->stream_type = read_32bit(offset + sb->stream_type_offset, streamFile); + sb->header_id = read_32bit(offset + 0x00, streamFile); + sb->header_type = read_32bit(offset + 0x04, streamFile); + sb->stream_size = read_32bit(offset + sb->cfg_stream_size, streamFile); + sb->extra_offset = read_32bit(offset + sb->cfg_extra_offset, streamFile) + sb->sectionX_offset; + sb->stream_offset = read_32bit(offset + sb->cfg_stream_offset, streamFile); + sb->channels = (sb->has_short_channels) ? + (uint16_t)read_16bit(offset + sb->cfg_channels, streamFile) : + (uint32_t)read_32bit(offset + sb->cfg_channels, streamFile); + 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->samples_flag_offset && - read_32bit(offset + sb->samples_flag_offset, streamFile) != 0) { - sb->stream_samples = read_32bit(offset + sb->num_samples_offset2, streamFile); - } else if (sb->num_samples_offset) { - sb->stream_samples = read_32bit(offset + sb->num_samples_offset, 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) { + sb->num_samples = read_32bit(offset + sb->cfg_num_samples, streamFile); } - if (sb-> has_rotating_ids) { - sb->stream_id = current_id; - } else if (sb->stream_id_offset) { - sb->stream_id = read_32bit(offset + sb->stream_id_offset, 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); } - /* external stream name can be found in the header (first versions) or the extra table (later versions) */ - if (sb->stream_name_offset) { - read_string(sb->extra_name, sb->stream_name_size, offset + sb->stream_name_offset, streamFile); + /* external stream name can be found in the header (first versions) or the sectionX table (later versions) */ + if (sb->cfg_stream_name) { + read_string(sb->resource_name, sb->resource_name_size, offset + sb->cfg_stream_name, streamFile); } else { - sb->stream_name_offset = read_32bit(offset + sb->extra_name_offset, streamFile); - read_string(sb->extra_name, sb->stream_name_size, sb->extra_section_offset + sb->stream_name_offset, streamFile); + 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); } - /* not always set and must be derived */ - if (sb->external_flag_offset) { - sb->is_external = read_32bit(offset + sb->external_flag_offset, streamFile); - } else if (sb->has_extra_name_flag && read_32bit(offset + sb->extra_name_offset, streamFile) != 0xFFFFFFFF) { + /* 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; + sb->autodetect_external = 1; /* let the parser guess later */ - if (sb->extra_name[0] == '\0') + if (sb->resource_name[0] == '\0') sb->autodetect_external = 0; /* no name */ - if (sb->extra_size > 0 && sb->stream_name_offset > sb->extra_size) + 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 */ if (sb->is_map) { - if (sb->extra_name[0]) { - snprintf(sb->stream_name, sizeof(sb->stream_name), "%s/%d/%s", sb->map_name, bank_streams, sb->extra_name); + if (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->stream_name, sizeof(sb->stream_name), "%s/%d", sb->map_name, bank_streams); + snprintf(sb->readable_name, sizeof(sb->readable_name), "%s/%d/%08x", sb->map_name, bank_streams, sb->header_id); } } else { - strncpy(sb->stream_name, sb->extra_name, sizeof(sb->stream_name)); + if (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); + } } + } -#if 0 - { - int i; - VGM_LOG("UBI subtypes: "); - for (i = 0; i < 16; i++) { - VGM_ASSERT(sb->subtypes[i], "%02x=%i ",i,sb->subtypes[i]); - } - VGM_LOG("\n"); - } -#endif + //;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_stream <= prev_streams || target_stream > sb->total_streams) @@ -938,11 +1019,13 @@ static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile, int targe } } - if (!(sb->stream_id_offset || sb->has_rotating_ids || sb->is_map) && sb->section3_num > 1) { + if (!(sb->cfg_stream_id || sb->has_rotating_ids || 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); + /* happens in some versions */ if (sb->stream_type > 0xFF) { @@ -955,6 +1038,7 @@ static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile, int targe case 0x00: /* platform default (rarely external) */ switch (sb->platform) { case UBI_PC: + case UBI_3DS: sb->codec = RAW_PCM; break; @@ -991,7 +1075,6 @@ static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile, int targe default: VGM_LOG("UBI SB: unknown internal format\n"); goto fail; - } break; @@ -1026,7 +1109,7 @@ static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile, int targe } break; - case 0x06: /* PS ADPCM (later PS3 games?) */ + case 0x06: /* PS ADPCM (later PSP and PS3(?) games) */ sb->codec = RAW_PSX; break; @@ -1045,14 +1128,14 @@ static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile, int targe 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->xma_pointer_offset, streamFile) + sb->extra_section_offset; + 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->stream_id_offset && sb->stream_id > 0, "UBI SB: unexpected external stream with stream id\n"); + //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) { @@ -1066,11 +1149,11 @@ static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile, int targe uint32_t table2_num = read_32bit(offset + 0x10, streamFile); for (j = 0; j < table_num; j++) { - int idx = read_32bit(table_offset + 0x08 * j + 0x00, streamFile) & 0x0000FFFF; + int index = read_32bit(table_offset + 0x08 * j + 0x00, streamFile) & 0x0000FFFF; - if (idx == sb->header_idx) { - if (!(sb->stream_id_offset || sb->has_rotating_ids) && table2_num > 1) { - VGM_LOG("UBI SB: unexpected number of internal stream groups %i\n", table2_num); + 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); goto fail; } @@ -1101,7 +1184,7 @@ static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile, int targe } else { sb->stream_offset += sb->sounds_offset; - if ((sb->stream_id_offset || sb->has_rotating_ids) && sb->section3_num > 1) { + 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 */ for (i = 0; i < sb->section3_num; i++) { @@ -1122,11 +1205,10 @@ fail: } -static int config_sb_header_version(ubi_sb_header * sb, STREAMFILE *streamFile) { +static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) { int is_biadd_psp = 0; - - /* The format varies with almost every game + platform (some kind of class serialization?), + /* 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 @@ -1141,15 +1223,20 @@ static int config_sb_header_version(ubi_sb_header * sb, STREAMFILE *streamFile) * Later version don't have filename per header but in the extra section. */ + //todo set samples flag where "varies" /* common */ sb->section3_entry_size = 0x08; - sb->stream_name_size = 0x24; /* maybe 0x28 or 0x20 for some but ok enough (null terminated) */ - + 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->stream_size_offset = 0x08; - sb->extra_pointer_offset = 0x0c; - sb->stream_pointer_offset = 0x10; + sb->cfg_stream_size = 0x08; + sb->cfg_extra_offset = 0x0c; + sb->cfg_stream_offset = 0x10; + + + /* Batman: Vengeance (2001)(PS2)-map 0x00000003 */ + /* Batman: Vengeance (2001)(GC)-map 0x00000003 */ + /* Disney's Tarzan: Untamed (2001)(GC)-map 0x00000003 */ #if 0 /* Donald Duck: Goin' Quackers (2002)(GC)-map */ @@ -1163,28 +1250,29 @@ static int config_sb_header_version(ubi_sb_header * sb, STREAMFILE *streamFile) sb->map_version = 1; - sb->stream_size_offset = 0x0c; - sb->extra_pointer_offset = 0x10; - sb->stream_pointer_offset = 0x14; + sb->cfg_stream_size = 0x0c; + sb->cfg_extra_offset = 0x10; + sb->cfg_stream_offset = 0x14; - sb->external_flag_offset = 0x30; - sb->stream_id_offset = 0x34; - sb->num_samples_offset = 0x48; - sb->sample_rate_offset = 0x50; - sb->channels_offset = 0x56; - sb->stream_type_offset = 0x58; - sb->stream_name_offset = 0x5c; + sb->cfg_external_flag = 0x30; + sb->cfg_stream_id = 0x34; + sb->cfg_num_samples = 0x48; + sb->cfg_sample_rate = 0x50; + sb->cfg_channels = 0x56; + sb->cfg_stream_type = 0x58; + sb->cfg_stream_name = 0x5c; sb->has_short_channels = 1; return 1; } - +#endif +#if 0 /* 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 another version of Ubi IMA + * 0x02: unsupported codec, appears to be Ubi IMA in a blocked layout * 0x04: Ubi IMA v3 (not Vorbis) */ sb->section1_entry_size = 0x58; @@ -1192,17 +1280,17 @@ static int config_sb_header_version(ubi_sb_header * sb, STREAMFILE *streamFile) sb->map_version = 1; - sb->stream_size_offset = 0x0c; - sb->extra_pointer_offset = 0x10; - sb->stream_pointer_offset = 0x14; + sb->cfg_stream_size = 0x0c; + sb->cfg_extra_offset = 0x10; + sb->cfg_stream_offset = 0x14; - sb->external_flag_offset = 0x28; - sb->stream_id_offset = 0x2c; - sb->num_samples_offset = 0x30; - sb->sample_rate_offset = 0x44; - sb->channels_offset = 0x4a; - sb->stream_type_offset = 0x4c; - sb->stream_name_offset = 0x50; + sb->cfg_external_flag = 0x28; + sb->cfg_stream_id = 0x2c; + sb->cfg_num_samples = 0x30; + 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; @@ -1216,15 +1304,16 @@ static int config_sb_header_version(ubi_sb_header * sb, STREAMFILE *streamFile) sb->section1_entry_size = 0x64; sb->section2_entry_size = 0x80; - sb->external_flag_offset = 0x24; /* maybe 0x28 */ - sb->num_samples_offset = 0x30; - sb->sample_rate_offset = 0x44; - sb->channels_offset = 0x4a; - sb->stream_type_offset = 0x4c; - sb->stream_name_offset = 0x50; + sb->cfg_external_flag = 0x24; /* maybe 0x28 */ + sb->cfg_num_samples = 0x30; + 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 (main game) return 1; } @@ -1234,13 +1323,14 @@ static int config_sb_header_version(ubi_sb_header * sb, STREAMFILE *streamFile) sb->section1_entry_size = 0x48; sb->section2_entry_size = 0x6c; - sb->external_flag_offset = 0; /* no apparent flag */ - sb->channels_offset = 0x20; - sb->sample_rate_offset = 0x24; - sb->num_samples_offset = 0x30; - sb->stream_name_offset = 0x40; - sb->stream_type_offset = 0x68; + sb->cfg_external_flag = 0; /* no apparent flag */ + sb->cfg_channels = 0x20; + sb->cfg_sample_rate = 0x24; + sb->cfg_num_samples = 0x30; + sb->cfg_stream_name = 0x40; + sb->cfg_stream_type = 0x68; + //has layer 0d (main game) return 1; } @@ -1250,15 +1340,39 @@ static int config_sb_header_version(ubi_sb_header * sb, STREAMFILE *streamFile) sb->section1_entry_size = 0x64; sb->section2_entry_size = 0x78; - sb->external_flag_offset = 0x24; /* maybe 0x28 */ - sb->num_samples_offset = 0x30; - sb->sample_rate_offset = 0x44; - sb->channels_offset = 0x4a; - sb->stream_type_offset = 0x4c; /* may contain garbage */ - sb->stream_name_offset = 0x50; + sb->cfg_external_flag = 0x24; /* maybe 0x28 */ + sb->cfg_num_samples = 0x30; + sb->cfg_sample_rate = 0x44; + sb->cfg_channels = 0x4a; + sb->cfg_stream_type = 0x4c; /* may contain garbage */ + sb->cfg_stream_name = 0x50; sb->has_short_channels = 1; sb->has_internal_names = 1; + //has layer 0d (main game) + 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 */ + 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; + + sb->map_version = 2; + + sb->cfg_external_flag = 0x20; /* maybe 0x24 */ + sb->cfg_num_samples = 0x2c; + sb->cfg_sample_rate = 0x40; + sb->cfg_channels = 0x46; + sb->cfg_stream_type = 0x48; + sb->cfg_stream_name = 0x4c; + + sb->has_short_channels = 1; + //has layer 0d (POP:SOT main game, Batman) return 1; } @@ -1267,44 +1381,28 @@ static int config_sb_header_version(ubi_sb_header * sb, STREAMFILE *streamFile) sb->section1_entry_size = 0x48; sb->section2_entry_size = 0x6c; - sb->external_flag_offset = 0; /* no apparent flag */ - sb->channels_offset = 0x20; - sb->sample_rate_offset = 0x24; - sb->num_samples_offset = 0x30; - sb->stream_name_offset = 0x40; - sb->stream_type_offset = 0x68; + sb->cfg_external_flag = 0; /* no apparent flag */ + sb->cfg_channels = 0x20; + sb->cfg_sample_rate = 0x24; + sb->cfg_num_samples = 0x30; + sb->cfg_stream_name = 0x40; + sb->cfg_stream_type = 0x68; + //has layer 0d return 1; } - /* Prince of Persia: Sands of Time (2003)(GC)-bank */ - 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; - - sb->external_flag_offset = 0x20; /* maybe 0x24 */ - sb->num_samples_offset = 0x2c; - sb->sample_rate_offset = 0x40; - sb->channels_offset = 0x46; - sb->stream_type_offset = 0x48; - sb->stream_name_offset = 0x4c; - - sb->has_short_channels = 1; - return 1; - } - - /* Myst IV Demo (2004)(PC) (final game is different)-bank */ - if (sb->version == 0x00100000 && sb->platform == UBI_PC) { + /* 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->external_flag_offset = 0x24; - sb->num_samples_offset = 0x34; - sb->sample_rate_offset = 0x44; - sb->channels_offset = 0x4c; - sb->stream_type_offset = 0x50; - sb->stream_name_offset = 0x54; + sb->cfg_external_flag = 0x24; + sb->cfg_num_samples = 0x34; + sb->cfg_sample_rate = 0x44; + sb->cfg_channels = 0x4c; + sb->cfg_stream_type = 0x50; + sb->cfg_stream_name = 0x54; sb->has_internal_names = 1; return 1; @@ -1315,14 +1413,15 @@ static int config_sb_header_version(ubi_sb_header * sb, STREAMFILE *streamFile) sb->section1_entry_size = 0x6c; sb->section2_entry_size = 0x84; - sb->external_flag_offset = 0x24; - sb->num_samples_offset = 0x30; - sb->sample_rate_offset = 0x44; - sb->channels_offset = 0x4c; - sb->stream_type_offset = 0x50; - sb->stream_name_offset = 0x54; + sb->cfg_external_flag = 0x24; + sb->cfg_num_samples = 0x30; + sb->cfg_sample_rate = 0x44; + sb->cfg_channels = 0x4c; + sb->cfg_stream_type = 0x50; + sb->cfg_stream_name = 0x54; sb->has_internal_names = 1; + //no layers return 1; } @@ -1331,13 +1430,14 @@ static int config_sb_header_version(ubi_sb_header * sb, STREAMFILE *streamFile) sb->section1_entry_size = 0x48; sb->section2_entry_size = 0x6c; - sb->external_flag_offset = 0; /* no apparent flag */ - sb->channels_offset = 0x20; - sb->sample_rate_offset = 0x24; - sb->num_samples_offset = 0x30; - sb->stream_name_offset = 0x40; - sb->stream_type_offset = 0x68; + sb->cfg_external_flag = 0; /* no apparent flag */ + sb->cfg_channels = 0x20; + sb->cfg_sample_rate = 0x24; + sb->cfg_num_samples = 0x30; + sb->cfg_stream_name = 0x40; + sb->cfg_stream_type = 0x68; + //no layers return 1; } @@ -1346,14 +1446,15 @@ static int config_sb_header_version(ubi_sb_header * sb, STREAMFILE *streamFile) sb->section1_entry_size = 0x6c; sb->section2_entry_size = 0x90; - sb->external_flag_offset = 0x24; - sb->num_samples_offset = 0x44; - sb->sample_rate_offset = 0x58; - sb->channels_offset = 0x60; - sb->stream_type_offset = 0x64; /* may contain garbage */ - sb->stream_name_offset = 0x68; + sb->cfg_external_flag = 0x24; + sb->cfg_num_samples = 0x44; + sb->cfg_sample_rate = 0x58; + sb->cfg_channels = 0x60; + sb->cfg_stream_type = 0x64; /* may contain garbage */ + sb->cfg_stream_name = 0x68; sb->has_internal_names = 1; + //no layers return 1; } @@ -1362,17 +1463,18 @@ static int config_sb_header_version(ubi_sb_header * sb, STREAMFILE *streamFile) sb->section1_entry_size = 0x6c; sb->section2_entry_size = 0x78; - sb->external_flag_offset = 0x20; - sb->num_samples_offset = 0x2c; - sb->sample_rate_offset = 0x40; - sb->channels_offset = 0x48; - sb->stream_type_offset = 0x4c; - sb->stream_name_offset = 0x50; + sb->cfg_external_flag = 0x20; + sb->cfg_num_samples = 0x2c; + sb->cfg_sample_rate = 0x40; + sb->cfg_channels = 0x48; + sb->cfg_stream_type = 0x4c; + sb->cfg_stream_name = 0x50; + //no layers return 1; } - /* two games with same id; use project file as identifier */ + /* two configs with same id; use project file as identifier */ if (sb->version == 0x0012000C && sb->platform == UBI_PSP) { STREAMFILE * streamTest = open_streamfile_by_filename(streamFile, "BIAAUDIO.SP4"); if (streamTest) { @@ -1383,20 +1485,25 @@ static int config_sb_header_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 */ if (sb->version == 0x0012000C && sb->platform == UBI_PSP && !is_biadd_psp) { sb->section1_entry_size = 0x68; sb->section2_entry_size = 0x84; sb->map_version = 2; - sb->external_flag_offset = 0x24; - sb->num_samples_offset = 0x30; - sb->sample_rate_offset = 0x44; - sb->channels_offset = 0x4c; - sb->stream_type_offset = 0x50; - sb->stream_name_offset = 0x54; + sb->cfg_external_flag = 0x24; + sb->cfg_num_samples = 0x30; + sb->cfg_sample_rate = 0x44; + sb->cfg_channels = 0x4c; + sb->cfg_stream_type = 0x50; + sb->cfg_stream_name = 0x54; 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; } @@ -1405,34 +1512,63 @@ static int config_sb_header_version(ubi_sb_header * sb, STREAMFILE *streamFile) sb->section1_entry_size = 0x80; sb->section2_entry_size = 0x94; - sb->stream_id_offset = 0x2c; - sb->external_flag_offset = 0x24; - sb->samples_flag_offset = 0x28; - sb->num_samples_offset = 0x30; - sb->num_samples_offset2 = 0x38; - sb->sample_rate_offset = 0x44; - sb->channels_offset = 0x4c; - sb->stream_type_offset = 0x50; - sb->stream_name_offset = 0x54; + sb->cfg_external_flag = 0x24; + sb->cfg_samples_flag = 0x28; + sb->cfg_stream_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; + sb->cfg_stream_name = 0x54; sb->has_internal_names = 1; return 1; } +#if 0 + /* Splinter Cell 3D (2011)(3DS)-map */ + if (sb->version == 0x00130001 && sb->platform == UBI_3DS) { + sb->section1_entry_size = 0x48; + sb->section2_entry_size = 0x4c; + + sb->map_version = 2; + + sb->cfg_external_flag = 0; + //sb->cfg_stream_id = 0; + sb->cfg_num_samples = 0x1c; + 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? + //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) { sb->section1_entry_size = 0x68; sb->section2_entry_size = 0x78; - sb->external_flag_offset = 0x2c; - sb->stream_id_offset = 0x34; - sb->num_samples_offset = 0x40; - sb->sample_rate_offset = 0x54; - sb->channels_offset = 0x5c; - sb->stream_type_offset = 0x60; - sb->extra_name_offset = 0x64; + sb->cfg_external_flag = 0x2c; + sb->cfg_stream_id = 0x34; + sb->cfg_num_samples = 0x40; + 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; } @@ -1441,14 +1577,15 @@ static int config_sb_header_version(ubi_sb_header * sb, STREAMFILE *streamFile) sb->section1_entry_size = 0x48; sb->section2_entry_size = 0x5c; - sb->external_flag_offset = 0; - sb->channels_offset = 0x2c; - sb->sample_rate_offset = 0x30; - sb->num_samples_offset = 0x3c; - sb->extra_name_offset = 0x4c; - sb->stream_type_offset = 0x50; + sb->cfg_external_flag = 0; + sb->cfg_channels = 0x2c; + sb->cfg_sample_rate = 0x30; + sb->cfg_num_samples = 0x3c; + sb->cfg_extra_name = 0x4c; + sb->cfg_stream_type = 0x50; sb->has_extra_name_flag = 1; + //no layers return 1; } @@ -1457,16 +1594,17 @@ static int config_sb_header_version(ubi_sb_header * sb, STREAMFILE *streamFile) sb->section1_entry_size = 0x48; sb->section2_entry_size = 0x58; - sb->external_flag_offset = 0; - sb->num_samples_offset = 0x28; - sb->stream_id_offset = 0; - sb->sample_rate_offset = 0x3c; - sb->channels_offset = 0x44; - sb->stream_type_offset = 0x48; - sb->extra_name_offset = 0x4c; + sb->cfg_external_flag = 0; + sb->cfg_stream_id = 0; + sb->cfg_num_samples = 0x28; + 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; + //no layers return 1; } @@ -1475,13 +1613,14 @@ static int config_sb_header_version(ubi_sb_header * sb, STREAMFILE *streamFile) sb->section1_entry_size = 0x68; sb->section2_entry_size = 0x6c; - sb->external_flag_offset = 0x28; /* maybe 0x2c */ - sb->num_samples_offset = 0x3c; - sb->sample_rate_offset = 0x50; - sb->channels_offset = 0x58; - sb->stream_type_offset = 0x5c; - sb->extra_name_offset = 0x60; + sb->cfg_external_flag = 0x28; /* maybe 0x2c */ + sb->cfg_num_samples = 0x3c; + sb->cfg_sample_rate = 0x50; + sb->cfg_channels = 0x58; + sb->cfg_stream_type = 0x5c; + sb->cfg_extra_name = 0x60; + //no layers return 1; } @@ -1492,12 +1631,12 @@ static int config_sb_header_version(ubi_sb_header * sb, STREAMFILE *streamFile) sb->map_version = 2; - sb->external_flag_offset = 0x24; - sb->num_samples_offset = 0x30; - sb->sample_rate_offset = 0x44; - sb->channels_offset = 0x4c; - sb->stream_type_offset = 0x50; - sb->extra_name_offset = 0x54; + sb->cfg_external_flag = 0x24; + sb->cfg_num_samples = 0x30; + sb->cfg_sample_rate = 0x44; + sb->cfg_channels = 0x4c; + sb->cfg_stream_type = 0x50; + sb->cfg_extra_name = 0x54; return 1; } @@ -1509,13 +1648,13 @@ static int config_sb_header_version(ubi_sb_header * sb, STREAMFILE *streamFile) sb->map_version = 2; - sb->external_flag_offset = 0; - sb->num_samples_offset = 0x18; - sb->stream_id_offset = 0; - sb->sample_rate_offset = 0x30; - sb->channels_offset = 0x38; - sb->stream_type_offset = 0x3c; - sb->extra_name_offset = 0x40; + sb->cfg_external_flag = 0; + sb->cfg_stream_id = 0; + sb->cfg_num_samples = 0x18; + 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; @@ -1528,13 +1667,13 @@ static int config_sb_header_version(ubi_sb_header * sb, STREAMFILE *streamFile) sb->section1_entry_size = 0x48; sb->section2_entry_size = 0x6c; - sb->external_flag_offset = 0; - sb->num_samples_offset = 0x28; - sb->stream_id_offset = 0; - sb->sample_rate_offset = 0x3c; - sb->channels_offset = 0x44; - sb->stream_type_offset = 0x48; - sb->extra_name_offset = 0x58; + sb->cfg_external_flag = 0; + sb->cfg_stream_id = 0; + sb->cfg_num_samples = 0x28; + sb->cfg_sample_rate = 0x3c; + sb->cfg_channels = 0x44; + sb->cfg_stream_type = 0x48; + sb->cfg_extra_name = 0x58; return 1; } @@ -1545,12 +1684,12 @@ static int config_sb_header_version(ubi_sb_header * sb, STREAMFILE *streamFile) sb->section1_entry_size = 0x68; sb->section2_entry_size = 0x6c; - sb->external_flag_offset = 0x28; /* maybe 0x2c */ - sb->num_samples_offset = 0x3c; - sb->sample_rate_offset = 0x50; - sb->channels_offset = 0x58; - sb->stream_type_offset = 0x5c; - sb->extra_name_offset = 0x60; + sb->cfg_external_flag = 0x28; /* maybe 0x2c */ + sb->cfg_num_samples = 0x3c; + sb->cfg_sample_rate = 0x50; + sb->cfg_channels = 0x58; + sb->cfg_stream_type = 0x5c; + sb->cfg_extra_name = 0x60; return 1; } @@ -1562,35 +1701,35 @@ static int config_sb_header_version(ubi_sb_header * sb, STREAMFILE *streamFile) sb->map_version = 3; - sb->external_flag_offset = 0x2c; - sb->stream_id_offset = 0x34; - sb->channels_offset = 0x5c; - sb->sample_rate_offset = 0x54; - sb->num_samples_offset = 0x40; - sb->num_samples_offset2 = 0x48; - sb->stream_type_offset = 0x60; - sb->extra_name_offset = 0x64; + sb->cfg_external_flag = 0x2c; + sb->cfg_stream_id = 0x34; + sb->cfg_channels = 0x5c; + sb->cfg_sample_rate = 0x54; + sb->cfg_num_samples = 0x40; + sb->cfg_num_samples2 = 0x48; + sb->cfg_stream_type = 0x60; + sb->cfg_extra_name = 0x64; 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; + sb->section1_entry_size = 0x68; + sb->section2_entry_size = 0x78; sb->map_version = 3; - sb->external_flag_offset = 0x2c; - sb->stream_id_offset = 0x30; - sb->samples_flag_offset = 0x34; - sb->channels_offset = 0x5c; - sb->sample_rate_offset = 0x54; - sb->num_samples_offset = 0x40; - sb->num_samples_offset2 = 0x48; - sb->stream_type_offset = 0x60; - sb->extra_name_offset = 0x64; - sb->xma_pointer_offset = 0x70; + sb->cfg_external_flag = 0x2c; + sb->cfg_stream_id = 0x30; + sb->cfg_samples_flag = 0x34; + sb->cfg_channels = 0x5c; + sb->cfg_sample_rate = 0x54; + sb->cfg_num_samples = 0x40; + sb->cfg_num_samples2 = 0x48; + sb->cfg_stream_type = 0x60; + sb->cfg_extra_name = 0x64; + sb->cfg_xma_offset = 0x70; return 1; } @@ -1602,13 +1741,13 @@ static int config_sb_header_version(ubi_sb_header * sb, STREAMFILE *streamFile) sb->map_version = 3; - sb->external_flag_offset = 0; - sb->num_samples_offset = 0x28; - sb->stream_id_offset = 0; - sb->sample_rate_offset = 0x3c; - sb->channels_offset = 0x44; - sb->stream_type_offset = 0x48; - sb->extra_name_offset = 0x4c; + sb->cfg_external_flag = 0; + sb->cfg_num_samples = 0x28; + sb->cfg_stream_id = 0; + 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; @@ -1622,61 +1761,75 @@ static int config_sb_header_version(ubi_sb_header * sb, STREAMFILE *streamFile) sb->map_version = 3; - sb->external_flag_offset = 0x28; - sb->stream_id_offset = 0x2c; - sb->num_samples_offset = 0x3c; - sb->sample_rate_offset = 0x50; - sb->channels_offset = 0x58; - sb->stream_type_offset = 0x5c; - sb->extra_name_offset = 0x60; + sb->cfg_external_flag = 0x28; + sb->cfg_stream_id = 0x2c; + sb->cfg_num_samples = 0x3c; + sb->cfg_sample_rate = 0x50; + sb->cfg_channels = 0x58; + sb->cfg_stream_type = 0x5c; + sb->cfg_extra_name = 0x60; return 1; } + /* Open Season (2005)(PS2)-map - 0x00180003 */ + /* Open Season (2005)(PSP)-map - 0x00180003 */ + /* 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 == 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_channels = 0x28; + sb->cfg_sample_rate = 0x2c; + sb->cfg_num_samples = 0x34; + sb->cfg_num_samples = 0x3c; + sb->cfg_extra_name = 0x44; + sb->cfg_stream_type = 0x48; + + sb->has_extra_name_flag = 1; + //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->external_flag_offset = 0; - sb->channels_offset = 0x28; - sb->sample_rate_offset = 0x2c; - //sb->num_samples_offset = 0x34 or 0x3c /* varies */ - sb->extra_name_offset = 0x44; - sb->stream_type_offset = 0x48; + 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 +#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->external_flag_offset = 0; - sb->channels_offset = 0x28; - sb->sample_rate_offset = 0x2c; - //sb->num_samples_offset = 0x34 or 0x3c /* varies */ - sb->extra_name_offset = 0x44; - sb->stream_type_offset = 0x48; + 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; + sb->has_rotating_ids = 1; //??? return 1; } - - /* todo Rainbow Six Vegas changes: - * some streams use type 0x06 instead of 0x01, known values: - * 0x0c: header offset in extra table? - * 0x2c: stream size - * 0x30: stream offset - * most other fields are fixed, comparing different files - * header is in the extra table, after the stream name (repeated?) - * (0x04: sample rate, 0x0c: channels, etc) - * stream data may be newer Ubi ADPCM (see DecUbiSnd) - */ #endif /* Prince of Persia: Rival Swords (2007)(Wii)-bank */ @@ -1684,12 +1837,12 @@ static int config_sb_header_version(ubi_sb_header * sb, STREAMFILE *streamFile) sb->section1_entry_size = 0x68; sb->section2_entry_size = 0x70; - sb->external_flag_offset = 0x28; /* maybe 0x2c */ - sb->channels_offset = 0x3c; - sb->sample_rate_offset = 0x40; - sb->num_samples_offset = 0x48; - sb->extra_name_offset = 0x58; - sb->stream_type_offset = 0x5c; + sb->cfg_external_flag = 0x28; /* maybe 0x2c */ + sb->cfg_channels = 0x3c; + sb->cfg_sample_rate = 0x40; + sb->cfg_num_samples = 0x48; + sb->cfg_extra_name = 0x58; + sb->cfg_stream_type = 0x5c; return 1; } @@ -1699,13 +1852,13 @@ static int config_sb_header_version(ubi_sb_header * sb, STREAMFILE *streamFile) sb->section1_entry_size = 0x68; sb->section2_entry_size = 0x74; - sb->external_flag_offset = 0x28; - sb->stream_id_offset = 0x2c; - sb->channels_offset = 0x3c; - sb->sample_rate_offset = 0x40; - sb->num_samples_offset = 0x48; - sb->stream_type_offset = 0x5c; - sb->extra_name_offset = 0x58; + sb->cfg_external_flag = 0x28; + sb->cfg_stream_id = 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; } @@ -1715,14 +1868,15 @@ static int config_sb_header_version(ubi_sb_header * sb, STREAMFILE *streamFile) sb->section1_entry_size = 0x48; sb->section2_entry_size = 0x5c; - sb->external_flag_offset = 0; - sb->channels_offset = 0x28; - sb->sample_rate_offset = 0x2c; - //sb->num_samples_offset = 0x34 or 0x3c /* varies */ - sb->extra_name_offset = 0x44; - sb->stream_type_offset = 0x48; + 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; + //has layer 06 return 1; } @@ -1731,12 +1885,12 @@ static int config_sb_header_version(ubi_sb_header * sb, STREAMFILE *streamFile) sb->section1_entry_size = 0x68; sb->section2_entry_size = 0x6c; - sb->external_flag_offset = 0x28; /* maybe 0x2c */ - sb->channels_offset = 0x3c; - sb->sample_rate_offset = 0x40; - sb->num_samples_offset = 0x48; - sb->stream_type_offset = 0x5c; - sb->extra_name_offset = 0x58; + 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; } @@ -1746,34 +1900,35 @@ static int config_sb_header_version(ubi_sb_header * sb, STREAMFILE *streamFile) sb->section1_entry_size = 0x68; sb->section2_entry_size = 0x70; - sb->external_flag_offset = 0x28; - sb->samples_flag_offset = 0x30; - sb->channels_offset = 0x3c; - sb->sample_rate_offset = 0x40; - sb->num_samples_offset = 0x48; - sb->num_samples_offset2 = 0x50; - sb->stream_type_offset = 0x5c; - sb->extra_name_offset = 0x58; - sb->xma_pointer_offset = 0x6c; + sb->cfg_external_flag = 0x28; + sb->cfg_samples_flag = 0x30; + sb->cfg_channels = 0x3c; + sb->cfg_sample_rate = 0x40; + sb->cfg_num_samples = 0x48; + sb->cfg_num_samples2 = 0x50; + sb->cfg_stream_type = 0x5c; + sb->cfg_extra_name = 0x58; + sb->cfg_xma_offset = 0x6c; return 1; } /* TMNT (2007)(PSP)-map */ if (sb->version == 0x00190001 && sb->platform == UBI_PSP) { - sb->section1_entry_size = 0x48; - sb->section2_entry_size = 0x58; + sb->section1_entry_size = 0x48; + sb->section2_entry_size = 0x58; sb->map_version = 3; - sb->external_flag_offset = 0; - sb->channels_offset = 0x28; - sb->sample_rate_offset = 0x2c; - sb->num_samples_offset = 0x34; - sb->stream_type_offset = 0x48; - sb->extra_name_offset = 0x44; + sb->cfg_external_flag = 0; + sb->cfg_channels = 0x28; + sb->cfg_sample_rate = 0x2c; + sb->cfg_num_samples = 0x34; + sb->cfg_stream_type = 0x48; + sb->cfg_extra_name = 0x44; sb->has_extra_name_flag = 1; + //has layer 06 return 1; } @@ -1782,12 +1937,12 @@ static int config_sb_header_version(ubi_sb_header * sb, STREAMFILE *streamFile) sb->section1_entry_size = 0x68; sb->section2_entry_size = 0x74; - sb->external_flag_offset = 0x28; /* maybe 0x2c */ - sb->channels_offset = 0x3c; - sb->sample_rate_offset = 0x40; - sb->num_samples_offset = 0x48; - sb->stream_type_offset = 0x5c; - sb->extra_name_offset = 0x58; + 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; } @@ -1795,77 +1950,99 @@ static int config_sb_header_version(ubi_sb_header * sb, STREAMFILE *streamFile) /* 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->section1_entry_size = 0x68; + sb->section2_entry_size = 0x70; sb->map_version = 3; - sb->external_flag_offset = 0x28; - sb->stream_id_offset = 0x2c; - sb->channels_offset = 0x3c; - sb->sample_rate_offset = 0x40; - sb->num_samples_offset = 0x48; - sb->stream_type_offset = 0x5c; - sb->extra_name_offset = 0x58; + sb->cfg_external_flag = 0x28; + sb->cfg_stream_id = 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; } /* 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->section1_entry_size = 0x68; + sb->section2_entry_size = 0x70; - sb->external_flag_offset = 0x28; - sb->samples_flag_offset = 0x30; - sb->channels_offset = 0x3c; - sb->sample_rate_offset = 0x40; - sb->num_samples_offset = 0x48; - sb->num_samples_offset2 = 0x50; - sb->stream_type_offset = 0x5c; - sb->extra_name_offset = 0x58; - sb->xma_pointer_offset = 0x6c; + sb->cfg_external_flag = 0x28; + sb->cfg_samples_flag = 0x30; + sb->cfg_channels = 0x3c; + sb->cfg_sample_rate = 0x40; + sb->cfg_num_samples = 0x48; + sb->cfg_num_samples2 = 0x50; + sb->cfg_stream_type = 0x5c; + sb->cfg_extra_name = 0x58; + sb->cfg_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->section1_entry_size = 0x48; + sb->section2_entry_size = 0x58; sb->map_version = 3; - sb->external_flag_offset = 0; - sb->channels_offset = 0x28; - sb->sample_rate_offset = 0x2c; - sb->num_samples_offset = 0x34; - sb->stream_type_offset = 0x48; - sb->extra_name_offset = 0x44; + sb->cfg_external_flag = 0; + sb->cfg_channels = 0x28; + sb->cfg_sample_rate = 0x2c; + sb->cfg_num_samples = 0x34; + sb->cfg_stream_type = 0x48; + sb->cfg_extra_name = 0x44; sb->has_extra_name_flag = 1; + //no layers + 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; + + sb->map_version = 3; + + sb->cfg_external_flag = 0; + sb->cfg_channels = 0x28; + sb->cfg_sample_rate = 0x30; + sb->cfg_num_samples = 0x38; + sb->cfg_extra_name = 0x48; + sb->cfg_stream_type = 0x4c; /* 06|08 */ + + sb->has_extra_name_flag = 1; + //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; + sb->section1_entry_size = 0x5c; + sb->section2_entry_size = 0x80; sb->map_version = 3; - sb->external_flag_offset = 0x28; - sb->stream_id_offset = 0x30; - sb->samples_flag_offset = 0x34; - sb->channels_offset = 0x44; - sb->sample_rate_offset = 0x4c; - sb->num_samples_offset = 0x54; - sb->num_samples_offset2 = 0x5c; - sb->stream_type_offset = 0x68; - sb->extra_name_offset = 0x64; + sb->cfg_external_flag = 0x28; + sb->cfg_stream_id = 0x30; + sb->cfg_samples_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; + //has layer 06 return 1; } + VGM_LOG("UBI SB: unknown SB/SM version+platform for %08x\n", sb->version); return 0; }