diff --git a/src/formats.c b/src/formats.c
index d446b535..f2252cc3 100644
--- a/src/formats.c
+++ b/src/formats.c
@@ -425,6 +425,7 @@ static const char* extension_list[] = {
"sb5",
"sb6",
"sb7",
+ "sbk",
"sbr",
"sbv",
"sm0",
@@ -1319,6 +1320,7 @@ static const meta_info meta_info_list[] = {
{meta_WADY, "Marble WADY header"},
{meta_DSP_SQEX, "Square Enix DSP header"},
{meta_DSP_WIIVOICE, "Koei Tecmo WiiVoice header"},
+ {meta_SBK, "Team17 SBK 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 a63a593d..892c94f3 100644
--- a/src/libvgmstream.vcxproj
+++ b/src/libvgmstream.vcxproj
@@ -227,6 +227,7 @@
+
diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters
index 9d76bc4a..8dda843c 100644
--- a/src/libvgmstream.vcxproj.filters
+++ b/src/libvgmstream.vcxproj.filters
@@ -1834,5 +1834,8 @@
meta\Source Files
+
+ meta\Source Files
+
\ No newline at end of file
diff --git a/src/meta/meta.h b/src/meta/meta.h
index c19b9c13..31ee4039 100644
--- a/src/meta/meta.h
+++ b/src/meta/meta.h
@@ -928,4 +928,6 @@ VGMSTREAM* init_vgmstream_wady(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_cpk(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_cpk_memory(STREAMFILE* sf, STREAMFILE* sf_acb);
+VGMSTREAM *init_vgmstream_sbk(STREAMFILE *sf);
+
#endif /*_META_H*/
diff --git a/src/meta/sbk.c b/src/meta/sbk.c
new file mode 100644
index 00000000..b6dc82e6
--- /dev/null
+++ b/src/meta/sbk.c
@@ -0,0 +1,95 @@
+#include "meta.h"
+#include "../coding/coding.h"
+
+/* .SBK - from Addiction Pinball (PC) */
+VGMSTREAM *init_vgmstream_sbk(STREAMFILE *sf) {
+ VGMSTREAM *vgmstream = NULL;
+ uint32_t sound_offset, sound_size, padding_size, sample_rate;
+ uint16_t format, channels, block_size, bps;
+ off_t table_offset, data_offset, entry_offset, start_offset;
+ size_t table_size, data_size;
+ int target_subsong = sf->stream_index, total_subsongs, loop_flag;
+
+ /* checks */
+ if (!check_extensions(sf, "sbk"))
+ goto fail;
+
+ /* check header */
+ if (read_u32be(0x00, sf) != 0x52494646) /* "RIFF" */
+ goto fail;
+ if (read_u32be(0x08, sf) != 0x53424E4B) /* "SBNK" */
+ goto fail;
+
+ if (!find_chunk_le(sf, 0x57415649, 0x0c, 0, &table_offset, &table_size)) /* "WAVI" */
+ goto fail;
+
+ total_subsongs = table_size / 0x38;
+
+ if (target_subsong == 0) target_subsong = 1;
+ if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1)
+ goto fail;
+
+ entry_offset = table_offset + 0x38 * (target_subsong - 1);
+ sound_offset = read_u32le(entry_offset + 0x04, sf);
+ sound_size = read_u32le(entry_offset + 0x00, sf);
+ padding_size = read_u32le(entry_offset + 0x10, sf);
+ sound_offset += padding_size;
+ sound_size -= padding_size;
+
+ /* read fmt chunk */
+ format = read_u16le(entry_offset + 0x1c, sf);
+ channels = read_u16le(entry_offset + 0x1e, sf);
+ sample_rate = read_u32le(entry_offset + 0x20, sf);
+ block_size = read_u16le(entry_offset + 0x28, sf);
+ bps = read_u16le(entry_offset + 0x2a, sf);
+
+ loop_flag = 0;
+
+ /* build the VGMSTREAM */
+ vgmstream = allocate_vgmstream(channels, loop_flag);
+ if (!vgmstream) goto fail;
+
+ /* fill in the vital statistics */
+ vgmstream->meta_type = meta_SBK;
+ vgmstream->sample_rate = sample_rate;
+ vgmstream->stream_size = sound_size;
+ vgmstream->num_streams = total_subsongs;
+
+ switch (format) {
+ case 0x01: /* PCM */
+ if (bps != 8 && bps != 16)
+ goto fail;
+
+ vgmstream->coding_type = (bps == 8) ? coding_PCM8_U : coding_PCM16LE;
+ vgmstream->layout_type = layout_interleave;
+ vgmstream->interleave_block_size = (bps == 8) ? 0x01 : 0x02;
+ vgmstream->num_samples = pcm_bytes_to_samples(sound_size, channels, bps);
+
+ if (!find_chunk_le(sf, 0x57415644, 0x0c, 0, &data_offset, &data_size)) /* "WAVD" */
+ goto fail;
+
+ start_offset = data_offset + sound_offset;
+ break;
+ case 0x11: /* Microsoft IMA */
+ vgmstream->coding_type = coding_MS_IMA;
+ vgmstream->layout_type = layout_none;
+ vgmstream->interleave_block_size = block_size;
+ vgmstream->num_samples = ms_ima_bytes_to_samples(sound_size, block_size, channels);
+
+ if (!find_chunk_le(sf, 0x53574156, 0x0c, 0, &data_offset, &data_size)) /* "SWAV" */
+ goto fail;
+
+ start_offset = data_offset + sound_offset;
+ break;
+ default:
+ goto fail;
+ }
+
+ if (!vgmstream_open_stream(vgmstream, sf, start_offset))
+ goto fail;
+ return vgmstream;
+
+fail:
+ close_vgmstream(vgmstream);
+ return NULL;
+}
diff --git a/src/meta/ubi_sb.c b/src/meta/ubi_sb.c
index 93a73fc1..a6afc335 100644
--- a/src/meta/ubi_sb.c
+++ b/src/meta/ubi_sb.c
@@ -1868,7 +1868,7 @@ static int parse_type_audio(ubi_sb_header* sb, off_t offset, STREAMFILE* sf) {
/* apparently, there may also be other subblocks based on various flags but they were not seen so far */
if (sb->cfg.audio_subblock_flag && sb->cfg.audio_subblock_and) {
- /* flag probably means "hardware decoded" */
+ /* flag probably means "software decoded" */
int subblock_flag = read_32bit(offset + sb->cfg.audio_subblock_flag, sf) & sb->cfg.audio_subblock_and;
sb->subblock_id = (!subblock_flag) ? 0 : 1;
@@ -3039,7 +3039,6 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
config_sb_sequence(sb, 0x2c, 0x1c);
/* no layers */
-
return 1;
}
diff --git a/src/vgmstream.c b/src/vgmstream.c
index ad6cf472..b86ffe9c 100644
--- a/src/vgmstream.c
+++ b/src/vgmstream.c
@@ -512,6 +512,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
init_vgmstream_xws,
init_vgmstream_cpk,
init_vgmstream_opus_nsopus,
+ init_vgmstream_sbk,
/* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */
init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */
diff --git a/src/vgmstream.h b/src/vgmstream.h
index 915b30e4..f4f5783b 100644
--- a/src/vgmstream.h
+++ b/src/vgmstream.h
@@ -741,6 +741,7 @@ typedef enum {
meta_WADY,
meta_DSP_SQEX,
meta_DSP_WIIVOICE,
+ meta_SBK,
} meta_t;
/* standard WAVEFORMATEXTENSIBLE speaker positions */