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,