diff --git a/src/formats.c b/src/formats.c
index 33241716..8ca3e58c 100644
--- a/src/formats.c
+++ b/src/formats.c
@@ -499,6 +499,7 @@ static const char* extension_list[] = {
"scd",
"sch",
"sd9",
+ "sdd",
"sdl",
"sdp", //txth/reserved [Metal Gear Arcade (AC)]
"sdf",
@@ -1446,6 +1447,7 @@ static const meta_info meta_info_list[] = {
{meta_EA_SBK, "Electronic Arts SBK header"},
{meta_DSP_ASURA, "Rebellion DSP header"},
{meta_ONGAKUKAN_RIFF_ADP, "Ongakukan RIFF WAVE header"},
+ {meta_SDD, "Doki Denki DSBH header"},
};
void get_vgmstream_coding_description(VGMSTREAM* vgmstream, char* out, size_t out_size) {
diff --git a/src/libvgmstream.vcxproj b/src/libvgmstream.vcxproj
index 7089e3cf..5fbf4e3a 100644
--- a/src/libvgmstream.vcxproj
+++ b/src/libvgmstream.vcxproj
@@ -667,6 +667,7 @@
+
diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters
index 2ccbc970..4f79a75d 100644
--- a/src/libvgmstream.vcxproj.filters
+++ b/src/libvgmstream.vcxproj.filters
@@ -1831,6 +1831,9 @@
meta\Source Files
+
+ meta\Source Files
+
meta\Source Files
diff --git a/src/meta/meta.h b/src/meta/meta.h
index 14467bea..71593e20 100644
--- a/src/meta/meta.h
+++ b/src/meta/meta.h
@@ -1011,4 +1011,6 @@ VGMSTREAM* init_vgmstream_dsp_asura_sfx(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_adp_ongakukan(STREAMFILE* sf);
+VGMSTREAM* init_vgmstream_sdd(STREAMFILE* sf);
+
#endif /*_META_H*/
diff --git a/src/meta/sdd.c b/src/meta/sdd.c
new file mode 100644
index 00000000..e282f845
--- /dev/null
+++ b/src/meta/sdd.c
@@ -0,0 +1,122 @@
+#include "meta.h"
+#include "../coding/coding.h"
+#include "../util/chunks.h"
+
+/* .SDD - from Piglet's Big Game (PS2/GC) */
+VGMSTREAM* init_vgmstream_sdd(STREAMFILE* sf) {
+ VGMSTREAM* vgmstream = NULL;
+ uint32_t header_size, data_size, sample_rate, sound_offset, sound_size;
+ uint8_t codec, channels;
+ off_t table_offset, data_offset, entry_offset, name_offset;
+ size_t name_size;
+ int target_subsong = sf->stream_index, total_subsongs, loop_flag;
+
+ if (!is_id32be(0x00, sf, "DSBH"))
+ goto fail;
+
+ if (!check_extensions(sf, "sdd"))
+ goto fail;
+
+ /* always little endian, even on GC */
+ header_size = read_u32le(0x04, sf);
+ table_offset = 0x20;
+
+ /* haven't seen any filenames larger than 16 bytes so should be safe */
+ total_subsongs = (header_size - table_offset) / 0x20;
+
+ if (target_subsong == 0) target_subsong = 1;
+ if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1)
+ goto fail;
+
+ /* get name buffer size */
+ name_offset = table_offset + (target_subsong - 1) * 0x20;
+ name_size = read_string(NULL, STREAM_NAME_SIZE, name_offset, sf) + 1;
+
+ entry_offset = name_offset + name_size;
+ codec = read_u8(entry_offset + 0x00, sf);
+ //bps = read_u8(entry_offset + 0x01, sf);
+ channels = read_u8(entry_offset + 0x02, sf);
+ sample_rate = read_u32le(entry_offset + 0x03, sf);
+ sound_offset = read_u32le(entry_offset + 0x07, sf);
+ sound_size = read_u32le(entry_offset + 0x0b, sf);
+
+ /* no stereo samples seen */
+ if (channels > 1)
+ goto fail;
+
+ data_offset = header_size;
+ if (!is_id32be(data_offset, sf, "DSBD"))
+ goto fail;
+
+ data_size = read_u32le(data_offset + 0x04, sf);
+ if (data_offset + data_size > get_streamfile_size(sf))
+ goto fail;
+
+ sound_offset += data_offset + 0x20;
+ loop_flag = 0;
+
+ /* build the VGMSTREAM */
+ vgmstream = allocate_vgmstream(channels, loop_flag);
+ if (!vgmstream) goto fail;
+
+ vgmstream->meta_type = meta_SDD;
+ vgmstream->sample_rate = sample_rate;
+ vgmstream->stream_size = sound_size;
+ vgmstream->num_streams = total_subsongs;
+ read_string(vgmstream->stream_name, STREAM_NAME_SIZE, name_offset, sf);
+
+ switch (codec) {
+ case 0x01: /* DSP */
+ /* starts with incomplete DSP header (nibble count not set) */
+ if (sound_size < 0x60)
+ goto fail;
+
+ vgmstream->coding_type = coding_NGC_DSP;
+ vgmstream->layout_type = layout_none;
+ vgmstream->num_samples = read_u32be(sound_offset + 0x00, sf);
+
+ /* set coefs and initial history */
+ dsp_read_coefs_be(vgmstream, sf, sound_offset + 0x1c, 0x00);
+ vgmstream->ch[0].adpcm_history1_16 = read_u16be(sound_offset + 0x40, sf);
+ vgmstream->ch[0].adpcm_history2_16 = read_u16be(sound_offset + 0x42, sf);
+
+ sound_offset += 0x60;
+ vgmstream->stream_size -= 0x60;
+ break;
+ case 0x02: { /* PCM */
+ off_t chunk_offset;
+ size_t chunk_size;
+
+ /* stored as RIFF */
+ sound_offset += 0x0c;
+ sound_size -= 0x0c;
+
+ /* find "data" chunk */
+ if (!find_chunk_riff_le(sf, 0x64617461, sound_offset, sound_size, &chunk_offset, &chunk_size))
+ goto fail;
+
+ vgmstream->coding_type = coding_PCM16LE;
+ vgmstream->layout_type = layout_none;
+ vgmstream->num_samples = pcm16_bytes_to_samples(chunk_size, channels);
+
+ sound_offset = chunk_offset;
+ vgmstream->stream_size = chunk_size;
+ break;
+ }
+ case 0x03: /* PSX */
+ vgmstream->coding_type = coding_PSX;
+ vgmstream->layout_type = layout_none;
+ vgmstream->num_samples = ps_bytes_to_samples(sound_size, channels);
+ break;
+ default:
+ goto fail;
+ }
+
+ if (!vgmstream_open_stream(vgmstream, sf, sound_offset))
+ goto fail;
+ return vgmstream;
+
+fail:
+ close_vgmstream(vgmstream);
+ return NULL;
+}
diff --git a/src/meta/ubi_apm.c b/src/meta/ubi_apm.c
index a0967da6..a5bb4a75 100644
--- a/src/meta/ubi_apm.c
+++ b/src/meta/ubi_apm.c
@@ -3,83 +3,83 @@
/* .APM - seen in old Ubisoft games [Rayman 2: The Great Escape (PC), Donald Duck: Goin' Quackers (PC)] */
VGMSTREAM* init_vgmstream_ubi_apm(STREAMFILE* sf) {
- VGMSTREAM* vgmstream = NULL;
- uint32_t channels, sample_rate, file_size, nibble_size;
- off_t start_offset;
- int loop_flag;
- uint32_t i;
+ VGMSTREAM* vgmstream = NULL;
+ uint32_t channels, sample_rate, file_size, nibble_size;
+ off_t start_offset;
+ int loop_flag;
+ uint32_t i;
- if (read_u16le(0x00, sf) != 0x2000 || !is_id32be(0x14, sf, "vs12"))
- goto fail;
+ if (read_u16le(0x00, sf) != 0x2000 || !is_id32be(0x14, sf, "vs12"))
+ goto fail;
- if (!check_extensions(sf, "apm"))
- goto fail;
+ if (!check_extensions(sf, "apm"))
+ goto fail;
- /* (info from https://github.com/Synthesis/ray2get)
- * 0x00(2): format tag (0x2000 for Ubisoft ADPCM)
- * 0x02(2): channels
- * 0x04(4): sample rate
- * 0x08(4): byte rate? PCM samples?
- * 0x0C(2): block align
- * 0x0E(2): bits per sample
- * 0x10(4): header size
- * 0x14(4): "vs12"
- * 0x18(4): file size
- * 0x1C(4): nibble size
- * 0x20(4): -1?
- * 0x24(4): 0?
- * 0x28(4): high/low nibble flag (when loaded in memory)
- * 0x2C(N): ADPCM info per channel, last to first
- * - 0x00(4): ADPCM hist
- * - 0x04(4): ADPCM step index
- * - 0x08(4): copy of ADPCM data (after interleave, ex. R from data + 0x01)
- * 0x60(4): "DATA"
- * 0x64(N): ADPCM data
- */
+ /* (info from https://github.com/Synthesis/ray2get)
+ * 0x00(2): format tag (0x2000 for Ubisoft ADPCM)
+ * 0x02(2): channels
+ * 0x04(4): sample rate
+ * 0x08(4): byte rate? PCM samples?
+ * 0x0C(2): block align
+ * 0x0E(2): bits per sample
+ * 0x10(4): header size
+ * 0x14(4): "vs12"
+ * 0x18(4): file size
+ * 0x1C(4): nibble size
+ * 0x20(4): -1?
+ * 0x24(4): 0?
+ * 0x28(4): high/low nibble flag (when loaded in memory)
+ * 0x2C(N): ADPCM info per channel, last to first
+ * - 0x00(4): ADPCM hist
+ * - 0x04(4): ADPCM step index
+ * - 0x08(4): copy of ADPCM data (after interleave, ex. R from data + 0x01)
+ * 0x60(4): "DATA"
+ * 0x64(N): ADPCM data
+ */
- channels = read_u16le(0x02, sf);
- sample_rate = read_u32le(0x04, sf);
- file_size = read_u32le(0x18, sf);
- nibble_size = read_u32le(0x1c, sf);
+ channels = read_u16le(0x02, sf);
+ sample_rate = read_u32le(0x04, sf);
+ file_size = read_u32le(0x18, sf);
+ nibble_size = read_u32le(0x1c, sf);
- start_offset = 0x64;
+ start_offset = 0x64;
- if (file_size != get_streamfile_size(sf))
- goto fail;
+ if (file_size != get_streamfile_size(sf))
+ goto fail;
- if (nibble_size > (file_size - start_offset))
- goto fail;
+ if (nibble_size > (file_size - start_offset))
+ goto fail;
- if (!is_id32be(0x60, sf, "DATA"))
- goto fail;
+ if (!is_id32be(0x60, sf, "DATA"))
+ goto fail;
- loop_flag = 0;
+ loop_flag = 0;
- /* build the VGMSTREAM */
- vgmstream = allocate_vgmstream(channels, loop_flag);
- if (!vgmstream) goto fail;
+ /* build the VGMSTREAM */
+ vgmstream = allocate_vgmstream(channels, loop_flag);
+ if (!vgmstream) goto fail;
- vgmstream->meta_type = meta_UBI_APM;
- vgmstream->coding_type = coding_DVI_IMA_int;
- vgmstream->layout_type = layout_interleave;
- vgmstream->interleave_block_size = 0x01;
- vgmstream->sample_rate = sample_rate;
- vgmstream->num_samples = ima_bytes_to_samples(file_size - start_offset, channels);
+ vgmstream->meta_type = meta_UBI_APM;
+ vgmstream->coding_type = coding_DVI_IMA_int;
+ vgmstream->layout_type = layout_interleave;
+ vgmstream->interleave_block_size = 0x01;
+ vgmstream->sample_rate = sample_rate;
+ vgmstream->num_samples = ima_bytes_to_samples(file_size - start_offset, channels);
- /* read initial hist (last to first) */
- for (i = 0; i < channels; i++) {
- vgmstream->ch[i].adpcm_history1_32 = read_s32le(0x2c + 0x0c * (channels - 1 - i) + 0x00, sf);
- vgmstream->ch[i].adpcm_step_index = read_s32le(0x2c + 0x0c * (channels - 1 - i) + 0x04, sf);
- }
- //todo supposedly APM IMA removes lower 3b after assigning step, but wave looks a bit off (Rayman 2 only?):
- // ...; step = adpcm_table[step_index]; delta = (step >> 3); step &= (~7); ...
+ /* read initial hist (last to first) */
+ for (i = 0; i < channels; i++) {
+ vgmstream->ch[i].adpcm_history1_32 = read_s32le(0x2c + 0x0c * (channels - 1 - i) + 0x00, sf);
+ vgmstream->ch[i].adpcm_step_index = read_s32le(0x2c + 0x0c * (channels - 1 - i) + 0x04, sf);
+ }
+ //todo supposedly APM IMA removes lower 3b after assigning step, but wave looks a bit off (Rayman 2 only?):
+ // ...; step = adpcm_table[step_index]; delta = (step >> 3); step &= (~7); ...
- if (!vgmstream_open_stream(vgmstream, sf, start_offset))
- goto fail;
+ if (!vgmstream_open_stream(vgmstream, sf, start_offset))
+ goto fail;
- return vgmstream;
+ return vgmstream;
fail:
- close_vgmstream(vgmstream);
- return NULL;
+ close_vgmstream(vgmstream);
+ return NULL;
}
diff --git a/src/vgmstream_init.c b/src/vgmstream_init.c
index e8c4a5ab..ceef04b0 100644
--- a/src/vgmstream_init.c
+++ b/src/vgmstream_init.c
@@ -509,6 +509,7 @@ init_vgmstream_t init_vgmstream_functions[] = {
init_vgmstream_dsp_asura_ttss,
init_vgmstream_dsp_asura_sfx,
init_vgmstream_adp_ongakukan,
+ init_vgmstream_sdd,
/* lower priority metas (no clean header identity, somewhat ambiguous, or need extension/companion file to identify) */
init_vgmstream_agsc,
diff --git a/src/vgmstream_types.h b/src/vgmstream_types.h
index eeb21c14..96eb0586 100644
--- a/src/vgmstream_types.h
+++ b/src/vgmstream_types.h
@@ -708,6 +708,7 @@ typedef enum {
meta_EA_SBK,
meta_DSP_ASURA,
meta_ONGAKUKAN_RIFF_ADP,
+ meta_SDD,
} meta_t;