diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj
index 3b307b75..f7a09870 100644
--- a/src/libvgmstream.vcproj
+++ b/src/libvgmstream.vcproj
@@ -1341,6 +1341,10 @@
                 <File
                     RelativePath=".\meta\xma.c"
                     >
+                </File>
+                <File
+                    RelativePath=".\meta\xnb.c"
+                    >
                 </File>
 				<File
 					RelativePath=".\meta\xss.c"
diff --git a/src/libvgmstream.vcxproj b/src/libvgmstream.vcxproj
index e4b36c61..da04898c 100644
--- a/src/libvgmstream.vcxproj
+++ b/src/libvgmstream.vcxproj
@@ -436,6 +436,7 @@
     <ClCompile Include="meta\xbox_xwav.c" />
     <ClCompile Include="meta\x360_pasx.c" />
     <ClCompile Include="meta\xma.c" />
+    <ClCompile Include="meta\xnb.c" />
     <ClCompile Include="meta\xss.c" />
     <ClCompile Include="meta\xvag.c" />
     <ClCompile Include="meta\xwb.c" />
diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters
index 01811bf0..9abfc79c 100644
--- a/src/libvgmstream.vcxproj.filters
+++ b/src/libvgmstream.vcxproj.filters
@@ -1216,6 +1216,9 @@
     <ClCompile Include="meta\xma.c">
       <Filter>meta\Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="meta\xnb.c">
+      <Filter>meta\Source Files</Filter>
+    </ClCompile>
     <ClCompile Include="meta\x360_cxs.c">
       <Filter>meta\Source Files</Filter>
     </ClCompile>
diff --git a/src/meta/meta.h b/src/meta/meta.h
index bc0ba6b0..c26f5f80 100644
--- a/src/meta/meta.h
+++ b/src/meta/meta.h
@@ -152,7 +152,7 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE * streamFile);
 
 VGMSTREAM * init_vgmstream_rifx(STREAMFILE * streamFile);
 
-VGMSTREAM * init_vgmstream_xnbm(STREAMFILE * streamFile);
+VGMSTREAM * init_vgmstream_xnb(STREAMFILE * streamFile);
 
 VGMSTREAM * init_vgmstream_pos(STREAMFILE * streamFile);
 
diff --git a/src/meta/riff.c b/src/meta/riff.c
index c7c4265b..56e6afec 100644
--- a/src/meta/riff.c
+++ b/src/meta/riff.c
@@ -840,146 +840,3 @@ fail:
     if (vgmstream) close_vgmstream(vgmstream);
     return NULL;
 }
