From 3a49c090a09b5c7d95a3f158009dbfe8d0d6c08e Mon Sep 17 00:00:00 2001
From: Adam Gashlin <agashlin@gmail.com>
Date: Thu, 17 Aug 2023 22:20:22 -0700
Subject: [PATCH] Support 32-bit integer PCM in RIFF

From masters for a Sonic Origins mod
---
 src/base/decode.c        |  9 +++++++++
 src/coding/coding.h      |  1 +
 src/coding/pcm_decoder.c | 15 +++++++++++++++
 src/formats.c            |  1 +
 src/meta/riff.c          |  4 ++++
 src/vgmstream_types.h    |  1 +
 6 files changed, 31 insertions(+)

diff --git a/src/base/decode.c b/src/base/decode.c
index 61bc66bb..3a9d2a1e 100644
--- a/src/base/decode.c
+++ b/src/base/decode.c
@@ -367,6 +367,7 @@ int decode_get_samples_per_frame(VGMSTREAM* vgmstream) {
         case coding_PCMFLOAT:
         case coding_PCM24LE:
         case coding_PCM24BE:
+        case coding_PCM32LE:
             return 1;
 #ifdef VGM_USE_VORBIS
         case coding_OGG_VORBIS:
@@ -594,6 +595,7 @@ int decode_get_frame_size(VGMSTREAM* vgmstream) {
         case coding_ALAW:
             return 0x01;
         case coding_PCMFLOAT:
+        case coding_PCM32LE:
             return 0x04;
         case coding_PCM24LE:
         case coding_PCM24BE:
@@ -907,6 +909,13 @@ void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_
             }
             break;
 
+        case coding_PCM32LE:
+            for (ch = 0; ch < vgmstream->channels; ch++) {
+                decode_pcm32le(&vgmstream->ch[ch], buffer+ch,
+                        vgmstream->channels, vgmstream->samples_into_block, samples_to_do);
+            }
+            break;
+
         case coding_NDS_IMA:
             for (ch = 0; ch < vgmstream->channels; ch++) {
                 decode_nds_ima(&vgmstream->ch[ch], buffer+ch,
diff --git a/src/coding/coding.h b/src/coding/coding.h
index c41f7f5b..edceec6c 100644
--- a/src/coding/coding.h
+++ b/src/coding/coding.h
@@ -96,6 +96,7 @@ void decode_alaw(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing,
 void decode_pcmfloat(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int big_endian);
 void decode_pcm24le(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
 void decode_pcm24be(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
+void decode_pcm32le(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
 int32_t pcm_bytes_to_samples(size_t bytes, int channels, int bits_per_sample);
 int32_t pcm24_bytes_to_samples(size_t bytes, int channels);
 int32_t pcm16_bytes_to_samples(size_t bytes, int channels);
diff --git a/src/coding/pcm_decoder.c b/src/coding/pcm_decoder.c
index 7a1e61e0..9b7dcccb 100644
--- a/src/coding/pcm_decoder.c
+++ b/src/coding/pcm_decoder.c
@@ -238,11 +238,26 @@ void decode_pcm24le(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspaci
     }
 }
 
+void decode_pcm32le(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
+    int i;
+    int32_t sample_count;
+
+    for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
+        off_t offset = stream->offset + i * 0x04;
+        int32_t v = read_s32le(offset, stream->streamfile);
+        outbuf[sample_count] = (v >> 16);
+    }
+}
+
 int32_t pcm_bytes_to_samples(size_t bytes, int channels, int bits_per_sample) {
     if (channels <= 0 || bits_per_sample <= 0) return 0;
     return ((int64_t)bytes * 8) / channels / bits_per_sample;
 }
 
+int32_t pcm32_bytes_to_samples(size_t bytes, int channels) {
+    return pcm_bytes_to_samples(bytes, channels, 32);
+}
+
 int32_t pcm24_bytes_to_samples(size_t bytes, int channels) {
     return pcm_bytes_to_samples(bytes, channels, 24);
 }
diff --git a/src/formats.c b/src/formats.c
index c15c82b6..0a397498 100644
--- a/src/formats.c
+++ b/src/formats.c
@@ -765,6 +765,7 @@ static const coding_info coding_info_list[] = {
         {coding_PCMFLOAT,           "32-bit float PCM"},
         {coding_PCM24LE,            "24-bit Little Endian PCM"},
         {coding_PCM24BE,            "24-bit Big Endian PCM"},
+        {coding_PCM32LE,            "32-bit Little Endian PCM"},
 
         {coding_CRI_ADX,            "CRI ADX 4-bit ADPCM"},
         {coding_CRI_ADX_fixed,      "CRI ADX 4-bit ADPCM (fixed coefficients)"},
diff --git a/src/meta/riff.c b/src/meta/riff.c
index 2e5a73fd..e349056a 100644
--- a/src/meta/riff.c
+++ b/src/meta/riff.c
@@ -146,6 +146,9 @@ static int read_fmt(int big_endian, STREAMFILE* sf, off_t offset, riff_fmt_chunk
 
         case 0x0001: /* PCM */
             switch (fmt->bps) {
+                case 32:
+                    fmt->coding_type = coding_PCM32LE;
+                    break;
                 case 24: /* Omori (PC) */
                     fmt->coding_type = coding_PCM24LE;
                     break;
@@ -701,6 +704,7 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) {
 
     /* samples, codec init (after setting coding to ensure proper close on failure) */
     switch (fmt.coding_type) {
+        case coding_PCM32LE:
         case coding_PCM24LE:
         case coding_PCM16LE:
         case coding_PCM8_U:
diff --git a/src/vgmstream_types.h b/src/vgmstream_types.h
index 64ded87e..9e5c053a 100644
--- a/src/vgmstream_types.h
+++ b/src/vgmstream_types.h
@@ -26,6 +26,7 @@ typedef enum {
     coding_PCMFLOAT,        /* 32-bit float PCM */
     coding_PCM24LE,         /* little endian 24-bit PCM */
     coding_PCM24BE,         /* big endian 24-bit PCM */
+    coding_PCM32LE,         /* little endian 32-bit PCM */
 
     /* ADPCM */
     coding_CRI_ADX,         /* CRI ADX */