diff --git a/src/coding/ffmpeg_decoder.c b/src/coding/ffmpeg_decoder.c
index ed3c3d7d..0e365262 100644
--- a/src/coding/ffmpeg_decoder.c
+++ b/src/coding/ffmpeg_decoder.c
@@ -222,7 +222,6 @@ static int ffmpeg_read(void *opaque, uint8_t *buf, int buf_size) {
     switch(data->config.type) {
         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_BGW_ATRAC3:     ret = ffmpeg_custom_read_bgw_atrac3(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;
@@ -291,7 +290,6 @@ static int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence) {
     switch(data->config.type) {
         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_BGW_ATRAC3:     offset = ffmpeg_custom_seek_bgw_atrac3(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;
@@ -309,7 +307,6 @@ static int64_t ffmpeg_size(ffmpeg_codec_data * data) {
     switch(data->config.type) {
         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_BGW_ATRAC3:     bytes = ffmpeg_custom_size_bgw_atrac3(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;
@@ -806,9 +803,6 @@ void free_ffmpeg(ffmpeg_codec_data *data) {
         close_streamfile(data->streamfile);
         data->streamfile = NULL;
     }
-    if (data->config.key) {
-        free(data->config.key);
-    }
     free(data);
 }
 
diff --git a/src/coding/ffmpeg_decoder_utils.h b/src/coding/ffmpeg_decoder_utils.h
index a7e12912..64461fe9 100644
--- a/src/coding/ffmpeg_decoder_utils.h
+++ b/src/coding/ffmpeg_decoder_utils.h
@@ -31,10 +31,6 @@ int ffmpeg_custom_read_switch_opus(ffmpeg_codec_data *data, uint8_t *buf, int bu
 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_bgw_atrac3(ffmpeg_codec_data *data, uint8_t *buf, int buf_size);
-int64_t ffmpeg_custom_seek_bgw_atrac3(ffmpeg_codec_data *data, int64_t virtual_offset);
-int64_t ffmpeg_custom_size_bgw_atrac3(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);
 //int64_t ffmpeg_custom_size_ea_schl(ffmpeg_codec_data *data);
diff --git a/src/coding/ffmpeg_decoder_utils_bgw_atrac3.c b/src/coding/ffmpeg_decoder_utils_bgw_atrac3.c
deleted file mode 100644
index 5b6bd441..00000000
--- a/src/coding/ffmpeg_decoder_utils_bgw_atrac3.c
+++ /dev/null
@@ -1,58 +0,0 @@
-#if 1
-#include "coding.h"
-#include "ffmpeg_decoder_utils.h"
-
-#ifdef VGM_USE_FFMPEG
-
-#define BGM_ATRAC3_FRAME_SIZE  0xC0
-
-/**
- * Encrypted ATRAC3 used in BGW (Final Fantasy XI PC).
- * Info from Moogle Toolbox: https://sourceforge.net/projects/mogbox/
- */
-
-int ffmpeg_custom_read_bgw_atrac3(ffmpeg_codec_data *data, uint8_t *buf, int buf_size) {
-    int i, ch;
-    size_t bytes;
-    size_t block_align = BGM_ATRAC3_FRAME_SIZE * data->config.channels;
-
-
-    /* init key: first frame + modified channel header */
-    if (data->config.key == NULL) {
-        data->config.key = malloc(block_align);
-        if (!data->config.key) return 0;
-
-        read_streamfile(data->config.key, data->real_start, block_align, data->streamfile);
-        for (ch = 0; ch < data->config.channels; ch++) {
-            uint32_t xor = get_32bitBE(data->config.key + ch*BGM_ATRAC3_FRAME_SIZE);
-            put_32bitBE(data->config.key + ch*BGM_ATRAC3_FRAME_SIZE, xor ^ 0xA0024E9F);
-        }
-    }
-
-
-    /* read normally and unXOR the data */
-    bytes = read_streamfile(buf, data->real_offset, buf_size, data->streamfile);
-    for (i = 0; i < bytes; i++) {
-        int key_pos = (data->real_offset - data->real_start + i) % block_align;
-        buf[i] = buf[i] ^ data->config.key[key_pos];
-    }
-
-
-    data->real_offset += bytes;
-    return bytes;
-}
-
-int64_t ffmpeg_custom_seek_bgw_atrac3(ffmpeg_codec_data *data, int64_t virtual_offset) {
-    int64_t seek_virtual_offset = virtual_offset - data->header_size;
-
-    data->real_offset = data->real_start + seek_virtual_offset;
-    return virtual_offset;
-}
-
-int64_t ffmpeg_custom_size_bgw_atrac3(ffmpeg_codec_data *data) {
-    return data->real_size + data->header_size;
-}
-
-
-#endif
-#endif
diff --git a/src/coding/vorbis_custom_utils_wwise.c b/src/coding/vorbis_custom_utils_wwise.c
index 7fba9417..73ee1508 100644
--- a/src/coding/vorbis_custom_utils_wwise.c
+++ b/src/coding/vorbis_custom_utils_wwise.c
@@ -47,7 +47,7 @@ int vorbis_custom_setup_init_wwise(STREAMFILE *streamFile, off_t start_offset, v
     size_t header_size, packet_size;
     vorbis_custom_config cfg = data->config;
 
-    if (cfg.setup_type == HEADER_TRIAD) {
+    if (cfg.setup_type == WWV_HEADER_TRIAD) {
         /* read 3 Wwise packets with triad (id/comment/setup), each with a Wwise header */
         off_t offset = start_offset;
 
@@ -132,17 +132,17 @@ static int get_packet_header(STREAMFILE *streamFile, off_t offset, wwise_header_
 
     /* packet size doesn't include header size */
     switch(header_type) {
-        case TYPE_8: /* size 4+4 */
+        case WWV_TYPE_8: /* size 4+4 */
             *packet_size = (uint32_t)read_32bit(offset, streamFile);
             *granulepos = read_32bit(offset+4, streamFile);
             return 8;
 
-        case TYPE_6: /* size 4+2 */
+        case WWV_TYPE_6: /* size 4+2 */
             *packet_size = (uint16_t)read_16bit(offset, streamFile);
             *granulepos = read_32bit(offset+2, streamFile);
             return 6;
 
-        case TYPE_2: /* size 2 */
+        case WWV_TYPE_2: /* size 2 */
             *packet_size = (uint16_t)read_16bit(offset, streamFile);
             *granulepos = 0; /* granule is an arbitrary unit so we could use offset instead; libvorbis has no actually need it actually */
             return 2;
@@ -306,7 +306,7 @@ static int ww2ogg_generate_vorbis_packet(vgm_bitstream * ow, vgm_bitstream * iw,
     //VGM_ASSERT(granule < 0, "Wwise Vorbis: negative granule %i @ 0x%lx\n", granule, offset);
 
 
-    if (data->config.packet_type == MODIFIED) {
+    if (data->config.packet_type == WWV_MODIFIED) {
         /* rebuild first bits of packet type and window info (for the i-MDCT) */
         uint32_t packet_type = 0, mode_number = 0, remainder = 0;
 
@@ -423,13 +423,13 @@ static int ww2ogg_generate_vorbis_setup(vgm_bitstream * ow, vgm_bitstream * iw,
     w_bits(ow,  8, codebook_count_less1);
     codebook_count = codebook_count_less1 + 1;
 
-    if (data->config.setup_type == FULL_SETUP) {
+    if (data->config.setup_type == WWV_FULL_SETUP) {
         /* rebuild Wwise codebooks: untouched */
         for (i = 0; i < codebook_count; i++) {
             if(!ww2ogg_codebook_library_copy(ow, iw)) goto fail;
         }
     }
-    else if (data->config.setup_type == INLINE_CODEBOOKS) {
+    else if (data->config.setup_type == WWV_INLINE_CODEBOOKS) {
         /* rebuild Wwise codebooks: inline in simplified format */
         for (i = 0; i < codebook_count; i++) {
             if(!ww2ogg_codebook_library_rebuild(ow, iw, 0, streamFile)) goto fail;
@@ -456,7 +456,7 @@ static int ww2ogg_generate_vorbis_setup(vgm_bitstream * ow, vgm_bitstream * iw,
     w_bits(ow, 16, dummy_time_value);
 
 
-    if (data->config.setup_type == FULL_SETUP) {
+    if (data->config.setup_type == WWV_FULL_SETUP) {
         /* rest of setup is untouched, copy bits */
         uint32_t bitly = 0;
         uint32_t total_bits_read = iw->b_off;
@@ -1174,11 +1174,11 @@ static int load_wvc_array(uint8_t * buf, size_t bufsize, uint32_t codebook_id, w
         const wvc_info * wvc_list;
 
         switch (setup_type) {
-            case EXTERNAL_CODEBOOKS:
+            case WWV_EXTERNAL_CODEBOOKS:
                 wvc_list = wvc_list_standard;
                 list_length = sizeof(wvc_list_standard) / sizeof(wvc_info);
                 break;
-            case AOTUV603_CODEBOOKS:
+            case WWV_AOTUV603_CODEBOOKS:
                 wvc_list = wvc_list_aotuv603;
                 list_length = sizeof(wvc_list_standard) / sizeof(wvc_info);
                 break;
diff --git a/src/formats.c b/src/formats.c
index f234fbf0..08b683cf 100644
--- a/src/formats.c
+++ b/src/formats.c
@@ -43,6 +43,8 @@ static const char* extension_list[] = {
     "ast",
     "at3",
     "at9",
+    "atsl3",
+    "atx",
     "aud",
     "aus",
     "awc",
@@ -173,6 +175,7 @@ static const char* extension_list[] = {
     "lstm", //fake extension, for STMs
     "lwav", //fake extension, for WAVs
 
+    "mab",
     "matx",
     "mc3",
     "mca",
@@ -263,6 +266,7 @@ static const char* extension_list[] = {
     "sb5",
     "sb6",
     "sb7",
+    "sbin",
     "sc",
     "scd",
     "sck",
@@ -377,6 +381,7 @@ static const char* extension_list[] = {
     "xvas",
     "xwav",
     "xwb",
+    "xwc",
     "xwm", //FFmpeg, not parsed (XWMA)
     "xwma", //FFmpeg, not parsed (XWMA)
     "xws",
@@ -474,7 +479,7 @@ static const coding_info coding_info_list[] = {
         {coding_APPLE_IMA4,         "Apple Quicktime 4-bit IMA ADPCM"},
         {coding_SNDS_IMA,           "Heavy Iron .snds 4-bit IMA ADPCM"},
         {coding_OTNS_IMA,           "Omikron: The Nomad Soul 4-bit IMA ADPCM"},
-        {coding_FSB_IMA,            "FSB multichannel 4-bit IMA ADPCM"},
+        {coding_FSB_IMA,            "FSB 4-bit IMA ADPCM"},
         {coding_WWISE_IMA,          "Audiokinetic Wwise 4-bit IMA ADPCM"},
         {coding_REF_IMA,            "Reflections 4-bit IMA ADPCM"},
         {coding_AWC_IMA,            "Rockstar AWC 4-bit IMA ADPCM"},
@@ -947,6 +952,9 @@ static const meta_info meta_info_list[] = {
         {meta_OGG_SNGW,             "Ogg Vorbis (Capcom)"},
         {meta_OGG_ISD,              "Ogg Vorbis (ISD)"},
         {meta_KMA9,                 "Koei Tecmo KMA9 header"},
+        {meta_XWC,                  "Starbreeze XWC header"},
+        {meta_SQEX_SAB,             "Square-Enix SAB header"},
+        {meta_SQEX_MAB,             "Square-Enix MAB header"},
 
 #ifdef VGM_USE_MP4V2
         {meta_MP4,                  "AAC header"},
diff --git a/src/layout/blocked_ea_schl.c b/src/layout/blocked_ea_schl.c
index 5e222339..e71844d0 100644
--- a/src/layout/blocked_ea_schl.c
+++ b/src/layout/blocked_ea_schl.c
@@ -95,6 +95,7 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) {
 
         /* id, size, samples, hists-per-channel, stereo/interleaved data */
         case coding_EA_XA:
+      //case coding_EA_XA_V2: /* handled in default */
         case coding_EA_XA_int:
             for (i = 0; i < vgmstream->channels; i++) {
                 int is_interleaved = vgmstream->coding_type == coding_EA_XA_int;
@@ -106,7 +107,9 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) {
 
                 /* the block can have padding so find the channel size from num_samples */
                 interleave = is_interleaved ? (block_samples / 28 * 0x0f) : 0;
-                vgmstream->ch[i].offset = block_offset + 0x0c + vgmstream->channels*0x04 + i*interleave;
+
+                /* NOT channels*0x04, as seen in Superbike 2000 (PC) EA-XA v1 mono vids */
+                vgmstream->ch[i].offset = block_offset + 0x0c + 2*0x04 + i*interleave;
             }
 
             break;
@@ -146,6 +149,7 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) {
             for (i = 0; i < vgmstream->channels; i++) {
                 off_t channel_start = read_32bit(block_offset + 0x0C + (0x04*i),streamFile);
                 vgmstream->ch[i].offset = block_offset + 0x0C + (0x04*vgmstream->channels) + channel_start;
+                VGM_LOG("ch=%x, off=%lx\n", i, vgmstream->ch[i].offset);
             }
 
             /* read ADPCM history before each channel if needed (not actually read in sx.exe) */
diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj
index c799eff3..6257180d 100644
--- a/src/libvgmstream.vcproj
+++ b/src/libvgmstream.vcproj
@@ -276,6 +276,14 @@
 					RelativePath=".\meta\ast.c"
 					>
 				</File>
+                <File
+                    RelativePath=".\meta\atsl3.c"
+                    >
+                </File>
+                <File
+                    RelativePath=".\meta\atx.c"
+                    >
+                </File>
 				<File
 					RelativePath=".\meta\awc.c"
 					>
@@ -1178,6 +1186,10 @@
 					RelativePath=".\meta\sli.c"
 					>
 				</File>
+                <File
+                    RelativePath=".\meta\sps_n1.c"
+                    >
+                </File>
 				<File
 					RelativePath=".\meta\spt_spd.c"
 					>
@@ -1186,6 +1198,10 @@
 					RelativePath=".\meta\sqex_scd.c"
 					>
 				</File>
+                <File
+                    RelativePath=".\meta\sqex_sead.c"
+                    >
+                </File>
                 <File
                     RelativePath=".\meta\stm.c"
                     >
@@ -1374,6 +1390,10 @@
 					RelativePath=".\meta\xwb.c"
 					>
 				</File>
+                <File
+                    RelativePath=".\meta\xwc.c"
+                    >
+                </File>
 				<File
 					RelativePath=".\meta\ydsp.c"
 					>
@@ -1470,10 +1490,6 @@
                     RelativePath=".\coding\ffmpeg_decoder_utils_ea_schl.c"
                     >
                 </File>
-                <File
-                    RelativePath=".\coding\ffmpeg_decoder_utils_bgw_atrac3.c"
-                    >
-                </File>
                 <File
                     RelativePath=".\coding\ffmpeg_decoder_utils_ea_xma.c"
                     >
diff --git a/src/libvgmstream.vcxproj b/src/libvgmstream.vcxproj
index d1387bd1..1941b101 100644
--- a/src/libvgmstream.vcxproj
+++ b/src/libvgmstream.vcxproj
@@ -112,7 +112,6 @@
     <ClCompile Include="coding\atrac9_decoder.c" />
     <ClCompile Include="coding\coding_utils.c" />
     <ClCompile Include="coding\ffmpeg_decoder.c" />
-    <ClCompile Include="coding\ffmpeg_decoder_utils_bgw_atrac3.c" />
     <ClCompile Include="coding\ffmpeg_decoder_utils_ea_xma.c" />
     <ClCompile Include="coding\ffmpeg_decoder_utils_switch_opus.c" />
     <ClCompile Include="coding\ffmpeg_decoder_utils.c" />
@@ -163,6 +162,7 @@
     <ClCompile Include="meta\vawx.c" />
     <ClCompile Include="meta\seg.c" />
     <ClCompile Include="meta\sqex_scd.c" />
+    <ClCompile Include="meta\sqex_sead.c" />
     <ClCompile Include="meta\tun.c" />
     <ClCompile Include="meta\txth.c" />
     <ClCompile Include="meta\wii_ras.c" />
@@ -186,6 +186,8 @@
     <ClCompile Include="meta\aix.c" />
     <ClCompile Include="meta\apple_caff.c" />
     <ClCompile Include="meta\ast.c" />
+    <ClCompile Include="meta\atsl3.c" />
+    <ClCompile Include="meta\atx.c" />
     <ClCompile Include="meta\baf.c" />
     <ClCompile Include="meta\bgw.c" />
     <ClCompile Include="meta\bik.c" />
@@ -376,6 +378,7 @@
     <ClCompile Include="meta\sdt.c" />
     <ClCompile Include="meta\sfl.c" />
     <ClCompile Include="meta\sli.c" />
+    <ClCompile Include="meta\sps_n1.c" />
     <ClCompile Include="meta\spt_spd.c" />
     <ClCompile Include="meta\stm.c" />
     <ClCompile Include="meta\str_asr.c" />
@@ -417,6 +420,7 @@
     <ClCompile Include="meta\xss.c" />
     <ClCompile Include="meta\xvag.c" />
     <ClCompile Include="meta\xwb.c" />
+    <ClCompile Include="meta\xwc.c" />
     <ClCompile Include="meta\ydsp.c" />
     <ClCompile Include="meta\zsd.c" />
     <ClCompile Include="meta\zwdsp.c" />
diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters
index c309ea96..c344eb98 100644
--- a/src/libvgmstream.vcxproj.filters
+++ b/src/libvgmstream.vcxproj.filters
@@ -169,6 +169,12 @@
     <ClCompile Include="meta\ast.c">
       <Filter>meta\Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="meta\atsl3.c">
+      <Filter>meta\Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="meta\atx.c">
+      <Filter>meta\Source Files</Filter>
+    </ClCompile>
     <ClCompile Include="meta\baf.c">
       <Filter>meta\Source Files</Filter>
     </ClCompile>
@@ -718,6 +724,9 @@
     <ClCompile Include="meta\sli.c">
       <Filter>meta\Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="meta\sps_n1.c">
+      <Filter>meta\Source Files</Filter>
+    </ClCompile>
     <ClCompile Include="meta\spt_spd.c">
       <Filter>meta\Source Files</Filter>
     </ClCompile>
@@ -835,6 +844,9 @@
     <ClCompile Include="meta\xwb.c">
       <Filter>meta\Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="meta\xwc.c">
+      <Filter>meta\Source Files</Filter>
+    </ClCompile>
     <ClCompile Include="meta\ydsp.c">
       <Filter>meta\Source Files</Filter>
     </ClCompile>
@@ -1117,6 +1129,9 @@
     <ClCompile Include="meta\sqex_scd.c">
       <Filter>meta\Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="meta\sqex_sead.c">
+      <Filter>meta\Source Files</Filter>
+    </ClCompile>
     <ClCompile Include="meta\seg.c">
       <Filter>meta\Source Files</Filter>
     </ClCompile>
@@ -1213,9 +1228,6 @@
     <ClCompile Include="coding\ffmpeg_decoder.c">
       <Filter>coding\Source Files</Filter>
     </ClCompile>
-    <ClCompile Include="coding\ffmpeg_decoder_utils_bgw_atrac3.c">
-      <Filter>coding\Source Files</Filter>
-    </ClCompile>
     <ClCompile Include="coding\ffmpeg_decoder_utils_ea_xma.c">
       <Filter>coding\Source Files</Filter>
     </ClCompile>
diff --git a/src/meta/adx_keys.h b/src/meta/adx_keys.h
index 4c047f89..9d5b750e 100644
--- a/src/meta/adx_keys.h
+++ b/src/meta/adx_keys.h
@@ -177,7 +177,7 @@ static const adxkey_info adxkey8_list[] = {
         {0x4133,0x5a01,0x5723, NULL,0},     // ?
 
         /* Shounen Onmyouji: Tsubasa yo Ima, Sora e Kaere [PS2] */
-        {0x55d9,0x46d3,0x5b01, NULL,0},     // ?
+        {0x55d9,0x46d3,0x5b01, "SONMYOJI",0},
 
 };
 
diff --git a/src/meta/atsl3.c b/src/meta/atsl3.c
new file mode 100644
index 00000000..722c3f77
--- /dev/null
+++ b/src/meta/atsl3.c
@@ -0,0 +1,76 @@
+#include "meta.h"
+#include "../coding/coding.h"
+
+static STREAMFILE* setup_atsl3_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size);
+
+/* .ATSL3 - Koei Tecmo container of multiple .AT3 [One Piece Pirate Warriors (PS3)] */
+VGMSTREAM * init_vgmstream_atsl3(STREAMFILE *streamFile) {
+    VGMSTREAM *vgmstream = NULL;
+    STREAMFILE *temp_streamFile = NULL;
+    int total_subsongs, target_subsong = streamFile->stream_index;
+    off_t subfile_offset;
+    size_t subfile_size, header_size, entry_size;
+
+    /* check extensions */
+    if ( !check_extensions(streamFile,"atsl3"))
+        goto fail;
+    if (read_32bitBE(0x00,streamFile) != 0x4154534C) /* "ATSL" */
+        goto fail;
+
+    /* main header (LE) */
+    header_size = read_32bitLE(0x04,streamFile);
+    /* 0x08/0c: flags?, 0x10: some size? */
+    total_subsongs = read_32bitLE(0x14,streamFile);
+    entry_size = read_32bitLE(0x18,streamFile);
+    /* 0x1c: null, 0x20: subheader size, 0x24/28: null */
+
+    if (target_subsong == 0) target_subsong = 1;
+    if (target_subsong > total_subsongs || total_subsongs <= 0) goto fail;
+
+    /* entry header (BE) */
+    /* 0x00: id */
+    subfile_offset = read_32bitBE(header_size + (target_subsong-1)*entry_size + 0x04,streamFile);
+    subfile_size   = read_32bitBE(header_size + (target_subsong-1)*entry_size + 0x08,streamFile);
+    /* 0x08+: sample rate/num_samples/loop_start/etc, matching subfile header */
+    /* some kind of seek/switch table follows */
+
+    temp_streamFile = setup_atsl3_streamfile(streamFile, subfile_offset,subfile_size);
+    if (!temp_streamFile) goto fail;
+
+    /* init the VGMSTREAM */
+    vgmstream = init_vgmstream_riff(temp_streamFile);
+    if (!vgmstream) goto fail;
+    vgmstream->num_streams = total_subsongs;
+
+    close_streamfile(temp_streamFile);
+    return vgmstream;
+
+fail:
+    close_streamfile(temp_streamFile);
+    close_vgmstream(vgmstream);
+    return NULL;
+}
+
+
+static STREAMFILE* setup_atsl3_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size) {
+    STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
+
+    /* setup subfile */
+    new_streamFile = open_wrap_streamfile(streamFile);
+    if (!new_streamFile) goto fail;
+    temp_streamFile = new_streamFile;
+
+    new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size);
+    if (!new_streamFile) goto fail;
+    temp_streamFile = new_streamFile;
+
+    new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,"at3");
+    if (!new_streamFile) goto fail;
+    temp_streamFile = new_streamFile;
+
+    return temp_streamFile;
+
+fail:
+    close_streamfile(temp_streamFile);
+    return NULL;
+}
diff --git a/src/meta/atx.c b/src/meta/atx.c
new file mode 100644
index 00000000..a58a8833
--- /dev/null
+++ b/src/meta/atx.c
@@ -0,0 +1,109 @@
+#include "meta.h"
+#include "../coding/coding.h"
+
+#define ATX_MAX_SEGMENTS  2
+
+static STREAMFILE* setup_atx_streamfile(STREAMFILE *streamFile);
+
+/* .ATX - Media.Vision's segmented RIFF AT3 wrapper [Senjo no Valkyria 3 (PSP), Shining Blade (PSP)] */
+VGMSTREAM * init_vgmstream_atx(STREAMFILE *streamFile) {
+    VGMSTREAM *vgmstream = NULL;
+    STREAMFILE *temp_streamFile = NULL;
+
+
+    /* check extensions */
+    if ( !check_extensions(streamFile,"atx"))
+        goto fail;
+    if (read_32bitBE(0x00,streamFile) != 0x41504133) /* "APA3" */
+        goto fail;
+
+    /* .ATX is made of subfile segments, handled by the streamFile.
+     * Each segment has a header/footer, and part of the whole data
+     * (i.e. ATRAC3 data ends in a subfile and continues in the next) */
+    temp_streamFile = setup_atx_streamfile(streamFile);
+    if (!temp_streamFile) goto fail;
+
+    vgmstream = init_vgmstream_riff(temp_streamFile);
+    if (!vgmstream) goto fail;
+
+    close_streamfile(temp_streamFile);
+    return vgmstream;
+
+fail:
+    close_streamfile(temp_streamFile);
+    close_vgmstream(vgmstream);
+    return NULL;
+}
+
+static STREAMFILE* setup_atx_streamfile(STREAMFILE *streamFile) {
+    STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
+    STREAMFILE *segment_streamFiles[ATX_MAX_SEGMENTS] = {0};
+    char filename[PATH_LIMIT];
+    size_t filename_len;
+    int i, num_segments = 0;
+    size_t riff_size;
+VGM_LOG("1\n");
+
+    if (read_16bitLE(0x1c,streamFile) != 0) goto fail; /* this must be first segment */
+    if (read_16bitLE(0x1e,streamFile) < 1 || read_16bitLE(0x1e,streamFile) > ATX_MAX_SEGMENTS) goto fail;
+    num_segments = read_16bitLE(0x1e,streamFile);
+
+    /* expected segment name: X_XXX_XXX_0n.ATX, starting from n=1 */
+    get_streamfile_name(streamFile, filename,PATH_LIMIT);
+    filename_len = strlen(filename);
+    if (filename_len < 7 || filename[filename_len - 5] != '1') goto fail;
+
+    /* setup segments (could avoid reopening first segment but meh) */
+    for (i = 0; i < num_segments; i++) {
+        off_t subfile_offset;
+        size_t subfile_size;
+VGM_LOG("loop\n");
+        filename[filename_len - 5] = ('0'+i+1); /* ghetto digit conversion */
+        new_streamFile = open_stream_name(streamFile, filename);
+        if (!new_streamFile) goto fail;
+        segment_streamFiles[i] = new_streamFile;
+
+        if (read_32bitBE(0x00,segment_streamFiles[i]) != 0x41504133) /* "APA3" */
+            goto fail;
+
+        /* parse block/segment header (other Media.Vision's files use it too) */
+        subfile_offset = read_32bitLE(0x08,segment_streamFiles[i]); /* header size */
+        subfile_size = read_32bitLE(0x14,segment_streamFiles[i]); /* can be 0 in other containers */
+VGM_LOG("subfile: %lx, %x\n", subfile_offset, subfile_size);
+        if (read_16bitLE(0x1c,segment_streamFiles[i]) != i)
+            goto fail; /* segment sequence */
+        /* 0x04: block size (should match subfile_size in .ATX) */
+        /* 0x0c: flags? also in other files, 0x10/18: null, 0x1e: segments */
+
+        /* clamp to ignore header/footer during next reads */
+        new_streamFile = open_clamp_streamfile(segment_streamFiles[i], subfile_offset,subfile_size);
+        if (!new_streamFile) goto fail;
+        segment_streamFiles[i] = new_streamFile;
+    }
+
+    /* setup with all segments and clamp further using riff_size (last segment has padding) */
+    riff_size = read_32bitLE(read_32bitLE(0x08,streamFile) + 0x04,streamFile) + 0x08;
+
+    new_streamFile = open_multifile_streamfile(segment_streamFiles, num_segments);
+    if (!new_streamFile) goto fail;
+    temp_streamFile = new_streamFile;
+
+    new_streamFile = open_clamp_streamfile(temp_streamFile, 0,riff_size);
+    if (!new_streamFile) goto fail;
+    temp_streamFile = new_streamFile;
+
+    new_streamFile = open_fakename_streamfile(temp_streamFile, NULL, "at3");
+    if (!new_streamFile) goto fail;
+    temp_streamFile = new_streamFile;
+
+    return temp_streamFile;
+
+fail:
+    if (!temp_streamFile) {
+        for (i = 0; i < num_segments; i++)
+            close_streamfile(segment_streamFiles[i]);
+    } else {
+        close_streamfile(temp_streamFile); /* closes all segments */
+    }
+    return NULL;
+}
diff --git a/src/meta/awc.c b/src/meta/awc.c
index ab2a605b..a102d1b4 100644
--- a/src/meta/awc.c
+++ b/src/meta/awc.c
@@ -7,7 +7,7 @@ typedef struct {
     int is_encrypted;
     int is_music;
 
-    int total_streams;
+    int total_subsongs;
 
     int channel_count;
     int sample_rate;
@@ -47,7 +47,8 @@ VGMSTREAM * init_vgmstream_awc(STREAMFILE *streamFile) {
 
     vgmstream->sample_rate = awc.sample_rate;
     vgmstream->num_samples = awc.num_samples;
-    vgmstream->num_streams = awc.total_streams;
+    vgmstream->num_streams = awc.total_subsongs;
+    vgmstream->stream_size = awc.stream_size;
     vgmstream->meta_type = meta_AWC;
 
 
@@ -113,7 +114,7 @@ static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc) {
     int i, ch, entries;
     uint32_t flags, info_header, tag_count = 0, tags_skip = 0;
     off_t off;
-    int target_stream = streamFile->stream_index;
+    int target_subsong = streamFile->stream_index;
 
     memset(awc,0,sizeof(awc_header));
 
@@ -161,13 +162,13 @@ static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc) {
      * Music seems layered (N-1/2 stereo pairs), maybe set with events? */
     awc->is_music = (read_32bit(off + 0x00,streamFile) & 0x1FFFFFFF) == 0x00000000;
     if (awc->is_music) { /* all streams except id 0 is a channel */
-        awc->total_streams = 1;
-        target_stream = 1; /* we only need id 0, though channels may have its own tags/chunks */
+        awc->total_subsongs = 1;
+        target_subsong = 1; /* we only need id 0, though channels may have its own tags/chunks */
     }
     else { /* each stream is a single sound */
-        awc->total_streams = entries;
-        if (target_stream == 0) target_stream = 1;
-        if (target_stream < 0 || target_stream > awc->total_streams || awc->total_streams < 1) goto fail;
+        awc->total_subsongs = entries;
+        if (target_subsong == 0) target_subsong = 1;
+        if (target_subsong < 0 || target_subsong > awc->total_subsongs || awc->total_subsongs < 1) goto fail;
     }
 
 
@@ -176,7 +177,7 @@ static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc) {
         info_header = read_32bit(off + 0x04*i, streamFile);
         tag_count   = (info_header >> 29) & 0x7; /* 3b */
         //id        = (info_header >>  0) & 0x1FFFFFFF; /* 29b */
-        if (target_stream-1 == i)
+        if (target_subsong-1 == i)
             break;
         tags_skip += tag_count; /* tags to skip to reach target's tags, in the next header */
     }
diff --git a/src/meta/bgw.c b/src/meta/bgw.c
index f95ac994..0be6ebab 100644
--- a/src/meta/bgw.c
+++ b/src/meta/bgw.c
@@ -1,9 +1,14 @@
 #include "meta.h"
 #include "../coding/coding.h"
 
+
+static STREAMFILE* setup_bgw_atrac3_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, size_t frame_size, int channels);
+
+
 /* BGW - from Final Fantasy XI (PC) music files */
 VGMSTREAM * init_vgmstream_bgw(STREAMFILE *streamFile) {
     VGMSTREAM * vgmstream = NULL;
+    STREAMFILE *temp_streamFile = NULL;
     uint32_t codec, file_size, block_size, sample_rate, block_align;
     int32_t loop_start;
     off_t start_offset;
@@ -32,18 +37,15 @@ VGMSTREAM * init_vgmstream_bgw(STREAMFILE *streamFile) {
     channel_count = read_8bit(0x2e,streamFile);
     block_align = read_8bit(0x2f,streamFile);
 
-
-    /* check file size with header value */
     if (file_size != get_streamfile_size(streamFile))
         goto fail;
 
     loop_flag = (loop_start > 0);
 
-	/* build the VGMSTREAM */
+    /* build the VGMSTREAM */
     vgmstream = allocate_vgmstream(channel_count,loop_flag);
     if (!vgmstream) goto fail;
 
-	/* fill in the vital statistics */
     vgmstream->meta_type = meta_FFXI_BGW;
     vgmstream->sample_rate = sample_rate;
 
@@ -65,7 +67,7 @@ VGMSTREAM * init_vgmstream_bgw(STREAMFILE *streamFile) {
         case 3: { /* ATRAC3 (encrypted) */
             uint8_t buf[0x100];
             int bytes, joint_stereo, skip_samples;
-            ffmpeg_custom_config cfg;
+            size_t data_size = file_size - start_offset;
 
             vgmstream->num_samples = block_size; /* atrac3_bytes_to_samples gives the same value */
             if (loop_flag) {
@@ -77,18 +79,18 @@ VGMSTREAM * init_vgmstream_bgw(STREAMFILE *streamFile) {
             joint_stereo = 0;
             skip_samples = 0;
 
-            bytes = ffmpeg_make_riff_atrac3(buf, 0x100, vgmstream->num_samples, file_size - start_offset, vgmstream->channels, vgmstream->sample_rate, block_align, joint_stereo, skip_samples);
+            bytes = ffmpeg_make_riff_atrac3(buf, 0x100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_align, joint_stereo, skip_samples);
             if (bytes <= 0) goto fail;
 
-            memset(&cfg, 0, sizeof(ffmpeg_custom_config));
-            cfg.type = FFMPEG_BGW_ATRAC3;
-            cfg.channels = vgmstream->channels;
+            temp_streamFile = setup_bgw_atrac3_streamfile(streamFile, start_offset,data_size, 0xC0,channel_count);
+            if (!temp_streamFile) goto fail;
 
-            vgmstream->codec_data = init_ffmpeg_config(streamFile, buf,bytes, start_offset,file_size - start_offset, &cfg);
+            vgmstream->codec_data = init_ffmpeg_header_offset(temp_streamFile, buf,bytes, 0,data_size);
             if (!vgmstream->codec_data) goto fail;
-
             vgmstream->coding_type = coding_FFmpeg;
             vgmstream->layout_type = layout_none;
+
+            close_streamfile(temp_streamFile);
             break;
         }
 #endif
@@ -98,17 +100,17 @@ VGMSTREAM * init_vgmstream_bgw(STREAMFILE *streamFile) {
     }
 
 
-    /* open the file for reading */
     if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
         goto fail;
-
     return vgmstream;
 
 fail:
+    close_streamfile(temp_streamFile);
     close_vgmstream(vgmstream);
     return NULL;
 }
 
+
 /* SPW (SEWave) - from  PlayOnline viewer for Final Fantasy XI (PC) */
 VGMSTREAM * init_vgmstream_spw(STREAMFILE *streamFile) {
     VGMSTREAM * vgmstream = NULL;
@@ -118,8 +120,8 @@ VGMSTREAM * init_vgmstream_spw(STREAMFILE *streamFile) {
 
     int channel_count, loop_flag = 0;
 
-	/* check extensions */
-	if ( !check_extensions(streamFile, "spw") )
+    /* check extensions */
+    if ( !check_extensions(streamFile, "spw") )
         goto fail;
 
     /* check header */
@@ -127,10 +129,6 @@ VGMSTREAM * init_vgmstream_spw(STREAMFILE *streamFile) {
         read_32bitBE(4,streamFile) != 0x76650000)   /* "ve\0\0" */
         goto fail;
 
-    /* check file size with header value */
-    if (read_32bitLE(0x8,streamFile) != get_streamfile_size(streamFile))
-        goto fail;
-
     file_size = read_32bitLE(0x08,streamFile);
     codec = read_32bitLE(0x0c,streamFile);
     /*file_id = read_32bitLE(0x10,streamFile);*/
@@ -144,17 +142,15 @@ VGMSTREAM * init_vgmstream_spw(STREAMFILE *streamFile) {
     block_align = read_8bit(0x2b,streamFile);
     /*0x2c: unk (0x01 when PCM, 0x10 when VAG?) */
 
-    /* check file size with header value */
     if (file_size != get_streamfile_size(streamFile))
         goto fail;
 
     loop_flag = (loop_start > 0);
 
-	/* build the VGMSTREAM */
+    /* build the VGMSTREAM */
     vgmstream = allocate_vgmstream(channel_count,loop_flag);
     if (!vgmstream) goto fail;
 
-	/* fill in the vital statistics */
     vgmstream->meta_type = meta_FFXI_SPW;
     vgmstream->sample_rate = sample_rate;
 
@@ -171,7 +167,7 @@ VGMSTREAM * init_vgmstream_spw(STREAMFILE *streamFile) {
             }
             
             break;
-            
+
         case 1: /* PCM */
             vgmstream->coding_type = coding_PCM16LE;
             vgmstream->layout_type = layout_interleave;
@@ -182,8 +178,9 @@ VGMSTREAM * init_vgmstream_spw(STREAMFILE *streamFile) {
                 vgmstream->loop_start_sample = (loop_start-1);
                 vgmstream->loop_end_sample = vgmstream->num_samples;
             }
-            
+
             break;
+
         default:
             goto fail;
     }
@@ -199,3 +196,61 @@ fail:
     close_vgmstream(vgmstream);
     return NULL;
 }
+
+
+#define BGW_KEY_MAX (0xC0*2)
+
+typedef struct {
+    uint8_t key[BGW_KEY_MAX];
+    size_t key_size;
+} bgw_decryption_data;
+
+/* Encrypted ATRAC3 info from Moogle Toolbox (https://sourceforge.net/projects/mogbox/) */
+static size_t bgw_decryption_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, bgw_decryption_data* data) {
+    size_t bytes_read;
+    int i;
+
+    bytes_read = streamfile->read(streamfile, dest, offset, length);
+
+    /* decrypt data (xor) */
+    for (i = 0; i < bytes_read; i++) {
+        dest[i] ^= data->key[(offset + i) % data->key_size];
+    }
+
+    return bytes_read;
+}
+
+static STREAMFILE* setup_bgw_atrac3_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, size_t frame_size, int channels) {
+    STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
+    bgw_decryption_data io_data = {0};
+    size_t io_data_size = sizeof(bgw_decryption_data);
+    int ch;
+
+    /* setup decryption with key (first frame + modified channel header) */
+    if (frame_size*channels == 0 || frame_size*channels > BGW_KEY_MAX) goto fail;
+
+    io_data.key_size = read_streamfile(io_data.key, subfile_offset, frame_size*channels, streamFile);
+    for (ch = 0; ch < channels; ch++) {
+        uint32_t xor = get_32bitBE(io_data.key + frame_size*ch);
+        put_32bitBE(io_data.key + frame_size*ch, xor ^ 0xA0024E9F);
+    }
+
+    /* setup subfile */
+    new_streamFile = open_wrap_streamfile(streamFile);
+    if (!new_streamFile) goto fail;
+    temp_streamFile = new_streamFile;
+
+    new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size);
+    if (!new_streamFile) goto fail;
+    temp_streamFile = new_streamFile;
+
+    new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, bgw_decryption_read);
+    if (!new_streamFile) goto fail;
+    temp_streamFile = new_streamFile;
+
+    return temp_streamFile;
+
+fail:
+    close_streamfile(temp_streamFile);
+    return NULL;
+}
diff --git a/src/meta/bik.c b/src/meta/bik.c
index f703b2a6..3e359576 100644
--- a/src/meta/bik.c
+++ b/src/meta/bik.c
@@ -2,13 +2,14 @@
 #include "../coding/coding.h"
 #include "../util.h"
 
-static int bink_get_info(STREAMFILE *streamFile, int * out_total_streams, int * out_channel_count, int * out_sample_rate, int * out_num_samples);
+static int bink_get_info(STREAMFILE *streamFile, int * out_total_streams, size_t *out_stream_size, int * out_channel_count, int * out_sample_rate, int * out_num_samples);
 
 /* BINK 1/2 - RAD Game Tools movies (audio/video format) */
 VGMSTREAM * init_vgmstream_bik(STREAMFILE *streamFile) {
     VGMSTREAM * vgmstream = NULL;
-    int channel_count = 0, loop_flag = 0, sample_rate = 0, num_samples = 0, total_streams = 0;
-    int stream_index = streamFile->stream_index;
+    int channel_count = 0, loop_flag = 0, sample_rate = 0, num_samples = 0;
+    int total_subsongs = 0, stream_index = streamFile->stream_index;
+    size_t stream_size;
 
 
     /* check extension, case insensitive (bika = manually demuxed audio) */
@@ -19,7 +20,7 @@ VGMSTREAM * init_vgmstream_bik(STREAMFILE *streamFile) {
         (read_32bitBE(0x00,streamFile) & 0xffffff00) != 0x4B423200 ) goto fail;
 
     /* find target stream info and samples */
-    if (!bink_get_info(streamFile, &total_streams, &channel_count, &sample_rate, &num_samples))
+    if (!bink_get_info(streamFile, &total_subsongs, &stream_size, &channel_count, &sample_rate, &num_samples))
         goto fail;
 
     /* build the VGMSTREAM */
@@ -29,7 +30,8 @@ VGMSTREAM * init_vgmstream_bik(STREAMFILE *streamFile) {
     vgmstream->layout_type = layout_none;
     vgmstream->sample_rate = sample_rate;
     vgmstream->num_samples = num_samples;
-    vgmstream->num_streams = total_streams;
+    vgmstream->num_streams = total_subsongs;
+    vgmstream->stream_size = stream_size;
     vgmstream->meta_type = meta_BINK;
 
 #ifdef VGM_USE_FFMPEG
@@ -58,12 +60,13 @@ fail:
  * as they are not in the main header. The header for BINK1 and 2 is the same.
  * (a ~3 min movie needs ~6000-7000 frames = fseeks, should be fast enough)
  */
-static int bink_get_info(STREAMFILE *streamFile, int * out_total_streams, int * out_channel_count, int * out_sample_rate, int * out_num_samples) {
+static int bink_get_info(STREAMFILE *streamFile, int * out_total_subsongs, size_t * out_stream_size, int * out_channel_count, int * out_sample_rate, int * out_num_samples) {
     uint32_t *offsets = NULL;
     uint32_t num_frames, num_samples_b = 0;
     off_t cur_offset;
     int i, j, sample_rate, channel_count;
-    int total_streams, target_stream = streamFile->stream_index;
+    int total_subsongs, target_subsong = streamFile->stream_index;
+    size_t stream_size = 0;
 
     size_t filesize = get_streamfile_size(streamFile);
     uint32_t signature = (read_32bitBE(0x00,streamFile) & 0xffffff00);
@@ -76,20 +79,20 @@ static int bink_get_info(STREAMFILE *streamFile, int * out_total_streams, int *
     if (num_frames == 0 || num_frames > 0x100000) goto fail; /* something must be off (avoids big allocs below) */
 
     /* multichannel/multilanguage audio is usually N streams of stereo/mono, no way to know channel layout */
-    total_streams = read_32bitLE(0x28,streamFile);
-    if (target_stream == 0) target_stream = 1;
-    if (target_stream < 0 || target_stream > total_streams || total_streams < 1 || total_streams > 255) goto fail;
+    total_subsongs = read_32bitLE(0x28,streamFile);
+    if (target_subsong == 0) target_subsong = 1;
+    if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1 || total_subsongs > 255) goto fail;
 
     /* find stream info and position in offset table */
     cur_offset = 0x2c;
     if ((signature == 0x42494B00 && (revision == 0x6b)) || /* k */
         (signature == 0x4B423200 && (revision == 0x69 || revision == 0x6a || revision == 0x6b))) /* i,j,k */
         cur_offset += 0x04; /* unknown v2 header field */
-    cur_offset += 0x04*total_streams; /* skip streams max packet bytes */
-    sample_rate   = (uint16_t)read_16bitLE(cur_offset+0x04*(target_stream-1)+0x00,streamFile);
-    channel_count = (uint16_t)read_16bitLE(cur_offset+0x04*(target_stream-1)+0x02,streamFile) & 0x2000 ? 2 : 1; /* stereo flag */
-    cur_offset += 0x04*total_streams; /* skip streams info */
-    cur_offset += 0x04*total_streams; /* skip streams ids */
+    cur_offset += 0x04*total_subsongs; /* skip streams max packet bytes */
+    sample_rate   = (uint16_t)read_16bitLE(cur_offset+0x04*(target_subsong-1)+0x00,streamFile);
+    channel_count = (uint16_t)read_16bitLE(cur_offset+0x04*(target_subsong-1)+0x02,streamFile) & 0x2000 ? 2 : 1; /* stereo flag */
+    cur_offset += 0x04*total_subsongs; /* skip streams info */
+    cur_offset += 0x04*total_subsongs; /* skip streams ids */
 
 
     /* read frame offsets in a buffer, to avoid fseeking to the table back and forth */
@@ -111,10 +114,11 @@ static int bink_get_info(STREAMFILE *streamFile, int * out_total_streams, int *
         cur_offset = offsets[i];
 
         /* read audio packet headers per stream */
-        for (j=0; j < total_streams; j++) {
+        for (j=0; j < total_subsongs; j++) {
             uint32_t ap_size = read_32bitLE(cur_offset+0x00,streamFile); /* not counting this int */
 
-            if (j == target_stream-1) {
+            if (j == target_subsong-1) {
+                stream_size += 0x04 + ap_size;
                 if (ap_size > 0)
                     num_samples_b += read_32bitLE(cur_offset+0x04,streamFile); /* decoded samples in bytes */
                 break; /* next frame */
@@ -128,7 +132,8 @@ static int bink_get_info(STREAMFILE *streamFile, int * out_total_streams, int *
     free(offsets);
 
 
-    if (out_total_streams)  *out_total_streams = total_streams;
+    if (out_total_subsongs) *out_total_subsongs = total_subsongs;
+    if (out_stream_size)    *out_stream_size = stream_size;
     if (out_sample_rate)    *out_sample_rate = sample_rate;
     if (out_channel_count)  *out_channel_count = channel_count;
     //todo returns a few more samples (~48) than binkconv.exe?
diff --git a/src/meta/flx.c b/src/meta/flx.c
index ab28a322..d37762e8 100644
--- a/src/meta/flx.c
+++ b/src/meta/flx.c
@@ -7,7 +7,8 @@ VGMSTREAM * init_vgmstream_flx(STREAMFILE *streamFile) {
     off_t start_offset, stream_offset = 0;
     size_t data_size;
     int loop_flag, channel_count, codec;
-    int total_streams = 0, target_stream = streamFile->stream_index;
+    int total_subsongs = 0, target_subsong = streamFile->stream_index;
+    size_t stream_size = 0;
 
 
     /* check extensions (.flx: name of archive, files inside don't have extensions) */
@@ -24,23 +25,26 @@ VGMSTREAM * init_vgmstream_flx(STREAMFILE *streamFile) {
                 || read_32bitLE(0x58,streamFile) != get_streamfile_size(streamFile))
             goto fail;
 
-        if (target_stream == 0) target_stream = 1;
+        if (target_subsong == 0) target_subsong = 1;
 
         for (i = 0; i < entries; i++) {
             off_t entry_offset = read_32bitLE(offset + 0x00, streamFile);
-            /* 0x04: stream size */
+            size_t entry_size = read_32bitLE(offset + 0x04, streamFile);
             offset += 0x08;
 
             if (entry_offset != 0x00)
-                total_streams++; /* many entries are empty */
-            if (total_streams == target_stream && stream_offset == 0)
+                total_subsongs++; /* many entries are empty */
+            if (total_subsongs == target_subsong && stream_offset == 0) {
                 stream_offset = entry_offset; /* found but let's keep adding total_streams */
+                stream_size = entry_size;
+            }
         }
-        if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail;
+        if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
         if (stream_offset == 0x00) goto fail;
     }
     else {
         stream_offset = 0x00;
+        stream_size = get_streamfile_size(streamFile);
     }
 
     if (read_32bitLE(stream_offset + 0x30,streamFile) != 0x10)
@@ -57,7 +61,8 @@ VGMSTREAM * init_vgmstream_flx(STREAMFILE *streamFile) {
     if (!vgmstream) goto fail;
 
     vgmstream->sample_rate = read_32bitLE(stream_offset + 0x2c,streamFile);
-    vgmstream->num_streams = total_streams;
+    vgmstream->num_streams = total_subsongs;
+    vgmstream->stream_size = stream_size;
     vgmstream->meta_type = meta_PC_FLX;
 
     switch(codec) {
diff --git a/src/meta/fsb.c b/src/meta/fsb.c
index 85e09ae6..6a6bbf53 100644
--- a/src/meta/fsb.c
+++ b/src/meta/fsb.c
@@ -100,12 +100,12 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) {
     off_t start_offset;
     size_t custom_data_offset;
     int loop_flag = 0;
-    int target_stream = streamFile->stream_index;
+    int target_subsong = streamFile->stream_index;
     fsb_header fsb = {0};
 
 
-    /* check extensions (.wii: fsb4_wav? .bnk = Hard Corps Uprising PS3) */
-    if ( !check_extensions(streamFile, "fsb,wii,bnk") )
+    /* check extensions (.bnk = Hard Corps Uprising PS3) */
+    if ( !check_extensions(streamFile, "fsb,bnk") )
         goto fail;
 
     /* check header */
@@ -185,8 +185,8 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) {
         }
 
         if (fsb.sample_header_size < fsb.sample_header_min) goto fail;
-        if (target_stream == 0) target_stream = 1;
-        if (target_stream < 0 || target_stream > fsb.total_subsongs || fsb.total_subsongs < 1) goto fail;
+        if (target_subsong == 0) target_subsong = 1;
+        if (target_subsong < 0 || target_subsong > fsb.total_subsongs || fsb.total_subsongs < 1) goto fail;
 
         /* sample header (N-stream) */
         {
@@ -210,7 +210,7 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) {
                 /* FSB3.1/4: 0x40:mindistance  0x44:maxdistance  0x48:varfreq/size_32bits  0x4c:varvol  0x4e:fsb.varpan */
                 /* FSB3/4: 0x50:extended_data size_32bits (not always given) */
 
-                if (i+1 == target_stream) /* d_off found */
+                if (i+1 == target_subsong) /* d_off found */
                     break;
 
                 s_off += stream_header_size;
@@ -259,6 +259,7 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) {
     vgmstream->loop_start_sample = fsb.loop_start;
     vgmstream->loop_end_sample = fsb.loop_end;
     vgmstream->num_streams = fsb.total_subsongs;
+    vgmstream->stream_size = fsb.stream_size;
     vgmstream->meta_type = fsb.meta_type;
     if (fsb.name_offset)
         read_string(vgmstream->stream_name,fsb.name_size+1, fsb.name_offset,streamFile);
@@ -289,8 +290,9 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) {
 
         vgmstream->coding_type = coding_XBOX;
         vgmstream->layout_type = layout_none;
-        /* "interleaved header" IMA, only used with >2ch (ex. Blade Kitten 5.1) */
-        if (vgmstream->channels > 2)
+        /* "interleaved header" IMA, only used with >2ch (ex. Blade Kitten 6ch)
+         * or (seemingly) when flag is used (ex. Dead to Rights 2 (Xbox) 2ch in FSB3.1 */
+        if (vgmstream->channels > 2 || (fsb.mode & FSOUND_MULTICHANNEL))
             vgmstream->coding_type = coding_FSB_IMA;
     }
     else if (fsb.mode & FSOUND_VAG) { /* FSB1: Jurassic Park Operation Genesis (PS2), FSB4: Spider Man Web of Shadows (PSP) */
@@ -363,13 +365,15 @@ fail:
 }
 
 
+static STREAMFILE* setup_fsb4_wav_streamfile(STREAMFILE *streamfile, off_t subfile_offset, size_t subfile_size);
+
 /* FSB4 with "\0WAV" Header, found in Deadly Creatures (Wii).
  * Has a 0x10 BE header that holds the filesize (unsure if this is from a proper rip). */
 VGMSTREAM * init_vgmstream_fsb4_wav(STREAMFILE *streamFile) {
     VGMSTREAM * vgmstream = NULL;
-    STREAMFILE *custom_streamFile = NULL;
-    off_t custom_start = 0x10;
-    size_t custom_size = get_streamfile_size(streamFile) - 0x10 - 0x10; //todo
+    STREAMFILE *test_streamFile = NULL;
+    off_t subfile_start = 0x10;
+    size_t subfile_size = get_streamfile_size(streamFile) - 0x10 - 0x10; //todo
 
     /* check extensions */
     if ( !check_extensions(streamFile, "fsb,wii") )
@@ -379,17 +383,41 @@ VGMSTREAM * init_vgmstream_fsb4_wav(STREAMFILE *streamFile) {
         goto fail;
 
     /* parse FSB subfile */
-    custom_streamFile = open_clamp_streamfile(open_wrap_streamfile(streamFile), custom_start,custom_size);
-    if (!custom_streamFile) goto fail;
+    test_streamFile = setup_fsb4_wav_streamfile(streamFile, subfile_start,subfile_size);
+    if (!test_streamFile) goto fail;
 
-    vgmstream = init_vgmstream_fsb(custom_streamFile);
+    vgmstream = init_vgmstream_fsb(test_streamFile);
     if (!vgmstream) goto fail;
 
-    close_streamfile(custom_streamFile);
+    /* init the VGMSTREAM */
+    close_streamfile(test_streamFile);
     return vgmstream;
 
 fail:
-    close_streamfile(custom_streamFile);
+    close_streamfile(test_streamFile);
     close_vgmstream(vgmstream);
     return NULL;
 }
+
+static STREAMFILE* setup_fsb4_wav_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size) {
+    STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
+
+    /* setup subfile */
+    new_streamFile = open_wrap_streamfile(streamFile);
+    if (!new_streamFile) goto fail;
+    temp_streamFile = new_streamFile;
+
+    new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size);
+    if (!new_streamFile) goto fail;
+    temp_streamFile = new_streamFile;
+
+    new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,"fsb");
+    if (!new_streamFile) goto fail;
+    temp_streamFile = new_streamFile;
+
+    return temp_streamFile;
+
+fail:
+    close_streamfile(temp_streamFile);
+    return NULL;
+}
diff --git a/src/meta/fsb5.c b/src/meta/fsb5.c
index a29fa70e..0d79502b 100644
--- a/src/meta/fsb5.c
+++ b/src/meta/fsb5.c
@@ -11,7 +11,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
 
     uint32_t NumSamples = 0, LoopStart = 0, LoopEnd = 0;
     int LoopFlag = 0, ChannelCount = 0, Version, SampleRate = 0, CodingID;
-    int TotalStreams, TargetStream = streamFile->stream_index;
+    int TotalSubsongs, TargetSubsong = streamFile->stream_index;
     int i;
 
     /* check extension, case insensitive */
@@ -25,7 +25,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
     Version = read_32bitLE(0x04,streamFile);
     if (Version != 0x00 && Version != 0x01) goto fail;
 
-    TotalStreams       = read_32bitLE(0x08,streamFile);
+    TotalSubsongs      = read_32bitLE(0x08,streamFile);
     SampleHeaderLength = read_32bitLE(0x0C,streamFile);
     NameTableLength    = read_32bitLE(0x10,streamFile);
     SampleDataLength   = read_32bitLE(0x14,streamFile);
@@ -37,14 +37,14 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
     if ((SampleHeaderLength + NameTableLength + SampleDataLength + BaseHeaderLength) != get_streamfile_size(streamFile))
         goto fail;
 
-    if (TargetStream == 0) TargetStream = 1; /* default to 1 */
-    if (TargetStream > TotalStreams || TotalStreams <= 0) goto fail;
+    if (TargetSubsong == 0) TargetSubsong = 1; /* default to 1 */
+    if (TargetSubsong > TotalSubsongs || TotalSubsongs <= 0) goto fail;
 
     SampleHeaderStart = BaseHeaderLength;
 
     /* find target stream header and data offset, and read all needed values for later use
      *  (reads one by one as the size of a single stream header is variable) */
-    for (i = 1; i <= TotalStreams; i++) {
+    for (i = 1; i <= TotalSubsongs; i++) {
         off_t  DataStart = 0;
         size_t StreamHeaderLength = 0;
         uint32_t SampleMode1, SampleMode2;
@@ -156,11 +156,11 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
         }
 
         /* stream found */
-        if (i == TargetStream) {
+        if (i == TargetSubsong) {
             StartOffset = BaseHeaderLength + SampleHeaderLength + NameTableLength + DataStart;
 
             /* get stream size from next stream or datasize if there is only one */
-            if (i == TotalStreams) {
+            if (i == TotalSubsongs) {
                 StreamSize = SampleDataLength - DataStart;
             } else {
                 uint32_t NextSampleMode  = (uint32_t)read_32bitLE(SampleHeaderStart+StreamHeaderLength+0x00,streamFile);
@@ -178,7 +178,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
 
     /* get stream name */
     if (NameTableLength) {
-        NameOffset = BaseHeaderLength + SampleHeaderLength + read_32bitLE(BaseHeaderLength + SampleHeaderLength + 0x04*(TargetStream-1),streamFile);
+        NameOffset = BaseHeaderLength + SampleHeaderLength + read_32bitLE(BaseHeaderLength + SampleHeaderLength + 0x04*(TargetSubsong-1),streamFile);
     }
 
 
@@ -187,12 +187,13 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
     if (!vgmstream) goto fail;
 
     vgmstream->sample_rate = SampleRate;
-    vgmstream->num_streams = TotalStreams;
     vgmstream->num_samples = NumSamples;
     if (LoopFlag) {
         vgmstream->loop_start_sample = LoopStart;
         vgmstream->loop_end_sample = LoopEnd;
     }
+    vgmstream->num_streams = TotalSubsongs;
+    vgmstream->stream_size = StreamSize;
     vgmstream->meta_type = meta_FSB5;
     if (NameOffset)
         read_string(vgmstream->stream_name,STREAM_NAME_SIZE, NameOffset,streamFile);
diff --git a/src/meta/fsb_encrypted.c b/src/meta/fsb_encrypted.c
index 20e0e352..98af7df9 100644
--- a/src/meta/fsb_encrypted.c
+++ b/src/meta/fsb_encrypted.c
@@ -3,58 +3,12 @@
 
 #define FSB_KEY_MAX 128 /* probably 32 */
 
-typedef struct {
-    uint8_t fsbkey[FSB_KEY_MAX];
-    size_t fsbkey_size;
-    int is_alt;
-} fsb_decryption_data;
-
-static size_t fsb_decryption_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, fsb_decryption_data* data) {
-    static const unsigned char reverse_bits_table[] = { /* LUT to simplify, could use some bitswap function */
-      0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
-      0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
-      0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
-      0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
-      0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
-      0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
-      0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
-      0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
-      0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
-      0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
-      0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
-      0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
-      0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
-      0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
-      0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
-      0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
-    };
-    size_t bytes_read;
-    int i;
-
-    bytes_read = streamfile->read(streamfile, dest, offset, length);
-
-    /* decrypt data (inverted bits and xor) */
-    for (i = 0; i < bytes_read; i++) {
-        uint8_t xor = data->fsbkey[(offset + i) % data->fsbkey_size];
-        uint8_t val = dest[i];
-        if (data->is_alt) {
-            dest[i] = reverse_bits_table[val ^ xor];
-        }
-        else {
-            dest[i] = reverse_bits_table[val] ^ xor;
-        }
-    }
-
-    return bytes_read;
-}
-
+static STREAMFILE* setup_fsb_streamfile(STREAMFILE *streamFile, const uint8_t * key, size_t key_size, int is_alt);
 
 
 /* fully encrypted FSBs */
 VGMSTREAM * init_vgmstream_fsb_encrypted(STREAMFILE * streamFile) {
     VGMSTREAM * vgmstream = NULL;
-    fsb_decryption_data io_data = {0};
-    size_t io_data_size = sizeof(fsb_decryption_data);
 
     /* check extensions */
     if ( !check_extensions(streamFile, "fsb") )
@@ -67,35 +21,29 @@ VGMSTREAM * init_vgmstream_fsb_encrypted(STREAMFILE * streamFile) {
 
     /* try fsbkey + all combinations of FSB4/5 and decryption algorithms */
     {
-        io_data.fsbkey_size = read_key_file(io_data.fsbkey, FSB_KEY_MAX, streamFile);
-        if (io_data.fsbkey_size) {
+        STREAMFILE *temp_streamFile = NULL;
+        uint8_t key[FSB_KEY_MAX];
+        size_t key_size = read_key_file(key, FSB_KEY_MAX, streamFile);
 
+        if (key_size) {
             {
-                STREAMFILE *custom_streamFile = NULL;
+                temp_streamFile = setup_fsb_streamfile(streamFile, key,key_size, 0);
+                if (!temp_streamFile) goto fail;
 
-                io_data.is_alt = 0;
-                custom_streamFile = open_io_streamfile(open_wrap_streamfile(streamFile), &io_data,io_data_size, fsb_decryption_read);
-                if (!custom_streamFile) goto fail;
+                if (!vgmstream) vgmstream = init_vgmstream_fsb(temp_streamFile);
+                if (!vgmstream) vgmstream = init_vgmstream_fsb5(temp_streamFile);
 
-                if (!vgmstream) vgmstream = init_vgmstream_fsb(custom_streamFile);
-                if (!vgmstream) vgmstream = init_vgmstream_fsb5(custom_streamFile);
-
-                close_streamfile(custom_streamFile);
+                close_streamfile(temp_streamFile);
             }
 
-
             if (!vgmstream) {
-                STREAMFILE *custom_streamFile = NULL;
+                temp_streamFile = setup_fsb_streamfile(streamFile, key,key_size, 1);
+                if (!temp_streamFile) goto fail;
 
-                io_data.is_alt = 1;
-                custom_streamFile = open_io_streamfile(open_wrap_streamfile(streamFile), &io_data,io_data_size, fsb_decryption_read);
-                if (!custom_streamFile) goto fail;
+                if (!vgmstream) vgmstream = init_vgmstream_fsb(temp_streamFile);
+                if (!vgmstream) vgmstream = init_vgmstream_fsb5(temp_streamFile);
 
-                vgmstream = init_vgmstream_fsb(custom_streamFile);
-                if (!vgmstream)
-                    vgmstream = init_vgmstream_fsb5(custom_streamFile);
-
-                close_streamfile(custom_streamFile);
+                close_streamfile(temp_streamFile);
             }
         }
     }
@@ -104,26 +52,22 @@ VGMSTREAM * init_vgmstream_fsb_encrypted(STREAMFILE * streamFile) {
     /* try all keys until one works */
     if (!vgmstream) {
         int i;
-        STREAMFILE *custom_streamFile = NULL;
+        STREAMFILE *temp_streamFile = NULL;
 
         for (i = 0; i < fsbkey_list_count; i++) {
-            if (!fsbkey_list[i].fsbkey_size || fsbkey_list[i].fsbkey_size > FSB_KEY_MAX) goto fail;
+            fsbkey_info entry = fsbkey_list[i];
+            ;VGM_LOG("fsbkey: size=%i, is_fsb5=%i, is_alt=%i\n", entry.fsbkey_size,entry.is_fsb5, entry.is_alt);
 
-            memcpy(io_data.fsbkey, fsbkey_list[i].fsbkey, fsbkey_list[i].fsbkey_size);
-            io_data.fsbkey_size = fsbkey_list[i].fsbkey_size;
-            io_data.is_alt = fsbkey_list[i].is_alt;
-            //;VGM_LOG("fsbkey: size=%i, is_fsb5=%i, is_alt=%i\n", fsbkey_list[i].fsbkey_size,fsbkey_list[i].is_fsb5, fsbkey_list[i].is_alt);
-
-            custom_streamFile = open_io_streamfile(open_wrap_streamfile(streamFile), &io_data,io_data_size, fsb_decryption_read);
-            if (!custom_streamFile) goto fail;
+            temp_streamFile = setup_fsb_streamfile(streamFile, entry.fsbkey, entry.fsbkey_size, entry.is_alt);
+            if (!temp_streamFile) goto fail;
 
             if (fsbkey_list[i].is_fsb5) {
-                vgmstream = init_vgmstream_fsb5(custom_streamFile);
+                vgmstream = init_vgmstream_fsb5(temp_streamFile);
             } else {
-                vgmstream = init_vgmstream_fsb(custom_streamFile);
+                vgmstream = init_vgmstream_fsb(temp_streamFile);
             }
 
-            close_streamfile(custom_streamFile);
+            close_streamfile(temp_streamFile);
             if (vgmstream) break;
         }
     }
@@ -137,3 +81,78 @@ fail:
     close_vgmstream(vgmstream);
     return NULL;
 }
+
+
+typedef struct {
+    uint8_t key[FSB_KEY_MAX];
+    size_t key_size;
+    int is_alt;
+} fsb_decryption_data;
+
+/* Encrypted FSB info from guessfsb and fsbext */
+static size_t fsb_decryption_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, fsb_decryption_data* data) {
+    static const unsigned char reverse_bits_table[] = { /* LUT to simplify, could use some bitswap function */
+      0x00,0x80,0x40,0xC0,0x20,0xA0,0x60,0xE0,0x10,0x90,0x50,0xD0,0x30,0xB0,0x70,0xF0,
+      0x08,0x88,0x48,0xC8,0x28,0xA8,0x68,0xE8,0x18,0x98,0x58,0xD8,0x38,0xB8,0x78,0xF8,
+      0x04,0x84,0x44,0xC4,0x24,0xA4,0x64,0xE4,0x14,0x94,0x54,0xD4,0x34,0xB4,0x74,0xF4,
+      0x0C,0x8C,0x4C,0xCC,0x2C,0xAC,0x6C,0xEC,0x1C,0x9C,0x5C,0xDC,0x3C,0xBC,0x7C,0xFC,
+      0x02,0x82,0x42,0xC2,0x22,0xA2,0x62,0xE2,0x12,0x92,0x52,0xD2,0x32,0xB2,0x72,0xF2,
+      0x0A,0x8A,0x4A,0xCA,0x2A,0xAA,0x6A,0xEA,0x1A,0x9A,0x5A,0xDA,0x3A,0xBA,0x7A,0xFA,
+      0x06,0x86,0x46,0xC6,0x26,0xA6,0x66,0xE6,0x16,0x96,0x56,0xD6,0x36,0xB6,0x76,0xF6,
+      0x0E,0x8E,0x4E,0xCE,0x2E,0xAE,0x6E,0xEE,0x1E,0x9E,0x5E,0xDE,0x3E,0xBE,0x7E,0xFE,
+      0x01,0x81,0x41,0xC1,0x21,0xA1,0x61,0xE1,0x11,0x91,0x51,0xD1,0x31,0xB1,0x71,0xF1,
+      0x09,0x89,0x49,0xC9,0x29,0xA9,0x69,0xE9,0x19,0x99,0x59,0xD9,0x39,0xB9,0x79,0xF9,
+      0x05,0x85,0x45,0xC5,0x25,0xA5,0x65,0xE5,0x15,0x95,0x55,0xD5,0x35,0xB5,0x75,0xF5,
+      0x0D,0x8D,0x4D,0xCD,0x2D,0xAD,0x6D,0xED,0x1D,0x9D,0x5D,0xDD,0x3D,0xBD,0x7D,0xFD,
+      0x03,0x83,0x43,0xC3,0x23,0xA3,0x63,0xE3,0x13,0x93,0x53,0xD3,0x33,0xB3,0x73,0xF3,
+      0x0B,0x8B,0x4B,0xCB,0x2B,0xAB,0x6B,0xEB,0x1B,0x9B,0x5B,0xDB,0x3B,0xBB,0x7B,0xFB,
+      0x07,0x87,0x47,0xC7,0x27,0xA7,0x67,0xE7,0x17,0x97,0x57,0xD7,0x37,0xB7,0x77,0xF7,
+      0x0F,0x8F,0x4F,0xCF,0x2F,0xAF,0x6F,0xEF,0x1F,0x9F,0x5F,0xDF,0x3F,0xBF,0x7F,0xFF
+    };
+    size_t bytes_read;
+    int i;
+
+    bytes_read = streamfile->read(streamfile, dest, offset, length);
+
+    /* decrypt data (inverted bits and xor) */
+    for (i = 0; i < bytes_read; i++) {
+        uint8_t xor = data->key[(offset + i) % data->key_size];
+        uint8_t val = dest[i];
+        if (data->is_alt) {
+            dest[i] = reverse_bits_table[val ^ xor];
+        }
+        else {
+            dest[i] = reverse_bits_table[val] ^ xor;
+        }
+    }
+
+    return bytes_read;
+}
+
+static STREAMFILE* setup_fsb_streamfile(STREAMFILE *streamFile, const uint8_t * key, size_t key_size, int is_alt) {
+    STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
+    fsb_decryption_data io_data = {0};
+    size_t io_data_size = sizeof(fsb_decryption_data);
+
+    /* setup decryption with key (external) */
+    if (!key_size || key_size > FSB_KEY_MAX) goto fail;
+
+    memcpy(io_data.key, key, key_size);
+    io_data.key_size = key_size;
+    io_data.is_alt = is_alt;
+
+    /* setup subfile */
+    new_streamFile = open_wrap_streamfile(streamFile);
+    if (!new_streamFile) goto fail;
+    temp_streamFile = new_streamFile;
+
+    new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, fsb_decryption_read);
+    if (!new_streamFile) goto fail;
+    temp_streamFile = new_streamFile;
+
+    return temp_streamFile;
+
+fail:
+    close_streamfile(temp_streamFile);
+    return NULL;
+}
diff --git a/src/meta/fsb_keys.h b/src/meta/fsb_keys.h
index e250ae10..1b3a6081 100644
--- a/src/meta/fsb_keys.h
+++ b/src/meta/fsb_keys.h
@@ -30,7 +30,7 @@ static const uint8_t key_gfs[] = { 0x25,0x6C,0x41,0x6E,0x32,0x7B,0x50,0x69,0x2A,
 /* RevHeadz Engine Sounds (Mobile) */ //"1^7%82#&5$~/8sz"
 static const uint8_t key_rev[] = { 0x31,0x5E,0x37,0x25,0x38,0x32,0x23,0x26,0x35,0x24,0x7E,0x2F,0x38,0x73,0x7A };
 
-/* Dark Souls 3 (PC) */ //"FDPrVuT4fAFvdHJYAgyMzRF4EcBAnKg" //todo ps4?
+/* Dark Souls 3 (PC) */ //"FDPrVuT4fAFvdHJYAgyMzRF4EcBAnKg"
 static const uint8_t key_ds3[] = { 0x46,0x44,0x50,0x72,0x56,0x75,0x54,0x34,0x66,0x41,0x46,0x76,0x64,0x48,0x4A,0x59,0x41,0x67,0x79,0x4D,0x7A,0x52,0x46,0x34,0x45,0x63,0x42,0x41,0x6E,0x4B,0x67 };
 
 /* Mortal Kombat X */
diff --git a/src/meta/hca_keys.h b/src/meta/hca_keys.h
index fba74c2a..231307cd 100644
--- a/src/meta/hca_keys.h
+++ b/src/meta/hca_keys.h
@@ -225,6 +225,9 @@ static const hcakey_info hcakey_list[] = {
         // Kai-ri-Sei Million Arthur (Vita)
         {1782351729464341796},      // 18BC2F7463867524
 
+        // Dx2 Shin Megami Tensei Liberation (iOS/Android)
+        {118714477},                // 000000000713706D
+
 };
 
 #endif/*_HCA_KEYS_H_*/
diff --git a/src/meta/kma9.c b/src/meta/kma9.c
index 30caa7b2..16f2e783 100644
--- a/src/meta/kma9.c
+++ b/src/meta/kma9.c
@@ -5,6 +5,7 @@
 VGMSTREAM * init_vgmstream_kma9(STREAMFILE *streamFile) {
     VGMSTREAM * vgmstream = NULL;
     off_t start_offset;
+    size_t stream_size;
     int loop_flag, channel_count;
     int total_subsongs = 0, target_subsong = streamFile->stream_index;
 
@@ -25,7 +26,7 @@ VGMSTREAM * init_vgmstream_kma9(STREAMFILE *streamFile) {
     if (target_subsong == 0) target_subsong = 1;
     if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
     /* 0x0c: unknown */
-    /* 0x14: data size of each subsong */
+    stream_size = read_32bitLE(0x14,streamFile); /* per subsong */
 
 
     /* build the VGMSTREAM */
@@ -37,7 +38,7 @@ VGMSTREAM * init_vgmstream_kma9(STREAMFILE *streamFile) {
     vgmstream->loop_start_sample = read_32bitLE(0x24,streamFile); /* with skip_samples? */
     vgmstream->loop_end_sample = vgmstream->num_samples; /* 0x28 looks like end samples but isn't, no idea */
     vgmstream->num_streams = total_subsongs;
-
+    vgmstream->stream_size = stream_size;
     vgmstream->meta_type = meta_KMA9;
 
 #ifdef VGM_USE_ATRAC9
diff --git a/src/meta/meta.h b/src/meta/meta.h
index 5005081f..8088dc7c 100644
--- a/src/meta/meta.h
+++ b/src/meta/meta.h
@@ -697,4 +697,13 @@ VGMSTREAM * init_vgmstream_kma9(STREAMFILE * streamFile);
 
 VGMSTREAM * init_vgmstream_fsb_encrypted(STREAMFILE * streamFile);
 
+VGMSTREAM * init_vgmstream_xwc(STREAMFILE *streamFile);
+
+VGMSTREAM * init_vgmstream_atsl3(STREAMFILE *streamFile);
+
+VGMSTREAM * init_vgmstream_sps_n1(STREAMFILE *streamFile);
+
+VGMSTREAM * init_vgmstream_atx(STREAMFILE *streamFile);
+
+VGMSTREAM * init_vgmstream_sqex_sead(STREAMFILE * streamFile);
 #endif /*_META_H*/
diff --git a/src/meta/ogg_vorbis.c b/src/meta/ogg_vorbis.c
index 0093e94f..a0a72ff2 100644
--- a/src/meta/ogg_vorbis.c
+++ b/src/meta/ogg_vorbis.c
@@ -274,6 +274,9 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, const ch
     int32_t loop_length = vgm_inf->loop_length;
     int loop_end_found = vgm_inf->loop_end_found;
     int32_t loop_end = vgm_inf->loop_end;
+    size_t stream_size = vgm_inf->stream_size ?
+            vgm_inf->stream_size :
+            get_streamfile_size(streamFile) - start;
 
     ov_callbacks default_callbacks;
 
@@ -295,9 +298,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, const ch
 
         temp_streamfile.start = start;
         temp_streamfile.offset = 0;
-        temp_streamfile.size = vgm_inf->stream_size ?
-                vgm_inf->stream_size :
-                get_streamfile_size(temp_streamfile.streamfile) - start;
+        temp_streamfile.size = stream_size;
 
         temp_streamfile.decryption_callback = vgm_inf->decryption_callback;
         temp_streamfile.scd_xor = vgm_inf->scd_xor;
@@ -324,9 +325,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, const ch
 
         data->ov_streamfile.start = start;
         data->ov_streamfile.offset = 0;
-        data->ov_streamfile.size = vgm_inf->stream_size ?
-                vgm_inf->stream_size :
-                get_streamfile_size(data->ov_streamfile.streamfile) - start;
+        data->ov_streamfile.size = stream_size;
 
         data->ov_streamfile.decryption_callback = vgm_inf->decryption_callback;
         data->ov_streamfile.scd_xor = vgm_inf->scd_xor;
@@ -412,6 +411,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, const ch
     vgmstream->channels = vi->channels;
     vgmstream->sample_rate = vi->rate;
     vgmstream->num_streams = vgm_inf->total_subsongs;
+    vgmstream->stream_size = stream_size;
 
     vgmstream->num_samples = ov_pcm_total(ovf,-1); /* let libvorbisfile find total samples */
     if (loop_flag) {
diff --git a/src/meta/ps2_rxws.c b/src/meta/ps2_rxws.c
index d5906243..d32b69e0 100644
--- a/src/meta/ps2_rxws.c
+++ b/src/meta/ps2_rxws.c
@@ -7,10 +7,10 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) {
     VGMSTREAM * vgmstream = NULL;
     STREAMFILE * streamHeader = NULL;
     off_t start_offset, chunk_offset, name_offset = 0;
-    size_t data_size, chunk_size;
+    size_t stream_size, chunk_size;
     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;
+    int total_subsongs, target_subsong = streamFile->stream_index;
 
     /* check extensions */
     /* .xws: header and data, .xwh+xwb: header + data (.bin+dat are also found in Wild Arms 4/5) */
@@ -46,14 +46,14 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) {
 
 
     /* check multi-streams */
-    total_streams = read_32bitLE(chunk_offset+0x00,streamHeader);
-    if (target_stream == 0) target_stream = 1;
-    if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail;
+    total_subsongs = read_32bitLE(chunk_offset+0x00,streamHeader);
+    if (target_subsong == 0) target_subsong = 1;
+    if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
 
 
     /* read stream header */
     {
-        off_t header_offset = chunk_offset + 0x4 + 0x1c * (target_stream-1); /* position in FORM */
+        off_t header_offset = chunk_offset + 0x4 + 0x1c * (target_subsong-1); /* position in FORM */
         off_t stream_offset, next_stream_offset, data_offset = 0;
 
         type = read_8bit(header_offset+0x00, streamHeader);
@@ -83,22 +83,22 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) {
             if (!data_offset) goto fail;
         }
 
-        if (target_stream == total_streams) {
+        if (target_subsong == total_subsongs) {
             next_stream_offset = data_offset + get_streamfile_size(is_separate ? streamFile : streamHeader);
         } else {
-            off_t next_header_offset = chunk_offset + 0x4 + 0x1c * (target_stream);
+            off_t next_header_offset = chunk_offset + 0x4 + 0x1c * (target_subsong);
             next_stream_offset = read_32bitLE(next_header_offset+0x10,streamHeader);
         }
 
-        data_size = next_stream_offset - stream_offset;
+        stream_size = next_stream_offset - stream_offset;
         start_offset = data_offset + stream_offset;
     }
 
     /* get stream name (always follows FORM) */
     if (read_32bitBE(0x10+0x10 + chunk_size,streamHeader) == 0x46545854) { /* "FTXT" */
         chunk_offset = 0x10+0x10 + chunk_size + 0x10;
-        if (read_32bitLE(chunk_offset+0x00,streamHeader) == total_streams) {
-            name_offset = chunk_offset + read_32bitLE(chunk_offset+0x04 + (target_stream-1)*0x04,streamHeader);
+        if (read_32bitLE(chunk_offset+0x00,streamHeader) == total_subsongs) {
+            name_offset = chunk_offset + read_32bitLE(chunk_offset+0x04 + (target_subsong-1)*0x04,streamHeader);
         }
     }
 
@@ -108,7 +108,8 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) {
     if (!vgmstream) goto fail;
 
     vgmstream->sample_rate = sample_rate;
-    vgmstream->num_streams = total_streams;
+    vgmstream->num_streams = total_subsongs;
+    vgmstream->stream_size = stream_size;
     vgmstream->meta_type = meta_PS2_RXWS;
     if (name_offset)
         read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamHeader);
@@ -143,10 +144,10 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) {
             joint_stereo = 0;
             encoder_delay = 0x0;
 
-            bytes = ffmpeg_make_riff_atrac3(buf, 0x100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_size, joint_stereo, encoder_delay);
+            bytes = ffmpeg_make_riff_atrac3(buf, 0x100, vgmstream->num_samples, stream_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,data_size);
+            vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,stream_size);
             if (!vgmstream->codec_data) goto fail;
             vgmstream->coding_type = coding_FFmpeg;
             vgmstream->layout_type = layout_none;
diff --git a/src/meta/psx_cdxa.c b/src/meta/psx_cdxa.c
index 6e612c09..1dd04d61 100644
--- a/src/meta/psx_cdxa.c
+++ b/src/meta/psx_cdxa.c
@@ -39,20 +39,27 @@ VGMSTREAM * init_vgmstream_cdxa(STREAMFILE *streamFile) {
         }
     }
 
-    /* test first block (except when RIFF) */
+    /* test some blocks (except when RIFF) since other .XA/STR may start blank */
     if (start_offset == 0) {
-        int i, j;
+        int i, j, block;
+        off_t test_offset = start_offset;
+        size_t sector_size = (is_blocked ? 0x900 : 0x800);
+        size_t block_size = 0x80;
 
-        /* 0x80 frames for 1 sector (max ~0x800 for ISO mode)  */
-        for (i = 0; i < (0x800/0x80); i++) {
-            off_t test_offset = start_offset + (is_blocked ? 0x18 : 0x00) + 0x80*i;
+        for (block = 0; block < 3; block++) {
+            test_offset += (is_blocked ? 0x18 : 0x00); /* header */
 
-            /* ADPCM predictors should be 0..3 index */
-            for (j = 0; j < 16; j++) {
-                uint8_t header = read_8bit(test_offset + i, streamFile);
-                if (((header >> 4) & 0xF) > 3)
-                    goto fail;
+            for (i = 0; i < (sector_size/block_size); i++) {
+                /* first 0x10 ADPCM predictors should be 0..3 index */
+                for (j = 0; j < 16; j++) {
+                    uint8_t header = read_8bit(test_offset + i, streamFile);
+                    if (((header >> 4) & 0xF) > 3)
+                        goto fail;
+                }
+                test_offset += 0x80;
             }
+
+            test_offset += (is_blocked ? 0x18 : 0x00); /* footer */
         }
     }
 
diff --git a/src/meta/rws.c b/src/meta/rws.c
index d2d2366f..3594eb45 100644
--- a/src/meta/rws.c
+++ b/src/meta/rws.c
@@ -15,7 +15,7 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
     size_t block_size = 0, block_size_total = 0;
     int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
     int i, total_segments;
-    int total_streams, target_stream = streamFile->stream_index;
+    int total_subsongs, target_subsong = streamFile->stream_index;
 
 
     if (!check_extensions(streamFile,"rws"))
@@ -49,7 +49,7 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
      * 0x1c: null?  0x30: 0x800?,  0x34: block_size_total?, 0x38: data offset,  0x3c: 0?, 0x40-50: file uuid */
     read_32bit = (read_32bitLE(off+0x00,streamFile) > header_size) ? read_32bitBE : read_32bitLE; /* GC/Wii/X360 = BE */
     total_segments = read_32bit(off+0x20,streamFile);
-    total_streams = read_32bit(off+0x28,streamFile);
+    total_subsongs = read_32bit(off+0x28,streamFile);
 
     /* skip audio file name */
     off += 0x50 + get_rws_string_size(off+0x50, streamFile);
@@ -58,8 +58,8 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
     /* Data is divided into "segments" (cues/divisions within data, ex. intro+main, voice1+2+..N) and "streams"
      * of interleaved blocks (for multichannel?). last stream (only?) has padding. Segments divide all streams.
      * ex.- 0x1800 data + 0 pad of stream_0 2ch, 0x1800 data + 0x200 pad of stream1 2ch (xN). */
-    if (target_stream == 0) target_stream = 1;
-    if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail;
+    if (target_subsong == 0) target_subsong = 1;
+    if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
 
     /* get segment info, for all streams */
     /* 0x00/04/0c: command?,  0x18: full segment size (including all streams),  0x1c: offset, others: ?) */
@@ -70,9 +70,9 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
 
     /* get usable segment sizes (usually ok but sometimes > stream_size), per stream */
     for (i = 0; i < total_segments; i++) { /* sum usable segment sizes (no padding) */
-        stream_size += read_32bit(off + 0x04*i + 0x04*total_segments*(target_stream-1),streamFile);
+        stream_size += read_32bit(off + 0x04*i + 0x04*total_segments*(target_subsong-1),streamFile);
     }
-    off += 0x04 * (total_segments * total_streams);
+    off += 0x04 * (total_segments * total_subsongs);
 
     /* skip segment uuids */
     off += 0x10 * total_segments;
@@ -85,21 +85,21 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
     /* get stream layout */
     /* 0x00/04/14: command?, 0x08: null?  0x0c: spf related? (XADPCM=07, VAG=1C, DSP=0E, PCM=01)
      * 0x24: offset within data chunk, 0x1c: codec related?,  others: ?) */
-    for (i = 0; i < total_streams; i++) { /* get block_sizes */
+    for (i = 0; i < total_subsongs; i++) { /* get block_sizes */
         block_size_total += read_32bit(off + 0x10 + 0x28*i, streamFile); /* for all streeams, to skip during decode */
-        if (i+1 == target_stream) {
+        if (i+1 == target_subsong) {
             //block_size_full = read_32bit(off + 0x10 + 0x28*i, streamFile); /* with padding, can be different per stream */
             block_size = read_32bit(off + 0x20 + 0x28*i, streamFile); /* without padding */
             stream_offset = read_32bit(off + 0x24 + 0x28*i, streamFile); /* within data */
         }
     }
-    off += 0x28 * total_streams;
+    off += 0x28 * total_subsongs;
 
     /* get stream config */
     /* 0x04: command?,  0x0c(1): bits per sample,  others: null? */
-    for (i = 0; i < total_streams; i++) { /* size depends on codec so we must parse it */
+    for (i = 0; i < total_subsongs; i++) { /* size depends on codec so we must parse it */
         int prev_codec = 0;
-        if (i+1 == target_stream) {
+        if (i+1 == target_subsong) {
             sample_rate   = read_32bit(off+0x00, streamFile);
             //unk_size    = read_32bit(off+0x08, streamFile); /* segment size again? loop-related? */
             channel_count =  read_8bit(off+0x0d, streamFile);
@@ -110,7 +110,7 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
 
         if (prev_codec == 0xF86215B0) { /* if codec is DSP there is an extra field per stream */
             /* 0x00: approx num samples?  0x04: approx size/loop related? (can be 0) */
-            if (i+1 == target_stream) {
+            if (i+1 == target_subsong) {
                 coefs_offset = off + 0x1c;
             }
             off += 0x60;
@@ -120,11 +120,11 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
     }
 
     /* skip stream uuids */
-    off += 0x10 * total_streams;
+    off += 0x10 * total_subsongs;
 
     /* get stream name */
-    for (i = 0; i < total_streams; i++) {
-        if (i+1 == target_stream) {
+    for (i = 0; i < total_subsongs; i++) {
+        if (i+1 == target_subsong) {
             name_offset = off;
         }
         off += get_rws_string_size(off, streamFile);
@@ -137,7 +137,7 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
     start_offset = 0x0c + 0x0c + header_size + 0x0c + stream_offset;
 
     /* sometimes it's wrong for no apparent reason (probably a bug in RWS) */
-    stream_size_expected = (stream_size_full / block_size_total) * (block_size * total_streams) / total_streams;
+    stream_size_expected = (stream_size_full / block_size_total) * (block_size * total_subsongs) / total_subsongs;
     if (stream_size > stream_size_expected) {
         VGM_LOG("RWS: readjusting wrong stream size %x vs expected %x\n", stream_size, stream_size_expected);
         stream_size = stream_size_expected;
@@ -149,7 +149,8 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
     if (!vgmstream) goto fail;
 
     vgmstream->sample_rate = sample_rate;
-    vgmstream->num_streams = total_streams;
+    vgmstream->num_streams = total_subsongs;
+    vgmstream->stream_size = stream_size;
     vgmstream->meta_type = meta_RWS;
     if (name_offset)
         read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamFile);
diff --git a/src/meta/sab.c b/src/meta/sab.c
index 450fd8d4..8532e478 100644
--- a/src/meta/sab.c
+++ b/src/meta/sab.c
@@ -8,8 +8,8 @@ static void get_stream_name(char * stream_name, STREAMFILE *streamFile, int targ
 VGMSTREAM * init_vgmstream_sab(STREAMFILE *streamFile) {
     VGMSTREAM * vgmstream = NULL;
     off_t start_offset;
-    int loop_flag, channel_count = 0, is_stream, align, codec, sample_rate, data_size, loop_start, loop_end;
-    int total_streams, target_stream = streamFile->stream_index;
+    int loop_flag, channel_count = 0, is_stream, align, codec, sample_rate, stream_size, loop_start, loop_end;
+    int total_subsongs, target_subsong  = streamFile->stream_index;
 
     /* .sab: main, .sob: config/names */
     if (!check_extensions(streamFile,"sab"))
@@ -20,29 +20,29 @@ VGMSTREAM * init_vgmstream_sab(STREAMFILE *streamFile) {
         goto fail;
 
     is_stream = read_32bitLE(0x04,streamFile) & 0x04; /* other flags don't seem to matter */
-    total_streams = is_stream ? 1 : read_32bitLE(0x08,streamFile);
-    if (target_stream == 0) target_stream = 1;
-    if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail;
+    total_subsongs = is_stream ? 1 : read_32bitLE(0x08,streamFile);
+    if (target_subsong == 0) target_subsong = 1;
+    if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
 
     align = read_32bitLE(0x0c,streamFile); /* doubles as interleave */
 
     /* stream config */
-    codec         = read_32bitLE(0x18 + 0x1c*(target_stream-1) + 0x00,streamFile);
-    channel_count = read_32bitLE(0x18 + 0x1c*(target_stream-1) + 0x04,streamFile);
-    sample_rate   = read_32bitLE(0x18 + 0x1c*(target_stream-1) + 0x08,streamFile);
-    data_size     = read_32bitLE(0x18 + 0x1c*(target_stream-1) + 0x0c,streamFile);
-    loop_start    = read_32bitLE(0x18 + 0x1c*(target_stream-1) + 0x10,streamFile);
-    loop_end      = read_32bitLE(0x18 + 0x1c*(target_stream-1) + 0x14,streamFile);
+    codec         = read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x00,streamFile);
+    channel_count = read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x04,streamFile);
+    sample_rate   = read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x08,streamFile);
+    stream_size   = read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x0c,streamFile);
+    loop_start    = read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x10,streamFile);
+    loop_end      = read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x14,streamFile);
     loop_flag     = (loop_end > 0);
 
-    start_offset  = 0x18 + 0x1c*total_streams;
+    start_offset  = 0x18 + 0x1c*total_subsongs;
     if (start_offset % align)
         start_offset += align - (start_offset % align);
-    start_offset += read_32bitLE(0x18 + 0x1c*(target_stream-1) + 0x18,streamFile);
+    start_offset += read_32bitLE(0x18 + 0x1c*(target_subsong-1) + 0x18,streamFile);
 
     if (is_stream) {
         channel_count = read_32bitLE(0x08,streamFile); /* uncommon, but non-stream stereo exists */
-        data_size *= channel_count;
+        stream_size *= channel_count;
     }
 
     /* build the VGMSTREAM */
@@ -50,8 +50,8 @@ VGMSTREAM * init_vgmstream_sab(STREAMFILE *streamFile) {
     if (!vgmstream) goto fail;
 
     vgmstream->sample_rate = sample_rate;
-
-    vgmstream->num_streams = total_streams;
+    vgmstream->num_streams = total_subsongs;
+    vgmstream->stream_size = stream_size;
     vgmstream->meta_type = meta_SAB;
 
     switch(codec) {
@@ -60,7 +60,7 @@ VGMSTREAM * init_vgmstream_sab(STREAMFILE *streamFile) {
             vgmstream->layout_type = layout_interleave;
             vgmstream->interleave_block_size = is_stream ? align : 0x02;
 
-            vgmstream->num_samples = pcm_bytes_to_samples(data_size, vgmstream->channels, 16);
+            vgmstream->num_samples = pcm_bytes_to_samples(stream_size, vgmstream->channels, 16);
             vgmstream->loop_start_sample = pcm_bytes_to_samples(loop_start, vgmstream->channels, 16);
             vgmstream->loop_end_sample = pcm_bytes_to_samples(loop_end, vgmstream->channels, 16);
 
@@ -71,7 +71,7 @@ VGMSTREAM * init_vgmstream_sab(STREAMFILE *streamFile) {
             vgmstream->layout_type = layout_interleave;
             vgmstream->interleave_block_size = is_stream ? align : 0x10;
 
-            vgmstream->num_samples = ps_bytes_to_samples(data_size, vgmstream->channels);
+            vgmstream->num_samples = ps_bytes_to_samples(stream_size, vgmstream->channels);
             vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start, vgmstream->channels);
             vgmstream->loop_end_sample = ps_bytes_to_samples(loop_end, vgmstream->channels);
             break;
@@ -81,7 +81,7 @@ VGMSTREAM * init_vgmstream_sab(STREAMFILE *streamFile) {
             vgmstream->layout_type = is_stream ? layout_interleave : layout_none;
             vgmstream->interleave_block_size = is_stream ? align : 0x00;
 
-            vgmstream->num_samples = ms_ima_bytes_to_samples(data_size, 0x24*vgmstream->channels, vgmstream->channels);
+            vgmstream->num_samples = ms_ima_bytes_to_samples(stream_size, 0x24*vgmstream->channels, vgmstream->channels);
             vgmstream->loop_start_sample = ms_ima_bytes_to_samples(loop_start, 0x24*vgmstream->channels, vgmstream->channels);
             vgmstream->loop_end_sample = ms_ima_bytes_to_samples(loop_end, 0x24*vgmstream->channels, vgmstream->channels);
             break;
@@ -91,7 +91,7 @@ VGMSTREAM * init_vgmstream_sab(STREAMFILE *streamFile) {
             goto fail;
     }
 
-    get_stream_name(vgmstream->stream_name, streamFile, target_stream);
+    get_stream_name(vgmstream->stream_name, streamFile, target_subsong);
 
     if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
         goto fail;
diff --git a/src/meta/sgxd.c b/src/meta/sgxd.c
index 07cfba50..33c04628 100644
--- a/src/meta/sgxd.c
+++ b/src/meta/sgxd.c
@@ -2,19 +2,17 @@
 #include "../coding/coding.h"
 
 
-/* SGXD - Sony/SCEI's format (SGB+SGH / SGD / SGX), found in:
- *  PS3: Genji, Folklore, Afrika (Short VAG), Tokyo Jungle
- *  PSP: Brave Story, Sarugetchu Sarusaru Daisakusen, Kurohyo 1/2, Pathwork Heroes */
+/* SGXD - Sony/SCEI's format (SGB+SGH / SGD / SGX) */
 VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
     VGMSTREAM * vgmstream = NULL;
     STREAMFILE * streamHeader = NULL;
     off_t start_offset, data_offset, chunk_offset, name_offset = 0;
-    size_t data_size;
+    size_t stream_size;
 
     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;
+    int total_subsongs, target_subsong = streamFile->stream_index;
 
 
     /* check extension, case insensitive */
@@ -59,14 +57,14 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
     /* 0x04  SGX: unknown; SGD/SGH: chunk length,  0x08  null */
 
     /* check multi-streams (usually only SE containers; Puppeteer) */
-    total_streams = read_32bitLE(chunk_offset+0x04,streamHeader);
-    if (target_stream == 0) target_stream = 1;
-    if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail;
+    total_subsongs = read_32bitLE(chunk_offset+0x04,streamHeader);
+    if (target_subsong == 0) target_subsong = 1;
+    if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
 
     /* read stream header */
     {
         off_t stream_offset;
-        chunk_offset += 0x08 + 0x38 * (target_stream-1); /* position in target header*/
+        chunk_offset += 0x08 + 0x38 * (target_subsong-1); /* position in target header*/
 
         /* 0x00  ? (00/01/02) */
         if (!is_sgx) /* meaning unknown in .sgx; offset 0 = not a stream (a RGND sample) */
@@ -85,7 +83,7 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
         num_samples = read_32bitLE(chunk_offset+0x20,streamHeader);
         loop_start_sample = read_32bitLE(chunk_offset+0x24,streamHeader);
         loop_end_sample = read_32bitLE(chunk_offset+0x28,streamHeader);
-        data_size = read_32bitLE(chunk_offset+0x2c,streamHeader); /* stream size (without padding) / interleave (for type3) */
+        stream_size = read_32bitLE(chunk_offset+0x2c,streamHeader); /* stream size (without padding) / interleave (for type3) */
 
         if (is_sgx) {
             stream_offset = 0x0;
@@ -107,7 +105,8 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
     vgmstream->num_samples = num_samples;
     vgmstream->loop_start_sample = loop_start_sample;
     vgmstream->loop_end_sample = loop_end_sample;
-    vgmstream->num_streams = total_streams;
+    vgmstream->num_streams = total_subsongs;
+    vgmstream->stream_size = stream_size;
     vgmstream->meta_type = meta_SGXD;
     if (name_offset)
         read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamHeader);
@@ -118,23 +117,23 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
         vgmstream->loop_end_sample -= 1;
 
     switch (type) {
-        case 0x03:      /* PS-ADPCM */
+        case 0x03:      /* PS-ADPCM [Genji (PS3), Ape Escape Move (PS3)]*/
             vgmstream->coding_type = coding_PSX;
             vgmstream->layout_type = layout_interleave;
             if (is_sgx || is_sgb) {
                 vgmstream->interleave_block_size = 0x10;
             } else { /* this only seems to happen with SFX */
-                vgmstream->interleave_block_size = data_size;
+                vgmstream->interleave_block_size = stream_size;
             }
 
             break;
 
 #ifdef VGM_USE_FFMPEG
-        case 0x04: {    /* ATRAC3plus */
+        case 0x04: {    /* ATRAC3plus [Kurohyo 1/2 (PSP), BraveStory (PSP)] */
             ffmpeg_codec_data *ffmpeg_data;
 
             /* internally has a RIFF header; but the SGXD  header / sample rate has priority over it (may not match) */
-            ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, data_size);
+            ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, stream_size);
             if ( !ffmpeg_data ) goto fail;
             vgmstream->codec_data = ffmpeg_data;
             vgmstream->coding_type = coding_FFmpeg;
@@ -158,7 +157,7 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
             break;
         }
 #endif
-        case 0x05:      /* Short PS-ADPCM */
+        case 0x05:      /* Short PS-ADPCM [Afrika (PS3)] */
             vgmstream->coding_type = coding_PSX_cfg;
             vgmstream->layout_type = layout_interleave;
             vgmstream->interleave_block_size = 0x4;
@@ -166,10 +165,10 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
             break;
 
 #ifdef VGM_USE_FFMPEG
-        case 0x06: {    /* AC3 */
+        case 0x06: {    /* AC3 [Tokyo Jungle (PS3), Afrika (PS3)] */
             ffmpeg_codec_data *ffmpeg_data;
 
-            ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, data_size);
+            ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, stream_size);
             if ( !ffmpeg_data ) goto fail;
             vgmstream->codec_data = ffmpeg_data;
             vgmstream->coding_type = coding_FFmpeg;
diff --git a/src/meta/sps_n1.c b/src/meta/sps_n1.c
new file mode 100644
index 00000000..5ac38d25
--- /dev/null
+++ b/src/meta/sps_n1.c
@@ -0,0 +1,82 @@
+#include "meta.h"
+#include "../coding/coding.h"
+
+static STREAMFILE* setup_sps_streamfile(STREAMFILE *streamfile, off_t subfile_offset, size_t subfile_size, char* extension);
+
+/* .SPS - Nippon Ichi's RIFF AT3 wrapper [ClaDun (PSP)] */
+VGMSTREAM * init_vgmstream_sps_n1(STREAMFILE *streamFile) {
+    VGMSTREAM *vgmstream = NULL;
+    STREAMFILE *temp_streamFile = NULL;
+    int type, sample_rate;
+    off_t subfile_offset;
+    size_t subfile_size;
+
+    /* check extensions */
+    if ( !check_extensions(streamFile,"sps"))
+        goto fail;
+
+    /* mini header */
+    type = read_32bitLE(0x00,streamFile); //todo channels? all known VAG are mono and AT3 stereo
+    subfile_size = read_32bitLE(0x04,streamFile);
+    sample_rate = (uint16_t)read_16bitLE(0x08,streamFile);
+    /* 0x0a/0b: stereo+loop flags? */
+    //num_samples = read_32bitLE(0x0c,streamFile);
+    subfile_offset = 0x10;
+
+    /* init the VGMSTREAM */
+    switch(type) {
+        case 1: /* .vag */
+            temp_streamFile = setup_sps_streamfile(streamFile, subfile_offset, subfile_size, "vag");
+            if (!temp_streamFile) goto fail;
+
+            vgmstream = init_vgmstream_ps2_vag(temp_streamFile);
+            if (!vgmstream) goto fail;
+            break;
+
+        case 2: /* .at3 */
+            temp_streamFile = setup_sps_streamfile(streamFile, subfile_offset, subfile_size, "at3");
+            if (!temp_streamFile) goto fail;
+
+            vgmstream = init_vgmstream_riff(temp_streamFile);
+            if (!vgmstream) goto fail;
+            break;
+        default:
+            goto fail;
+    }
+
+    //VGM_LOG(vgmstream->num_samples != num_samples,
+    //        "SPS: sps num_samples and subfile num_samples don't match\n");
+    //vgmstream->num_samples = num_samples; //todo adjusted for MAIATRAC3
+    vgmstream->sample_rate = sample_rate; /* .vag header doesn't match */
+
+    close_streamfile(temp_streamFile);
+    return vgmstream;
+
+fail:
+    close_streamfile(temp_streamFile);
+    close_vgmstream(vgmstream);
+    return NULL;
+}
+
+static STREAMFILE* setup_sps_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, char* extension) {
+    STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
+
+    /* setup subfile */
+    new_streamFile = open_wrap_streamfile(streamFile);
+    if (!new_streamFile) goto fail;
+    temp_streamFile = new_streamFile;
+
+    new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size);
+    if (!new_streamFile) goto fail;
+    temp_streamFile = new_streamFile;
+
+    new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,extension);
+    if (!new_streamFile) goto fail;
+    temp_streamFile = new_streamFile;
+
+    return temp_streamFile;
+
+fail:
+    close_streamfile(temp_streamFile);
+    return NULL;
+}
diff --git a/src/meta/sqex_scd.c b/src/meta/sqex_scd.c
index 9862b69e..203fbd9d 100644
--- a/src/meta/sqex_scd.c
+++ b/src/meta/sqex_scd.c
@@ -185,10 +185,9 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
     vgmstream = allocate_vgmstream(channel_count,loop_flag);
     if (!vgmstream) goto fail;
 
-    /* fill in the vital statistics */
-    vgmstream->channels = channel_count;
     vgmstream->sample_rate = sample_rate;
     vgmstream->num_streams = total_subsongs;
+    vgmstream->stream_size = stream_size;
     vgmstream->meta_type = meta_SQEX_SCD;
 
     switch (codec) {
diff --git a/src/meta/sqex_sead.c b/src/meta/sqex_sead.c
new file mode 100644
index 00000000..2c98eaa2
--- /dev/null
+++ b/src/meta/sqex_sead.c
@@ -0,0 +1,273 @@
+#include "meta.h"
+#include "../coding/coding.h"
+
+
+static STREAMFILE* setup_sead_hca_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size);
+
+/* SABF/MABF - Square Enix's "Sead" audio games [Dragon Quest Builders (PS3), Dissidia Opera Omnia (mobile), FF XV (PS4)] */
+VGMSTREAM * init_vgmstream_sqex_sead(STREAMFILE * streamFile) {
+    VGMSTREAM * vgmstream = NULL;
+    off_t start_offset, mtrl_offset, meta_offset, post_meta_offset; //, info_offset, name_offset = 0;
+    size_t stream_size, subheader_size; //, name_size = 0;
+
+    int loop_flag = 0, channel_count, codec, sample_rate, loop_start, loop_end;
+    int is_sab = 0, is_mab = 0;
+    int total_subsongs, target_subsong = streamFile->stream_index;
+
+    int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
+    int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
+
+
+    /* check extensions (.sab: sound/bgm, .mab: music, .sbin: Dissidia Opera Omnia .sab) */
+    if ( !check_extensions(streamFile,"sab,mab,sbin"))
+        goto fail;
+
+
+    /** main header **/
+    if (read_32bitBE(0x00,streamFile) == 0x73616266) { /* "sabf" */
+        is_sab = 1;
+    } else if (read_32bitBE(0x00,streamFile) == 0x6D616266) { /* "mabf" */
+        is_mab = 1;
+    } else {
+        goto fail;
+    }
+
+    //if (read_8bit(0x04,streamFile) != 0x02)  /* version? */
+    //    goto fail;
+    /* 0x04(1): version? (usually 0x02, rarely 0x01, ex FF XV title) */
+    /* 0x05(1): 0x00/01? */
+    /* 0x06(2): chunk size? (usually 0x10, rarely 0x20) */
+    if (read_16bitBE(0x06,streamFile) < 0x100) { /* use size as no apparent flag */
+        read_32bit = read_32bitBE;
+        read_16bit = read_16bitBE;
+    } else {
+        read_32bit = read_32bitLE;
+        read_16bit = read_16bitLE;
+    }
+    /* 0x08(1): ?, 0x09(1): ?, 0x0a(2): ?  */
+    if (read_32bit(0x0c,streamFile) != get_streamfile_size(streamFile))
+        goto fail;
+    /* 0x10(10): file descriptor ("BGM", "Music", "SE", etc) */
+
+
+    /** offset tables **/
+    if (is_sab) {
+        if (read_32bitBE(0x20,streamFile) != 0x736E6420) goto fail; /* "snd " (info) */
+        if (read_32bitBE(0x30,streamFile) != 0x73657120) goto fail; /* "seq " (unknown) */
+        if (read_32bitBE(0x40,streamFile) != 0x74726B20) goto fail; /* "trk " (unknown) */
+        if (read_32bitBE(0x50,streamFile) != 0x6D74726C) goto fail; /* "mtrl" (headers/streams) */
+      //info_offset = read_32bit(0x28,streamFile);
+      //seq_offset  = read_32bit(0x38,streamFile);
+      //trk_offset  = read_32bit(0x48,streamFile);
+        mtrl_offset = read_32bit(0x58,streamFile);
+    }
+    else if (is_mab) {
+        if (read_32bitBE(0x20,streamFile) != 0x6D757363) goto fail; /* "musc" (info) */
+        if (read_32bitBE(0x30,streamFile) != 0x696E7374) goto fail; /* "inst" (unknown) */
+        if (read_32bitBE(0x40,streamFile) != 0x6D74726C) goto fail; /* "mtrl" (headers/streams) */
+      //info_offset = read_32bit(0x28,streamFile);
+      //inst_offset = read_32bit(0x38,streamFile);
+        mtrl_offset = read_32bit(0x48,streamFile);
+    }
+    else {
+        goto fail;
+    }
+    /* each section starts with:
+     * 0x00(2): 0x00/01?, 0x02: size? (0x10), 0x04(2): entries, 0x06+: padded to 0x10
+     * 0x10+0x04*entry: offset from section start, also padded to 0x10 at the end */
+
+    /* find meta_offset in mtrl and total subsongs */
+    {
+        int i;
+        int entries = read_16bit(mtrl_offset+0x04,streamFile);
+        off_t entries_offset = mtrl_offset + 0x10;
+
+        if (target_subsong == 0) target_subsong = 1;
+        total_subsongs = 0;
+        meta_offset = 0;
+
+        /* manually find subsongs as entries can be dummy (ex. sfx banks in Dissidia Opera Omnia) */
+        for (i = 0; i < entries; i++) {
+            off_t entry_offset = mtrl_offset + read_32bit(entries_offset + i*0x04,streamFile);
+
+            if (read_8bit(entry_offset+0x05,streamFile) == 0)
+                continue; /* codec 0 when dummy */
+
+            total_subsongs++;
+            if (!meta_offset && total_subsongs == target_subsong)
+                meta_offset = entry_offset;
+        }
+        if (meta_offset == 0) goto fail;
+        /* SAB can contain 0 entries too */
+    }
+
+
+    /** stream header **/
+    /* 0x00(2): 0x00/01? */
+    /* 0x02(2): base entry size? (0x20) */
+    channel_count   =  read_8bit(meta_offset+0x04,streamFile);
+    codec           =  read_8bit(meta_offset+0x05,streamFile);
+  //entry_id        = read_16bit(meta_offset+0x06,streamFile);
+    sample_rate     = read_32bit(meta_offset+0x08,streamFile);
+    loop_start      = read_32bit(meta_offset+0x0c,streamFile); /* in samples but usually ignored */
+
+    loop_end        = read_32bit(meta_offset+0x10,streamFile);
+    subheader_size  = read_32bit(meta_offset+0x14,streamFile); /* including subfile header */
+    stream_size     = read_32bit(meta_offset+0x18,streamFile); /* not including subfile header */
+    /* 0x1c: null? */
+
+    loop_flag       = (loop_end > 0);
+    post_meta_offset = meta_offset + 0x20;
+
+
+    /** info section (get stream name) **/
+    //if (is_sab) { //todo load name based on entry id
+        /* "snd ": unknown flags/sizes and name */
+        /* 0x08(2): file number within descriptor */
+        /* 0x1a(2): base_entry size (-0x10?) */
+        //name_size = read_32bit(snd_offset+0x20,streamFile);
+        //name_offset = snd_offset+0x70;
+        /* 0x24(4): unique id? (referenced in "seq" section) */
+    //}
+    //else if (is_mab) {
+        /* "musc": unknown flags sizes and names, another format */
+    //}
+
+
+    /* build the VGMSTREAM */
+    vgmstream = allocate_vgmstream(channel_count,loop_flag);
+    if (!vgmstream) goto fail;
+
+    vgmstream->sample_rate = sample_rate;
+    vgmstream->num_streams = total_subsongs;
+    vgmstream->stream_size = stream_size;
+    vgmstream->meta_type = is_sab ? meta_SQEX_SAB : meta_SQEX_MAB;
+
+    switch(codec) {
+
+        case 0x02: { /* MSADPCM [Dragon Quest Builders (Vita) sfx] */
+            start_offset = post_meta_offset + subheader_size;
+
+            /* 0x00 (2): null?, 0x02(2): entry size? */
+            vgmstream->coding_type = coding_MSADPCM;
+            vgmstream->layout_type = layout_none;
+            vgmstream->interleave_block_size = read_16bit(post_meta_offset+0x04,streamFile);
+
+            /* much like AKBs, there are slightly different loop values here, probably more accurate
+             * (if no loop, loop_end doubles as num_samples) */
+            vgmstream->num_samples = msadpcm_bytes_to_samples(stream_size, vgmstream->interleave_block_size, vgmstream->channels);
+            vgmstream->loop_start_sample = read_32bit(post_meta_offset+0x08, streamFile); //loop_start
+            vgmstream->loop_end_sample   = read_32bit(post_meta_offset+0x0c, streamFile); //loop_end
+            break;
+        }
+
+#ifdef VGM_USE_ATRAC9
+        case 0x04: { /* ATRAC9 [Dragon Quest Builders (Vita), Final Fantaxy XV (PS4)] */
+            atrac9_config cfg = {0};
+
+            start_offset = post_meta_offset + subheader_size;
+            /* post header has various typical ATRAC9 values */
+            cfg.channels = vgmstream->channels;
+            cfg.config_data = read_32bit(post_meta_offset+0x0c,streamFile);
+            cfg.encoder_delay = read_32bit(post_meta_offset+0x18,streamFile);
+VGM_LOG("1\n");
+            vgmstream->codec_data = init_atrac9(&cfg);
+            if (!vgmstream->codec_data) goto fail;
+            vgmstream->coding_type = coding_ATRAC9;
+            vgmstream->layout_type = layout_none;
+VGM_LOG("2\n");
+            vgmstream->sample_rate = read_32bit(post_meta_offset+0x1c,streamFile); /* SAB's sample rate can be different but it's ignored */
+            vgmstream->num_samples = read_32bit(post_meta_offset+0x10,streamFile); /* loop values above are also weird and ignored */
+            vgmstream->loop_start_sample = read_32bit(post_meta_offset+0x20, streamFile) - (loop_flag ? cfg.encoder_delay : 0); //loop_start
+            vgmstream->loop_end_sample   = read_32bit(post_meta_offset+0x24, streamFile) - (loop_flag ? cfg.encoder_delay : 0); //loop_end
+            break;
+        }
+#endif
+
+#ifdef VGM_USE_MPEG
+        case 0x06: {  /* MSF subfile (MPEG mode) [Dragon Quest Builders (PS3)] */
+            mpeg_codec_data *mpeg_data = NULL;
+            mpeg_custom_config cfg = {0};
+
+            start_offset = post_meta_offset + subheader_size;
+            /* post header is a proper MSF, but sample rate/loops are ignored in favor of SAB's */
+
+            mpeg_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg);
+            if (!mpeg_data) goto fail;
+            vgmstream->codec_data = mpeg_data;
+            vgmstream->layout_type = layout_none;
+
+            vgmstream->num_samples = mpeg_bytes_to_samples(stream_size, mpeg_data);
+            vgmstream->loop_start_sample = loop_start;
+            vgmstream->loop_end_sample = loop_end;
+            break;
+        }
+#endif
+
+        case 0x07: { /* HCA subfile [Dissidia Opera Omnia (Mobile), Final Fantaxy XV (PS4)] */
+            //todo there is no easy way to use the HCA decoder; try subfile hack for now
+            VGMSTREAM *temp_vgmstream = NULL;
+            STREAMFILE *temp_streamFile = NULL;
+            off_t subfile_offset = post_meta_offset + 0x10;
+            size_t subfile_size = stream_size + subheader_size - 0x10;
+            /* post header has 0x10 unknown + HCA header */
+
+
+            temp_streamFile = setup_sead_hca_streamfile(streamFile, subfile_offset, subfile_size);
+            if (!temp_streamFile) goto fail;
+
+            temp_vgmstream = init_vgmstream_hca(temp_streamFile);
+            if (temp_vgmstream) {
+                /* loops can be slightly different (~1000 samples) but probably HCA's are more accurate */
+                temp_vgmstream->num_streams = vgmstream->num_streams;
+                temp_vgmstream->stream_size = vgmstream->stream_size;
+                temp_vgmstream->meta_type = vgmstream->meta_type;
+
+                close_streamfile(temp_streamFile);
+                close_vgmstream(vgmstream);
+                return temp_vgmstream;
+            }
+            else {
+                close_streamfile(temp_streamFile);
+                goto fail;
+            }
+        }
+
+        case 0x00: /* dummy entry */
+        default:
+            VGM_LOG("SQEX SEAD: unknown codec %x\n", codec);
+            goto fail;
+    }
+
+    /* open the file for reading */
+    if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
+        goto fail;
+    return vgmstream;
+
+fail:
+    close_vgmstream(vgmstream);
+    return NULL;
+}
+
+static STREAMFILE* setup_sead_hca_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size) {
+    STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
+
+    /* setup subfile */
+    new_streamFile = open_wrap_streamfile(streamFile);
+    if (!new_streamFile) goto fail;
+    temp_streamFile = new_streamFile;
+
+    new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size);
+    if (!new_streamFile) goto fail;
+    temp_streamFile = new_streamFile;
+
+    new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,"hca");
+    if (!new_streamFile) goto fail;
+    temp_streamFile = new_streamFile;
+
+    return temp_streamFile;
+
+fail:
+    close_streamfile(temp_streamFile);
+    return NULL;
+}
diff --git a/src/meta/sxd.c b/src/meta/sxd.c
index 26e9904a..13801cad 100644
--- a/src/meta/sxd.c
+++ b/src/meta/sxd.c
@@ -7,13 +7,13 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
     VGMSTREAM * vgmstream = NULL;
     STREAMFILE * streamHeader = NULL;
     off_t start_offset, chunk_offset, first_offset = 0x60, name_offset = 0;
-    size_t chunk_size;
+    size_t chunk_size, stream_size = 0;
 
     int is_separate;
     int loop_flag, channels, codec;
     int sample_rate, num_samples, loop_start_sample, loop_end_sample;
     uint32_t at9_config_data = 0;
-    int total_streams, target_stream = streamFile->stream_index;
+    int total_subsongs, target_subsong = streamFile->stream_index;
 
 
     /* check extension, case insensitive */
@@ -38,16 +38,16 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
     if (!find_chunk_le(streamHeader, 0x57415645,first_offset,0, &chunk_offset,&chunk_size)) goto fail; /* "WAVE" */
 
     /* check multi-streams (usually only in SFX containers) */
-    total_streams = read_32bitLE(chunk_offset+0x04,streamHeader);
-    if (target_stream == 0) target_stream = 1;
-    if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail;
+    total_subsongs = read_32bitLE(chunk_offset+0x04,streamHeader);
+    if (target_subsong == 0) target_subsong = 1;
+    if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
 
     /* read stream header */
     {
         off_t table_offset, header_offset, stream_offset;
 
         /* get target offset using table of relative offsets within WAVE */
-        table_offset  = chunk_offset + 0x08 + 4*(target_stream-1);
+        table_offset  = chunk_offset + 0x08 + 4*(target_subsong-1);
         header_offset = table_offset + read_32bitLE(table_offset,streamHeader);
 
         /* 0x00(4): type/location? (00/01=sxd/RAM?, 02/03=sxd2/stream?) */
@@ -59,7 +59,7 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
         num_samples       = read_32bitLE(header_offset+0x14,streamHeader);
         loop_start_sample = read_32bitLE(header_offset+0x18,streamHeader);
         loop_end_sample   = read_32bitLE(header_offset+0x1c,streamHeader);
-        /* 0x20(4): data size */
+        stream_size       = read_32bitLE(header_offset+0x20,streamHeader);
         stream_offset     = read_32bitLE(header_offset+0x24,streamHeader);
 
         /* Extra data, variable sized and uses some kind of TLVs (HEVAG's is optional and much smaller).
@@ -100,7 +100,7 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
         int num_entries = read_16bitLE(chunk_offset+0x04,streamHeader); /* can be bigger than streams */
         for (i = 0; i < num_entries; i++) {
             uint32_t index = (uint32_t)read_32bitLE(chunk_offset+0x08 + 0x08 + i*0x0c,streamHeader);
-            if (index+1 == target_stream) {
+            if (index+1 == target_subsong) {
                 name_offset = chunk_offset+0x08 + 0x00 + i*0x0c + read_32bitLE(chunk_offset+0x08 + 0x00 + i*0x0c,streamHeader);
                 break;
             }
@@ -116,7 +116,8 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
     vgmstream->num_samples = num_samples;
     vgmstream->loop_start_sample = loop_start_sample;
     vgmstream->loop_end_sample = loop_end_sample;
-    vgmstream->num_streams = total_streams;
+    vgmstream->num_streams = total_subsongs;
+    vgmstream->stream_size = stream_size;
     vgmstream->meta_type = meta_SXD;
     if (name_offset)
         read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamHeader);
@@ -148,7 +149,7 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
             break;
         }
 #endif
-
+      //case 0x28:      /* dummy codec? (found with 0 samples) [Hot Shots Golf: World Invitational (Vita) sfx] */
         default:
             VGM_LOG("SXD: unknown codec 0x%x\n", codec);
             goto fail;
diff --git a/src/meta/ubi_sb.c b/src/meta/ubi_sb.c
index 27495f35..d0ad0272 100644
--- a/src/meta/ubi_sb.c
+++ b/src/meta/ubi_sb.c
@@ -127,6 +127,7 @@ VGMSTREAM * init_vgmstream_ubi_sb(STREAMFILE *streamFile) {
 
     vgmstream->sample_rate = sb.sample_rate;
     vgmstream->num_streams = sb.total_streams;
+    vgmstream->stream_size = sb.stream_size;
     vgmstream->meta_type = meta_UBI_SB;
 
 
diff --git a/src/meta/vxn.c b/src/meta/vxn.c
index 238d3e24..27aacce0 100644
--- a/src/meta/vxn.c
+++ b/src/meta/vxn.c
@@ -7,7 +7,7 @@ VGMSTREAM * init_vgmstream_vxn(STREAMFILE *streamFile) {
     int loop_flag = 0, channel_count, codec, sample_rate, block_align, bits, num_samples;
     off_t start_offset, stream_offset, chunk_offset, first_offset = 0x00;
     size_t stream_size;
-    int total_streams, target_stream = streamFile->stream_index;
+    int total_subsongs, target_subsong = streamFile->stream_index;
 
     /* check extensions */
     if (!check_extensions(streamFile,"vxn"))
@@ -31,13 +31,13 @@ VGMSTREAM * init_vgmstream_vxn(STREAMFILE *streamFile) {
      * (the "Plst" and "Rule" chunks may have order info) */
     if (!find_chunk_le(streamFile, 0x5365676D,first_offset,0, &chunk_offset,NULL))  /* "Segm" */
         goto fail;
-    total_streams = read_32bitLE(chunk_offset+0x00, streamFile);
-    if (target_stream == 0) target_stream = 1;
-    if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail;
+    total_subsongs = read_32bitLE(chunk_offset+0x00, streamFile);
+    if (target_subsong == 0) target_subsong = 1;
+    if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
 
-    stream_offset = read_32bitLE(chunk_offset+0x04 + (target_stream-1)*0x18 + 0x00, streamFile);
-    stream_size   = read_32bitLE(chunk_offset+0x04 + (target_stream-1)*0x18 + 0x04, streamFile);
-    num_samples   = read_32bitLE(chunk_offset+0x04 + (target_stream-1)*0x18 + 0x08, streamFile);
+    stream_offset = read_32bitLE(chunk_offset+0x04 + (target_subsong-1)*0x18 + 0x00, streamFile);
+    stream_size   = read_32bitLE(chunk_offset+0x04 + (target_subsong-1)*0x18 + 0x04, streamFile);
+    num_samples   = read_32bitLE(chunk_offset+0x04 + (target_subsong-1)*0x18 + 0x08, streamFile);
 
     if (!find_chunk_le(streamFile, 0x44617461,first_offset,0, &chunk_offset,NULL)) /* "Data" */
         goto fail;
@@ -50,8 +50,8 @@ VGMSTREAM * init_vgmstream_vxn(STREAMFILE *streamFile) {
 
     vgmstream->sample_rate = sample_rate;
     vgmstream->num_samples = num_samples;
-    vgmstream->num_streams = total_streams;
-
+    vgmstream->num_streams = total_subsongs;
+    vgmstream->stream_size = stream_size;
     vgmstream->meta_type = meta_VXN;
 
     switch (codec) {
diff --git a/src/meta/wwise.c b/src/meta/wwise.c
index e4619791..bf5e97bc 100644
--- a/src/meta/wwise.c
+++ b/src/meta/wwise.c
@@ -233,25 +233,25 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
                     case 0x28: /* early (~2009), ex. The Lord of the Rings: Conquest PC */
                         data_offsets = 0x18;
                         block_offsets = 0; /* no need, full headers are present */
-                        cfg.header_type = TYPE_8;
-                        cfg.packet_type = STANDARD;
-                        cfg.setup_type = HEADER_TRIAD;
+                        cfg.header_type = WWV_TYPE_8;
+                        cfg.packet_type = WWV_STANDARD;
+                        cfg.setup_type = WWV_HEADER_TRIAD;
                         break;
 
                     //case 0x32:  /* ? */
                     case 0x34:  /* common (2010~2011) */
                         data_offsets = 0x18;
                         block_offsets = 0x30;
-                        cfg.header_type = TYPE_6;
-                        cfg.packet_type = STANDARD;
-                        cfg.setup_type = EXTERNAL_CODEBOOKS; /* setup_type will be corrected later */
+                        cfg.header_type = WWV_TYPE_6;
+                        cfg.packet_type = WWV_STANDARD;
+                        cfg.setup_type = WWV_EXTERNAL_CODEBOOKS; /* setup_type will be corrected later */
                         break;
                     case 0x2a:  /* uncommon (mid 2011), ex. infamous 2 PS3 */
                         data_offsets = 0x10;
                         block_offsets = 0x28;
-                        cfg.header_type = TYPE_2;
-                        cfg.packet_type = MODIFIED;
-                        cfg.setup_type = EXTERNAL_CODEBOOKS;
+                        cfg.header_type = WWV_TYPE_2;
+                        cfg.packet_type = WWV_MODIFIED;
+                        cfg.setup_type = WWV_EXTERNAL_CODEBOOKS;
                         break;
                     default:
                         VGM_LOG("WWISE: unknown vorb size 0x%x\n", vorb_size);
@@ -277,11 +277,11 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
 
                     /* if the setup after header starts with "(data)BCV" it's an inline codebook) */
                     if ((id & 0x00FFFFFF) == 0x00424356) { /* 0"BCV" */
-                        cfg.setup_type = FULL_SETUP;
+                        cfg.setup_type = WWV_FULL_SETUP;
                     }
                     /* if the setup is suspiciously big it's probably trimmed inline codebooks */
                     else if (setup_size > 0x200) { /* an external setup it's ~0x100 max + some threshold */
-                        cfg.setup_type = INLINE_CODEBOOKS;
+                        cfg.setup_type = WWV_INLINE_CODEBOOKS;
                     }
                 }
 
@@ -297,13 +297,13 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
                     case 0x30:
                         data_offsets = 0x10;
                         block_offsets = 0x28;
-                        cfg.header_type = TYPE_2;
-                        cfg.packet_type = MODIFIED;
+                        cfg.header_type = WWV_TYPE_2;
+                        cfg.packet_type = WWV_MODIFIED;
 
                         /* setup not detectable by header, so we'll try both; hopefully libvorbis will reject wrong codebooks
                          * - standard: early (<2012), ex. The King of Fighters XIII X360 (2011/11), .ogg (cbs are from aoTuV, too)
                          * - aoTuV603: later (>2012), ex. Sonic & All-Stars Racing Transformed PC (2012/11), .wem */
-                        cfg.setup_type  = is_wem ? AOTUV603_CODEBOOKS : EXTERNAL_CODEBOOKS; /* aoTuV came along .wem */
+                        cfg.setup_type  = is_wem ? WWV_AOTUV603_CODEBOOKS : WWV_EXTERNAL_CODEBOOKS; /* aoTuV came along .wem */
                         break;
 
                     //case 0x2a: /* Rocksmith 2011 X360? */
@@ -326,14 +326,14 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
                     /* all blocksizes I've seen are 0x08+0x0B except Oddworld PSV, that uses 0x09+0x09
                      * (maybe lower spec machines = needs simpler packets) */
                     if (cfg.blocksize_0_exp == cfg.blocksize_1_exp)
-                        cfg.packet_type = STANDARD;
+                        cfg.packet_type = WWV_STANDARD;
                 }
 
                 /* try with the selected codebooks */
                 vgmstream->codec_data = init_vorbis_custom(streamFile, start_offset + setup_offset, VORBIS_WWISE, &cfg);
                 if (!vgmstream->codec_data) {
                     /* codebooks failed: try again with the other type */
-                    cfg.setup_type  = is_wem ? EXTERNAL_CODEBOOKS : AOTUV603_CODEBOOKS;
+                    cfg.setup_type  = is_wem ? WWV_EXTERNAL_CODEBOOKS : WWV_AOTUV603_CODEBOOKS;
                     vgmstream->codec_data = init_vorbis_custom(streamFile, start_offset + setup_offset, VORBIS_WWISE, &cfg);
                     if (!vgmstream->codec_data) goto fail;
                 }
diff --git a/src/meta/xvag.c b/src/meta/xvag.c
index 57a9a9f8..382a4e0a 100644
--- a/src/meta/xvag.c
+++ b/src/meta/xvag.c
@@ -16,7 +16,7 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) {
 
     off_t start_offset, loop_start = 0, loop_end = 0, chunk_offset;
     off_t first_offset = 0x20;
-    size_t chunk_size;
+    size_t chunk_size, stream_size;
 
     /* check extension, case insensitive */
     if (!check_extensions(streamFile,"xvag"))
@@ -51,7 +51,7 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) {
 
     interleave_factor = read_32bit(chunk_offset+0x10,streamFile);
     sample_rate = read_32bit(chunk_offset+0x14,streamFile);
-    /* 0x18: datasize */
+    stream_size = read_32bit(chunk_offset+0x18,streamFile);
 
     /* extra data, seen in versions 0x61+ */
     if (chunk_size > 0x1c) {
@@ -86,6 +86,7 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) {
     vgmstream->sample_rate = sample_rate;
     vgmstream->num_samples = num_samples;
     vgmstream->num_streams = total_subsongs;
+    vgmstream->stream_size = (stream_size / total_subsongs);
     vgmstream->meta_type = meta_XVAG;
 
     switch (codec) {
diff --git a/src/meta/xwb.c b/src/meta/xwb.c
index 403f0d98..88335d95 100644
--- a/src/meta/xwb.c
+++ b/src/meta/xwb.c
@@ -9,7 +9,7 @@
 
 /* the x.x version is just to make it clearer, MS only classifies XACT as 1/2/3 */
 #define XACT1_0_MAX     1           /* Project Gotham Racing 2 (v1), Silent Hill 4 (v1) */
-#define XACT1_1_MAX     3           /* The King of Fighters 2003 (v3) */
+#define XACT1_1_MAX     3           /* Unreal Championship (v2), The King of Fighters 2003 (v3) */
 #define XACT2_0_MAX     34          /* Dead or Alive 4 (v17), Kameo (v23), Table Tennis (v34) */ // v35/36/37 too?
 #define XACT2_1_MAX     38          /* Prey (v38) */ // v39 too?
 #define XACT2_2_MAX     41          /* Blue Dragon (v40) */
@@ -354,47 +354,45 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) {
     vgmstream->loop_start_sample = xwb.loop_start_sample;
     vgmstream->loop_end_sample   = xwb.loop_end_sample;
     vgmstream->num_streams = xwb.streams;
+    vgmstream->stream_size = xwb.stream_size;
     vgmstream->meta_type = meta_XWB;
     get_xsb_name(vgmstream->stream_name,STREAM_NAME_SIZE, target_stream, &xwb, streamFile);
 
     switch(xwb.codec) {
-        case PCM:
-            vgmstream->coding_type = xwb.bits_per_sample == 0 ? coding_PCM8 :
+        case PCM: /* Unreal Championship (Xbox)[PCM8], KOF2003 (Xbox)[PCM16LE], Otomedius (X360)[PCM16BE] */
+            vgmstream->coding_type = xwb.bits_per_sample == 0 ? coding_PCM8_U :
                     (xwb.little_endian ? coding_PCM16LE : coding_PCM16BE);
             vgmstream->layout_type = xwb.channels > 1 ? layout_interleave : layout_none;
             vgmstream->interleave_block_size = xwb.bits_per_sample == 0 ? 0x01 : 0x02;
             break;
 
-        case XBOX_ADPCM:
+        case XBOX_ADPCM: /* Silent Hill 4 (Xbox) */
             vgmstream->coding_type = coding_XBOX;
             vgmstream->layout_type = layout_none;
             break;
 
-        case MS_ADPCM:
+        case MS_ADPCM: /* Persona 4 Ultimax (AC) */
             vgmstream->coding_type = coding_MSADPCM;
             vgmstream->layout_type = layout_none;
             vgmstream->interleave_block_size = (xwb.block_align + 22) * xwb.channels; /*22=CONVERSION_OFFSET (?)*/
             break;
 
 #ifdef VGM_USE_FFMPEG
-        case XMA1: {
-            ffmpeg_codec_data *ffmpeg_data = NULL;
+        case XMA1: { /* Kameo (X360) */
             uint8_t buf[100];
             int bytes;
 
             bytes = ffmpeg_make_riff_xma1(buf, 100, vgmstream->num_samples, xwb.stream_size, vgmstream->channels, vgmstream->sample_rate, 0);
             if (bytes <= 0) goto fail;
 
-            ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size);
-            if ( !ffmpeg_data ) goto fail;
-            vgmstream->codec_data = ffmpeg_data;
+            vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size);
+            if (!vgmstream->codec_data) goto fail;
             vgmstream->coding_type = coding_FFmpeg;
             vgmstream->layout_type = layout_none;
             break;
         }
 
-        case XMA2: {
-            ffmpeg_codec_data *ffmpeg_data = NULL;
+        case XMA2: { /* Blue Dragon (X360) */
             uint8_t buf[100];
             int bytes, block_size, block_count;
 
@@ -404,15 +402,14 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) {
             bytes = ffmpeg_make_riff_xma2(buf, 100, vgmstream->num_samples, xwb.stream_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size);
             if (bytes <= 0) goto fail;
 
-            ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size);
-            if ( !ffmpeg_data ) goto fail;
-            vgmstream->codec_data = ffmpeg_data;
+            vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size);
+            if (!vgmstream->codec_data) goto fail;
             vgmstream->coding_type = coding_FFmpeg;
             vgmstream->layout_type = layout_none;
             break;
         }
 
-        case WMA: { /* WMAudio1 (WMA v1) */
+        case WMA: { /* WMAudio1 (WMA v1): Prince of Persia 2 port (Xbox) */
             ffmpeg_codec_data *ffmpeg_data = NULL;
 
             ffmpeg_data = init_ffmpeg_offset(streamFile, xwb.stream_offset,xwb.stream_size);
@@ -427,8 +424,7 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) {
             break;
         }
 
-        case XWMA: { /* WMAudio2 (WMA v2), WMAudio3 (WMA Pro) */
-            ffmpeg_codec_data *ffmpeg_data = NULL;
+        case XWMA: { /* WMAudio2 (WMA v2): BlazBlue (X360), WMAudio3 (WMA Pro): ? */
             uint8_t buf[100];
             int bytes, bps_index, block_align, block_index, avg_bps, wma_codec;
 
@@ -444,15 +440,14 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) {
             bytes = ffmpeg_make_riff_xwma(buf, 100, wma_codec, xwb.stream_size, vgmstream->channels, vgmstream->sample_rate, avg_bps, block_align);
             if (bytes <= 0) goto fail;
 
-            ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size);
-            if ( !ffmpeg_data ) goto fail;
-            vgmstream->codec_data = ffmpeg_data;
+            vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size);
+            if (!vgmstream->codec_data) goto fail;
             vgmstream->coding_type = coding_FFmpeg;
             vgmstream->layout_type = layout_none;
             break;
         }
 
-        case ATRAC3: { /* Techland PS3 extension */
+        case ATRAC3: { /* Techland PS3 extension: Sniper Ghost Warrior (PS3) */
             uint8_t buf[200];
             int bytes;
 
diff --git a/src/meta/xwc.c b/src/meta/xwc.c
new file mode 100644
index 00000000..a9f7b4c5
--- /dev/null
+++ b/src/meta/xwc.c
@@ -0,0 +1,108 @@
+#include "meta.h"
+#include "../coding/coding.h"
+
+/* .XWC - Starbreeze games [Chronicles of Riddick: Assault on Dark Athena, Syndicate] */
+VGMSTREAM * init_vgmstream_xwc(STREAMFILE *streamFile) {
+    VGMSTREAM * vgmstream = NULL;
+	off_t start_offset;
+	size_t data_size;
+    int loop_flag, channel_count, codec;
+
+    /* check extensions (.xwc is the extension of the bigfile, individual files don't have one) */
+    if ( !check_extensions(streamFile,"xwc"))
+        goto fail;
+
+    if(read_32bitBE(0x00,streamFile) != 0x00040000 && /* version? */
+       read_32bitBE(0x04,streamFile) != 0x00900000)
+        goto fail;
+
+    data_size = read_32bitLE(0x08, streamFile); /* including subheader */
+	channel_count = read_32bitLE(0x0c, streamFile);
+	/* 0x10: num_samples */
+	/* 0x14: 0x8000? */
+	codec = read_32bitBE(0x24, streamFile);
+	/* 0x28: num_samples */
+	/* 0x2c: config data? (first nibble: 0x4=mono, 0x8=stereo) */
+	/* 0x30+: codec dependant */
+    loop_flag = 0; /* seemingly not in the file */
+
+
+    /* build the VGMSTREAM */
+    vgmstream = allocate_vgmstream(channel_count,loop_flag);
+    if (!vgmstream) goto fail;
+
+    vgmstream->num_samples = read_32bitLE(0x28, streamFile);
+    vgmstream->meta_type = meta_XWC;
+
+    switch(codec) {
+#ifdef VGM_USE_MPEG
+        case 0x4D504547: { /* "MPEG" (PS3) */
+            mpeg_custom_config cfg = {0};
+
+            start_offset = 0x800;
+            vgmstream->num_samples = read_32bitLE(0x30, streamFile); /* with encoder delay */ //todo improve
+            cfg.data_size = read_32bitLE(0x34, streamFile); //data_size - 0x28;
+
+            vgmstream->codec_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg);
+            if (!vgmstream->codec_data) goto fail;
+            vgmstream->layout_type = layout_none;
+
+            vgmstream->sample_rate = ((mpeg_codec_data*)vgmstream->codec_data)->sample_rate_per_frame;
+            break;
+        }
+#endif
+#ifdef VGM_USE_FFMPEG
+        case 0x584D4100: { /* "XMA\0" (X360) */
+            uint8_t buf[0x100];
+            int32_t bytes, seek_size, block_size, block_count, sample_rate;
+
+            seek_size = read_32bitLE(0x30, streamFile);
+            start_offset = 0x34 + seek_size + read_32bitLE(0x34+seek_size, streamFile) + 0x08;
+            start_offset += (start_offset % 0x800) ? 0x800 - (start_offset % 0x800) : 0; /* padded */
+
+            sample_rate = read_32bitBE(0x34+seek_size+0x10, streamFile);
+            block_size = read_32bitBE(0x34+seek_size+0x1c, streamFile);
+            block_count = read_32bitBE(0x34+seek_size+0x28, streamFile);
+            /* others: scrambled RIFF fmt BE values */
+
+            bytes = ffmpeg_make_riff_xma2(buf,0x100, vgmstream->num_samples, data_size, vgmstream->channels, sample_rate, block_count, block_size);
+            if (bytes <= 0) goto fail;
+
+            vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size - start_offset - 0x28);
+            if (!vgmstream->codec_data) goto fail;
+            vgmstream->coding_type = coding_FFmpeg;
+            vgmstream->layout_type = layout_none;
+
+            vgmstream->sample_rate = sample_rate;
+            break;
+        }
+
+        case 0x564F5242: { /* "VORB" (PC) */
+            start_offset = 0x30;
+
+            vgmstream->codec_data = init_ffmpeg_offset(streamFile, start_offset, data_size - start_offset - 0x28);
+            if ( !vgmstream->codec_data ) goto fail;
+            vgmstream->coding_type = coding_FFmpeg;
+            vgmstream->layout_type = layout_none;
+
+            vgmstream->sample_rate = read_32bitLE(start_offset + 0x28, streamFile);
+            break;
+        }
+#endif
+        default:
+            goto fail;
+    }
+
+    if (vgmstream->sample_rate != 48000) { /* get from config data instead of codecs? */
+        VGM_LOG("XWC: unexpected sample rate %i\n",vgmstream->sample_rate);
+        goto fail;
+    }
+
+    if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
+        goto fail;
+    return vgmstream;
+
+fail:
+    close_vgmstream(vgmstream);
+    return NULL;
+}
diff --git a/src/streamfile.c b/src/streamfile.c
index 61e8b3bf..f62b7842 100644
--- a/src/streamfile.c
+++ b/src/streamfile.c
@@ -250,6 +250,8 @@ STREAMFILE * open_stdio_streamfile_by_file(FILE * file, const char * filename) {
 /* **************************************************** */
 
 //todo stream_index: copy? pass? funtion? external?
+//todo use realnames on reopen? simplify?
+//todo use safe string ops, this ain't easy
 
 typedef struct {
     STREAMFILE sf;
@@ -331,8 +333,18 @@ static void clamp_get_realname(CLAMP_STREAMFILE *streamfile, char *buffer, size_
     streamfile->inner_sf->get_realname(streamfile->inner_sf, buffer, length); /* default */
 }
 static STREAMFILE *clamp_open(CLAMP_STREAMFILE *streamfile, const char * const filename, size_t buffersize) {
-    STREAMFILE *new_inner_sf = streamfile->inner_sf->open(streamfile->inner_sf,filename,buffersize);
-    return open_clamp_streamfile(new_inner_sf, streamfile->start, streamfile->size);
+    char original_filename[PATH_LIMIT];
+    STREAMFILE *new_inner_sf;
+
+    new_inner_sf = streamfile->inner_sf->open(streamfile->inner_sf,filename,buffersize);
+    streamfile->inner_sf->get_name(streamfile->inner_sf, original_filename, PATH_LIMIT);
+
+    /* detect re-opening the file */
+    if (strcmp(filename, original_filename) == 0) {
+        return open_clamp_streamfile(new_inner_sf, streamfile->start, streamfile->size); /* clamp again */
+    } else {
+        return new_inner_sf; /**/
+    }
 }
 static void clamp_close(CLAMP_STREAMFILE *streamfile) {
     streamfile->inner_sf->close(streamfile->inner_sf);
@@ -342,7 +354,8 @@ static void clamp_close(CLAMP_STREAMFILE *streamfile) {
 STREAMFILE *open_clamp_streamfile(STREAMFILE *streamfile, off_t start, size_t size) {
     CLAMP_STREAMFILE *this_sf;
 
-    if (!streamfile || !size || start > size) return NULL;
+    if (!streamfile || !size) return NULL;
+    if (start + size > get_streamfile_size(streamfile)) return NULL;
 
     this_sf = calloc(1,sizeof(CLAMP_STREAMFILE));
     if (!this_sf) return NULL;
@@ -391,6 +404,7 @@ static void io_get_realname(IO_STREAMFILE *streamfile, char *buffer, size_t leng
     streamfile->inner_sf->get_realname(streamfile->inner_sf, buffer, length); /* default */
 }
 static STREAMFILE *io_open(IO_STREAMFILE *streamfile, const char * const filename, size_t buffersize) {
+    //todo should have some flag to decide if opening other files with IO
     STREAMFILE *new_inner_sf = streamfile->inner_sf->open(streamfile->inner_sf,filename,buffersize);
     return open_io_streamfile(new_inner_sf, streamfile->data, streamfile->data_size, streamfile->read_callback);
 }
@@ -436,6 +450,239 @@ STREAMFILE *open_io_streamfile(STREAMFILE *streamfile, void* data, size_t data_s
 
 /* **************************************************** */
 
+typedef struct {
+    STREAMFILE sf;
+
+    STREAMFILE *inner_sf;
+    char fakename[PATH_LIMIT];
+} FAKENAME_STREAMFILE;
+
+static size_t fakename_read(FAKENAME_STREAMFILE *streamfile, uint8_t * dest, off_t offset, size_t length) {
+    return streamfile->inner_sf->read(streamfile->inner_sf, dest, offset, length); /* default */
+}
+static size_t fakename_get_size(FAKENAME_STREAMFILE * streamfile) {
+    return streamfile->inner_sf->get_size(streamfile->inner_sf); /* default */
+}
+static size_t fakename_get_offset(FAKENAME_STREAMFILE * streamfile) {
+    return streamfile->inner_sf->get_offset(streamfile->inner_sf); /* default */
+}
+static void fakename_get_name(FAKENAME_STREAMFILE *streamfile, char *buffer, size_t length) {
+    strncpy(buffer,streamfile->fakename,length);
+    buffer[length-1]='\0';
+}
+static void fakename_get_realname(FAKENAME_STREAMFILE *streamfile, char *buffer, size_t length) {
+    fakename_get_name(streamfile, buffer, length);
+}
+static STREAMFILE *fakename_open(FAKENAME_STREAMFILE *streamfile, const char * const filename, size_t buffersize) {
+    /* detect re-opening the file */
+    if (strcmp(filename, streamfile->fakename) == 0) {
+        STREAMFILE *new_inner_sf;
+        char original_filename[PATH_LIMIT];
+
+        streamfile->inner_sf->get_name(streamfile->inner_sf, original_filename, PATH_LIMIT);
+        new_inner_sf = streamfile->inner_sf->open(streamfile->inner_sf, original_filename, buffersize);
+        return open_fakename_streamfile(new_inner_sf, streamfile->fakename, NULL);
+    }
+    else {
+        return streamfile->inner_sf->open(streamfile->inner_sf, filename, buffersize);
+    }
+}
+static void fakename_close(FAKENAME_STREAMFILE *streamfile) {
+    streamfile->inner_sf->close(streamfile->inner_sf);
+    free(streamfile);
+}
+
+STREAMFILE *open_fakename_streamfile(STREAMFILE *streamfile, char * fakename, char* fakeext) {
+    FAKENAME_STREAMFILE *this_sf;
+
+    if (!streamfile || (!fakename && !fakeext)) return NULL;
+
+    this_sf = calloc(1,sizeof(FAKENAME_STREAMFILE));
+    if (!this_sf) return NULL;
+
+    /* set callbacks and internals */
+    this_sf->sf.read = (void*)fakename_read;
+    this_sf->sf.get_size = (void*)fakename_get_size;
+    this_sf->sf.get_offset = (void*)fakename_get_offset;
+    this_sf->sf.get_name = (void*)fakename_get_name;
+    this_sf->sf.get_realname = (void*)fakename_get_realname;
+    this_sf->sf.open = (void*)fakename_open;
+    this_sf->sf.close = (void*)fakename_close;
+    this_sf->sf.stream_index = streamfile->stream_index;
+
+    this_sf->inner_sf = streamfile;
+
+    /* copy passed name or retain current, and swap extension if expected */
+    if (fakename) {
+        strcpy(this_sf->fakename,fakename);
+    } else {
+        streamfile->get_name(streamfile, this_sf->fakename, PATH_LIMIT);
+    }
+    if (fakeext) {
+        char * ext = strrchr(this_sf->fakename,'.');
+        if (ext != NULL)
+            ext[1] = '\0'; /* truncate past dot */
+        strcat(this_sf->fakename, fakeext);
+    }
+
+    return &this_sf->sf;
+}
+
+/* **************************************************** */
+
+
+typedef struct {
+    STREAMFILE sf;
+
+    STREAMFILE **inner_sfs;
+    size_t inner_sfs_size;
+    size_t *sizes;
+    off_t size;
+    off_t offset;
+} MULTIFILE_STREAMFILE;
+
+static size_t multifile_read(MULTIFILE_STREAMFILE *streamfile, uint8_t * dest, off_t offset, size_t length) {
+    int i, segment = 0;
+    off_t segment_offset = 0;
+    size_t done = 0;
+
+    if (offset > streamfile->size) {
+        streamfile->offset = streamfile->size;
+        return 0;
+    }
+
+    /* map external offset to multifile offset */
+    for (i = 0; i < streamfile->inner_sfs_size; i++) {
+        size_t segment_size = streamfile->sizes[i];
+        /* check if offset falls in this segment */
+        if (offset >= segment_offset && offset < segment_offset + segment_size) {
+            segment = i;
+            segment_offset = offset - segment_offset;
+            break;
+        }
+
+        segment_offset += segment_size;
+    }
+
+    /* reads can span multiple segments */
+    while(done < length) {
+        if (segment >= streamfile->inner_sfs_size) /* over last segment, not fully done */
+            break;
+        /* reads over segment size are ok, will return smaller value and continue next segment */
+        done += streamfile->inner_sfs[segment]->read(streamfile->inner_sfs[segment], dest+done, segment_offset, length - done);
+        segment++;
+        segment_offset = 0;
+    }
+
+    streamfile->offset = offset + done;
+    return done;
+}
+static size_t multifile_get_size(MULTIFILE_STREAMFILE *streamfile) {
+    return streamfile->size;
+}
+static size_t multifile_get_offset(MULTIFILE_STREAMFILE * streamfile) {
+    return streamfile->offset;
+}
+static void multifile_get_name(MULTIFILE_STREAMFILE *streamfile, char *buffer, size_t length) {
+    streamfile->inner_sfs[0]->get_name(streamfile->inner_sfs[0], buffer, length);
+}
+static void multifile_get_realname(MULTIFILE_STREAMFILE *streamfile, char *buffer, size_t length) {
+    multifile_get_name(streamfile, buffer, length);
+}
+static STREAMFILE *multifile_open(MULTIFILE_STREAMFILE *streamfile, const char * const filename, size_t buffersize) {
+    char original_filename[PATH_LIMIT];
+    STREAMFILE *new_sf = NULL;
+    STREAMFILE **new_inner_sfs = NULL;
+    int i;
+
+    streamfile->inner_sfs[0]->get_name(streamfile->inner_sfs[0], original_filename, PATH_LIMIT);
+
+    /* detect re-opening the file */
+    if (strcmp(filename, original_filename) == 0) { /* same multifile */
+        new_inner_sfs = calloc(streamfile->inner_sfs_size, sizeof(STREAMFILE*));
+        if (!new_inner_sfs) goto fail;
+
+        for (i = 0; i < streamfile->inner_sfs_size; i++) {
+            streamfile->inner_sfs[i]->get_name(streamfile->inner_sfs[i], original_filename, PATH_LIMIT);
+            new_inner_sfs[i] = streamfile->inner_sfs[i]->open(streamfile->inner_sfs[i], original_filename, buffersize);
+            if (!new_inner_sfs[i]) goto fail;
+        }
+
+        new_sf = open_multifile_streamfile(new_inner_sfs, streamfile->inner_sfs_size);
+        if (!new_sf) goto fail;
+
+        return new_sf;
+    }
+    else {
+        return streamfile->inner_sfs[0]->open(streamfile->inner_sfs[0], filename, buffersize); /* regular file */
+    }
+
+fail:
+    if (new_inner_sfs) {
+        for (i = 0; i < streamfile->inner_sfs_size; i++)
+            close_streamfile(new_inner_sfs[i]);
+    }
+    free(new_inner_sfs);
+    return NULL;
+}
+static void multifile_close(MULTIFILE_STREAMFILE *streamfile) {
+    int i;
+    for (i = 0; i < streamfile->inner_sfs_size; i++) {
+        for (i = 0; i < streamfile->inner_sfs_size; i++)
+            close_streamfile(streamfile->inner_sfs[i]);
+    }
+    free(streamfile->inner_sfs);
+    free(streamfile->sizes);
+    free(streamfile);
+}
+
+STREAMFILE *open_multifile_streamfile(STREAMFILE **streamfiles, size_t streamfiles_size) {
+    MULTIFILE_STREAMFILE *this_sf;
+    int i;
+
+    if (!streamfiles || !streamfiles_size) return NULL;
+    for (i = 0; i < streamfiles_size; i++) {
+        if (!streamfiles[i]) return NULL;
+    }
+
+    this_sf = calloc(1,sizeof(MULTIFILE_STREAMFILE));
+    if (!this_sf) goto fail;
+
+    /* set callbacks and internals */
+    this_sf->sf.read = (void*)multifile_read;
+    this_sf->sf.get_size = (void*)multifile_get_size;
+    this_sf->sf.get_offset = (void*)multifile_get_offset;
+    this_sf->sf.get_name = (void*)multifile_get_name;
+    this_sf->sf.get_realname = (void*)multifile_get_realname;
+    this_sf->sf.open = (void*)multifile_open;
+    this_sf->sf.close = (void*)multifile_close;
+    this_sf->sf.stream_index = streamfiles[0]->stream_index;
+
+    this_sf->inner_sfs_size = streamfiles_size;
+    this_sf->inner_sfs = calloc(streamfiles_size, sizeof(STREAMFILE*));
+    if (!this_sf->inner_sfs) goto fail;
+    this_sf->sizes = calloc(streamfiles_size, sizeof(size_t));
+    if (!this_sf->sizes) goto fail;
+
+    for (i = 0; i < this_sf->inner_sfs_size; i++) {
+        this_sf->inner_sfs[i] = streamfiles[i];
+        this_sf->sizes[i] = streamfiles[i]->get_size(streamfiles[i]);
+        this_sf->size += this_sf->sizes[i];
+    }
+
+    return &this_sf->sf;
+
+fail:
+    if (this_sf) {
+        free(this_sf->inner_sfs);
+        free(this_sf->sizes);
+    }
+    free(this_sf);
+    return NULL;
+}
+
+/* **************************************************** */
+
 /* Read a line into dst. The source files are lines separated by CRLF (Windows) / LF (Unux) / CR (Mac).
  * The line will be null-terminated and CR/LF removed if found.
  *
diff --git a/src/streamfile.h b/src/streamfile.h
index fa9da54f..4d0aba65 100644
--- a/src/streamfile.h
+++ b/src/streamfile.h
@@ -81,6 +81,17 @@ STREAMFILE *open_clamp_streamfile(STREAMFILE *streamfile, off_t start, size_t si
  * Can be used with subfiles inside a bigger file, so it looks standard to a meta. */
 STREAMFILE *open_io_streamfile(STREAMFILE *streamfile, void* data, size_t data_size, void* read_callback);//void* size_callback, void* seek_callback);
 
+/* A STREAMFILE that reports a fake name, but still re-opens itself properly.
+ * Can be used to trick a meta's extension check (to call from another, with a modified SF).
+ * When fakename isn't supplied it's read from the streamfile, and the extension swapped with fakeext.
+ * If the fakename is an existing file, open won't work on it as it'll reopen the fake-named streamfile. */
+STREAMFILE *open_fakename_streamfile(STREAMFILE *streamfile, char * fakename, char * fakeext);
+
+/* A streamfile formed from multiple streamfiles, their data joined during reads.
+ * Can be used when data is segmented in multiple separate files.
+ * The first streamfile is used to get names, stream index and so on. */
+STREAMFILE *open_multifile_streamfile(STREAMFILE **streamfiles, size_t streamfiles_size);
+
 
 /* close a file, destroy the STREAMFILE object */
 static inline void close_streamfile(STREAMFILE * streamfile) {
diff --git a/src/vgmstream.c b/src/vgmstream.c
index 48cb3bcb..3677f6fc 100644
--- a/src/vgmstream.c
+++ b/src/vgmstream.c
@@ -375,6 +375,11 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
     init_vgmstream_mogg,
     init_vgmstream_kma9,
     init_vgmstream_fsb_encrypted,
+    init_vgmstream_xwc,
+    init_vgmstream_atsl3,
+    init_vgmstream_sps_n1,
+    init_vgmstream_atx,
+    init_vgmstream_sqex_sead,
 
     init_vgmstream_txth,  /* should go at the end (lower priority) */
 #ifdef VGM_USE_FFMPEG
@@ -2195,6 +2200,11 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
     }
     concatn(length,desc,temp);
 
+    snprintf(temp,TEMPSIZE,
+            "\nbitrate: %d kbps",
+            get_vgmstream_average_bitrate(vgmstream) / 1000);
+    concatn(length,desc,temp);
+
     /* only interesting if more than one */
     if (vgmstream->num_streams > 1) {
         snprintf(temp,TEMPSIZE,
@@ -2443,9 +2453,11 @@ static STREAMFILE * get_vgmstream_average_bitrate_channel_streamfile(VGMSTREAM *
     return vgmstream->ch[channel].streamfile;
 }
 
+static int get_vgmstream_average_bitrate_from_size(size_t size, int sample_rate, int length_samples) {
+    return (int)((int64_t)size * 8 * sample_rate / length_samples);
+}
 static int get_vgmstream_average_bitrate_from_streamfile(STREAMFILE * streamfile, int sample_rate, int length_samples) {
-    // todo: not correct in subsongs or formats which only use part of the data
-    return (int)((int64_t)get_streamfile_size(streamfile) * 8 * sample_rate / length_samples);
+    return get_vgmstream_average_bitrate_from_size(get_streamfile_size(streamfile), sample_rate, length_samples);
 }
 
 /* Return the average bitrate in bps of all unique files contained within this stream. */
@@ -2463,6 +2475,11 @@ int get_vgmstream_average_bitrate(VGMSTREAM * vgmstream) {
     if (!sample_rate || !channels || !length_samples)
         return 0;
 
+    /* subsongs need to report this to properly calculate */
+    if (vgmstream->stream_size) {
+        return get_vgmstream_average_bitrate_from_size(vgmstream->stream_size, sample_rate, length_samples);
+    }
+
     if (channels >= 1) {
         streamFile = get_vgmstream_average_bitrate_channel_streamfile(vgmstream, 0);
         if (streamFile) {
diff --git a/src/vgmstream.h b/src/vgmstream.h
index b474a4e7..63c9d2bc 100644
--- a/src/vgmstream.h
+++ b/src/vgmstream.h
@@ -650,12 +650,15 @@ typedef enum {
     meta_OGG_SLI,           /* Ogg Vorbis file w/ companion .sli for looping */
     meta_OGG_SLI2,          /* Ogg Vorbis file w/ different styled .sli for looping */
     meta_OGG_SFL,           /* Ogg Vorbis file w/ .sfl (RIFF SFPL) for looping */
-    meta_OGG_UM3,           /* Ogg Vorbis with optional first 0x800 bytes XOR 0xFF */
-    meta_OGG_KOVS,          /* Ogg Vorbis with extra header and 0x100 bytes XOR */
-    meta_OGG_PSYCHIC,       /* Ogg Vorbis with all bytes -0x23 */
-    meta_OGG_SNGW,          /* Ogg Vorbis with optional key XOR + nibble swap (Capcom PC games) */
-    meta_OGG_ISD,           /* Ogg Vorbis with key XOR (Azure Striker Gunvolt PC) */
+    meta_OGG_UM3,           /* Ogg Vorbis with optional encryption */
+    meta_OGG_KOVS,          /* Ogg Vorbis with encryption (Koei Tecmo Games) */
+    meta_OGG_PSYCHIC,       /* Ogg Vorbis with encryption */
+    meta_OGG_SNGW,          /* Ogg Vorbis with optional encryption (Capcom PC games) */
+    meta_OGG_ISD,           /* Ogg Vorbis with encryption (Azure Striker Gunvolt PC) */
     meta_KMA9,              /* Koei Tecmo [Nobunaga no Yabou - Souzou (Vita)] */
+    meta_XWC,               /* Starbreeze games */
+    meta_SQEX_SAB,          /* Square-Enix newest middleware (sound) */
+    meta_SQEX_MAB,          /* Square-Enix newest middleware (music) */
 
 #ifdef VGM_USE_MP4V2
     meta_MP4,               /* AAC (iOS) */
@@ -734,6 +737,7 @@ typedef struct {
     int num_streams;        /* for multi-stream formats (0=not set/one stream, 1=one stream) */
     int stream_index;       /* selected stream (also 1-based) */
     char stream_name[STREAM_NAME_SIZE]; /* name of the current stream (info), if the file stores it and it's filled */
+    size_t stream_size;     /* info to properly calculate bitrate */
 
     /* looping */
     int loop_flag;              /* is this stream looped? */
@@ -827,9 +831,9 @@ typedef enum {
 } vorbis_custom_t;
 
 /* config for Wwise Vorbis (3 types for flexibility though not all combinations exist) */
-typedef enum { HEADER_TRIAD, FULL_SETUP, INLINE_CODEBOOKS, EXTERNAL_CODEBOOKS, AOTUV603_CODEBOOKS } wwise_setup_t; /* Vorbis setup style */
-typedef enum { TYPE_8, TYPE_6, TYPE_2 } wwise_header_t; /* size of packet headers */
-typedef enum { STANDARD, MODIFIED } wwise_packet_t; /* type of Vorbis packets */
+typedef enum { WWV_HEADER_TRIAD, WWV_FULL_SETUP, WWV_INLINE_CODEBOOKS, WWV_EXTERNAL_CODEBOOKS, WWV_AOTUV603_CODEBOOKS } wwise_setup_t;
+typedef enum { WWV_TYPE_8, WWV_TYPE_6, WWV_TYPE_2 } wwise_header_t;
+typedef enum { WWV_STANDARD, WWV_MODIFIED } wwise_packet_t;
 
 typedef struct {
     /* to reconstruct init packets */
@@ -1097,7 +1101,6 @@ typedef enum {
     FFMPEG_STANDARD,        /* default FFmpeg */
     FFMPEG_SWITCH_OPUS,     /* Opus without Ogg layer */
     FFMPEG_EA_XMA,          /* XMA with padding removed and custom streams in SNS blocks */
-    FFMPEG_BGW_ATRAC3,      /* Encrypted raw ATRAC3 */
   //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 */
@@ -1115,7 +1118,6 @@ typedef struct {
     /* internal sequences, when needed */
     int sequence;
     int samples_done;
-    uint8_t * key;
 } ffmpeg_custom_config;
 
 typedef struct {