From 1b4049c4452ae83753556ae508980e4d670b05c7 Mon Sep 17 00:00:00 2001
From: bxaimc <bxaimc@yahoo.com>
Date: Wed, 20 Apr 2022 23:05:37 -0400
Subject: [PATCH] Add S3V support for SVDX (AC). Fix looping for 2DX9 and SD9.

---
 msvc-build.bat                   |  2 +-
 src/formats.c                    |  3 +-
 src/libvgmstream.vcxproj         |  1 +
 src/libvgmstream.vcxproj.filters |  3 ++
 src/meta/2dx9.c                  |  7 +++-
 src/meta/meta.h                  |  2 ++
 src/meta/s3v.c                   | 62 ++++++++++++++++++++++++++++++++
 src/meta/sd9.c                   | 21 +++++++----
 src/vgmstream.c                  |  1 +
 src/vgmstream.h                  |  1 +
 10 files changed, 94 insertions(+), 9 deletions(-)
 create mode 100644 src/meta/s3v.c

diff --git a/msvc-build.bat b/msvc-build.bat
index 2b026c25..00b6aa05 100644
--- a/msvc-build.bat
+++ b/msvc-build.bat
@@ -1 +1 @@
-powershell -ExecutionPolicy Bypass -NoProfile -File .\msvc-build.ps1 Build
+powershell -ExecutionPolicy Bypass -NoProfile -File .\msvc-build.ps1 Build
\ No newline at end of file
diff --git a/src/formats.c b/src/formats.c
index f7a90af4..823dc393 100644
--- a/src/formats.c
+++ b/src/formats.c
@@ -431,7 +431,7 @@ static const char* extension_list[] = {
     "rxx", //txth/reserved [Full Auto (X360)]
 
     "s14",
-    "s3v", //txth/reserved [Sound Voltex 5 (AC)]
+    "s3v", //Sound Voltex (AC)
     "sab",
     "sad",
     "saf",
@@ -1388,6 +1388,7 @@ static const meta_info meta_info_list[] = {
         {meta_DSP_APEX,             "Koei Tecmo APEX header"},
         {meta_MPEG,                 "MPEG header"},
         {meta_SSPF,                 "Konami SSPF header"},
+        {meta_S3V,                  "Konami S3V 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 f2d84dd6..01291886 100644
--- a/src/libvgmstream.vcxproj
+++ b/src/libvgmstream.vcxproj
@@ -245,6 +245,7 @@
     <ClCompile Include="meta\ps2_iab.c" />
     <ClCompile Include="meta\mss.c" />
     <ClCompile Include="meta\mtaf.c" />
+    <ClCompile Include="meta\s3v.c" />
     <ClCompile Include="meta\spm.c" />
     <ClCompile Include="meta\rad.c" />
     <ClCompile Include="meta\sbk.c" />
diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters
index a3c74ab1..5dd9e6f2 100644
--- a/src/libvgmstream.vcxproj.filters
+++ b/src/libvgmstream.vcxproj.filters
@@ -1981,5 +1981,8 @@
     <ClCompile Include="meta\sspf.c">
       <Filter>meta\Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="meta\s3v.c">
+      <Filter>meta\Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>
\ No newline at end of file
diff --git a/src/meta/2dx9.c b/src/meta/2dx9.c
index 911b6f6a..cefd664e 100644
--- a/src/meta/2dx9.c
+++ b/src/meta/2dx9.c
@@ -24,7 +24,7 @@ VGMSTREAM * init_vgmstream_2dx9(STREAMFILE *streamFile) {
     if (read_32bitBE(0x6a,streamFile) != 0x64617461) /* data */
         goto fail;
 
-    loop_flag = 0;
+    loop_flag = (read_16bitLE(0x0e,streamFile) > 0);
     channel_count = read_16bitLE(0x2e,streamFile);
     start_offset = 0x72;
     
@@ -35,6 +35,11 @@ VGMSTREAM * init_vgmstream_2dx9(STREAMFILE *streamFile) {
     vgmstream->meta_type = meta_2DX9;
     vgmstream->sample_rate = read_32bitLE(0x30,streamFile);
     vgmstream->num_samples = read_32bitLE(0x66,streamFile);
+    if (loop_flag) {
+        vgmstream->loop_start_sample = 0;
+        vgmstream->loop_end_sample = vgmstream->num_samples;
+    }
+
     vgmstream->coding_type = coding_MSADPCM;
     vgmstream->layout_type = layout_none;
     vgmstream->frame_size  = read_16bitLE(0x38,streamFile);
diff --git a/src/meta/meta.h b/src/meta/meta.h
index 3a70308e..034751d2 100644
--- a/src/meta/meta.h
+++ b/src/meta/meta.h
@@ -980,4 +980,6 @@ VGMSTREAM* init_vgmstream_mpeg(STREAMFILE* sf);
 
 VGMSTREAM* init_vgmstream_sspf(STREAMFILE* sf);
 
+VGMSTREAM* init_vgmstream_s3v(STREAMFILE* sf);
+
 #endif /*_META_H*/
diff --git a/src/meta/s3v.c b/src/meta/s3v.c
new file mode 100644
index 00000000..d309bed8
--- /dev/null
+++ b/src/meta/s3v.c
@@ -0,0 +1,62 @@
+#include "meta.h"
+#include "../coding/coding.h"
+
+/* S3V - from Konami arcade games [SOUND VOLTEX series (AC)] */
+VGMSTREAM * init_vgmstream_s3v(STREAMFILE *sf) {
+    VGMSTREAM * vgmstream = NULL;
+    off_t start_offset;
+	int32_t channel_count, loop_flag;
+    size_t data_size;
+
+    /* checks */
+    if (!check_extensions(sf, "s3v"))
+        goto fail;
+
+    /* check header */
+    if (read_32bitBE(0x00,sf) != 0x53335630) /* S3V0 */
+        goto fail;
+
+    /* No discernible loop_flag so we'll just use signatures.
+       Might have to update on a per game basis. */
+	switch (read_32bitBE(0x14, sf)) {
+        case 0x82FA0000: // SOUND VOLTEX EXCEED GEAR ver5
+        case 0x1BFD0000: // SOUND VOLTEX EXCEED GEAR ver6
+            loop_flag = 1;
+            break;
+
+        default:
+            loop_flag = 0;
+    }
+
+    start_offset = 0x20;
+    data_size = read_32bitLE(0x08, sf);
+    channel_count = 2;
+
+    /* build the VGMSTREAM */
+    vgmstream = allocate_vgmstream(channel_count,loop_flag);
+    if (!vgmstream) goto fail;
+    vgmstream->meta_type = meta_S3V;
+
+#ifdef VGM_USE_FFMPEG
+
+    vgmstream->codec_data = init_ffmpeg_offset(sf, start_offset, data_size);
+    if (!vgmstream->codec_data) goto fail;
+    vgmstream->sample_rate = ffmpeg_get_sample_rate(vgmstream->codec_data);
+    vgmstream->num_samples = ffmpeg_get_samples(vgmstream->codec_data);
+    if (loop_flag) {
+        vgmstream->loop_start_sample = read_32bitLE(0x10, sf);
+        vgmstream->loop_end_sample = vgmstream->num_samples;
+    }
+    vgmstream->coding_type = coding_FFmpeg;
+    vgmstream->layout_type = layout_none;
+
+#endif
+
+    if (!vgmstream_open_stream(vgmstream, sf, start_offset))
+        goto fail;
+    return vgmstream;
+
+fail:
+    close_vgmstream(vgmstream);
+    return NULL;
+}
diff --git a/src/meta/sd9.c b/src/meta/sd9.c
index dbe9d264..8fa47d35 100644
--- a/src/meta/sd9.c
+++ b/src/meta/sd9.c
@@ -22,12 +22,11 @@ VGMSTREAM * init_vgmstream_sd9(STREAMFILE *streamFile) {
     if (read_32bitBE(0x72, streamFile) != 0x64617461) /* data */
         goto fail;
 
-    /* Probably better to check if loop end exists and use as loop flag.
-       Blame SD9s from beatmania IIDX 21: Spada that have a flase flag
-       but still "loop" */
+    /* Some SD9s may loop without any loop points specificed.
+       If loop_flag is set with no points, loop entire song. */
 
-    //loop_flag = (read_16bitLE(0x0e,streamFile)==0x1);
-    loop_flag = read_32bitLE(0x18, streamFile); // use loop end
+    loop_flag = read_16bitLE(0x0e,streamFile);
+    //loop_flag = read_32bitLE(0x18, streamFile); // use loop end
     channel_count = read_16bitLE(0x36, streamFile);
     start_offset = 0x7a;
 
@@ -37,7 +36,17 @@ VGMSTREAM * init_vgmstream_sd9(STREAMFILE *streamFile) {
 
     vgmstream->sample_rate = read_32bitLE(0x38, streamFile);
     vgmstream->num_samples = read_32bitLE(0x6e, streamFile);
-    if (loop_flag) {
+    if (loop_flag > 0) {
+        vgmstream->loop_start_sample = read_32bitLE(0x14, streamFile) / 2 / channel_count;
+        vgmstream->loop_end_sample = read_32bitLE(0x18, streamFile) / 2 / channel_count;
+        if (vgmstream->loop_end_sample == 0) {
+            vgmstream->loop_end_sample = vgmstream->num_samples;
+        }
+    }
+
+    /* beatmania IIDX 21: Spada is a special case. Loop flag is false but loops exist.
+       Konami, Why? */
+    if ((loop_flag < 0) && (read_32bitLE(0x18, streamFile) !=0)) {
         vgmstream->loop_start_sample = read_32bitLE(0x14, streamFile) / 2 / channel_count;
         vgmstream->loop_end_sample = read_32bitLE(0x18, streamFile) / 2 / channel_count;
     }
diff --git a/src/vgmstream.c b/src/vgmstream.c
index 3e0b9b95..bad69dc5 100644
--- a/src/vgmstream.c
+++ b/src/vgmstream.c
@@ -521,6 +521,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
     init_vgmstream_ubi_ckd_cwav,
     init_vgmstream_sspf,
     init_vgmstream_opus_rsnd,
+    init_vgmstream_s3v,
 
     /* lower priority metas (no clean header identity, somewhat ambiguous, or need extension/companion file to identify) */
     init_vgmstream_mpeg,
diff --git a/src/vgmstream.h b/src/vgmstream.h
index 690aed90..b3142e30 100644
--- a/src/vgmstream.h
+++ b/src/vgmstream.h
@@ -755,6 +755,7 @@ typedef enum {
     meta_DSP_APEX,
     meta_MPEG,
     meta_SSPF,
+    meta_S3V,
 
 } meta_t;