From a7acf244ae14a9dc2b44d94dab9bc6a4307d72ce Mon Sep 17 00:00:00 2001
From: bnnm <bananaman255@gmail.com>
Date: Fri, 29 Sep 2017 23:24:25 +0200
Subject: [PATCH 01/12] Fix HCA crash bug when testing keys

---
 src/meta/hca.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/src/meta/hca.c b/src/meta/hca.c
index 7417c225..0dd76970 100644
--- a/src/meta/hca.c
+++ b/src/meta/hca.c
@@ -94,7 +94,7 @@ fail:
 /* Tries to find the decryption key from a list. Simply decodes a few frames and checks if there aren't too many
  * clipped samples, as it's common for invalid keys (though possible with valid keys in poorly mastered files). */
 static void find_hca_key(hca_codec_data * hca_data, clHCA * hca, uint8_t * buffer, int header_size, unsigned int * out_key1, unsigned int * out_key2) {
-    sample testbuf[clHCA_samplesPerBlock];
+    sample testbuf[clHCA_samplesPerBlock * 16]; /* max 16 channels, let's be generous */
     int i;
     size_t keys_length = sizeof(hcakey_list) / sizeof(hcakey_info);
 
@@ -124,6 +124,10 @@ static void find_hca_key(hca_codec_data * hca_data, clHCA * hca, uint8_t * buffe
         if (clHCA_Decode(hca, buffer, header_size, 0) < 0) continue;
         if (clHCA_getInfo(hca, &hca_data->info) < 0) continue;
 
+        if (hca_data->info.channelCount > 16) {
+            VGM_LOG("HCA: too many channels, cannot test keys\n");
+            goto end;
+        }
 
         /* test enough frames, but not too many */
         while (f < HCA_KEY_MAX_TEST_FRAMES && f < hca_data->info.blockCount) {
@@ -164,7 +168,7 @@ static void find_hca_key(hca_codec_data * hca_data, clHCA * hca, uint8_t * buffe
     hca_data->curblock = 0;
     hca_data->sample_ptr = clHCA_samplesPerBlock;
     read_streamfile(buffer, hca_data->start, header_size, hca_data->streamfile);
-
+end:
     VGM_LOG("HCA: best key=%08x%08x (clips=%i)\n", best_key2,best_key1, min_clip_count);
     *out_key2 = best_key2;
     *out_key1 = best_key1;

From 0bb62ce57d1654352ccf804ad90461cc3dd52338 Mon Sep 17 00:00:00 2001
From: bnnm <bananaman255@gmail.com>
Date: Fri, 29 Sep 2017 23:26:42 +0200
Subject: [PATCH 02/12] Minor comments/documentation

---
 src/coding/coding_utils.c |  4 ++--
 src/meta/fsb.c            |  2 +-
 src/meta/fsb5.c           |  6 +++---
 src/meta/hca_keys.h       |  8 +++++---
 src/meta/vawx.c           | 31 ++++++++++++++-----------------
 5 files changed, 25 insertions(+), 26 deletions(-)

diff --git a/src/coding/coding_utils.c b/src/coding/coding_utils.c
index 308180c7..9a7f481d 100644
--- a/src/coding/coding_utils.c
+++ b/src/coding/coding_utils.c
@@ -243,7 +243,7 @@ int ffmpeg_make_riff_xma2(uint8_t * buf, size_t buf_size, size_t sample_count, s
     put_16bitLE(buf+0x26, streams); /* number of streams */
     put_32bitLE(buf+0x28, speakers); /* speaker position  */
     put_32bitLE(buf+0x2c, bytecount); /* PCM samples */
-    put_32bitLE(buf+0x30, block_size); /* XMA block size */
+    put_32bitLE(buf+0x30, block_size); /* XMA block size (can be zero, it's for seeking only) */
     /* (looping values not set, expected to be handled externally) */
     put_32bitLE(buf+0x34, 0); /* play begin */
     put_32bitLE(buf+0x38, 0); /* play length */
@@ -251,7 +251,7 @@ int ffmpeg_make_riff_xma2(uint8_t * buf, size_t buf_size, size_t sample_count, s
     put_32bitLE(buf+0x40, 0); /* loop length */
     put_8bit(buf+0x44, 0); /* loop count */
     put_8bit(buf+0x45, 4); /* encoder version */
-    put_16bitLE(buf+0x46, block_count); /* blocks count = entries in seek table */
+    put_16bitLE(buf+0x46, block_count); /* blocks count (entries in seek table, can be zero) */
 
     memcpy(buf+0x48, "data", 4);
     put_32bitLE(buf+0x4c, data_size); /* data size */
diff --git a/src/meta/fsb.c b/src/meta/fsb.c
index 733ae293..cd84c40c 100644
--- a/src/meta/fsb.c
+++ b/src/meta/fsb.c
@@ -332,7 +332,7 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) {
         uint8_t buf[FAKE_RIFF_BUFFER_SIZE];
         size_t bytes, block_size, block_count;
         /* not accurate but not needed by FFmpeg */
-        block_size = 2048;
+        block_size = 0x8000; /* FSB default */
         block_count = fsbh.datasize / block_size; /* read_32bitLE(custom_data_offset +0x14) -1? */
 
         /* make a fake riff so FFmpeg can parse the XMA2 */
diff --git a/src/meta/fsb5.c b/src/meta/fsb5.c
index f358cf31..f6395b69 100644
--- a/src/meta/fsb5.c
+++ b/src/meta/fsb5.c
@@ -251,13 +251,13 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
 
 #ifdef VGM_USE_FFMPEG
         case 0x0A: {/* FMOD_SOUND_FORMAT_XMA */
-            uint8_t buf[100];
+            uint8_t buf[0x100];
             int bytes, block_size, block_count;
 
-            block_size = 0x10000; /* XACT default */
+            block_size = 0x8000; /* FSB default */
             block_count = StreamSize / block_size + (StreamSize % block_size ? 1 : 0);
 
-            bytes = ffmpeg_make_riff_xma2(buf, 100, vgmstream->num_samples, StreamSize, vgmstream->channels, vgmstream->sample_rate, block_count, block_size);
+            bytes = ffmpeg_make_riff_xma2(buf, 0x100, vgmstream->num_samples, StreamSize, vgmstream->channels, vgmstream->sample_rate, block_count, block_size);
             if (bytes <= 0) goto fail;
 
             vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, StartOffset,StreamSize);
diff --git a/src/meta/hca_keys.h b/src/meta/hca_keys.h
index 3d1cba9b..48ac3394 100644
--- a/src/meta/hca_keys.h
+++ b/src/meta/hca_keys.h
@@ -5,7 +5,11 @@ typedef struct {
     uint64_t key;
 } hcakey_info;
 
-/* CRI's tools expect an unsigned 64 bit number, but keys are commonly found online in hex form */
+/**
+ * List of known keys, extracted from the game files (mostly found in 2ch.net).
+ * CRI's tools expect an unsigned 64 bit number string, but keys are commonly found online in hex form.
+ * Keys only use 56 bits though, so the upper 8 bits can be ignored.
+ */
 static const hcakey_info hcakey_list[] = {
 
         // HCA Decoder default
@@ -14,8 +18,6 @@ static const hcakey_info hcakey_list[] = {
         // Phantasy Star Online 2 (multi?)
         // used by most console games
         {0xCC55463930DBE1AB},       // CC55463930DBE1AB / 14723751768204501419
-        // variation from VGAudio, but some 2ch poster says the above works with CRI's tools; seems to decode the same
-        {24002584467202475},        // 0055463930DBE1AB
 
         // Old Phantasy Star Online 2 (multi?)
         {61891147883431481},        // 30DBE1ABCC554639
diff --git a/src/meta/vawx.c b/src/meta/vawx.c
index 9812f50b..befdaef2 100644
--- a/src/meta/vawx.c
+++ b/src/meta/vawx.c
@@ -1,14 +1,11 @@
 #include "meta.h"
 #include "../coding/coding.h"
 
-#define FAKE_RIFF_BUFFER_SIZE           100
 
-/**
- * VAWX - found in feelplus games: No More Heroes Heroes Paradise, Moon Diver
- */
+/* VAWX - found in feelplus games (No More Heroes Heroes Paradise, Moon Diver) */
 VGMSTREAM * init_vgmstream_vawx(STREAMFILE *streamFile) {
     VGMSTREAM * vgmstream = NULL;
-    off_t start_offset, datasize;
+    off_t start_offset, data_size;
 
     int loop_flag = 0, channel_count, type;
 
@@ -52,17 +49,17 @@ VGMSTREAM * init_vgmstream_vawx(STREAMFILE *streamFile) {
 #ifdef VGM_USE_FFMPEG
         case 1: { /* XMA2 */
             ffmpeg_codec_data *ffmpeg_data = NULL;
-            uint8_t buf[FAKE_RIFF_BUFFER_SIZE];
+            uint8_t buf[0x100];
             int32_t bytes, block_size, block_count;
-            /* todo not accurate (needed for >2ch) */
-            datasize = get_streamfile_size(streamFile)-start_offset;
-            block_size = 2048;
-            block_count = datasize / block_size; /* read_32bitLE(custom_data_offset +0x14) -1? */
 
-            bytes = ffmpeg_make_riff_xma2(buf, FAKE_RIFF_BUFFER_SIZE, vgmstream->num_samples, datasize, vgmstream->channels, vgmstream->sample_rate, block_count, block_size);
+            data_size = get_streamfile_size(streamFile)-start_offset;
+            block_size = 0x10000; /* VAWX default */
+            block_count = (uint16_t)read_16bitBE(0x3A, streamFile); /* also at 0x56 */
+
+            bytes = ffmpeg_make_riff_xma2(buf,0x100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size);
             if (bytes <= 0) goto fail;
 
-            ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,datasize);
+            ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size);
             if ( !ffmpeg_data ) goto fail;
             vgmstream->codec_data = ffmpeg_data;
             vgmstream->coding_type = coding_FFmpeg;
@@ -75,22 +72,22 @@ VGMSTREAM * init_vgmstream_vawx(STREAMFILE *streamFile) {
         }
 
         case 7: { /* ATRAC3 */
-            uint8_t buf[FAKE_RIFF_BUFFER_SIZE];
+            uint8_t buf[0x100];
             int32_t bytes, block_size, encoder_delay, joint_stereo, max_samples;
 
-            datasize = read_32bitBE(0x54,streamFile);
+            data_size = read_32bitBE(0x54,streamFile);
             block_size = 0x98 * vgmstream->channels;
             joint_stereo = 0;
-            max_samples = atrac3_bytes_to_samples(datasize, block_size);
+            max_samples = atrac3_bytes_to_samples(data_size, block_size);
             encoder_delay = 0x0; //max_samples - vgmstream->num_samples; /* todo not correct */
             vgmstream->num_samples = max_samples; /* use calc samples since loop points are too, breaks looping in some files otherwise */
 
             /* make a fake riff so FFmpeg can parse the ATRAC3 */
-            bytes = ffmpeg_make_riff_atrac3(buf, FAKE_RIFF_BUFFER_SIZE, vgmstream->num_samples, datasize, vgmstream->channels, vgmstream->sample_rate, block_size, joint_stereo, encoder_delay);
+            bytes = ffmpeg_make_riff_atrac3(buf,0x100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_size, joint_stereo, encoder_delay);
             if (bytes <= 0)
                 goto fail;
 
-            vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,datasize);
+            vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size);
             if (!vgmstream->codec_data) goto fail;
             vgmstream->coding_type = coding_FFmpeg;
             vgmstream->layout_type = layout_none;

From 8dfac7c4652ce94e8141050592146b2676a42ea6 Mon Sep 17 00:00:00 2001
From: bnnm <bananaman255@gmail.com>
Date: Fri, 29 Sep 2017 23:27:21 +0200
Subject: [PATCH 03/12] Fix minor size bug in Wwise Vorbis

---
 src/meta/wwise.c | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/src/meta/wwise.c b/src/meta/wwise.c
index 435377c9..ecf9fe01 100644
--- a/src/meta/wwise.c
+++ b/src/meta/wwise.c
@@ -265,6 +265,7 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
                     cfg.blocksize_1_exp = read_8bit(vorb_offset + block_offsets + 0x00, streamFile); /* small */
                     cfg.blocksize_0_exp = read_8bit(vorb_offset + block_offsets + 0x01, streamFile); /* big */
                 }
+                ww.data_size -= audio_offset;
 
                 /* detect setup type:
                  * - full inline: ~2009, ex. The King of Fighters XII X360, The Saboteur PC
@@ -284,8 +285,6 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
                     }
                 }
 
-                //ww.data_size -= audio_offset; //todo test
-
                 vgmstream->codec_data = init_vorbis_custom_codec_data(streamFile, start_offset + setup_offset, VORBIS_WWISE, &cfg);
                 if (!vgmstream->codec_data) goto fail;
             }
@@ -319,6 +318,7 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
                 audio_offset = read_32bit(extra_offset + data_offsets + 0x04, streamFile); /* within data */
                 cfg.blocksize_1_exp = read_8bit(extra_offset + block_offsets + 0x00, streamFile); /* small */
                 cfg.blocksize_0_exp = read_8bit(extra_offset + block_offsets + 0x01, streamFile); /* big */
+                ww.data_size -= audio_offset;
 
                 /* Normal packets are used rarely (ex. Oddworld New 'n' Tasty! PSV). They are hard to detect (decoding
                  * will mostly work with garbage results) but we'll try. Setup size and "fmt" bitrate fields may matter too. */
@@ -329,8 +329,6 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
                         cfg.packet_type = STANDARD;
                 }
 
-                //ww.data_size -= audio_offset; //todo test
-
                 /* try with the selected codebooks */
                 vgmstream->codec_data = init_vorbis_custom_codec_data(streamFile, start_offset + setup_offset, VORBIS_WWISE, &cfg);
                 if (!vgmstream->codec_data) {
@@ -349,7 +347,7 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
             /* Vorbis is VBR so this is very approximate, meh */
             if (ww.truncated)
                 vgmstream->num_samples = vgmstream->num_samples * (ww.file_size - start_offset) / ww.data_size;
-
+VGM_LOG("so=%lx, ds=%x\n", start_offset, ww.data_size);
             break;
         }
 #endif
@@ -482,7 +480,7 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
                 size_t seek_size;
 
                 vgmstream->num_samples += read_32bit(ww.fmt_offset + 0x18, streamFile);
-                //todo 0x1c and 0x20: related to samples/looping?
+                /* 0x1c: null? 0x20: data_size without seek_size */
                 seek_size = read_32bit(ww.fmt_offset + 0x24, streamFile);
 
                 start_offset += seek_size;

From 8aae6ed794c2829409b4d27508722dabfa6a3ff3 Mon Sep 17 00:00:00 2001
From: bnnm <bananaman255@gmail.com>
Date: Fri, 29 Sep 2017 23:28:27 +0200
Subject: [PATCH 04/12] Fix multichannel EA-XMA [Dante's Inferno (X360)]

---
 src/coding/coding.h                      |   2 +-
 src/coding/ffmpeg_decoder_utils_ea_xma.c | 172 ++++++++++++++++++-----
 src/meta/ea_snu.c                        |  16 ++-
 src/vgmstream.h                          |   1 +
 4 files changed, 146 insertions(+), 45 deletions(-)

diff --git a/src/coding/coding.h b/src/coding/coding.h
index 0c21a18d..af164bd0 100644
--- a/src/coding/coding.h
+++ b/src/coding/coding.h
@@ -214,7 +214,7 @@ void ffmpeg_set_skip_samples(ffmpeg_codec_data * data, int skip_samples);
 
 
 size_t ffmpeg_make_opus_header(uint8_t * buf, int buf_size, int channels, int skip, int sample_rate);
-size_t ffmpeg_get_eaxma_virtual_size(off_t real_offset, size_t real_size, STREAMFILE *streamFile);
+size_t ffmpeg_get_eaxma_virtual_size(int channels, off_t real_offset, size_t real_size, STREAMFILE *streamFile);
 #endif
 
 /* coding_utils */
diff --git a/src/coding/ffmpeg_decoder_utils_ea_xma.c b/src/coding/ffmpeg_decoder_utils_ea_xma.c
index 84e69fd7..6c79a60b 100644
--- a/src/coding/ffmpeg_decoder_utils_ea_xma.c
+++ b/src/coding/ffmpeg_decoder_utils_ea_xma.c
@@ -3,58 +3,121 @@
 
 #ifdef VGM_USE_FFMPEG
 
-#define EAXMA_XMA_BLOCK_SIZE 0x800
+#define EAXMA_XMA_MAX_PACKETS_PER_SNS_BLOCK 3 /* only seen up to 3 (Dante's Inferno) */
+#define EAXMA_XMA_MAX_STREAMS_PER_SNS_BLOCK 4 /* XMA2 max is 8ch = 4 * 2ch */
+#define EAXMA_XMA_PACKET_SIZE 0x800
+#define EAXMA_XMA_BUFFER_SIZE (EAXMA_XMA_MAX_PACKETS_PER_SNS_BLOCK * EAXMA_XMA_MAX_STREAMS_PER_SNS_BLOCK * EAXMA_XMA_PACKET_SIZE)
 
 /**
- * EA-XMA is XMA with padding removed (so a real 0x450 block would be padded to a virtual 0x800 block).
- * //todo missing multichannel (packet multistream) support, unknown layout
+ * EA-XMA is XMA2 with padding removed (so a real 0x450 block would be padded to a virtual 0x800 block).
+ * Each EA-XMA SNS block contains 1~3 packets per stream, and multistream uses fully separate streams
+ * (no packet_skip set). We'll pad and reinterleave packets so it resembles standard XMA2.
+ *
+ * XMA2 data layout (XMA1 is the same but doesn't use blocks, they are only for seeking):
+ * - frames (containing 1..4 subframes): decode into 128*4 samples
+ * - packets: size 0x800, containing N frames (last frame can spill into next packet), must be padded
+ * - blocks: fixed size, containing N packets (last packet's frames won't spill into next block)
+ * - stream: N interleaved packets (1/2ch) for multichannel (Nch) audio. Interleave is not fixed:
+ *   at file start/new block has one packet per stream, then must follow the "packet_skip" value
+ *   in the XMA packet header to find its next packet (skiping packets from other streams).
+ *   ex.: s1_p1 skip1, s2_p1 skip2, s1_p2 skip0 s1_p3 skip1, s2_p2 skip1, s1_p4...
  */
 
+static int get_block_max_packets(int num_streams, off_t packets_offset, STREAMFILE * streamfile);
+
 
 int ffmpeg_custom_read_eaxma(ffmpeg_codec_data *data, uint8_t *buf, int buf_size) {
-    uint8_t v_buf[0x8000]; /* intermediate buffer, could be simplified */
+    uint8_t v_buf[EAXMA_XMA_BUFFER_SIZE]; /* intermediate buffer, could be simplified */
     int buf_done = 0;
     uint64_t real_offset = data->real_offset;
     uint64_t virtual_offset = data->virtual_offset - data->header_size;
     uint64_t virtual_base = data->virtual_base;
+    /* EA-XMA always uses late XMA2 streams (2ch + ... + 1/2ch) */
+    int num_streams = (data->config.channels / 2) + (data->config.channels % 2 ? 1 : 0);
 
 
-    /* read and transform SNS/EA-XMA block into XMA block by adding padding */
+    /* read and transform SNS/EA-XMA blocks into XMA packets */
     while (buf_done < buf_size) {
-        int bytes_to_copy;
-        size_t data_size, extra_size = 0, gap_size = 0;
+        int s, p, bytes_to_copy, max_packets;
+        size_t data_size = 0, gap_size = 0;
         size_t block_size = read_32bitBE(real_offset, data->streamfile);
-        /* 0x04(4): some kind of size? 0x08(4): decoded samples */
+        /* 0x04(4): decoded samples */
+        off_t packets_offset = real_offset + 0x08;
 
-        /* setup */
-        data_size = (block_size & 0x00FFFFFF) - 0x0c; //todo last block size may be slightly off?
-        if (data_size % EAXMA_XMA_BLOCK_SIZE) /* aligned padding */
-            extra_size = EAXMA_XMA_BLOCK_SIZE - (data_size % EAXMA_XMA_BLOCK_SIZE);
-        if (buf_done == 0) /* first read */
-            gap_size = virtual_offset - virtual_base; /* might start a few bytes into the block */
+        max_packets = get_block_max_packets(num_streams, packets_offset, data->streamfile);
+        if (max_packets == 0) goto fail;
 
-        if (data_size + extra_size > 0x8000) {
-            VGM_LOG("EA-XMA: total size bigger than buffer at %lx\n", (off_t)real_offset);
-            return 0;
+        if (max_packets * num_streams * EAXMA_XMA_PACKET_SIZE > EAXMA_XMA_BUFFER_SIZE) {
+            VGM_LOG("EA XMA: block too big at %lx\n", (off_t)real_offset);
+            goto fail;
         }
 
-        bytes_to_copy = data_size + extra_size - gap_size;
+        /* data is divided into a sub-block per stream (N packets), can be smaller than block_size (= has padding)
+         * copy XMA data re-interleaving for multichannel. To simplify some calcs fills the same number of packets
+         * per stream and adjusts packet headers (see above for XMA2 multichannel layout). */
+        //to-do this doesn't make correct blocks sizes (but blocks are not needed to decode)
+        for (s = 0; s < num_streams; s++) {
+            size_t packets_size;
+            size_t packets_size4 = read_32bitBE(packets_offset, data->streamfile); /* size * 4, no idea */
+
+            packets_size = (packets_size4 / 4) - 0x04;
+
+            /* Re-interleave all packets in order, one per stream. If one stream has more packets than
+             * others we add empty packets to keep the same number for all, avoiding packet_skip calcs */
+            for (p = 0; p < max_packets; p++) {
+                off_t packet_offset = packets_offset + 0x04 + p * EAXMA_XMA_PACKET_SIZE; /* can be off but will copy 0 */
+                off_t v_buf_offset  = p * EAXMA_XMA_PACKET_SIZE * num_streams + s * EAXMA_XMA_PACKET_SIZE;
+                size_t packet_to_do = packets_size - p * EAXMA_XMA_PACKET_SIZE;
+                size_t extra_size = 0;
+                uint32_t header;
+
+                if (packets_size < p * EAXMA_XMA_PACKET_SIZE)
+                    packet_to_do = 0; /* empty packet */
+                else if (packet_to_do > EAXMA_XMA_PACKET_SIZE)
+                    packet_to_do = EAXMA_XMA_PACKET_SIZE;
+
+                /* padding will be full size if packet_to_do is 0 */
+                if (packet_to_do < EAXMA_XMA_PACKET_SIZE)
+                    extra_size = EAXMA_XMA_PACKET_SIZE - (packet_to_do % EAXMA_XMA_PACKET_SIZE);
+
+                /* copy data (or fully pad if empty packet) */
+                read_streamfile(v_buf + v_buf_offset, packet_offset, packet_to_do, data->streamfile);
+                memset(v_buf + v_buf_offset + packet_to_do, 0xFF, extra_size); /* add padding, typically 0xFF */
+
+                /* rewrite packet header to add packet skips for multichannel (EA XMA streams are fully separate and have none)
+                 * header bits: 6=num_frames, 15=first_frame_bits_offset, 3=metadata, 8=packet_skip */
+                if (packet_to_do == 0)
+                    header = 0x3FFF800; /* new empty packet header (0 num_frames, first_frame_bits_offset set to max) */
+                else
+                    header = (uint32_t)read_32bitBE(packet_offset, data->streamfile);
+
+                /* get base header + change packet_skip since we know interleave is always 1 packet per stream */
+                header = (header & 0xFFFFFF00) | ((header & 0x000000FF) + num_streams - 1);
+                put_32bitBE(v_buf + v_buf_offset, header);
+            }
+
+            packets_offset += (packets_size4 / 4);
+        }
+
+        if (buf_done == 0) /* first read */
+            gap_size = virtual_offset - virtual_base; /* might start a few bytes into the XMA */
+
+        data_size = max_packets * num_streams * EAXMA_XMA_PACKET_SIZE;
+
+        bytes_to_copy = data_size - gap_size;
         if (bytes_to_copy > buf_size - buf_done)
             bytes_to_copy = buf_size - buf_done;
 
-        /* transform */
-        read_streamfile(v_buf, real_offset + 0x0c, data_size, data->streamfile);
-        memset(v_buf + data_size, 0xFF, extra_size); /* padding can be any value, typically 0xFF */
+        /* pad + copy */
         memcpy(buf + buf_done, v_buf + gap_size, bytes_to_copy);
+        buf_done += bytes_to_copy;
 
         /* move when block is fully done */
-        if (data_size + extra_size == bytes_to_copy + gap_size) {
+        if (data_size == bytes_to_copy + gap_size) {
             real_offset += (block_size & 0x00FFFFFF);
-            virtual_base += data_size + extra_size;
+            virtual_base += data_size;
         }
 
-        buf_done += bytes_to_copy;
-
         /* exit on last block just in case, though should reach file size */
         if (block_size & 0x80000000)
             break;
@@ -64,6 +127,9 @@ int ffmpeg_custom_read_eaxma(ffmpeg_codec_data *data, uint8_t *buf, int buf_size
     data->real_offset = real_offset;
     data->virtual_base = virtual_base;
     return buf_size;
+
+fail:
+    return 0;
 }
 
 int64_t ffmpeg_custom_seek_eaxma(ffmpeg_codec_data *data, int64_t virtual_offset) {
@@ -89,8 +155,8 @@ int64_t ffmpeg_custom_seek_eaxma(ffmpeg_codec_data *data, int64_t virtual_offset
         size_t block_size = read_32bitBE(real_offset, data->streamfile);
 
         data_size = (block_size & 0x00FFFFFF) - 0x0c;
-        if (data_size % EAXMA_XMA_BLOCK_SIZE)
-            extra_size = EAXMA_XMA_BLOCK_SIZE - (data_size % EAXMA_XMA_BLOCK_SIZE);
+        if (data_size % EAXMA_XMA_PACKET_SIZE)
+            extra_size = EAXMA_XMA_PACKET_SIZE - (data_size % EAXMA_XMA_PACKET_SIZE);
 
         /* stop if virtual_offset lands inside current block */
         if (data_size + extra_size > virtual_offset)
@@ -117,26 +183,32 @@ int64_t ffmpeg_custom_size_eaxma(ffmpeg_codec_data *data) {
 }
 
 /* needed to know in meta for fake RIFF */
-size_t ffmpeg_get_eaxma_virtual_size(off_t real_offset, size_t real_size, STREAMFILE *streamFile) {
+size_t ffmpeg_get_eaxma_virtual_size(int channels, off_t real_offset, size_t real_size, STREAMFILE *streamFile) {
     size_t virtual_size = 0;
+    size_t real_end_offset = real_offset + real_size;
+    /* EA-XMA always uses late XMA2 streams (2ch + ... + 1/2ch) */
+    int num_streams = (channels / 2) + (channels % 2 ? 1 : 0);
+
 
     /* count all SNS/EAXMA blocks size + padding size */
-    while (real_offset < real_size) {
-        size_t data_size;
-        size_t block_size = read_32bitBE(real_offset, streamFile);
-
-        data_size = (block_size & 0x00FFFFFF) - 0x0c;
+    while (real_offset < real_end_offset) {
+        int max_packets;
+        size_t block_size = read_32bitBE(real_offset + 0x00, streamFile);
+        /* 0x04(4): decoded samples */
+        off_t packets_offset = real_offset + 0x08;
 
         if ((block_size & 0xFF000000) && !(block_size & 0x80000000)) {
             VGM_LOG("EA-XMA: unknown flag found at %lx\n", (off_t)real_offset);
             goto fail;
         }
 
-        real_offset += (block_size & 0x00FFFFFF);
+        max_packets = get_block_max_packets(num_streams, packets_offset, streamFile);
+        if (max_packets == 0) goto fail;
 
-        virtual_size += data_size;
-        if (data_size % EAXMA_XMA_BLOCK_SIZE) /* XMA block padding */
-            virtual_size += EAXMA_XMA_BLOCK_SIZE - (data_size % EAXMA_XMA_BLOCK_SIZE);
+        /* fixed data_size per block for multichannel, see reads */
+        virtual_size += max_packets * num_streams * EAXMA_XMA_PACKET_SIZE;
+
+        real_offset += (block_size & 0x00FFFFFF);
 
         /* exit on last block just in case, though should reach real_size */
         if (block_size & 0x80000000)
@@ -147,7 +219,33 @@ size_t ffmpeg_get_eaxma_virtual_size(off_t real_offset, size_t real_size, STREAM
 
 fail:
     return 0;
+}
 
+/* a block can have N streams each with a varying number of packets, get max */
+static int get_block_max_packets(int num_streams, off_t packets_offset, STREAMFILE * streamfile) {
+    int s;
+    int max_packets = 0;
+
+    for (s = 0; s < num_streams; s++) {
+        size_t packets_size;
+        size_t packets_size4 = read_32bitBE(packets_offset, streamfile); /* size * 4, no idea */
+        int num_packets;
+
+        if (packets_size4 == 0) {
+            VGM_LOG("EA XMA: null packets in stream %i at %lx\n", s, (off_t)packets_offset);
+            goto fail;
+        }
+        packets_size = (packets_size4 / 4) - 0x04;
+
+        num_packets = (int)(packets_size / EAXMA_XMA_PACKET_SIZE) + 1;
+        if (num_packets > max_packets)
+            max_packets = num_packets;
+    }
+
+    return max_packets;
+
+fail:
+    return 0;
 }
 
 #endif
diff --git a/src/meta/ea_snu.c b/src/meta/ea_snu.c
index c21a7252..31fd41eb 100644
--- a/src/meta/ea_snu.c
+++ b/src/meta/ea_snu.c
@@ -18,15 +18,15 @@ VGMSTREAM * init_vgmstream_ea_snu(STREAMFILE *streamFile) {
 
     /* check header (the first 0x10 are BE/LE depending on platform) */
     /* 0x00(1): related to sample rate? (03=48000)
-     * 0x01(1): flags? (when set seems to be a bank and has extra data before start_offset) //todo
+     * 0x01(1): flags/count? (when set has extra block data before start_offset)
      * 0x02(1): always 0?
      * 0x03(1): channels? (usually matches but rarely may be 0)
      * 0x04(4): some size, maybe >>2 ~= number of frames
      * 0x08(4): start offset
-     * 0x0c(4): some sub-offset? (0x20, found when 0x01 is set) */
+     * 0x0c(4): some sub-offset? (0x20, found when @0x01 is set) */
 
-    /* use start offset as endianness flag */
-    if ((uint32_t)read_32bitLE(0x08,streamFile) > 0x00F00000) {
+    /* use start_offset as endianness flag */
+    if ((uint32_t)read_32bitLE(0x08,streamFile) > 0x0000FFFF) {
         read_32bit = read_32bitBE;
     } else {
         read_32bit = read_32bitLE;
@@ -48,6 +48,7 @@ VGMSTREAM * init_vgmstream_ea_snu(STREAMFILE *streamFile) {
 
 #if 0
     //todo not working ok with blocks in XAS
+    //todo check if EA-XMA loops (Dante's Inferno doesn't)
     if (flags & 0x60) { /* full loop, seen in ambient tracks */
         loop_flag = 1;
         loop_start = 0;
@@ -81,7 +82,7 @@ VGMSTREAM * init_vgmstream_ea_snu(STREAMFILE *streamFile) {
     vgmstream->meta_type = meta_EA_SNU;
 
     switch(codec) {
-        case 0x04:      /* "Xas1": EA-XAS (Dead Space) */
+        case 0x04:      /* "Xas1": EA-XAS (Dead Space PC/PS3) */
             vgmstream->coding_type = coding_EA_XAS;
             vgmstream->layout_type = layout_ea_sns_blocked;
             break;
@@ -112,8 +113,8 @@ VGMSTREAM * init_vgmstream_ea_snu(STREAMFILE *streamFile) {
             ffmpeg_custom_config cfg;
 
             stream_size = get_streamfile_size(streamFile) - start_offset;
-            virtual_size = ffmpeg_get_eaxma_virtual_size(start_offset,stream_size, streamFile);
-            block_size = 0x8000; /* ? */
+            virtual_size = ffmpeg_get_eaxma_virtual_size(vgmstream->channels, start_offset,stream_size, streamFile);
+            block_size = 0x10000; /* todo unused and not correctly done by the parser */
             block_count = stream_size / block_size + (stream_size % block_size ? 1 : 0);
 
             bytes = ffmpeg_make_riff_xma2(buf, 0x100, vgmstream->num_samples, virtual_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size);
@@ -122,6 +123,7 @@ VGMSTREAM * init_vgmstream_ea_snu(STREAMFILE *streamFile) {
             memset(&cfg, 0, sizeof(ffmpeg_custom_config));
             cfg.type = FFMPEG_EA_XMA;
             cfg.virtual_size = virtual_size;
+            cfg.channels = vgmstream->channels;
 
             vgmstream->codec_data = init_ffmpeg_config(streamFile, buf,bytes, start_offset,stream_size, &cfg);
             if (!vgmstream->codec_data) goto fail;
diff --git a/src/vgmstream.h b/src/vgmstream.h
index 90c4da86..71fd7bb7 100644
--- a/src/vgmstream.h
+++ b/src/vgmstream.h
@@ -1039,6 +1039,7 @@ typedef enum {
 typedef struct {
     int stream_index; /* FFmpeg's sub-stream (as opposed to an internal stream in custom read/seeks) */
     int codec_endian;
+    int channels;
 
     ffmpeg_custom_t type; /* ffmpeg subtype */
     size_t virtual_size; /* external value, if meta needs to know/supply it */

From c63866aad629079a6170d0ac1949978215ec545d Mon Sep 17 00:00:00 2001
From: bnnm <bananaman255@gmail.com>
Date: Fri, 29 Sep 2017 23:29:06 +0200
Subject: [PATCH 05/12] Fix Wwise Opus abrupt end

---
 src/coding/ffmpeg_decoder_utils_wwise_opus.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/coding/ffmpeg_decoder_utils_wwise_opus.c b/src/coding/ffmpeg_decoder_utils_wwise_opus.c
index 37770132..f635ca87 100644
--- a/src/coding/ffmpeg_decoder_utils_wwise_opus.c
+++ b/src/coding/ffmpeg_decoder_utils_wwise_opus.c
@@ -140,11 +140,11 @@ int64_t ffmpeg_custom_seek_wwise_opus(ffmpeg_codec_data *data, int64_t virtual_o
 
 int64_t ffmpeg_custom_size_wwise_opus(ffmpeg_codec_data *data) {
     uint64_t real_offset = data->real_start;
-    uint64_t real_size = data->real_size;
+    uint64_t real_end_offset = data->real_start + data->real_size;
     uint64_t virtual_size = data->header_size;
 
     /* count all Wwise Opus blocks size + OggS page size */
-    while (real_offset < real_size) {
+    while (real_offset < real_end_offset) {
         size_t extra_size;
         size_t data_size = read_32bitBE(real_offset, data->streamfile);
         /* 0x00: data size, 0x04: ? (not a sequence or CRC), 0x08+: data */
@@ -316,8 +316,8 @@ fail:
 static size_t make_opus_comment(uint8_t * buf, int buf_size) {
     size_t comment_size;
     int vendor_string_length, user_comment_0_length;
-    char * vendor_string = "libopus 1.0.2";
-    char * user_comment_0_string = "ENCODER=opusenc from opus-tools 0.1.6";
+    char * vendor_string = "vgmstream";
+    char * user_comment_0_string = "vgmstream Opus converter";
     vendor_string_length = strlen(vendor_string);
     user_comment_0_length = strlen(user_comment_0_string);
 

From 8a8ebe3e249535a3f94a8fa66c505f44e3908f3c Mon Sep 17 00:00:00 2001
From: bnnm <bananaman255@gmail.com>
Date: Fri, 29 Sep 2017 23:37:20 +0200
Subject: [PATCH 06/12] Minor renames for style (ps3_xvag > xvag, x360_nub >
 nub_xma)

---
 src/formats.c                      |  2 +-
 src/libvgmstream.vcproj            | 16 ++++++++--------
 src/libvgmstream.vcxproj           |  4 ++--
 src/libvgmstream.vcxproj.filters   | 12 ++++++------
 src/meta/meta.h                    |  4 ++--
 src/meta/{x360_nub.c => nub_xma.c} |  2 +-
 src/meta/{ps3_xvag.c => xvag.c}    |  4 ++--
 src/vgmstream.c                    |  4 ++--
 src/vgmstream.h                    |  2 +-
 9 files changed, 25 insertions(+), 25 deletions(-)
 rename src/meta/{x360_nub.c => nub_xma.c} (95%)
 rename src/meta/{ps3_xvag.c => xvag.c} (96%)

diff --git a/src/formats.c b/src/formats.c
index 658bc7b5..08df838c 100644
--- a/src/formats.c
+++ b/src/formats.c
@@ -811,7 +811,7 @@ static const meta_info meta_info_list[] = {
         {meta_FFW,                  "Freedom Fighters BGM header"},
         {meta_DSP_DSPW,             "DSPW dsp header"},
         {meta_PS2_JSTM,             "JSTM Header"},
-        {meta_PS3_XVAG,             "XVAG Header"},
+        {meta_XVAG,                 "Sony XVAG header"},
         {meta_PS3_CPS,              "tri-Crescendo CPS Header"},
         {meta_SQEX_SCD,             "Square-Enix SCD header"},
         {meta_NGC_NST_DSP,          "Animaniacs NST header"},
diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj
index 76bdce31..d469f3d4 100644
--- a/src/libvgmstream.vcproj
+++ b/src/libvgmstream.vcproj
@@ -626,6 +626,10 @@
 					RelativePath=".\meta\nub_vag.c"
 					>
 				</File>
+                <File
+                    RelativePath=".\meta\nub_xma.c"
+                    >
+                </File>
 				<File
 					RelativePath=".\meta\nwa.c"
 					>
@@ -998,10 +1002,6 @@
 					RelativePath=".\meta\vawx.c"
 					>
 				</File>
-				<File
-					RelativePath=".\meta\ps3_xvag.c"
-					>
-				</File>
 				<File
 					RelativePath=".\meta\psx_cdxa.c"
 					>
@@ -1230,10 +1230,6 @@
                     RelativePath=".\meta\x360_cxs.c"
                     >
                 </File>
-                <File
-                    RelativePath=".\meta\x360_nub.c"
-                    >
-                </File>
                 <File
                     RelativePath=".\meta\x360_pasx.c"
                     >
@@ -1278,6 +1274,10 @@
 					RelativePath=".\meta\xss.c"
 					>
 				</File>
+                <File
+                    RelativePath=".\meta\xvag.c"
+                    >
+                </File>
 				<File
 					RelativePath=".\meta\xwb.c"
 					>
diff --git a/src/libvgmstream.vcxproj b/src/libvgmstream.vcxproj
index f5511888..49d64b48 100644
--- a/src/libvgmstream.vcxproj
+++ b/src/libvgmstream.vcxproj
@@ -281,6 +281,7 @@
     <ClCompile Include="meta\ngc_tydsp.c" />
     <ClCompile Include="meta\ngc_ymf.c" />
     <ClCompile Include="meta\ngc_ulw.c" />
+    <ClCompile Include="meta\nub_xma.c" />
     <ClCompile Include="meta\nwa.c" />
     <ClCompile Include="meta\ogg_vorbis_file.c" />
     <ClCompile Include="meta\ogl.c" />
@@ -359,7 +360,6 @@
     <ClCompile Include="meta\ps3_cps.c" />
     <ClCompile Include="meta\ps3_msf.c" />
     <ClCompile Include="meta\ps3_mta2.c" />
-    <ClCompile Include="meta\ps3_xvag.c" />
     <ClCompile Include="meta\psx_cdxa.c" />
     <ClCompile Include="meta\psx_fag.c" />
     <ClCompile Include="meta\psx_gms.c" />
@@ -416,9 +416,9 @@
     <ClCompile Include="meta\xbox_xvas.c" />
     <ClCompile Include="meta\xbox_xwav.c" />
     <ClCompile Include="meta\x360_pasx.c" />
-    <ClCompile Include="meta\x360_nub.c" />
     <ClCompile Include="meta\xma.c" />
     <ClCompile Include="meta\xss.c" />
+    <ClCompile Include="meta\xvag.c" />
     <ClCompile Include="meta\xwb.c" />
     <ClCompile Include="meta\ydsp.c" />
     <ClCompile Include="meta\zsd.c" />
diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters
index 6927059d..f8f49919 100644
--- a/src/libvgmstream.vcxproj.filters
+++ b/src/libvgmstream.vcxproj.filters
@@ -373,6 +373,9 @@
     <ClCompile Include="meta\ngc_ulw.c">
       <Filter>meta\Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="meta\nub_xma.c">
+      <Filter>meta\Source Files</Filter>
+    </ClCompile>
     <ClCompile Include="meta\nwa.c">
       <Filter>meta\Source Files</Filter>
     </ClCompile>
@@ -607,9 +610,6 @@
     <ClCompile Include="meta\ps3_mta2.c">
       <Filter>meta\Source Files</Filter>
     </ClCompile>
-    <ClCompile Include="meta\ps3_xvag.c">
-      <Filter>meta\Source Files</Filter>
-    </ClCompile>
     <ClCompile Include="meta\psx_cdxa.c">
       <Filter>meta\Source Files</Filter>
     </ClCompile>
@@ -754,9 +754,6 @@
     <ClCompile Include="meta\wwise.c">
       <Filter>meta\Source Files</Filter>
     </ClCompile>
-    <ClCompile Include="meta\x360_nub.c">
-      <Filter>meta\Source Files</Filter>
-    </ClCompile>
     <ClCompile Include="meta\x360_pasx.c">
       <Filter>meta\Source Files</Filter>
     </ClCompile>
@@ -784,6 +781,9 @@
     <ClCompile Include="meta\xss.c">
       <Filter>meta\Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="meta\xvag.c">
+      <Filter>meta\Source Files</Filter>
+    </ClCompile>
     <ClCompile Include="meta\xwb.c">
       <Filter>meta\Source Files</Filter>
     </ClCompile>
diff --git a/src/meta/meta.h b/src/meta/meta.h
index fbbb0699..a5194d87 100644
--- a/src/meta/meta.h
+++ b/src/meta/meta.h
@@ -536,7 +536,7 @@ VGMSTREAM * init_vgmstream_dsp_dspw(STREAMFILE* streamFile);
 
 VGMSTREAM * init_vgmstream_ps2_jstm(STREAMFILE* streamFile);
 
-VGMSTREAM * init_vgmstream_ps3_xvag(STREAMFILE* streamFile);
+VGMSTREAM * init_vgmstream_xvag(STREAMFILE* streamFile);
 
 VGMSTREAM * init_vgmstream_ps3_cps(STREAMFILE* streamFile);
 
@@ -645,7 +645,7 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile);
 
 VGMSTREAM * init_vgmstream_ubi_raki(STREAMFILE* streamFile);
 
-VGMSTREAM * init_vgmstream_x360_nub(STREAMFILE *streamFile);
+VGMSTREAM * init_vgmstream_nub_xma(STREAMFILE *streamFile);
 
 VGMSTREAM * init_vgmstream_x360_pasx(STREAMFILE *streamFile);
 
diff --git a/src/meta/x360_nub.c b/src/meta/nub_xma.c
similarity index 95%
rename from src/meta/x360_nub.c
rename to src/meta/nub_xma.c
index 3c4cae95..cb33d551 100644
--- a/src/meta/x360_nub.c
+++ b/src/meta/nub_xma.c
@@ -2,7 +2,7 @@
 #include "../coding/coding.h"
 
 /* Namco NUB xma - from Tekken 6, Galaga Legions DX */
-VGMSTREAM * init_vgmstream_x360_nub(STREAMFILE *streamFile) {
+VGMSTREAM * init_vgmstream_nub_xma(STREAMFILE *streamFile) {
     VGMSTREAM * vgmstream = NULL;
     off_t start_offset, chunk_offset;
     size_t data_size, chunk_size;
diff --git a/src/meta/ps3_xvag.c b/src/meta/xvag.c
similarity index 96%
rename from src/meta/ps3_xvag.c
rename to src/meta/xvag.c
index 009376ee..987db2f1 100644
--- a/src/meta/ps3_xvag.c
+++ b/src/meta/xvag.c
@@ -5,7 +5,7 @@
 static int ps_adpcm_find_loop_offsets(STREAMFILE *streamFile, int channel_count, off_t start_offset, off_t * loop_start, off_t * loop_end);
 
 /* XVAG - Sony's (second party?) format (God of War III, Ratchet & Clank Future, The Last of Us, Uncharted) */
-VGMSTREAM * init_vgmstream_ps3_xvag(STREAMFILE *streamFile) {
+VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) {
     VGMSTREAM * vgmstream = NULL;
     int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
     int loop_flag = 0, channel_count, codec;
@@ -57,7 +57,7 @@ VGMSTREAM * init_vgmstream_ps3_xvag(STREAMFILE *streamFile) {
 
     vgmstream->sample_rate = sample_rate;
     vgmstream->num_samples = num_samples;
-    vgmstream->meta_type = meta_PS3_XVAG;
+    vgmstream->meta_type = meta_XVAG;
 
     switch (codec) {
         case 0x06:   /* PS ADPCM: God of War III, Uncharted 1/2, Ratchet and Clank Future */
diff --git a/src/vgmstream.c b/src/vgmstream.c
index 2ef7dbe0..f1d330b4 100644
--- a/src/vgmstream.c
+++ b/src/vgmstream.c
@@ -292,7 +292,7 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = {
     init_vgmstream_ffw,
     init_vgmstream_dsp_dspw,
     init_vgmstream_ps2_jstm,
-    init_vgmstream_ps3_xvag,
+    init_vgmstream_xvag,
 	init_vgmstream_ps3_cps,
     init_vgmstream_sqex_scd,
     init_vgmstream_ngc_nst_dsp,
@@ -350,7 +350,7 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = {
     init_vgmstream_wwise,
     init_vgmstream_ubi_raki,
     init_vgmstream_x360_pasx,
-    init_vgmstream_x360_nub,
+    init_vgmstream_nub_xma,
     init_vgmstream_xma,
     init_vgmstream_sxd,
     init_vgmstream_ogl,
diff --git a/src/vgmstream.h b/src/vgmstream.h
index 71fd7bb7..76b92009 100644
--- a/src/vgmstream.h
+++ b/src/vgmstream.h
@@ -556,7 +556,7 @@ typedef enum {
     meta_SQEX_SCD,          /* Square-Enix SCD */
     meta_NGC_NST_DSP,       /* Animaniacs [NGC] */
     meta_BAF,               /* Bizarre Creations (Blur, James Bond) */
-	meta_PS3_XVAG,          /* Ratchet & Clank Future: Quest for Booty (PS3) */
+	meta_XVAG,              /* Ratchet & Clank Future: Quest for Booty (PS3) */
 	meta_PS3_CPS,           /* Eternal Sonata (PS3) */
     meta_PS3_MSF,           /* MSF header */
 	meta_NUB_VAG,           /* Namco VAG from NUB archives */

From abaefd17cd04f3d5522b9930c64da60b51951c4e Mon Sep 17 00:00:00 2001
From: bnnm <bananaman255@gmail.com>
Date: Sat, 30 Sep 2017 00:16:40 +0200
Subject: [PATCH 07/12] Rename wwise_opus to switch_opus as it's apparently so

---
 src/coding/ffmpeg_decoder.c                   | 30 +++++++++----------
 src/coding/ffmpeg_decoder_utils.h             |  6 ++--
 ...s.c => ffmpeg_decoder_utils_switch_opus.c} |  6 ++--
 src/libvgmstream.vcproj                       |  2 +-
 src/libvgmstream.vcxproj                      |  2 +-
 src/libvgmstream.vcxproj.filters              |  2 +-
 src/meta/wwise.c                              |  2 +-
 src/vgmstream.h                               |  4 +--
 8 files changed, 27 insertions(+), 27 deletions(-)
 rename src/coding/{ffmpeg_decoder_utils_wwise_opus.c => ffmpeg_decoder_utils_switch_opus.c} (95%)

diff --git a/src/coding/ffmpeg_decoder.c b/src/coding/ffmpeg_decoder.c
index 1dc44550..fd703699 100644
--- a/src/coding/ffmpeg_decoder.c
+++ b/src/coding/ffmpeg_decoder.c
@@ -218,11 +218,11 @@ static int ffmpeg_read(void *opaque, uint8_t *buf, int buf_size) {
 
     /* main read */
     switch(data->config.type) {
-        case FFMPEG_EA_XMA:     ret = ffmpeg_custom_read_eaxma(data, buf, buf_size); break;
-        case FFMPEG_WWISE_OPUS: ret = ffmpeg_custom_read_wwise_opus(data, buf, buf_size); break;
-      //case FFMPEG_EA_SCHL:    ret = ffmpeg_custom_read_ea_schl(data, buf, buf_size); break;
-      //case FFMPEG_SFH:        ret = ffmpeg_custom_read_sfh(data, buf, buf_size); break;
-        default:                ret = ffmpeg_custom_read_standard(data, buf, buf_size); break;
+        case FFMPEG_EA_XMA:         ret = ffmpeg_custom_read_eaxma(data, buf, buf_size); break;
+        case FFMPEG_SWITCH_OPUS:    ret = ffmpeg_custom_read_switch_opus(data, buf, buf_size); break;
+      //case FFMPEG_EA_SCHL:        ret = ffmpeg_custom_read_ea_schl(data, buf, buf_size); break;
+      //case FFMPEG_SFH:            ret = ffmpeg_custom_read_sfh(data, buf, buf_size); break;
+        default:                    ret = ffmpeg_custom_read_standard(data, buf, buf_size); break;
     }
     data->virtual_offset += ret;
     //data->real_offset = ; /* must be updated in function */
@@ -285,11 +285,11 @@ static int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence) {
 
     /* main seek */
     switch(data->config.type) {
-        case FFMPEG_EA_XMA:     offset = ffmpeg_custom_seek_eaxma(data, offset); break;
-        case FFMPEG_WWISE_OPUS: offset = ffmpeg_custom_seek_wwise_opus(data, offset); break;
-      //case FFMPEG_EA_SCHL:    offset = ffmpeg_custom_seek_ea_schl(data, offset); break;
-      //case FFMPEG_SFH:        offset = ffmpeg_custom_seek_sfh(data, offset); break;
-        default:                offset = ffmpeg_custom_seek_standard(data, offset); break;
+        case FFMPEG_EA_XMA:         offset = ffmpeg_custom_seek_eaxma(data, offset); break;
+        case FFMPEG_SWITCH_OPUS:    offset = ffmpeg_custom_seek_switch_opus(data, offset); break;
+      //case FFMPEG_EA_SCHL:        offset = ffmpeg_custom_seek_ea_schl(data, offset); break;
+      //case FFMPEG_SFH:            offset = ffmpeg_custom_seek_sfh(data, offset); break;
+        default:                    offset = ffmpeg_custom_seek_standard(data, offset); break;
     }
     data->virtual_offset = offset;
     //data->real_offset = ; /* must be updated in function */
@@ -302,11 +302,11 @@ static int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence) {
 static int64_t ffmpeg_size(ffmpeg_codec_data * data) {
     int64_t bytes;
     switch(data->config.type) {
-        case FFMPEG_EA_XMA:     bytes = ffmpeg_custom_size_eaxma(data); break;
-        case FFMPEG_WWISE_OPUS: bytes = ffmpeg_custom_size_wwise_opus(data); break;
-      //case FFMPEG_EA_SCHL:    bytes = ffmpeg_custom_size_ea_schl(data); break;
-      //case FFMPEG_SFH:        bytes = ffmpeg_custom_size_sfh(data); break;
-        default:                bytes = ffmpeg_custom_size_standard(data); break;
+        case FFMPEG_EA_XMA:         bytes = ffmpeg_custom_size_eaxma(data); break;
+        case FFMPEG_SWITCH_OPUS:    bytes = ffmpeg_custom_size_switch_opus(data); break;
+      //case FFMPEG_EA_SCHL:        bytes = ffmpeg_custom_size_ea_schl(data); break;
+      //case FFMPEG_SFH:            bytes = ffmpeg_custom_size_sfh(data); break;
+        default:                    bytes = ffmpeg_custom_size_standard(data); break;
     }
 
     return bytes;
diff --git a/src/coding/ffmpeg_decoder_utils.h b/src/coding/ffmpeg_decoder_utils.h
index 18957e3c..64461fe9 100644
--- a/src/coding/ffmpeg_decoder_utils.h
+++ b/src/coding/ffmpeg_decoder_utils.h
@@ -27,9 +27,9 @@ int ffmpeg_custom_read_eaxma(ffmpeg_codec_data *data, uint8_t *buf, int buf_size
 int64_t ffmpeg_custom_seek_eaxma(ffmpeg_codec_data *data, int64_t virtual_offset);
 int64_t ffmpeg_custom_size_eaxma(ffmpeg_codec_data *data);
 
-int ffmpeg_custom_read_wwise_opus(ffmpeg_codec_data *data, uint8_t *buf, int buf_size);
-int64_t ffmpeg_custom_seek_wwise_opus(ffmpeg_codec_data *data, int64_t virtual_offset);
-int64_t ffmpeg_custom_size_wwise_opus(ffmpeg_codec_data *data);
+int ffmpeg_custom_read_switch_opus(ffmpeg_codec_data *data, uint8_t *buf, int buf_size);
+int64_t ffmpeg_custom_seek_switch_opus(ffmpeg_codec_data *data, int64_t virtual_offset);
+int64_t ffmpeg_custom_size_switch_opus(ffmpeg_codec_data *data);
 
 //int ffmpeg_custom_read_ea_schl(ffmpeg_codec_data *data, uint8_t *buf, int buf_size);
 //int64_t ffmpeg_custom_seek_ea_schl(ffmpeg_codec_data *data, int64_t virtual_offset);
diff --git a/src/coding/ffmpeg_decoder_utils_wwise_opus.c b/src/coding/ffmpeg_decoder_utils_switch_opus.c
similarity index 95%
rename from src/coding/ffmpeg_decoder_utils_wwise_opus.c
rename to src/coding/ffmpeg_decoder_utils_switch_opus.c
index f635ca87..8af6a7f5 100644
--- a/src/coding/ffmpeg_decoder_utils_wwise_opus.c
+++ b/src/coding/ffmpeg_decoder_utils_switch_opus.c
@@ -44,7 +44,7 @@ fail:
 }
 
 
-int ffmpeg_custom_read_wwise_opus(ffmpeg_codec_data *data, uint8_t *buf, int buf_size) {
+int ffmpeg_custom_read_switch_opus(ffmpeg_codec_data *data, uint8_t *buf, int buf_size) {
     uint8_t v_buf[0x8000]; /* intermediate buffer, could be simplified */
     int buf_done = 0;
     uint64_t real_offset = data->real_offset;
@@ -98,7 +98,7 @@ int ffmpeg_custom_read_wwise_opus(ffmpeg_codec_data *data, uint8_t *buf, int buf
     return buf_size;
 }
 
-int64_t ffmpeg_custom_seek_wwise_opus(ffmpeg_codec_data *data, int64_t virtual_offset) {
+int64_t ffmpeg_custom_seek_switch_opus(ffmpeg_codec_data *data, int64_t virtual_offset) {
     int64_t real_offset, virtual_base;
     int64_t current_virtual_offset = data->virtual_offset;
 
@@ -138,7 +138,7 @@ int64_t ffmpeg_custom_seek_wwise_opus(ffmpeg_codec_data *data, int64_t virtual_o
     return virtual_offset;
 }
 
-int64_t ffmpeg_custom_size_wwise_opus(ffmpeg_codec_data *data) {
+int64_t ffmpeg_custom_size_switch_opus(ffmpeg_codec_data *data) {
     uint64_t real_offset = data->real_start;
     uint64_t real_end_offset = data->real_start + data->real_size;
     uint64_t virtual_size = data->header_size;
diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj
index d469f3d4..b0c7d1a6 100644
--- a/src/libvgmstream.vcproj
+++ b/src/libvgmstream.vcproj
@@ -1379,7 +1379,7 @@
                     >
                 </File>
                 <File
-                    RelativePath=".\coding\ffmpeg_decoder_utils_wwise_opus.c"
+                    RelativePath=".\coding\ffmpeg_decoder_utils_switch_opus.c"
                     >
                 </File>
                 <File
diff --git a/src/libvgmstream.vcxproj b/src/libvgmstream.vcxproj
index 49d64b48..9e0ff19b 100644
--- a/src/libvgmstream.vcxproj
+++ b/src/libvgmstream.vcxproj
@@ -133,7 +133,7 @@
     <ClCompile Include="coding\coding_utils.c" />
     <ClCompile Include="coding\ffmpeg_decoder.c" />
     <ClCompile Include="coding\ffmpeg_decoder_utils_ea_xma.c" />
-    <ClCompile Include="coding\ffmpeg_decoder_utils_wwise_opus.c" />
+    <ClCompile Include="coding\ffmpeg_decoder_utils_switch_opus.c" />
     <ClCompile Include="coding\ffmpeg_decoder_utils.c" />
     <ClCompile Include="coding\lsf_decoder.c" />
     <ClCompile Include="coding\mp4_aac_decoder.c" />
diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters
index f8f49919..e9e9e3ad 100644
--- a/src/libvgmstream.vcxproj.filters
+++ b/src/libvgmstream.vcxproj.filters
@@ -1147,7 +1147,7 @@
     <ClCompile Include="coding\ffmpeg_decoder_utils_ea_xma.c">
       <Filter>coding\Source Files</Filter>
     </ClCompile>
-    <ClCompile Include="coding\ffmpeg_decoder_utils_wwise_opus.c">
+    <ClCompile Include="coding\ffmpeg_decoder_utils_switch_opus.c">
       <Filter>coding\Source Files</Filter>
     </ClCompile>
     <ClCompile Include="coding\ffmpeg_decoder_utils.c">
diff --git a/src/meta/wwise.c b/src/meta/wwise.c
index ecf9fe01..a9cf6775 100644
--- a/src/meta/wwise.c
+++ b/src/meta/wwise.c
@@ -496,7 +496,7 @@ VGM_LOG("so=%lx, ds=%x\n", start_offset, ww.data_size);
             if (bytes <= 0) goto fail;
 
             memset(&cfg, 0, sizeof(ffmpeg_custom_config));
-            cfg.type = FFMPEG_WWISE_OPUS;
+            cfg.type = FFMPEG_SWITCH_OPUS;
             //cfg.big_endian = ww.big_endian; /* internally BE */
 
             vgmstream->codec_data = init_ffmpeg_config(streamFile, buf,bytes, start_offset,ww.data_size, &cfg);
diff --git a/src/vgmstream.h b/src/vgmstream.h
index 76b92009..4457b420 100644
--- a/src/vgmstream.h
+++ b/src/vgmstream.h
@@ -1028,8 +1028,8 @@ typedef struct {
 /* Custom FFMPEG modes */
 typedef enum {
     FFMPEG_STANDARD,        /* default FFmpeg */
-    FFMPEG_WWISE_OPUS,      /* Opus without Ogg layer */
-    FFMPEG_EA_XMA,          /* XMA with padding removed in SNS blocks */
+    FFMPEG_SWITCH_OPUS,     /* Opus without Ogg layer */
+    FFMPEG_EA_XMA,          /* XMA with padding removed and custom streams in SNS blocks */
   //FFMPEG_EA_SCHL,         /* Normal header+data (ex. ATRAC3) in SCxx blocks */
   //FFMPEG_SFH,             /* ATRAC3plus header+data in SFH blocks */
   //FFMPEG_AWC_XMA,         /* XMA data in AWC blocks, 1 streams per channel */

From c3568ffd26bc5e3f6f9a48c34cf895bf1c8bf90f Mon Sep 17 00:00:00 2001
From: bnnm <bananaman255@gmail.com>
Date: Sat, 30 Sep 2017 01:26:44 +0200
Subject: [PATCH 08/12] Fix MSVC compiling, remove UNICODE as there may be
 issues with MSVC

---
 winamp/in_vgmstream.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/winamp/in_vgmstream.c b/winamp/in_vgmstream.c
index 9f092ad6..9ca571bd 100644
--- a/winamp/in_vgmstream.c
+++ b/winamp/in_vgmstream.c
@@ -5,7 +5,7 @@
 /* Normally Winamp opens unicode files by their DOS 8.3 name. #define this to use wchar_t filenames,
  * which must be opened with _wfopen in a WINAMP_STREAMFILE (needed for dual files like .pos).
  * Only for Winamp paths, other parts would need #define UNICODE for Windows. */
-#define UNICODE_INPUT_PLUGIN
+//#define UNICODE_INPUT_PLUGIN
 
 
 #ifdef _MSC_VER
@@ -106,7 +106,7 @@ in_char lastfn[PATH_LIMIT] = {0}; /* name of the currently playing file */
 #define wa_strlen wcslen
 #define wa_strchr wcschr
 #define wa_sscanf swscanf
-#define wa_snprintf snwprintf
+#define wa_snprintf _snwprintf
 #define wa_fileinfo fileinfoW
 #define wa_IPC_PE_INSERTFILENAME IPC_PE_INSERTFILENAMEW
 #define wa_L(x) L ##x

From 16786b78d4702a43db92a180c77967a99bdaa10e Mon Sep 17 00:00:00 2001
From: bnnm <bananaman255@gmail.com>
Date: Sat, 30 Sep 2017 01:27:47 +0200
Subject: [PATCH 09/12] Add .opus [Lego City Undercover]

---
 fb2k/foo_filetypes.h                          |  1 +
 src/coding/coding.h                           |  3 +
 src/coding/ffmpeg_decoder_utils_switch_opus.c | 17 ++++-
 src/formats.c                                 |  2 +
 src/libvgmstream.vcproj                       |  4 ++
 src/libvgmstream.vcxproj                      |  1 +
 src/libvgmstream.vcxproj.filters              |  3 +
 src/meta/meta.h                               |  2 +
 src/meta/nsw_opus.c                           | 64 +++++++++++++++++++
 src/vgmstream.c                               |  1 +
 src/vgmstream.h                               |  1 +
 11 files changed, 98 insertions(+), 1 deletion(-)
 create mode 100644 src/meta/nsw_opus.c

diff --git a/fb2k/foo_filetypes.h b/fb2k/foo_filetypes.h
index 657d20a9..5093ba36 100644
--- a/fb2k/foo_filetypes.h
+++ b/fb2k/foo_filetypes.h
@@ -207,6 +207,7 @@ VGMSTREAM_DECLARE_FILE_TYPE("NWA", nwa);
 VGMSTREAM_DECLARE_FILE_TYPE("OGL", ogl);
 VGMSTREAM_DECLARE_FILE_TYPE("OMA", oma);
 VGMSTREAM_DECLARE_FILE_TYPE("OMU", omu);
+VGMSTREAM_DECLARE_FILE_TYPE("OPUS", opus);
 VGMSTREAM_DECLARE_FILE_TYPE("OTM", otm);
 
 VGMSTREAM_DECLARE_FILE_TYPE("P2BT", p2bt);
diff --git a/src/coding/coding.h b/src/coding/coding.h
index af164bd0..dc95cd9f 100644
--- a/src/coding/coding.h
+++ b/src/coding/coding.h
@@ -215,6 +215,9 @@ void ffmpeg_set_skip_samples(ffmpeg_codec_data * data, int skip_samples);
 
 size_t ffmpeg_make_opus_header(uint8_t * buf, int buf_size, int channels, int skip, int sample_rate);
 size_t ffmpeg_get_eaxma_virtual_size(int channels, off_t real_offset, size_t real_size, STREAMFILE *streamFile);
+
+size_t switch_opus_get_samples(off_t offset, size_t data_size, int sample_rate, STREAMFILE *streamFile);
+
 #endif
 
 /* coding_utils */
diff --git a/src/coding/ffmpeg_decoder_utils_switch_opus.c b/src/coding/ffmpeg_decoder_utils_switch_opus.c
index 8af6a7f5..d8ce46db 100644
--- a/src/coding/ffmpeg_decoder_utils_switch_opus.c
+++ b/src/coding/ffmpeg_decoder_utils_switch_opus.c
@@ -159,7 +159,23 @@ int64_t ffmpeg_custom_size_switch_opus(ffmpeg_codec_data *data) {
     return virtual_size;
 }
 
+size_t switch_opus_get_samples(off_t offset, size_t data_size, int sample_rate, STREAMFILE *streamFile) {
+    size_t num_samples = 0;
+    off_t end_offset = offset + data_size;
 
+    /* count by reading all frames */
+    while (offset < end_offset) {
+        uint8_t buf[4];
+        size_t block_size = read_32bitBE(offset, streamFile);
+
+        read_streamfile(buf, offset+4, 4, streamFile);
+        num_samples += get_opus_samples_per_frame(buf, sample_rate);
+
+        offset += 0x08 + block_size;
+    }
+
+    return num_samples;
+}
 
 /* ************************************************** */
 
@@ -342,5 +358,4 @@ fail:
     return 0;
 }
 
-
 #endif
diff --git a/src/formats.c b/src/formats.c
index 08df838c..504f6b3e 100644
--- a/src/formats.c
+++ b/src/formats.c
@@ -200,6 +200,7 @@ static const char* extension_list[] = {
     "ogl",
     "oma", //FFmpeg, not parsed (ATRAC3/ATRAC3PLUS/MP3/LPCM/WMA)
     "omu",
+    "opus",
     "otm",
 
     "p1d", //txth/reserved [Farming Simulator 18 (3DS)]
@@ -888,6 +889,7 @@ static const meta_info meta_info_list[] = {
         {meta_BINK,                 "RAD Game Tools Bink header"},
         {meta_EA_SNU,               "Electronic Arts SNU header"},
         {meta_AWC,                  "Rockstar AWC header"},
+        {meta_NSW_OPUS,             ".OPUS header"},
 
 #ifdef VGM_USE_VORBIS
         {meta_OGG_VORBIS,           "Ogg Vorbis"},
diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj
index b0c7d1a6..94aae551 100644
--- a/src/libvgmstream.vcproj
+++ b/src/libvgmstream.vcproj
@@ -622,6 +622,10 @@
 					RelativePath=".\meta\ngca.c"
 					>
 				</File>
+                <File
+                    RelativePath=".\meta\nsw_opus.c"
+                    >
+                </File>
 				<File
 					RelativePath=".\meta\nub_vag.c"
 					>
diff --git a/src/libvgmstream.vcxproj b/src/libvgmstream.vcxproj
index 9e0ff19b..4e407b38 100644
--- a/src/libvgmstream.vcxproj
+++ b/src/libvgmstream.vcxproj
@@ -157,6 +157,7 @@
     <ClCompile Include="meta\mn_str.c" />
     <ClCompile Include="meta\mp4.c" />
     <ClCompile Include="meta\ngca.c" />
+    <ClCompile Include="meta\nsw_opus.c" />
     <ClCompile Include="meta\nub_vag.c" />
     <ClCompile Include="meta\pc_adp.c" />
     <ClCompile Include="meta\pc_snds.c" />
diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters
index e9e9e3ad..83392562 100644
--- a/src/libvgmstream.vcxproj.filters
+++ b/src/libvgmstream.vcxproj.filters
@@ -1012,6 +1012,9 @@
     <ClCompile Include="meta\ngca.c">
       <Filter>meta\Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="meta\nsw_opus.c">
+      <Filter>meta\Source Files</Filter>
+    </ClCompile>
     <ClCompile Include="meta\ps2_mtaf.c">
       <Filter>meta\Source Files</Filter>
     </ClCompile>
diff --git a/src/meta/meta.h b/src/meta/meta.h
index a5194d87..4afb986e 100644
--- a/src/meta/meta.h
+++ b/src/meta/meta.h
@@ -682,4 +682,6 @@ VGMSTREAM * init_vgmstream_ea_snu(STREAMFILE * streamFile);
 
 VGMSTREAM * init_vgmstream_awc(STREAMFILE * streamFile);
 
+VGMSTREAM * init_vgmstream_nsw_opus(STREAMFILE * streamFile);
+
 #endif /*_META_H*/
diff --git a/src/meta/nsw_opus.c b/src/meta/nsw_opus.c
new file mode 100644
index 00000000..9339d477
--- /dev/null
+++ b/src/meta/nsw_opus.c
@@ -0,0 +1,64 @@
+#include "meta.h"
+#include "../util.h"
+#include "../coding/coding.h"
+
+/* .OPUS - from Lego City Undercover (Switch)  */
+VGMSTREAM * init_vgmstream_nsw_opus(STREAMFILE *streamFile) {
+    VGMSTREAM * vgmstream = NULL;
+    off_t start_offset;
+    int loop_flag = 0, channel_count;
+
+    /* check extension, case insensitive */
+    if ( !check_extensions(streamFile,"opus")) /* no relation to Ogg Opus */
+        goto fail;
+
+    if (read_32bitBE(0x00,streamFile) != 0x01000080)
+        goto fail;
+
+    start_offset = 0x28;
+    channel_count = read_8bit(0x09,streamFile); /* assumed */
+    /* other values in the header: no idea */
+
+    /* build the VGMSTREAM */
+    vgmstream = allocate_vgmstream(channel_count,loop_flag);
+    if (!vgmstream) goto fail;
+
+    vgmstream->sample_rate = read_32bitLE(0x0c,streamFile);
+    vgmstream->meta_type = meta_NSW_OPUS;
+
+#ifdef VGM_USE_FFMPEG
+    {
+        uint8_t buf[0x100];
+        size_t bytes, skip, data_size;
+        ffmpeg_custom_config cfg;
+
+        data_size = get_streamfile_size(streamFile) - start_offset;
+        skip = 0; //todo
+
+        bytes = ffmpeg_make_opus_header(buf,0x100, vgmstream->channels, skip, vgmstream->sample_rate);
+        if (bytes <= 0) goto fail;
+
+        memset(&cfg, 0, sizeof(ffmpeg_custom_config));
+        cfg.type = FFMPEG_SWITCH_OPUS;
+
+        vgmstream->codec_data = init_ffmpeg_config(streamFile, buf,bytes, start_offset,data_size, &cfg);
+        if (!vgmstream->codec_data) goto fail;
+
+        vgmstream->coding_type = coding_FFmpeg;
+        vgmstream->layout_type = layout_none;
+
+        vgmstream->num_samples = switch_opus_get_samples(start_offset, data_size, vgmstream->sample_rate, streamFile);
+    }
+#else
+    goto fail;
+#endif
+
+    /* open the file for reading */
+    if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
+        goto fail;
+    return vgmstream;
+
+fail:
+    close_vgmstream(vgmstream);
+    return NULL;
+}
diff --git a/src/vgmstream.c b/src/vgmstream.c
index f1d330b4..517d345d 100644
--- a/src/vgmstream.c
+++ b/src/vgmstream.c
@@ -369,6 +369,7 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = {
     init_vgmstream_stm,
     init_vgmstream_ea_snu,
     init_vgmstream_awc,
+    init_vgmstream_nsw_opus,
 
     init_vgmstream_txth,  /* should go at the end (lower priority) */
 #ifdef VGM_USE_FFMPEG
diff --git a/src/vgmstream.h b/src/vgmstream.h
index 4457b420..a3c106b6 100644
--- a/src/vgmstream.h
+++ b/src/vgmstream.h
@@ -626,6 +626,7 @@ typedef enum {
     meta_BINK,              /* RAD Game Tools BINK audio/video */
     meta_EA_SNU,            /* Electronic Arts SNU (Dead Space) */
     meta_AWC,               /* Rockstar AWC (GTA5, RDR) */
+    meta_NSW_OPUS,          /* Lego City Undercover (Switch) */
 
 #ifdef VGM_USE_VORBIS
     meta_OGG_VORBIS,        /* Ogg Vorbis */

From 14957d6269d42137e1365009349baf671eefc08a Mon Sep 17 00:00:00 2001
From: bnnm <bananaman255@gmail.com>
Date: Sat, 30 Sep 2017 01:52:49 +0200
Subject: [PATCH 10/12] Fix XWB split bug with some codecs

---
 src/meta/xwb.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/meta/xwb.c b/src/meta/xwb.c
index 99f251df..83c44fe6 100644
--- a/src/meta/xwb.c
+++ b/src/meta/xwb.c
@@ -455,7 +455,7 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) {
     }
 
 
-    start_offset = xwb.data_offset;
+    start_offset = xwb.stream_offset;
 
     if ( !vgmstream_open_stream(vgmstream,streamFile,start_offset) )
         goto fail;

From 6c5a97c8ed68aa76139cbcb12df99b9a96fbcdf7 Mon Sep 17 00:00:00 2001
From: bnnm <bananaman255@gmail.com>
Date: Sat, 30 Sep 2017 19:20:09 +0200
Subject: [PATCH 11/12] Fix compiling in GCC (false not defined)

---
 src/meta/ps2_rxws.c | 2 +-
 src/meta/sgxd.c     | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/meta/ps2_rxws.c b/src/meta/ps2_rxws.c
index 862bfc53..01b0ea90 100644
--- a/src/meta/ps2_rxws.c
+++ b/src/meta/ps2_rxws.c
@@ -8,7 +8,7 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) {
     STREAMFILE * streamHeader = NULL;
     off_t start_offset, chunk_offset, name_offset = 0;
     size_t data_size, chunk_size;
-    int loop_flag = 0, channel_count, is_separate = false, type, sample_rate;
+    int loop_flag = 0, channel_count, is_separate = 0, type, sample_rate;
     int32_t loop_start, loop_end;
     int total_streams, target_stream = streamFile->stream_index;
 
diff --git a/src/meta/sgxd.c b/src/meta/sgxd.c
index e820718b..07cfba50 100644
--- a/src/meta/sgxd.c
+++ b/src/meta/sgxd.c
@@ -11,7 +11,7 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
     off_t start_offset, data_offset, chunk_offset, name_offset = 0;
     size_t data_size;
 
-    int is_sgx, is_sgb = false;
+    int is_sgx, is_sgb = 0;
     int loop_flag, channels, type;
     int sample_rate, num_samples, loop_start_sample, loop_end_sample;
     int total_streams, target_stream = streamFile->stream_index;

From 07a6fba10f0959bd59de36ea4986e64d46a66fb1 Mon Sep 17 00:00:00 2001
From: bnnm <bananaman255@gmail.com>
Date: Sat, 30 Sep 2017 19:36:13 +0200
Subject: [PATCH 12/12] Free HCA testbuf and reduce allocs

---
 src/meta/hca.c | 16 ++++++++++------
 1 file changed, 10 insertions(+), 6 deletions(-)

diff --git a/src/meta/hca.c b/src/meta/hca.c
index ad133da6..2f99ab2e 100644
--- a/src/meta/hca.c
+++ b/src/meta/hca.c
@@ -95,7 +95,7 @@ fail:
  * clipped samples, as it's common for invalid keys (though possible with valid keys in poorly mastered files). */
 static void find_hca_key(hca_codec_data * hca_data, clHCA * hca, uint8_t * buffer, int header_size, unsigned int * out_key1, unsigned int * out_key2) {
     sample *testbuf = NULL, *temp;
-    int i, j;
+    int i, j, bufsize = 0, tempsize;
     size_t keys_length = sizeof(hcakey_list) / sizeof(hcakey_info);
 
     int min_clip_count = -1;
@@ -123,13 +123,15 @@ static void find_hca_key(hca_codec_data * hca_data, clHCA * hca, uint8_t * buffe
         clHCA_clear(hca, key1, key2);
         if (clHCA_Decode(hca, buffer, header_size, 0) < 0) continue;
         if (clHCA_getInfo(hca, &hca_data->info) < 0) continue;
+        if (hca_data->info.channelCount > 32) continue; /* nonsense don't alloc too much */
 
-        temp = (sample *)realloc(testbuf, sizeof(sample) * clHCA_samplesPerBlock * hca_data->info.channelCount);
-        if (!temp) {
-            if (testbuf) free(testbuf);
-            return;
+        tempsize = sizeof(sample) * clHCA_samplesPerBlock * hca_data->info.channelCount;
+        if (tempsize > bufsize) { /* should happen once */
+            temp = (sample *)realloc(testbuf, tempsize);
+            if (!temp) goto end;
+            testbuf = temp;
+            bufsize = tempsize;
         }
-        testbuf = temp;
 
         /* test enough frames, but not too many */
         while (f < HCA_KEY_MAX_TEST_FRAMES && f < hca_data->info.blockCount) {
@@ -172,8 +174,10 @@ static void find_hca_key(hca_codec_data * hca_data, clHCA * hca, uint8_t * buffe
     hca_data->curblock = 0;
     hca_data->sample_ptr = clHCA_samplesPerBlock;
     read_streamfile(buffer, hca_data->start, header_size, hca_data->streamfile);
+
 end:
     VGM_LOG("HCA: best key=%08x%08x (clips=%i)\n", best_key2,best_key1, min_clip_count);
     *out_key2 = best_key2;
     *out_key1 = best_key1;
+    free(testbuf);//free(temp);
 }