-
-//todo move
-/* XNB - Microsoft XNA Game Studio 4.0 format */
-VGMSTREAM * init_vgmstream_xnbm(STREAMFILE *streamFile) {
-    VGMSTREAM * vgmstream = NULL;
-    off_t start_offset;
-    int loop_flag = 0, version, flags, num_samples = 0;
-    size_t xnb_size, data_size;
-
-    struct riff_fmt_chunk fmt;
-
-
-    /* check extension, case insensitive */
-    if ( !check_extensions(streamFile,"xnb"))
-        goto fail;
-
-    /* check header */
-    if ((read_32bitBE(0,streamFile) & 0xFFFFFF00) != 0x584E4200) /* "XNB" */
-        goto fail;
-    /* 0x04: platform: �w� = Microsoft Windows, �m� = Windows Phone 7, �x� = Xbox 360, 'a' = Android */
-
-    version = read_8bit(0x04,streamFile);
-    if (version != 5) goto fail; /* XNA 4.0 only */
-
-    flags = read_8bit(0x05,streamFile);
-    if (flags & 0x80) goto fail; /* compressed with XMemCompress, not public */
-    //if (flags & 0x01) goto fail; /* XMA flag? */
-
-    /* "check for truncated XNB" (???) */
-    xnb_size = read_32bitLE(0x06,streamFile);
-    if (get_streamfile_size(streamFile) < xnb_size) goto fail;
-
-    /* XNB contains "type reader" class references to parse "shared resource" data (can be any implemented filetype) */
-    {
-        char reader_name[255+1];
-        off_t current_chunk = 0xa;
-        int reader_string_len;
-        uint32_t fmt_chunk_size;
-        const char * type_sound =  "Microsoft.Xna.Framework.Content.SoundEffectReader"; /* partial "fmt" chunk or XMA */
-        //const char * type_song =  "Microsoft.Xna.Framework.Content.SongReader"; /* just references a companion .wma */
-
-        /* type reader count, accept only one for now */
-        if (read_8bit(current_chunk++, streamFile) != 1)
-            goto fail;
-
-        reader_string_len = read_8bit(current_chunk++, streamFile); /* doesn't count null */
-        if (reader_string_len > 255) goto fail;
-
-        /* check SoundEffect type string */
-        if (read_string(reader_name,reader_string_len+1,current_chunk,streamFile) != reader_string_len)
-            goto fail;
-        if ( strcmp(reader_name, type_sound) != 0 )
-            goto fail;
-        current_chunk += reader_string_len + 1;
-        current_chunk += 4; /* reader version */
-
-        /* shared resource count */
-        if (read_8bit(current_chunk++, streamFile) != 1)
-            goto fail;
-
-        /* shared resource: partial "fmt" chunk */
-        fmt_chunk_size = read_32bitLE(current_chunk, streamFile);
-        current_chunk += 4;
-
-        if (-1 == read_fmt(0, /* big endian == false */
-                  streamFile,
-                  current_chunk-8,  /* read_fmt() expects to skip "fmt "+size */
-                  &fmt,
-                  0,    /* sns == false */
-                  0))   /* mwv == false */
-                  goto fail;
-        current_chunk += fmt_chunk_size;
-
-        data_size = read_32bitLE(current_chunk, streamFile);
-        current_chunk += 4;
-
-        start_offset = current_chunk;
-    }
-
-    switch (fmt.coding_type) {
-        case coding_PCM16LE:
-            num_samples = pcm_bytes_to_samples(data_size, fmt.channel_count, 16);
-            break;
-        case coding_PCM8_U_int:
-            num_samples = pcm_bytes_to_samples(data_size, fmt.channel_count, 8);
-            break;
-        case coding_MSADPCM:
-            num_samples = msadpcm_bytes_to_samples(data_size, fmt.block_size, fmt.channel_count);
-            break;
-        case coding_MS_IMA:
-            num_samples = (data_size / fmt.block_size) * (fmt.block_size - 4 * fmt.channel_count) * 2 / fmt.channel_count +
-                ((data_size % fmt.block_size) ? (data_size % fmt.block_size - 4 * fmt.channel_count) * 2 / fmt.channel_count : 0);
-            break;
-        default:
-            VGM_LOG("XNB: unknown codec 0x%x\n", fmt.coding_type);
-            goto fail;
-    }
-
-
-    /* build the VGMSTREAM */
-    vgmstream = allocate_vgmstream(fmt.channel_count,loop_flag);
-    if (!vgmstream) goto fail;
-
-    vgmstream->num_samples = num_samples;
-    vgmstream->sample_rate = fmt.sample_rate;
-
-    vgmstream->meta_type = meta_XNB;
-    vgmstream->coding_type = fmt.coding_type;
-
-    if (fmt.channel_count > 1) {
-        switch (fmt.coding_type) {
-            case coding_PCM8_U_int:
-            case coding_MS_IMA:
-            case coding_MSADPCM:
-                vgmstream->layout_type = layout_none;
-                break;
-            default:
-                vgmstream->layout_type = layout_interleave;
-                break;
-        }
-    }
-
-    switch (fmt.coding_type) {
-        case coding_MSADPCM:
-        case coding_MS_IMA:
-            vgmstream->interleave_block_size = fmt.block_size;
-            break;
-        default:
-            vgmstream->interleave_block_size = fmt.interleave;
-            break;
-    }
-
-
-    if ( !vgmstream_open_stream(vgmstream,streamFile,start_offset) )
-        goto fail;
-
-    return vgmstream;
-
-fail:
-    close_vgmstream(vgmstream);
-    return NULL;
-}
-
diff --git a/src/meta/xnb.c b/src/meta/xnb.c
new file mode 100644
index 00000000..c0034a71
--- /dev/null
+++ b/src/meta/xnb.c
@@ -0,0 +1,123 @@
+#include "meta.h"
+#include "../coding/coding.h"
+
+/* XNB - Microsoft XNA Game Studio 4.0 format */
+VGMSTREAM * init_vgmstream_xnb(STREAMFILE *streamFile) {
+    VGMSTREAM * vgmstream = NULL;
+    off_t start_offset;
+    int loop_flag = 0, channel_count;
+    int flags, codec, sample_rate, block_size, bps;
+    size_t xnb_size, data_size;
+
+
+    /* check extension, case insensitive */
+    if ( !check_extensions(streamFile,"xnb"))
+        goto fail;
+
+    /* check header */
+    if ((read_32bitBE(0,streamFile) & 0xFFFFFF00) != 0x584E4200) /* "XNB" */
+        goto fail;
+    /* 0x04: platform: �w� = Microsoft Windows, �m� = Windows Phone 7, �x� = Xbox 360, 'a' = Android */
+
+    if (read_8bit(0x04,streamFile) != 0x05)  /* XNA 4.0 version only */
+        goto fail;
+
+    flags = read_8bit(0x05,streamFile);
+    if (flags & 0x80) goto fail; /* compressed with XMemCompress */
+    //if (flags & 0x01) goto fail; /* XMA/big endian flag? */
+
+    /* "check for truncated XNB" (???) */
+    xnb_size = read_32bitLE(0x06,streamFile);
+    if (get_streamfile_size(streamFile) < xnb_size) goto fail;
+
+    /* XNB contains "type reader" class references to parse "shared resource" data (can be any implemented filetype) */
+    {
+        char reader_name[255+1];
+        off_t current_chunk = 0xa;
+        int reader_string_len;
+        uint32_t fmt_chunk_size;
+        const char * type_sound =  "Microsoft.Xna.Framework.Content.SoundEffectReader"; /* partial "fmt" chunk or XMA */
+        //const char * type_song =  "Microsoft.Xna.Framework.Content.SongReader"; /* just references a companion .wma */
+
+        /* type reader count, accept only one for now */
+        if (read_8bit(current_chunk++, streamFile) != 1)
+            goto fail;
+
+        reader_string_len = read_8bit(current_chunk++, streamFile); /* doesn't count null */
+        if (reader_string_len > 255) goto fail;
+
+        /* check SoundEffect type string */
+        if (read_string(reader_name,reader_string_len+1,current_chunk,streamFile) != reader_string_len)
+            goto fail;
+        if ( strcmp(reader_name, type_sound) != 0 )
+            goto fail;
+        current_chunk += reader_string_len + 1;
+        current_chunk += 4; /* reader version */
+
+        /* shared resource count */
+        if (read_8bit(current_chunk++, streamFile) != 1)
+            goto fail;
+
+        /* shared resource: partial "fmt" chunk */
+        fmt_chunk_size = read_32bitLE(current_chunk, streamFile);
+        current_chunk += 4;
+
+        {
+            codec         = read_16bitLE(current_chunk+0x00, streamFile);
+            channel_count = read_16bitLE(current_chunk+0x02, streamFile);
+            sample_rate   = read_32bitLE(current_chunk+0x04, streamFile);
+            block_size    = read_16bitLE(current_chunk+0x0c, streamFile);
+            bps           = read_16bitLE(current_chunk+0x0e, streamFile);
+        }
+
+        current_chunk += fmt_chunk_size;
+
+        data_size = read_32bitLE(current_chunk, streamFile);
+        current_chunk += 4;
+
+        start_offset = current_chunk;
+    }
+
+
+    /* build the VGMSTREAM */
+    vgmstream = allocate_vgmstream(channel_count,loop_flag);
+    if (!vgmstream) goto fail;
+
+    vgmstream->sample_rate = sample_rate;
+    vgmstream->meta_type = meta_XNB;
+
+    switch (codec) {
+        case 0x01:
+            vgmstream->coding_type = bps == 8 ? coding_PCM8_U_int : coding_PCM16LE;
+            vgmstream->layout_type = layout_interleave;
+            vgmstream->interleave_block_size = block_size / channel_count;
+            vgmstream->num_samples = pcm_bytes_to_samples(data_size, channel_count, bps);
+            break;
+
+        case 0x02:
+            vgmstream->coding_type = coding_MSADPCM;
+            vgmstream->layout_type = layout_none;
+            vgmstream->interleave_block_size = block_size;
+            vgmstream->num_samples = msadpcm_bytes_to_samples(data_size, block_size, channel_count);
+            break;
+
+        case 0x11:
+            vgmstream->coding_type = coding_MS_IMA;
+            vgmstream->layout_type = layout_none;
+            vgmstream->interleave_block_size = block_size;
+            vgmstream->num_samples = ms_ima_bytes_to_samples(data_size, block_size, channel_count);
+            break;
+
+        default:
+            VGM_LOG("XNB: unknown codec 0x%x\n", codec);
+            goto fail;
+    }
+
+    if ( !vgmstream_open_stream(vgmstream,streamFile,start_offset) )
+        goto fail;
+    return vgmstream;
+
+fail:
+    close_vgmstream(vgmstream);
+    return NULL;
+}
diff --git a/src/vgmstream.c b/src/vgmstream.c
index 50015c89..9297713f 100644
--- a/src/vgmstream.c
+++ b/src/vgmstream.c
@@ -316,7 +316,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
 	init_vgmstream_ps2_hsf,
 	init_vgmstream_ps3_ivag,
 	init_vgmstream_ps2_2pfs,
-    init_vgmstream_xnbm,
+    init_vgmstream_xnb,
 	init_vgmstream_rsd6oogv,
 	init_vgmstream_ubi_ckd,
 	init_vgmstream_ps2_vbk,