diff --git a/src/coding/coding.h b/src/coding/coding.h index 1b3d52b1..1ef00f5b 100644 --- a/src/coding/coding.h +++ b/src/coding/coding.h @@ -265,13 +265,12 @@ size_t ptadpcm_bytes_to_samples(size_t bytes, int channels, size_t frame_size); /* ubi_adpcm_decoder */ typedef struct ubi_adpcm_codec_data ubi_adpcm_codec_data; -ubi_adpcm_codec_data* init_ubi_adpcm(STREAMFILE* sf, off_t offset, int channels); +ubi_adpcm_codec_data* init_ubi_adpcm(STREAMFILE* sf, uint32_t offset, uint32_t size, int channels); void decode_ubi_adpcm(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do); void reset_ubi_adpcm(ubi_adpcm_codec_data* data); void seek_ubi_adpcm(ubi_adpcm_codec_data* data, int32_t num_sample); void free_ubi_adpcm(ubi_adpcm_codec_data* data); int32_t ubi_adpcm_get_samples(ubi_adpcm_codec_data* data); -int32_t ubi_adpcm_bytes_to_samples(ubi_adpcm_codec_data* data, uint32_t size); /* imuse_decoder */ diff --git a/src/coding/ubi_adpcm_decoder.c b/src/coding/ubi_adpcm_decoder.c index 948f039d..729ff524 100644 --- a/src/coding/ubi_adpcm_decoder.c +++ b/src/coding/ubi_adpcm_decoder.c @@ -71,8 +71,8 @@ struct ubi_adpcm_codec_data { ubi_adpcm_header_data header; ubi_adpcm_channel_data ch[UBI_CHANNELS_MAX]; - off_t start_offset; - off_t offset; + uint32_t start_offset; + uint32_t offset; int subframe_number; uint8_t frame[UBI_FRAME_SIZE_MAX]; @@ -86,16 +86,17 @@ struct ubi_adpcm_codec_data { /* *********************************************************************** */ -static int parse_header(STREAMFILE* sf, ubi_adpcm_codec_data* data, off_t offset); +static int parse_header(STREAMFILE* sf, ubi_adpcm_codec_data* data, uint32_t offset, uint32_t size); static void decode_frame(STREAMFILE* sf, ubi_adpcm_codec_data* data); +static void fix_samples(ubi_adpcm_codec_data* data, uint32_t size); -ubi_adpcm_codec_data* init_ubi_adpcm(STREAMFILE* sf, off_t offset, int channels) { +ubi_adpcm_codec_data* init_ubi_adpcm(STREAMFILE* sf, uint32_t offset, uint32_t size, int channels) { ubi_adpcm_codec_data* data = NULL; data = calloc(1, sizeof(ubi_adpcm_codec_data)); if (!data) goto fail; - if (!parse_header(sf, data, offset)) { + if (!parse_header(sf, data, offset, size)) { VGM_LOG("UBI ADPCM: wrong header\n"); goto fail; } @@ -193,7 +194,7 @@ static void read_header_state(uint8_t* data, ubi_adpcm_header_data* header) { header->channels = get_u32le(data + 0x2c); } -static int parse_header(STREAMFILE* sf, ubi_adpcm_codec_data* data, off_t offset) { +static int parse_header(STREAMFILE* sf, ubi_adpcm_codec_data* data, uint32_t offset, uint32_t size) { uint8_t buf[0x30]; size_t bytes; @@ -214,6 +215,11 @@ static int parse_header(STREAMFILE* sf, ubi_adpcm_codec_data* data, off_t offset if (data->header.channels > UBI_CHANNELS_MAX || data->header.channels < UBI_CHANNELS_MIN) goto fail; + /* some kind of internal bug I guess, seen in a few subsongs in Rayman 3 PC demo */ + if (data->header.sample_count == 0x77E7A374 * data->header.channels) { + fix_samples(data, size); + } + return 1; fail: return 0; @@ -548,8 +554,8 @@ static void decode_frame(STREAMFILE* sf, ubi_adpcm_codec_data* data) { bytes = read_streamfile(data->frame, data->offset, frame_size, sf); if (bytes != frame_size) { - VGM_LOG("UBI ADPCM: wrong bytes read %x vs %x at %lx\n", bytes, frame_size, data->offset); - //goto fail; //? + VGM_LOG("UBI ADPCM: wrong bytes read %x vs %x at %x\n", bytes, frame_size, data->offset); + //goto fail; //may reach EOF earlier } if (channels == 1) { @@ -587,21 +593,37 @@ int32_t ubi_adpcm_get_samples(ubi_adpcm_codec_data* data) { return data->header.sample_count / data->header.channels; } -int32_t ubi_adpcm_bytes_to_samples(ubi_adpcm_codec_data* data, uint32_t size) { - uint32_t frame_size; +static void fix_samples(ubi_adpcm_codec_data* data, uint32_t size) { + uint32_t frame_size, setup_size, subframe_size, base_frames, last_size; + int subframes; + int32_t samples; - if (!data || !data->header.channels || !data->header.subframes_per_frame) - return 0; + if (!data || !data->header.channels || !data->header.subframes_per_frame || !size) + return; + + size -= 0x30; /* ignore header */ + + setup_size = 0x34 * data->header.channels; /* setup per channel */ + subframe_size = (data->header.codes_per_subframe * data->header.bits_per_sample /*+ 8*/) / 8 + 0x01; /* padding byte */ + frame_size = setup_size + subframe_size * data->header.subframes_per_frame; /* don't trust subframe count */ + base_frames = ((size - 0x01) / frame_size); /* force smaller size just in case so last frame isn't used */ + last_size = size - (base_frames * frame_size); + subframes = base_frames * data->header.subframes_per_frame; - size -= 0x30; /* header */ + samples = base_frames * (data->header.codes_per_subframe * data->header.subframes_per_frame); - frame_size = 0x34 * data->header.channels; /* setup per channel */ - frame_size += (data->header.codes_per_subframe * data->header.bits_per_sample /*+ 8*/) * data->header.subframes_per_frame / 8; - frame_size += data->header.subframes_per_frame * 0x01; /* padding byte */ + /* last subframe is shorter (and may contain padding after codes_per_subframe_last), and last frame may not contain all subframes */ + if (last_size > setup_size + subframe_size) { + samples += data->header.codes_per_subframe * (data->header.subframes_per_frame - 1); + subframes += (data->header.subframes_per_frame - 1); + } - return ((size - 0x01) / frame_size) * /* force smaller size so last frame isn't used */ - data->header.codes_per_subframe * data->header.subframes_per_frame + - data->header.codes_per_subframe_last * data->header.subframes_per_frame; + /* for some reason several files that need fixing seem to have garbage in the 2nd half of last codes */ + samples += data->header.codes_per_subframe_last / 2; + subframes += 1; + + data->header.sample_count = samples; /* for all channels */ + data->header.subframe_count = subframes; } diff --git a/src/meta/hca_keys.h b/src/meta/hca_keys.h index b456a86f..834965ee 100644 --- a/src/meta/hca_keys.h +++ b/src/meta/hca_keys.h @@ -952,6 +952,9 @@ static const hcakey_info hcakey_list[] = { // Priconne! Grand Masters (iOS/Android) {185705658241}, // 0000002B3CEB7781 + // Iris Mysteria (Android) + {62049655719861786}, // 00DC71D5479E1E1A + }; #endif/*_HCA_KEYS_H_*/ diff --git a/src/meta/p3d.c b/src/meta/p3d.c index 2e3895f9..0a7bb263 100644 --- a/src/meta/p3d.c +++ b/src/meta/p3d.c @@ -1,15 +1,17 @@ #include "meta.h" #include "../coding/coding.h" +#include "../util/endianness.h" + /* P3D - from Radical's Prototype 1/2 (PC/PS3/X360), Spider-Man 4 Beta (X360) */ VGMSTREAM* init_vgmstream_p3d(STREAMFILE* sf) { VGMSTREAM* vgmstream = NULL; - off_t start_offset, offset, name_offset = 0; + uint32_t start_offset, offset, name_offset = 0; size_t header_size, file_size, data_size; uint32_t xma2_offset = 0, xma2_size = 0; int loop_flag, channels, sample_rate, codec; - int i, name_count, text_len, block_size = 0, num_samples; - uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL; + int i, name_count, unk_count, text_len, block_size = 0, num_samples; + read_u32_t read_u32; /* checks */ @@ -19,30 +21,36 @@ VGMSTREAM* init_vgmstream_p3d(STREAMFILE* sf) { if (!check_extensions(sf,"p3d")) goto fail; - read_u32 = guess_endianness32bit(0x04,sf) ? read_u32be : read_u32le; + read_u32 = guess_endian32(0x04,sf) ? read_u32be : read_u32le; file_size = get_streamfile_size(sf); /* base header */ header_size = read_u32(0x04,sf); - if (header_size != 0x0C) goto fail; + if (header_size != 0x0c) goto fail; if (read_u32(0x08,sf) != file_size) goto fail; - if (read_u32(0x0C,sf) != 0xFE000000) goto fail; /* fixed */ - if (read_u32(0x10,sf) + header_size != file_size) goto fail; - if (read_u32(0x14,sf) + header_size != file_size) goto fail; /* body size again */ - if (read_u32(0x18,sf) != 0x0000000A) goto fail; /* fixed */ + offset = 0x0c; - /* header text */ - offset = 0x1C; - text_len = read_u32(offset,sf); + /* P3D is just a generic container used in Radical's games, so we only want "AudioFile". + * Rarely some voice files start with a "AudioDialogueSubtitle" section, so skip that first */ + if (is_id64be(offset+0x14,sf, "AudioDia")) { + offset += read_u32(offset + 0x04,sf); /* section size */ + } + + /* AudioFile section */ + if (read_u32(offset + 0x00,sf) != 0xFE000000) goto fail; /* section marker */ + if (read_u32(offset + 0x04,sf) + offset != file_size) goto fail; /* AudioFile size */ + if (read_u32(offset + 0x08,sf) + offset != file_size) goto fail; /* again */ + if (read_u32(offset + 0x0c,sf) != 0x0000000A) goto fail; /* fixed */ + + text_len = read_u32(offset + 0x10,sf); if (text_len != 9) goto fail; - offset += 0x04; + offset += 0x14; - /* check the type as P3D is just a generic container used in Radical's games */ if (!is_id64be(offset+0x00,sf, "AudioFil") || read_u16be(offset+0x08,sf) != 0x6500) /* "AudioFile\0" */ goto fail; offset += text_len + 0x01; - /* file names: always 2 (repeated); but if it's 3 there is an extra string later */ + /* file names: 1 internal stream name + 1 external filename (usually repeated) + 1 an extra string later (P2) */ name_count = read_u32(offset,sf); if (name_count != 2 && name_count != 3) goto fail; /* 2: Prototype1, 3: Prototype2 */ offset += 0x04; @@ -55,8 +63,9 @@ VGMSTREAM* init_vgmstream_p3d(STREAMFILE* sf) { offset += 0x04 + text_len; } - /* info count? */ - if (0x01 != read_u32(offset,sf)) goto fail; + /* 1=music, 0=dialogues */ + unk_count = read_u32(offset,sf); + if (unk_count != 0x00 && unk_count != 0x01) goto fail; offset += 0x04; /* next string can be used as a codec id */ @@ -64,8 +73,8 @@ VGMSTREAM* init_vgmstream_p3d(STREAMFILE* sf) { codec = read_u32be(offset+0x04,sf); offset += 0x04 + text_len + 0x01; - /* extra "Music" string in Prototype 2 */ - if (name_count == 3) { + /* extra "Music" or "Dialogue" string in Prototype 2 */ + if (name_count >= 3) { text_len = read_u32(offset,sf) + 1; /* null-terminated */ offset += 0x04 + text_len; } diff --git a/src/meta/ubi_hx.c b/src/meta/ubi_hx.c index 3b80d4b8..6dc4c5a1 100644 --- a/src/meta/ubi_hx.c +++ b/src/meta/ubi_hx.c @@ -705,18 +705,13 @@ static VGMSTREAM* init_vgmstream_ubi_hx_header(ubi_hx_header* hx, STREAMFILE* sf break; case UBI: - vgmstream->codec_data = init_ubi_adpcm(sb, hx->stream_offset, vgmstream->channels); + vgmstream->codec_data = init_ubi_adpcm(sb, hx->stream_offset, hx->stream_size, vgmstream->channels); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_UBI_ADPCM; vgmstream->layout_type = layout_none; vgmstream->num_samples = ubi_adpcm_get_samples(vgmstream->codec_data); - /* some kind of internal bug I guess, seen in a few subsongs in Rayman 3 PC demo, other values are also buggy */ - if (vgmstream->num_samples == 0x77E7A374) { - vgmstream->num_samples = ubi_adpcm_bytes_to_samples(vgmstream->codec_data, hx->stream_size); - } - /* XIII has 6-bit stereo music, Rayman 3 4-bit music, both use 6-bit mono) */ break; diff --git a/src/meta/ubi_sb.c b/src/meta/ubi_sb.c index b9782771..10bbdf39 100644 --- a/src/meta/ubi_sb.c +++ b/src/meta/ubi_sb.c @@ -1018,7 +1018,7 @@ static VGMSTREAM* init_vgmstream_ubi_sb_base(ubi_sb_header* sb, STREAMFILE* sf_h sb->stream_size -= 0x08; } - vgmstream->codec_data = init_ubi_adpcm(sf_data, start_offset, vgmstream->channels); + vgmstream->codec_data = init_ubi_adpcm(sf_data, start_offset, 0, vgmstream->channels); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_UBI_ADPCM; vgmstream->layout_type = layout_none;