diff --git a/src/coding/circus_decoder.c b/src/coding/circus_decoder.c
new file mode 100644
index 00000000..54dde1a7
--- /dev/null
+++ b/src/coding/circus_decoder.c
@@ -0,0 +1,29 @@
+#include "coding.h"
+
+
+/* Circus XPCM mode 2 decoding, verified vs EF.exe (info from foo_adpcm/libpcm and https://github.com/lioncash/ExtractData) */
+void decode_circus_adpcm(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
+ int i, sample_pos = 0;
+ int32_t hist = stream->adpcm_history1_32;
+ int scale = stream->adpcm_scale;
+ off_t frame_offset = stream->offset; /* frame size is 1 */
+
+
+ for (i = first_sample; i < first_sample + samples_to_do; i++) {
+ int8_t code = read_8bit(frame_offset+i,stream->streamfile);
+
+ hist += code << scale;
+ if (code == 0) {
+ if (scale > 0)
+ scale--;
+ }
+ else if (code == 127 || code == -128) {
+ if (scale < 8)
+ scale++;
+ }
+ outbuf[sample_pos] = hist;
+ }
+
+ stream->adpcm_history1_32 = hist;
+ stream->adpcm_scale = scale;
+}
diff --git a/src/coding/coding.h b/src/coding/coding.h
index aa9d0827..ae1a5338 100644
--- a/src/coding/coding.h
+++ b/src/coding/coding.h
@@ -165,6 +165,8 @@ void decode_xmd(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing,
/* derf_decoder */
void decode_derf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
+/* circus_decoder */
+void decode_circus_adpcm(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
/* ea_mt_decoder*/
ea_mt_codec_data *init_ea_mt(int channels, int type);
diff --git a/src/formats.c b/src/formats.c
index 49eaaad8..ab3eb805 100644
--- a/src/formats.c
+++ b/src/formats.c
@@ -605,6 +605,7 @@ static const coding_info coding_info_list[] = {
{coding_DERF, "Xilam DERF 8-bit DPCM"},
{coding_ACM, "InterPlay ACM"},
{coding_NWA, "VisualArt's NWA DPCM"},
+ {coding_CIRCUS_ADPCM, "Circus 8-bit ADPCM"},
{coding_EA_MT, "Electronic Arts MicroTalk"},
diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj
index 7553c96e..97288545 100644
--- a/src/libvgmstream.vcproj
+++ b/src/libvgmstream.vcproj
@@ -1726,6 +1726,14 @@
RelativePath=".\coding\yamaha_decoder.c"
>
+
+
+
+
diff --git a/src/libvgmstream.vcxproj b/src/libvgmstream.vcxproj
index 424d0496..ccfb2082 100644
--- a/src/libvgmstream.vcxproj
+++ b/src/libvgmstream.vcxproj
@@ -126,6 +126,7 @@
+
diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters
index 80875ffd..7799f549 100644
--- a/src/libvgmstream.vcxproj.filters
+++ b/src/libvgmstream.vcxproj.filters
@@ -1417,6 +1417,9 @@
coding\Source Files
+
+ coding\Source Files
+
meta\Source Files
diff --git a/src/meta/xpcm.c b/src/meta/xpcm.c
index 15db1ab2..9635bdd2 100644
--- a/src/meta/xpcm.c
+++ b/src/meta/xpcm.c
@@ -9,7 +9,6 @@ VGMSTREAM * init_vgmstream_xpcm(STREAMFILE *streamFile) {
int loop_flag, channel_count, codec, subcodec, sample_rate;
-
/* checks */
if (!check_extensions(streamFile, "pcm"))
goto fail;
@@ -27,7 +26,7 @@ VGMSTREAM * init_vgmstream_xpcm(STREAMFILE *streamFile) {
/* 0x14: average bitrate */
/* 0x18: block size */
/* 0x1a: output bits (16) */
- start_offset = 0x1c;
+ start_offset = 0x1c; /* compressed size in codec 0x01/03 */
loop_flag = 0;
@@ -47,11 +46,16 @@ VGMSTREAM * init_vgmstream_xpcm(STREAMFILE *streamFile) {
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x02;
break;
+ case 0x02:
+ if (subcodec != 0) goto fail;
+ vgmstream->coding_type = coding_CIRCUS_ADPCM;
+ vgmstream->layout_type = layout_interleave;
+ vgmstream->interleave_block_size = 0x01;
+ break;
+
case 0x01: /* LZSS + VQ */
- case 0x02: /* ADPCM */
case 0x03: /* unknown */
default:
- /* 0x1c contains compressed size for those */
goto fail;
}
diff --git a/src/vgmstream.c b/src/vgmstream.c
index 2e0d290c..2f9550a1 100644
--- a/src/vgmstream.c
+++ b/src/vgmstream.c
@@ -1124,6 +1124,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
case coding_DERF:
case coding_NWA:
case coding_SASSC:
+ case coding_CIRCUS_ADPCM:
return 1;
case coding_IMA:
@@ -1299,6 +1300,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
case coding_DERF:
case coding_NWA:
case coding_SASSC:
+ case coding_CIRCUS_ADPCM:
return 0x01;
case coding_IMA:
@@ -1743,6 +1745,12 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
vgmstream->channels,vgmstream->samples_into_block,samples_to_do);
}
break;
+ case coding_CIRCUS_ADPCM:
+ for (ch = 0; ch < vgmstream->channels; ch++) {
+ decode_circus_adpcm(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch,
+ vgmstream->channels,vgmstream->samples_into_block,samples_to_do);
+ }
+ break;
case coding_IMA:
case coding_IMA_int:
diff --git a/src/vgmstream.h b/src/vgmstream.h
index 5e4ef722..afd3a2b5 100644
--- a/src/vgmstream.h
+++ b/src/vgmstream.h
@@ -159,6 +159,7 @@ typedef enum {
coding_DERF, /* DERF 8-bit DPCM */
coding_ACM, /* InterPlay ACM */
coding_NWA, /* VisualArt's NWA */
+ coding_CIRCUS_ADPCM, /* Circus 8-bit ADPCM */
coding_EA_MT, /* Electronic Arts MicroTalk (linear-predictive speech codec) */