From ccca7ad9498b2c9bc07842825bee6a94a60f4d3f Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 9 Feb 2019 23:43:50 +0100 Subject: [PATCH] Fix BAO layer/sequences and add .cpk and more games --- src/formats.c | 1 + src/meta/ubi_bao.c | 994 +++++++++++++++++++++++++-------------------- 2 files changed, 563 insertions(+), 432 deletions(-) diff --git a/src/formats.c b/src/formats.c index f1f06122..6e34c800 100644 --- a/src/formats.c +++ b/src/formats.c @@ -107,6 +107,7 @@ static const char* extension_list[] = { "ckd", "cks", "cnk", + "cpk", "cps", "csa", //txth/reserved [LEGO Racers 2 (PS2)] "csmp", diff --git a/src/meta/ubi_bao.c b/src/meta/ubi_bao.c index 0f4276d5..a2a32ec9 100644 --- a/src/meta/ubi_bao.c +++ b/src/meta/ubi_bao.c @@ -4,9 +4,12 @@ #include "ubi_bao_streamfile.h" -typedef enum { CODEC_NONE = 0, UBI_IMA, RAW_PCM, RAW_PSX, RAW_XMA1, RAW_XMA2, RAW_AT3, RAW_AT3_105, FMT_AT3, RAW_DSP, FMT_OGG } ubi_bao_codec; +#define BAO_MAX_LAYER_COUNT 16 /* arbitrary max */ +#define BAO_MAX_CHAIN_COUNT 128 /* POP:TFS goes up to ~100 */ + +typedef enum { CODEC_NONE = 0, UBI_IMA, RAW_PCM, RAW_PSX, RAW_XMA1, RAW_XMA2_OLD, RAW_XMA2_NEW, RAW_AT3, RAW_AT3_105, FMT_AT3, RAW_DSP, FMT_OGG } ubi_bao_codec; typedef enum { TYPE_NONE = 0, UBI_AUDIO, UBI_LAYER, UBI_SEQUENCE } ubi_bao_type; -typedef enum { FILE_NONE = 0, UBI_FORGE } ubi_bao_file; +typedef enum { FILE_NONE = 0, UBI_FORGE, UBI_FORGE_b } ubi_bao_file; typedef struct { size_t bao_class; @@ -26,8 +29,6 @@ typedef struct { off_t audio_num_samples2; off_t audio_stream_type; off_t audio_prefetch_size; - off_t audio_xma_offset; //todo remove, depends on extra table - off_t audio_dsp_offset; size_t audio_interleave; int audio_channel_samples; int audio_external_and; @@ -44,16 +45,18 @@ typedef struct { off_t layer_stream_id; off_t layer_stream_size; off_t layer_prefetch_size; - size_t layer_extra_size; + off_t layer_extra_size; + off_t layer_cue_count; + off_t layer_cue_labels; off_t layer_sample_rate; off_t layer_channels; off_t layer_stream_type; off_t layer_num_samples; - off_t layer_entry_id; size_t layer_entry_size; int layer_external_and; + int layer_ignore_error; - off_t silence_duration_float; + //off_t silence_duration_float; ubi_bao_codec codec_map[16]; ubi_bao_file file_type; @@ -61,6 +64,7 @@ typedef struct { } ubi_bao_config; typedef struct { + int is_atomic; int version; ubi_bao_type type; @@ -68,8 +72,6 @@ typedef struct { int big_endian; int total_subsongs; - int is_atomic; - /* config */ ubi_bao_config cfg; @@ -77,21 +79,22 @@ typedef struct { off_t header_offset; uint8_t header_format; uint32_t header_version; - size_t header_skip; uint32_t header_id; uint32_t header_type; + size_t header_skip; /* common sub-header size */ + size_t header_size; /* normal base size (not counting extra tables) */ + size_t extra_size; /* extra tables size */ uint32_t stream_id; size_t stream_size; off_t stream_offset; - off_t prefetch_skip; + uint32_t prefetch_id; size_t prefetch_size; off_t prefetch_offset; - size_t main_size; - off_t main_offset; - off_t xma_offset; - off_t dsp_offset; - size_t extra_size; + + size_t memory_skip; + size_t stream_skip; + int is_prefetched; int is_external; @@ -104,11 +107,11 @@ typedef struct { int layer_count; int sequence_count; - uint32_t sequence_chain[64]; + uint32_t sequence_chain[BAO_MAX_CHAIN_COUNT]; int sequence_loop; int sequence_single; - float duration; + //float duration; char resource_name[255]; @@ -120,13 +123,15 @@ typedef struct { static int parse_header(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset); static int parse_bao(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset, int target_subsong); -static int parse_pk_header(ubi_bao_header * bao, STREAMFILE *streamFile); -static VGMSTREAM * init_vgmstream_ubi_bao_main(ubi_bao_header * bao, STREAMFILE *streamFile); +static int parse_pk(ubi_bao_header * bao, STREAMFILE *streamFile); +static VGMSTREAM * init_vgmstream_ubi_bao_header(ubi_bao_header * bao, STREAMFILE *streamFile); static STREAMFILE * setup_bao_streamfile(ubi_bao_header *bao, STREAMFILE *streamFile); +static STREAMFILE * open_atomic_bao(ubi_bao_file file_type, uint32_t file_id, int is_stream, STREAMFILE *streamFile); +static int find_package_bao(uint32_t bao_id, STREAMFILE *streamFile, off_t *out_offset, size_t *out_size); + static int config_bao_version(ubi_bao_header * bao, STREAMFILE *streamFile); static void config_bao_endian(ubi_bao_header * bao, off_t offset, STREAMFILE *streamFile); static void build_readable_name(char * buf, size_t buf_size, ubi_bao_header * bao); -static STREAMFILE * open_atomic_bao(ubi_bao_file file_type, uint32_t file_id, int is_stream, STREAMFILE *streamFile); /* .PK - packages with BAOs from Ubisoft's sound engine ("DARE") games in 2008+ */ @@ -134,17 +139,18 @@ VGMSTREAM * init_vgmstream_ubi_bao_pk(STREAMFILE *streamFile) { ubi_bao_header bao = { 0 }; /* checks */ - if (!check_extensions(streamFile, "pk,lpk")) + if (!check_extensions(streamFile, "pk,lpk,cpk")) goto fail; /* package .pk+spk (or .lpk+lspk for localized) database-like format, evolved from Ubi sbN/smN. - * .pk has an index pointing to memory BAOs and tables with external stream BAOs in .spk */ + * .pk has an index pointing to memory BAOs and tables with external stream BAOs in .spk. */ /* main parse */ - if (!parse_pk_header(&bao, streamFile)) + if (!parse_pk(&bao, streamFile)) goto fail; - return init_vgmstream_ubi_bao_main(&bao, streamFile); + build_readable_name(bao.readable_name, sizeof(bao.readable_name), &bao); + return init_vgmstream_ubi_bao_header(&bao, streamFile); fail: return NULL; } @@ -162,8 +168,8 @@ VGMSTREAM * init_vgmstream_ubi_bao_atomic(STREAMFILE *streamFile) { * since BAOs reference each other by id and are named by it (though the internal BAO id may * be other) we can simulate it. Extension is .bao/sbao or extensionaless in some games. */ - /* format: 0x01=base BAO (all atomic BAOs */ - if (read_8bit(0x00, streamFile) != 0x01) + /* format: 0x01=AC1, 0x02=POP2008 */ + if (read_8bit(0x00, streamFile) != 0x01 && read_8bit(0x00, streamFile) != 0x02) goto fail; bao.is_atomic = 1; @@ -176,14 +182,8 @@ VGMSTREAM * init_vgmstream_ubi_bao_atomic(STREAMFILE *streamFile) { if (!parse_bao(&bao, streamFile, 0x00, 1)) goto fail; - if (bao.total_subsongs == 0) { - VGM_LOG("UBI BAO: no streams\n"); - goto fail; /* not uncommon */ - } - build_readable_name(bao.readable_name, sizeof(bao.readable_name), &bao); - - return init_vgmstream_ubi_bao_main(&bao, streamFile); + return init_vgmstream_ubi_bao_header(&bao, streamFile); fail: close_streamfile(streamData); return NULL; @@ -261,64 +261,87 @@ static VGMSTREAM * init_vgmstream_ubi_bao_base(ubi_bao_header * bao, STREAMFILE vgmstream->coding_type = coding_NGC_DSP; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = bao->stream_size / bao->channels; - dsp_read_coefs_be(vgmstream, streamHead, bao->dsp_offset + 0x10, 0x40); + + /* mini DSP header (first 0x10 seem to contain DSP header fields like nibbles and format) */ + dsp_read_coefs_be(vgmstream, streamHead, bao->header_offset + bao->header_size + 0x10, 0x40); + dsp_read_hist_be (vgmstream, streamHead, bao->header_offset + bao->header_size + 0x34, 0x40); /* after gain/initial ps */ break; #ifdef VGM_USE_FFMPEG + //todo: some XMA1 decode a bit strangely at certain positions (FFmpeg bug?) case RAW_XMA1: - case RAW_XMA2: { + case RAW_XMA2_OLD: + case RAW_XMA2_NEW: { uint8_t buf[0x100]; - uint32_t num_frames; - size_t bytes, chunk_size, frame_size, data_size; - int is_xma2_old; - STREAMFILE *header_data; - off_t xma_offset; + size_t bytes, chunk_size, data_size; + off_t chunk_offset; + STREAMFILE *streamXMA; - if (bao->version == 0x00230008) { - is_xma2_old = 1; - chunk_size = 0x2c; + switch(bao->codec) { + case RAW_XMA1: chunk_size = 0x20; break; + case RAW_XMA2_OLD: chunk_size = 0x2c; break; + case RAW_XMA2_NEW: chunk_size = 0x34; break; + default: goto fail; + } + + //todo improve XMA subheader skip + //- audio memory: in header + //- audio stream: in data + //- layer memory: in layer mem, right before audio (technically in header...) + //- layer stream: same? + + /* XMA header chunk is stored in different places, setup and also find actual data start */ + if (bao->is_external || bao->type == UBI_LAYER) { + uint8_t flag, bits_per_frame; + uint32_t sec1_num, sec2_num, sec3_num; + size_t header_size, frame_size; + off_t header_offset = start_offset + chunk_size; + + /* skip custom XMA seek? table after standard XMA/fmt header chunk */ + if (bao->codec == RAW_XMA1) { + flag = read_8bit(header_offset + 0x00, streamData); + sec2_num = read_32bitBE(header_offset + 0x04, streamData); /* number of XMA frames */ + frame_size = 0x800; + sec1_num = read_32bitBE(header_offset + 0x08, streamData); + sec3_num = read_32bitBE(header_offset + 0x0c, streamData); + header_size = chunk_size + 0x10; + } + else { + flag = read_8bit(header_offset + 0x00, streamData); + sec2_num = read_32bitBE(header_offset + 0x04, streamData); /* number of XMA frames */ + frame_size = 0x800; //read_32bitBE(header_offset + 0x08, streamData); /* not always present? */ + sec1_num = read_32bitBE(header_offset + 0x0c, streamData); + sec3_num = read_32bitBE(header_offset + 0x10, streamData); /* assumed */ + header_size = chunk_size + 0x14; + } + + bits_per_frame = 4; + if (flag == 0x02 || flag == 0x04) + bits_per_frame = 2; + else if (flag == 0x08) + bits_per_frame = 1; + + header_size += sec1_num * 0x04; + header_size += align_size_to_block(sec2_num * bits_per_frame, 32) / 8; /* bitstream seek table? */ + header_size += sec3_num * 0x08; + + streamXMA = streamData; + chunk_offset = 0x00; + start_offset += header_size; + data_size = sec2_num * frame_size; } else { - is_xma2_old = 0; - chunk_size = (bao->codec == RAW_XMA1) ? 0x20 : 0x34; - } - - if (bao->is_external) { - /* external XMA sounds have a custom header */ - /* first there's XMA2/FMT chunk, after that: */ - /* 0x00: some low number like 0x01 or 0x04 */ - /* 0x04: number of frames */ - /* 0x08: frame size (not always present?) */ - /* then there's a set of rising numbers followed by some weird data?.. */ - /* calculate true XMA size and use that get data start offset */ - //todo see Ubi SB - num_frames = read_32bitBE(start_offset + chunk_size + 0x04, streamData); - //frame_size = read_32bitBE(start_offset + chunk_size + 0x08, streamData); - frame_size = 0x800; - - data_size = num_frames * frame_size; - start_offset = bao->stream_size - data_size; - } - else { - data_size = bao->stream_size; + streamXMA = streamHead; + chunk_offset = bao->header_offset + bao->header_size; start_offset = 0x00; + data_size = bao->stream_size; } - /* XMA header is stored in 0x20 header for internal sounds and before audio data for external sounds */ - if (bao->is_external) { - header_data = streamData; - xma_offset = 0x00; + if (bao->codec == RAW_XMA2_OLD) { + bytes = ffmpeg_make_riff_xma2_from_xma2_chunk(buf,0x100, chunk_offset, chunk_size, data_size, streamXMA); } else { - header_data = streamHead; - xma_offset = bao->xma_offset; - } - - if (is_xma2_old) { - bytes = ffmpeg_make_riff_xma2_from_xma2_chunk(buf,sizeof(buf), xma_offset, chunk_size, data_size, header_data); - } - else { - bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,sizeof(buf), xma_offset, chunk_size, data_size, header_data, 1); + bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,0x100, chunk_offset, chunk_size, data_size, streamXMA, 1); } vgmstream->codec_data = init_ffmpeg_header_offset(streamData, buf, bytes, start_offset, data_size); @@ -328,7 +351,7 @@ static VGMSTREAM * init_vgmstream_ubi_bao_base(ubi_bao_header * bao, STREAMFILE vgmstream->stream_size = data_size; - xma_fix_raw_samples(vgmstream, streamData, start_offset,data_size, 0, 0,0); + xma_fix_raw_samples(vgmstream, streamData, start_offset,data_size,0, 0,0); break; } @@ -339,7 +362,7 @@ static VGMSTREAM * init_vgmstream_ubi_bao_base(ubi_bao_header * bao, STREAMFILE block_size = (bao->codec == RAW_AT3_105 ? 0x98 : 0xc0) * vgmstream->channels; joint_stereo = 0; - encoder_delay = 0x00;//todo not correct + encoder_delay = 0; /* num_samples is full bytes-to-samples (unlike FMT_AT3) and comparing X360 vs PS3 games seems ok */ bytes = ffmpeg_make_riff_atrac3(buf, 0x100, vgmstream->num_samples, vgmstream->stream_size, vgmstream->channels, vgmstream->sample_rate, block_size, joint_stereo, encoder_delay); vgmstream->codec_data = init_ffmpeg_header_offset(streamData, buf, bytes, start_offset, bao->stream_size); @@ -389,6 +412,8 @@ static VGMSTREAM * init_vgmstream_ubi_bao_base(ubi_bao_header * bao, STREAMFILE fail: close_vgmstream(vgmstream); + + VGM_LOG("UBI BAO: failed init base\n"); return NULL; } @@ -408,6 +433,8 @@ static VGMSTREAM * init_vgmstream_ubi_bao_audio(ubi_bao_header * bao, STREAMFILE fail: close_streamfile(streamData); close_vgmstream(vgmstream); + + VGM_LOG("UBI BAO: failed init audio\n"); return NULL; } @@ -416,6 +443,7 @@ static VGMSTREAM * init_vgmstream_ubi_bao_layer(ubi_bao_header *bao, STREAMFILE layered_layout_data* data = NULL; STREAMFILE* temp_streamFile = NULL; STREAMFILE * streamData = NULL; + size_t full_stream_size = bao->stream_size; int i; streamData = setup_bao_streamfile(bao, streamFile); @@ -427,10 +455,13 @@ static VGMSTREAM * init_vgmstream_ubi_bao_layer(ubi_bao_header *bao, STREAMFILE /* open all layers and mix */ for (i = 0; i < bao->layer_count; i++) { + /* prepare streamfile from a single layer section */ - temp_streamFile = setup_ubi_bao_streamfile(streamData, 0x00, bao->stream_size, i, bao->layer_count, bao->big_endian); + temp_streamFile = setup_ubi_bao_streamfile(streamData, 0x00, full_stream_size, i, bao->layer_count, bao->big_endian); if (!temp_streamFile) goto fail; + bao->stream_size = get_streamfile_size(temp_streamFile); + /* build the layer VGMSTREAM (standard sb with custom streamfile) */ data->layers[i] = init_vgmstream_ubi_bao_base(bao, streamFile, temp_streamFile); if (!data->layers[i]) goto fail; @@ -446,10 +477,10 @@ static VGMSTREAM * init_vgmstream_ubi_bao_layer(ubi_bao_header *bao, STREAMFILE vgmstream = allocate_vgmstream(bao->channels * bao->layer_count, bao->loop_flag); if (!vgmstream) goto fail; - vgmstream->meta_type = meta_UBI_SB; + vgmstream->meta_type = meta_UBI_BAO; vgmstream->sample_rate = bao->sample_rate; vgmstream->num_streams = bao->total_subsongs; - vgmstream->stream_size = bao->stream_size; + vgmstream->stream_size = full_stream_size; vgmstream->num_samples = bao->num_samples; vgmstream->loop_start_sample = bao->loop_start; @@ -469,6 +500,8 @@ fail: close_vgmstream(vgmstream); else free_layout_layered(data); + + VGM_LOG("UBI BAO: failed init layer\n"); return NULL; } @@ -492,7 +525,7 @@ static VGMSTREAM * init_vgmstream_ubi_bao_sequence(ubi_bao_header *bao, STREAMFI int entry_id = bao->sequence_chain[i]; if (bao->is_atomic) { - /* get the base memory BAO */ + /* open memory audio BAO */ streamChain = open_atomic_bao(bao->cfg.file_type, entry_id, 0, streamFile); if (!streamChain) { VGM_LOG("UBI BAO: chain BAO %08x not found\n", entry_id); @@ -503,13 +536,21 @@ static VGMSTREAM * init_vgmstream_ubi_bao_sequence(ubi_bao_header *bao, STREAMFI if (!parse_header(&temp_bao, streamChain, 0x00)) goto fail; - /* should be ready, will open its BAOs in ubi_bao_main */ + /* will open its companion BAOs later */ close_streamfile(streamChain); streamChain = NULL; } else { - //todo find base BAO offset in index and parse - goto fail; + /* find memory audio BAO */ + off_t entry_offset; + if (!find_package_bao(entry_id, streamFile, &entry_offset, NULL)) { + VGM_LOG("UBI BAO: expected chain id %08x not found\n", entry_id); + goto fail; + } + + /* parse BAO */ + if (!parse_header(&temp_bao, streamFile, entry_offset)) + goto fail; } if (temp_bao.type == TYPE_NONE || temp_bao.type == UBI_SEQUENCE) { @@ -518,7 +559,7 @@ static VGMSTREAM * init_vgmstream_ubi_bao_sequence(ubi_bao_header *bao, STREAMFI } /* build the layer VGMSTREAM (current sb entry config) */ - data->segments[i] = init_vgmstream_ubi_bao_main(&temp_bao, streamFile); + data->segments[i] = init_vgmstream_ubi_bao_header(&temp_bao, streamFile); if (!data->segments[i]) goto fail; if (i == bao->sequence_loop) @@ -530,6 +571,8 @@ static VGMSTREAM * init_vgmstream_ubi_bao_sequence(ubi_bao_header *bao, STREAMFI bao->sample_rate = temp_bao.sample_rate; } + //todo Rabbids 0x200000bd.pk#24 mixes 2ch audio with 2ch*3 layers + if (!setup_layout_segmented(data)) goto fail; @@ -537,7 +580,7 @@ static VGMSTREAM * init_vgmstream_ubi_bao_sequence(ubi_bao_header *bao, STREAMFI vgmstream = allocate_vgmstream(data->segments[0]->channels, !bao->sequence_single); if (!vgmstream) goto fail; - vgmstream->meta_type = meta_UBI_SB; + vgmstream->meta_type = meta_UBI_BAO; vgmstream->sample_rate = data->segments[0]->sample_rate; vgmstream->num_streams = bao->total_subsongs; //vgmstream->stream_size = bao->stream_size; /* auto when getting avg br */ @@ -557,6 +600,8 @@ fail: close_vgmstream(vgmstream); else free_layout_segmented(data); + + VGM_LOG("UBI BAO: failed init sequence\n"); return NULL; } @@ -565,20 +610,20 @@ fail: //} -static VGMSTREAM * init_vgmstream_ubi_bao_main(ubi_bao_header * bao, STREAMFILE * streamFile) { +static VGMSTREAM * init_vgmstream_ubi_bao_header(ubi_bao_header * bao, STREAMFILE * streamFile) { VGMSTREAM * vgmstream = NULL; - if (bao->total_subsongs == 0) { + if (bao->total_subsongs <= 0) { VGM_LOG("UBI BAO: no subsongs\n"); - goto fail; + goto fail; /* not uncommon */ } - ;VGM_LOG("UBI BAO: target at %x, id=%x, s_id=%x\n", - (uint32_t)bao->header_offset, bao->header_id, bao->stream_id); + ;VGM_LOG("UBI BAO: target at %x, h_id=%08x, s_id=%08x, p_id=%08x\n", + (uint32_t)bao->header_offset, bao->header_id, bao->stream_id, bao->prefetch_id); ;VGM_LOG("UBI BAO: stream=%x, size=%x, res=%s\n", (uint32_t)bao->stream_offset, bao->stream_size, (bao->is_external ? bao->resource_name : "internal")); - ;VGM_LOG("UBI BAO: prefetch=%x, size=%x, main=%x, size=%x\n", - (uint32_t)bao->prefetch_offset, bao->prefetch_size, (uint32_t)bao->main_offset, bao->main_size); + ;VGM_LOG("UBI BAO: type=%i, header=%x, extra=%x, prefetch=%x, size=%x\n", + bao->header_type, bao->header_size, bao->extra_size, (uint32_t)bao->prefetch_offset, bao->prefetch_size); switch(bao->type) { @@ -618,26 +663,28 @@ fail: /* ************************************************************************* */ /* parse a .pk (package) file: index + BAOs + external .spk resource table. We want header - * BAOs pointing to internal/external stream BAOs (.spk is the same, with stream BAOs only). */ -static int parse_pk_header(ubi_bao_header * bao, STREAMFILE *streamFile) { + * BAOs pointing to internal/external stream BAOs (.spk is the same, with stream BAOs only). + * A fun feature of .pk is that different BAOs in a .pk can point to different .spk BAOs + * that actually hold the same data, with different GUID too, somehow. */ +static int parse_pk(ubi_bao_header * bao, STREAMFILE *streamFile) { int i; int index_entries; size_t index_size, index_header_size; - off_t bao_offset, resources_offset; + off_t bao_offset; int target_subsong = streamFile->stream_index; STREAMFILE *streamIndex = NULL; STREAMFILE *streamTest = NULL; - /* format: 0x01=index, 0x02=BAO */ + /* format: 0x01=package index, 0x02=package BAO */ if (read_8bit(0x00, streamFile) != 0x01) goto fail; /* index and resources are always LE */ - if (target_subsong == 0) target_subsong = 1; + if (target_subsong <= 0) target_subsong = 1; bao->version = read_32bitBE(0x00, streamFile) & 0x00FFFFFF; index_size = read_32bitLE(0x04, streamFile); /* can be 0, not including */ - resources_offset = read_32bitLE(0x08, streamFile); /* always found even if not used */ + /* 0x08: resource table offset, always found even if not used */ /* 0x0c: always 0? */ /* 0x10: unknown, null if no entries */ /* 0x14: config/flags/time? (changes a bit between files), null if no entries */ @@ -685,101 +732,6 @@ static int parse_pk_header(ubi_bao_header * bao, STREAMFILE *streamFile) { ;VGM_LOG("UBI BAO: class "); {int i; for (i=0;i<16;i++){ VGM_ASSERT(bao->classes[i],"%02x=%i ",i,bao->classes[i]); }} VGM_LOG("\n"); ;VGM_LOG("UBI BAO: types "); {int i; for (i=0;i<16;i++){ VGM_ASSERT(bao->types[i],"%02x=%i ",i,bao->types[i]); }} VGM_LOG("\n"); - if (bao->total_subsongs == 0) { - VGM_LOG("UBI BAO: no streams\n"); - goto fail; /* not uncommon */ - } - if (target_subsong < 0 || target_subsong > bao->total_subsongs || bao->total_subsongs < 1) goto fail; - - //todo prefetch in layers - /* get stream pointed by header */ - if (bao->is_external) { - off_t offset; - int resources_count; - size_t strings_size; - - /* Some sounds have a prefetched part stored internally with the remaining streamed part stored externally. - * Both share stream ID in the .pk and outside, so first we find this prefetch */ - bao_offset = index_header_size + index_size; - for (i = 0; i < index_entries; i++) { - uint32_t bao_id = read_32bitLE(index_header_size + 0x08 * i + 0x00, streamFile); - size_t bao_size = read_32bitLE(index_header_size + 0x08 * i + 0x04, streamFile); - - if (bao_id == bao->stream_id) { - bao->prefetch_offset = bao_offset + bao->prefetch_skip; - break; - } - - bao_offset += bao_size; - } - - if (bao->prefetch_size) { - if (bao->prefetch_offset == 0) { - VGM_LOG("UBI BAO: couldn't find expected prefetch\n"); - goto fail; - } - bao->is_prefetched = 1; - } - else { - if (bao->prefetch_offset != 0) { - VGM_LOG("UBI BAO: unexpected prefetch for stream id found\n"); - goto fail; - } - } - - /* parse resource table to external stream (may be empty, or exist even with nothing in the file) */ - resources_count = read_32bitLE(resources_offset+0x00, streamFile); - strings_size = read_32bitLE(resources_offset+0x04, streamFile); - - offset = resources_offset + 0x04+0x04 + strings_size; - for (i = 0; i < resources_count; i++) { - uint32_t resource_id = read_32bitLE(offset+0x10*i+0x00, streamFile); - off_t name_offset = read_32bitLE(offset+0x10*i+0x04, streamFile); - off_t resource_offset = read_32bitLE(offset+0x10*i+0x08, streamFile); - size_t resource_size = read_32bitLE(offset+0x10*i+0x0c, streamFile); - - if (resource_id == bao->stream_id) { - bao->stream_offset = resource_offset + bao->header_skip; - read_string(bao->resource_name,255, resources_offset + 0x04+0x04 + name_offset, streamFile); - - if (bao->is_prefetched) { - bao->main_offset = resource_offset + bao->header_skip; - bao->main_size = resource_size - bao->header_skip; - VGM_ASSERT(bao->stream_size != bao->main_size + bao->prefetch_size, "UBI BAO: stream vs resource size mismatch\n"); - } - else { - VGM_ASSERT(bao->stream_size != resource_size - bao->header_skip, "UBI BAO: stream vs resource size mismatch\n"); - } - break; - } - } - } - else { - /* find within index */ - bao_offset = index_header_size + index_size; - for (i = 0; i < index_entries; i++) { - uint32_t bao_id = read_32bitLE(index_header_size+0x08*i+0x00, streamFile); - size_t bao_size = read_32bitLE(index_header_size+0x08*i+0x04, streamFile); - - if (bao_id == bao->stream_id) { - /* in some cases, stream size value from 0x20 header can be bigger than */ - /* the actual audio chunk o_O [Rayman Raving Rabbids: TV Party (Wii)] */ - bao->stream_size = bao_size - bao->header_skip; - bao->stream_offset = bao_offset + bao->header_skip; /* relative, adjust to skip descriptor */ - break; - } - - bao_offset += bao_size; - } - } - - if (!bao->stream_offset) { - VGM_LOG("UBI BAO: stream not found (id=%08x, external=%i)\n", bao->stream_id, bao->is_external); - goto fail; - } - - build_readable_name(bao->readable_name, sizeof(bao->readable_name), bao); - close_streamfile(streamIndex); close_streamfile(streamTest); return 1; @@ -798,12 +750,15 @@ static void build_readable_name(char * buf, size_t buf_size, ubi_bao_header * ba const char *res_name; uint32_t h_id, s_id, type; + if (bao->type == TYPE_NONE) + return; + /* config */ if (bao->is_atomic) grp_name = "atomic"; else grp_name = "package"; - pft_name = bao->prefetch_size ? "p" : "n"; + pft_name = bao->is_prefetched ? "p" : "n"; typ_name = bao->is_external ? "str" : "mem"; h_id = bao->header_id; @@ -825,10 +780,11 @@ static void build_readable_name(char * buf, size_t buf_size, ubi_bao_header * ba } } else { - if (!bao->is_atomic && bao->is_external) - res_name = bao->resource_name; - else - res_name = NULL; + res_name = NULL; + //if (!bao->is_atomic && bao->is_external) + // res_name = bao->resource_name; /* too big? */ + //else + // res_name = NULL; } /* .pk can contain many subsongs, we need something helpful @@ -858,7 +814,7 @@ static int parse_type_audio(ubi_bao_header * bao, off_t offset, STREAMFILE* stre /* prefetch data is in another internal BAO right after the base header */ if (bao->cfg.audio_prefetch_size) { bao->prefetch_size = read_32bit(h_offset + bao->cfg.audio_prefetch_size, streamFile); - bao->prefetch_skip = bao->header_skip; + bao->is_prefetched = (bao->prefetch_size > 0); } if (bao->loop_flag) { @@ -870,27 +826,14 @@ static int parse_type_audio(ubi_bao_header * bao, off_t offset, STREAMFILE* stre } bao->stream_type = read_32bit(h_offset + bao->cfg.audio_stream_type, streamFile); - if (bao->stream_type > 0x10) { - VGM_LOG("UBI BAO: unknown stream_type at %x\n", (uint32_t)offset); goto fail; - goto fail; - } - - bao->codec = bao->cfg.codec_map[bao->stream_type]; - if (bao->codec == 0x00) { - VGM_LOG("UBI BAO: unknown codec at %x\n", (uint32_t)offset); goto fail; - goto fail; - } if (bao->loop_flag && bao->cfg.audio_channel_samples) { bao->num_samples = bao->num_samples / bao->channels; } - bao->dsp_offset = bao->cfg.audio_dsp_offset; - bao->xma_offset = bao->cfg.audio_xma_offset; - return 1; -fail: - return 0; +//fail: +// return 0; } static int parse_type_sequence(ubi_bao_header * bao, off_t offset, STREAMFILE* streamFile) { @@ -909,16 +852,13 @@ static int parse_type_sequence(ubi_bao_header * bao, off_t offset, STREAMFILE* s bao->sequence_loop = read_32bit(h_offset + bao->cfg.sequence_sequence_loop, streamFile); bao->sequence_single = read_32bit(h_offset + bao->cfg.sequence_sequence_single, streamFile); bao->sequence_count = read_32bit(h_offset + bao->cfg.sequence_sequence_count, streamFile); - - if (bao->sequence_count > sizeof(bao->sequence_chain)) { /* arbitrary max */ + if (bao->sequence_count > BAO_MAX_CHAIN_COUNT) { VGM_LOG("UBI BAO: incorrect sequence count\n"); goto fail; } /* get chain in extra table */ - table_offset = offset + bao->cfg.header_base_size; - if (read_32bit(table_offset + 0x00, streamFile) == -1 || read_32bit(table_offset + 0x00, streamFile) == 0) - table_offset += 0x04; + table_offset = offset + bao->header_size; for (i = 0; i < bao->sequence_count; i++) { uint32_t entry_id = (uint32_t)read_32bit(table_offset + bao->cfg.sequence_entry_number, streamFile); @@ -937,6 +877,7 @@ static int parse_type_layer(ubi_bao_header * bao, off_t offset, STREAMFILE* stre int32_t (*read_32bit)(off_t,STREAMFILE*) = bao->big_endian ? read_32bitBE : read_32bitLE; off_t h_offset = offset + bao->header_skip; off_t table_offset; + size_t cues_size = 0; int i; /* audio header */ @@ -947,60 +888,60 @@ static int parse_type_layer(ubi_bao_header * bao, off_t offset, STREAMFILE* stre } bao->layer_count = read_32bit(h_offset + bao->cfg.layer_layer_count, streamFile); - if (bao->layer_count > 16) { /* arbitrary max */ + if (bao->layer_count > BAO_MAX_LAYER_COUNT) { VGM_LOG("UBI BAO: incorrect layer count\n"); goto fail; } - bao->is_external = read_32bit(h_offset + bao->cfg.layer_external_flag, streamFile) & bao->cfg.layer_external_and; - + bao->is_external = read_32bit(h_offset + bao->cfg.layer_external_flag, streamFile) & bao->cfg.layer_external_and; bao->stream_size = read_32bit(h_offset + bao->cfg.layer_stream_size, streamFile); - if (bao->cfg.layer_stream_id) { - bao->stream_id = read_32bit(h_offset + bao->cfg.layer_stream_id, streamFile); + bao->stream_id = read_32bit(h_offset + bao->cfg.layer_stream_id, streamFile); + + if (bao->cfg.layer_prefetch_size) { + bao->prefetch_size = read_32bit(h_offset + bao->cfg.layer_prefetch_size, streamFile); + bao->is_prefetched = (bao->prefetch_size > 0); + } + + /* extra cue table (rare, has N variable-sized labels + cue table pointing to them) */ + if (bao->cfg.layer_cue_labels) { + cues_size += read_32bit(h_offset + bao->cfg.layer_cue_labels, streamFile); + } + if (bao->cfg.layer_cue_count) { + cues_size += read_32bit(h_offset + bao->cfg.layer_cue_count, streamFile) * 0x08; } if (bao->cfg.layer_extra_size) { bao->extra_size = read_32bit(h_offset + bao->cfg.layer_extra_size, streamFile); } else { - bao->extra_size = bao->layer_count * bao->cfg.layer_entry_size; - } - - /* prefetch data can be in this BAO or in another memory BAO */ //todo find correct flag? - if (bao->cfg.layer_prefetch_size) { - bao->prefetch_size = read_32bit(h_offset + bao->cfg.layer_prefetch_size, streamFile); - bao->prefetch_skip = bao->cfg.header_base_size + bao->extra_size; + bao->extra_size = cues_size + bao->layer_count * bao->cfg.layer_entry_size + cues_size; } /* get 1st layer header in extra table and validate all headers match */ - table_offset = offset + bao->cfg.header_base_size; - if (read_32bit(table_offset + 0x00, streamFile) == -1 || read_32bit(table_offset + 0x00, streamFile) == 0) //todo improve - table_offset += 0x04; + table_offset = offset + bao->header_size + cues_size; bao->channels = read_32bit(table_offset + bao->cfg.layer_channels, streamFile); bao->sample_rate = read_32bit(table_offset + bao->cfg.layer_sample_rate, streamFile); bao->stream_type = read_32bit(table_offset + bao->cfg.layer_stream_type, streamFile); bao->num_samples = read_32bit(table_offset + bao->cfg.layer_num_samples, streamFile); - if (bao->cfg.layer_entry_id) { - bao->stream_id = read_32bit(table_offset + bao->cfg.layer_entry_id, streamFile); - } for (i = 0; i < bao->layer_count; i++) { int channels = read_32bit(table_offset + bao->cfg.layer_channels, streamFile); int sample_rate = read_32bit(table_offset + bao->cfg.layer_sample_rate, streamFile); int stream_type = read_32bit(table_offset + bao->cfg.layer_stream_type, streamFile); int num_samples = read_32bit(table_offset + bao->cfg.layer_num_samples, streamFile); - if (bao->channels != channels || bao->sample_rate != sample_rate || bao->stream_type != stream_type) { + if (bao->sample_rate != sample_rate || bao->stream_type != stream_type) { VGM_LOG("UBI BAO: layer headers don't match at %x\n", (uint32_t)table_offset); + + if (bao->cfg.layer_ignore_error) { + bao->layer_count -= 1; + break; + } + goto fail; } - if (bao->cfg.layer_entry_id) { - int stream_id = read_32bit(table_offset + bao->cfg.layer_entry_id, streamFile); - if (bao->stream_id != stream_id) { - VGM_LOG("UBI BAO: layer stream ids don't match at %x\n", (uint32_t)table_offset); - goto fail; - } - } + /* unusual but happens, layers handle it fine [Rayman Raving Rabbids: TV Party (Wii) ex. 0x22000cbc.pk] */ + VGM_ASSERT_ONCE(bao->channels != channels, "UBI BAO: layer channels don't match at %x\n", (uint32_t)table_offset); /* can be +-1 */ if (bao->num_samples != num_samples && bao->num_samples + 1 == num_samples) { @@ -1010,49 +951,216 @@ static int parse_type_layer(ubi_bao_header * bao, off_t offset, STREAMFILE* stre table_offset += bao->cfg.layer_entry_size; } + return 1; +fail: + return 0; +} +/* adjust some common values */ +static int parse_values(ubi_bao_header * bao, STREAMFILE *streamFile) { + + if (bao->type == UBI_SEQUENCE) + return 1; + + /* common validations */ + if (bao->stream_size == 0) { + VGM_LOG("UBI BAO: unknown stream_size at %x\n", (uint32_t)bao->header_offset); goto fail; + goto fail; + } + + /* set codec */ + if (bao->stream_type > 0x10) { + VGM_LOG("UBI BAO: unknown stream_type at %x\n", (uint32_t)bao->header_offset); goto fail; + goto fail; + } bao->codec = bao->cfg.codec_map[bao->stream_type]; if (bao->codec == 0x00) { - VGM_LOG("UBI BAO: unknown codec at %x\n", (uint32_t)offset); goto fail; + VGM_LOG("UBI BAO: unknown codec at %x\n", (uint32_t)bao->header_offset); goto fail; goto fail; } + /* set prefetch id */ + if (bao->is_prefetched) { + if (bao->is_atomic && bao->cfg.file_type == UBI_FORGE) { + /* AC1's stream BAO are 0x5NNNNNNN and prefetch BAO 0x3NNNNNNN (all filenames include class) */ + bao->prefetch_id = (bao->stream_id & 0x0FFFFFFF) | 0x30000000; + } + else { + /* shared id in index and resource table, or named atomic BAOs */ + bao->prefetch_id = bao->stream_id; + } + } + + /* normalize base skips, as memory data (prefetch or not, atomic or package) can be + * in a memory BAO after base header or audio layer BAO after the extra table */ + if (bao->stream_id == bao->header_id && (!bao->is_external || bao->is_prefetched)) { /* layers with memory data */ + bao->memory_skip = bao->header_size + bao->extra_size; + bao->stream_skip = bao->header_skip; + } + else { + bao->memory_skip = bao->header_skip; + bao->stream_skip = bao->header_skip; + } + + return 1; fail: return 0; } + +/* set actual offsets in various places */ +static int parse_offsets(ubi_bao_header * bao, STREAMFILE *streamFile) { + off_t bao_offset; + size_t bao_size; + + if (bao->type == UBI_SEQUENCE) + return 1; + + if (!bao->is_external && bao->is_prefetched) { + VGM_LOG("UBI BAO: unexpected non-streamed prefetch at %x\n", (uint32_t)bao->header_offset); + goto fail; + } + + /* Audio headers can point to audio data in multiple forms we must configure here: + * - memory part (internal .pk BAO or separate atomic .bao) + * - streamed part (external .spk BAO or separate atomic .sbao) + * - prefetched memory part + streamed part (must join both during reads) + * + * Offsets are absolute (ignoring the index table that even .spk has) but point to BAO + * base header start, that we must also skip to reach actual audio data. + */ + + if (bao->is_atomic) { + if (bao->is_prefetched) { + bao->prefetch_offset = bao->memory_skip; + } + else if (bao->is_external) { + bao->stream_offset = bao->stream_skip; + } + else { + bao->stream_offset = bao->memory_skip; + } + } + else { + if (bao->is_prefetched) { + if (!find_package_bao(bao->prefetch_id, streamFile, &bao_offset, &bao_size)) { + VGM_LOG("UBI BAO: expected prefetch id %08x not found\n", bao->prefetch_id); + goto fail; + } + + bao->prefetch_offset = bao_offset + bao->memory_skip; + if (bao->prefetch_size + bao->memory_skip != bao_size) { + VGM_LOG("UBI BAO: unexpected prefetch size %x vs %x\n", bao->prefetch_size + bao->memory_skip, bao_size); + goto fail; + } + } + + if (bao->is_external) { + int i; + off_t offset; + off_t resources_offset = read_32bitLE(0x08, streamFile); + int resources_count = read_32bitLE(resources_offset+0x00, streamFile); + size_t strings_size = read_32bitLE(resources_offset+0x04, streamFile); + + /* parse resource table to external stream (may be empty, or exist even with nothing in the file) */ + offset = resources_offset + 0x04+0x04 + strings_size; + for (i = 0; i < resources_count; i++) { + uint32_t resource_id = read_32bitLE(offset+0x10*i+0x00, streamFile); + off_t name_offset = read_32bitLE(offset+0x10*i+0x04, streamFile); + off_t resource_offset = read_32bitLE(offset+0x10*i+0x08, streamFile); + size_t resource_size = read_32bitLE(offset+0x10*i+0x0c, streamFile); + + if (resource_id == bao->stream_id) { + bao->stream_offset = resource_offset + bao->stream_skip; + + read_string(bao->resource_name,255, resources_offset + 0x04+0x04 + name_offset, streamFile); + + if (bao->stream_size != resource_size - bao->stream_skip + bao->prefetch_size) { + VGM_ASSERT(bao->stream_size != resource_size - bao->stream_skip + bao->prefetch_size, + "UBI BAO: stream vs resource size mismatch at %lx\n", offset+0x10*i); + goto fail; + } + break; + } + } + + if (bao->stream_offset == 0) { + VGM_LOG("UBI BAO: expected external id %08x not found\n", bao->stream_id); + goto fail; + } + } + else { + if (!find_package_bao(bao->stream_id, streamFile, &bao_offset, &bao_size)) { + VGM_LOG("UBI BAO: expected internal id %08x not found\n", bao->stream_id); + goto fail; + } + bao->stream_offset = bao_offset + bao->memory_skip; + + /* in some cases, stream size value from audio header can be bigger (~0x18) + * than actual audio chunk o_O [Rayman Raving Rabbids: TV Party (Wii)] */ + if (bao->stream_size > bao_size - bao->memory_skip) { + VGM_LOG("UBI BAO: bad stream size found: %x + %x vs %x\n", bao->stream_size, bao->memory_skip, bao_size); + + /* too big is usually bad config */ + if (bao->stream_size > bao_size + bao->header_size) { + VGM_LOG("UBI BAO: bad stream config at %x\n", (uint32_t)bao->header_offset); + goto fail; + } + + bao->stream_size = bao_size - bao->memory_skip; + } + } + + } + + return 1; +fail: + return 0; +} + +/* parse a single known header resource at offset (see config_bao for info) */ static int parse_header(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset) { int32_t (*read_32bit)(off_t,STREAMFILE*) = bao->big_endian ? read_32bitBE : read_32bitLE; - ;VGM_LOG("UBI BAO: header at %x\n", (uint32_t)offset); - - /* parse known headers (see config_bao for info) */ bao->header_offset = offset; - bao->header_format = read_8bit (offset + 0x00, streamFile); /* usually 0x02 but older BAOs have 0x01 too */ + bao->header_format = read_8bit (offset + 0x00, streamFile); /* 0x01: atomic, 0x02: package */ bao->header_version = read_32bitBE(offset + 0x00, streamFile) & 0x00FFFFFF; - /* early versions: + if (bao->version != bao->header_version) { + VGM_LOG("UBI BAO: mismatched header version at %x\n", (uint32_t)offset); + goto fail; + } + + /* - base part in early versions: * 0x04: header skip (usually 0x28, rarely 0x24), can be LE unlike other fields (ex. Assassin's Creed PS3) * 0x08(10): GUID, or id-like fields in early versions * 0x18: null * 0x1c: null * 0x20: class - * 0x24: extra config? (0x00/0x02) + * 0x24: config/version? (0x00/0x01/0x02) * - * later versions: + * - base part in later versions: * 0x04(10): GUID * 0x14: class - * 0x18: extra config? (0x02) */ + * 0x18: config/version? (0x02) + * 0x1c: fixed value? */ bao->header_skip = bao->cfg.header_skip; bao->header_id = read_32bit(offset + bao->header_skip + 0x00, streamFile); bao->header_type = read_32bit(offset + bao->header_skip + 0x04, streamFile); - if (bao->version != bao->header_version) { - VGM_LOG("UBI BAO: mismatched header version at %x: %08x vs %08x\n", (uint32_t)offset, bao->version, bao->header_version); - goto fail; + bao->header_size = bao->cfg.header_base_size; + + /* detect extra unused field in PC/Wii + * (could be improved but no apparent flags or anything useful) */ + if (get_streamfile_size(streamFile) > offset + bao->header_size) { + /* may read next BAO version, layer header, cues, resource table size, etc, always > 1 */ + int32_t end_field = read_32bit(offset + bao->header_size, streamFile); + + if (end_field == -1 || end_field == 0 || end_field == 1) /* some count? */ + bao->header_size += 0x04; } switch(bao->header_type) { @@ -1073,6 +1181,12 @@ static int parse_header(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offs goto fail; } + if (!parse_values(bao, streamFile)) + goto fail; + + if (!parse_offsets(bao, streamFile)) + goto fail; + return 1; fail: return 0; @@ -1098,7 +1212,7 @@ static int parse_bao(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset, return 1; header_type = read_32bit(offset + bao->cfg.header_skip + 0x04, streamFile); - if (header_type > 8) { + if (header_type > 9) { VGM_LOG("UBI BAO: unknown type %x at %x\n", header_type, (uint32_t)offset); goto fail; } @@ -1121,19 +1235,21 @@ fail: return 0; } +/* ************************************************************************* */ + /* opens a file BAO's companion BAO (memory or stream) */ static STREAMFILE * open_atomic_bao(ubi_bao_file file_type, uint32_t file_id, int is_stream, STREAMFILE *streamFile) { STREAMFILE *streamBAO = NULL; char buf[255]; - size_t buf_size = sizeof(buf); + size_t buf_size = 255; /* Get referenced BAOs, in different naming styles for "internal" (=memory) or "external" (=stream). */ switch(file_type) { case UBI_FORGE: + case UBI_FORGE_b: /* Try default extensionless (as extracted from .forge bigfile) and with common extension. - * .forge data can be uncompressed (stream BAOs) and compressed (subfiles per area with memory BAOs). - * They are named after their class too (0x1NNNNNNN: events, 0x2NNNNNNN: headers, etc) */ + * .forge data can be uncompressed (stream BAOs) and compressed (subfiles per area with memory BAOs). */ if (is_stream) { snprintf(buf,buf_size, "Common_BAO_0x%08x", file_id); streamBAO = open_streamfile_by_filename(streamFile, buf); @@ -1143,10 +1259,31 @@ static STREAMFILE * open_atomic_bao(ubi_bao_file file_type, uint32_t file_id, in streamBAO = open_streamfile_by_filename(streamFile, buf); if (streamBAO) return streamBAO; - /* there are many per language but whatevs, could be renamed */ snprintf(buf,buf_size, "English_BAO_0x%08x", file_id); streamBAO = open_streamfile_by_filename(streamFile, buf); if (streamBAO) return streamBAO; + + snprintf(buf,buf_size, "French_BAO_0x%08x", file_id); + streamBAO = open_streamfile_by_filename(streamFile, buf); + if (streamBAO) return streamBAO; + + snprintf(buf,buf_size, "German_BAO_0x%08x", file_id); + streamBAO = open_streamfile_by_filename(streamFile, buf); + if (streamBAO) return streamBAO; + + snprintf(buf,buf_size, "Italian_BAO_0x%08x", file_id); + streamBAO = open_streamfile_by_filename(streamFile, buf); + if (streamBAO) return streamBAO; + + snprintf(buf,buf_size, "Spanish_BAO_0x%08x", file_id); + streamBAO = open_streamfile_by_filename(streamFile, buf); + if (streamBAO) return streamBAO; + + snprintf(buf,buf_size, "Russian_BAO_0x%08x", file_id); + streamBAO = open_streamfile_by_filename(streamFile, buf); + if (streamBAO) return streamBAO; + + /* there may be more per language */ } else { snprintf(buf,buf_size, "BAO_0x%08x", file_id); @@ -1164,15 +1301,42 @@ static STREAMFILE * open_atomic_bao(ubi_bao_file file_type, uint32_t file_id, in goto fail; } - //todo try default naming scheme? - return streamBAO; /* may be NULL */ fail: - VGM_LOG("UBI BAO: can't find BAO id %08x\n", file_id); close_streamfile(streamBAO); + + VGM_LOG("UBI BAO: failed opening atomic BAO id %08x\n", file_id); return NULL; } +static int find_package_bao(uint32_t target_id, STREAMFILE *streamFile, off_t *out_offset, size_t *out_size) { + int i; + int index_entries; + off_t bao_offset; + size_t index_size, index_header_size; + + index_size = read_32bitLE(0x04, streamFile); + index_entries = index_size / 0x08; + index_header_size = 0x40; + + /* parse index to get target BAO */ + bao_offset = index_header_size + index_size; + for (i = 0; i < index_entries; i++) { + uint32_t bao_id = read_32bitLE(index_header_size + 0x08*i + 0x00, streamFile); + size_t bao_size = read_32bitLE(index_header_size + 0x08*i + 0x04, streamFile); + + if (bao_id == target_id) { + if (out_offset) *out_offset = bao_offset; + if (out_size) *out_size = bao_size; + return 1; + } + bao_offset += bao_size; + } + + return 0; +} + + /* create a usable streamfile */ static STREAMFILE * setup_bao_streamfile(ubi_bao_header *bao, STREAMFILE *streamFile) { STREAMFILE *new_streamFile = NULL; @@ -1191,35 +1355,21 @@ static STREAMFILE * setup_bao_streamfile(ubi_bao_header *bao, STREAMFILE *stream */ if (bao->is_atomic) { - //todo prefetch_id naming convention isn't probably always true - //todo AC1 can't contain prefetch in the base/layer BAO but what about others? - //todo do all this right after parse? - /* file BAOs re-open new STREAMFILEs so no need to wrap them */ - bao->is_prefetched = bao->prefetch_size; // - if (bao->is_prefetched) { - /* stream BAO is 0x5NNNNNNN and prefetch memory BAO 0x3NNNNNNN */ - uint32_t prefetch_id = (bao->stream_id & 0x0FFFFFFF) | 0x30000000; - uint32_t main_id = (bao->stream_id); - size_t prefetch_offset = bao->header_skip; - size_t prefetch_size = bao->prefetch_size; - size_t main_offset = bao->header_skip; - size_t main_size = bao->stream_size - bao->prefetch_size; - - new_streamFile = open_atomic_bao(bao->cfg.file_type, prefetch_id, 0, streamFile); + new_streamFile = open_atomic_bao(bao->cfg.file_type, bao->prefetch_id, 0, streamFile); if (!new_streamFile) goto fail; stream_segments[0] = new_streamFile; - new_streamFile = open_clamp_streamfile(stream_segments[0], prefetch_offset, prefetch_size); + new_streamFile = open_clamp_streamfile(stream_segments[0], bao->prefetch_offset, bao->prefetch_size); if (!new_streamFile) goto fail; stream_segments[0] = new_streamFile; - new_streamFile = open_atomic_bao(bao->cfg.file_type, main_id, 1, streamFile); + new_streamFile = open_atomic_bao(bao->cfg.file_type, bao->stream_id, 1, streamFile); if (!new_streamFile) goto fail; stream_segments[1] = new_streamFile; - new_streamFile = open_clamp_streamfile(stream_segments[1], main_offset, main_size); + new_streamFile = open_clamp_streamfile(stream_segments[1], bao->stream_offset, (bao->stream_size - bao->prefetch_size)); if (!new_streamFile) goto fail; stream_segments[1] = new_streamFile; @@ -1234,7 +1384,7 @@ static STREAMFILE * setup_bao_streamfile(ubi_bao_header *bao, STREAMFILE *stream if (!new_streamFile) goto fail; temp_streamFile = new_streamFile; - new_streamFile = open_clamp_streamfile(temp_streamFile, bao->header_skip, bao->stream_size); + new_streamFile = open_clamp_streamfile(temp_streamFile, bao->stream_offset, bao->stream_size); if (!new_streamFile) goto fail; temp_streamFile = new_streamFile; } @@ -1253,7 +1403,7 @@ static STREAMFILE * setup_bao_streamfile(ubi_bao_header *bao, STREAMFILE *stream if (!new_streamFile) { VGM_LOG("UBI BAO: external stream '%s' not found\n", bao->resource_name); goto fail; } stream_segments[1] = new_streamFile; - new_streamFile = open_clamp_streamfile(stream_segments[1], bao->main_offset, bao->main_size); + new_streamFile = open_clamp_streamfile(stream_segments[1], bao->stream_offset, (bao->stream_size - bao->prefetch_size)); if (!new_streamFile) goto fail; stream_segments[1] = new_streamFile; temp_streamFile = NULL; @@ -1265,7 +1415,6 @@ static STREAMFILE * setup_bao_streamfile(ubi_bao_header *bao, STREAMFILE *stream stream_segments[1] = NULL; } else if (bao->is_external) { - /* open external file */ new_streamFile = open_streamfile_by_filename(streamFile, bao->resource_name); if (!new_streamFile) { VGM_LOG("UBI BAO: external stream '%s' not found\n", bao->resource_name); goto fail; } temp_streamFile = new_streamFile; @@ -1293,46 +1442,71 @@ fail: close_streamfile(stream_segments[1]); close_streamfile(temp_streamFile); + VGM_LOG("UBI BAO: failed streamfile setup\n"); return NULL; } +/* ************************************************************************* */ static void config_bao_endian(ubi_bao_header * bao, off_t offset, STREAMFILE *streamFile) { - //todo this could be done once as all BAOs share endianness (would save a few checks) - /* detect endianness using the 'class' field (the 'header skip' field is LE in early - * versions, and was removed in later versions) */ + /* Detect endianness using the 'class' field (the 'header skip' field is LE in early + * versions, and was removed in later versions). + * This could be done once as all BAOs share endianness */ /* negate as fields looks like LE (0xN0000000) */ bao->big_endian = !guess_endianness32bit(offset+bao->cfg.bao_class, streamFile); } +static void config_bao_entry(ubi_bao_header * bao, size_t header_base_size, size_t header_skip) { + bao->cfg.header_base_size = header_base_size; + bao->cfg.header_skip = header_skip; +} + +static void config_bao_audio_b(ubi_bao_header * bao, off_t stream_size, off_t stream_id, off_t external_flag, off_t loop_flag, int external_and, int loop_and) { + /* audio header base */ + bao->cfg.audio_stream_size = stream_size; + bao->cfg.audio_stream_id = stream_id; + bao->cfg.audio_external_flag = external_flag; + bao->cfg.audio_loop_flag = loop_flag; + bao->cfg.audio_external_and = external_and; + bao->cfg.audio_loop_and = loop_and; +} +static void config_bao_audio_m(ubi_bao_header * bao, off_t channels, off_t sample_rate, off_t num_samples, off_t num_samples2, off_t stream_type, off_t prefetch_size) { + /* audio header main */ + bao->cfg.audio_channels = channels; + bao->cfg.audio_sample_rate = sample_rate; + bao->cfg.audio_num_samples = num_samples; + bao->cfg.audio_num_samples2 = num_samples2; + bao->cfg.audio_stream_type = stream_type; + //bao->cfg.audio_cue_count = cue_count; + //bao->cfg.audio_cue_labels = cue_labels; + bao->cfg.audio_prefetch_size = prefetch_size; +} + static void config_bao_sequence(ubi_bao_header * bao, off_t sequence_count, off_t sequence_single, off_t sequence_loop, off_t entry_size) { /* sequence header and chain table */ bao->cfg.sequence_sequence_count = sequence_count; - bao->cfg.sequence_sequence_single = sequence_count; - bao->cfg.sequence_sequence_loop = sequence_count; + bao->cfg.sequence_sequence_single = sequence_single; + bao->cfg.sequence_sequence_loop = sequence_loop; bao->cfg.sequence_entry_size = entry_size; bao->cfg.sequence_entry_number = 0x00; } -static void config_bao_layer_h1(ubi_bao_header * bao, off_t layer_count, off_t external_flag, off_t stream_size, off_t stream_id, off_t prefetch_size) { - /* layer header in the main BAO */ + +static void config_bao_layer_m(ubi_bao_header * bao, off_t stream_id, off_t layer_count, off_t external_flag, off_t stream_size, off_t extra_size, off_t prefetch_size, off_t cue_count, off_t cue_labels, int external_and) { + /* layer header in the main part */ + bao->cfg.layer_stream_id = stream_id; bao->cfg.layer_layer_count = layer_count; bao->cfg.layer_external_flag = external_flag; bao->cfg.layer_stream_size = stream_size; - bao->cfg.layer_stream_id = stream_id; - bao->cfg.layer_prefetch_size = prefetch_size; - bao->cfg.layer_external_and = 1; -} -static void config_bao_layer_h2(ubi_bao_header * bao, off_t layer_count, off_t stream_size, off_t extra_size, off_t prefetch_size) { - /* layer header in the main BAO with extra size */ - bao->cfg.layer_layer_count = layer_count; - bao->cfg.layer_stream_size = stream_size; bao->cfg.layer_extra_size = extra_size; - bao->cfg.layer_prefetch_size = prefetch_size; + bao->cfg.layer_prefetch_size = prefetch_size; /* possible flag: 0x3c */ + bao->cfg.layer_cue_count = cue_count; + bao->cfg.layer_cue_labels = cue_labels; + bao->cfg.layer_external_and = external_and; } -static void config_bao_layer_s1(ubi_bao_header * bao, off_t entry_size, off_t sample_rate, off_t channels, off_t stream_type, off_t num_samples) { +static void config_bao_layer_e(ubi_bao_header * bao, off_t entry_size, off_t sample_rate, off_t channels, off_t stream_type, off_t num_samples) { /* layer sub-headers in extra table */ bao->cfg.layer_entry_size = entry_size; bao->cfg.layer_sample_rate = sample_rate; @@ -1340,15 +1514,6 @@ static void config_bao_layer_s1(ubi_bao_header * bao, off_t entry_size, off_t sa bao->cfg.layer_stream_type = stream_type; bao->cfg.layer_num_samples = num_samples; } -static void config_bao_layer_s2(ubi_bao_header * bao, off_t entry_size, off_t sample_rate, off_t channels, off_t stream_type, off_t num_samples, off_t entry_id) { - /* layer sub-headers in extra table + stream id */ - bao->cfg.layer_entry_size = entry_size; - bao->cfg.layer_sample_rate = sample_rate; - bao->cfg.layer_channels = channels; - bao->cfg.layer_stream_type = stream_type; - bao->cfg.layer_num_samples = num_samples; - bao->cfg.layer_entry_id = entry_id; -} static int config_bao_version(ubi_bao_header * bao, STREAMFILE *streamFile) { @@ -1377,21 +1542,21 @@ static int config_bao_version(ubi_bao_header * bao, STREAMFILE *streamFile) { * * We want header classes, also similar to SB types: * - 01: single audio (samples, channels, bitrate, samples+size, etc) - * - 02: unknown chain (has probability?) + * - 02: play chain with config? (ex. silence + audio, or rarely audio 2ch intro + layer 4ch body) * - 03: unknown chain * - 04: random (count, etc) + BAO IDs and float probability to play * - 05: sequence (count, etc) + BAO IDs and unknown data * - 06: layer (count, etc) + layer headers * - 07: unknown chain * - 08: silence (duration, etc) + * - 09: silence with config? (channels, sample rate, etc), extremely rare [Shaun White Skateboarding (Wii)] * - * Right after base BAO size is the extra table for that BAO (what sectionX had). This - * can exist even for type 01 (some kind of cue-like table) though size calcs are hazy. + * Right after base BAO size is the extra table for that BAO (what sectionX had, plus + * extra crap like cue-like labels, even for type 0x01). * * Just to throw us off, the base BAO size may add +0x04 (with a field value of 0/-1) on - * some game versions/platforms (PC/Wii?). Doesn't look like there is a header field - * (comparing many BAOs from different platforms of the same games) so it's autodetected - * as needed, for layers and sequences basically. + * some game versions/platforms (PC/Wii only?). Doesn't look like there is a header field + * (comparing many BAOs from different platforms of the same games) so it's autodetected. * * Most types + tables are pretty much the same as SB (with config styles ported straight) but * now can "prefetch" part of the data (signaled by a size in the header, or perhaps a flag but @@ -1402,8 +1567,8 @@ static int config_bao_version(ubi_bao_header * bao, STREAMFILE *streamFile) { */ bao->allowed_types[0x01] = 1; - //bao->allowed_types[0x05] = 1; - //bao->allowed_types[0x06] = 1; + bao->allowed_types[0x05] = 1; + bao->allowed_types[0x06] = 1; /* absolute */ bao->cfg.bao_class = 0x20; @@ -1412,168 +1577,133 @@ static int config_bao_version(ubi_bao_header * bao, STREAMFILE *streamFile) { bao->cfg.header_id = 0x00; bao->cfg.header_type = 0x04; - bao->cfg.audio_external_and = 1; - bao->cfg.audio_loop_and = 1; - /* config per version*/ switch(bao->version) { - case 0x001B0100: /* Assassin's Creed (PS3/X360/PC)-file */ - bao->cfg.header_base_size = 0xA4; /* 0xA8: PC */ - bao->cfg.header_skip = 0x28; - //todo call config_bao_audio - bao->cfg.audio_stream_size = 0x08; - bao->cfg.audio_stream_id = 0x1c; - bao->cfg.audio_external_flag = 0x28; /* 0x2c: prefetch flag */ - bao->cfg.audio_loop_flag = 0x34; - bao->cfg.audio_channels = 0x44; - bao->cfg.audio_sample_rate = 0x4c; - bao->cfg.audio_num_samples = 0x50; - bao->cfg.audio_num_samples2 = 0x58; - bao->cfg.audio_stream_type = 0x64; - bao->cfg.audio_prefetch_size = 0x74; + case 0x001B0100: /* Assassin's Creed (PS3/X360/PC)-atomic-forge */ + config_bao_entry(bao, 0xA4, 0x28); + config_bao_audio_b(bao, 0x08, 0x1c, 0x28, 0x34, 1, 1); /* 0x2c: prefetch flag? */ + config_bao_audio_m(bao, 0x44, 0x4c, 0x50, 0x58, 0x64, 0x74); bao->cfg.audio_interleave = 0x10; bao->cfg.audio_channel_samples = 1; //todo check all looping ps-adpcm - config_bao_sequence(bao, 0x2c, 0x4c, 0x50, 0x14); //todo loop/single wrong? + config_bao_sequence(bao, 0x2c, 0x20, 0x1c, 0x14); - config_bao_layer_h1(bao, 0x20, 0x2c, 0x44 /*0x48?*/, 0x4c, 0x50); - config_bao_layer_s1(bao, 0x30, 0x00, 0x04, 0x08, 0x10); - bao->cfg.audio_external_flag = 0x28; - //0x28+0x2c may be set when full external? check layers again - - //silence: 0x1C + config_bao_layer_m(bao, 0x4c, 0x20, 0x2c, 0x44, 0x00, 0x50, 0x00, 0x00, 1); /* stream size: 0x48? */ + config_bao_layer_e(bao, 0x30, 0x00, 0x04, 0x08, 0x10); bao->cfg.codec_map[0x02] = RAW_PSX; bao->cfg.codec_map[0x03] = UBI_IMA; bao->cfg.codec_map[0x04] = FMT_OGG; bao->cfg.codec_map[0x07] = RAW_AT3_105; - //todo move - bao->allowed_types[0x05] = 1; - bao->allowed_types[0x06] = 1; - bao->cfg.file_type = UBI_FORGE; return 1; + case 0x001F0008: /* Rayman Raving Rabbids: TV Party (Wii)-package */ + case 0x001F0010: /* Prince of Persia 2008 (PC/PS3/X360)-atomic-forge, Far Cry 2 (PS3)-atomic-dunia? */ + case 0x001F0011: /* Naruto: The Broken Bond (X360)-package */ + case 0x0022000D: /* Just Dance (Wii)-package */ + case 0x0022001B: /* Prince of Persia: The Forgotten Sands (Wii)-package */ + config_bao_entry(bao, 0xA4, 0x28); - case 0x001F0008: /* Rayman Raving Rabbids: TV Party (Wii)-pk */ - case 0x001F0011: /* Naruto: The Broken Bond (X360)-pk */ - case 0x0022000D: /* Just Dance (Wii)-pk */ - bao->cfg.header_base_size = 0xA4; /* 0xA8: Wii */ - bao->cfg.header_skip = 0x28; + config_bao_audio_b(bao, 0x08, 0x1c, 0x28, 0x34, 1, 1); + config_bao_audio_m(bao, 0x44, 0x4c, 0x54, 0x5c, 0x64, 0x74); /* cues: 0x68, 0x6c */ - bao->cfg.audio_stream_size = 0x08; - bao->cfg.audio_stream_id = 0x1c; - bao->cfg.audio_external_flag = 0x28; - bao->cfg.audio_loop_flag = 0x34; - bao->cfg.audio_channels = 0x44; - bao->cfg.audio_sample_rate = 0x4c; - bao->cfg.audio_num_samples = 0x54; - bao->cfg.audio_num_samples2 = 0x5c; + config_bao_sequence(bao, 0x2c, 0x20, 0x1c, 0x14); - bao->cfg.audio_stream_type = 0x64; - bao->cfg.audio_prefetch_size = 0x74; - bao->cfg.audio_xma_offset = 0x7c; /* only if internal */ - bao->cfg.audio_dsp_offset = 0x80; + config_bao_layer_m(bao, 0x00, 0x20, 0x2c, 0x44, 0x4c, 0x50, 0x54, 0x58, 1); /* 0x1c: id-like, 0x3c: prefetch flag? */ + config_bao_layer_e(bao, 0x28, 0x00, 0x04, 0x08, 0x10); bao->cfg.codec_map[0x01] = RAW_PCM; - //bao->cfg.codec_map[0x02] = FMT_OGG; bao->cfg.codec_map[0x03] = UBI_IMA; + bao->cfg.codec_map[0x04] = FMT_OGG; bao->cfg.codec_map[0x05] = RAW_XMA1; + bao->cfg.codec_map[0x07] = RAW_AT3_105; bao->cfg.codec_map[0x09] = RAW_DSP; + bao->cfg.file_type = UBI_FORGE_b; return 1; - case 0x00220015: /* James Cameron's Avatar: The Game (PSP)-pk */ - case 0x0022001E: /* Prince of Persia: The Forgotten Sands (PSP)-pk */ - bao->cfg.header_base_size = 0x84; - //skip 0x28 + case 0x00220015: /* James Cameron's Avatar: The Game (PSP)-package */ + case 0x0022001E: /* Prince of Persia: The Forgotten Sands (PSP)-package */ + config_bao_entry(bao, 0x84, 0x28); - bao->cfg.audio_stream_size = 0x08; - bao->cfg.audio_stream_id = 0x1c; - bao->cfg.audio_external_flag = 0x20; - bao->cfg.audio_loop_flag = 0x20; /* 0x10? */ - bao->cfg.audio_channels = 0x28; - bao->cfg.audio_sample_rate = 0x30; - bao->cfg.audio_num_samples = 0x38; - bao->cfg.audio_num_samples2 = 0x40; - bao->cfg.audio_stream_type = 0x48; + config_bao_audio_b(bao, 0x08, 0x1c, 0x20, 0x20, (1 << 2), (1 << 5)); /* (1 << 4): prefetch flag? */ + config_bao_audio_m(bao, 0x28, 0x30, 0x38, 0x40, 0x48, 0x58); - bao->cfg.audio_external_and = (1 << 2); - bao->cfg.audio_loop_and = (1 << 5); + config_bao_layer_m(bao, 0x00, 0x20, 0x24, 0x34, 0x3c, 0x40, 0x00, 0x00, (1 << 2)); /* 0x1c: id-like */ + config_bao_layer_e(bao, 0x28, 0x00, 0x04, 0x08, 0x10); bao->cfg.codec_map[0x06] = RAW_PSX; bao->cfg.codec_map[0x07] = FMT_AT3; return 1; - case 0x00230008: /* Splinter Cell: Conviction (X360/PC)-pk */ - bao->cfg.header_base_size = 0xB4; - bao->cfg.header_skip = 0x28; + case 0x00230008: /* Splinter Cell: Conviction (X360/PC)-package */ + config_bao_entry(bao, 0xB4, 0x28); - bao->cfg.audio_stream_size = 0x08; - bao->cfg.audio_stream_id = 0x24; - bao->cfg.audio_external_flag = 0x38; - bao->cfg.audio_loop_flag = 0x44; - bao->cfg.audio_channels = 0x54; - bao->cfg.audio_sample_rate = 0x5c; - bao->cfg.audio_num_samples = 0x64; - bao->cfg.audio_num_samples2 = 0x6c; - bao->cfg.audio_stream_type = 0x74; - bao->cfg.audio_prefetch_size = 0x84; - bao->cfg.audio_xma_offset = 0x8c; + config_bao_audio_b(bao, 0x08, 0x24, 0x38, 0x44, 1, 1); + config_bao_audio_m(bao, 0x54, 0x5c, 0x64, 0x6c, 0x74, 0x84); - config_bao_sequence(bao, 0x34, 0x54, 0x58, 0x14); + config_bao_sequence(bao, 0x34, 0x28, 0x24, 0x14); - config_bao_layer_h2(bao, 0x28, 0x50, 0x5c, 0x54); - config_bao_layer_s2(bao, 0x30, 0x00, 0x04, 0x08, 0x14, 0x2c); + config_bao_layer_m(bao, 0x00, 0x28, 0x3c, 0x54, 0x5c, 0x00 /*0x60?*/, 0x00, 0x00, 1); /* 0x24: id-like */ + config_bao_layer_e(bao, 0x30, 0x00, 0x04, 0x08, 0x18); + bao->cfg.layer_ignore_error = 1; //todo last sfx layer (bass) may have smaller sample rate bao->cfg.codec_map[0x01] = RAW_PCM; bao->cfg.codec_map[0x02] = UBI_IMA; bao->cfg.codec_map[0x03] = FMT_OGG; - bao->cfg.codec_map[0x04] = RAW_XMA2; + bao->cfg.codec_map[0x04] = RAW_XMA2_OLD; return 1; - case 0x00250108: /* Scott Pilgrim vs the World (PS3/X360)-pk */ - case 0x0025010A: /* Prince of Persia: The Forgotten Sands (PS3/X360)-file */ - bao->cfg.header_base_size = 0xB4; - bao->cfg.header_skip = 0x28; + case 0x00250108: /* Scott Pilgrim vs the World (PS3/X360)-package */ + case 0x0025010A: /* Prince of Persia: The Forgotten Sands (PS3/X360)-atomic-forge */ + case 0x00250119: /* Shaun White Skateboarding (Wii)-package */ + case 0x0025011D: /* Shaun White Skateboarding (PS3)-atomic-forge */ + config_bao_entry(bao, 0xB4, 0x28); - bao->cfg.audio_stream_size = 0x08; - bao->cfg.audio_stream_id = 0x24; - bao->cfg.audio_external_flag = 0x30; - bao->cfg.audio_loop_flag = 0x38; - bao->cfg.audio_channels = 0x48; - bao->cfg.audio_sample_rate = 0x50; - bao->cfg.audio_num_samples = 0x58; - bao->cfg.audio_num_samples2 = 0x60; - bao->cfg.audio_stream_type = 0x68; - bao->cfg.audio_prefetch_size = 0x78; - bao->cfg.audio_xma_offset = 0x8c; + config_bao_audio_b(bao, 0x08, 0x24, 0x2c, 0x38, 1, 1); + config_bao_audio_m(bao, 0x48, 0x50, 0x58, 0x60, 0x68, 0x78); - config_bao_layer_h2(bao, 0x28, 0x48, 0x50, 0x54); - config_bao_layer_s2(bao, 0x30, 0x00, 0x04, 0x08, 0x14, 0x2c); + config_bao_sequence(bao, 0x34, 0x28, 0x24, 0x14); + + config_bao_layer_m(bao, 0x00, 0x28, 0x30, 0x48, 0x50, 0x54, 0x58, 0x5c, 1); /* 0x24: id-like */ + config_bao_layer_e(bao, 0x30, 0x00, 0x04, 0x08, 0x18); + //todo some SPvsTW layers look like should loop (0x30 flag?) bao->cfg.codec_map[0x01] = RAW_PCM; - bao->cfg.codec_map[0x02] = UBI_IMA; /* assumed */ - bao->cfg.codec_map[0x03] = FMT_OGG; /* assumed */ - bao->cfg.codec_map[0x04] = RAW_XMA2; + bao->cfg.codec_map[0x02] = UBI_IMA; + bao->cfg.codec_map[0x03] = FMT_OGG; + bao->cfg.codec_map[0x04] = RAW_XMA2_NEW; bao->cfg.codec_map[0x05] = RAW_PSX; bao->cfg.codec_map[0x06] = RAW_AT3; + if (bao->version == 0x0025010A) /* no apparent flag */ + bao->cfg.codec_map[0x06] = RAW_AT3_105; + bao->cfg.file_type = UBI_FORGE_b; return 1; - case 0x001B0200: /* Beowulf (PS3)-file */ - case 0x001C0000: /* Lost: Via Domus (PS3)-file */ - case 0x001D0A00: /* Shaun White Snowboarding (PSP)-file? */ - case 0x001F0010: /* Prince of Persia 2008 (PS3/X360)-file, Far Cry 2 (PS3)-file */ - case 0x00220018: /* Avatar (PS3)-file/spk */ - case 0x00260102: /* Prince of Persia Trilogy HD (PS3)-pk */ - case 0x00280306: /* Far Cry 3: Blood Dragon (X360)-file */ - case 0x00290106: /* Splinter Cell: Blacklist (PS3)-file */ + case 0x001B0200: /* Beowulf (PS3)-atomic-bin+fat */ + /* same as 0x001B0100 except: + * - base 0xA0, skip 0x24, name style %08x (.bao/sbao?) */ + case 0x001C0000: /* Lost: Via Domus (PS3)-atomic-gear */ + /* same as 0x001B0100 except: + * - base 0xA0, skip 0x24, name style %08x.bao (not .sbao?) */ + case 0x001D0A00: /* Shaun White Snowboarding (PSP)-atomic-opal */ + case 0x00220018: /* Avatar (PS3)-atomic/spk */ + case 0x00260102: /* Prince of Persia Trilogy HD (PS3)-package-gear */ + /* similar to 0x00250108 but most values are moved +4 + * - base 0xB8, skip 0x28 */ + case 0x00280306: /* Far Cry 3: Blood Dragon (X360)-atomic-hashed */ + case 0x00290106: /* Splinter Cell: Blacklist (PS3)-atomic-gear */ + /* quite different, lots of flags and random values + * - base varies per type (0xF0=audio), skip 0x20 + * - 0x74: type, 0x78: channels, 0x7c: sample rate, 0x80: num_samples + * - 0x94: stream id? 0x9C: extra size */ default: /* others possibly using BAO: Just Dance, Watch_Dogs, Far Cry Primal, Far Cry 4 */ VGM_LOG("UBI BAO: unknown BAO version %08x\n", bao->version); return 0;