diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index bcd6e6b3..9535c54b 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -194,6 +194,9 @@ meta\Header Files + + meta\Header Files + meta\Header Files @@ -1150,6 +1153,9 @@ meta\Source Files + + meta\Source Files + meta\Source Files diff --git a/src/meta/meta.h b/src/meta/meta.h index 0b22dc92..6ce3b167 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -971,4 +971,6 @@ VGMSTREAM* init_vgmstream_lpcm_fb(STREAMFILE* sf); VGMSTREAM* init_vgmstream_wbk(STREAMFILE* sf); VGMSTREAM* init_vgmstream_wbk_nslb(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_ubi_ckd_cwav(STREAMFILE* sf); + #endif /*_META_H*/ diff --git a/src/meta/riff.c b/src/meta/riff.c index 453c4b8f..2b0a5d23 100644 --- a/src/meta/riff.c +++ b/src/meta/riff.c @@ -355,13 +355,14 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) { * .rws: Climax ATRAC3 [Silent Hill Origins (PSP), Oblivion (PSP)] * .aud: EA Replay ATRAC3 * .at9: standard ATRAC9 + * .ckd: renamed ATRAC9 [Rayman Origins (Vita)] * .saf: Whacked! (Xbox) * .mwv: Level-5 games [Dragon Quest VIII (PS2), Rogue Galaxy (PS2)] * .ima: Baja: Edge of Control (PS3/X360) * .nsa: Studio Ring games that uses NScripter [Hajimete no Otetsudai (PC)] * .pcm: Silent Hill Arcade (PC) */ - if ( check_extensions(sf, "wav,lwav,xwav,da,dax,cd,med,snd,adx,adp,xss,xsew,adpcm,adw,wd,,sbv,wvx,str,at3,rws,aud,at9,saf,ima,nsa,pcm") ) { + if ( check_extensions(sf, "wav,lwav,xwav,da,dax,cd,med,snd,adx,adp,xss,xsew,adpcm,adw,wd,,sbv,wvx,str,at3,rws,aud,at9,ckd,saf,ima,nsa,pcm") ) { ; } else if ( check_extensions(sf, "mwv") ) { @@ -590,6 +591,11 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) { JunkFound = 1; break; + + case 0x64737068: /* "dsph" */ + case 0x63776176: /* "cwav" */ + goto fail; /* parse elsewhere */ + default: /* ignorance is bliss */ break; @@ -625,6 +631,10 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) { read_u32be(start_offset+0x3c, sf) == 0xFFFFFFFF) goto fail; + ///* MSADPCM .ckd are parsed elsewhere, though they are valid so no big deal if parsed here (just that loops should be ignored) */ + if (!fmt.is_at9 && check_extensions(sf, "ckd")) + goto fail; + /* ignore Gitaroo Man Live! (PSP) multi-RIFF (to allow chunked TXTH) */ if (fmt.is_at3 && get_streamfile_size(sf) > 0x2800 && read_32bitBE(0x2800, sf) == 0x52494646) { /* "RIFF" */ goto fail; diff --git a/src/meta/ubi_ckd.c b/src/meta/ubi_ckd.c index 17776a56..baac19f1 100644 --- a/src/meta/ubi_ckd.c +++ b/src/meta/ubi_ckd.c @@ -1,5 +1,6 @@ #include "meta.h" #include "../coding/coding.h" +#include "../coding/coding.h" typedef enum { MSADPCM, DSP, MP3, XMA2 } ckd_codec; @@ -9,31 +10,36 @@ VGMSTREAM* init_vgmstream_ubi_ckd(STREAMFILE* sf) { VGMSTREAM* vgmstream = NULL; off_t start_offset, first_offset = 0x0c, chunk_offset; size_t chunk_size, data_size; - int loop_flag, channel_count, interleave = 0, format; + int loop_flag, channels, interleave = 0, format; ckd_codec codec; int big_endian; uint32_t (*read_u32)(off_t,STREAMFILE*); uint16_t (*read_u16)(off_t,STREAMFILE*); /* checks */ + if (!is_id32be(0x00,sf, "RIFF")) + goto fail; + if (!is_id32be(0x08,sf, "WAVE")) + goto fail; + /* .wav.ckd: main (other files are called .xxx.ckd too) */ if (!check_extensions(sf,"ckd")) goto fail; - /* another slighly funny RIFF */ - if (read_u32be(0x00,sf) != 0x52494646) /* "RIFF" */ + /* another slighly funny RIFF, mostly standard except machine endian and minor oddities */ + if (!is_id32be(0x0c,sf, "fmt ")) goto fail; - if (read_u32be(0x08,sf) != 0x57415645) /* "WAVE" */ - goto fail; - /* RIFF size is normal */ big_endian = guess_endianness32bit(0x04, sf); read_u32 = big_endian ? read_u32be : read_u32le; read_u16 = big_endian ? read_u16be : read_u16le; + if (read_u32(0x04, sf) + 0x04 + 0x04 != get_streamfile_size(sf)) + goto fail; + loop_flag = 0; format = read_u16(0x14,sf); - channel_count = read_u16(0x16,sf); + channels = read_u16(0x16,sf); switch(format) { case 0x0002: @@ -50,7 +56,7 @@ VGMSTREAM* init_vgmstream_ubi_ckd(STREAMFILE* sf) { } else if (find_chunk_be(sf, 0x6461744C,first_offset,0, &chunk_offset,&chunk_size)) { /* "datL" */ /* mono "datL" or full interleave with a "datR" after the "datL" (no check, pretend it exists) */ start_offset = chunk_offset; - data_size = chunk_size * channel_count; + data_size = chunk_size * channels; interleave = (0x4+0x4) + chunk_size; /* don't forget to skip the "datR"+size chunk */ } else { goto fail; @@ -107,11 +113,11 @@ VGMSTREAM* init_vgmstream_ubi_ckd(STREAMFILE* sf) { /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); + vgmstream = allocate_vgmstream(channels,loop_flag); if (!vgmstream) goto fail; vgmstream->sample_rate = read_u32(0x18,sf); - vgmstream->num_samples = dsp_bytes_to_samples(data_size, channel_count); + vgmstream->num_samples = dsp_bytes_to_samples(data_size, channels); vgmstream->coding_type = coding_NGC_DSP; vgmstream->meta_type = meta_UBI_CKD; @@ -170,4 +176,3 @@ fail: close_vgmstream(vgmstream); return NULL; } - diff --git a/src/meta/ubi_ckd_cwav.c b/src/meta/ubi_ckd_cwav.c new file mode 100644 index 00000000..f7f51af2 --- /dev/null +++ b/src/meta/ubi_ckd_cwav.c @@ -0,0 +1,37 @@ +#include "meta.h" +#include "../coding/coding.h" +#include "ubi_ckd_cwav_streamfile.h" + +/* CKD RIFF - UbiArt Framework (v1) audio container [Rayman Origins (3DS)] */ +VGMSTREAM* init_vgmstream_ubi_ckd_cwav(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; + + /* checks */ + if (!is_id32be(0x00,sf, "RIFF")) + goto fail; + if (!is_id32be(0x08,sf, "WAVE")) + goto fail; + + if (!(is_id32be(0x0c,sf, "dsph") || is_id32be(0x0c,sf, "cwav"))) + goto fail; + + /* .wav: main (unlike .wav.cdk of other versions) */ + if (!check_extensions(sf,"wav,lwav")) + goto fail; + + /* inside dsph (header+start, optional) and cwav (body, always) RIFF chunks is a full "CWAV", + * since dsph also contains some data just deblock */ + + temp_sf = setup_ubi_ckd_cwav_streamfile(sf); + if (!temp_sf) goto fail; + + vgmstream = init_vgmstream_rwsd(temp_sf); + if (!vgmstream) goto fail; + + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/src/meta/ubi_ckd_cwav_streamfile.h b/src/meta/ubi_ckd_cwav_streamfile.h new file mode 100644 index 00000000..e1a81b6c --- /dev/null +++ b/src/meta/ubi_ckd_cwav_streamfile.h @@ -0,0 +1,37 @@ +#ifndef _UBI_CKD_CWAV_STREAMFILE_H_ +#define _UBI_CKD_CWAV_STREAMFILE_H_ +#include "deblock_streamfile.h" + +static void block_callback(STREAMFILE *sf, deblock_io_data *data) { + uint32_t chunk_type = read_u32be(data->physical_offset + 0x00, sf); + uint32_t chunk_size = read_u32le(data->physical_offset + 0x04, sf); + + if (chunk_type == get_id32be("RIFF")) { + data->data_size = 0x0; + data->skip_size = 0x0; + data->block_size = 0x0c; + } + else { + data->data_size = chunk_size; + data->skip_size = 0x08; + data->block_size = data->data_size + data->skip_size; + } +} + +/* Deblocks CWAV streams inside RIFF */ +static STREAMFILE* setup_ubi_ckd_cwav_streamfile(STREAMFILE* sf) { + STREAMFILE *new_sf = NULL; + deblock_config_t cfg = {0}; + + cfg.stream_start = 0x00; + cfg.stream_size = get_streamfile_size(sf); + cfg.block_callback = block_callback; + + /* setup sf */ + new_sf = open_wrap_streamfile(sf); + new_sf = open_io_deblock_streamfile_f(new_sf, &cfg); + new_sf = open_fakename_streamfile_f(new_sf, NULL, "bcwav"); + return new_sf; +} + +#endif /* _UBI_CKD_CWAV_STREAMFILE_H_ */ diff --git a/src/vgmstream.c b/src/vgmstream.c index 4866a403..56391723 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -518,6 +518,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = { init_vgmstream_wbk, init_vgmstream_wbk_nslb, init_vgmstream_dsp_apex, + init_vgmstream_ubi_ckd_cwav, /* lower priority metas (no clean header identity, somewhat ambiguous, or need extension/companion file to identify) */ init_vgmstream_agsc,