diff --git a/src/coding/coding.h b/src/coding/coding.h index e132f791..d9c237e8 100644 --- a/src/coding/coding.h +++ b/src/coding/coding.h @@ -48,7 +48,7 @@ size_t ms_ima_bytes_to_samples(size_t bytes, int block_align, int channels); size_t xbox_ima_bytes_to_samples(size_t bytes, int channels); size_t dat4_ima_bytes_to_samples(size_t bytes, int channels); size_t apple_ima4_bytes_to_samples(size_t bytes, int channels); - +int xbox_check_format(STREAMFILE* sf, uint32_t offset, uint32_t max, int channels); /* ngc_dsp_decoder */ void decode_ngc_dsp(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); diff --git a/src/coding/ima_decoder.c b/src/coding/ima_decoder.c index 14f14a1d..a6ec41ac 100644 --- a/src/coding/ima_decoder.c +++ b/src/coding/ima_decoder.c @@ -1326,3 +1326,27 @@ size_t apple_ima4_bytes_to_samples(size_t bytes, int channels) { return (bytes / block_align) * (block_align - 0x02*channels) * 2 / channels + ((bytes % block_align) ? ((bytes % block_align) - 0x02*channels) * 2 / channels : 0); } + + +/* test XBOX-ADPCM frames for correctness */ +int xbox_check_format(STREAMFILE* sf, uint32_t offset, uint32_t max, int channels) { + off_t max_offset = offset + max; + int ch; + + if (max_offset > get_streamfile_size(sf)) + max_offset = get_streamfile_size(sf); + if (!channels) + return 0; + + while (offset < max_offset) { + for (ch = 0; ch < channels; ch++) { + uint16_t step = read_u16le(offset + 0x04 * ch + 0x02,sf); + if (step > 88) + return 0; + } + + offset += 0x24 * channels; + } + + return 1; +} diff --git a/src/meta/hca_keys.h b/src/meta/hca_keys.h index 76585d82..1582d0a6 100644 --- a/src/meta/hca_keys.h +++ b/src/meta/hca_keys.h @@ -882,6 +882,9 @@ static const hcakey_info hcakey_list[] = { // Shaman King: Funbari Chronicle (Android) {1620612098671}, // 0000017954022A6F + // Heaven Burns Red (Android) + {7355875239102698567}, // 6615518E8ECED447 + }; #endif/*_HCA_KEYS_H_*/ diff --git a/src/meta/musx.c b/src/meta/musx.c index 441d4ec4..0e2623ce 100644 --- a/src/meta/musx.c +++ b/src/meta/musx.c @@ -1,5 +1,6 @@ #include "meta.h" #include "../coding/coding.h" +#include "../util/endianness.h" typedef enum { MFX, MFX_BANK, SFX_BANK, SBNK, FORM } musx_form; typedef enum { PSX, DSP, XBOX, IMA, DAT, NGCA, PCM } musx_codec; @@ -187,16 +188,24 @@ static int parse_musx_stream(STREAMFILE* sf, musx_header* musx) { musx->platform = get_id32be("GC02"); /* (fake) */ } else { + int channels = musx->channels; off_t offset = musx->stream_offset; size_t max = 0x5000; if (max > musx->stream_size) max = musx->stream_size; + if (!channels) + channels = 2; + /* since engine seems to hardcode codecs no apparent way to detect in some cases + * [Sphinx and the Cursed Mummy (multi), Buffy the Vampire Slayer: Chaos Bleeds (multi)] */ if (ps_check_format(sf, offset, max)) { musx->platform = get_id32be("PS2_"); - } else { + } else if (xbox_check_format(sf, offset, max, channels)) { musx->platform = get_id32be("XB02"); /* (fake) */ } + else { + musx->platform = get_id32be("PC02"); /* (fake) */ + } } } @@ -267,6 +276,12 @@ static int parse_musx_stream(STREAMFILE* sf, musx_header* musx) { musx->codec = DAT; break; + case 0x50433032: /* "PC02" */ + default_channels = 2; + default_sample_rate = 32000; + musx->codec = IMA; + break; + default: VGM_LOG("MUSX: unknown platform %x\n", musx->platform); goto fail; @@ -307,7 +322,7 @@ static int parse_musx_stream(STREAMFILE* sf, musx_header* musx) { } /* other types (0x0a, 0x09) look like section/end markers, 0x06/07 only seems to exist once */ - if (type == 0x06 || type == 0x07) { + if (type == 0x06 || type == 0x07) { /* loop / goto */ musx->loop_start = offset2; musx->loop_end = offset1; musx->loop_flag = 1; @@ -361,6 +376,8 @@ fail: return 0; } +//TODO: check possible info here: +// https://sphinxandthecursedmummy.fandom.com/wiki/SFX static int parse_musx(STREAMFILE* sf, musx_header* musx) { int32_t (*read_s32)(off_t,STREAMFILE*) = NULL; uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL; @@ -378,7 +395,7 @@ static int parse_musx(STREAMFILE* sf, musx_header* musx) { case 1: /* Athens 2004 (PS2) */ musx->platform = 0; /* guess later */ musx->tables_offset = 0x10; - musx->big_endian = guess_endianness32bit(0x10, sf); + musx->big_endian = guess_endian32(0x10, sf); musx->is_old = 1; break; @@ -670,7 +687,7 @@ static int parse_musx(STREAMFILE* sf, musx_header* musx) { data_offset = read_u32(musx->tables_offset+0x3c, sf); target_offset = head_offset + (target_subsong - 1) * 0x1c; - ;VGM_LOG("MUSX: ho=%lx, do=%lx, to=%lx\n", head_offset, data_offset, target_offset); + //;VGM_LOG("MUSX: ho=%lx, do=%lx, to=%lx\n", head_offset, data_offset, target_offset); /* 0x00: subfile ID */ musx->num_samples = read_s32(target_offset + 0x04, sf); diff --git a/src/meta/ps2_mic.c b/src/meta/ps2_mic.c index 84bbfff4..d403e7fe 100644 --- a/src/meta/ps2_mic.c +++ b/src/meta/ps2_mic.c @@ -18,12 +18,12 @@ VGMSTREAM* init_vgmstream_mic_koei(STREAMFILE* sf) { if (start_offset != 0x800) goto fail; sample_rate = read_u32le(0x04,sf); channels = read_u32le(0x08,sf); - if (channels > 2) goto fail; + if (channels > 4) goto fail; /* 1/2/4 are known */ interleave = read_u32le(0x0c,sf); if (interleave != 0x10) goto fail; - loop_end = read_32bitLE(0x10,sf); - loop_start = read_32bitLE(0x14,sf); + loop_end = read_s32le(0x10,sf); + loop_start = read_s32le(0x14,sf); if (read_u32le(0x18,sf) != 0) goto fail; if (read_u32le(0x1c,sf) != 0) goto fail;