From 54340f9bcdf8fee99f9a222e126a97fb10b7493a Mon Sep 17 00:00:00 2001
From: bnnm <bananaman255@gmail.com>
Date: Tue, 1 Dec 2020 23:25:13 +0100
Subject: [PATCH] Fix SNDS with CBD2 [Battle Tryst (Arcade)]

---
 src/layout/blocked_str_snds.c | 65 ++++++++++++++---------------------
 src/meta/str_snds.c           | 18 +++++++---
 2 files changed, 39 insertions(+), 44 deletions(-)

diff --git a/src/layout/blocked_str_snds.c b/src/layout/blocked_str_snds.c
index 7a7fca9a..d47473ed 100644
--- a/src/layout/blocked_str_snds.c
+++ b/src/layout/blocked_str_snds.c
@@ -1,54 +1,39 @@
 #include "layout.h"
 #include "../vgmstream.h"
 
-/* set up for the block at the given offset */
-void block_update_str_snds(off_t block_offset, VGMSTREAM * vgmstream) {
-    off_t current_chunk;
-    size_t file_size;
+
+void block_update_str_snds(off_t block_offset, VGMSTREAM* vgmstream) {
+    STREAMFILE* sf = vgmstream->ch[0].streamfile;
+    uint32_t block_type, block_subtype, block_size, block_current;
     int i;
-    STREAMFILE *streamfile;
-    int FoundSSMP = 0;
-    off_t SSMP_offset = -1;
 
-    current_chunk = block_offset;
-    streamfile = vgmstream->ch[0].streamfile;
-    file_size = get_streamfile_size(streamfile);
+    /* EOF reads: signal we have nothing and let the layout fail */
+    if (block_offset >= get_streamfile_size(sf)) {
+        vgmstream->current_block_samples = -1;
+        return;
+    }
 
-    /* we may have to skip some chunks */
-    while (!FoundSSMP && current_chunk < file_size) {
-        if (current_chunk+read_32bitBE(current_chunk+4,streamfile)>=file_size)
-            break;
-        switch (read_32bitBE(current_chunk,streamfile)) {
-            case 0x534e4453:    /* SNDS */
-                /* SSMP */
-                if (read_32bitBE(current_chunk+0x10,streamfile)==0x53534d50) {
-                    FoundSSMP = 1;
-                    SSMP_offset = current_chunk;
-                }
-                break;
-            case 0x46494c4c:    /* FILL, the main culprit */
-            default:
-                break;
+
+    block_type = read_u32be(block_offset + 0x00,sf);
+    block_size = read_u32be(block_offset + 0x04,sf);
+
+    block_current = 0; /* ignore block by default (other chunks include MPVD + VHDR/FRAM and FILL) */
+    if (block_type == 0x534e4453) { /* SNDS */
+        block_subtype = read_u32be(block_offset + 0x10,sf); /* SNDS */
+        if (block_subtype == 0x53534d50) {
+            block_current = read_u32be(block_offset + 0x14, sf) / vgmstream->channels;
         }
-
-        current_chunk += read_32bitBE(current_chunk+4,streamfile);
     }
 
-    if (!FoundSSMP) {
-        /* if we couldn't find it all we can do is try playing the current
-         * block, which is going to suck */
-        vgmstream->current_block_offset = block_offset;
-    }
+    /* seen in Battle Tryst video frames */
+    if (block_size % 0x04)
+        block_size += 0x04 - (block_size % 0x04);
 
-    vgmstream->current_block_offset = SSMP_offset;
-    vgmstream->current_block_size = (read_32bitBE(
-            vgmstream->current_block_offset+4,
-            vgmstream->ch[0].streamfile) - 0x18) / vgmstream->channels;
-    vgmstream->next_block_offset = vgmstream->current_block_offset +
-            read_32bitBE(vgmstream->current_block_offset+4,
-                    vgmstream->ch[0].streamfile);
+    vgmstream->current_block_offset = block_offset;
+    vgmstream->next_block_offset = block_offset + block_size;
+    vgmstream->current_block_size = block_current;
 
     for (i = 0; i < vgmstream->channels; i++) {
-        vgmstream->ch[i].offset = vgmstream->current_block_offset + 0x18 + i * vgmstream->interleave_block_size;
+        vgmstream->ch[i].offset = block_offset + 0x18 + i * vgmstream->interleave_block_size;
     }
 }
diff --git a/src/meta/str_snds.c b/src/meta/str_snds.c
index d4edcb7e..d3f2f935 100644
--- a/src/meta/str_snds.c
+++ b/src/meta/str_snds.c
@@ -13,10 +13,10 @@ VGMSTREAM* init_vgmstream_str_snds(STREAMFILE* sf) {
 
     /* checks */
     /* .str: standard
+     * .stream: Battle Tryst (Arcade) movies
      * .3do: Aqua World - Umimi Monogatari (3DO) movies */
-    if (!check_extensions(sf, "str,3do"))
+    if (!check_extensions(sf, "str,stream,3do"))
         goto fail;
-
     if (read_u32be(0x00,sf) != 0x4354524c &&   /* "CTRL" */
         read_u32be(0x00,sf) != 0x534e4453 &&   /* "SNDS" */
         read_u32be(0x00,sf) != 0x53484452)     /* "SHDR" */
@@ -96,14 +96,24 @@ VGMSTREAM* init_vgmstream_str_snds(STREAMFILE* sf) {
     vgmstream->num_samples /= vgmstream->channels;
 
     switch (read_u32be(shdr_offset + 0x24,sf)) {
-        case 0x53445832:    /* "SDX2" */
+        case 0x53445832:    /* "SDX2" (common) */
             if (channels > 1) {
                 vgmstream->coding_type = coding_SDX2_int;
-                vgmstream->interleave_block_size = 1;
+                vgmstream->interleave_block_size = 0x01;
             } else {
                 vgmstream->coding_type = coding_SDX2;
             }
             break;
+
+        case 0x43424432:    /* "CBD2" (rare, Battle Tryst) */
+            if (channels > 1) {
+                vgmstream->coding_type = coding_CBD2_int;
+                vgmstream->interleave_block_size = 0x01;
+            } else {
+                vgmstream->coding_type = coding_CBD2; /* assumed */
+            }
+            break;
+
         default:
             goto fail;
     }