diff --git a/Ryujinx.Graphics/Gal/GalFrameBufferFormat.cs b/Ryujinx.Graphics/Gal/GalFrameBufferFormat.cs
index 3180aeff9..08bd622b3 100644
--- a/Ryujinx.Graphics/Gal/GalFrameBufferFormat.cs
+++ b/Ryujinx.Graphics/Gal/GalFrameBufferFormat.cs
@@ -1,6 +1,6 @@
 namespace Ryujinx.Graphics.Gal
 {
-    public enum GalFrameBufferFormat
+    public enum GalSurfaceFormat
     {
         Bitmap               = 0x1c,
         Unknown1D            = 0x1d,
diff --git a/Ryujinx.Graphics/Gal/GalImageFormat.cs b/Ryujinx.Graphics/Gal/GalImageFormat.cs
index 4e84067bb..ba555684f 100644
--- a/Ryujinx.Graphics/Gal/GalImageFormat.cs
+++ b/Ryujinx.Graphics/Gal/GalImageFormat.cs
@@ -1,204 +1,93 @@
-namespace Ryujinx.Graphics.Gal
+using System;
+
+namespace Ryujinx.Graphics.Gal
 {
-    //These are Vulkan-based enumerations, do not take them as Tegra values
+    [Flags]
     public enum GalImageFormat
     {
-        Undefined = 0,
+        Snorm  = 1 << 27,
+        Unorm  = 1 << 28,
+        Sint   = 1 << 29,
+        Uint   = 1 << 30,
+        Sfloat = 1 << 31,
 
-        R4G4_UNORM_PACK8 = 1,
-        R4G4B4A4_UNORM_PACK16 = 2,
-        B4G4R4A4_UNORM_PACK16 = 3,
-        R5G6B5_UNORM_PACK16 = 4,
-        B5G6R5_UNORM_PACK16 = 5,
-        R5G5B5A1_UNORM_PACK16 = 6,
-        B5G5R5A1_UNORM_PACK16 = 7,
-        A1R5G5B5_UNORM_PACK16 = 8,
-        R8_UNORM = 9,
-        R8_SNORM = 10,
-        R8_USCALED = 11,
-        R8_SSCALED = 12,
-        R8_UINT = 13,
-        R8_SINT = 14,
-        R8_SRGB = 15,
-        R8G8_UNORM = 16,
-        R8G8_SNORM = 17,
-        R8G8_USCALED = 18,
-        R8G8_SSCALED = 19,
-        R8G8_UINT = 20,
-        R8G8_SINT = 21,
-        R8G8_SRGB = 22,
-        R8G8B8_UNORM = 23,
-        R8G8B8_SNORM = 24,
-        R8G8B8_USCALED = 25,
-        R8G8B8_SSCALED = 26,
-        R8G8B8_UINT = 27,
-        R8G8B8_SINT = 28,
-        R8G8B8_SRGB = 29,
-        B8G8R8_UNORM = 30,
-        B8G8R8_SNORM = 31,
-        B8G8R8_USCALED = 32,
-        B8G8R8_SSCALED = 33,
-        B8G8R8_UINT = 34,
-        B8G8R8_SINT = 35,
-        B8G8R8_SRGB = 36,
-        R8G8B8A8_UNORM = 37,
-        R8G8B8A8_SNORM = 38,
-        R8G8B8A8_USCALED = 39,
-        R8G8B8A8_SSCALED = 40,
-        R8G8B8A8_UINT = 41,
-        R8G8B8A8_SINT = 42,
-        R8G8B8A8_SRGB = 43,
-        B8G8R8A8_UNORM = 44,
-        B8G8R8A8_SNORM = 45,
-        B8G8R8A8_USCALED = 46,
-        B8G8R8A8_SSCALED = 47,
-        B8G8R8A8_UINT = 48,
-        B8G8R8A8_SINT = 49,
-        B8G8R8A8_SRGB = 50,
-        A8B8G8R8_UNORM_PACK32 = 51,
-        A8B8G8R8_SNORM_PACK32 = 52,
-        A8B8G8R8_USCALED_PACK32 = 53,
-        A8B8G8R8_SSCALED_PACK32 = 54,
-        A8B8G8R8_UINT_PACK32 = 55,
-        A8B8G8R8_SINT_PACK32 = 56,
-        A8B8G8R8_SRGB_PACK32 = 57,
-        A2R10G10B10_UNORM_PACK32 = 58,
-        A2R10G10B10_SNORM_PACK32 = 59,
-        A2R10G10B10_USCALED_PACK32 = 60,
-        A2R10G10B10_SSCALED_PACK32 = 61,
-        A2R10G10B10_UINT_PACK32 = 62,
-        A2R10G10B10_SINT_PACK32 = 63,
-        A2B10G10R10_UNORM_PACK32 = 64,
-        A2B10G10R10_SNORM_PACK32 = 65,
-        A2B10G10R10_USCALED_PACK32 = 66,
-        A2B10G10R10_SSCALED_PACK32 = 67,
-        A2B10G10R10_UINT_PACK32 = 68,
-        A2B10G10R10_SINT_PACK32 = 69,
-        R16_UNORM = 70,
-        R16_SNORM = 71,
-        R16_USCALED = 72,
-        R16_SSCALED = 73,
-        R16_UINT = 74,
-        R16_SINT = 75,
-        R16_SFLOAT = 76,
-        R16G16_UNORM = 77,
-        R16G16_SNORM = 78,
-        R16G16_USCALED = 79,
-        R16G16_SSCALED = 80,
-        R16G16_UINT = 81,
-        R16G16_SINT = 82,
-        R16G16_SFLOAT = 83,
-        R16G16B16_UNORM = 84,
-        R16G16B16_SNORM = 85,
-        R16G16B16_USCALED = 86,
-        R16G16B16_SSCALED = 87,
-        R16G16B16_UINT = 88,
-        R16G16B16_SINT = 89,
-        R16G16B16_SFLOAT = 90,
-        R16G16B16A16_UNORM = 91,
-        R16G16B16A16_SNORM = 92,
-        R16G16B16A16_USCALED = 93,
-        R16G16B16A16_SSCALED = 94,
-        R16G16B16A16_UINT = 95,
-        R16G16B16A16_SINT = 96,
-        R16G16B16A16_SFLOAT = 97,
-        R32_UINT = 98,
-        R32_SINT = 99,
-        R32_SFLOAT = 100,
-        R32G32_UINT = 101,
-        R32G32_SINT = 102,
-        R32G32_SFLOAT = 103,
-        R32G32B32_UINT = 104,
-        R32G32B32_SINT = 105,
-        R32G32B32_SFLOAT = 106,
-        R32G32B32A32_UINT = 107,
-        R32G32B32A32_SINT = 108,
-        R32G32B32A32_SFLOAT = 109,
-        R64_UINT = 110,
-        R64_SINT = 111,
-        R64_SFLOAT = 112,
-        R64G64_UINT = 113,
-        R64G64_SINT = 114,
-        R64G64_SFLOAT = 115,
-        R64G64B64_UINT = 116,
-        R64G64B64_SINT = 117,
-        R64G64B64_SFLOAT = 118,
-        R64G64B64A64_UINT = 119,
-        R64G64B64A64_SINT = 120,
-        R64G64B64A64_SFLOAT = 121,
-        B10G11R11_UFLOAT_PACK32 = 122,
-        E5B9G9R9_UFLOAT_PACK32 = 123,
-        D16_UNORM = 124,
-        X8_D24_UNORM_PACK32 = 125,
-        D32_SFLOAT = 126,
-        S8_UINT = 127,
-        D16_UNORM_S8_UINT = 128,
-        D24_UNORM_S8_UINT = 129,
-        D32_SFLOAT_S8_UINT = 130,
-        BC1_RGB_UNORM_BLOCK = 131,
-        BC1_RGB_SRGB_BLOCK = 132,
-        BC1_RGBA_UNORM_BLOCK = 133,
-        BC1_RGBA_SRGB_BLOCK = 134,
-        BC2_UNORM_BLOCK = 135,
-        BC2_SRGB_BLOCK = 136,
-        BC3_UNORM_BLOCK = 137,
-        BC3_SRGB_BLOCK = 138,
-        BC4_UNORM_BLOCK = 139,
-        BC4_SNORM_BLOCK = 140,
-        BC5_UNORM_BLOCK = 141,
-        BC5_SNORM_BLOCK = 142,
-        BC6H_UFLOAT_BLOCK = 143,
-        BC6H_SFLOAT_BLOCK = 144,
-        BC7_UNORM_BLOCK = 145,
-        BC7_SRGB_BLOCK = 146,
-        ETC2_R8G8B8_UNORM_BLOCK = 147,
-        ETC2_R8G8B8_SRGB_BLOCK = 148,
-        ETC2_R8G8B8A1_UNORM_BLOCK = 149,
-        ETC2_R8G8B8A1_SRGB_BLOCK = 150,
-        ETC2_R8G8B8A8_UNORM_BLOCK = 151,
-        ETC2_R8G8B8A8_SRGB_BLOCK = 152,
-        EAC_R11_UNORM_BLOCK = 153,
-        EAC_R11_SNORM_BLOCK = 154,
-        EAC_R11G11_UNORM_BLOCK = 155,
-        EAC_R11G11_SNORM_BLOCK = 156,
+        TypeMask = Snorm | Unorm | Sint | Uint | Sfloat,
 
-        ASTC_BEGIN = ASTC_4x4_UNORM_BLOCK,
+        FormatMask = ~TypeMask,
 
-        ASTC_4x4_UNORM_BLOCK = 157,
-        ASTC_4x4_SRGB_BLOCK = 158,
-        ASTC_5x4_UNORM_BLOCK = 159,
-        ASTC_5x4_SRGB_BLOCK = 160,
-        ASTC_5x5_UNORM_BLOCK = 161,
-        ASTC_5x5_SRGB_BLOCK = 162,
-        ASTC_6x5_UNORM_BLOCK = 163,
-        ASTC_6x5_SRGB_BLOCK = 164,
-        ASTC_6x6_UNORM_BLOCK = 165,
-        ASTC_6x6_SRGB_BLOCK = 166,
-        ASTC_8x5_UNORM_BLOCK = 167,
-        ASTC_8x5_SRGB_BLOCK = 168,
-        ASTC_8x6_UNORM_BLOCK = 169,
-        ASTC_8x6_SRGB_BLOCK = 170,
-        ASTC_8x8_UNORM_BLOCK = 171,
-        ASTC_8x8_SRGB_BLOCK = 172,
-        ASTC_10x5_UNORM_BLOCK = 173,
-        ASTC_10x5_SRGB_BLOCK = 174,
-        ASTC_10x6_UNORM_BLOCK = 175,
-        ASTC_10x6_SRGB_BLOCK = 176,
-        ASTC_10x8_UNORM_BLOCK = 177,
-        ASTC_10x8_SRGB_BLOCK = 178,
-        ASTC_10x10_UNORM_BLOCK = 179,
-        ASTC_10x10_SRGB_BLOCK = 180,
-        ASTC_12x10_UNORM_BLOCK = 181,
-        ASTC_12x10_SRGB_BLOCK = 182,
-        ASTC_12x12_UNORM_BLOCK = 183,
-        ASTC_12x12_SRGB_BLOCK = 184,
+        ASTC_BEGIN = ASTC_4x4,
 
-        ASTC_END = ASTC_12x12_SRGB_BLOCK,
+        ASTC_4x4 = 1,
+        ASTC_5x4,
+        ASTC_5x5,
+        ASTC_6x5,
+        ASTC_6x6,
+        ASTC_8x5,
+        ASTC_8x6,
+        ASTC_8x8,
+        ASTC_10x5,
+        ASTC_10x6,
+        ASTC_10x8,
+        ASTC_10x10,
+        ASTC_12x10,
+        ASTC_12x12,
 
-        REVERSED_BEGIN,
+        ASTC_END = ASTC_12x12,
 
-        R4G4B4A4_UNORM_PACK16_REVERSED = REVERSED_BEGIN,
-
-        REVERSED_END
+        R4G4,
+        R4G4B4A4,
+        B4G4R4A4,
+        A4B4G4R4,
+        R5G6B5,
+        B5G6R5,
+        R5G5B5A1,
+        B5G5R5A1,
+        A1R5G5B5,
+        R8,
+        R8G8,
+        G8R8,
+        R8G8B8,
+        B8G8R8,
+        R8G8B8A8,
+        B8G8R8A8,
+        A8B8G8R8,
+        A8B8G8R8_SRGB,
+        A2R10G10B10,
+        A2B10G10R10,
+        R16,
+        R16G16,
+        R16G16B16,
+        R16G16B16A16,
+        R32,
+        R32G32,
+        R32G32B32,
+        R32G32B32A32,
+        R64,
+        R64G64,
+        R64G64B64,
+        R64G64B64A64,
+        B10G11R11,
+        E5B9G9R9,
+        D16,
+        X8_D24,
+        D32,
+        S8,
+        D16_S8,
+        D24_S8,
+        D32_S8,
+        BC1_RGB,
+        BC1_RGBA,
+        BC2,
+        BC3,
+        BC4,
+        BC5,
+        BC6H_SF16,
+        BC6H_UF16,
+        BC7,
+        ETC2_R8G8B8,
+        ETC2_R8G8B8A1,
+        ETC2_R8G8B8A8,
+        EAC_R11,
+        EAC_R11G11,
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/IGalFrameBuffer.cs b/Ryujinx.Graphics/Gal/IGalRenderTarget.cs
similarity index 96%
rename from Ryujinx.Graphics/Gal/IGalFrameBuffer.cs
rename to Ryujinx.Graphics/Gal/IGalRenderTarget.cs
index 108d3d9b1..c44434ef6 100644
--- a/Ryujinx.Graphics/Gal/IGalFrameBuffer.cs
+++ b/Ryujinx.Graphics/Gal/IGalRenderTarget.cs
@@ -2,7 +2,7 @@ using System;
 
 namespace Ryujinx.Graphics.Gal
 {
-    public interface IGalFrameBuffer
+    public interface IGalRenderTarget
     {
         void BindColor(long Key, int Attachment);
 
diff --git a/Ryujinx.Graphics/Gal/IGalRenderer.cs b/Ryujinx.Graphics/Gal/IGalRenderer.cs
index b47ac71d0..41e95a878 100644
--- a/Ryujinx.Graphics/Gal/IGalRenderer.cs
+++ b/Ryujinx.Graphics/Gal/IGalRenderer.cs
@@ -10,7 +10,7 @@ namespace Ryujinx.Graphics.Gal
 
         IGalConstBuffer Buffer { get; }
 
-        IGalFrameBuffer FrameBuffer { get; }
+        IGalRenderTarget RenderTarget { get; }
 
         IGalRasterizer Rasterizer { get; }
 
diff --git a/Ryujinx.Graphics/Gal/ImageFormatConverter.cs b/Ryujinx.Graphics/Gal/ImageFormatConverter.cs
deleted file mode 100644
index 7aa39f5fb..000000000
--- a/Ryujinx.Graphics/Gal/ImageFormatConverter.cs
+++ /dev/null
@@ -1,279 +0,0 @@
-using System;
-
-namespace Ryujinx.Graphics.Gal
-{
-    public static class ImageFormatConverter
-    {
-        public static GalImageFormat ConvertTexture(
-            GalTextureFormat Format,
-            GalTextureType RType,
-            GalTextureType GType,
-            GalTextureType BType,
-            GalTextureType AType)
-        {
-            if (RType != GType || RType != BType || RType != AType)
-            {
-                throw new NotImplementedException("Per component types are not implemented");
-            }
-
-            GalTextureType Type = RType;
-
-            switch (Type)
-            {
-                case GalTextureType.Snorm:
-                    switch (Format)
-                    {
-                        case GalTextureFormat.R16G16B16A16: return GalImageFormat.R16G16B16A16_SNORM;
-                        case GalTextureFormat.A8B8G8R8:     return GalImageFormat.A8B8G8R8_SNORM_PACK32;
-                        case GalTextureFormat.A2B10G10R10:  return GalImageFormat.A2B10G10R10_SNORM_PACK32;
-                        case GalTextureFormat.G8R8:         return GalImageFormat.R8G8_SNORM;
-                        case GalTextureFormat.R16:          return GalImageFormat.R16_SNORM;
-                        case GalTextureFormat.R8:           return GalImageFormat.R8_SNORM;
-                        case GalTextureFormat.BC4:          return GalImageFormat.BC4_SNORM_BLOCK;
-                        case GalTextureFormat.BC5:          return GalImageFormat.BC5_SNORM_BLOCK;
-                    }
-                    break;
-
-                case GalTextureType.Unorm:
-                    switch (Format)
-                    {
-                        case GalTextureFormat.R16G16B16A16: return GalImageFormat.R16G16B16A16_UNORM;
-                        case GalTextureFormat.A8B8G8R8:     return GalImageFormat.A8B8G8R8_UNORM_PACK32;
-                        case GalTextureFormat.A2B10G10R10:  return GalImageFormat.A2B10G10R10_UNORM_PACK32;
-                        case GalTextureFormat.A4B4G4R4:     return GalImageFormat.R4G4B4A4_UNORM_PACK16_REVERSED;
-                        case GalTextureFormat.A1B5G5R5:     return GalImageFormat.A1R5G5B5_UNORM_PACK16;
-                        case GalTextureFormat.B5G6R5:       return GalImageFormat.B5G6R5_UNORM_PACK16;
-                        case GalTextureFormat.BC7U:         return GalImageFormat.BC7_UNORM_BLOCK;
-                        case GalTextureFormat.G8R8:         return GalImageFormat.R8G8_UNORM;
-                        case GalTextureFormat.R16:          return GalImageFormat.R16_UNORM;
-                        case GalTextureFormat.R8:           return GalImageFormat.R8_UNORM;
-                        case GalTextureFormat.BC1:          return GalImageFormat.BC1_RGBA_UNORM_BLOCK;
-                        case GalTextureFormat.BC2:          return GalImageFormat.BC2_UNORM_BLOCK;
-                        case GalTextureFormat.BC3:          return GalImageFormat.BC3_UNORM_BLOCK;
-                        case GalTextureFormat.BC4:          return GalImageFormat.BC4_UNORM_BLOCK;
-                        case GalTextureFormat.BC5:          return GalImageFormat.BC5_UNORM_BLOCK;
-                        case GalTextureFormat.Z24S8:        return GalImageFormat.D24_UNORM_S8_UINT;
-                        case GalTextureFormat.ZF32_X24S8:   return GalImageFormat.D32_SFLOAT_S8_UINT;
-                        case GalTextureFormat.Astc2D4x4:    return GalImageFormat.ASTC_4x4_UNORM_BLOCK;
-                        case GalTextureFormat.Astc2D5x5:    return GalImageFormat.ASTC_5x5_UNORM_BLOCK;
-                        case GalTextureFormat.Astc2D6x6:    return GalImageFormat.ASTC_6x6_UNORM_BLOCK;
-                        case GalTextureFormat.Astc2D8x8:    return GalImageFormat.ASTC_8x8_UNORM_BLOCK;
-                        case GalTextureFormat.Astc2D10x10:  return GalImageFormat.ASTC_10x10_UNORM_BLOCK;
-                        case GalTextureFormat.Astc2D12x12:  return GalImageFormat.ASTC_12x12_UNORM_BLOCK;
-                        case GalTextureFormat.Astc2D5x4:    return GalImageFormat.ASTC_5x4_UNORM_BLOCK;
-                        case GalTextureFormat.Astc2D6x5:    return GalImageFormat.ASTC_6x5_UNORM_BLOCK;
-                        case GalTextureFormat.Astc2D8x6:    return GalImageFormat.ASTC_8x6_UNORM_BLOCK;
-                        case GalTextureFormat.Astc2D10x8:   return GalImageFormat.ASTC_10x8_UNORM_BLOCK;
-                        case GalTextureFormat.Astc2D12x10:  return GalImageFormat.ASTC_12x10_UNORM_BLOCK;
-                        case GalTextureFormat.Astc2D8x5:    return GalImageFormat.ASTC_8x5_UNORM_BLOCK;
-                        case GalTextureFormat.Astc2D10x5:   return GalImageFormat.ASTC_10x5_UNORM_BLOCK;
-                        case GalTextureFormat.Astc2D10x6:   return GalImageFormat.ASTC_10x6_UNORM_BLOCK;
-                    }
-                    break;
-
-                case GalTextureType.Sint:
-                    switch (Format)
-                    {
-                        case GalTextureFormat.R32G32B32A32: return GalImageFormat.R32G32B32A32_SINT;
-                        case GalTextureFormat.R16G16B16A16: return GalImageFormat.R16G16B16A16_SINT;
-                        case GalTextureFormat.R32G32:       return GalImageFormat.R32G32_SINT;
-                        case GalTextureFormat.A8B8G8R8:     return GalImageFormat.A8B8G8R8_SINT_PACK32;
-                        case GalTextureFormat.A2B10G10R10:  return GalImageFormat.A2B10G10R10_SINT_PACK32;
-                        case GalTextureFormat.R32:          return GalImageFormat.R32_SINT;
-                        case GalTextureFormat.G8R8:         return GalImageFormat.R8G8_SINT;
-                        case GalTextureFormat.R16:          return GalImageFormat.R16_SINT;
-                        case GalTextureFormat.R8:           return GalImageFormat.R8_SINT;
-                    }
-                    break;
-
-                case GalTextureType.Uint:
-                    switch (Format)
-                    {
-                        case GalTextureFormat.R32G32B32A32: return GalImageFormat.R32G32B32A32_UINT;
-                        case GalTextureFormat.R16G16B16A16: return GalImageFormat.R16G16B16A16_UINT;
-                        case GalTextureFormat.R32G32:       return GalImageFormat.R32G32_UINT;
-                        case GalTextureFormat.A8B8G8R8:     return GalImageFormat.A8B8G8R8_UINT_PACK32;
-                        case GalTextureFormat.A2B10G10R10:  return GalImageFormat.A2B10G10R10_UINT_PACK32;
-                        case GalTextureFormat.R32:          return GalImageFormat.R32_UINT;
-                        case GalTextureFormat.G8R8:         return GalImageFormat.R8G8_UINT;
-                        case GalTextureFormat.R16:          return GalImageFormat.R16_UINT;
-                        case GalTextureFormat.R8:           return GalImageFormat.R8_UINT;
-                    }
-                    break;
-
-                case GalTextureType.Snorm_Force_Fp16:
-                    //TODO
-                    break;
-
-                case GalTextureType.Unorm_Force_Fp16:
-                    //TODO
-                    break;
-
-                case GalTextureType.Float:
-                    switch (Format)
-                    {
-                        case GalTextureFormat.R32G32B32A32: return GalImageFormat.R32G32B32A32_SFLOAT;
-                        case GalTextureFormat.R16G16B16A16: return GalImageFormat.R16G16B16A16_SFLOAT;
-                        case GalTextureFormat.R32G32:       return GalImageFormat.R32G32_SFLOAT;
-                        case GalTextureFormat.R32:          return GalImageFormat.R32_SFLOAT;
-                        case GalTextureFormat.BC6H_SF16:    return GalImageFormat.BC6H_SFLOAT_BLOCK;
-                        case GalTextureFormat.BC6H_UF16:    return GalImageFormat.BC6H_UFLOAT_BLOCK;
-                        case GalTextureFormat.R16:          return GalImageFormat.R16_SFLOAT;
-                        case GalTextureFormat.BF10GF11RF11: return GalImageFormat.B10G11R11_UFLOAT_PACK32;
-                        case GalTextureFormat.ZF32:         return GalImageFormat.D32_SFLOAT;
-                    }
-                    break;
-            }
-
-            throw new NotImplementedException("0x" + ((int)Format).ToString("x2") + " " + Type.ToString());
-        }
-
-        public static GalImageFormat ConvertFrameBuffer(GalFrameBufferFormat Format)
-        {
-            switch (Format)
-            {
-                case GalFrameBufferFormat.R32Float:       return GalImageFormat.R32_SFLOAT;
-                case GalFrameBufferFormat.RGB10A2Unorm:   return GalImageFormat.A2B10G10R10_UNORM_PACK32;
-                case GalFrameBufferFormat.RGBA8Srgb:      return GalImageFormat.A8B8G8R8_SRGB_PACK32;
-                case GalFrameBufferFormat.RGBA16Float:    return GalImageFormat.R16G16B16A16_SFLOAT;
-                case GalFrameBufferFormat.R16Float:       return GalImageFormat.R16_SFLOAT;
-                case GalFrameBufferFormat.R8Unorm:        return GalImageFormat.R8_UNORM;
-                case GalFrameBufferFormat.RGBA8Unorm:     return GalImageFormat.A8B8G8R8_UNORM_PACK32;
-                case GalFrameBufferFormat.R11G11B10Float: return GalImageFormat.B10G11R11_UFLOAT_PACK32;
-                case GalFrameBufferFormat.RGBA32Float:    return GalImageFormat.R32G32B32A32_SFLOAT;
-                case GalFrameBufferFormat.RG16Snorm:      return GalImageFormat.R16G16_SNORM;
-                case GalFrameBufferFormat.RG16Float:      return GalImageFormat.R16G16_SFLOAT;
-                case GalFrameBufferFormat.RG8Snorm:       return GalImageFormat.R8_SNORM;
-                case GalFrameBufferFormat.RGBA8Snorm:     return GalImageFormat.A8B8G8R8_SNORM_PACK32;
-                case GalFrameBufferFormat.RG8Unorm:       return GalImageFormat.R8G8_UNORM;
-                case GalFrameBufferFormat.BGRA8Unorm:     return GalImageFormat.A8B8G8R8_UNORM_PACK32;
-                case GalFrameBufferFormat.BGRA8Srgb:      return GalImageFormat.A8B8G8R8_SRGB_PACK32;
-                case GalFrameBufferFormat.RG32Float:      return GalImageFormat.R32G32_SFLOAT;
-                case GalFrameBufferFormat.RG32Sint:       return GalImageFormat.R32G32_SINT;
-                case GalFrameBufferFormat.RG32Uint:       return GalImageFormat.R32G32_UINT;
-            }
-
-            throw new NotImplementedException(Format.ToString());
-        }
-
-        public static GalImageFormat ConvertZeta(GalZetaFormat Format)
-        {
-            switch (Format)
-            {
-                case GalZetaFormat.Z32Float:      return GalImageFormat.D32_SFLOAT;
-                case GalZetaFormat.S8Z24Unorm:    return GalImageFormat.D24_UNORM_S8_UINT;
-                case GalZetaFormat.Z16Unorm:      return GalImageFormat.D16_UNORM;
-                case GalZetaFormat.Z32S8X24Float: return GalImageFormat.D32_SFLOAT_S8_UINT;
-            }
-
-            throw new NotImplementedException(Format.ToString());
-        }
-
-        public static bool HasColor(GalImageFormat Format)
-        {
-            switch (Format)
-            {
-                case GalImageFormat.R32G32B32A32_SFLOAT:
-                case GalImageFormat.R32G32B32A32_SINT:
-                case GalImageFormat.R32G32B32A32_UINT:
-                case GalImageFormat.R16G16B16A16_SFLOAT:
-                case GalImageFormat.R16G16B16A16_SINT:
-                case GalImageFormat.R16G16B16A16_UINT:
-                case GalImageFormat.R32G32_SFLOAT:
-                case GalImageFormat.R32G32_SINT:
-                case GalImageFormat.R32G32_UINT:
-                case GalImageFormat.A8B8G8R8_SNORM_PACK32:
-                case GalImageFormat.A8B8G8R8_UNORM_PACK32:
-                case GalImageFormat.A8B8G8R8_SINT_PACK32:
-                case GalImageFormat.A8B8G8R8_UINT_PACK32:
-                case GalImageFormat.A2B10G10R10_SINT_PACK32:
-                case GalImageFormat.A2B10G10R10_SNORM_PACK32:
-                case GalImageFormat.A2B10G10R10_UINT_PACK32:
-                case GalImageFormat.A2B10G10R10_UNORM_PACK32:
-                case GalImageFormat.R32_SFLOAT:
-                case GalImageFormat.R32_SINT:
-                case GalImageFormat.R32_UINT:
-                case GalImageFormat.BC6H_SFLOAT_BLOCK:
-                case GalImageFormat.BC6H_UFLOAT_BLOCK:
-                case GalImageFormat.A1R5G5B5_UNORM_PACK16:
-                case GalImageFormat.B5G6R5_UNORM_PACK16:
-                case GalImageFormat.BC7_UNORM_BLOCK:
-                case GalImageFormat.R16G16_SFLOAT:
-                case GalImageFormat.R16G16_SINT:
-                case GalImageFormat.R16G16_SNORM:
-                case GalImageFormat.R16G16_UNORM:
-                case GalImageFormat.R8G8_SINT:
-                case GalImageFormat.R8G8_SNORM:
-                case GalImageFormat.R8G8_UINT:
-                case GalImageFormat.R8G8_UNORM:
-                case GalImageFormat.R16_SFLOAT:
-                case GalImageFormat.R16_SINT:
-                case GalImageFormat.R16_SNORM:
-                case GalImageFormat.R16_UINT:
-                case GalImageFormat.R16_UNORM:
-                case GalImageFormat.R8_SINT:
-                case GalImageFormat.R8_SNORM:
-                case GalImageFormat.R8_UINT:
-                case GalImageFormat.R8_UNORM:
-                case GalImageFormat.B10G11R11_UFLOAT_PACK32:
-                case GalImageFormat.BC1_RGBA_UNORM_BLOCK:
-                case GalImageFormat.BC2_UNORM_BLOCK:
-                case GalImageFormat.BC3_UNORM_BLOCK:
-                case GalImageFormat.BC4_UNORM_BLOCK:
-                case GalImageFormat.BC5_UNORM_BLOCK:
-                case GalImageFormat.ASTC_4x4_UNORM_BLOCK:
-                case GalImageFormat.ASTC_5x5_UNORM_BLOCK:
-                case GalImageFormat.ASTC_6x6_UNORM_BLOCK:
-                case GalImageFormat.ASTC_8x8_UNORM_BLOCK:
-                case GalImageFormat.ASTC_10x10_UNORM_BLOCK:
-                case GalImageFormat.ASTC_12x12_UNORM_BLOCK:
-                case GalImageFormat.ASTC_5x4_UNORM_BLOCK:
-                case GalImageFormat.ASTC_6x5_UNORM_BLOCK:
-                case GalImageFormat.ASTC_8x6_UNORM_BLOCK:
-                case GalImageFormat.ASTC_10x8_UNORM_BLOCK:
-                case GalImageFormat.ASTC_12x10_UNORM_BLOCK:
-                case GalImageFormat.ASTC_8x5_UNORM_BLOCK:
-                case GalImageFormat.ASTC_10x5_UNORM_BLOCK:
-                case GalImageFormat.ASTC_10x6_UNORM_BLOCK:
-                case GalImageFormat.R4G4B4A4_UNORM_PACK16_REVERSED:
-                    return true;
-
-                case GalImageFormat.D24_UNORM_S8_UINT:
-                case GalImageFormat.D32_SFLOAT:
-                case GalImageFormat.D16_UNORM:
-                case GalImageFormat.D32_SFLOAT_S8_UINT:
-                    return false;
-            }
-
-            throw new NotImplementedException(Format.ToString());
-        }
-
-        public static bool HasDepth(GalImageFormat Format)
-        {
-            switch (Format)
-            {
-                case GalImageFormat.D24_UNORM_S8_UINT:
-                case GalImageFormat.D32_SFLOAT:
-                case GalImageFormat.D16_UNORM:
-                case GalImageFormat.D32_SFLOAT_S8_UINT:
-                    return true;
-            }
-
-            //Depth formats are fewer than colors, so it's harder to miss one
-            //Instead of checking for individual formats, return false
-            return false;
-        }
-
-        public static bool HasStencil(GalImageFormat Format)
-        {
-            switch (Format)
-            {
-                case GalImageFormat.D24_UNORM_S8_UINT:
-                case GalImageFormat.D32_SFLOAT_S8_UINT:
-                    return true;
-            }
-
-            return false;
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/OpenGL/ImageHandler.cs b/Ryujinx.Graphics/Gal/OpenGL/ImageHandler.cs
index 74f18dcd3..dda825385 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/ImageHandler.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/ImageHandler.cs
@@ -1,13 +1,11 @@
 using OpenTK.Graphics.OpenGL;
+using Ryujinx.Graphics.Texture;
 using System;
 
 namespace Ryujinx.Graphics.Gal.OpenGL
 {
     class ImageHandler
     {
-        //TODO: Use a variable value here
-        public const int MaxBpp = 16;
-
         private static int CopyBuffer = 0;
         private static int CopyBufferSize = 0;
 
@@ -38,87 +36,124 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             this.Image = Image;
         }
 
-        public void EnsureSetup(GalImage Image)
+        public void EnsureSetup(GalImage NewImage)
         {
-            if (Width  != Image.Width  ||
-                Height != Image.Height ||
-                Format != Image.Format ||
-                !Initialized)
+            if (Width  == NewImage.Width  &&
+                Height == NewImage.Height &&
+                Format == NewImage.Format &&
+                Initialized)
             {
-                (PixelInternalFormat InternalFormat, PixelFormat PixelFormat, PixelType PixelType) =
-                    OGLEnumConverter.GetImageFormat(Image.Format);
+                return;
+            }
 
-                GL.BindTexture(TextureTarget.Texture2D, Handle);
+            PixelInternalFormat InternalFmt;
+            PixelFormat         PixelFormat;
+            PixelType           PixelType;
 
-                if (Initialized)
+            if (ImageUtils.IsCompressed(NewImage.Format))
+            {
+                InternalFmt = (PixelInternalFormat)OGLEnumConverter.GetCompressedImageFormat(NewImage.Format);
+
+                PixelFormat = default(PixelFormat);
+                PixelType   = default(PixelType);
+            }
+            else
+            {
+                (InternalFmt, PixelFormat, PixelType) = OGLEnumConverter.GetImageFormat(NewImage.Format);
+            }
+
+            GL.BindTexture(TextureTarget.Texture2D, Handle);
+
+            if (Initialized)
+            {
+                if (CopyBuffer == 0)
                 {
-                    if (CopyBuffer == 0)
-                    {
-                        CopyBuffer = GL.GenBuffer();
-                    }
-
-                    int MaxWidth  = Math.Max(Image.Width, Width);
-                    int MaxHeight = Math.Max(Image.Height, Height);
-
-                    int CurrentSize = MaxWidth * MaxHeight * MaxBpp;
-
-                    GL.BindBuffer(BufferTarget.PixelPackBuffer, CopyBuffer);
-                    GL.BindBuffer(BufferTarget.PixelUnpackBuffer, CopyBuffer);
-
-                    if (CopyBufferSize < CurrentSize)
-                    {
-                        CopyBufferSize = CurrentSize;
-
-                        GL.BufferData(BufferTarget.PixelPackBuffer, CurrentSize, IntPtr.Zero, BufferUsageHint.StreamCopy);
-                    }
-
-                    GL.GetTexImage(TextureTarget.Texture2D, 0, this.PixelFormat, this.PixelType, IntPtr.Zero);
-
-                    GL.DeleteTexture(Handle);
-
-                    Handle = GL.GenTexture();
-
-                    GL.BindTexture(TextureTarget.Texture2D, Handle);
+                    CopyBuffer = GL.GenBuffer();
                 }
 
-                const int MinFilter = (int)TextureMinFilter.Linear;
-                const int MagFilter = (int)TextureMagFilter.Linear;
+                int CurrentSize = Math.Max(ImageUtils.GetSize(NewImage),
+                                           ImageUtils.GetSize(Image));
 
-                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, MinFilter);
-                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, MagFilter);
+                GL.BindBuffer(BufferTarget.PixelPackBuffer, CopyBuffer);
+                GL.BindBuffer(BufferTarget.PixelUnpackBuffer, CopyBuffer);
 
-                const int Level = 0;
-                const int Border = 0;
+                if (CopyBufferSize < CurrentSize)
+                {
+                    CopyBufferSize = CurrentSize;
 
+                    GL.BufferData(BufferTarget.PixelPackBuffer, CurrentSize, IntPtr.Zero, BufferUsageHint.StreamCopy);
+                }
+
+                if (ImageUtils.IsCompressed(Image.Format))
+                {
+                    GL.GetCompressedTexImage(TextureTarget.Texture2D, 0, IntPtr.Zero);
+                }
+                else
+                {
+                    GL.GetTexImage(TextureTarget.Texture2D, 0, this.PixelFormat, this.PixelType, IntPtr.Zero);
+                }
+
+                GL.DeleteTexture(Handle);
+
+                Handle = GL.GenTexture();
+
+                GL.BindTexture(TextureTarget.Texture2D, Handle);
+            }
+
+            const int MinFilter = (int)TextureMinFilter.Linear;
+            const int MagFilter = (int)TextureMagFilter.Linear;
+
+            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, MinFilter);
+            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, MagFilter);
+
+            const int Level = 0;
+            const int Border = 0;
+
+            if (ImageUtils.IsCompressed(NewImage.Format))
+            {
+                Console.WriteLine("Hit");
+
+                GL.CompressedTexImage2D(
+                    TextureTarget.Texture2D,
+                    Level,
+                    (InternalFormat)InternalFmt,
+                    NewImage.Width,
+                    NewImage.Height,
+                    Border,
+                    ImageUtils.GetSize(NewImage),
+                    IntPtr.Zero);
+            }
+            else
+            {
                 GL.TexImage2D(
                     TextureTarget.Texture2D,
                     Level,
-                    InternalFormat,
-                    Image.Width,
-                    Image.Height,
+                    InternalFmt,
+                    NewImage.Width,
+                    NewImage.Height,
                     Border,
                     PixelFormat,
                     PixelType,
                     IntPtr.Zero);
-
-                if (Initialized)
-                {
-                    GL.BindBuffer(BufferTarget.PixelPackBuffer, 0);
-                    GL.BindBuffer(BufferTarget.PixelUnpackBuffer, 0);
-                }
-
-                this.Image = Image;
-
-                this.InternalFormat = InternalFormat;
-                this.PixelFormat = PixelFormat;
-                this.PixelType = PixelType;
-
-                Initialized = true;
             }
+
+            if (Initialized)
+            {
+                GL.BindBuffer(BufferTarget.PixelPackBuffer,   0);
+                GL.BindBuffer(BufferTarget.PixelUnpackBuffer, 0);
+            }
+
+            Image = NewImage;
+
+            this.InternalFormat = InternalFmt;
+            this.PixelFormat = PixelFormat;
+            this.PixelType = PixelType;
+
+            Initialized = true;
         }
 
-        public bool HasColor   { get => ImageFormatConverter.HasColor(Format); }
-        public bool HasDepth   { get => ImageFormatConverter.HasDepth(Format); }
-        public bool HasStencil { get => ImageFormatConverter.HasStencil(Format); }
+        public bool HasColor   => ImageUtils.HasColor(Image.Format);
+        public bool HasDepth   => ImageUtils.HasDepth(Image.Format);
+        public bool HasStencil => ImageUtils.HasStencil(Image.Format);
     }
 }
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs
index 959d0e329..1d4f4cf77 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs
@@ -129,52 +129,51 @@ namespace Ryujinx.Graphics.Gal.OpenGL
         {
             switch (Format)
             {
-                case GalImageFormat.R32G32B32A32_SFLOAT:      return (PixelInternalFormat.Rgba32f,      PixelFormat.Rgba,        PixelType.Float);
-                case GalImageFormat.R32G32B32A32_SINT:        return (PixelInternalFormat.Rgba32i,      PixelFormat.RgbaInteger, PixelType.Int);
-                case GalImageFormat.R32G32B32A32_UINT:        return (PixelInternalFormat.Rgba32ui,     PixelFormat.RgbaInteger, PixelType.UnsignedInt);
-                case GalImageFormat.R16G16B16A16_SFLOAT:      return (PixelInternalFormat.Rgba16f,      PixelFormat.Rgba,        PixelType.HalfFloat);
-                case GalImageFormat.R16G16B16A16_SINT:        return (PixelInternalFormat.Rgba16i,      PixelFormat.RgbaInteger, PixelType.Short);
-                case GalImageFormat.R16G16B16A16_UINT:        return (PixelInternalFormat.Rgba16ui,     PixelFormat.RgbaInteger, PixelType.UnsignedShort);
-                case GalImageFormat.R32G32_SFLOAT:            return (PixelInternalFormat.Rg32f,        PixelFormat.Rg,          PixelType.Float);
-                case GalImageFormat.R32G32_SINT:              return (PixelInternalFormat.Rg32i,        PixelFormat.RgInteger,   PixelType.Int);
-                case GalImageFormat.R32G32_UINT:              return (PixelInternalFormat.Rg32ui,       PixelFormat.RgInteger,   PixelType.UnsignedInt);
-                case GalImageFormat.A8B8G8R8_SNORM_PACK32:    return (PixelInternalFormat.Rgba8Snorm,   PixelFormat.Rgba,        PixelType.Byte);
-                case GalImageFormat.A8B8G8R8_UNORM_PACK32:    return (PixelInternalFormat.Rgba8,        PixelFormat.Rgba,        PixelType.UnsignedByte);
-                case GalImageFormat.A8B8G8R8_SINT_PACK32:     return (PixelInternalFormat.Rgba8i,       PixelFormat.RgbaInteger, PixelType.Byte);
-                case GalImageFormat.A8B8G8R8_UINT_PACK32:     return (PixelInternalFormat.Rgba8ui,      PixelFormat.RgbaInteger, PixelType.UnsignedByte);
-                case GalImageFormat.A8B8G8R8_SRGB_PACK32:     return (PixelInternalFormat.Srgb8Alpha8,  PixelFormat.Rgba,        PixelType.UnsignedByte);
-                case GalImageFormat.A2B10G10R10_UINT_PACK32:  return (PixelInternalFormat.Rgb10A2ui,    PixelFormat.RgbaInteger, PixelType.UnsignedInt2101010Reversed);
-                case GalImageFormat.A2B10G10R10_UNORM_PACK32: return (PixelInternalFormat.Rgb10A2,      PixelFormat.Rgba,        PixelType.UnsignedInt2101010Reversed);
-                case GalImageFormat.R32_SFLOAT:               return (PixelInternalFormat.R32f,         PixelFormat.Red,         PixelType.Float);
-                case GalImageFormat.R32_SINT:                 return (PixelInternalFormat.R32i,         PixelFormat.Red,         PixelType.Int);
-                case GalImageFormat.R32_UINT:                 return (PixelInternalFormat.R32ui,        PixelFormat.Red,         PixelType.UnsignedInt);
-                case GalImageFormat.A1R5G5B5_UNORM_PACK16:    return (PixelInternalFormat.Rgb5A1,       PixelFormat.Rgba,        PixelType.UnsignedShort5551);
-                case GalImageFormat.B5G6R5_UNORM_PACK16:      return (PixelInternalFormat.Rgba,         PixelFormat.Rgb,         PixelType.UnsignedShort565);
-                case GalImageFormat.R16G16_SFLOAT:            return (PixelInternalFormat.Rg16f,        PixelFormat.Rg,          PixelType.HalfFloat);
-                case GalImageFormat.R16G16_SINT:              return (PixelInternalFormat.Rg16i,        PixelFormat.RgInteger,   PixelType.Short);
-                case GalImageFormat.R16G16_SNORM:             return (PixelInternalFormat.Rg16Snorm,    PixelFormat.Rg,          PixelType.Byte);
-                case GalImageFormat.R16G16_UNORM:             return (PixelInternalFormat.Rg16,         PixelFormat.Rg,          PixelType.UnsignedShort);
-                case GalImageFormat.R8G8_SINT:                return (PixelInternalFormat.Rg8i,         PixelFormat.RgInteger,   PixelType.Byte);
-                case GalImageFormat.R8G8_SNORM:               return (PixelInternalFormat.Rg8Snorm,     PixelFormat.Rg,          PixelType.Byte);
-                case GalImageFormat.R8G8_UINT:                return (PixelInternalFormat.Rg8ui,        PixelFormat.RgInteger,   PixelType.UnsignedByte);
-                case GalImageFormat.R8G8_UNORM:               return (PixelInternalFormat.Rg8,          PixelFormat.Rg,          PixelType.UnsignedByte);
-                case GalImageFormat.R16_SFLOAT:               return (PixelInternalFormat.R16f,         PixelFormat.Red,         PixelType.HalfFloat);
-                case GalImageFormat.R16_SINT:                 return (PixelInternalFormat.R16i,         PixelFormat.RedInteger,  PixelType.Short);
-                case GalImageFormat.R16_SNORM:                return (PixelInternalFormat.R16Snorm,     PixelFormat.Red,         PixelType.Byte);
-                case GalImageFormat.R16_UINT:                 return (PixelInternalFormat.R16ui,        PixelFormat.RedInteger,  PixelType.UnsignedShort);
-                case GalImageFormat.R16_UNORM:                return (PixelInternalFormat.R16,          PixelFormat.Red,         PixelType.UnsignedShort);
-                case GalImageFormat.R8_SINT:                  return (PixelInternalFormat.R8i,          PixelFormat.RedInteger,  PixelType.Byte);
-                case GalImageFormat.R8_SNORM:                 return (PixelInternalFormat.R8Snorm,      PixelFormat.Red,         PixelType.Byte);
-                case GalImageFormat.R8_UINT:                  return (PixelInternalFormat.R8ui,         PixelFormat.RedInteger,  PixelType.UnsignedByte);
-                case GalImageFormat.R8_UNORM:                 return (PixelInternalFormat.R8,           PixelFormat.Red,         PixelType.UnsignedByte);
-                case GalImageFormat.B10G11R11_UFLOAT_PACK32:  return (PixelInternalFormat.R11fG11fB10f, PixelFormat.Rgb,         PixelType.UnsignedInt10F11F11FRev);
+                case GalImageFormat.R32G32B32A32 | GalImageFormat.Sfloat: return (PixelInternalFormat.Rgba32f,      PixelFormat.Rgba,        PixelType.Float);
+                case GalImageFormat.R32G32B32A32 | GalImageFormat.Sint:   return (PixelInternalFormat.Rgba32i,      PixelFormat.RgbaInteger, PixelType.Int);
+                case GalImageFormat.R32G32B32A32 | GalImageFormat.Uint:   return (PixelInternalFormat.Rgba32ui,     PixelFormat.RgbaInteger, PixelType.UnsignedInt);
+                case GalImageFormat.R16G16B16A16 | GalImageFormat.Sfloat: return (PixelInternalFormat.Rgba16f,      PixelFormat.Rgba,        PixelType.HalfFloat);
+                case GalImageFormat.R16G16B16A16 | GalImageFormat.Sint:   return (PixelInternalFormat.Rgba16i,      PixelFormat.RgbaInteger, PixelType.Short);
+                case GalImageFormat.R16G16B16A16 | GalImageFormat.Uint:   return (PixelInternalFormat.Rgba16ui,     PixelFormat.RgbaInteger, PixelType.UnsignedShort);
+                case GalImageFormat.R32G32       | GalImageFormat.Sfloat: return (PixelInternalFormat.Rg32f,        PixelFormat.Rg,          PixelType.Float);
+                case GalImageFormat.R32G32       | GalImageFormat.Sint:   return (PixelInternalFormat.Rg32i,        PixelFormat.RgInteger,   PixelType.Int);
+                case GalImageFormat.R32G32       | GalImageFormat.Uint:   return (PixelInternalFormat.Rg32ui,       PixelFormat.RgInteger,   PixelType.UnsignedInt);
+                case GalImageFormat.A8B8G8R8     | GalImageFormat.Snorm:  return (PixelInternalFormat.Rgba8Snorm,   PixelFormat.Rgba,        PixelType.Byte);
+                case GalImageFormat.A8B8G8R8     | GalImageFormat.Unorm:  return (PixelInternalFormat.Rgba8,        PixelFormat.Rgba,        PixelType.UnsignedByte);
+                case GalImageFormat.A8B8G8R8     | GalImageFormat.Sint:   return (PixelInternalFormat.Rgba8i,       PixelFormat.RgbaInteger, PixelType.Byte);
+                case GalImageFormat.A8B8G8R8     | GalImageFormat.Uint:   return (PixelInternalFormat.Rgba8ui,      PixelFormat.RgbaInteger, PixelType.UnsignedByte);
+                case GalImageFormat.A8B8G8R8_SRGB:                        return (PixelInternalFormat.Srgb8Alpha8,  PixelFormat.Rgba,        PixelType.UnsignedByte);
+                case GalImageFormat.A4B4G4R4     | GalImageFormat.Unorm:  return (PixelInternalFormat.Rgba4,        PixelFormat.Rgba,        PixelType.UnsignedShort4444Reversed);
+                case GalImageFormat.A2B10G10R10  | GalImageFormat.Uint:   return (PixelInternalFormat.Rgb10A2ui,    PixelFormat.RgbaInteger, PixelType.UnsignedInt2101010Reversed);
+                case GalImageFormat.A2B10G10R10  | GalImageFormat.Unorm:  return (PixelInternalFormat.Rgb10A2,      PixelFormat.Rgba,        PixelType.UnsignedInt2101010Reversed);
+                case GalImageFormat.R32          | GalImageFormat.Sfloat: return (PixelInternalFormat.R32f,         PixelFormat.Red,         PixelType.Float);
+                case GalImageFormat.R32          | GalImageFormat.Sint:   return (PixelInternalFormat.R32i,         PixelFormat.Red,         PixelType.Int);
+                case GalImageFormat.R32          | GalImageFormat.Uint:   return (PixelInternalFormat.R32ui,        PixelFormat.Red,         PixelType.UnsignedInt);
+                case GalImageFormat.A1R5G5B5     | GalImageFormat.Unorm:  return (PixelInternalFormat.Rgb5A1,       PixelFormat.Rgba,        PixelType.UnsignedShort5551);
+                case GalImageFormat.B5G6R5       | GalImageFormat.Unorm:  return (PixelInternalFormat.Rgba,         PixelFormat.Rgb,         PixelType.UnsignedShort565);
+                case GalImageFormat.R16G16       | GalImageFormat.Sfloat: return (PixelInternalFormat.Rg16f,        PixelFormat.Rg,          PixelType.HalfFloat);
+                case GalImageFormat.R16G16       | GalImageFormat.Sint:   return (PixelInternalFormat.Rg16i,        PixelFormat.RgInteger,   PixelType.Short);
+                case GalImageFormat.R16G16       | GalImageFormat.Snorm:  return (PixelInternalFormat.Rg16Snorm,    PixelFormat.Rg,          PixelType.Byte);
+                case GalImageFormat.R16G16       | GalImageFormat.Unorm:  return (PixelInternalFormat.Rg16,         PixelFormat.Rg,          PixelType.UnsignedShort);
+                case GalImageFormat.R8G8         | GalImageFormat.Sint:   return (PixelInternalFormat.Rg8i,         PixelFormat.RgInteger,   PixelType.Byte);
+                case GalImageFormat.R8G8         | GalImageFormat.Snorm:  return (PixelInternalFormat.Rg8Snorm,     PixelFormat.Rg,          PixelType.Byte);
+                case GalImageFormat.R8G8         | GalImageFormat.Uint:   return (PixelInternalFormat.Rg8ui,        PixelFormat.RgInteger,   PixelType.UnsignedByte);
+                case GalImageFormat.R8G8         | GalImageFormat.Unorm:  return (PixelInternalFormat.Rg8,          PixelFormat.Rg,          PixelType.UnsignedByte);
+                case GalImageFormat.R16          | GalImageFormat.Sfloat: return (PixelInternalFormat.R16f,         PixelFormat.Red,         PixelType.HalfFloat);
+                case GalImageFormat.R16          | GalImageFormat.Sint:   return (PixelInternalFormat.R16i,         PixelFormat.RedInteger,  PixelType.Short);
+                case GalImageFormat.R16          | GalImageFormat.Snorm:  return (PixelInternalFormat.R16Snorm,     PixelFormat.Red,         PixelType.Byte);
+                case GalImageFormat.R16          | GalImageFormat.Uint:   return (PixelInternalFormat.R16ui,        PixelFormat.RedInteger,  PixelType.UnsignedShort);
+                case GalImageFormat.R16          | GalImageFormat.Unorm:  return (PixelInternalFormat.R16,          PixelFormat.Red,         PixelType.UnsignedShort);
+                case GalImageFormat.R8           | GalImageFormat.Sint:   return (PixelInternalFormat.R8i,          PixelFormat.RedInteger,  PixelType.Byte);
+                case GalImageFormat.R8           | GalImageFormat.Snorm:  return (PixelInternalFormat.R8Snorm,      PixelFormat.Red,         PixelType.Byte);
+                case GalImageFormat.R8           | GalImageFormat.Uint:   return (PixelInternalFormat.R8ui,         PixelFormat.RedInteger,  PixelType.UnsignedByte);
+                case GalImageFormat.R8           | GalImageFormat.Unorm:  return (PixelInternalFormat.R8,           PixelFormat.Red,         PixelType.UnsignedByte);
+                case GalImageFormat.B10G11R11    | GalImageFormat.Sfloat: return (PixelInternalFormat.R11fG11fB10f, PixelFormat.Rgb,         PixelType.UnsignedInt10F11F11FRev);
 
-                case GalImageFormat.R4G4B4A4_UNORM_PACK16_REVERSED: return (PixelInternalFormat.Rgba4, PixelFormat.Rgba, PixelType.UnsignedShort4444Reversed);
-
-                case GalImageFormat.D24_UNORM_S8_UINT:  return (PixelInternalFormat.Depth24Stencil8,   PixelFormat.DepthStencil,   PixelType.UnsignedInt248);
-                case GalImageFormat.D32_SFLOAT:         return (PixelInternalFormat.DepthComponent32f, PixelFormat.DepthComponent, PixelType.Float);
-                case GalImageFormat.D16_UNORM:          return (PixelInternalFormat.DepthComponent16,  PixelFormat.DepthComponent, PixelType.UnsignedShort);
-                case GalImageFormat.D32_SFLOAT_S8_UINT: return (PixelInternalFormat.Depth32fStencil8,  PixelFormat.DepthStencil,   PixelType.Float32UnsignedInt248Rev);
+                case GalImageFormat.D24_S8 | GalImageFormat.Unorm:  return (PixelInternalFormat.Depth24Stencil8,   PixelFormat.DepthStencil,   PixelType.UnsignedInt248);
+                case GalImageFormat.D32    | GalImageFormat.Sfloat: return (PixelInternalFormat.DepthComponent32f, PixelFormat.DepthComponent, PixelType.Float);
+                case GalImageFormat.D16    | GalImageFormat.Unorm:  return (PixelInternalFormat.DepthComponent16,  PixelFormat.DepthComponent, PixelType.UnsignedShort);
+                case GalImageFormat.D32_S8 | GalImageFormat.Uint:   return (PixelInternalFormat.Depth32fStencil8,  PixelFormat.DepthStencil,   PixelType.Float32UnsignedInt248Rev);
             }
 
             throw new NotImplementedException(Format.ToString());
@@ -184,16 +183,16 @@ namespace Ryujinx.Graphics.Gal.OpenGL
         {
             switch (Format)
             {
-                case GalImageFormat.BC6H_UFLOAT_BLOCK:    return InternalFormat.CompressedRgbBptcUnsignedFloat;
-                case GalImageFormat.BC6H_SFLOAT_BLOCK:    return InternalFormat.CompressedRgbBptcSignedFloat;
-                case GalImageFormat.BC7_UNORM_BLOCK:      return InternalFormat.CompressedRgbaBptcUnorm;
-                case GalImageFormat.BC1_RGBA_UNORM_BLOCK: return InternalFormat.CompressedRgbaS3tcDxt1Ext;
-                case GalImageFormat.BC2_UNORM_BLOCK:      return InternalFormat.CompressedRgbaS3tcDxt3Ext;
-                case GalImageFormat.BC3_UNORM_BLOCK:      return InternalFormat.CompressedRgbaS3tcDxt5Ext;
-                case GalImageFormat.BC4_SNORM_BLOCK:      return InternalFormat.CompressedSignedRedRgtc1;
-                case GalImageFormat.BC4_UNORM_BLOCK:      return InternalFormat.CompressedRedRgtc1;
-                case GalImageFormat.BC5_SNORM_BLOCK:      return InternalFormat.CompressedSignedRgRgtc2;
-                case GalImageFormat.BC5_UNORM_BLOCK:      return InternalFormat.CompressedRgRgtc2;
+                case GalImageFormat.BC6H_UF16 | GalImageFormat.Unorm: return InternalFormat.CompressedRgbBptcUnsignedFloat;
+                case GalImageFormat.BC6H_SF16 | GalImageFormat.Unorm: return InternalFormat.CompressedRgbBptcSignedFloat;
+                case GalImageFormat.BC7       | GalImageFormat.Unorm: return InternalFormat.CompressedRgbaBptcUnorm;
+                case GalImageFormat.BC1_RGBA  | GalImageFormat.Unorm: return InternalFormat.CompressedRgbaS3tcDxt1Ext;
+                case GalImageFormat.BC2       | GalImageFormat.Unorm: return InternalFormat.CompressedRgbaS3tcDxt3Ext;
+                case GalImageFormat.BC3       | GalImageFormat.Unorm: return InternalFormat.CompressedRgbaS3tcDxt5Ext;
+                case GalImageFormat.BC4       | GalImageFormat.Snorm: return InternalFormat.CompressedSignedRedRgtc1;
+                case GalImageFormat.BC4       | GalImageFormat.Unorm: return InternalFormat.CompressedRedRgtc1;
+                case GalImageFormat.BC5       | GalImageFormat.Snorm: return InternalFormat.CompressedSignedRgRgtc2;
+                case GalImageFormat.BC5       | GalImageFormat.Unorm: return InternalFormat.CompressedRgRgtc2;
             }
 
             throw new NotImplementedException(Format.ToString());
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs
similarity index 98%
rename from Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs
rename to Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs
index 12239c4f0..99bfa350d 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs
@@ -1,9 +1,10 @@
 using OpenTK.Graphics.OpenGL;
+using Ryujinx.Graphics.Texture;
 using System;
 
 namespace Ryujinx.Graphics.Gal.OpenGL
 {
-    class OGLFrameBuffer : IGalFrameBuffer
+    class OGLRenderTarget : IGalRenderTarget
     {
         private struct Rect
         {
@@ -24,7 +25,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
         private const int NativeWidth  = 1280;
         private const int NativeHeight = 720;
 
-        private const GalImageFormat RawFormat = GalImageFormat.A8B8G8R8_UNORM_PACK32;
+        private const GalImageFormat RawFormat = GalImageFormat.A8B8G8R8 | GalImageFormat.Unorm;
 
         private OGLTexture Texture;
 
@@ -56,7 +57,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
         private int DepthAttachment;
         private int StencilAttachment;
 
-        public OGLFrameBuffer(OGLTexture Texture)
+        public OGLRenderTarget(OGLTexture Texture)
         {
             ColorAttachments = new int[8];
 
@@ -379,7 +380,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
         {
             if (Texture.TryGetImage(Key, out ImageHandler Tex))
             {
-                byte[] Data = new byte[Tex.Width * Tex.Height * ImageHandler.MaxBpp];
+                byte[] Data = new byte[ImageUtils.GetSize(Tex.Image)];
 
                 GL.BindTexture(TextureTarget.Texture2D, Tex.Handle);
 
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLRenderer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLRenderer.cs
index 985f1086f..a23541f3d 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLRenderer.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLRenderer.cs
@@ -7,7 +7,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
     {
         public IGalConstBuffer Buffer { get; private set; }
 
-        public IGalFrameBuffer FrameBuffer { get; private set; }
+        public IGalRenderTarget RenderTarget { get; private set; }
 
         public IGalRasterizer Rasterizer { get; private set; }
 
@@ -25,7 +25,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
 
             Texture = new OGLTexture();
 
-            FrameBuffer = new OGLFrameBuffer(Texture as OGLTexture);
+            RenderTarget = new OGLRenderTarget(Texture as OGLTexture);
 
             Rasterizer = new OGLRasterizer();
 
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs
index e4d4bd648..82f9c9139 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs
@@ -1,5 +1,5 @@
 using OpenTK.Graphics.OpenGL;
-using Ryujinx.Graphics.Gal.Texture;
+using Ryujinx.Graphics.Texture;
 using System;
 
 namespace Ryujinx.Graphics.Gal.OpenGL
@@ -39,7 +39,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             const int Level  = 0; //TODO: Support mipmap textures.
             const int Border = 0;
 
-            if (IsCompressedTextureFormat(Image.Format))
+            GalImageFormat TypeLess = Image.Format & GalImageFormat.FormatMask;
+
+            bool IsASTC = TypeLess >= GalImageFormat.ASTC_BEGIN && TypeLess <= GalImageFormat.ASTC_END;
+
+            if (ImageUtils.IsCompressed(Image.Format) && !IsASTC)
             {
                 InternalFormat InternalFmt = OGLEnumConverter.GetCompressedImageFormat(Image.Format);
 
@@ -55,7 +59,8 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             }
             else
             {
-                if (Image.Format >= GalImageFormat.ASTC_BEGIN && Image.Format <= GalImageFormat.ASTC_END)
+                //TODO: Use KHR_texture_compression_astc_hdr when available
+                if (IsASTC)
                 {
                     int TextureBlockWidth  = GetAstcBlockWidth(Image.Format);
                     int TextureBlockHeight = GetAstcBlockHeight(Image.Format);
@@ -67,7 +72,17 @@ namespace Ryujinx.Graphics.Gal.OpenGL
                         Image.Width,
                         Image.Height, 1);
 
-                    Image.Format = GalImageFormat.A8B8G8R8_UNORM_PACK32;
+                    Image.Format = GalImageFormat.A8B8G8R8 | GalImageFormat.Unorm;
+                }
+                else if (TypeLess == GalImageFormat.G8R8)
+                {
+                    Data = ImageConverter.G8R8ToR8G8(
+                        Data,
+                        Image.Width,
+                        Image.Height,
+                        1);
+
+                    Image.Format = GalImageFormat.R8G8 | (Image.Format & GalImageFormat.FormatMask);
                 }
 
                 (PixelInternalFormat InternalFormat, PixelFormat Format, PixelType Type) = OGLEnumConverter.GetImageFormat(Image.Format);
@@ -123,20 +138,20 @@ namespace Ryujinx.Graphics.Gal.OpenGL
         {
             switch (Format)
             {
-                case GalImageFormat.ASTC_4x4_UNORM_BLOCK:   return 4;
-                case GalImageFormat.ASTC_5x5_UNORM_BLOCK:   return 5;
-                case GalImageFormat.ASTC_6x6_UNORM_BLOCK:   return 6;
-                case GalImageFormat.ASTC_8x8_UNORM_BLOCK:   return 8;
-                case GalImageFormat.ASTC_10x10_UNORM_BLOCK: return 10;
-                case GalImageFormat.ASTC_12x12_UNORM_BLOCK: return 12;
-                case GalImageFormat.ASTC_5x4_UNORM_BLOCK:   return 5;
-                case GalImageFormat.ASTC_6x5_UNORM_BLOCK:   return 6;
-                case GalImageFormat.ASTC_8x6_UNORM_BLOCK:   return 8;
-                case GalImageFormat.ASTC_10x8_UNORM_BLOCK:  return 10;
-                case GalImageFormat.ASTC_12x10_UNORM_BLOCK: return 12;
-                case GalImageFormat.ASTC_8x5_UNORM_BLOCK:   return 8;
-                case GalImageFormat.ASTC_10x5_UNORM_BLOCK:  return 10;
-                case GalImageFormat.ASTC_10x6_UNORM_BLOCK:  return 10;
+                case GalImageFormat.ASTC_4x4   | GalImageFormat.Unorm: return 4;
+                case GalImageFormat.ASTC_5x5   | GalImageFormat.Unorm: return 5;
+                case GalImageFormat.ASTC_6x6   | GalImageFormat.Unorm: return 6;
+                case GalImageFormat.ASTC_8x8   | GalImageFormat.Unorm: return 8;
+                case GalImageFormat.ASTC_10x10 | GalImageFormat.Unorm: return 10;
+                case GalImageFormat.ASTC_12x12 | GalImageFormat.Unorm: return 12;
+                case GalImageFormat.ASTC_5x4   | GalImageFormat.Unorm: return 5;
+                case GalImageFormat.ASTC_6x5   | GalImageFormat.Unorm: return 6;
+                case GalImageFormat.ASTC_8x6   | GalImageFormat.Unorm: return 8;
+                case GalImageFormat.ASTC_10x8  | GalImageFormat.Unorm: return 10;
+                case GalImageFormat.ASTC_12x10 | GalImageFormat.Unorm: return 12;
+                case GalImageFormat.ASTC_8x5   | GalImageFormat.Unorm: return 8;
+                case GalImageFormat.ASTC_10x5  | GalImageFormat.Unorm: return 10;
+                case GalImageFormat.ASTC_10x6  | GalImageFormat.Unorm: return 10;
             }
 
             throw new ArgumentException(nameof(Format));
@@ -146,20 +161,20 @@ namespace Ryujinx.Graphics.Gal.OpenGL
         {
             switch (Format)
             {
-                case GalImageFormat.ASTC_4x4_UNORM_BLOCK:   return 4;
-                case GalImageFormat.ASTC_5x5_UNORM_BLOCK:   return 5;
-                case GalImageFormat.ASTC_6x6_UNORM_BLOCK:   return 6;
-                case GalImageFormat.ASTC_8x8_UNORM_BLOCK:   return 8;
-                case GalImageFormat.ASTC_10x10_UNORM_BLOCK: return 10;
-                case GalImageFormat.ASTC_12x12_UNORM_BLOCK: return 12;
-                case GalImageFormat.ASTC_5x4_UNORM_BLOCK:   return 4;
-                case GalImageFormat.ASTC_6x5_UNORM_BLOCK:   return 5;
-                case GalImageFormat.ASTC_8x6_UNORM_BLOCK:   return 6;
-                case GalImageFormat.ASTC_10x8_UNORM_BLOCK:  return 8;
-                case GalImageFormat.ASTC_12x10_UNORM_BLOCK: return 10;
-                case GalImageFormat.ASTC_8x5_UNORM_BLOCK:   return 5;
-                case GalImageFormat.ASTC_10x5_UNORM_BLOCK:  return 5;
-                case GalImageFormat.ASTC_10x6_UNORM_BLOCK:  return 6;
+                case GalImageFormat.ASTC_4x4   | GalImageFormat.Unorm: return 4;
+                case GalImageFormat.ASTC_5x5   | GalImageFormat.Unorm: return 5;
+                case GalImageFormat.ASTC_6x6   | GalImageFormat.Unorm: return 6;
+                case GalImageFormat.ASTC_8x8   | GalImageFormat.Unorm: return 8;
+                case GalImageFormat.ASTC_10x10 | GalImageFormat.Unorm: return 10;
+                case GalImageFormat.ASTC_12x12 | GalImageFormat.Unorm: return 12;
+                case GalImageFormat.ASTC_5x4   | GalImageFormat.Unorm: return 4;
+                case GalImageFormat.ASTC_6x5   | GalImageFormat.Unorm: return 5;
+                case GalImageFormat.ASTC_8x6   | GalImageFormat.Unorm: return 6;
+                case GalImageFormat.ASTC_10x8  | GalImageFormat.Unorm: return 8;
+                case GalImageFormat.ASTC_12x10 | GalImageFormat.Unorm: return 10;
+                case GalImageFormat.ASTC_8x5   | GalImageFormat.Unorm: return 5;
+                case GalImageFormat.ASTC_10x5  | GalImageFormat.Unorm: return 5;
+                case GalImageFormat.ASTC_10x6  | GalImageFormat.Unorm: return 6;
             }
 
             throw new ArgumentException(nameof(Format));
@@ -216,25 +231,5 @@ namespace Ryujinx.Graphics.Gal.OpenGL
 
             GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureBorderColor, Color);
         }
-
-        private static bool IsCompressedTextureFormat(GalImageFormat Format)
-        {
-            switch (Format)
-            {
-                case GalImageFormat.BC6H_UFLOAT_BLOCK:
-                case GalImageFormat.BC6H_SFLOAT_BLOCK:
-                case GalImageFormat.BC7_UNORM_BLOCK:
-                case GalImageFormat.BC1_RGBA_UNORM_BLOCK:
-                case GalImageFormat.BC2_UNORM_BLOCK:
-                case GalImageFormat.BC3_UNORM_BLOCK:
-                case GalImageFormat.BC4_SNORM_BLOCK:
-                case GalImageFormat.BC4_UNORM_BLOCK:
-                case GalImageFormat.BC5_SNORM_BLOCK:
-                case GalImageFormat.BC5_UNORM_BLOCK:
-                    return true;
-            }
-
-            return false;
-        }
     }
 }
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs
index afff7e9b3..c2ee474b1 100644
--- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs
@@ -33,20 +33,20 @@ namespace Ryujinx.Graphics.Gal.Shader
 
         public static void Fadd_I32(ShaderIrBlock Block, long OpCode, long Position)
         {
-            ShaderIrNode OperA = GetOperGpr8     (OpCode);
-            ShaderIrNode OperB = GetOperImmf32_20(OpCode);
+            ShaderIrNode OperA = OpCode.Gpr8();
+            ShaderIrNode OperB = OpCode.Immf32_20();
 
-            bool NegB = ((OpCode >> 53) & 1) != 0;
-            bool AbsA = ((OpCode >> 54) & 1) != 0;
-            bool NegA = ((OpCode >> 56) & 1) != 0;
-            bool AbsB = ((OpCode >> 57) & 1) != 0;
+            bool NegB = OpCode.Read(53);
+            bool AbsA = OpCode.Read(54);
+            bool NegA = OpCode.Read(56);
+            bool AbsB = OpCode.Read(57);
 
             OperA = GetAluFabsFneg(OperA, AbsA, NegA);
             OperB = GetAluFabsFneg(OperB, AbsB, NegB);
 
             ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Fadd, OperA, OperB);
 
-            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
+            Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op)));
         }
 
         public static void Fadd_R(ShaderIrBlock Block, long OpCode, long Position)
@@ -91,12 +91,12 @@ namespace Ryujinx.Graphics.Gal.Shader
 
         public static void Fmul_I32(ShaderIrBlock Block, long OpCode, long Position)
         {
-            ShaderIrNode OperA = GetOperGpr8     (OpCode);
-            ShaderIrNode OperB = GetOperImmf32_20(OpCode);
+            ShaderIrNode OperA = OpCode.Gpr8();
+            ShaderIrNode OperB = OpCode.Immf32_20();
 
             ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Fmul, OperA, OperB);
 
-            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
+            Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op)));
         }
 
         public static void Fmul_C(ShaderIrBlock Block, long OpCode, long Position)
@@ -156,16 +156,16 @@ namespace Ryujinx.Graphics.Gal.Shader
 
         public static void Iadd_I32(ShaderIrBlock Block, long OpCode, long Position)
         {
-            ShaderIrNode OperA = GetOperGpr8    (OpCode);
-            ShaderIrNode OperB = GetOperImm32_20(OpCode);
+            ShaderIrNode OperA = OpCode.Gpr8();
+            ShaderIrNode OperB = OpCode.Imm32_20();
 
-            bool NegA = ((OpCode >> 56) & 1) != 0;
+            bool NegA = OpCode.Read(56);
 
             OperA = GetAluIneg(OperA, NegA);
 
             ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Add, OperA, OperB);
 
-            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
+            Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op)));
         }
 
         public static void Iadd_R(ShaderIrBlock Block, long OpCode, long Position)
@@ -205,16 +205,16 @@ namespace Ryujinx.Graphics.Gal.Shader
 
         public static void Ipa(ShaderIrBlock Block, long OpCode, long Position)
         {
-            ShaderIrNode OperA = GetOperAbuf28(OpCode);
-            ShaderIrNode OperB = GetOperGpr20 (OpCode);
+            ShaderIrNode OperA = OpCode.Abuf28();
+            ShaderIrNode OperB = OpCode.Gpr20();
 
-            ShaderIpaMode Mode = (ShaderIpaMode)((OpCode >> 54) & 3);
+            ShaderIpaMode Mode = (ShaderIpaMode)(OpCode.Read(54, 3));
 
             ShaderIrMetaIpa Meta = new ShaderIrMetaIpa(Mode);
 
             ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Ipa, OperA, OperB, null, Meta);
 
-            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
+            Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op)));
         }
 
         public static void Iscadd_C(ShaderIrBlock Block, long OpCode, long Position)
@@ -264,10 +264,10 @@ namespace Ryujinx.Graphics.Gal.Shader
 
         public static void Lop_I32(ShaderIrBlock Block, long OpCode, long Position)
         {
-            int SubOp = (int)(OpCode >> 53) & 3;
+            int SubOp = OpCode.Read(53, 3);
 
-            bool InvA = ((OpCode >> 55) & 1) != 0;
-            bool InvB = ((OpCode >> 56) & 1) != 0;
+            bool InvA = OpCode.Read(55);
+            bool InvB = OpCode.Read(56);
 
             ShaderIrInst Inst = 0;
 
@@ -278,21 +278,21 @@ namespace Ryujinx.Graphics.Gal.Shader
                 case 2: Inst = ShaderIrInst.Xor; break;
             }
 
-            ShaderIrNode OperB = GetAluNot(GetOperImm32_20(OpCode), InvB);
+            ShaderIrNode OperB = GetAluNot(OpCode.Imm32_20(), InvB);
 
             //SubOp == 3 is pass, used by the not instruction
             //which just moves the inverted register value.
             if (SubOp < 3)
             {
-                ShaderIrNode OperA = GetAluNot(GetOperGpr8(OpCode), InvA);
+                ShaderIrNode OperA = GetAluNot(OpCode.Gpr8(), InvA);
 
                 ShaderIrOp Op = new ShaderIrOp(Inst, OperA, OperB);
 
-                Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
+                Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op)));
             }
             else
             {
-                Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), OperB), OpCode));
+                Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), OperB)));
             }
         }
 
@@ -313,10 +313,10 @@ namespace Ryujinx.Graphics.Gal.Shader
 
         public static void Mufu(ShaderIrBlock Block, long OpCode, long Position)
         {
-            int SubOp = (int)(OpCode >> 20) & 0xf;
+            int SubOp = OpCode.Read(20, 0xf);
 
-            bool AbsA = ((OpCode >> 46) & 1) != 0;
-            bool NegA = ((OpCode >> 48) & 1) != 0;
+            bool AbsA = OpCode.Read(46);
+            bool NegA = OpCode.Read(48);
 
             ShaderIrInst Inst = 0;
 
@@ -333,23 +333,23 @@ namespace Ryujinx.Graphics.Gal.Shader
                 default: throw new NotImplementedException(SubOp.ToString());
             }
 
-            ShaderIrNode OperA = GetOperGpr8(OpCode);
+            ShaderIrNode OperA = OpCode.Gpr8();
 
             ShaderIrOp Op = new ShaderIrOp(Inst, GetAluFabsFneg(OperA, AbsA, NegA));
 
-            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
+            Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op)));
         }
 
         public static void Psetp(ShaderIrBlock Block, long OpCode, long Position)
         {
-            bool NegA = ((OpCode >> 15) & 1) != 0;
-            bool NegB = ((OpCode >> 32) & 1) != 0;
-            bool NegP = ((OpCode >> 42) & 1) != 0;
+            bool NegA = OpCode.Read(15);
+            bool NegB = OpCode.Read(32);
+            bool NegP = OpCode.Read(42);
 
-            ShaderIrInst LopInst = GetBLop24(OpCode);
+            ShaderIrInst LopInst = OpCode.BLop24();
 
-            ShaderIrNode OperA = GetOperPred12(OpCode);
-            ShaderIrNode OperB = GetOperPred29(OpCode);
+            ShaderIrNode OperA = OpCode.Pred12();
+            ShaderIrNode OperB = OpCode.Pred29();
 
             if (NegA)
             {
@@ -363,13 +363,13 @@ namespace Ryujinx.Graphics.Gal.Shader
 
             ShaderIrOp Op = new ShaderIrOp(LopInst, OperA, OperB);
 
-            ShaderIrOperPred P0Node = GetOperPred3 (OpCode);
-            ShaderIrOperPred P1Node = GetOperPred0 (OpCode);
-            ShaderIrOperPred P2Node = GetOperPred39(OpCode);
+            ShaderIrOperPred P0Node = OpCode.Pred3();
+            ShaderIrOperPred P1Node = OpCode.Pred0();
+            ShaderIrOperPred P2Node = OpCode.Pred39();
 
-            Block.AddNode(GetPredNode(new ShaderIrAsg(P0Node, Op), OpCode));
+            Block.AddNode(OpCode.PredNode(new ShaderIrAsg(P0Node, Op)));
 
-            LopInst = GetBLop45(OpCode);
+            LopInst = OpCode.BLop45();
 
             if (LopInst == ShaderIrInst.Band && P1Node.IsConst && P2Node.IsConst)
             {
@@ -387,11 +387,11 @@ namespace Ryujinx.Graphics.Gal.Shader
 
             Op = new ShaderIrOp(LopInst, Op, P2NNode);
 
-            Block.AddNode(GetPredNode(new ShaderIrAsg(P1Node, Op), OpCode));
+            Block.AddNode(OpCode.PredNode(new ShaderIrAsg(P1Node, Op)));
 
             Op = new ShaderIrOp(LopInst, P0Node, P2NNode);
 
-            Block.AddNode(GetPredNode(new ShaderIrAsg(P0Node, Op), OpCode));
+            Block.AddNode(OpCode.PredNode(new ShaderIrAsg(P0Node, Op)));
         }
 
         public static void Rro_C(ShaderIrBlock Block, long OpCode, long Position)
@@ -441,33 +441,33 @@ namespace Ryujinx.Graphics.Gal.Shader
 
         private static ShaderIrInst GetShrInst(long OpCode)
         {
-            bool Signed = ((OpCode >> 48) & 1) != 0;
+            bool Signed = OpCode.Read(48);
 
             return Signed ? ShaderIrInst.Asr : ShaderIrInst.Lsr;
         }
 
         public static void Vmad(ShaderIrBlock Block, long OpCode, long Position)
         {
-            ShaderIrNode OperA = GetOperGpr8(OpCode);
+            ShaderIrNode OperA = OpCode.Gpr8();
 
             ShaderIrNode OperB;
 
-            if (((OpCode >> 50) & 1) != 0)
+            if (OpCode.Read(50))
             {
-                OperB = GetOperGpr20(OpCode);
+                OperB = OpCode.Gpr20();
             }
             else
             {
-                OperB = GetOperImm19_20(OpCode);
+                OperB = OpCode.Imm19_20();
             }
 
-            ShaderIrOperGpr OperC = GetOperGpr39(OpCode);
+            ShaderIrOperGpr OperC = OpCode.Gpr39();
 
             ShaderIrNode Tmp = new ShaderIrOp(ShaderIrInst.Mul, OperA, OperB);
 
             ShaderIrNode Final = new ShaderIrOp(ShaderIrInst.Add, Tmp, OperC);
 
-            int Shr = (int)((OpCode >> 51) & 3);
+            int Shr = OpCode.Read(51, 3);
 
             if (Shr != 0)
             {
@@ -478,7 +478,7 @@ namespace Ryujinx.Graphics.Gal.Shader
 
             Block.AddNode(new ShaderIrCmnt("Stubbed. Instruction is reduced to a * b + c"));
 
-            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Final), OpCode));
+            Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Final)));
         }
 
         public static void Xmad_CR(ShaderIrBlock Block, long OpCode, long Position)
@@ -507,20 +507,20 @@ namespace Ryujinx.Graphics.Gal.Shader
             ShaderOper    Oper,
             ShaderIrInst  Inst)
         {
-            ShaderIrNode OperA = GetOperGpr8(OpCode), OperB;
+            ShaderIrNode OperA = OpCode.Gpr8(), OperB;
 
             switch (Oper)
             {
-                case ShaderOper.CR:  OperB = GetOperCbuf34  (OpCode); break;
-                case ShaderOper.Imm: OperB = GetOperImm19_20(OpCode); break;
-                case ShaderOper.RR:  OperB = GetOperGpr20   (OpCode); break;
+                case ShaderOper.CR:  OperB = OpCode.Cbuf34();   break;
+                case ShaderOper.Imm: OperB = OpCode.Imm19_20(); break;
+                case ShaderOper.RR:  OperB = OpCode.Gpr20();    break;
 
                 default: throw new ArgumentException(nameof(Oper));
             }
 
             ShaderIrNode Op = new ShaderIrOp(Inst, OperA, OperB);
 
-            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
+            Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op)));
         }
 
         private static void EmitBfe(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
@@ -528,23 +528,23 @@ namespace Ryujinx.Graphics.Gal.Shader
             //TODO: Handle the case where position + length
             //is greater than the word size, in this case the sign bit
             //needs to be replicated to fill the remaining space.
-            bool NegB = ((OpCode >> 48) & 1) != 0;
-            bool NegA = ((OpCode >> 49) & 1) != 0;
+            bool NegB = OpCode.Read(48);
+            bool NegA = OpCode.Read(49);
 
-            ShaderIrNode OperA = GetOperGpr8(OpCode), OperB;
+            ShaderIrNode OperA = OpCode.Gpr8(), OperB;
 
             switch (Oper)
             {
-                case ShaderOper.CR:  OperB = GetOperCbuf34  (OpCode); break;
-                case ShaderOper.Imm: OperB = GetOperImm19_20(OpCode); break;
-                case ShaderOper.RR:  OperB = GetOperGpr20   (OpCode); break;
+                case ShaderOper.CR:  OperB = OpCode.Cbuf34();   break;
+                case ShaderOper.Imm: OperB = OpCode.Imm19_20(); break;
+                case ShaderOper.RR:  OperB = OpCode.Gpr20();    break;
 
                 default: throw new ArgumentException(nameof(Oper));
             }
 
             ShaderIrNode Op;
 
-            bool Signed = ((OpCode >> 48) & 1) != 0; //?
+            bool Signed = OpCode.Read(48); //?
 
             if (OperB is ShaderIrOperImm PosLen)
             {
@@ -576,25 +576,25 @@ namespace Ryujinx.Graphics.Gal.Shader
                 Op = ExtendTo32(Op, Signed, OpLen);
             }
 
-            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
+            Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op)));
         }
 
         private static void EmitFadd(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
         {
-            bool NegB = ((OpCode >> 45) & 1) != 0;
-            bool AbsA = ((OpCode >> 46) & 1) != 0;
-            bool NegA = ((OpCode >> 48) & 1) != 0;
-            bool AbsB = ((OpCode >> 49) & 1) != 0;
+            bool NegB = OpCode.Read(45);
+            bool AbsA = OpCode.Read(46);
+            bool NegA = OpCode.Read(48);
+            bool AbsB = OpCode.Read(49);
 
-            ShaderIrNode OperA = GetOperGpr8(OpCode), OperB;
+            ShaderIrNode OperA = OpCode.Gpr8(), OperB;
 
             OperA = GetAluFabsFneg(OperA, AbsA, NegA);
 
             switch (Oper)
             {
-                case ShaderOper.CR:   OperB = GetOperCbuf34   (OpCode); break;
-                case ShaderOper.Immf: OperB = GetOperImmf19_20(OpCode); break;
-                case ShaderOper.RR:   OperB = GetOperGpr20    (OpCode); break;
+                case ShaderOper.CR:   OperB = OpCode.Cbuf34();    break;
+                case ShaderOper.Immf: OperB = OpCode.Immf19_20(); break;
+                case ShaderOper.RR:   OperB = OpCode.Gpr20();     break;
 
                 default: throw new ArgumentException(nameof(Oper));
             }
@@ -603,20 +603,20 @@ namespace Ryujinx.Graphics.Gal.Shader
 
             ShaderIrNode Op = new ShaderIrOp(ShaderIrInst.Fadd, OperA, OperB);
 
-            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
+            Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op)));
         }
 
         private static void EmitFmul(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
         {
-            bool NegB = ((OpCode >> 48) & 1) != 0;
+            bool NegB = OpCode.Read(48);
 
-            ShaderIrNode OperA = GetOperGpr8(OpCode), OperB;
+            ShaderIrNode OperA = OpCode.Gpr8(), OperB;
 
             switch (Oper)
             {
-                case ShaderOper.CR:   OperB = GetOperCbuf34   (OpCode); break;
-                case ShaderOper.Immf: OperB = GetOperImmf19_20(OpCode); break;
-                case ShaderOper.RR:   OperB = GetOperGpr20    (OpCode); break;
+                case ShaderOper.CR:   OperB = OpCode.Cbuf34();    break;
+                case ShaderOper.Immf: OperB = OpCode.Immf19_20(); break;
+                case ShaderOper.RR:   OperB = OpCode.Gpr20();     break;
 
                 default: throw new ArgumentException(nameof(Oper));
             }
@@ -625,22 +625,22 @@ namespace Ryujinx.Graphics.Gal.Shader
 
             ShaderIrNode Op = new ShaderIrOp(ShaderIrInst.Fmul, OperA, OperB);
 
-            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
+            Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op)));
         }
 
         private static void EmitFfma(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
         {
-            bool NegB = ((OpCode >> 48) & 1) != 0;
-            bool NegC = ((OpCode >> 49) & 1) != 0;
+            bool NegB = OpCode.Read(48);
+            bool NegC = OpCode.Read(49);
 
-            ShaderIrNode OperA = GetOperGpr8(OpCode), OperB, OperC;
+            ShaderIrNode OperA = OpCode.Gpr8(), OperB, OperC;
 
             switch (Oper)
             {
-                case ShaderOper.CR:   OperB = GetOperCbuf34   (OpCode); break;
-                case ShaderOper.Immf: OperB = GetOperImmf19_20(OpCode); break;
-                case ShaderOper.RC:   OperB = GetOperGpr39    (OpCode); break;
-                case ShaderOper.RR:   OperB = GetOperGpr20    (OpCode); break;
+                case ShaderOper.CR:   OperB = OpCode.Cbuf34();    break;
+                case ShaderOper.Immf: OperB = OpCode.Immf19_20(); break;
+                case ShaderOper.RC:   OperB = OpCode.Gpr39();     break;
+                case ShaderOper.RR:   OperB = OpCode.Gpr20();     break;
 
                 default: throw new ArgumentException(nameof(Oper));
             }
@@ -649,62 +649,62 @@ namespace Ryujinx.Graphics.Gal.Shader
 
             if (Oper == ShaderOper.RC)
             {
-                OperC = GetAluFneg(GetOperCbuf34(OpCode), NegC);
+                OperC = GetAluFneg(OpCode.Cbuf34(), NegC);
             }
             else
             {
-                OperC = GetAluFneg(GetOperGpr39(OpCode), NegC);
+                OperC = GetAluFneg(OpCode.Gpr39(), NegC);
             }
 
             ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Ffma, OperA, OperB, OperC);
 
-            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
+            Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op)));
         }
 
         private static void EmitIadd(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
         {
-            ShaderIrNode OperA = GetOperGpr8(OpCode);
+            ShaderIrNode OperA = OpCode.Gpr8();
             ShaderIrNode OperB;
 
             switch (Oper)
             {
-                case ShaderOper.CR:  OperB = GetOperCbuf34  (OpCode); break;
-                case ShaderOper.Imm: OperB = GetOperImm19_20(OpCode); break;
-                case ShaderOper.RR:  OperB = GetOperGpr20   (OpCode); break;
+                case ShaderOper.CR:  OperB = OpCode.Cbuf34();   break;
+                case ShaderOper.Imm: OperB = OpCode.Imm19_20(); break;
+                case ShaderOper.RR:  OperB = OpCode.Gpr20();    break;
 
                 default: throw new ArgumentException(nameof(Oper));
             }
 
-            bool NegA = ((OpCode >> 49) & 1) != 0;
-            bool NegB = ((OpCode >> 48) & 1) != 0;
+            bool NegA = OpCode.Read(49);
+            bool NegB = OpCode.Read(48);
 
             OperA = GetAluIneg(OperA, NegA);
             OperB = GetAluIneg(OperB, NegB);
 
             ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Add, OperA, OperB);
 
-            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
+            Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op)));
         }
 
         private static void EmitIadd3(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
         {
-            int Mode = (int)((OpCode >> 37) & 3);
+            int Mode = OpCode.Read(37, 3);
 
-            bool Neg1 = ((OpCode >> 51) & 1) != 0;
-            bool Neg2 = ((OpCode >> 50) & 1) != 0;
-            bool Neg3 = ((OpCode >> 49) & 1) != 0;
+            bool Neg1 = OpCode.Read(51);
+            bool Neg2 = OpCode.Read(50);
+            bool Neg3 = OpCode.Read(49);
 
-            int Height1 = (int)((OpCode >> 35) & 3);
-            int Height2 = (int)((OpCode >> 33) & 3);
-            int Height3 = (int)((OpCode >> 31) & 3);
+            int Height1 = OpCode.Read(35, 3);
+            int Height2 = OpCode.Read(33, 3);
+            int Height3 = OpCode.Read(31, 3);
 
             ShaderIrNode OperB;
 
             switch (Oper)
             {
-                case ShaderOper.CR:  OperB = GetOperCbuf34  (OpCode); break;
-                case ShaderOper.Imm: OperB = GetOperImm19_20(OpCode); break;
-                case ShaderOper.RR:  OperB = GetOperGpr20   (OpCode); break;
+                case ShaderOper.CR:  OperB = OpCode.Cbuf34();   break;
+                case ShaderOper.Imm: OperB = OpCode.Imm19_20(); break;
+                case ShaderOper.RR:  OperB = OpCode.Gpr20();    break;
 
                 default: throw new ArgumentException(nameof(Oper));
             }
@@ -726,9 +726,9 @@ namespace Ryujinx.Graphics.Gal.Shader
                 }
             }
 
-            ShaderIrNode Src1 = GetAluIneg(ApplyHeight(GetOperGpr8(OpCode),  Height1), Neg1);
+            ShaderIrNode Src1 = GetAluIneg(ApplyHeight(OpCode.Gpr8(),  Height1), Neg1);
             ShaderIrNode Src2 = GetAluIneg(ApplyHeight(OperB,                Height2), Neg2);
-            ShaderIrNode Src3 = GetAluIneg(ApplyHeight(GetOperGpr39(OpCode), Height3), Neg3);
+            ShaderIrNode Src3 = GetAluIneg(ApplyHeight(OpCode.Gpr39(), Height3), Neg3);
 
             ShaderIrOp Sum = new ShaderIrOp(ShaderIrInst.Add, Src1, Src2);
 
@@ -744,23 +744,23 @@ namespace Ryujinx.Graphics.Gal.Shader
             //Note: Here there should be a "+ 1" when carry flag is set
             //but since carry is mostly ignored by other instructions, it's excluded for now
 
-            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), new ShaderIrOp(ShaderIrInst.Add, Sum, Src3)), OpCode));
+            Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), new ShaderIrOp(ShaderIrInst.Add, Sum, Src3))));
         }
 
         private static void EmitIscadd(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
         {
-            bool NegB = ((OpCode >> 48) & 1) != 0;
-            bool NegA = ((OpCode >> 49) & 1) != 0;
+            bool NegB = OpCode.Read(48);
+            bool NegA = OpCode.Read(49);
 
-            ShaderIrNode OperA = GetOperGpr8(OpCode), OperB;
+            ShaderIrNode OperA = OpCode.Gpr8(), OperB;
 
-            ShaderIrOperImm Scale = GetOperImm5_39(OpCode);
+            ShaderIrOperImm Scale = OpCode.Imm5_39();
 
             switch (Oper)
             {
-                case ShaderOper.CR:  OperB = GetOperCbuf34  (OpCode); break;
-                case ShaderOper.Imm: OperB = GetOperImm19_20(OpCode); break;
-                case ShaderOper.RR:  OperB = GetOperGpr20   (OpCode); break;
+                case ShaderOper.CR:  OperB = OpCode.Cbuf34();   break;
+                case ShaderOper.Imm: OperB = OpCode.Imm19_20(); break;
+                case ShaderOper.RR:  OperB = OpCode.Gpr20();    break;
 
                 default: throw new ArgumentException(nameof(Oper));
             }
@@ -771,7 +771,7 @@ namespace Ryujinx.Graphics.Gal.Shader
             ShaderIrOp ScaleOp = new ShaderIrOp(ShaderIrInst.Lsl, OperA, Scale);
             ShaderIrOp AddOp   = new ShaderIrOp(ShaderIrInst.Add, OperB, ScaleOp);
 
-            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), AddOp), OpCode));
+            Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), AddOp)));
         }
 
         private static void EmitFmnmx(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
@@ -786,12 +786,12 @@ namespace Ryujinx.Graphics.Gal.Shader
 
         private static void EmitMnmx(ShaderIrBlock Block, long OpCode, bool IsFloat, ShaderOper Oper)
         {
-            bool NegB = ((OpCode >> 45) & 1) != 0;
-            bool AbsA = ((OpCode >> 46) & 1) != 0;
-            bool NegA = ((OpCode >> 48) & 1) != 0;
-            bool AbsB = ((OpCode >> 49) & 1) != 0;
+            bool NegB = OpCode.Read(45);
+            bool AbsA = OpCode.Read(46);
+            bool NegA = OpCode.Read(48);
+            bool AbsB = OpCode.Read(49);
 
-            ShaderIrNode OperA = GetOperGpr8(OpCode), OperB;
+            ShaderIrNode OperA = OpCode.Gpr8(), OperB;
 
             if (IsFloat)
             {
@@ -804,10 +804,10 @@ namespace Ryujinx.Graphics.Gal.Shader
 
             switch (Oper)
             {
-                case ShaderOper.CR:   OperB = GetOperCbuf34   (OpCode); break;
-                case ShaderOper.Imm:  OperB = GetOperImm19_20 (OpCode); break;
-                case ShaderOper.Immf: OperB = GetOperImmf19_20(OpCode); break;
-                case ShaderOper.RR:   OperB = GetOperGpr20    (OpCode); break;
+                case ShaderOper.CR:   OperB = OpCode.Cbuf34();    break;
+                case ShaderOper.Imm:  OperB = OpCode.Imm19_20();  break;
+                case ShaderOper.Immf: OperB = OpCode.Immf19_20(); break;
+                case ShaderOper.RR:   OperB = OpCode.Gpr20();     break;
 
                 default: throw new ArgumentException(nameof(Oper));
             }
@@ -821,7 +821,7 @@ namespace Ryujinx.Graphics.Gal.Shader
                 OperB = GetAluIabsIneg(OperB, AbsB, NegB);
             }
 
-            ShaderIrOperPred Pred = GetOperPred39(OpCode);
+            ShaderIrOperPred Pred = OpCode.Pred39();
 
             ShaderIrOp Op;
 
@@ -830,26 +830,26 @@ namespace Ryujinx.Graphics.Gal.Shader
 
             if (Pred.IsConst)
             {
-                bool IsMax = ((OpCode >> 42) & 1) != 0;
+                bool IsMax = OpCode.Read(42);
 
                 Op = new ShaderIrOp(IsMax
                     ? MaxInst
                     : MinInst, OperA, OperB);
 
-                Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
+                Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op)));
             }
             else
             {
-                ShaderIrNode PredN = GetOperPred39N(OpCode);
+                ShaderIrNode PredN = OpCode.Pred39N();
 
                 ShaderIrOp OpMax = new ShaderIrOp(MaxInst, OperA, OperB);
                 ShaderIrOp OpMin = new ShaderIrOp(MinInst, OperA, OperB);
 
-                ShaderIrAsg AsgMax = new ShaderIrAsg(GetOperGpr0(OpCode), OpMax);
-                ShaderIrAsg AsgMin = new ShaderIrAsg(GetOperGpr0(OpCode), OpMin);
+                ShaderIrAsg AsgMax = new ShaderIrAsg(OpCode.Gpr0(), OpMax);
+                ShaderIrAsg AsgMin = new ShaderIrAsg(OpCode.Gpr0(), OpMin);
 
-                Block.AddNode(GetPredNode(new ShaderIrCond(PredN, AsgMax, Not: true),  OpCode));
-                Block.AddNode(GetPredNode(new ShaderIrCond(PredN, AsgMin, Not: false), OpCode));
+                Block.AddNode(OpCode.PredNode(new ShaderIrCond(PredN, AsgMax, Not: true)));
+                Block.AddNode(OpCode.PredNode(new ShaderIrCond(PredN, AsgMin, Not: false)));
             }
         }
 
@@ -857,16 +857,16 @@ namespace Ryujinx.Graphics.Gal.Shader
         {
             //Note: this is a range reduction instruction and is supposed to
             //be used with Mufu, here it just moves the value and ignores the operation.
-            bool NegA = ((OpCode >> 45) & 1) != 0;
-            bool AbsA = ((OpCode >> 49) & 1) != 0;
+            bool NegA = OpCode.Read(45);
+            bool AbsA = OpCode.Read(49);
 
             ShaderIrNode OperA;
 
             switch (Oper)
             {
-                case ShaderOper.CR:   OperA = GetOperCbuf34   (OpCode); break;
-                case ShaderOper.Immf: OperA = GetOperImmf19_20(OpCode); break;
-                case ShaderOper.RR:   OperA = GetOperGpr20    (OpCode); break;
+                case ShaderOper.CR:   OperA = OpCode.Cbuf34();    break;
+                case ShaderOper.Immf: OperA = OpCode.Immf19_20(); break;
+                case ShaderOper.RR:   OperA = OpCode.Gpr20();     break;
 
                 default: throw new ArgumentException(nameof(Oper));
             }
@@ -875,7 +875,7 @@ namespace Ryujinx.Graphics.Gal.Shader
 
             Block.AddNode(new ShaderIrCmnt("Stubbed."));
 
-            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), OperA), OpCode));
+            Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), OperA)));
         }
 
         private static void EmitFset(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
@@ -890,21 +890,21 @@ namespace Ryujinx.Graphics.Gal.Shader
 
         private static void EmitSet(ShaderIrBlock Block, long OpCode, bool IsFloat, ShaderOper Oper)
         {
-            bool NegA = ((OpCode >> 43) & 1) != 0;
-            bool AbsB = ((OpCode >> 44) & 1) != 0;
-            bool NegB = ((OpCode >> 53) & 1) != 0;
-            bool AbsA = ((OpCode >> 54) & 1) != 0;
+            bool NegA = OpCode.Read(43);
+            bool AbsB = OpCode.Read(44);
+            bool NegB = OpCode.Read(53);
+            bool AbsA = OpCode.Read(54);
 
-            bool BoolFloat = ((OpCode >> (IsFloat ? 52 : 44)) & 1) != 0;
+            bool BoolFloat = OpCode.Read(IsFloat ? 52 : 44);
 
-            ShaderIrNode OperA = GetOperGpr8(OpCode), OperB;
+            ShaderIrNode OperA = OpCode.Gpr8(), OperB;
 
             switch (Oper)
             {
-                case ShaderOper.CR:   OperB = GetOperCbuf34   (OpCode); break;
-                case ShaderOper.Imm:  OperB = GetOperImm19_20 (OpCode); break;
-                case ShaderOper.Immf: OperB = GetOperImmf19_20(OpCode); break;
-                case ShaderOper.RR:   OperB = GetOperGpr20    (OpCode); break;
+                case ShaderOper.CR:   OperB = OpCode.Cbuf34();    break;
+                case ShaderOper.Imm:  OperB = OpCode.Imm19_20();  break;
+                case ShaderOper.Immf: OperB = OpCode.Immf19_20(); break;
+                case ShaderOper.RR:   OperB = OpCode.Gpr20();     break;
 
                 default: throw new ArgumentException(nameof(Oper));
             }
@@ -916,18 +916,18 @@ namespace Ryujinx.Graphics.Gal.Shader
                 OperA = GetAluFabsFneg(OperA, AbsA, NegA);
                 OperB = GetAluFabsFneg(OperB, AbsB, NegB);
 
-                CmpInst = GetCmpF(OpCode);
+                CmpInst = OpCode.CmpF();
             }
             else
             {
-                CmpInst = GetCmp(OpCode);
+                CmpInst = OpCode.Cmp();
             }
 
             ShaderIrOp Op = new ShaderIrOp(CmpInst, OperA, OperB);
 
-            ShaderIrInst LopInst = GetBLop45(OpCode);
+            ShaderIrInst LopInst = OpCode.BLop45();
 
-            ShaderIrOperPred PNode = GetOperPred39(OpCode);
+            ShaderIrOperPred PNode = OpCode.Pred39();
 
             ShaderIrNode Imm0, Imm1;
 
@@ -942,8 +942,8 @@ namespace Ryujinx.Graphics.Gal.Shader
                 Imm1 = new ShaderIrOperImm(-1);
             }
 
-            ShaderIrNode Asg0 = new ShaderIrAsg(GetOperGpr0(OpCode), Imm0);
-            ShaderIrNode Asg1 = new ShaderIrAsg(GetOperGpr0(OpCode), Imm1);
+            ShaderIrNode Asg0 = new ShaderIrAsg(OpCode.Gpr0(), Imm0);
+            ShaderIrNode Asg1 = new ShaderIrAsg(OpCode.Gpr0(), Imm1);
 
             if (LopInst != ShaderIrInst.Band || !PNode.IsConst)
             {
@@ -958,8 +958,8 @@ namespace Ryujinx.Graphics.Gal.Shader
                 Asg1 = new ShaderIrCond(Op, Asg1, Not: false);
             }
 
-            Block.AddNode(GetPredNode(Asg0, OpCode));
-            Block.AddNode(GetPredNode(Asg1, OpCode));
+            Block.AddNode(OpCode.PredNode(Asg0));
+            Block.AddNode(OpCode.PredNode(Asg1));
         }
 
         private static void EmitFsetp(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
@@ -974,19 +974,19 @@ namespace Ryujinx.Graphics.Gal.Shader
 
         private static void EmitSetp(ShaderIrBlock Block, long OpCode, bool IsFloat, ShaderOper Oper)
         {
-            bool AbsA = ((OpCode >>  7) & 1) != 0;
-            bool NegP = ((OpCode >> 42) & 1) != 0;
-            bool NegA = ((OpCode >> 43) & 1) != 0;
-            bool AbsB = ((OpCode >> 44) & 1) != 0;
+            bool AbsA = OpCode.Read(7);
+            bool NegP = OpCode.Read(42);
+            bool NegA = OpCode.Read(43);
+            bool AbsB = OpCode.Read(44);
 
-            ShaderIrNode OperA = GetOperGpr8(OpCode), OperB;
+            ShaderIrNode OperA = OpCode.Gpr8(), OperB;
 
             switch (Oper)
             {
-                case ShaderOper.CR:   OperB = GetOperCbuf34   (OpCode); break;
-                case ShaderOper.Imm:  OperB = GetOperImm19_20 (OpCode); break;
-                case ShaderOper.Immf: OperB = GetOperImmf19_20(OpCode); break;
-                case ShaderOper.RR:   OperB = GetOperGpr20    (OpCode); break;
+                case ShaderOper.CR:   OperB = OpCode.Cbuf34();    break;
+                case ShaderOper.Imm:  OperB = OpCode.Imm19_20();  break;
+                case ShaderOper.Immf: OperB = OpCode.Immf19_20(); break;
+                case ShaderOper.RR:   OperB = OpCode.Gpr20();     break;
 
                 default: throw new ArgumentException(nameof(Oper));
             }
@@ -998,22 +998,22 @@ namespace Ryujinx.Graphics.Gal.Shader
                 OperA = GetAluFabsFneg(OperA, AbsA, NegA);
                 OperB = GetAluFabs    (OperB, AbsB);
 
-                CmpInst = GetCmpF(OpCode);
+                CmpInst = OpCode.CmpF();
             }
             else
             {
-                CmpInst = GetCmp(OpCode);
+                CmpInst = OpCode.Cmp();
             }
 
             ShaderIrOp Op = new ShaderIrOp(CmpInst, OperA, OperB);
 
-            ShaderIrOperPred P0Node = GetOperPred3 (OpCode);
-            ShaderIrOperPred P1Node = GetOperPred0 (OpCode);
-            ShaderIrOperPred P2Node = GetOperPred39(OpCode);
+            ShaderIrOperPred P0Node = OpCode.Pred3();
+            ShaderIrOperPred P1Node = OpCode.Pred0();
+            ShaderIrOperPred P2Node = OpCode.Pred39();
 
-            Block.AddNode(GetPredNode(new ShaderIrAsg(P0Node, Op), OpCode));
+            Block.AddNode(OpCode.PredNode(new ShaderIrAsg(P0Node, Op)));
 
-            ShaderIrInst LopInst = GetBLop45(OpCode);
+            ShaderIrInst LopInst = OpCode.BLop45();
 
             if (LopInst == ShaderIrInst.Band && P1Node.IsConst && P2Node.IsConst)
             {
@@ -1031,19 +1031,19 @@ namespace Ryujinx.Graphics.Gal.Shader
 
             Op = new ShaderIrOp(LopInst, Op, P2NNode);
 
-            Block.AddNode(GetPredNode(new ShaderIrAsg(P1Node, Op), OpCode));
+            Block.AddNode(OpCode.PredNode(new ShaderIrAsg(P1Node, Op)));
 
             Op = new ShaderIrOp(LopInst, P0Node, P2NNode);
 
-            Block.AddNode(GetPredNode(new ShaderIrAsg(P0Node, Op), OpCode));
+            Block.AddNode(OpCode.PredNode(new ShaderIrAsg(P0Node, Op)));
         }
 
         private static void EmitLop(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
         {
-            int SubOp = (int)(OpCode >> 41) & 3;
+            int SubOp = OpCode.Read(41, 3);
 
-            bool InvA = ((OpCode >> 39) & 1) != 0;
-            bool InvB = ((OpCode >> 40) & 1) != 0;
+            bool InvA = OpCode.Read(39);
+            bool InvB = OpCode.Read(40);
 
             ShaderIrInst Inst = 0;
 
@@ -1054,14 +1054,14 @@ namespace Ryujinx.Graphics.Gal.Shader
                 case 2: Inst = ShaderIrInst.Xor; break;
             }
 
-            ShaderIrNode OperA = GetAluNot(GetOperGpr8(OpCode), InvA);
+            ShaderIrNode OperA = GetAluNot(OpCode.Gpr8(), InvA);
             ShaderIrNode OperB;
 
             switch (Oper)
             {
-                case ShaderOper.CR:  OperB = GetOperCbuf34  (OpCode); break;
-                case ShaderOper.Imm: OperB = GetOperImm19_20(OpCode); break;
-                case ShaderOper.RR:  OperB = GetOperGpr20   (OpCode); break;
+                case ShaderOper.CR:  OperB = OpCode.Cbuf34();   break;
+                case ShaderOper.Imm: OperB = OpCode.Imm19_20(); break;
+                case ShaderOper.RR:  OperB = OpCode.Gpr20();    break;
 
                 default: throw new ArgumentException(nameof(Oper));
             }
@@ -1081,23 +1081,23 @@ namespace Ryujinx.Graphics.Gal.Shader
 
             ShaderIrNode Compare = new ShaderIrOp(ShaderIrInst.Cne, Op, new ShaderIrOperImm(0));
 
-            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperPred48(OpCode), Compare), OpCode));
+            Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Pred48(), Compare)));
 
-            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
+            Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op)));
         }
 
         private static void EmitXmad(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
         {
             //TODO: Confirm SignAB/C, it is just a guess.
             //TODO: Implement Mode 3 (CSFU), what it does?
-            bool SignAB = ((OpCode >> 48) & 1) != 0;
-            bool SignC  = ((OpCode >> 49) & 1) != 0;
-            bool HighB  = ((OpCode >> 52) & 1) != 0;
-            bool HighA  = ((OpCode >> 53) & 1) != 0;
+            bool SignAB = OpCode.Read(48);
+            bool SignC  = OpCode.Read(49);
+            bool HighB  = OpCode.Read(52);
+            bool HighA  = OpCode.Read(53);
 
-            int Mode = (int)(OpCode >> 50) & 7;
+            int Mode = OpCode.Read(50, 7);
 
-            ShaderIrNode OperA = GetOperGpr8(OpCode), OperB, OperC;
+            ShaderIrNode OperA = OpCode.Gpr8(), OperB, OperC;
 
             ShaderIrOperImm Imm16  = new ShaderIrOperImm(16);
             ShaderIrOperImm ImmMsk = new ShaderIrOperImm(0xffff);
@@ -1112,10 +1112,10 @@ namespace Ryujinx.Graphics.Gal.Shader
 
             switch (Oper)
             {
-                case ShaderOper.CR:  OperB = GetOperCbuf34  (OpCode); break;
-                case ShaderOper.Imm: OperB = GetOperImm19_20(OpCode); break;
-                case ShaderOper.RC:  OperB = GetOperGpr39   (OpCode); break;
-                case ShaderOper.RR:  OperB = GetOperGpr20   (OpCode); break;
+                case ShaderOper.CR:  OperB = OpCode.Cbuf34();   break;
+                case ShaderOper.Imm: OperB = OpCode.Imm19_20(); break;
+                case ShaderOper.RC:  OperB = OpCode.Gpr39();    break;
+                case ShaderOper.RR:  OperB = OpCode.Gpr20();    break;
 
                 default: throw new ArgumentException(nameof(Oper));
             }
@@ -1124,14 +1124,14 @@ namespace Ryujinx.Graphics.Gal.Shader
 
             if (Oper == ShaderOper.RC)
             {
-                OperC = GetOperCbuf34(OpCode);
+                OperC = OpCode.Cbuf34();
             }
             else
             {
-                OperC = GetOperGpr39(OpCode);
+                OperC = OpCode.Gpr39();
 
-                ProductShiftLeft = ((OpCode >> 36) & 1) != 0;
-                Merge            = ((OpCode >> 37) & 1) != 0;
+                ProductShiftLeft = OpCode.Read(36);
+                Merge            = OpCode.Read(37);
             }
 
             switch (Mode)
@@ -1172,7 +1172,7 @@ namespace Ryujinx.Graphics.Gal.Shader
                 AddOp = new ShaderIrOp(ShaderIrInst.Or,  AddOp, OperB);
             }
 
-            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), AddOp), OpCode));
+            Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), AddOp)));
         }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs
index 2c699a1b8..dfd10e002 100644
--- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs
@@ -15,11 +15,11 @@ namespace Ryujinx.Graphics.Gal.Shader
                 throw new NotImplementedException();
             }
 
-            int Target = ((int)(OpCode >> 20) << 8) >> 8;
+            int Target = OpCode.Branch();
 
             ShaderIrOperImm Imm = new ShaderIrOperImm(Target);
 
-            Block.AddNode(GetPredNode(new ShaderIrOp(ShaderIrInst.Bra, Imm), OpCode));
+            Block.AddNode(OpCode.PredNode(new ShaderIrOp(ShaderIrInst.Bra, Imm)));
         }
 
         public static void Exit(ShaderIrBlock Block, long OpCode, long Position)
@@ -29,14 +29,14 @@ namespace Ryujinx.Graphics.Gal.Shader
             //TODO: Figure out what the other condition codes mean...
             if (CCode == 0xf)
             {
-                Block.AddNode(GetPredNode(new ShaderIrOp(ShaderIrInst.Exit), OpCode));
+                Block.AddNode(OpCode.PredNode(new ShaderIrOp(ShaderIrInst.Exit)));
             }
 
         }
 
         public static void Kil(ShaderIrBlock Block, long OpCode, long Position)
         {
-            Block.AddNode(GetPredNode(new ShaderIrOp(ShaderIrInst.Kil), OpCode));
+            Block.AddNode(OpCode.PredNode(new ShaderIrOp(ShaderIrInst.Kil)));
         }
 
         public static void Ssy(ShaderIrBlock Block, long OpCode, long Position)
@@ -48,7 +48,7 @@ namespace Ryujinx.Graphics.Gal.Shader
                 throw new NotImplementedException();
             }
 
-            int Offset = ((int)(OpCode >> 20) << 8) >> 8;
+            int Offset = OpCode.Branch();
 
             int Target = (int)(Position + Offset);
 
@@ -61,7 +61,7 @@ namespace Ryujinx.Graphics.Gal.Shader
         {
             //TODO: Implement Sync condition codes
 
-            Block.AddNode(GetPredNode(new ShaderIrOp(ShaderIrInst.Sync), OpCode));
+            Block.AddNode(OpCode.PredNode(new ShaderIrOp(ShaderIrInst.Sync)));
         }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs
index 2093f0706..010f06aa6 100644
--- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs
@@ -4,244 +4,6 @@ namespace Ryujinx.Graphics.Gal.Shader
 {
     static class ShaderDecodeHelper
     {
-        public static ShaderIrOperAbuf[] GetOperAbuf20(long OpCode)
-        {
-            int Abuf = (int)(OpCode >> 20) & 0x3ff;
-            int Size = (int)(OpCode >> 47) & 3;
-
-            ShaderIrOperGpr Vertex = GetOperGpr39(OpCode);
-
-            ShaderIrOperAbuf[] Opers = new ShaderIrOperAbuf[Size + 1];
-
-            for (int Index = 0; Index <= Size; Index++)
-            {
-                Opers[Index] = new ShaderIrOperAbuf(Abuf + Index * 4, Vertex);
-            }
-
-            return Opers;
-        }
-
-        public static ShaderIrOperAbuf GetOperAbuf28(long OpCode)
-        {
-            int Abuf = (int)(OpCode >> 28) & 0x3ff;
-
-            return new ShaderIrOperAbuf(Abuf, GetOperGpr39(OpCode));
-        }
-
-        public static ShaderIrOperCbuf GetOperCbuf34(long OpCode)
-        {
-            return new ShaderIrOperCbuf(
-                (int)(OpCode >> 34) & 0x1f,
-                (int)(OpCode >> 20) & 0x3fff);
-        }
-
-        public static ShaderIrOperGpr GetOperGpr8(long OpCode)
-        {
-            return new ShaderIrOperGpr((int)(OpCode >> 8) & 0xff);
-        }
-
-        public static ShaderIrOperGpr GetOperGpr20(long OpCode)
-        {
-            return new ShaderIrOperGpr((int)(OpCode >> 20) & 0xff);
-        }
-
-        public static ShaderIrOperGpr GetOperGpr39(long OpCode)
-        {
-            return new ShaderIrOperGpr((int)(OpCode >> 39) & 0xff);
-        }
-
-        public static ShaderIrOperGpr GetOperGpr0(long OpCode)
-        {
-            return new ShaderIrOperGpr((int)(OpCode >> 0) & 0xff);
-        }
-
-        public static ShaderIrOperGpr GetOperGpr28(long OpCode)
-        {
-            return new ShaderIrOperGpr((int)(OpCode >> 28) & 0xff);
-        }
-
-        public static ShaderIrOperImm GetOperImm5_39(long OpCode)
-        {
-            return new ShaderIrOperImm((int)(OpCode >> 39) & 0x1f);
-        }
-
-        public static ShaderIrOperImm GetOperImm13_36(long OpCode)
-        {
-            return new ShaderIrOperImm((int)(OpCode >> 36) & 0x1fff);
-        }
-
-        public static ShaderIrOperImm GetOperImm32_20(long OpCode)
-        {
-            return new ShaderIrOperImm((int)(OpCode >> 20));
-        }
-
-        public static ShaderIrOperImmf GetOperImmf32_20(long OpCode)
-        {
-            return new ShaderIrOperImmf(BitConverter.Int32BitsToSingle((int)(OpCode >> 20)));
-        }
-
-        public static ShaderIrOperImm GetOperImm19_20(long OpCode)
-        {
-            int Value = (int)(OpCode >> 20) & 0x7ffff;
-
-            bool Neg = ((OpCode >> 56) & 1) != 0;
-
-            if (Neg)
-            {
-                Value = -Value;
-            }
-
-            return new ShaderIrOperImm((int)Value);
-        }
-
-        public static ShaderIrOperImmf GetOperImmf19_20(long OpCode)
-        {
-            uint Imm = (uint)(OpCode >> 20) & 0x7ffff;
-
-            bool Neg = ((OpCode >> 56) & 1) != 0;
-
-            Imm <<= 12;
-
-            if (Neg)
-            {
-                Imm |= 0x80000000;
-            }
-
-            float Value = BitConverter.Int32BitsToSingle((int)Imm);
-
-            return new ShaderIrOperImmf(Value);
-        }
-
-        public static ShaderIrOperPred GetOperPred0(long OpCode)
-        {
-            return new ShaderIrOperPred((int)(OpCode >> 0) & 7);
-        }
-
-        public static ShaderIrOperPred GetOperPred3(long OpCode)
-        {
-            return new ShaderIrOperPred((int)(OpCode >> 3) & 7);
-        }
-
-        public static ShaderIrOperPred GetOperPred12(long OpCode)
-        {
-            return new ShaderIrOperPred((int)(OpCode >> 12) & 7);
-        }
-
-        public static ShaderIrOperPred GetOperPred29(long OpCode)
-        {
-            return new ShaderIrOperPred((int)(OpCode >> 29) & 7);
-        }
-
-        public static ShaderIrNode GetOperPred39N(long OpCode)
-        {
-            ShaderIrNode Node = GetOperPred39(OpCode);
-
-            if (((OpCode >> 42) & 1) != 0)
-            {
-                Node = new ShaderIrOp(ShaderIrInst.Bnot, Node);
-            }
-
-            return Node;
-        }
-
-        public static ShaderIrOperPred GetOperPred39(long OpCode)
-        {
-            return new ShaderIrOperPred((int)(OpCode >> 39) & 7);
-        }
-
-        public static ShaderIrOperPred GetOperPred48(long OpCode)
-        {
-            return new ShaderIrOperPred((int)((OpCode >> 48) & 7));
-        }
-
-        public static ShaderIrInst GetCmp(long OpCode)
-        {
-            switch ((int)(OpCode >> 49) & 7)
-            {
-                case 1: return ShaderIrInst.Clt;
-                case 2: return ShaderIrInst.Ceq;
-                case 3: return ShaderIrInst.Cle;
-                case 4: return ShaderIrInst.Cgt;
-                case 5: return ShaderIrInst.Cne;
-                case 6: return ShaderIrInst.Cge;
-            }
-
-            throw new ArgumentException(nameof(OpCode));
-        }
-
-        public static ShaderIrInst GetCmpF(long OpCode)
-        {
-            switch ((int)(OpCode >> 48) & 0xf)
-            {
-                case 0x1: return ShaderIrInst.Fclt;
-                case 0x2: return ShaderIrInst.Fceq;
-                case 0x3: return ShaderIrInst.Fcle;
-                case 0x4: return ShaderIrInst.Fcgt;
-                case 0x5: return ShaderIrInst.Fcne;
-                case 0x6: return ShaderIrInst.Fcge;
-                case 0x7: return ShaderIrInst.Fcnum;
-                case 0x8: return ShaderIrInst.Fcnan;
-                case 0x9: return ShaderIrInst.Fcltu;
-                case 0xa: return ShaderIrInst.Fcequ;
-                case 0xb: return ShaderIrInst.Fcleu;
-                case 0xc: return ShaderIrInst.Fcgtu;
-                case 0xd: return ShaderIrInst.Fcneu;
-                case 0xe: return ShaderIrInst.Fcgeu;
-            }
-
-            throw new ArgumentException(nameof(OpCode));
-        }
-
-        public static ShaderIrInst GetBLop45(long OpCode)
-        {
-            switch ((int)(OpCode >> 45) & 3)
-            {
-                case 0: return ShaderIrInst.Band;
-                case 1: return ShaderIrInst.Bor;
-                case 2: return ShaderIrInst.Bxor;
-            }
-
-            throw new ArgumentException(nameof(OpCode));
-        }
-
-        public static ShaderIrInst GetBLop24(long OpCode)
-        {
-            switch ((int)(OpCode >> 24) & 3)
-            {
-                case 0: return ShaderIrInst.Band;
-                case 1: return ShaderIrInst.Bor;
-                case 2: return ShaderIrInst.Bxor;
-            }
-
-            throw new ArgumentException(nameof(OpCode));
-        }
-
-        public static ShaderIrNode GetPredNode(ShaderIrNode Node, long OpCode)
-        {
-            ShaderIrOperPred Pred = GetPredNode(OpCode);
-
-            if (Pred.Index != ShaderIrOperPred.UnusedIndex)
-            {
-                bool Inv = ((OpCode >> 19) & 1) != 0;
-
-                Node = new ShaderIrCond(Pred, Node, Inv);
-            }
-
-            return Node;
-        }
-
-        private static ShaderIrOperPred GetPredNode(long OpCode)
-        {
-            int Pred = (int)(OpCode >> 16) & 0xf;
-
-            if (Pred != 0xf)
-            {
-                Pred &= 7;
-            }
-
-            return new ShaderIrOperPred(Pred);
-        }
-
         public static ShaderIrNode GetAluFabsFneg(ShaderIrNode Node, bool Abs, bool Neg)
         {
             return GetAluFneg(GetAluFabs(Node, Abs), Neg);
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs
index 2ae58bf89..508a0205d 100644
--- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs
@@ -33,28 +33,28 @@ namespace Ryujinx.Graphics.Gal.Shader
 
         public static void Ld_A(ShaderIrBlock Block, long OpCode, long Position)
         {
-            ShaderIrNode[] Opers = GetOperAbuf20(OpCode);
+            ShaderIrNode[] Opers = OpCode.Abuf20();
 
             //Used by GS
-            ShaderIrOperGpr Vertex = GetOperGpr39(OpCode);
+            ShaderIrOperGpr Vertex = OpCode.Gpr39();
 
             int Index = 0;
 
             foreach (ShaderIrNode OperA in Opers)
             {
-                ShaderIrOperGpr OperD = GetOperGpr0(OpCode);
+                ShaderIrOperGpr OperD = OpCode.Gpr0();
 
                 OperD.Index += Index++;
 
-                Block.AddNode(GetPredNode(new ShaderIrAsg(OperD, OperA), OpCode));
+                Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OperD, OperA)));
             }
         }
 
         public static void Ld_C(ShaderIrBlock Block, long OpCode, long Position)
         {
-            int CbufPos   = (int)(OpCode >> 22) & 0x3fff;
-            int CbufIndex = (int)(OpCode >> 36) & 0x1f;
-            int Type      = (int)(OpCode >> 48) & 7;
+            int CbufPos   = OpCode.Read(22, 0x3fff);
+            int CbufIndex = OpCode.Read(36, 0x1f);
+            int Type      = OpCode.Read(48, 7);
 
             if (Type > 5)
             {
@@ -63,7 +63,7 @@ namespace Ryujinx.Graphics.Gal.Shader
 
             ShaderIrOperGpr Temp = ShaderIrOperGpr.MakeTemporary();
 
-            Block.AddNode(new ShaderIrAsg(Temp, GetOperGpr8(OpCode)));
+            Block.AddNode(new ShaderIrAsg(Temp, OpCode.Gpr8()));
 
             int Count = Type == 5 ? 2 : 1;
 
@@ -71,7 +71,7 @@ namespace Ryujinx.Graphics.Gal.Shader
             {
                 ShaderIrOperCbuf OperA = new ShaderIrOperCbuf(CbufIndex, CbufPos, Temp);
 
-                ShaderIrOperGpr OperD = GetOperGpr0(OpCode);
+                ShaderIrOperGpr OperD = OpCode.Gpr0();
 
                 OperA.Pos   += Index;
                 OperD.Index += Index;
@@ -93,43 +93,43 @@ namespace Ryujinx.Graphics.Gal.Shader
                     Node = ExtendTo32(Node, Signed, Size);
                 }
 
-                Block.AddNode(GetPredNode(new ShaderIrAsg(OperD, Node), OpCode));
+                Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OperD, Node)));
             }
         }
 
         public static void St_A(ShaderIrBlock Block, long OpCode, long Position)
         {
-            ShaderIrNode[] Opers = GetOperAbuf20(OpCode);
+            ShaderIrNode[] Opers = OpCode.Abuf20();
 
             int Index = 0;
 
             foreach (ShaderIrNode OperA in Opers)
             {
-                ShaderIrOperGpr OperD = GetOperGpr0(OpCode);
+                ShaderIrOperGpr OperD = OpCode.Gpr0();
 
                 OperD.Index += Index++;
 
-                Block.AddNode(GetPredNode(new ShaderIrAsg(OperA, OperD), OpCode));
+                Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OperA, OperD)));
             }
         }
 
         public static void Texq(ShaderIrBlock Block, long OpCode, long Position)
         {
-            ShaderIrNode OperD = GetOperGpr0(OpCode);
-            ShaderIrNode OperA = GetOperGpr8(OpCode);
+            ShaderIrNode OperD = OpCode.Gpr0();
+            ShaderIrNode OperA = OpCode.Gpr8();
 
-            ShaderTexqInfo Info = (ShaderTexqInfo)((OpCode >> 22) & 0x1f);
+            ShaderTexqInfo Info = (ShaderTexqInfo)(OpCode.Read(22, 0x1f));
 
             ShaderIrMetaTexq Meta0 = new ShaderIrMetaTexq(Info, 0);
             ShaderIrMetaTexq Meta1 = new ShaderIrMetaTexq(Info, 1);
 
-            ShaderIrNode OperC = GetOperImm13_36(OpCode);
+            ShaderIrNode OperC = OpCode.Imm13_36();
 
             ShaderIrOp Op0 = new ShaderIrOp(ShaderIrInst.Texq, OperA, null, OperC, Meta0);
             ShaderIrOp Op1 = new ShaderIrOp(ShaderIrInst.Texq, OperA, null, OperC, Meta1);
 
-            Block.AddNode(GetPredNode(new ShaderIrAsg(OperD, Op0), OpCode));
-            Block.AddNode(GetPredNode(new ShaderIrAsg(OperA, Op1), OpCode)); //Is this right?
+            Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OperD, Op0)));
+            Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OperA, Op1))); //Is this right?
         }
 
         public static void Tex(ShaderIrBlock Block, long OpCode, long Position)
@@ -149,7 +149,7 @@ namespace Ryujinx.Graphics.Gal.Shader
 
             for (int Index = 0; Index < Coords.Length; Index++)
             {
-                Coords[Index] = GetOperGpr8(OpCode);
+                Coords[Index] = OpCode.Gpr8();
 
                 Coords[Index].Index += Index;
 
@@ -159,11 +159,11 @@ namespace Ryujinx.Graphics.Gal.Shader
                 }
             }
 
-            int ChMask = (int)(OpCode >> 31) & 0xf;
+            int ChMask = OpCode.Read(31, 0xf);
 
             ShaderIrNode OperC = GprHandle
-                ? (ShaderIrNode)GetOperGpr20   (OpCode)
-                : (ShaderIrNode)GetOperImm13_36(OpCode);
+                ? (ShaderIrNode)OpCode.Gpr20()
+                : (ShaderIrNode)OpCode.Imm13_36();
 
             ShaderIrInst Inst = GprHandle ? ShaderIrInst.Texb : ShaderIrInst.Texs;
 
@@ -175,7 +175,7 @@ namespace Ryujinx.Graphics.Gal.Shader
 
                 ShaderIrOp Op = new ShaderIrOp(Inst, Coords[0], Coords[1], OperC, Meta);
 
-                Block.AddNode(GetPredNode(new ShaderIrAsg(Dst, Op), OpCode));
+                Block.AddNode(OpCode.PredNode(new ShaderIrAsg(Dst, Op)));
             }
 
             int RegInc = 0;
@@ -189,7 +189,7 @@ namespace Ryujinx.Graphics.Gal.Shader
 
                 ShaderIrOperGpr Src = new ShaderIrOperGpr(TempRegStart + Ch);
 
-                ShaderIrOperGpr Dst = GetOperGpr0(OpCode);
+                ShaderIrOperGpr Dst = OpCode.Gpr0();
 
                 Dst.Index += RegInc++;
 
@@ -198,7 +198,7 @@ namespace Ryujinx.Graphics.Gal.Shader
                     continue;
                 }
 
-                Block.AddNode(GetPredNode(new ShaderIrAsg(Dst, Src), OpCode));
+                Block.AddNode(OpCode.PredNode(new ShaderIrAsg(Dst, Src)));
             }
         }
 
@@ -215,14 +215,14 @@ namespace Ryujinx.Graphics.Gal.Shader
         private static void EmitTexs(ShaderIrBlock Block, long OpCode, ShaderIrInst Inst)
         {
             //TODO: Support other formats.
-            ShaderIrNode OperA = GetOperGpr8    (OpCode);
-            ShaderIrNode OperB = GetOperGpr20   (OpCode);
-            ShaderIrNode OperC = GetOperImm13_36(OpCode);
+            ShaderIrNode OperA = OpCode.Gpr8();
+            ShaderIrNode OperB = OpCode.Gpr20();
+            ShaderIrNode OperC = OpCode.Imm13_36();
 
             int LutIndex;
 
-            LutIndex  = GetOperGpr0 (OpCode).Index != ShaderIrOperGpr.ZRIndex ? 1 : 0;
-            LutIndex |= GetOperGpr28(OpCode).Index != ShaderIrOperGpr.ZRIndex ? 2 : 0;
+            LutIndex  = OpCode.Gpr0 ().Index != ShaderIrOperGpr.ZRIndex ? 1 : 0;
+            LutIndex |= OpCode.Gpr28().Index != ShaderIrOperGpr.ZRIndex ? 2 : 0;
 
             if (LutIndex == 0)
             {
@@ -231,7 +231,7 @@ namespace Ryujinx.Graphics.Gal.Shader
                 return;
             }
 
-            int ChMask = MaskLut[LutIndex, (OpCode >> 50) & 7];
+            int ChMask = MaskLut[LutIndex, OpCode.Read(50, 7)];
 
             for (int Ch = 0; Ch < 4; Ch++)
             {
@@ -241,7 +241,7 @@ namespace Ryujinx.Graphics.Gal.Shader
 
                 ShaderIrOp Op = new ShaderIrOp(Inst, OperA, OperB, OperC, Meta);
 
-                Block.AddNode(GetPredNode(new ShaderIrAsg(Dst, Op), OpCode));
+                Block.AddNode(OpCode.PredNode(new ShaderIrAsg(Dst, Op)));
             }
 
             int RegInc = 0;
@@ -252,11 +252,11 @@ namespace Ryujinx.Graphics.Gal.Shader
 
                 switch (LutIndex)
                 {
-                    case 1: Dst = GetOperGpr0 (OpCode); break;
-                    case 2: Dst = GetOperGpr28(OpCode); break;
+                    case 1: Dst = OpCode.Gpr0();  break;
+                    case 2: Dst = OpCode.Gpr28(); break;
                     case 3: Dst = (RegInc >> 1) != 0
-                        ? GetOperGpr28(OpCode)
-                        : GetOperGpr0 (OpCode); break;
+                        ? OpCode.Gpr28()
+                        : OpCode.Gpr0 (); break;
 
                     default: throw new InvalidOperationException();
                 }
@@ -279,7 +279,7 @@ namespace Ryujinx.Graphics.Gal.Shader
 
                 if (Dst.Index != ShaderIrOperGpr.ZRIndex)
                 {
-                    Block.AddNode(GetPredNode(new ShaderIrAsg(Dst, Src), OpCode));
+                    Block.AddNode(OpCode.PredNode(new ShaderIrAsg(Dst, Src)));
                 }
             }
         }
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs
index aef92c5a9..add394027 100644
--- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs
@@ -92,35 +92,35 @@ namespace Ryujinx.Graphics.Gal.Shader
 
             Block.AddNode(new ShaderIrCmnt("Stubbed."));
 
-            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), GetOperGpr8(OpCode)), OpCode));
+            Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), OpCode.Gpr8())));
         }
 
         public static void Mov_C(ShaderIrBlock Block, long OpCode, long Position)
         {
-            ShaderIrOperCbuf Cbuf = GetOperCbuf34(OpCode);
+            ShaderIrOperCbuf Cbuf = OpCode.Cbuf34();
 
-            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Cbuf), OpCode));
+            Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Cbuf)));
         }
 
         public static void Mov_I(ShaderIrBlock Block, long OpCode, long Position)
         {
-            ShaderIrOperImm Imm = GetOperImm19_20(OpCode);
+            ShaderIrOperImm Imm = OpCode.Imm19_20();
 
-            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Imm), OpCode));
+            Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Imm)));
         }
 
         public static void Mov_I32(ShaderIrBlock Block, long OpCode, long Position)
         {
-            ShaderIrOperImm Imm = GetOperImm32_20(OpCode);
+            ShaderIrOperImm Imm = OpCode.Imm32_20();
 
-            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Imm), OpCode));
+            Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Imm)));
         }
 
         public static void Mov_R(ShaderIrBlock Block, long OpCode, long Position)
         {
-            ShaderIrOperGpr Gpr = GetOperGpr20(OpCode);
+            ShaderIrOperGpr Gpr = OpCode.Gpr20();
 
-            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Gpr), OpCode));
+            Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Gpr)));
         }
 
         public static void Sel_C(ShaderIrBlock Block, long OpCode, long Position)
@@ -145,21 +145,21 @@ namespace Ryujinx.Graphics.Gal.Shader
             //Zero is used as a special number to get a valid "0 * 0 + VertexIndex" in a GS
             ShaderIrNode Source = new ShaderIrOperImm(0);
 
-            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Source), OpCode));
+            Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Source)));
         }
 
         private static void EmitF2f(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
         {
-            bool NegA = ((OpCode >> 45) & 1) != 0;
-            bool AbsA = ((OpCode >> 49) & 1) != 0;
+            bool NegA = OpCode.Read(45);
+            bool AbsA = OpCode.Read(49);
 
             ShaderIrNode OperA;
 
             switch (Oper)
             {
-                case ShaderOper.CR:   OperA = GetOperCbuf34   (OpCode); break;
-                case ShaderOper.Immf: OperA = GetOperImmf19_20(OpCode); break;
-                case ShaderOper.RR:   OperA = GetOperGpr20    (OpCode); break;
+                case ShaderOper.CR:   OperA = OpCode.Cbuf34();    break;
+                case ShaderOper.Immf: OperA = OpCode.Immf19_20(); break;
+                case ShaderOper.RR:   OperA = OpCode.Gpr20();     break;
 
                 default: throw new ArgumentException(nameof(Oper));
             }
@@ -173,7 +173,7 @@ namespace Ryujinx.Graphics.Gal.Shader
                 OperA = new ShaderIrOp(RoundInst, OperA);
             }
 
-            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), OperA), OpCode));
+            Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), OperA)));
         }
 
         private static void EmitF2i(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
@@ -188,16 +188,16 @@ namespace Ryujinx.Graphics.Gal.Shader
                 throw new NotImplementedException();
             }
 
-            bool NegA = ((OpCode >> 45) & 1) != 0;
-            bool AbsA = ((OpCode >> 49) & 1) != 0;
+            bool NegA = OpCode.Read(45);
+            bool AbsA = OpCode.Read(49);
 
             ShaderIrNode OperA;
 
             switch (Oper)
             {
-                case ShaderOper.CR:   OperA = GetOperCbuf34   (OpCode); break;
-                case ShaderOper.Immf: OperA = GetOperImmf19_20(OpCode); break;
-                case ShaderOper.RR:   OperA = GetOperGpr20    (OpCode); break;
+                case ShaderOper.CR:   OperA = OpCode.Cbuf34();    break;
+                case ShaderOper.Immf: OperA = OpCode.Immf19_20(); break;
+                case ShaderOper.RR:   OperA = OpCode.Gpr20();     break;
 
                 default: throw new ArgumentException(nameof(Oper));
             }
@@ -242,7 +242,7 @@ namespace Ryujinx.Graphics.Gal.Shader
 
             ShaderIrNode Op = new ShaderIrOp(Inst, OperA);
 
-            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
+            Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op)));
         }
 
         private static void EmitI2f(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
@@ -257,18 +257,18 @@ namespace Ryujinx.Graphics.Gal.Shader
                 throw new NotImplementedException();
             }
 
-            int Sel = (int)(OpCode >> 41) & 3;
+            int Sel = OpCode.Read(41, 3);
 
-            bool NegA = ((OpCode >> 45) & 1) != 0;
-            bool AbsA = ((OpCode >> 49) & 1) != 0;
+            bool NegA = OpCode.Read(45);
+            bool AbsA = OpCode.Read(49);
 
             ShaderIrNode OperA;
 
             switch (Oper)
             {
-                case ShaderOper.CR:  OperA = GetOperCbuf34  (OpCode); break;
-                case ShaderOper.Imm: OperA = GetOperImm19_20(OpCode); break;
-                case ShaderOper.RR:  OperA = GetOperGpr20   (OpCode); break;
+                case ShaderOper.CR:  OperA = OpCode.Cbuf34();   break;
+                case ShaderOper.Imm: OperA = OpCode.Imm19_20(); break;
+                case ShaderOper.RR:  OperA = OpCode.Gpr20();    break;
 
                 default: throw new ArgumentException(nameof(Oper));
             }
@@ -297,7 +297,7 @@ namespace Ryujinx.Graphics.Gal.Shader
 
             ShaderIrNode Op = new ShaderIrOp(Inst, OperA);
 
-            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
+            Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op)));
         }
 
         private static void EmitI2i(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
@@ -312,19 +312,19 @@ namespace Ryujinx.Graphics.Gal.Shader
                 throw new NotImplementedException();
             }
 
-            int Sel = (int)(OpCode >> 41) & 3;
+            int Sel = OpCode.Read(41, 3);
 
-            bool NegA = ((OpCode >> 45) & 1) != 0;
-            bool AbsA = ((OpCode >> 49) & 1) != 0;
-            bool SatA = ((OpCode >> 50) & 1) != 0;
+            bool NegA = OpCode.Read(45);
+            bool AbsA = OpCode.Read(49);
+            bool SatA = OpCode.Read(50);
 
             ShaderIrNode OperA;
 
             switch (Oper)
             {
-                case ShaderOper.CR:   OperA = GetOperCbuf34   (OpCode); break;
-                case ShaderOper.Immf: OperA = GetOperImmf19_20(OpCode); break;
-                case ShaderOper.RR:   OperA = GetOperGpr20    (OpCode); break;
+                case ShaderOper.CR:   OperA = OpCode.Cbuf34();    break;
+                case ShaderOper.Immf: OperA = OpCode.Immf19_20(); break;
+                case ShaderOper.RR:   OperA = OpCode.Gpr20();     break;
 
                 default: throw new ArgumentException(nameof(Oper));
             }
@@ -372,36 +372,36 @@ namespace Ryujinx.Graphics.Gal.Shader
                 }
             }
 
-            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), OperA), OpCode));
+            Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), OperA)));
         }
 
         private static void EmitSel(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
         {
-            ShaderIrOperGpr Dst  = GetOperGpr0   (OpCode);
-            ShaderIrNode    Pred = GetOperPred39N(OpCode);
+            ShaderIrOperGpr Dst  = OpCode.Gpr0();
+            ShaderIrNode    Pred = OpCode.Pred39N();
 
-            ShaderIrNode ResultA = GetOperGpr8(OpCode);
+            ShaderIrNode ResultA = OpCode.Gpr8();
             ShaderIrNode ResultB;
 
             switch (Oper)
             {
-                case ShaderOper.CR:  ResultB = GetOperCbuf34  (OpCode); break;
-                case ShaderOper.Imm: ResultB = GetOperImm19_20(OpCode); break;
-                case ShaderOper.RR:  ResultB = GetOperGpr20   (OpCode); break;
+                case ShaderOper.CR:  ResultB = OpCode.Cbuf34();   break;
+                case ShaderOper.Imm: ResultB = OpCode.Imm19_20(); break;
+                case ShaderOper.RR:  ResultB = OpCode.Gpr20();    break;
 
                 default: throw new ArgumentException(nameof(Oper));
             }
 
-            Block.AddNode(GetPredNode(new ShaderIrCond(Pred, new ShaderIrAsg(Dst, ResultA), false), OpCode));
+            Block.AddNode(OpCode.PredNode(new ShaderIrCond(Pred, new ShaderIrAsg(Dst, ResultA), false)));
 
-            Block.AddNode(GetPredNode(new ShaderIrCond(Pred, new ShaderIrAsg(Dst, ResultB), true),  OpCode));
+            Block.AddNode(OpCode.PredNode(new ShaderIrCond(Pred, new ShaderIrAsg(Dst, ResultB), true)));
         }
 
         private static IntType GetIntType(long OpCode)
         {
-            bool Signed = ((OpCode >> 13) & 1) != 0;
+            bool Signed = OpCode.Read(13);
 
-            IntType Type = (IntType)((OpCode >> 10) & 3);
+            IntType Type = (IntType)(OpCode.Read(10, 3));
 
             if (Signed)
             {
@@ -413,12 +413,12 @@ namespace Ryujinx.Graphics.Gal.Shader
 
         private static FloatType GetFloatType(long OpCode)
         {
-            return (FloatType)((OpCode >> 8) & 3);
+            return (FloatType)(OpCode.Read(8, 3));
         }
 
         private static ShaderIrInst GetRoundInst(long OpCode)
         {
-            switch ((OpCode >> 39) & 3)
+            switch (OpCode.Read(39, 3))
             {
                 case 1: return ShaderIrInst.Floor;
                 case 2: return ShaderIrInst.Ceil;
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeOpCode.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeOpCode.cs
new file mode 100644
index 000000000..3af17cae8
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeOpCode.cs
@@ -0,0 +1,260 @@
+using System;
+
+namespace Ryujinx.Graphics.Gal.Shader
+{
+    static partial class ShaderDecode
+    {
+        private static int Read(this long OpCode, int Position, int Mask)
+        {
+            return (int)(OpCode >> Position) & Mask;
+        }
+
+        private static bool Read(this long OpCode, int Position)
+        {
+            return ((OpCode >> Position) & 1) != 0;
+        }
+
+        private static int Branch(this long OpCode)
+        {
+            return ((int)(OpCode >> 20) << 8) >> 8;
+        }
+
+        private static ShaderIrOperAbuf[] Abuf20(this long OpCode)
+        {
+            int Abuf = OpCode.Read(20, 0x3ff);
+            int Size = OpCode.Read(47, 3);
+
+            ShaderIrOperGpr Vertex = OpCode.Gpr39();
+
+            ShaderIrOperAbuf[] Opers = new ShaderIrOperAbuf[Size + 1];
+
+            for (int Index = 0; Index <= Size; Index++)
+            {
+                Opers[Index] = new ShaderIrOperAbuf(Abuf + Index * 4, Vertex);
+            }
+
+            return Opers;
+        }
+
+        private static ShaderIrOperAbuf Abuf28(this long OpCode)
+        {
+            int Abuf = OpCode.Read(28, 0x3ff);
+
+            return new ShaderIrOperAbuf(Abuf, OpCode.Gpr39());
+        }
+
+        private static ShaderIrOperCbuf Cbuf34(this long OpCode)
+        {
+            return new ShaderIrOperCbuf(
+                OpCode.Read(34, 0x1f),
+                OpCode.Read(20, 0x3fff));
+        }
+
+        private static ShaderIrOperGpr Gpr8(this long OpCode)
+        {
+            return new ShaderIrOperGpr(OpCode.Read(8, 0xff));
+        }
+
+        private static ShaderIrOperGpr Gpr20(this long OpCode)
+        {
+            return new ShaderIrOperGpr(OpCode.Read(20, 0xff));
+        }
+
+        private static ShaderIrOperGpr Gpr39(this long OpCode)
+        {
+            return new ShaderIrOperGpr(OpCode.Read(39, 0xff));
+        }
+
+        private static ShaderIrOperGpr Gpr0(this long OpCode)
+        {
+            return new ShaderIrOperGpr(OpCode.Read(0, 0xff));
+        }
+
+        private static ShaderIrOperGpr Gpr28(this long OpCode)
+        {
+            return new ShaderIrOperGpr(OpCode.Read(28, 0xff));
+        }
+
+        private static ShaderIrOperImm Imm5_39(this long OpCode)
+        {
+            return new ShaderIrOperImm(OpCode.Read(39, 0x1f));
+        }
+
+        private static ShaderIrOperImm Imm13_36(this long OpCode)
+        {
+            return new ShaderIrOperImm(OpCode.Read(36, 0x1fff));
+        }
+
+        private static ShaderIrOperImm Imm32_20(this long OpCode)
+        {
+            return new ShaderIrOperImm((int)(OpCode >> 20));
+        }
+
+        private static ShaderIrOperImmf Immf32_20(this long OpCode)
+        {
+            return new ShaderIrOperImmf(BitConverter.Int32BitsToSingle((int)(OpCode >> 20)));
+        }
+
+        private static ShaderIrOperImm Imm19_20(this long OpCode)
+        {
+            int Value = OpCode.Read(20, 0x7ffff);
+
+            bool Neg = OpCode.Read(56);
+
+            if (Neg)
+            {
+                Value = -Value;
+            }
+
+            return new ShaderIrOperImm(Value);
+        }
+
+        private static ShaderIrOperImmf Immf19_20(this long OpCode)
+        {
+            uint Imm = (uint)(OpCode >> 20) & 0x7ffff;
+
+            bool Neg = OpCode.Read(56);
+
+            Imm <<= 12;
+
+            if (Neg)
+            {
+                Imm |= 0x80000000;
+            }
+
+            float Value = BitConverter.Int32BitsToSingle((int)Imm);
+
+            return new ShaderIrOperImmf(Value);
+        }
+
+        private static ShaderIrOperPred Pred0(this long OpCode)
+        {
+            return new ShaderIrOperPred(OpCode.Read(0, 7));
+        }
+
+        private static ShaderIrOperPred Pred3(this long OpCode)
+        {
+            return new ShaderIrOperPred(OpCode.Read(3, 7));
+        }
+
+        private static ShaderIrOperPred Pred12(this long OpCode)
+        {
+            return new ShaderIrOperPred(OpCode.Read(12, 7));
+        }
+
+        private static ShaderIrOperPred Pred29(this long OpCode)
+        {
+            return new ShaderIrOperPred(OpCode.Read(29, 7));
+        }
+
+        private static ShaderIrNode Pred39N(this long OpCode)
+        {
+            ShaderIrNode Node = OpCode.Pred39();
+
+            if (OpCode.Read(42))
+            {
+                Node = new ShaderIrOp(ShaderIrInst.Bnot, Node);
+            }
+
+            return Node;
+        }
+
+        private static ShaderIrOperPred Pred39(this long OpCode)
+        {
+            return new ShaderIrOperPred(OpCode.Read(39, 7));
+        }
+
+        private static ShaderIrOperPred Pred48(this long OpCode)
+        {
+            return new ShaderIrOperPred(OpCode.Read(48, 7));
+        }
+
+        private static ShaderIrInst Cmp(this long OpCode)
+        {
+            switch (OpCode.Read(49, 7))
+            {
+                case 1: return ShaderIrInst.Clt;
+                case 2: return ShaderIrInst.Ceq;
+                case 3: return ShaderIrInst.Cle;
+                case 4: return ShaderIrInst.Cgt;
+                case 5: return ShaderIrInst.Cne;
+                case 6: return ShaderIrInst.Cge;
+            }
+
+            throw new ArgumentException(nameof(OpCode));
+        }
+
+        private static ShaderIrInst CmpF(this long OpCode)
+        {
+            switch (OpCode.Read(48, 0xf))
+            {
+                case 0x1: return ShaderIrInst.Fclt;
+                case 0x2: return ShaderIrInst.Fceq;
+                case 0x3: return ShaderIrInst.Fcle;
+                case 0x4: return ShaderIrInst.Fcgt;
+                case 0x5: return ShaderIrInst.Fcne;
+                case 0x6: return ShaderIrInst.Fcge;
+                case 0x7: return ShaderIrInst.Fcnum;
+                case 0x8: return ShaderIrInst.Fcnan;
+                case 0x9: return ShaderIrInst.Fcltu;
+                case 0xa: return ShaderIrInst.Fcequ;
+                case 0xb: return ShaderIrInst.Fcleu;
+                case 0xc: return ShaderIrInst.Fcgtu;
+                case 0xd: return ShaderIrInst.Fcneu;
+                case 0xe: return ShaderIrInst.Fcgeu;
+            }
+
+            throw new ArgumentException(nameof(OpCode));
+        }
+
+        private static ShaderIrInst BLop45(this long OpCode)
+        {
+            switch (OpCode.Read(45, 3))
+            {
+                case 0: return ShaderIrInst.Band;
+                case 1: return ShaderIrInst.Bor;
+                case 2: return ShaderIrInst.Bxor;
+            }
+
+            throw new ArgumentException(nameof(OpCode));
+        }
+
+        private static ShaderIrInst BLop24(this long OpCode)
+        {
+            switch (OpCode.Read(24, 3))
+            {
+                case 0: return ShaderIrInst.Band;
+                case 1: return ShaderIrInst.Bor;
+                case 2: return ShaderIrInst.Bxor;
+            }
+
+            throw new ArgumentException(nameof(OpCode));
+        }
+
+        private static ShaderIrNode PredNode(this long OpCode, ShaderIrNode Node)
+        {
+            ShaderIrOperPred Pred = OpCode.PredNode();
+
+            if (Pred.Index != ShaderIrOperPred.UnusedIndex)
+            {
+                bool Inv = OpCode.Read(19);
+
+                Node = new ShaderIrCond(Pred, Node, Inv);
+            }
+
+            return Node;
+        }
+
+        private static ShaderIrOperPred PredNode(this long OpCode)
+        {
+            int Pred = OpCode.Read(16, 0xf);
+
+            if (Pred != 0xf)
+            {
+                Pred &= 7;
+            }
+
+            return new ShaderIrOperPred(Pred);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeSpecial.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeSpecial.cs
index 4300c32e3..c3e42654a 100644
--- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeSpecial.cs
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeSpecial.cs
@@ -7,20 +7,20 @@ namespace Ryujinx.Graphics.Gal.Shader
         public static void Out_R(ShaderIrBlock Block, long OpCode, long Position)
         {
             //TODO: Those registers have to be used for something
-            ShaderIrOperGpr Gpr0  = GetOperGpr0(OpCode);
-            ShaderIrOperGpr Gpr8  = GetOperGpr8(OpCode);
-            ShaderIrOperGpr Gpr20 = GetOperGpr20(OpCode);
+            ShaderIrOperGpr Gpr0  = OpCode.Gpr0();
+            ShaderIrOperGpr Gpr8  = OpCode.Gpr8();
+            ShaderIrOperGpr Gpr20 = OpCode.Gpr20();
 
-            int Type = (int)((OpCode >> 39) & 3);
+            int Type = OpCode.Read(39, 3);
 
             if ((Type & 1) != 0)
             {
-                Block.AddNode(GetPredNode(new ShaderIrOp(ShaderIrInst.Emit), OpCode));
+                Block.AddNode(OpCode.PredNode(new ShaderIrOp(ShaderIrInst.Emit)));
             }
 
             if ((Type & 2) != 0)
             {
-                Block.AddNode(GetPredNode(new ShaderIrOp(ShaderIrInst.Cut), OpCode));
+                Block.AddNode(OpCode.PredNode(new ShaderIrOp(ShaderIrInst.Cut)));
             }
         }
     }
diff --git a/Ryujinx.HLE/Gpu/Engines/INvGpuEngine.cs b/Ryujinx.Graphics/INvGpuEngine.cs
similarity index 67%
rename from Ryujinx.HLE/Gpu/Engines/INvGpuEngine.cs
rename to Ryujinx.Graphics/INvGpuEngine.cs
index 068878a98..810303b9f 100644
--- a/Ryujinx.HLE/Gpu/Engines/INvGpuEngine.cs
+++ b/Ryujinx.Graphics/INvGpuEngine.cs
@@ -1,6 +1,6 @@
-using Ryujinx.HLE.Gpu.Memory;
+using Ryujinx.Graphics.Memory;
 
-namespace Ryujinx.HLE.Gpu.Engines
+namespace Ryujinx.Graphics
 {
     interface INvGpuEngine
     {
diff --git a/Ryujinx.HLE/Gpu/Engines/MacroInterpreter.cs b/Ryujinx.Graphics/MacroInterpreter.cs
similarity index 99%
rename from Ryujinx.HLE/Gpu/Engines/MacroInterpreter.cs
rename to Ryujinx.Graphics/MacroInterpreter.cs
index 423e20213..20e7895b4 100644
--- a/Ryujinx.HLE/Gpu/Engines/MacroInterpreter.cs
+++ b/Ryujinx.Graphics/MacroInterpreter.cs
@@ -1,8 +1,8 @@
-using Ryujinx.HLE.Gpu.Memory;
+using Ryujinx.Graphics.Memory;
 using System;
 using System.Collections.Generic;
 
-namespace Ryujinx.HLE.Gpu.Engines
+namespace Ryujinx.Graphics
 {
     class MacroInterpreter
     {
diff --git a/Ryujinx.HLE/Gpu/Memory/NvGpuBufferType.cs b/Ryujinx.Graphics/Memory/NvGpuBufferType.cs
similarity index 59%
rename from Ryujinx.HLE/Gpu/Memory/NvGpuBufferType.cs
rename to Ryujinx.Graphics/Memory/NvGpuBufferType.cs
index a6c03f425..6f0d25718 100644
--- a/Ryujinx.HLE/Gpu/Memory/NvGpuBufferType.cs
+++ b/Ryujinx.Graphics/Memory/NvGpuBufferType.cs
@@ -1,6 +1,6 @@
-namespace Ryujinx.HLE.Gpu.Memory
+namespace Ryujinx.Graphics.Memory
 {
-    enum NvGpuBufferType
+    public enum NvGpuBufferType
     {
         Index,
         Vertex,
diff --git a/Ryujinx.HLE/Gpu/Memory/NvGpuPBEntry.cs b/Ryujinx.Graphics/Memory/NvGpuPBEntry.cs
similarity index 88%
rename from Ryujinx.HLE/Gpu/Memory/NvGpuPBEntry.cs
rename to Ryujinx.Graphics/Memory/NvGpuPBEntry.cs
index aba89e3c9..6b93c1699 100644
--- a/Ryujinx.HLE/Gpu/Memory/NvGpuPBEntry.cs
+++ b/Ryujinx.Graphics/Memory/NvGpuPBEntry.cs
@@ -1,9 +1,9 @@
 using System;
 using System.Collections.ObjectModel;
 
-namespace Ryujinx.HLE.Gpu.Memory
+namespace Ryujinx.Graphics.Memory
 {
-    struct NvGpuPBEntry
+    public struct NvGpuPBEntry
     {
         public int Method { get; private set; }
 
diff --git a/Ryujinx.HLE/Gpu/Memory/NvGpuPushBuffer.cs b/Ryujinx.Graphics/Memory/NvGpuPushBuffer.cs
similarity index 97%
rename from Ryujinx.HLE/Gpu/Memory/NvGpuPushBuffer.cs
rename to Ryujinx.Graphics/Memory/NvGpuPushBuffer.cs
index 6121b3e61..0902ebfc9 100644
--- a/Ryujinx.HLE/Gpu/Memory/NvGpuPushBuffer.cs
+++ b/Ryujinx.Graphics/Memory/NvGpuPushBuffer.cs
@@ -1,9 +1,9 @@
 using System.Collections.Generic;
 using System.IO;
 
-namespace Ryujinx.HLE.Gpu.Memory
+namespace Ryujinx.Graphics.Memory
 {
-    static class NvGpuPushBuffer
+    public static class NvGpuPushBuffer
     {
         private enum SubmissionMode
         {
diff --git a/Ryujinx.HLE/Gpu/Memory/NvGpuVmm.cs b/Ryujinx.Graphics/Memory/NvGpuVmm.cs
similarity index 99%
rename from Ryujinx.HLE/Gpu/Memory/NvGpuVmm.cs
rename to Ryujinx.Graphics/Memory/NvGpuVmm.cs
index e7e180646..0082fb275 100644
--- a/Ryujinx.HLE/Gpu/Memory/NvGpuVmm.cs
+++ b/Ryujinx.Graphics/Memory/NvGpuVmm.cs
@@ -2,9 +2,9 @@ using ChocolArm64.Memory;
 using Ryujinx.Graphics.Gal;
 using System;
 
-namespace Ryujinx.HLE.Gpu.Memory
+namespace Ryujinx.Graphics.Memory
 {
-    class NvGpuVmm : IAMemory, IGalMemory
+    public class NvGpuVmm : IAMemory, IGalMemory
     {
         public const long AddrSize = 1L << 40;
 
diff --git a/Ryujinx.HLE/Gpu/Memory/NvGpuVmmCache.cs b/Ryujinx.Graphics/Memory/NvGpuVmmCache.cs
similarity index 97%
rename from Ryujinx.HLE/Gpu/Memory/NvGpuVmmCache.cs
rename to Ryujinx.Graphics/Memory/NvGpuVmmCache.cs
index b3f253b3e..56979e1f0 100644
--- a/Ryujinx.HLE/Gpu/Memory/NvGpuVmmCache.cs
+++ b/Ryujinx.Graphics/Memory/NvGpuVmmCache.cs
@@ -1,12 +1,13 @@
 using ChocolArm64.Memory;
-using Ryujinx.HLE.Memory;
 using System;
 using System.Collections.Generic;
 
-namespace Ryujinx.HLE.Gpu.Memory
+namespace Ryujinx.Graphics.Memory
 {
     class NvGpuVmmCache
     {
+        private const long RamSize = 4L * 1024 * 1024 * 1024;
+
         private const int MaxCpCount     = 10000;
         private const int MaxCpTimeDelta = 60000;
 
@@ -226,7 +227,7 @@ namespace Ryujinx.HLE.Gpu.Memory
         {
             if (Residency == null)
             {
-                Residency = new HashSet<long>[DeviceMemory.RamSize / PageSize];
+                Residency = new HashSet<long>[RamSize / PageSize];
 
                 for (int i = 0; i < Residency.Length; i++)
                 {
diff --git a/Ryujinx.HLE/Gpu/NvGpu.cs b/Ryujinx.Graphics/NvGpu.cs
similarity index 89%
rename from Ryujinx.HLE/Gpu/NvGpu.cs
rename to Ryujinx.Graphics/NvGpu.cs
index 625cb727f..7ba700b89 100644
--- a/Ryujinx.HLE/Gpu/NvGpu.cs
+++ b/Ryujinx.Graphics/NvGpu.cs
@@ -1,9 +1,9 @@
 using Ryujinx.Graphics.Gal;
-using Ryujinx.HLE.Gpu.Engines;
+using Ryujinx.Graphics;
 
-namespace Ryujinx.HLE.Gpu
+namespace Ryujinx.Graphics
 {
-    class NvGpu
+    public class NvGpu
     {
         public IGalRenderer Renderer { get; private set; }
 
diff --git a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine.cs b/Ryujinx.Graphics/NvGpuEngine.cs
similarity index 82%
rename from Ryujinx.HLE/Gpu/Engines/NvGpuEngine.cs
rename to Ryujinx.Graphics/NvGpuEngine.cs
index f9d6342cf..3d5e118a5 100644
--- a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine.cs
+++ b/Ryujinx.Graphics/NvGpuEngine.cs
@@ -1,4 +1,4 @@
-namespace Ryujinx.HLE.Gpu.Engines
+namespace Ryujinx.Graphics
 {
     enum NvGpuEngine
     {
diff --git a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine2d.cs b/Ryujinx.Graphics/NvGpuEngine2d.cs
similarity index 92%
rename from Ryujinx.HLE/Gpu/Engines/NvGpuEngine2d.cs
rename to Ryujinx.Graphics/NvGpuEngine2d.cs
index 7fb5ea8af..f26b00204 100644
--- a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine2d.cs
+++ b/Ryujinx.Graphics/NvGpuEngine2d.cs
@@ -1,12 +1,12 @@
 using Ryujinx.Graphics.Gal;
-using Ryujinx.HLE.Gpu.Memory;
-using Ryujinx.HLE.Gpu.Texture;
+using Ryujinx.Graphics.Memory;
+using Ryujinx.Graphics.Texture;
 using System;
 using System.Collections.Generic;
 
-namespace Ryujinx.HLE.Gpu.Engines
+namespace Ryujinx.Graphics
 {
-    class NvGpuEngine2d : INvGpuEngine
+    public class NvGpuEngine2d : INvGpuEngine
     {
         private enum CopyOperation
         {
@@ -103,7 +103,7 @@ namespace Ryujinx.HLE.Gpu.Engines
                     SrcPitch,
                     SrcBlockHeight, 1,
                     SrcSwizzle,
-                    GalTextureFormat.A8B8G8R8);
+                    GalImageFormat.A8B8G8R8 | GalImageFormat.Unorm);
             }
 
             TextureInfo DstTexture()
@@ -115,7 +115,7 @@ namespace Ryujinx.HLE.Gpu.Engines
                     DstPitch,
                     DstBlockHeight, 1,
                     DstSwizzle,
-                    GalTextureFormat.A8B8G8R8);
+                    GalImageFormat.A8B8G8R8 | GalImageFormat.Unorm);
             }
 
             //TODO: fb -> fb copies, tex -> fb copies, formats other than RGBA8,
@@ -123,7 +123,7 @@ namespace Ryujinx.HLE.Gpu.Engines
             if (IsSrcFb && IsDstFb)
             {
                 //Frame Buffer -> Frame Buffer copy.
-                Gpu.Renderer.FrameBuffer.Copy(
+                Gpu.Renderer.RenderTarget.Copy(
                     SrcKey,
                     DstKey,
                     0,
@@ -138,7 +138,7 @@ namespace Ryujinx.HLE.Gpu.Engines
             if (IsSrcFb)
             {
                 //Frame Buffer -> Texture copy.
-                Gpu.Renderer.FrameBuffer.GetBufferData(SrcKey, (byte[] Buffer) =>
+                Gpu.Renderer.RenderTarget.GetBufferData(SrcKey, (byte[] Buffer) =>
                 {
                     TextureInfo Src = SrcTexture();
                     TextureInfo Dst = DstTexture();
@@ -156,7 +156,7 @@ namespace Ryujinx.HLE.Gpu.Engines
             {
                 byte[] Buffer = TextureReader.Read(Vmm, SrcTexture());
 
-                Gpu.Renderer.FrameBuffer.SetBufferData(
+                Gpu.Renderer.RenderTarget.SetBufferData(
                     DstKey,
                     DstWidth,
                     DstHeight,
diff --git a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine2dReg.cs b/Ryujinx.Graphics/NvGpuEngine2dReg.cs
similarity index 95%
rename from Ryujinx.HLE/Gpu/Engines/NvGpuEngine2dReg.cs
rename to Ryujinx.Graphics/NvGpuEngine2dReg.cs
index 29d66d463..00f6f578d 100644
--- a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine2dReg.cs
+++ b/Ryujinx.Graphics/NvGpuEngine2dReg.cs
@@ -1,4 +1,4 @@
-namespace Ryujinx.HLE.Gpu.Engines
+namespace Ryujinx.Graphics
 {
     enum NvGpuEngine2dReg
     {
diff --git a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs b/Ryujinx.Graphics/NvGpuEngine3d.cs
similarity index 96%
rename from Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs
rename to Ryujinx.Graphics/NvGpuEngine3d.cs
index 0d2f3befd..624eddae0 100644
--- a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs
+++ b/Ryujinx.Graphics/NvGpuEngine3d.cs
@@ -1,12 +1,12 @@
 using Ryujinx.Graphics.Gal;
-using Ryujinx.HLE.Gpu.Memory;
-using Ryujinx.HLE.Gpu.Texture;
+using Ryujinx.Graphics.Memory;
+using Ryujinx.Graphics.Texture;
 using System;
 using System.Collections.Generic;
 
-namespace Ryujinx.HLE.Gpu.Engines
+namespace Ryujinx.Graphics
 {
-    class NvGpuEngine3d : INvGpuEngine
+    public class NvGpuEngine3d : INvGpuEngine
     {
         public int[] Registers { get; private set; }
 
@@ -158,6 +158,7 @@ namespace Ryujinx.HLE.Gpu.Engines
             int Stencil = ReadRegister(NvGpuEngine3dReg.ClearStencil);
 
             SetFrameBuffer(Vmm, FbIndex);
+
             SetZeta(Vmm);
 
             Gpu.Renderer.Rasterizer.ClearBuffers(
@@ -176,7 +177,7 @@ namespace Ryujinx.HLE.Gpu.Engines
 
             if (VA == 0 || Format == 0)
             {
-                Gpu.Renderer.FrameBuffer.UnbindColor(FbIndex);
+                Gpu.Renderer.RenderTarget.UnbindColor(FbIndex);
 
                 return;
             }
@@ -200,16 +201,17 @@ namespace Ryujinx.HLE.Gpu.Engines
             int VpW = (int)(TX + MathF.Abs(SX)) - VpX;
             int VpH = (int)(TY + MathF.Abs(SY)) - VpY;
 
-            GalImageFormat ImageFormat = ImageFormatConverter.ConvertFrameBuffer((GalFrameBufferFormat)Format);
+            GalImageFormat ImageFormat = ImageUtils.ConvertSurface((GalSurfaceFormat)Format);
 
             GalImage Image = new GalImage(Width, Height, ImageFormat);
 
-            long Size = TextureHelper.GetTextureSize(Image);
+            long Size = ImageUtils.GetSize(Image);
 
             Gpu.Renderer.Texture.CreateFb(Key, Size, Image);
-            Gpu.Renderer.FrameBuffer.BindColor(Key, FbIndex);
 
-            Gpu.Renderer.FrameBuffer.SetViewport(VpX, VpY, VpW, VpH);
+            Gpu.Renderer.RenderTarget.BindColor(Key, FbIndex);
+
+            Gpu.Renderer.RenderTarget.SetViewport(VpX, VpY, VpW, VpH);
         }
 
         private void SetZeta(NvGpuVmm Vmm)
@@ -222,7 +224,7 @@ namespace Ryujinx.HLE.Gpu.Engines
 
             if (ZA == 0 || Format == 0 || !ZetaEnable)
             {
-                Gpu.Renderer.FrameBuffer.UnbindZeta();
+                Gpu.Renderer.RenderTarget.UnbindZeta();
 
                 return;
             }
@@ -232,14 +234,15 @@ namespace Ryujinx.HLE.Gpu.Engines
             int Width  = ReadRegister(NvGpuEngine3dReg.ZetaHoriz);
             int Height = ReadRegister(NvGpuEngine3dReg.ZetaVert);
 
-            GalImageFormat ImageFormat = ImageFormatConverter.ConvertZeta((GalZetaFormat)Format);
+            GalImageFormat ImageFormat = ImageUtils.ConvertZeta((GalZetaFormat)Format);
 
             GalImage Image = new GalImage(Width, Height, ImageFormat);
 
-            long Size = TextureHelper.GetTextureSize(Image);
+            long Size = ImageUtils.GetSize(Image);
 
             Gpu.Renderer.Texture.CreateFb(Key, Size, Image);
-            Gpu.Renderer.FrameBuffer.BindZeta(Key);
+
+            Gpu.Renderer.RenderTarget.BindZeta(Key);
         }
 
         private long[] UploadShaders(NvGpuVmm Vmm)
@@ -441,11 +444,11 @@ namespace Ryujinx.HLE.Gpu.Engines
                     Map[i] = (int)((Control >> Shift) & 7);
                 }
 
-                Gpu.Renderer.FrameBuffer.SetMap(Map);
+                Gpu.Renderer.RenderTarget.SetMap(Map);
             }
             else
             {
-                Gpu.Renderer.FrameBuffer.SetMap(null);
+                Gpu.Renderer.RenderTarget.SetMap(null);
             }
         }
 
@@ -519,13 +522,13 @@ namespace Ryujinx.HLE.Gpu.Engines
                 //we shouldn't read anything from memory and bind
                 //the frame buffer texture instead, since we're not
                 //really writing anything to memory.
-                Gpu.Renderer.FrameBuffer.BindTexture(Key, TexIndex);
+                Gpu.Renderer.RenderTarget.BindTexture(Key, TexIndex);
             }
             else
             {
                 GalImage NewImage = TextureFactory.MakeTexture(Vmm, TicPosition);
 
-                long Size = (uint)TextureHelper.GetTextureSize(NewImage);
+                long Size = (uint)ImageUtils.GetSize(NewImage);
 
                 bool HasCachedTexture = false;
 
diff --git a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3dReg.cs b/Ryujinx.Graphics/NvGpuEngine3dReg.cs
similarity index 99%
rename from Ryujinx.HLE/Gpu/Engines/NvGpuEngine3dReg.cs
rename to Ryujinx.Graphics/NvGpuEngine3dReg.cs
index ace324e91..f96e71150 100644
--- a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3dReg.cs
+++ b/Ryujinx.Graphics/NvGpuEngine3dReg.cs
@@ -1,4 +1,4 @@
-namespace Ryujinx.HLE.Gpu.Engines
+namespace Ryujinx.Graphics
 {
     enum NvGpuEngine3dReg
     {
diff --git a/Ryujinx.HLE/Gpu/Engines/NvGpuEngineDma.cs b/Ryujinx.Graphics/NvGpuEngineDma.cs
similarity index 96%
rename from Ryujinx.HLE/Gpu/Engines/NvGpuEngineDma.cs
rename to Ryujinx.Graphics/NvGpuEngineDma.cs
index 7e355e8de..04be742f8 100644
--- a/Ryujinx.HLE/Gpu/Engines/NvGpuEngineDma.cs
+++ b/Ryujinx.Graphics/NvGpuEngineDma.cs
@@ -1,10 +1,10 @@
-using Ryujinx.HLE.Gpu.Memory;
-using Ryujinx.HLE.Gpu.Texture;
+using Ryujinx.Graphics.Memory;
+using Ryujinx.Graphics.Texture;
 using System.Collections.Generic;
 
-namespace Ryujinx.HLE.Gpu.Engines
+namespace Ryujinx.Graphics
 {
-    class NvGpuEngineDma : INvGpuEngine
+    public class NvGpuEngineDma : INvGpuEngine
     {
         public int[] Registers { get; private set; }
 
diff --git a/Ryujinx.HLE/Gpu/Engines/NvGpuEngineDmaReg.cs b/Ryujinx.Graphics/NvGpuEngineDmaReg.cs
similarity index 93%
rename from Ryujinx.HLE/Gpu/Engines/NvGpuEngineDmaReg.cs
rename to Ryujinx.Graphics/NvGpuEngineDmaReg.cs
index 835a822d1..b0fa1fbf3 100644
--- a/Ryujinx.HLE/Gpu/Engines/NvGpuEngineDmaReg.cs
+++ b/Ryujinx.Graphics/NvGpuEngineDmaReg.cs
@@ -1,4 +1,4 @@
-namespace Ryujinx.HLE.Gpu.Engines
+namespace Ryujinx.Graphics
 {
     enum NvGpuEngineDmaReg
     {
diff --git a/Ryujinx.HLE/Gpu/Engines/NvGpuFifo.cs b/Ryujinx.Graphics/NvGpuFifo.cs
similarity index 98%
rename from Ryujinx.HLE/Gpu/Engines/NvGpuFifo.cs
rename to Ryujinx.Graphics/NvGpuFifo.cs
index 0e6266548..3b79a055e 100644
--- a/Ryujinx.HLE/Gpu/Engines/NvGpuFifo.cs
+++ b/Ryujinx.Graphics/NvGpuFifo.cs
@@ -1,10 +1,10 @@
-using Ryujinx.HLE.Gpu.Memory;
+using Ryujinx.Graphics.Memory;
 using System.Collections.Concurrent;
 using System.Threading;
 
-namespace Ryujinx.HLE.Gpu.Engines
+namespace Ryujinx.Graphics
 {
-    class NvGpuFifo
+    public class NvGpuFifo
     {
         private const int MacrosCount    = 0x80;
         private const int MacroIndexMask = MacrosCount - 1;
diff --git a/Ryujinx.HLE/Gpu/Engines/NvGpuFifoMeth.cs b/Ryujinx.Graphics/NvGpuFifoMeth.cs
similarity index 86%
rename from Ryujinx.HLE/Gpu/Engines/NvGpuFifoMeth.cs
rename to Ryujinx.Graphics/NvGpuFifoMeth.cs
index ffd179f26..c5cb6e942 100644
--- a/Ryujinx.HLE/Gpu/Engines/NvGpuFifoMeth.cs
+++ b/Ryujinx.Graphics/NvGpuFifoMeth.cs
@@ -1,4 +1,4 @@
-namespace Ryujinx.HLE.Gpu.Engines
+namespace Ryujinx.Graphics
 {
     enum NvGpuFifoMeth
     {
diff --git a/Ryujinx.HLE/Gpu/Engines/NvGpuMethod.cs b/Ryujinx.Graphics/NvGpuMethod.cs
similarity index 51%
rename from Ryujinx.HLE/Gpu/Engines/NvGpuMethod.cs
rename to Ryujinx.Graphics/NvGpuMethod.cs
index 04c92f2a9..5babf2c32 100644
--- a/Ryujinx.HLE/Gpu/Engines/NvGpuMethod.cs
+++ b/Ryujinx.Graphics/NvGpuMethod.cs
@@ -1,6 +1,6 @@
-using Ryujinx.HLE.Gpu.Memory;
+using Ryujinx.Graphics.Memory;
 
-namespace Ryujinx.HLE.Gpu.Engines
+namespace Ryujinx.Graphics
 {
     delegate void NvGpuMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry);
 }
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Texture/ASTCDecoder.cs b/Ryujinx.Graphics/Texture/ASTCDecoder.cs
similarity index 97%
rename from Ryujinx.Graphics/Gal/Texture/ASTCDecoder.cs
rename to Ryujinx.Graphics/Texture/ASTCDecoder.cs
index da1b9ef41..1efa02552 100644
--- a/Ryujinx.Graphics/Gal/Texture/ASTCDecoder.cs
+++ b/Ryujinx.Graphics/Texture/ASTCDecoder.cs
@@ -1,1384 +1,1384 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.IO;
-
-namespace Ryujinx.Graphics.Gal.Texture
-{
-    public class ASTCDecoderException : Exception
-    {
-        public ASTCDecoderException(string ExMsg) : base(ExMsg) { }
-    }
-
-    //https://github.com/GammaUNC/FasTC/blob/master/ASTCEncoder/src/Decompressor.cpp
-    public static class ASTCDecoder
-    {
-        struct TexelWeightParams
-        {
-            public int  Width;
-            public int  Height;
-            public bool DualPlane;
-            public int  MaxWeight;
-            public bool Error;
-            public bool VoidExtentLDR;
-            public bool VoidExtentHDR;
-
-            public int GetPackedBitSize()
-            {
-                // How many indices do we have?
-                int Indices = Height * Width;
-
-                if (DualPlane)
-                {
-                    Indices *= 2;
-                }
-
-                IntegerEncoded IntEncoded = IntegerEncoded.CreateEncoding(MaxWeight);
-
-                return IntEncoded.GetBitLength(Indices);
-            }
-
-            public int GetNumWeightValues()
-            {
-                int Ret = Width * Height;
-
-                if (DualPlane)
-                {
-                    Ret *= 2;
-                }
-
-                return Ret;
-            }
-        }
-
-        public static byte[] DecodeToRGBA8888(
-            byte[] InputBuffer, 
-            int    BlockX, 
-            int    BlockY, 
-            int    BlockZ, 
-            int    X, 
-            int    Y, 
-            int    Z)
-        {
-            using (MemoryStream InputStream = new MemoryStream(InputBuffer))
-            {
-                BinaryReader BinReader = new BinaryReader(InputStream);
-
-                if (BlockX > 12 || BlockY > 12)
-                {
-                    throw new ASTCDecoderException("Block size unsupported!");
-                }
-
-                if (BlockZ != 1 || Z != 1)
-                {
-                    throw new ASTCDecoderException("3D compressed textures unsupported!");
-                }
-
-                using (MemoryStream OutputStream = new MemoryStream())
-                {
-                    int BlockIndex = 0;
-
-                    for (int j = 0; j < Y; j += BlockY)
-                    {
-                        for (int i = 0; i < X; i += BlockX)
-                        {
-                            int[] DecompressedData = new int[144];
-
-                            DecompressBlock(BinReader.ReadBytes(0x10), DecompressedData, BlockX, BlockY);
-
-                            int DecompressedWidth = Math.Min(BlockX, X - i);
-                            int DecompressedHeight = Math.Min(BlockY, Y - j);
-                            int BaseOffsets = (j * X + i) * 4;
-
-                            for (int jj = 0; jj < DecompressedHeight; jj++)
-                            {
-                                OutputStream.Seek(BaseOffsets + jj * X * 4, SeekOrigin.Begin);
-
-                                byte[] OutputBuffer = new byte[DecompressedData.Length * sizeof(int)];
-                                Buffer.BlockCopy(DecompressedData, 0, OutputBuffer, 0, OutputBuffer.Length);
-
-                                OutputStream.Write(OutputBuffer, jj * BlockX * 4, DecompressedWidth * 4);
-                            }
-
-                            BlockIndex++;
-                        }
-                    }
-
-                    return OutputStream.ToArray();
-                }
-            }
-        }
-
-        public static bool DecompressBlock(
-            byte[] InputBuffer, 
-            int[]  OutputBuffer, 
-            int    BlockWidth, 
-            int    BlockHeight)
-        {
-            BitArrayStream    BitStream   = new BitArrayStream(new BitArray(InputBuffer));
-            TexelWeightParams TexelParams = DecodeBlockInfo(BitStream);
-
-            if (TexelParams.Error)
-            {
-                throw new ASTCDecoderException("Invalid block mode");
-            }
-
-            if (TexelParams.VoidExtentLDR)
-            {
-                FillVoidExtentLDR(BitStream, OutputBuffer, BlockWidth, BlockHeight);
-
-                return true;
-            }
-
-            if (TexelParams.VoidExtentHDR)
-            {
-                throw new ASTCDecoderException("HDR void extent blocks are unsupported!");
-            }
-
-            if (TexelParams.Width > BlockWidth)
-            {
-                throw new ASTCDecoderException("Texel weight grid width should be smaller than block width");
-            }
-
-            if (TexelParams.Height > BlockHeight)
-            {
-                throw new ASTCDecoderException("Texel weight grid height should be smaller than block height");
-            }
-
-            // Read num partitions
-            int NumberPartitions = BitStream.ReadBits(2) + 1;
-            Debug.Assert(NumberPartitions <= 4);
-
-            if (NumberPartitions == 4 && TexelParams.DualPlane)
-            {
-                throw new ASTCDecoderException("Dual plane mode is incompatible with four partition blocks");
-            }
-
-            // Based on the number of partitions, read the color endpoint mode for
-            // each partition.
-
-            // Determine partitions, partition index, and color endpoint modes
-            int    PlaneIndices      = -1;
-            int    PartitionIndex;
-            uint[] ColorEndpointMode = { 0, 0, 0, 0 };
-
-            BitArrayStream ColorEndpointStream = new BitArrayStream(new BitArray(16 * 8));
-
-            // Read extra config data...
-            uint BaseColorEndpointMode = 0;
-
-            if (NumberPartitions == 1)
-            {
-                ColorEndpointMode[0] = (uint)BitStream.ReadBits(4);
-                PartitionIndex       = 0;
-            }
-            else
-            {
-                PartitionIndex        = BitStream.ReadBits(10);
-                BaseColorEndpointMode = (uint)BitStream.ReadBits(6);
-            }
-
-            uint BaseMode = (BaseColorEndpointMode & 3);
-
-            // Remaining bits are color endpoint data...
-            int NumberWeightBits = TexelParams.GetPackedBitSize();
-            int RemainingBits    = 128 - NumberWeightBits - BitStream.Position;
-
-            // Consider extra bits prior to texel data...
-            uint ExtraColorEndpointModeBits = 0;
-
-            if (BaseMode != 0)
-            {
-                switch (NumberPartitions)
-                {
-                    case 2:  ExtraColorEndpointModeBits += 2; break;
-                    case 3:  ExtraColorEndpointModeBits += 5; break;
-                    case 4:  ExtraColorEndpointModeBits += 8; break;
-                    default: Debug.Assert(false); break;
-                }
-            }
-
-            RemainingBits -= (int)ExtraColorEndpointModeBits;
-
-            // Do we have a dual plane situation?
-            int PlaneSelectorBits = 0;
-
-            if (TexelParams.DualPlane)
-            {
-                PlaneSelectorBits = 2;
-            }
-
-            RemainingBits -= PlaneSelectorBits;
-
-            // Read color data...
-            int ColorDataBits = RemainingBits;
-
-            while (RemainingBits > 0)
-            {
-                int NumberBits = Math.Min(RemainingBits, 8);
-                int Bits = BitStream.ReadBits(NumberBits);
-                ColorEndpointStream.WriteBits(Bits, NumberBits);
-                RemainingBits -= 8;
-            }
-
-            // Read the plane selection bits
-            PlaneIndices = BitStream.ReadBits(PlaneSelectorBits);
-
-            // Read the rest of the CEM
-            if (BaseMode != 0)
-            {
-                uint ExtraColorEndpointMode = (uint)BitStream.ReadBits((int)ExtraColorEndpointModeBits);
-                uint TempColorEndpointMode  = (ExtraColorEndpointMode << 6) | BaseColorEndpointMode;
-                TempColorEndpointMode     >>= 2;
-
-                bool[] C = new bool[4];
-
-                for (int i = 0; i < NumberPartitions; i++)
-                {
-                    C[i] = (TempColorEndpointMode & 1) != 0;
-                    TempColorEndpointMode >>= 1;
-                }
-
-                byte[] M = new byte[4];
-
-                for (int i = 0; i < NumberPartitions; i++)
-                {
-                    M[i] = (byte)(TempColorEndpointMode & 3);
-                    TempColorEndpointMode >>= 2;
-                    Debug.Assert(M[i] <= 3);
-                }
-
-                for (int i = 0; i < NumberPartitions; i++)
-                {
-                    ColorEndpointMode[i] = BaseMode;
-                    if (!(C[i])) ColorEndpointMode[i] -= 1;
-                    ColorEndpointMode[i] <<= 2;
-                    ColorEndpointMode[i] |= M[i];
-                }
-            }
-            else if (NumberPartitions > 1)
-            {
-                uint TempColorEndpointMode = BaseColorEndpointMode >> 2;
-
-                for (uint i = 0; i < NumberPartitions; i++)
-                {
-                    ColorEndpointMode[i] = TempColorEndpointMode;
-                }
-            }
-
-            // Make sure everything up till here is sane.
-            for (int i = 0; i < NumberPartitions; i++)
-            {
-                Debug.Assert(ColorEndpointMode[i] < 16);
-            }
-            Debug.Assert(BitStream.Position + TexelParams.GetPackedBitSize() == 128);
-
-            // Decode both color data and texel weight data
-            int[] ColorValues = new int[32]; // Four values * two endpoints * four maximum partitions
-            DecodeColorValues(ColorValues, ColorEndpointStream.ToByteArray(), ColorEndpointMode, NumberPartitions, ColorDataBits);
-
-            ASTCPixel[][] EndPoints = new ASTCPixel[4][];
-            EndPoints[0] = new ASTCPixel[2];
-            EndPoints[1] = new ASTCPixel[2];
-            EndPoints[2] = new ASTCPixel[2];
-            EndPoints[3] = new ASTCPixel[2];
-
-            int ColorValuesPosition = 0;
-
-            for (int i = 0; i < NumberPartitions; i++)
-            {
-                ComputeEndpoints(EndPoints[i], ColorValues, ColorEndpointMode[i], ref ColorValuesPosition);
-            }
-
-            // Read the texel weight data.
-            byte[] TexelWeightData = (byte[])InputBuffer.Clone();
-
-            // Reverse everything
-            for (int i = 0; i < 8; i++)
-            {
-                byte a = ReverseByte(TexelWeightData[i]);
-                byte b = ReverseByte(TexelWeightData[15 - i]);
-
-                TexelWeightData[i]      = b;
-                TexelWeightData[15 - i] = a;
-            }
-
-            // Make sure that higher non-texel bits are set to zero
-            int ClearByteStart                   = (TexelParams.GetPackedBitSize() >> 3) + 1;
-            TexelWeightData[ClearByteStart - 1] &= (byte)((1 << (TexelParams.GetPackedBitSize() % 8)) - 1);
-
-            int cLen = 16 - ClearByteStart;
-            for (int i = ClearByteStart; i < ClearByteStart + cLen; i++) TexelWeightData[i] = 0;
-
-            List<IntegerEncoded> TexelWeightValues = new List<IntegerEncoded>();
-            BitArrayStream WeightBitStream         = new BitArrayStream(new BitArray(TexelWeightData));
-
-            IntegerEncoded.DecodeIntegerSequence(TexelWeightValues, WeightBitStream, TexelParams.MaxWeight, TexelParams.GetNumWeightValues());
-            
-            // Blocks can be at most 12x12, so we can have as many as 144 weights
-            int[][] Weights = new int[2][];
-            Weights[0] = new int[144];
-            Weights[1] = new int[144];
-
-            UnquantizeTexelWeights(Weights, TexelWeightValues, TexelParams, BlockWidth, BlockHeight);
-
-            // Now that we have endpoints and weights, we can interpolate and generate
-            // the proper decoding...
-            for (int j = 0; j < BlockHeight; j++)
-            {
-                for (int i = 0; i < BlockWidth; i++)
-                {
-                    int Partition = Select2DPartition(PartitionIndex, i, j, NumberPartitions, ((BlockHeight * BlockWidth) < 32));
-                    Debug.Assert(Partition < NumberPartitions);
-
-                    ASTCPixel Pixel = new ASTCPixel(0, 0, 0, 0);
-                    for (int Component = 0; Component < 4; Component++)
-                    {
-                        int Component0 = EndPoints[Partition][0].GetComponent(Component);
-                        Component0     = BitArrayStream.Replicate(Component0, 8, 16);
-                        int Component1 = EndPoints[Partition][1].GetComponent(Component);
-                        Component1     = BitArrayStream.Replicate(Component1, 8, 16);
-
-                        int Plane = 0;
-
-                        if (TexelParams.DualPlane && (((PlaneIndices + 1) & 3) == Component))
-                        {
-                            Plane = 1;
-                        }
-
-                        int Weight = Weights[Plane][j * BlockWidth + i];
-                        int FinalComponent = (Component0 * (64 - Weight) + Component1 * Weight + 32) / 64;
-
-                        if (FinalComponent == 65535)
-                        {
-                            Pixel.SetComponent(Component, 255);
-                        }
-                        else
-                        {
-                            double FinalComponentFloat = FinalComponent;
-                            Pixel.SetComponent(Component, (int)(255.0 * (FinalComponentFloat / 65536.0) + 0.5));
-                        }
-                    }
-
-                    OutputBuffer[j * BlockWidth + i] = Pixel.Pack();
-                }
-            }
-
-            return true;
-        }
-
-        private static int Select2DPartition(int Seed, int X, int Y, int PartitionCount, bool IsSmallBlock)
-        {
-            return SelectPartition(Seed, X, Y, 0, PartitionCount, IsSmallBlock);
-        }
-
-        private static int SelectPartition(int Seed, int X, int Y, int Z, int PartitionCount, bool IsSmallBlock)
-        {
-            if (PartitionCount == 1)
-            {
-                return 0;
-            }
-
-            if (IsSmallBlock)
-            {
-                X <<= 1;
-                Y <<= 1;
-                Z <<= 1;
-            }
-
-            Seed += (PartitionCount - 1) * 1024;
-
-            int  RightNum = Hash52((uint)Seed);
-            byte Seed01   = (byte)(RightNum & 0xF);
-            byte Seed02   = (byte)((RightNum >> 4) & 0xF);
-            byte Seed03   = (byte)((RightNum >> 8) & 0xF);
-            byte Seed04   = (byte)((RightNum >> 12) & 0xF);
-            byte Seed05   = (byte)((RightNum >> 16) & 0xF);
-            byte Seed06   = (byte)((RightNum >> 20) & 0xF);
-            byte Seed07   = (byte)((RightNum >> 24) & 0xF);
-            byte Seed08   = (byte)((RightNum >> 28) & 0xF);
-            byte Seed09   = (byte)((RightNum >> 18) & 0xF);
-            byte Seed10   = (byte)((RightNum >> 22) & 0xF);
-            byte Seed11   = (byte)((RightNum >> 26) & 0xF);
-            byte Seed12   = (byte)(((RightNum >> 30) | (RightNum << 2)) & 0xF);
-
-            Seed01 *= Seed01; Seed02 *= Seed02;
-            Seed03 *= Seed03; Seed04 *= Seed04;
-            Seed05 *= Seed05; Seed06 *= Seed06;
-            Seed07 *= Seed07; Seed08 *= Seed08;
-            Seed09 *= Seed09; Seed10 *= Seed10;
-            Seed11 *= Seed11; Seed12 *= Seed12;
-
-            int SeedHash1, SeedHash2, SeedHash3;
-
-            if ((Seed & 1) != 0)
-            {
-                SeedHash1 = (Seed & 2) != 0 ? 4 : 5;
-                SeedHash2 = (PartitionCount == 3) ? 6 : 5;
-            }
-            else
-            {
-                SeedHash1 = (PartitionCount == 3) ? 6 : 5;
-                SeedHash2 = (Seed & 2) != 0 ? 4 : 5;
-            }
-
-            SeedHash3 = (Seed & 0x10) != 0 ? SeedHash1 : SeedHash2;
-
-            Seed01 >>= SeedHash1; Seed02 >>= SeedHash2; Seed03 >>= SeedHash1; Seed04 >>= SeedHash2;
-            Seed05 >>= SeedHash1; Seed06 >>= SeedHash2; Seed07 >>= SeedHash1; Seed08 >>= SeedHash2;
-            Seed09 >>= SeedHash3; Seed10 >>= SeedHash3; Seed11 >>= SeedHash3; Seed12 >>= SeedHash3;
-
-            int a = Seed01 * X + Seed02 * Y + Seed11 * Z + (RightNum >> 14);
-            int b = Seed03 * X + Seed04 * Y + Seed12 * Z + (RightNum >> 10);
-            int c = Seed05 * X + Seed06 * Y + Seed09 * Z + (RightNum >> 6);
-            int d = Seed07 * X + Seed08 * Y + Seed10 * Z + (RightNum >> 2);
-
-            a &= 0x3F; b &= 0x3F; c &= 0x3F; d &= 0x3F;
-
-            if (PartitionCount < 4) d = 0;
-            if (PartitionCount < 3) c = 0;
-
-            if (a >= b && a >= c && a >= d) return 0;
-            else if (b >= c && b >= d) return 1;
-            else if (c >= d) return 2;
-            return 3;
-        }
-
-        static int Hash52(uint Val)
-        {
-            Val ^= Val >> 15; Val -= Val << 17; Val += Val << 7; Val += Val << 4;
-            Val ^= Val >> 5;  Val += Val << 16; Val ^= Val >> 7; Val ^= Val >> 3;
-            Val ^= Val << 6;  Val ^= Val >> 17;
-
-            return (int)Val;
-        }
-
-        static void UnquantizeTexelWeights(
-            int[][]              OutputBuffer, 
-            List<IntegerEncoded> Weights, 
-            TexelWeightParams    TexelParams, 
-            int                  BlockWidth, 
-            int                  BlockHeight)
-        {
-            int WeightIndices   = 0;
-            int[][] Unquantized = new int[2][];
-            Unquantized[0]      = new int[144];
-            Unquantized[1]      = new int[144];
-
-            for (int i = 0; i < Weights.Count; i++)
-            {
-                Unquantized[0][WeightIndices] = UnquantizeTexelWeight(Weights[i]);
-
-                if (TexelParams.DualPlane)
-                {
-                    i++;
-                    Unquantized[1][WeightIndices] = UnquantizeTexelWeight(Weights[i]);
-
-                    if (i == Weights.Count)
-                    {
-                        break;
-                    }
-                }
-
-                if (++WeightIndices >= (TexelParams.Width * TexelParams.Height)) break;
-            }
-
-            // Do infill if necessary (Section C.2.18) ...
-            int Ds = (1024 + (BlockWidth / 2)) / (BlockWidth - 1);
-            int Dt = (1024 + (BlockHeight / 2)) / (BlockHeight - 1);
-
-            int PlaneScale = TexelParams.DualPlane ? 2 : 1;
-
-            for (int Plane = 0; Plane < PlaneScale; Plane++)
-            {
-                for (int t = 0; t < BlockHeight; t++)
-                {
-                    for (int s = 0; s < BlockWidth; s++)
-                    {
-                        int cs = Ds * s;
-                        int ct = Dt * t;
-
-                        int gs = (cs * (TexelParams.Width - 1) + 32) >> 6;
-                        int gt = (ct * (TexelParams.Height - 1) + 32) >> 6;
-
-                        int js = gs >> 4;
-                        int fs = gs & 0xF;
-
-                        int jt = gt >> 4;
-                        int ft = gt & 0x0F;
-
-                        int w11 = (fs * ft + 8) >> 4;
-                        int w10 = ft - w11;
-                        int w01 = fs - w11;
-                        int w00 = 16 - fs - ft + w11;
-
-                        int v0 = js + jt * TexelParams.Width;
-
-                        int p00 = 0;
-                        int p01 = 0;
-                        int p10 = 0;
-                        int p11 = 0;
-
-                        if (v0 < (TexelParams.Width * TexelParams.Height))
-                        {
-                            p00 = Unquantized[Plane][v0];
-                        }
-
-                        if (v0 + 1 < (TexelParams.Width * TexelParams.Height))
-                        {
-                            p01 = Unquantized[Plane][v0 + 1];
-                        }
-                        
-                        if (v0 + TexelParams.Width < (TexelParams.Width * TexelParams.Height))
-                        {
-                            p10 = Unquantized[Plane][v0 + TexelParams.Width];
-                        }
-                        
-                        if (v0 + TexelParams.Width + 1 < (TexelParams.Width * TexelParams.Height))
-                        {
-                            p11 = Unquantized[Plane][v0 + TexelParams.Width + 1];
-                        }
-
-                        OutputBuffer[Plane][t * BlockWidth + s] = (p00 * w00 + p01 * w01 + p10 * w10 + p11 * w11 + 8) >> 4;
-                    }
-                }
-            }
-        }
-
-        static int UnquantizeTexelWeight(IntegerEncoded IntEncoded)
-        {
-            int BitValue  = IntEncoded.BitValue;
-            int BitLength = IntEncoded.NumberBits;
-
-            int A = BitArrayStream.Replicate(BitValue & 1, 1, 7);
-            int B = 0, C = 0, D = 0;
-
-            int Result = 0;
-
-            switch (IntEncoded.GetEncoding())
-            {
-                case IntegerEncoded.EIntegerEncoding.JustBits:
-                    Result = BitArrayStream.Replicate(BitValue, BitLength, 6);
-                    break;
-
-                case IntegerEncoded.EIntegerEncoding.Trit:
-                {
-                    D = IntEncoded.TritValue;
-                    Debug.Assert(D < 3);
-
-                    switch (BitLength)
-                    {
-                        case 0:
-                        {
-                            int[] Results = { 0, 32, 63 };
-                            Result = Results[D];
-
-                            break;
-                        }
-
-                        case 1:
-                        {
-                            C = 50;
-                            break;
-                        }
-
-                        case 2:
-                        {
-                            C = 23;
-                            int b = (BitValue >> 1) & 1;
-                            B = (b << 6) | (b << 2) | b;
-
-                            break;
-                        }
-
-                        case 3:
-                        {
-                            C = 11;
-                            int cb = (BitValue >> 1) & 3;
-                            B = (cb << 5) | cb;
-
-                            break;
-                        }
-
-                        default:
-                            throw new ASTCDecoderException("Invalid trit encoding for texel weight");
-                    }
-
-                    break;
-                }    
-
-                case IntegerEncoded.EIntegerEncoding.Quint:
-                {
-                    D = IntEncoded.QuintValue;
-                    Debug.Assert(D < 5);
-
-                    switch (BitLength)
-                    {
-                        case 0:
-                        {
-                            int[] Results = { 0, 16, 32, 47, 63 };
-                            Result = Results[D];
-
-                            break;
-                        }
-
-                        case 1:
-                        {
-                            C = 28;
-
-                            break;
-                        }
-
-                        case 2:
-                        {
-                            C = 13;
-                            int b = (BitValue >> 1) & 1;
-                            B = (b << 6) | (b << 1);
-
-                            break;
-                        }
-                                
-                        default:
-                            throw new ASTCDecoderException("Invalid quint encoding for texel weight");
-                    }
-
-                    break;
-                }    
-            }
-
-            if (IntEncoded.GetEncoding() != IntegerEncoded.EIntegerEncoding.JustBits && BitLength > 0)
-            {
-                // Decode the value...
-                Result  = D * C + B;
-                Result ^= A;
-                Result  = (A & 0x20) | (Result >> 2);
-            }
-
-            Debug.Assert(Result < 64);
-
-            // Change from [0,63] to [0,64]
-            if (Result > 32)
-            {
-                Result += 1;
-            }
-
-            return Result;
-        }
-
-        static byte ReverseByte(byte b)
-        {
-            // Taken from http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith64Bits
-            return (byte)((((b) * 0x80200802L) & 0x0884422110L) * 0x0101010101L >> 32);
-        }
-
-        static uint[] ReadUintColorValues(int Number, int[] ColorValues, ref int ColorValuesPosition)
-        {
-            uint[] Ret = new uint[Number];
-
-            for (int i = 0; i < Number; i++)
-            {
-                Ret[i] = (uint)ColorValues[ColorValuesPosition++];
-            }
-
-            return Ret;
-        }
-
-        static int[] ReadIntColorValues(int Number, int[] ColorValues, ref int ColorValuesPosition)
-        {
-            int[] Ret = new int[Number];
-
-            for (int i = 0; i < Number; i++)
-            {
-                Ret[i] = ColorValues[ColorValuesPosition++];
-            }
-
-            return Ret;
-        }
-
-        static void ComputeEndpoints(
-            ASTCPixel[] EndPoints, 
-            int[]       ColorValues, 
-            uint        ColorEndpointMode, 
-            ref int     ColorValuesPosition)
-        {
-            switch (ColorEndpointMode)
-            {
-                case 0:
-                {
-                    uint[] Val = ReadUintColorValues(2, ColorValues, ref ColorValuesPosition);
-
-                    EndPoints[0] = new ASTCPixel(0xFF, (short)Val[0], (short)Val[0], (short)Val[0]);
-                    EndPoints[1] = new ASTCPixel(0xFF, (short)Val[1], (short)Val[1], (short)Val[1]);
-
-                    break;
-                }
-                    
-
-                case 1:
-                {
-                    uint[] Val = ReadUintColorValues(2, ColorValues, ref ColorValuesPosition);
-                    int L0     = (int)((Val[0] >> 2) | (Val[1] & 0xC0));
-                    int L1     = (int)Math.Max(L0 + (Val[1] & 0x3F), 0xFFU);
-
-                    EndPoints[0] = new ASTCPixel(0xFF, (short)L0, (short)L0, (short)L0);
-                    EndPoints[1] = new ASTCPixel(0xFF, (short)L1, (short)L1, (short)L1);
-
-                    break;
-                }
-
-                case 4:
-                {
-                    uint[] Val = ReadUintColorValues(4, ColorValues, ref ColorValuesPosition);
-
-                    EndPoints[0] = new ASTCPixel((short)Val[2], (short)Val[0], (short)Val[0], (short)Val[0]);
-                    EndPoints[1] = new ASTCPixel((short)Val[3], (short)Val[1], (short)Val[1], (short)Val[1]);
-
-                    break;
-                }
-
-                case 5:
-                {
-                    int[] Val = ReadIntColorValues(4, ColorValues, ref ColorValuesPosition);
-
-                    BitArrayStream.BitTransferSigned(ref Val[1], ref Val[0]);
-                    BitArrayStream.BitTransferSigned(ref Val[3], ref Val[2]);
-
-                    EndPoints[0] = new ASTCPixel((short)Val[2], (short)Val[0], (short)Val[0], (short)Val[0]);
-                    EndPoints[1] = new ASTCPixel((short)(Val[2] + Val[3]), (short)(Val[0] + Val[1]), (short)(Val[0] + Val[1]), (short)(Val[0] + Val[1]));
-
-                    EndPoints[0].ClampByte();
-                    EndPoints[1].ClampByte();
-
-                    break;
-                }
-
-                case 6:
-                {
-                    uint[] Val = ReadUintColorValues(4, ColorValues, ref ColorValuesPosition);
-
-                    EndPoints[0] = new ASTCPixel(0xFF, (short)(Val[0] * Val[3] >> 8), (short)(Val[1] * Val[3] >> 8), (short)(Val[2] * Val[3] >> 8));
-                    EndPoints[1] = new ASTCPixel(0xFF, (short)Val[0], (short)Val[1], (short)Val[2]);
-
-                    break;
-                }
-
-                case 8:
-                {
-                    uint[] Val = ReadUintColorValues(6, ColorValues, ref ColorValuesPosition);
-
-                    if (Val[1] + Val[3] + Val[5] >= Val[0] + Val[2] + Val[4])
-                    {
-                        EndPoints[0] = new ASTCPixel(0xFF, (short)Val[0], (short)Val[2], (short)Val[4]);
-                        EndPoints[1] = new ASTCPixel(0xFF, (short)Val[1], (short)Val[3], (short)Val[5]);
-                    }
-                    else
-                    {
-                        EndPoints[0] = ASTCPixel.BlueContract(0xFF, (short)Val[1], (short)Val[3], (short)Val[5]);
-                        EndPoints[1] = ASTCPixel.BlueContract(0xFF, (short)Val[0], (short)Val[2], (short)Val[4]);
-                    }
-
-                    break;
-                }
-
-                case 9:
-                {
-                    int[] Val = ReadIntColorValues(6, ColorValues, ref ColorValuesPosition);
-
-                    BitArrayStream.BitTransferSigned(ref Val[1], ref Val[0]);
-                    BitArrayStream.BitTransferSigned(ref Val[3], ref Val[2]);
-                    BitArrayStream.BitTransferSigned(ref Val[5], ref Val[4]);
-
-                    if (Val[1] + Val[3] + Val[5] >= 0)
-                    {
-                        EndPoints[0] = new ASTCPixel(0xFF, (short)Val[0], (short)Val[2], (short)Val[4]);
-                        EndPoints[1] = new ASTCPixel(0xFF, (short)(Val[0] + Val[1]), (short)(Val[2] + Val[3]), (short)(Val[4] + Val[5]));
-                    }
-                    else
-                    {
-                        EndPoints[0] = ASTCPixel.BlueContract(0xFF, Val[0] + Val[1], Val[2] + Val[3], Val[4] + Val[5]);
-                        EndPoints[1] = ASTCPixel.BlueContract(0xFF, Val[0], Val[2], Val[4]);
-                    }
-
-                    EndPoints[0].ClampByte();
-                    EndPoints[1].ClampByte();
-
-                    break;
-                }
-
-                case 10:
-                {
-                    uint[] Val = ReadUintColorValues(6, ColorValues, ref ColorValuesPosition);
-
-                    EndPoints[0] = new ASTCPixel((short)Val[4], (short)(Val[0] * Val[3] >> 8), (short)(Val[1] * Val[3] >> 8), (short)(Val[2] * Val[3] >> 8));
-                    EndPoints[1] = new ASTCPixel((short)Val[5], (short)Val[0], (short)Val[1], (short)Val[2]);
-
-                    break;
-                }
-
-                case 12:
-                {
-                    uint[] Val = ReadUintColorValues(8, ColorValues, ref ColorValuesPosition);
-
-                    if (Val[1] + Val[3] + Val[5] >= Val[0] + Val[2] + Val[4])
-                    {
-                        EndPoints[0] = new ASTCPixel((short)Val[6], (short)Val[0], (short)Val[2], (short)Val[4]);
-                        EndPoints[1] = new ASTCPixel((short)Val[7], (short)Val[1], (short)Val[3], (short)Val[5]);
-                    }
-                    else
-                    {
-                        EndPoints[0] = ASTCPixel.BlueContract((short)Val[7], (short)Val[1], (short)Val[3], (short)Val[5]);
-                        EndPoints[1] = ASTCPixel.BlueContract((short)Val[6], (short)Val[0], (short)Val[2], (short)Val[4]);
-                    }
-
-                    break;
-                }
-
-                case 13:
-                {
-                    int[] Val = ReadIntColorValues(8, ColorValues, ref ColorValuesPosition);
-
-                    BitArrayStream.BitTransferSigned(ref Val[1], ref Val[0]);
-                    BitArrayStream.BitTransferSigned(ref Val[3], ref Val[2]);
-                    BitArrayStream.BitTransferSigned(ref Val[5], ref Val[4]);
-                    BitArrayStream.BitTransferSigned(ref Val[7], ref Val[6]);
-
-                    if (Val[1] + Val[3] + Val[5] >= 0)
-                    {
-                        EndPoints[0] = new ASTCPixel((short)Val[6], (short)Val[0], (short)Val[2], (short)Val[4]);
-                        EndPoints[1] = new ASTCPixel((short)(Val[7] + Val[6]), (short)(Val[0] + Val[1]), (short)(Val[2] + Val[3]), (short)(Val[4] + Val[5]));
-                    }
-                    else
-                    {
-                        EndPoints[0] = ASTCPixel.BlueContract(Val[6] + Val[7], Val[0] + Val[1], Val[2] + Val[3], Val[4] + Val[5]);
-                        EndPoints[1] = ASTCPixel.BlueContract(Val[6], Val[0], Val[2], Val[4]);
-                    }
-
-                    EndPoints[0].ClampByte();
-                    EndPoints[1].ClampByte();
-
-                    break;
-                }
-
-                default:
-                    throw new ASTCDecoderException("Unsupported color endpoint mode (is it HDR?)");
-            }
-        }
-
-        static void DecodeColorValues(
-            int[]  OutputValues, 
-            byte[] InputData, 
-            uint[] Modes, 
-            int    NumberPartitions, 
-            int    NumberBitsForColorData)
-        {
-            // First figure out how many color values we have
-            int NumberValues = 0;
-
-            for (int i = 0; i < NumberPartitions; i++)
-            {
-                NumberValues += (int)((Modes[i] >> 2) + 1) << 1;
-            }
-
-            // Then based on the number of values and the remaining number of bits,
-            // figure out the max value for each of them...
-            int Range = 256;
-
-            while (--Range > 0)
-            {
-                IntegerEncoded IntEncoded = IntegerEncoded.CreateEncoding(Range);
-                int BitLength             = IntEncoded.GetBitLength(NumberValues);
-
-                if (BitLength <= NumberBitsForColorData)
-                {
-                    // Find the smallest possible range that matches the given encoding
-                    while (--Range > 0)
-                    {
-                        IntegerEncoded NewIntEncoded = IntegerEncoded.CreateEncoding(Range);
-                        if (!NewIntEncoded.MatchesEncoding(IntEncoded))
-                        {
-                            break;
-                        }
-                    }
-
-                    // Return to last matching range.
-                    Range++;
-                    break;
-                }
-            }
-
-            // We now have enough to decode our integer sequence.
-            List<IntegerEncoded> IntegerEncodedSequence = new List<IntegerEncoded>();
-            BitArrayStream ColorBitStream               = new BitArrayStream(new BitArray(InputData));
-
-            IntegerEncoded.DecodeIntegerSequence(IntegerEncodedSequence, ColorBitStream, Range, NumberValues);
-
-            // Once we have the decoded values, we need to dequantize them to the 0-255 range
-            // This procedure is outlined in ASTC spec C.2.13
-            int OutputIndices = 0;
-
-            foreach (IntegerEncoded IntEncoded in IntegerEncodedSequence)
-            {
-                int BitLength = IntEncoded.NumberBits;
-                int BitValue  = IntEncoded.BitValue;
-
-                Debug.Assert(BitLength >= 1);
-
-                int A = 0, B = 0, C = 0, D = 0;
-                // A is just the lsb replicated 9 times.
-                A = BitArrayStream.Replicate(BitValue & 1, 1, 9);
-
-                switch (IntEncoded.GetEncoding())
-                {
-                    case IntegerEncoded.EIntegerEncoding.JustBits:
-                    {
-                        OutputValues[OutputIndices++] = BitArrayStream.Replicate(BitValue, BitLength, 8);
-
-                        break;
-                    }
-
-                    case IntegerEncoded.EIntegerEncoding.Trit:
-                    {
-                        D = IntEncoded.TritValue;
-
-                        switch (BitLength)
-                        {
-                            case 1:
-                            {
-                                C = 204;
-
-                                break;
-                            }
-                                    
-                            case 2:
-                            {
-                                C = 93;
-                                // B = b000b0bb0
-                                int b = (BitValue >> 1) & 1;
-                                B = (b << 8) | (b << 4) | (b << 2) | (b << 1);
-
-                                break;
-                            }
-
-                            case 3:
-                            {
-                                C = 44;
-                                // B = cb000cbcb
-                                int cb = (BitValue >> 1) & 3;
-                                B = (cb << 7) | (cb << 2) | cb;
-
-                                break;
-                            }
-                                    
-
-                            case 4:
-                            {
-                                C = 22;
-                                // B = dcb000dcb
-                                int dcb = (BitValue >> 1) & 7;
-                                B = (dcb << 6) | dcb;
-
-                                break;
-                            }
-
-                            case 5:
-                            {
-                                C = 11;
-                                // B = edcb000ed
-                                int edcb = (BitValue >> 1) & 0xF;
-                                B = (edcb << 5) | (edcb >> 2);
-
-                                break;
-                            }
-
-                            case 6:
-                            {
-                                C = 5;
-                                // B = fedcb000f
-                                int fedcb = (BitValue >> 1) & 0x1F;
-                                B = (fedcb << 4) | (fedcb >> 4);
-
-                                break;
-                            }
-
-                            default:
-                                throw new ASTCDecoderException("Unsupported trit encoding for color values!");
-                        }
-
-                        break;
-                    }
-                        
-                    case IntegerEncoded.EIntegerEncoding.Quint:
-                    {
-                        D = IntEncoded.QuintValue;
-
-                        switch (BitLength)
-                        {
-                            case 1:
-                            {
-                                C = 113;
-
-                                break;
-                            }
-                                    
-                            case 2:
-                            {
-                                C = 54;
-                                // B = b0000bb00
-                                int b = (BitValue >> 1) & 1;
-                                B = (b << 8) | (b << 3) | (b << 2);
-
-                                break;
-                            }
-                                    
-                            case 3:
-                            {
-                                C = 26;
-                                // B = cb0000cbc
-                                int cb = (BitValue >> 1) & 3;
-                                B = (cb << 7) | (cb << 1) | (cb >> 1);
-
-                                break;
-                            }
-
-                            case 4:
-                            {
-                                C = 13;
-                                // B = dcb0000dc
-                                int dcb = (BitValue >> 1) & 7;
-                                B = (dcb << 6) | (dcb >> 1);
-
-                                break;
-                            }
-                                  
-                            case 5:
-                            {
-                                C = 6;
-                                // B = edcb0000e
-                                int edcb = (BitValue >> 1) & 0xF;
-                                B = (edcb << 5) | (edcb >> 3);
-
-                                break;
-                            }
-
-                            default:
-                                throw new ASTCDecoderException("Unsupported quint encoding for color values!");
-                        }
-                        break;
-                    }   
-                }
-
-                if (IntEncoded.GetEncoding() != IntegerEncoded.EIntegerEncoding.JustBits)
-                {
-                    int T = D * C + B;
-                    T    ^= A;
-                    T     = (A & 0x80) | (T >> 2);
-
-                    OutputValues[OutputIndices++] = T;
-                }
-            }
-
-            // Make sure that each of our values is in the proper range...
-            for (int i = 0; i < NumberValues; i++)
-            {
-                Debug.Assert(OutputValues[i] <= 255);
-            }
-        }
-
-        static void FillVoidExtentLDR(BitArrayStream BitStream, int[] OutputBuffer, int BlockWidth, int BlockHeight)
-        {
-            // Don't actually care about the void extent, just read the bits...
-            for (int i = 0; i < 4; ++i)
-            {
-                BitStream.ReadBits(13);
-            }
-
-            // Decode the RGBA components and renormalize them to the range [0, 255]
-            ushort R = (ushort)BitStream.ReadBits(16);
-            ushort G = (ushort)BitStream.ReadBits(16);
-            ushort B = (ushort)BitStream.ReadBits(16);
-            ushort A = (ushort)BitStream.ReadBits(16);
-
-            int RGBA = (R >> 8) | (G & 0xFF00) | ((B) & 0xFF00) << 8 | ((A) & 0xFF00) << 16;
-
-            for (int j = 0; j < BlockHeight; j++)
-            {
-                for (int i = 0; i < BlockWidth; i++)
-                {
-                    OutputBuffer[j * BlockWidth + i] = RGBA;
-                }
-            }
-        }
-
-        static TexelWeightParams DecodeBlockInfo(BitArrayStream BitStream)
-        {
-            TexelWeightParams TexelParams = new TexelWeightParams();
-
-            // Read the entire block mode all at once
-            ushort ModeBits = (ushort)BitStream.ReadBits(11);
-
-            // Does this match the void extent block mode?
-            if ((ModeBits & 0x01FF) == 0x1FC)
-            {
-                if ((ModeBits & 0x200) != 0)
-                {
-                    TexelParams.VoidExtentHDR = true;
-                }
-                else
-                {
-                    TexelParams.VoidExtentLDR = true;
-                }
-
-                // Next two bits must be one.
-                if ((ModeBits & 0x400) == 0 || BitStream.ReadBits(1) == 0)
-                {
-                    TexelParams.Error = true;
-                }
-
-                return TexelParams;
-            }
-
-            // First check if the last four bits are zero
-            if ((ModeBits & 0xF) == 0)
-            {
-                TexelParams.Error = true;
-                return TexelParams;
-            }
-
-            // If the last two bits are zero, then if bits
-            // [6-8] are all ones, this is also reserved.
-            if ((ModeBits & 0x3) == 0 && (ModeBits & 0x1C0) == 0x1C0)
-            {
-                TexelParams.Error = true;
-
-                return TexelParams;
-            }
-
-            // Otherwise, there is no error... Figure out the layout
-            // of the block mode. Layout is determined by a number
-            // between 0 and 9 corresponding to table C.2.8 of the
-            // ASTC spec.
-            int Layout = 0;
-
-            if ((ModeBits & 0x1) != 0 || (ModeBits & 0x2) != 0)
-            {
-                // layout is in [0-4]
-                if ((ModeBits & 0x8) != 0)
-                {
-                    // layout is in [2-4]
-                    if ((ModeBits & 0x4) != 0)
-                    {
-                        // layout is in [3-4]
-                        if ((ModeBits & 0x100) != 0)
-                        {
-                            Layout = 4;
-                        }
-                        else
-                        {
-                            Layout = 3;
-                        }
-                    }
-                    else
-                    {
-                        Layout = 2;
-                    }
-                }
-                else
-                {
-                    // layout is in [0-1]
-                    if ((ModeBits & 0x4) != 0)
-                    {
-                        Layout = 1;
-                    }
-                    else
-                    {
-                        Layout = 0;
-                    }
-                }
-            }
-            else
-            {
-                // layout is in [5-9]
-                if ((ModeBits & 0x100) != 0)
-                {
-                    // layout is in [7-9]
-                    if ((ModeBits & 0x80) != 0)
-                    {
-                        // layout is in [7-8]
-                        Debug.Assert((ModeBits & 0x40) == 0);
-
-                        if ((ModeBits & 0x20) != 0)
-                        {
-                            Layout = 8;
-                        }
-                        else
-                        {
-                            Layout = 7;
-                        }
-                    }
-                    else
-                    {
-                        Layout = 9;
-                    }
-                }
-                else
-                {
-                    // layout is in [5-6]
-                    if ((ModeBits & 0x80) != 0)
-                    {
-                        Layout = 6;
-                    }
-                    else
-                    {
-                        Layout = 5;
-                    }
-                }
-            }
-
-            Debug.Assert(Layout < 10);
-
-            // Determine R
-            int R = (ModeBits >> 4) & 1;
-            if (Layout < 5)
-            {
-                R |= (ModeBits & 0x3) << 1;
-            }
-            else
-            {
-                R |= (ModeBits & 0xC) >> 1;
-            }
-
-            Debug.Assert(2 <= R && R <= 7);
-
-            // Determine width & height
-            switch (Layout)
-            {
-                case 0:
-                {
-                    int A = (ModeBits >> 5) & 0x3;
-                    int B = (ModeBits >> 7) & 0x3;
-
-                    TexelParams.Width  = B + 4;
-                    TexelParams.Height = A + 2;
-
-                    break;
-                }
-
-                case 1:
-                {
-                    int A = (ModeBits >> 5) & 0x3;
-                    int B = (ModeBits >> 7) & 0x3;
-
-                    TexelParams.Width  = B + 8;
-                    TexelParams.Height = A + 2;
-
-                    break;
-                }
-
-                case 2:
-                {
-                    int A = (ModeBits >> 5) & 0x3;
-                    int B = (ModeBits >> 7) & 0x3;
-
-                    TexelParams.Width  = A + 2;
-                    TexelParams.Height = B + 8;
-
-                    break;
-                }
-
-                case 3:
-                {
-                    int A = (ModeBits >> 5) & 0x3;
-                    int B = (ModeBits >> 7) & 0x1;
-
-                    TexelParams.Width  = A + 2;
-                    TexelParams.Height = B + 6;
-
-                    break;
-                }
-
-                case 4:
-                {
-                    int A = (ModeBits >> 5) & 0x3;
-                    int B = (ModeBits >> 7) & 0x1;
-
-                    TexelParams.Width  = B + 2;
-                    TexelParams.Height = A + 2;
-
-                    break;
-                }
-
-                case 5:
-                {
-                    int A = (ModeBits >> 5) & 0x3;
-
-                    TexelParams.Width  = 12;
-                    TexelParams.Height = A + 2;
-
-                    break;
-                }
-
-                case 6:
-                {
-                    int A = (ModeBits >> 5) & 0x3;
-
-                    TexelParams.Width  = A + 2;
-                    TexelParams.Height = 12;
-
-                    break;
-                }
-
-                case 7:
-                {
-                    TexelParams.Width  = 6;
-                    TexelParams.Height = 10;
-
-                    break;
-                }
-
-                case 8:
-                {
-                    TexelParams.Width  = 10;
-                    TexelParams.Height = 6;
-                    break;
-                }
-
-                case 9:
-                {
-                    int A = (ModeBits >> 5) & 0x3;
-                    int B = (ModeBits >> 9) & 0x3;
-
-                    TexelParams.Width  = A + 6;
-                    TexelParams.Height = B + 6;
-
-                    break;
-                }
-
-                default:
-                    //Don't know this layout...
-                    TexelParams.Error = true;
-                    break;
-            }
-
-            // Determine whether or not we're using dual planes
-            // and/or high precision layouts.
-            bool D = ((Layout != 9) && ((ModeBits & 0x400) != 0));
-            bool H = (Layout != 9) && ((ModeBits & 0x200) != 0);
-
-            if (H)
-            {
-                int[] MaxWeights = { 9, 11, 15, 19, 23, 31 };
-                TexelParams.MaxWeight = MaxWeights[R - 2];
-            }
-            else
-            {
-                int[] MaxWeights = { 1, 2, 3, 4, 5, 7 };
-                TexelParams.MaxWeight = MaxWeights[R - 2];
-            }
-
-            TexelParams.DualPlane = D;
-
-            return TexelParams;
-        }
-    }
-}
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+
+namespace Ryujinx.Graphics.Texture
+{
+    public class ASTCDecoderException : Exception
+    {
+        public ASTCDecoderException(string ExMsg) : base(ExMsg) { }
+    }
+
+    //https://github.com/GammaUNC/FasTC/blob/master/ASTCEncoder/src/Decompressor.cpp
+    public static class ASTCDecoder
+    {
+        struct TexelWeightParams
+        {
+            public int  Width;
+            public int  Height;
+            public bool DualPlane;
+            public int  MaxWeight;
+            public bool Error;
+            public bool VoidExtentLDR;
+            public bool VoidExtentHDR;
+
+            public int GetPackedBitSize()
+            {
+                // How many indices do we have?
+                int Indices = Height * Width;
+
+                if (DualPlane)
+                {
+                    Indices *= 2;
+                }
+
+                IntegerEncoded IntEncoded = IntegerEncoded.CreateEncoding(MaxWeight);
+
+                return IntEncoded.GetBitLength(Indices);
+            }
+
+            public int GetNumWeightValues()
+            {
+                int Ret = Width * Height;
+
+                if (DualPlane)
+                {
+                    Ret *= 2;
+                }
+
+                return Ret;
+            }
+        }
+
+        public static byte[] DecodeToRGBA8888(
+            byte[] InputBuffer, 
+            int    BlockX, 
+            int    BlockY, 
+            int    BlockZ, 
+            int    X, 
+            int    Y, 
+            int    Z)
+        {
+            using (MemoryStream InputStream = new MemoryStream(InputBuffer))
+            {
+                BinaryReader BinReader = new BinaryReader(InputStream);
+
+                if (BlockX > 12 || BlockY > 12)
+                {
+                    throw new ASTCDecoderException("Block size unsupported!");
+                }
+
+                if (BlockZ != 1 || Z != 1)
+                {
+                    throw new ASTCDecoderException("3D compressed textures unsupported!");
+                }
+
+                using (MemoryStream OutputStream = new MemoryStream())
+                {
+                    int BlockIndex = 0;
+
+                    for (int j = 0; j < Y; j += BlockY)
+                    {
+                        for (int i = 0; i < X; i += BlockX)
+                        {
+                            int[] DecompressedData = new int[144];
+
+                            DecompressBlock(BinReader.ReadBytes(0x10), DecompressedData, BlockX, BlockY);
+
+                            int DecompressedWidth = Math.Min(BlockX, X - i);
+                            int DecompressedHeight = Math.Min(BlockY, Y - j);
+                            int BaseOffsets = (j * X + i) * 4;
+
+                            for (int jj = 0; jj < DecompressedHeight; jj++)
+                            {
+                                OutputStream.Seek(BaseOffsets + jj * X * 4, SeekOrigin.Begin);
+
+                                byte[] OutputBuffer = new byte[DecompressedData.Length * sizeof(int)];
+                                Buffer.BlockCopy(DecompressedData, 0, OutputBuffer, 0, OutputBuffer.Length);
+
+                                OutputStream.Write(OutputBuffer, jj * BlockX * 4, DecompressedWidth * 4);
+                            }
+
+                            BlockIndex++;
+                        }
+                    }
+
+                    return OutputStream.ToArray();
+                }
+            }
+        }
+
+        public static bool DecompressBlock(
+            byte[] InputBuffer, 
+            int[]  OutputBuffer, 
+            int    BlockWidth, 
+            int    BlockHeight)
+        {
+            BitArrayStream    BitStream   = new BitArrayStream(new BitArray(InputBuffer));
+            TexelWeightParams TexelParams = DecodeBlockInfo(BitStream);
+
+            if (TexelParams.Error)
+            {
+                throw new ASTCDecoderException("Invalid block mode");
+            }
+
+            if (TexelParams.VoidExtentLDR)
+            {
+                FillVoidExtentLDR(BitStream, OutputBuffer, BlockWidth, BlockHeight);
+
+                return true;
+            }
+
+            if (TexelParams.VoidExtentHDR)
+            {
+                throw new ASTCDecoderException("HDR void extent blocks are unsupported!");
+            }
+
+            if (TexelParams.Width > BlockWidth)
+            {
+                throw new ASTCDecoderException("Texel weight grid width should be smaller than block width");
+            }
+
+            if (TexelParams.Height > BlockHeight)
+            {
+                throw new ASTCDecoderException("Texel weight grid height should be smaller than block height");
+            }
+
+            // Read num partitions
+            int NumberPartitions = BitStream.ReadBits(2) + 1;
+            Debug.Assert(NumberPartitions <= 4);
+
+            if (NumberPartitions == 4 && TexelParams.DualPlane)
+            {
+                throw new ASTCDecoderException("Dual plane mode is incompatible with four partition blocks");
+            }
+
+            // Based on the number of partitions, read the color endpoint mode for
+            // each partition.
+
+            // Determine partitions, partition index, and color endpoint modes
+            int    PlaneIndices      = -1;
+            int    PartitionIndex;
+            uint[] ColorEndpointMode = { 0, 0, 0, 0 };
+
+            BitArrayStream ColorEndpointStream = new BitArrayStream(new BitArray(16 * 8));
+
+            // Read extra config data...
+            uint BaseColorEndpointMode = 0;
+
+            if (NumberPartitions == 1)
+            {
+                ColorEndpointMode[0] = (uint)BitStream.ReadBits(4);
+                PartitionIndex       = 0;
+            }
+            else
+            {
+                PartitionIndex        = BitStream.ReadBits(10);
+                BaseColorEndpointMode = (uint)BitStream.ReadBits(6);
+            }
+
+            uint BaseMode = (BaseColorEndpointMode & 3);
+
+            // Remaining bits are color endpoint data...
+            int NumberWeightBits = TexelParams.GetPackedBitSize();
+            int RemainingBits    = 128 - NumberWeightBits - BitStream.Position;
+
+            // Consider extra bits prior to texel data...
+            uint ExtraColorEndpointModeBits = 0;
+
+            if (BaseMode != 0)
+            {
+                switch (NumberPartitions)
+                {
+                    case 2:  ExtraColorEndpointModeBits += 2; break;
+                    case 3:  ExtraColorEndpointModeBits += 5; break;
+                    case 4:  ExtraColorEndpointModeBits += 8; break;
+                    default: Debug.Assert(false); break;
+                }
+            }
+
+            RemainingBits -= (int)ExtraColorEndpointModeBits;
+
+            // Do we have a dual plane situation?
+            int PlaneSelectorBits = 0;
+
+            if (TexelParams.DualPlane)
+            {
+                PlaneSelectorBits = 2;
+            }
+
+            RemainingBits -= PlaneSelectorBits;
+
+            // Read color data...
+            int ColorDataBits = RemainingBits;
+
+            while (RemainingBits > 0)
+            {
+                int NumberBits = Math.Min(RemainingBits, 8);
+                int Bits = BitStream.ReadBits(NumberBits);
+                ColorEndpointStream.WriteBits(Bits, NumberBits);
+                RemainingBits -= 8;
+            }
+
+            // Read the plane selection bits
+            PlaneIndices = BitStream.ReadBits(PlaneSelectorBits);
+
+            // Read the rest of the CEM
+            if (BaseMode != 0)
+            {
+                uint ExtraColorEndpointMode = (uint)BitStream.ReadBits((int)ExtraColorEndpointModeBits);
+                uint TempColorEndpointMode  = (ExtraColorEndpointMode << 6) | BaseColorEndpointMode;
+                TempColorEndpointMode     >>= 2;
+
+                bool[] C = new bool[4];
+
+                for (int i = 0; i < NumberPartitions; i++)
+                {
+                    C[i] = (TempColorEndpointMode & 1) != 0;
+                    TempColorEndpointMode >>= 1;
+                }
+
+                byte[] M = new byte[4];
+
+                for (int i = 0; i < NumberPartitions; i++)
+                {
+                    M[i] = (byte)(TempColorEndpointMode & 3);
+                    TempColorEndpointMode >>= 2;
+                    Debug.Assert(M[i] <= 3);
+                }
+
+                for (int i = 0; i < NumberPartitions; i++)
+                {
+                    ColorEndpointMode[i] = BaseMode;
+                    if (!(C[i])) ColorEndpointMode[i] -= 1;
+                    ColorEndpointMode[i] <<= 2;
+                    ColorEndpointMode[i] |= M[i];
+                }
+            }
+            else if (NumberPartitions > 1)
+            {
+                uint TempColorEndpointMode = BaseColorEndpointMode >> 2;
+
+                for (uint i = 0; i < NumberPartitions; i++)
+                {
+                    ColorEndpointMode[i] = TempColorEndpointMode;
+                }
+            }
+
+            // Make sure everything up till here is sane.
+            for (int i = 0; i < NumberPartitions; i++)
+            {
+                Debug.Assert(ColorEndpointMode[i] < 16);
+            }
+            Debug.Assert(BitStream.Position + TexelParams.GetPackedBitSize() == 128);
+
+            // Decode both color data and texel weight data
+            int[] ColorValues = new int[32]; // Four values * two endpoints * four maximum partitions
+            DecodeColorValues(ColorValues, ColorEndpointStream.ToByteArray(), ColorEndpointMode, NumberPartitions, ColorDataBits);
+
+            ASTCPixel[][] EndPoints = new ASTCPixel[4][];
+            EndPoints[0] = new ASTCPixel[2];
+            EndPoints[1] = new ASTCPixel[2];
+            EndPoints[2] = new ASTCPixel[2];
+            EndPoints[3] = new ASTCPixel[2];
+
+            int ColorValuesPosition = 0;
+
+            for (int i = 0; i < NumberPartitions; i++)
+            {
+                ComputeEndpoints(EndPoints[i], ColorValues, ColorEndpointMode[i], ref ColorValuesPosition);
+            }
+
+            // Read the texel weight data.
+            byte[] TexelWeightData = (byte[])InputBuffer.Clone();
+
+            // Reverse everything
+            for (int i = 0; i < 8; i++)
+            {
+                byte a = ReverseByte(TexelWeightData[i]);
+                byte b = ReverseByte(TexelWeightData[15 - i]);
+
+                TexelWeightData[i]      = b;
+                TexelWeightData[15 - i] = a;
+            }
+
+            // Make sure that higher non-texel bits are set to zero
+            int ClearByteStart                   = (TexelParams.GetPackedBitSize() >> 3) + 1;
+            TexelWeightData[ClearByteStart - 1] &= (byte)((1 << (TexelParams.GetPackedBitSize() % 8)) - 1);
+
+            int cLen = 16 - ClearByteStart;
+            for (int i = ClearByteStart; i < ClearByteStart + cLen; i++) TexelWeightData[i] = 0;
+
+            List<IntegerEncoded> TexelWeightValues = new List<IntegerEncoded>();
+            BitArrayStream WeightBitStream         = new BitArrayStream(new BitArray(TexelWeightData));
+
+            IntegerEncoded.DecodeIntegerSequence(TexelWeightValues, WeightBitStream, TexelParams.MaxWeight, TexelParams.GetNumWeightValues());
+            
+            // Blocks can be at most 12x12, so we can have as many as 144 weights
+            int[][] Weights = new int[2][];
+            Weights[0] = new int[144];
+            Weights[1] = new int[144];
+
+            UnquantizeTexelWeights(Weights, TexelWeightValues, TexelParams, BlockWidth, BlockHeight);
+
+            // Now that we have endpoints and weights, we can interpolate and generate
+            // the proper decoding...
+            for (int j = 0; j < BlockHeight; j++)
+            {
+                for (int i = 0; i < BlockWidth; i++)
+                {
+                    int Partition = Select2DPartition(PartitionIndex, i, j, NumberPartitions, ((BlockHeight * BlockWidth) < 32));
+                    Debug.Assert(Partition < NumberPartitions);
+
+                    ASTCPixel Pixel = new ASTCPixel(0, 0, 0, 0);
+                    for (int Component = 0; Component < 4; Component++)
+                    {
+                        int Component0 = EndPoints[Partition][0].GetComponent(Component);
+                        Component0     = BitArrayStream.Replicate(Component0, 8, 16);
+                        int Component1 = EndPoints[Partition][1].GetComponent(Component);
+                        Component1     = BitArrayStream.Replicate(Component1, 8, 16);
+
+                        int Plane = 0;
+
+                        if (TexelParams.DualPlane && (((PlaneIndices + 1) & 3) == Component))
+                        {
+                            Plane = 1;
+                        }
+
+                        int Weight = Weights[Plane][j * BlockWidth + i];
+                        int FinalComponent = (Component0 * (64 - Weight) + Component1 * Weight + 32) / 64;
+
+                        if (FinalComponent == 65535)
+                        {
+                            Pixel.SetComponent(Component, 255);
+                        }
+                        else
+                        {
+                            double FinalComponentFloat = FinalComponent;
+                            Pixel.SetComponent(Component, (int)(255.0 * (FinalComponentFloat / 65536.0) + 0.5));
+                        }
+                    }
+
+                    OutputBuffer[j * BlockWidth + i] = Pixel.Pack();
+                }
+            }
+
+            return true;
+        }
+
+        private static int Select2DPartition(int Seed, int X, int Y, int PartitionCount, bool IsSmallBlock)
+        {
+            return SelectPartition(Seed, X, Y, 0, PartitionCount, IsSmallBlock);
+        }
+
+        private static int SelectPartition(int Seed, int X, int Y, int Z, int PartitionCount, bool IsSmallBlock)
+        {
+            if (PartitionCount == 1)
+            {
+                return 0;
+            }
+
+            if (IsSmallBlock)
+            {
+                X <<= 1;
+                Y <<= 1;
+                Z <<= 1;
+            }
+
+            Seed += (PartitionCount - 1) * 1024;
+
+            int  RightNum = Hash52((uint)Seed);
+            byte Seed01   = (byte)(RightNum & 0xF);
+            byte Seed02   = (byte)((RightNum >> 4) & 0xF);
+            byte Seed03   = (byte)((RightNum >> 8) & 0xF);
+            byte Seed04   = (byte)((RightNum >> 12) & 0xF);
+            byte Seed05   = (byte)((RightNum >> 16) & 0xF);
+            byte Seed06   = (byte)((RightNum >> 20) & 0xF);
+            byte Seed07   = (byte)((RightNum >> 24) & 0xF);
+            byte Seed08   = (byte)((RightNum >> 28) & 0xF);
+            byte Seed09   = (byte)((RightNum >> 18) & 0xF);
+            byte Seed10   = (byte)((RightNum >> 22) & 0xF);
+            byte Seed11   = (byte)((RightNum >> 26) & 0xF);
+            byte Seed12   = (byte)(((RightNum >> 30) | (RightNum << 2)) & 0xF);
+
+            Seed01 *= Seed01; Seed02 *= Seed02;
+            Seed03 *= Seed03; Seed04 *= Seed04;
+            Seed05 *= Seed05; Seed06 *= Seed06;
+            Seed07 *= Seed07; Seed08 *= Seed08;
+            Seed09 *= Seed09; Seed10 *= Seed10;
+            Seed11 *= Seed11; Seed12 *= Seed12;
+
+            int SeedHash1, SeedHash2, SeedHash3;
+
+            if ((Seed & 1) != 0)
+            {
+                SeedHash1 = (Seed & 2) != 0 ? 4 : 5;
+                SeedHash2 = (PartitionCount == 3) ? 6 : 5;
+            }
+            else
+            {
+                SeedHash1 = (PartitionCount == 3) ? 6 : 5;
+                SeedHash2 = (Seed & 2) != 0 ? 4 : 5;
+            }
+
+            SeedHash3 = (Seed & 0x10) != 0 ? SeedHash1 : SeedHash2;
+
+            Seed01 >>= SeedHash1; Seed02 >>= SeedHash2; Seed03 >>= SeedHash1; Seed04 >>= SeedHash2;
+            Seed05 >>= SeedHash1; Seed06 >>= SeedHash2; Seed07 >>= SeedHash1; Seed08 >>= SeedHash2;
+            Seed09 >>= SeedHash3; Seed10 >>= SeedHash3; Seed11 >>= SeedHash3; Seed12 >>= SeedHash3;
+
+            int a = Seed01 * X + Seed02 * Y + Seed11 * Z + (RightNum >> 14);
+            int b = Seed03 * X + Seed04 * Y + Seed12 * Z + (RightNum >> 10);
+            int c = Seed05 * X + Seed06 * Y + Seed09 * Z + (RightNum >> 6);
+            int d = Seed07 * X + Seed08 * Y + Seed10 * Z + (RightNum >> 2);
+
+            a &= 0x3F; b &= 0x3F; c &= 0x3F; d &= 0x3F;
+
+            if (PartitionCount < 4) d = 0;
+            if (PartitionCount < 3) c = 0;
+
+            if (a >= b && a >= c && a >= d) return 0;
+            else if (b >= c && b >= d) return 1;
+            else if (c >= d) return 2;
+            return 3;
+        }
+
+        static int Hash52(uint Val)
+        {
+            Val ^= Val >> 15; Val -= Val << 17; Val += Val << 7; Val += Val << 4;
+            Val ^= Val >> 5;  Val += Val << 16; Val ^= Val >> 7; Val ^= Val >> 3;
+            Val ^= Val << 6;  Val ^= Val >> 17;
+
+            return (int)Val;
+        }
+
+        static void UnquantizeTexelWeights(
+            int[][]              OutputBuffer, 
+            List<IntegerEncoded> Weights, 
+            TexelWeightParams    TexelParams, 
+            int                  BlockWidth, 
+            int                  BlockHeight)
+        {
+            int WeightIndices   = 0;
+            int[][] Unquantized = new int[2][];
+            Unquantized[0]      = new int[144];
+            Unquantized[1]      = new int[144];
+
+            for (int i = 0; i < Weights.Count; i++)
+            {
+                Unquantized[0][WeightIndices] = UnquantizeTexelWeight(Weights[i]);
+
+                if (TexelParams.DualPlane)
+                {
+                    i++;
+                    Unquantized[1][WeightIndices] = UnquantizeTexelWeight(Weights[i]);
+
+                    if (i == Weights.Count)
+                    {
+                        break;
+                    }
+                }
+
+                if (++WeightIndices >= (TexelParams.Width * TexelParams.Height)) break;
+            }
+
+            // Do infill if necessary (Section C.2.18) ...
+            int Ds = (1024 + (BlockWidth / 2)) / (BlockWidth - 1);
+            int Dt = (1024 + (BlockHeight / 2)) / (BlockHeight - 1);
+
+            int PlaneScale = TexelParams.DualPlane ? 2 : 1;
+
+            for (int Plane = 0; Plane < PlaneScale; Plane++)
+            {
+                for (int t = 0; t < BlockHeight; t++)
+                {
+                    for (int s = 0; s < BlockWidth; s++)
+                    {
+                        int cs = Ds * s;
+                        int ct = Dt * t;
+
+                        int gs = (cs * (TexelParams.Width - 1) + 32) >> 6;
+                        int gt = (ct * (TexelParams.Height - 1) + 32) >> 6;
+
+                        int js = gs >> 4;
+                        int fs = gs & 0xF;
+
+                        int jt = gt >> 4;
+                        int ft = gt & 0x0F;
+
+                        int w11 = (fs * ft + 8) >> 4;
+                        int w10 = ft - w11;
+                        int w01 = fs - w11;
+                        int w00 = 16 - fs - ft + w11;
+
+                        int v0 = js + jt * TexelParams.Width;
+
+                        int p00 = 0;
+                        int p01 = 0;
+                        int p10 = 0;
+                        int p11 = 0;
+
+                        if (v0 < (TexelParams.Width * TexelParams.Height))
+                        {
+                            p00 = Unquantized[Plane][v0];
+                        }
+
+                        if (v0 + 1 < (TexelParams.Width * TexelParams.Height))
+                        {
+                            p01 = Unquantized[Plane][v0 + 1];
+                        }
+                        
+                        if (v0 + TexelParams.Width < (TexelParams.Width * TexelParams.Height))
+                        {
+                            p10 = Unquantized[Plane][v0 + TexelParams.Width];
+                        }
+                        
+                        if (v0 + TexelParams.Width + 1 < (TexelParams.Width * TexelParams.Height))
+                        {
+                            p11 = Unquantized[Plane][v0 + TexelParams.Width + 1];
+                        }
+
+                        OutputBuffer[Plane][t * BlockWidth + s] = (p00 * w00 + p01 * w01 + p10 * w10 + p11 * w11 + 8) >> 4;
+                    }
+                }
+            }
+        }
+
+        static int UnquantizeTexelWeight(IntegerEncoded IntEncoded)
+        {
+            int BitValue  = IntEncoded.BitValue;
+            int BitLength = IntEncoded.NumberBits;
+
+            int A = BitArrayStream.Replicate(BitValue & 1, 1, 7);
+            int B = 0, C = 0, D = 0;
+
+            int Result = 0;
+
+            switch (IntEncoded.GetEncoding())
+            {
+                case IntegerEncoded.EIntegerEncoding.JustBits:
+                    Result = BitArrayStream.Replicate(BitValue, BitLength, 6);
+                    break;
+
+                case IntegerEncoded.EIntegerEncoding.Trit:
+                {
+                    D = IntEncoded.TritValue;
+                    Debug.Assert(D < 3);
+
+                    switch (BitLength)
+                    {
+                        case 0:
+                        {
+                            int[] Results = { 0, 32, 63 };
+                            Result = Results[D];
+
+                            break;
+                        }
+
+                        case 1:
+                        {
+                            C = 50;
+                            break;
+                        }
+
+                        case 2:
+                        {
+                            C = 23;
+                            int b = (BitValue >> 1) & 1;
+                            B = (b << 6) | (b << 2) | b;
+
+                            break;
+                        }
+
+                        case 3:
+                        {
+                            C = 11;
+                            int cb = (BitValue >> 1) & 3;
+                            B = (cb << 5) | cb;
+
+                            break;
+                        }
+
+                        default:
+                            throw new ASTCDecoderException("Invalid trit encoding for texel weight");
+                    }
+
+                    break;
+                }    
+
+                case IntegerEncoded.EIntegerEncoding.Quint:
+                {
+                    D = IntEncoded.QuintValue;
+                    Debug.Assert(D < 5);
+
+                    switch (BitLength)
+                    {
+                        case 0:
+                        {
+                            int[] Results = { 0, 16, 32, 47, 63 };
+                            Result = Results[D];
+
+                            break;
+                        }
+
+                        case 1:
+                        {
+                            C = 28;
+
+                            break;
+                        }
+
+                        case 2:
+                        {
+                            C = 13;
+                            int b = (BitValue >> 1) & 1;
+                            B = (b << 6) | (b << 1);
+
+                            break;
+                        }
+                                
+                        default:
+                            throw new ASTCDecoderException("Invalid quint encoding for texel weight");
+                    }
+
+                    break;
+                }    
+            }
+
+            if (IntEncoded.GetEncoding() != IntegerEncoded.EIntegerEncoding.JustBits && BitLength > 0)
+            {
+                // Decode the value...
+                Result  = D * C + B;
+                Result ^= A;
+                Result  = (A & 0x20) | (Result >> 2);
+            }
+
+            Debug.Assert(Result < 64);
+
+            // Change from [0,63] to [0,64]
+            if (Result > 32)
+            {
+                Result += 1;
+            }
+
+            return Result;
+        }
+
+        static byte ReverseByte(byte b)
+        {
+            // Taken from http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith64Bits
+            return (byte)((((b) * 0x80200802L) & 0x0884422110L) * 0x0101010101L >> 32);
+        }
+
+        static uint[] ReadUintColorValues(int Number, int[] ColorValues, ref int ColorValuesPosition)
+        {
+            uint[] Ret = new uint[Number];
+
+            for (int i = 0; i < Number; i++)
+            {
+                Ret[i] = (uint)ColorValues[ColorValuesPosition++];
+            }
+
+            return Ret;
+        }
+
+        static int[] ReadIntColorValues(int Number, int[] ColorValues, ref int ColorValuesPosition)
+        {
+            int[] Ret = new int[Number];
+
+            for (int i = 0; i < Number; i++)
+            {
+                Ret[i] = ColorValues[ColorValuesPosition++];
+            }
+
+            return Ret;
+        }
+
+        static void ComputeEndpoints(
+            ASTCPixel[] EndPoints, 
+            int[]       ColorValues, 
+            uint        ColorEndpointMode, 
+            ref int     ColorValuesPosition)
+        {
+            switch (ColorEndpointMode)
+            {
+                case 0:
+                {
+                    uint[] Val = ReadUintColorValues(2, ColorValues, ref ColorValuesPosition);
+
+                    EndPoints[0] = new ASTCPixel(0xFF, (short)Val[0], (short)Val[0], (short)Val[0]);
+                    EndPoints[1] = new ASTCPixel(0xFF, (short)Val[1], (short)Val[1], (short)Val[1]);
+
+                    break;
+                }
+                    
+
+                case 1:
+                {
+                    uint[] Val = ReadUintColorValues(2, ColorValues, ref ColorValuesPosition);
+                    int L0     = (int)((Val[0] >> 2) | (Val[1] & 0xC0));
+                    int L1     = (int)Math.Max(L0 + (Val[1] & 0x3F), 0xFFU);
+
+                    EndPoints[0] = new ASTCPixel(0xFF, (short)L0, (short)L0, (short)L0);
+                    EndPoints[1] = new ASTCPixel(0xFF, (short)L1, (short)L1, (short)L1);
+
+                    break;
+                }
+
+                case 4:
+                {
+                    uint[] Val = ReadUintColorValues(4, ColorValues, ref ColorValuesPosition);
+
+                    EndPoints[0] = new ASTCPixel((short)Val[2], (short)Val[0], (short)Val[0], (short)Val[0]);
+                    EndPoints[1] = new ASTCPixel((short)Val[3], (short)Val[1], (short)Val[1], (short)Val[1]);
+
+                    break;
+                }
+
+                case 5:
+                {
+                    int[] Val = ReadIntColorValues(4, ColorValues, ref ColorValuesPosition);
+
+                    BitArrayStream.BitTransferSigned(ref Val[1], ref Val[0]);
+                    BitArrayStream.BitTransferSigned(ref Val[3], ref Val[2]);
+
+                    EndPoints[0] = new ASTCPixel((short)Val[2], (short)Val[0], (short)Val[0], (short)Val[0]);
+                    EndPoints[1] = new ASTCPixel((short)(Val[2] + Val[3]), (short)(Val[0] + Val[1]), (short)(Val[0] + Val[1]), (short)(Val[0] + Val[1]));
+
+                    EndPoints[0].ClampByte();
+                    EndPoints[1].ClampByte();
+
+                    break;
+                }
+
+                case 6:
+                {
+                    uint[] Val = ReadUintColorValues(4, ColorValues, ref ColorValuesPosition);
+
+                    EndPoints[0] = new ASTCPixel(0xFF, (short)(Val[0] * Val[3] >> 8), (short)(Val[1] * Val[3] >> 8), (short)(Val[2] * Val[3] >> 8));
+                    EndPoints[1] = new ASTCPixel(0xFF, (short)Val[0], (short)Val[1], (short)Val[2]);
+
+                    break;
+                }
+
+                case 8:
+                {
+                    uint[] Val = ReadUintColorValues(6, ColorValues, ref ColorValuesPosition);
+
+                    if (Val[1] + Val[3] + Val[5] >= Val[0] + Val[2] + Val[4])
+                    {
+                        EndPoints[0] = new ASTCPixel(0xFF, (short)Val[0], (short)Val[2], (short)Val[4]);
+                        EndPoints[1] = new ASTCPixel(0xFF, (short)Val[1], (short)Val[3], (short)Val[5]);
+                    }
+                    else
+                    {
+                        EndPoints[0] = ASTCPixel.BlueContract(0xFF, (short)Val[1], (short)Val[3], (short)Val[5]);
+                        EndPoints[1] = ASTCPixel.BlueContract(0xFF, (short)Val[0], (short)Val[2], (short)Val[4]);
+                    }
+
+                    break;
+                }
+
+                case 9:
+                {
+                    int[] Val = ReadIntColorValues(6, ColorValues, ref ColorValuesPosition);
+
+                    BitArrayStream.BitTransferSigned(ref Val[1], ref Val[0]);
+                    BitArrayStream.BitTransferSigned(ref Val[3], ref Val[2]);
+                    BitArrayStream.BitTransferSigned(ref Val[5], ref Val[4]);
+
+                    if (Val[1] + Val[3] + Val[5] >= 0)
+                    {
+                        EndPoints[0] = new ASTCPixel(0xFF, (short)Val[0], (short)Val[2], (short)Val[4]);
+                        EndPoints[1] = new ASTCPixel(0xFF, (short)(Val[0] + Val[1]), (short)(Val[2] + Val[3]), (short)(Val[4] + Val[5]));
+                    }
+                    else
+                    {
+                        EndPoints[0] = ASTCPixel.BlueContract(0xFF, Val[0] + Val[1], Val[2] + Val[3], Val[4] + Val[5]);
+                        EndPoints[1] = ASTCPixel.BlueContract(0xFF, Val[0], Val[2], Val[4]);
+                    }
+
+                    EndPoints[0].ClampByte();
+                    EndPoints[1].ClampByte();
+
+                    break;
+                }
+
+                case 10:
+                {
+                    uint[] Val = ReadUintColorValues(6, ColorValues, ref ColorValuesPosition);
+
+                    EndPoints[0] = new ASTCPixel((short)Val[4], (short)(Val[0] * Val[3] >> 8), (short)(Val[1] * Val[3] >> 8), (short)(Val[2] * Val[3] >> 8));
+                    EndPoints[1] = new ASTCPixel((short)Val[5], (short)Val[0], (short)Val[1], (short)Val[2]);
+
+                    break;
+                }
+
+                case 12:
+                {
+                    uint[] Val = ReadUintColorValues(8, ColorValues, ref ColorValuesPosition);
+
+                    if (Val[1] + Val[3] + Val[5] >= Val[0] + Val[2] + Val[4])
+                    {
+                        EndPoints[0] = new ASTCPixel((short)Val[6], (short)Val[0], (short)Val[2], (short)Val[4]);
+                        EndPoints[1] = new ASTCPixel((short)Val[7], (short)Val[1], (short)Val[3], (short)Val[5]);
+                    }
+                    else
+                    {
+                        EndPoints[0] = ASTCPixel.BlueContract((short)Val[7], (short)Val[1], (short)Val[3], (short)Val[5]);
+                        EndPoints[1] = ASTCPixel.BlueContract((short)Val[6], (short)Val[0], (short)Val[2], (short)Val[4]);
+                    }
+
+                    break;
+                }
+
+                case 13:
+                {
+                    int[] Val = ReadIntColorValues(8, ColorValues, ref ColorValuesPosition);
+
+                    BitArrayStream.BitTransferSigned(ref Val[1], ref Val[0]);
+                    BitArrayStream.BitTransferSigned(ref Val[3], ref Val[2]);
+                    BitArrayStream.BitTransferSigned(ref Val[5], ref Val[4]);
+                    BitArrayStream.BitTransferSigned(ref Val[7], ref Val[6]);
+
+                    if (Val[1] + Val[3] + Val[5] >= 0)
+                    {
+                        EndPoints[0] = new ASTCPixel((short)Val[6], (short)Val[0], (short)Val[2], (short)Val[4]);
+                        EndPoints[1] = new ASTCPixel((short)(Val[7] + Val[6]), (short)(Val[0] + Val[1]), (short)(Val[2] + Val[3]), (short)(Val[4] + Val[5]));
+                    }
+                    else
+                    {
+                        EndPoints[0] = ASTCPixel.BlueContract(Val[6] + Val[7], Val[0] + Val[1], Val[2] + Val[3], Val[4] + Val[5]);
+                        EndPoints[1] = ASTCPixel.BlueContract(Val[6], Val[0], Val[2], Val[4]);
+                    }
+
+                    EndPoints[0].ClampByte();
+                    EndPoints[1].ClampByte();
+
+                    break;
+                }
+
+                default:
+                    throw new ASTCDecoderException("Unsupported color endpoint mode (is it HDR?)");
+            }
+        }
+
+        static void DecodeColorValues(
+            int[]  OutputValues, 
+            byte[] InputData, 
+            uint[] Modes, 
+            int    NumberPartitions, 
+            int    NumberBitsForColorData)
+        {
+            // First figure out how many color values we have
+            int NumberValues = 0;
+
+            for (int i = 0; i < NumberPartitions; i++)
+            {
+                NumberValues += (int)((Modes[i] >> 2) + 1) << 1;
+            }
+
+            // Then based on the number of values and the remaining number of bits,
+            // figure out the max value for each of them...
+            int Range = 256;
+
+            while (--Range > 0)
+            {
+                IntegerEncoded IntEncoded = IntegerEncoded.CreateEncoding(Range);
+                int BitLength             = IntEncoded.GetBitLength(NumberValues);
+
+                if (BitLength <= NumberBitsForColorData)
+                {
+                    // Find the smallest possible range that matches the given encoding
+                    while (--Range > 0)
+                    {
+                        IntegerEncoded NewIntEncoded = IntegerEncoded.CreateEncoding(Range);
+                        if (!NewIntEncoded.MatchesEncoding(IntEncoded))
+                        {
+                            break;
+                        }
+                    }
+
+                    // Return to last matching range.
+                    Range++;
+                    break;
+                }
+            }
+
+            // We now have enough to decode our integer sequence.
+            List<IntegerEncoded> IntegerEncodedSequence = new List<IntegerEncoded>();
+            BitArrayStream ColorBitStream               = new BitArrayStream(new BitArray(InputData));
+
+            IntegerEncoded.DecodeIntegerSequence(IntegerEncodedSequence, ColorBitStream, Range, NumberValues);
+
+            // Once we have the decoded values, we need to dequantize them to the 0-255 range
+            // This procedure is outlined in ASTC spec C.2.13
+            int OutputIndices = 0;
+
+            foreach (IntegerEncoded IntEncoded in IntegerEncodedSequence)
+            {
+                int BitLength = IntEncoded.NumberBits;
+                int BitValue  = IntEncoded.BitValue;
+
+                Debug.Assert(BitLength >= 1);
+
+                int A = 0, B = 0, C = 0, D = 0;
+                // A is just the lsb replicated 9 times.
+                A = BitArrayStream.Replicate(BitValue & 1, 1, 9);
+
+                switch (IntEncoded.GetEncoding())
+                {
+                    case IntegerEncoded.EIntegerEncoding.JustBits:
+                    {
+                        OutputValues[OutputIndices++] = BitArrayStream.Replicate(BitValue, BitLength, 8);
+
+                        break;
+                    }
+
+                    case IntegerEncoded.EIntegerEncoding.Trit:
+                    {
+                        D = IntEncoded.TritValue;
+
+                        switch (BitLength)
+                        {
+                            case 1:
+                            {
+                                C = 204;
+
+                                break;
+                            }
+                                    
+                            case 2:
+                            {
+                                C = 93;
+                                // B = b000b0bb0
+                                int b = (BitValue >> 1) & 1;
+                                B = (b << 8) | (b << 4) | (b << 2) | (b << 1);
+
+                                break;
+                            }
+
+                            case 3:
+                            {
+                                C = 44;
+                                // B = cb000cbcb
+                                int cb = (BitValue >> 1) & 3;
+                                B = (cb << 7) | (cb << 2) | cb;
+
+                                break;
+                            }
+                                    
+
+                            case 4:
+                            {
+                                C = 22;
+                                // B = dcb000dcb
+                                int dcb = (BitValue >> 1) & 7;
+                                B = (dcb << 6) | dcb;
+
+                                break;
+                            }
+
+                            case 5:
+                            {
+                                C = 11;
+                                // B = edcb000ed
+                                int edcb = (BitValue >> 1) & 0xF;
+                                B = (edcb << 5) | (edcb >> 2);
+
+                                break;
+                            }
+
+                            case 6:
+                            {
+                                C = 5;
+                                // B = fedcb000f
+                                int fedcb = (BitValue >> 1) & 0x1F;
+                                B = (fedcb << 4) | (fedcb >> 4);
+
+                                break;
+                            }
+
+                            default:
+                                throw new ASTCDecoderException("Unsupported trit encoding for color values!");
+                        }
+
+                        break;
+                    }
+                        
+                    case IntegerEncoded.EIntegerEncoding.Quint:
+                    {
+                        D = IntEncoded.QuintValue;
+
+                        switch (BitLength)
+                        {
+                            case 1:
+                            {
+                                C = 113;
+
+                                break;
+                            }
+                                    
+                            case 2:
+                            {
+                                C = 54;
+                                // B = b0000bb00
+                                int b = (BitValue >> 1) & 1;
+                                B = (b << 8) | (b << 3) | (b << 2);
+
+                                break;
+                            }
+                                    
+                            case 3:
+                            {
+                                C = 26;
+                                // B = cb0000cbc
+                                int cb = (BitValue >> 1) & 3;
+                                B = (cb << 7) | (cb << 1) | (cb >> 1);
+
+                                break;
+                            }
+
+                            case 4:
+                            {
+                                C = 13;
+                                // B = dcb0000dc
+                                int dcb = (BitValue >> 1) & 7;
+                                B = (dcb << 6) | (dcb >> 1);
+
+                                break;
+                            }
+                                  
+                            case 5:
+                            {
+                                C = 6;
+                                // B = edcb0000e
+                                int edcb = (BitValue >> 1) & 0xF;
+                                B = (edcb << 5) | (edcb >> 3);
+
+                                break;
+                            }
+
+                            default:
+                                throw new ASTCDecoderException("Unsupported quint encoding for color values!");
+                        }
+                        break;
+                    }   
+                }
+
+                if (IntEncoded.GetEncoding() != IntegerEncoded.EIntegerEncoding.JustBits)
+                {
+                    int T = D * C + B;
+                    T    ^= A;
+                    T     = (A & 0x80) | (T >> 2);
+
+                    OutputValues[OutputIndices++] = T;
+                }
+            }
+
+            // Make sure that each of our values is in the proper range...
+            for (int i = 0; i < NumberValues; i++)
+            {
+                Debug.Assert(OutputValues[i] <= 255);
+            }
+        }
+
+        static void FillVoidExtentLDR(BitArrayStream BitStream, int[] OutputBuffer, int BlockWidth, int BlockHeight)
+        {
+            // Don't actually care about the void extent, just read the bits...
+            for (int i = 0; i < 4; ++i)
+            {
+                BitStream.ReadBits(13);
+            }
+
+            // Decode the RGBA components and renormalize them to the range [0, 255]
+            ushort R = (ushort)BitStream.ReadBits(16);
+            ushort G = (ushort)BitStream.ReadBits(16);
+            ushort B = (ushort)BitStream.ReadBits(16);
+            ushort A = (ushort)BitStream.ReadBits(16);
+
+            int RGBA = (R >> 8) | (G & 0xFF00) | ((B) & 0xFF00) << 8 | ((A) & 0xFF00) << 16;
+
+            for (int j = 0; j < BlockHeight; j++)
+            {
+                for (int i = 0; i < BlockWidth; i++)
+                {
+                    OutputBuffer[j * BlockWidth + i] = RGBA;
+                }
+            }
+        }
+
+        static TexelWeightParams DecodeBlockInfo(BitArrayStream BitStream)
+        {
+            TexelWeightParams TexelParams = new TexelWeightParams();
+
+            // Read the entire block mode all at once
+            ushort ModeBits = (ushort)BitStream.ReadBits(11);
+
+            // Does this match the void extent block mode?
+            if ((ModeBits & 0x01FF) == 0x1FC)
+            {
+                if ((ModeBits & 0x200) != 0)
+                {
+                    TexelParams.VoidExtentHDR = true;
+                }
+                else
+                {
+                    TexelParams.VoidExtentLDR = true;
+                }
+
+                // Next two bits must be one.
+                if ((ModeBits & 0x400) == 0 || BitStream.ReadBits(1) == 0)
+                {
+                    TexelParams.Error = true;
+                }
+
+                return TexelParams;
+            }
+
+            // First check if the last four bits are zero
+            if ((ModeBits & 0xF) == 0)
+            {
+                TexelParams.Error = true;
+                return TexelParams;
+            }
+
+            // If the last two bits are zero, then if bits
+            // [6-8] are all ones, this is also reserved.
+            if ((ModeBits & 0x3) == 0 && (ModeBits & 0x1C0) == 0x1C0)
+            {
+                TexelParams.Error = true;
+
+                return TexelParams;
+            }
+
+            // Otherwise, there is no error... Figure out the layout
+            // of the block mode. Layout is determined by a number
+            // between 0 and 9 corresponding to table C.2.8 of the
+            // ASTC spec.
+            int Layout = 0;
+
+            if ((ModeBits & 0x1) != 0 || (ModeBits & 0x2) != 0)
+            {
+                // layout is in [0-4]
+                if ((ModeBits & 0x8) != 0)
+                {
+                    // layout is in [2-4]
+                    if ((ModeBits & 0x4) != 0)
+                    {
+                        // layout is in [3-4]
+                        if ((ModeBits & 0x100) != 0)
+                        {
+                            Layout = 4;
+                        }
+                        else
+                        {
+                            Layout = 3;
+                        }
+                    }
+                    else
+                    {
+                        Layout = 2;
+                    }
+                }
+                else
+                {
+                    // layout is in [0-1]
+                    if ((ModeBits & 0x4) != 0)
+                    {
+                        Layout = 1;
+                    }
+                    else
+                    {
+                        Layout = 0;
+                    }
+                }
+            }
+            else
+            {
+                // layout is in [5-9]
+                if ((ModeBits & 0x100) != 0)
+                {
+                    // layout is in [7-9]
+                    if ((ModeBits & 0x80) != 0)
+                    {
+                        // layout is in [7-8]
+                        Debug.Assert((ModeBits & 0x40) == 0);
+
+                        if ((ModeBits & 0x20) != 0)
+                        {
+                            Layout = 8;
+                        }
+                        else
+                        {
+                            Layout = 7;
+                        }
+                    }
+                    else
+                    {
+                        Layout = 9;
+                    }
+                }
+                else
+                {
+                    // layout is in [5-6]
+                    if ((ModeBits & 0x80) != 0)
+                    {
+                        Layout = 6;
+                    }
+                    else
+                    {
+                        Layout = 5;
+                    }
+                }
+            }
+
+            Debug.Assert(Layout < 10);
+
+            // Determine R
+            int R = (ModeBits >> 4) & 1;
+            if (Layout < 5)
+            {
+                R |= (ModeBits & 0x3) << 1;
+            }
+            else
+            {
+                R |= (ModeBits & 0xC) >> 1;
+            }
+
+            Debug.Assert(2 <= R && R <= 7);
+
+            // Determine width & height
+            switch (Layout)
+            {
+                case 0:
+                {
+                    int A = (ModeBits >> 5) & 0x3;
+                    int B = (ModeBits >> 7) & 0x3;
+
+                    TexelParams.Width  = B + 4;
+                    TexelParams.Height = A + 2;
+
+                    break;
+                }
+
+                case 1:
+                {
+                    int A = (ModeBits >> 5) & 0x3;
+                    int B = (ModeBits >> 7) & 0x3;
+
+                    TexelParams.Width  = B + 8;
+                    TexelParams.Height = A + 2;
+
+                    break;
+                }
+
+                case 2:
+                {
+                    int A = (ModeBits >> 5) & 0x3;
+                    int B = (ModeBits >> 7) & 0x3;
+
+                    TexelParams.Width  = A + 2;
+                    TexelParams.Height = B + 8;
+
+                    break;
+                }
+
+                case 3:
+                {
+                    int A = (ModeBits >> 5) & 0x3;
+                    int B = (ModeBits >> 7) & 0x1;
+
+                    TexelParams.Width  = A + 2;
+                    TexelParams.Height = B + 6;
+
+                    break;
+                }
+
+                case 4:
+                {
+                    int A = (ModeBits >> 5) & 0x3;
+                    int B = (ModeBits >> 7) & 0x1;
+
+                    TexelParams.Width  = B + 2;
+                    TexelParams.Height = A + 2;
+
+                    break;
+                }
+
+                case 5:
+                {
+                    int A = (ModeBits >> 5) & 0x3;
+
+                    TexelParams.Width  = 12;
+                    TexelParams.Height = A + 2;
+
+                    break;
+                }
+
+                case 6:
+                {
+                    int A = (ModeBits >> 5) & 0x3;
+
+                    TexelParams.Width  = A + 2;
+                    TexelParams.Height = 12;
+
+                    break;
+                }
+
+                case 7:
+                {
+                    TexelParams.Width  = 6;
+                    TexelParams.Height = 10;
+
+                    break;
+                }
+
+                case 8:
+                {
+                    TexelParams.Width  = 10;
+                    TexelParams.Height = 6;
+                    break;
+                }
+
+                case 9:
+                {
+                    int A = (ModeBits >> 5) & 0x3;
+                    int B = (ModeBits >> 9) & 0x3;
+
+                    TexelParams.Width  = A + 6;
+                    TexelParams.Height = B + 6;
+
+                    break;
+                }
+
+                default:
+                    //Don't know this layout...
+                    TexelParams.Error = true;
+                    break;
+            }
+
+            // Determine whether or not we're using dual planes
+            // and/or high precision layouts.
+            bool D = ((Layout != 9) && ((ModeBits & 0x400) != 0));
+            bool H = (Layout != 9) && ((ModeBits & 0x200) != 0);
+
+            if (H)
+            {
+                int[] MaxWeights = { 9, 11, 15, 19, 23, 31 };
+                TexelParams.MaxWeight = MaxWeights[R - 2];
+            }
+            else
+            {
+                int[] MaxWeights = { 1, 2, 3, 4, 5, 7 };
+                TexelParams.MaxWeight = MaxWeights[R - 2];
+            }
+
+            TexelParams.DualPlane = D;
+
+            return TexelParams;
+        }
+    }
+}
diff --git a/Ryujinx.Graphics/Gal/Texture/ASTCPixel.cs b/Ryujinx.Graphics/Texture/ASTCPixel.cs
similarity index 95%
rename from Ryujinx.Graphics/Gal/Texture/ASTCPixel.cs
rename to Ryujinx.Graphics/Texture/ASTCPixel.cs
index 4a2998186..c43eaf938 100644
--- a/Ryujinx.Graphics/Gal/Texture/ASTCPixel.cs
+++ b/Ryujinx.Graphics/Texture/ASTCPixel.cs
@@ -1,138 +1,138 @@
-using System;
-using System.Diagnostics;
-
-namespace Ryujinx.Graphics.Gal.Texture
-{
-    class ASTCPixel
-    {
-        public short R { get; set; }
-        public short G { get; set; }
-        public short B { get; set; }
-        public short A { get; set; }
-
-        byte[] BitDepth = new byte[4];
-
-        public ASTCPixel(short _A, short _R, short _G, short _B)
-        {
-            A = _A;
-            R = _R;
-            G = _G;
-            B = _B;
-
-            for (int i = 0; i < 4; i++)
-                BitDepth[i] = 8;
-        }
-
-        public void ClampByte()
-        {
-            R = Math.Min(Math.Max(R, (short)0), (short)255);
-            G = Math.Min(Math.Max(G, (short)0), (short)255);
-            B = Math.Min(Math.Max(B, (short)0), (short)255);
-            A = Math.Min(Math.Max(A, (short)0), (short)255);
-        }
-
-        public short GetComponent(int Index)
-        {
-            switch(Index)
-            {
-                case 0: return A;
-                case 1: return R;
-                case 2: return G;
-                case 3: return B;
-            }
-
-            return 0;
-        }
-
-        public void SetComponent(int Index, int Value)
-        {
-            switch (Index)
-            {
-                case 0:
-                    A = (short)Value;
-                    break;
-                case 1:
-                    R = (short)Value;
-                    break;
-                case 2:
-                    G = (short)Value;
-                    break;
-                case 3:
-                    B = (short)Value;
-                    break;
-            }
-        }
-
-        public void ChangeBitDepth(byte[] Depth)
-        {
-            for(int i = 0; i< 4; i++)
-            {
-                int Value = ChangeBitDepth(GetComponent(i), BitDepth[i], Depth[i]);
-
-                SetComponent(i, Value);
-                BitDepth[i] = Depth[i];
-            }
-        }
-
-        short ChangeBitDepth(short Value, byte OldDepth, byte NewDepth)
-        {
-            Debug.Assert(NewDepth <= 8);
-            Debug.Assert(OldDepth <= 8);
-
-            if (OldDepth == NewDepth)
-            {
-                // Do nothing
-                return Value;
-            }
-            else if (OldDepth == 0 && NewDepth != 0)
-            {
-                return (short)((1 << NewDepth) - 1);
-            }
-            else if (NewDepth > OldDepth)
-            {
-                return (short)BitArrayStream.Replicate(Value, OldDepth, NewDepth);
-            }
-            else
-            {
-                // oldDepth > newDepth
-                if (NewDepth == 0)
-                {
-                    return 0xFF;
-                }
-                else
-                {
-                    byte BitsWasted = (byte)(OldDepth - NewDepth);
-                    short TempValue = Value;
-
-                    TempValue = (short)((TempValue + (1 << (BitsWasted - 1))) >> BitsWasted);
-                    TempValue = Math.Min(Math.Max((short)0, TempValue), (short)((1 << NewDepth) - 1));
-
-                    return (byte)(TempValue);
-                }
-            }
-        }
-
-        public int Pack()
-        {
-            ASTCPixel NewPixel   = new ASTCPixel(A, R, G, B);
-            byte[] eightBitDepth = { 8, 8, 8, 8 };
-
-            NewPixel.ChangeBitDepth(eightBitDepth);
-
-            return (byte)NewPixel.A << 24 |
-                   (byte)NewPixel.B << 16 |
-                   (byte)NewPixel.G << 8  |
-                   (byte)NewPixel.R << 0;
-        }
-
-        // Adds more precision to the blue channel as described
-        // in C.2.14
-        public static ASTCPixel BlueContract(int a, int r, int g, int b)
-        {
-            return new ASTCPixel((short)(a),
-                                 (short)((r + b) >> 1),
-                                 (short)((g + b) >> 1),
-                                 (short)(b));
-        }
-    }
-}
+using System;
+using System.Diagnostics;
+
+namespace Ryujinx.Graphics.Texture
+{
+    class ASTCPixel
+    {
+        public short R { get; set; }
+        public short G { get; set; }
+        public short B { get; set; }
+        public short A { get; set; }
+
+        byte[] BitDepth = new byte[4];
+
+        public ASTCPixel(short _A, short _R, short _G, short _B)
+        {
+            A = _A;
+            R = _R;
+            G = _G;
+            B = _B;
+
+            for (int i = 0; i < 4; i++)
+                BitDepth[i] = 8;
+        }
+
+        public void ClampByte()
+        {
+            R = Math.Min(Math.Max(R, (short)0), (short)255);
+            G = Math.Min(Math.Max(G, (short)0), (short)255);
+            B = Math.Min(Math.Max(B, (short)0), (short)255);
+            A = Math.Min(Math.Max(A, (short)0), (short)255);
+        }
+
+        public short GetComponent(int Index)
+        {
+            switch(Index)
+            {
+                case 0: return A;
+                case 1: return R;
+                case 2: return G;
+                case 3: return B;
+            }
+
+            return 0;
+        }
+
+        public void SetComponent(int Index, int Value)
+        {
+            switch (Index)
+            {
+                case 0:
+                    A = (short)Value;
+                    break;
+                case 1:
+                    R = (short)Value;
+                    break;
+                case 2:
+                    G = (short)Value;
+                    break;
+                case 3:
+                    B = (short)Value;
+                    break;
+            }
+        }
+
+        public void ChangeBitDepth(byte[] Depth)
+        {
+            for(int i = 0; i< 4; i++)
+            {
+                int Value = ChangeBitDepth(GetComponent(i), BitDepth[i], Depth[i]);
+
+                SetComponent(i, Value);
+                BitDepth[i] = Depth[i];
+            }
+        }
+
+        short ChangeBitDepth(short Value, byte OldDepth, byte NewDepth)
+        {
+            Debug.Assert(NewDepth <= 8);
+            Debug.Assert(OldDepth <= 8);
+
+            if (OldDepth == NewDepth)
+            {
+                // Do nothing
+                return Value;
+            }
+            else if (OldDepth == 0 && NewDepth != 0)
+            {
+                return (short)((1 << NewDepth) - 1);
+            }
+            else if (NewDepth > OldDepth)
+            {
+                return (short)BitArrayStream.Replicate(Value, OldDepth, NewDepth);
+            }
+            else
+            {
+                // oldDepth > newDepth
+                if (NewDepth == 0)
+                {
+                    return 0xFF;
+                }
+                else
+                {
+                    byte BitsWasted = (byte)(OldDepth - NewDepth);
+                    short TempValue = Value;
+
+                    TempValue = (short)((TempValue + (1 << (BitsWasted - 1))) >> BitsWasted);
+                    TempValue = Math.Min(Math.Max((short)0, TempValue), (short)((1 << NewDepth) - 1));
+
+                    return (byte)(TempValue);
+                }
+            }
+        }
+
+        public int Pack()
+        {
+            ASTCPixel NewPixel   = new ASTCPixel(A, R, G, B);
+            byte[] eightBitDepth = { 8, 8, 8, 8 };
+
+            NewPixel.ChangeBitDepth(eightBitDepth);
+
+            return (byte)NewPixel.A << 24 |
+                   (byte)NewPixel.B << 16 |
+                   (byte)NewPixel.G << 8  |
+                   (byte)NewPixel.R << 0;
+        }
+
+        // Adds more precision to the blue channel as described
+        // in C.2.14
+        public static ASTCPixel BlueContract(int a, int r, int g, int b)
+        {
+            return new ASTCPixel((short)(a),
+                                 (short)((r + b) >> 1),
+                                 (short)((g + b) >> 1),
+                                 (short)(b));
+        }
+    }
+}
diff --git a/Ryujinx.Graphics/Gal/Texture/BitArrayStream.cs b/Ryujinx.Graphics/Texture/BitArrayStream.cs
similarity index 95%
rename from Ryujinx.Graphics/Gal/Texture/BitArrayStream.cs
rename to Ryujinx.Graphics/Texture/BitArrayStream.cs
index eb2204c4b..2a8ed0910 100644
--- a/Ryujinx.Graphics/Gal/Texture/BitArrayStream.cs
+++ b/Ryujinx.Graphics/Texture/BitArrayStream.cs
@@ -1,120 +1,121 @@
-using System;
-using System.Collections;
-
-namespace Ryujinx.Graphics.Gal.Texture
-{
-    public class BitArrayStream
-    {
-        public BitArray BitsArray;
-        public int Position { get; private set; }
-
-        public BitArrayStream(BitArray BitArray)
-        {
-            BitsArray = BitArray;
-            Position  = 0;
-        }
-
-        public short ReadBits(int Length)
-        {
-            int RetValue = 0;
-            for (int i = Position; i < Position + Length; i++)
-            {
-                if (BitsArray[i])
-                {
-                    RetValue |= 1 << (i - Position);
-                }
-            }
-
-            Position += Length;
-            return (short)RetValue;
-        }
-
-        public int ReadBits(int Start, int End)
-        {
-            int RetValue = 0;
-            for (int i = Start; i <= End; i++)
-            {
-                if (BitsArray[i])
-                {
-                    RetValue |= 1 << (i - Start);
-                }
-            }
-
-            return RetValue;
-        }
-
-        public int ReadBit(int Index)
-        {
-            return Convert.ToInt32(BitsArray[Index]);
-        }
-
-        public void WriteBits(int Value, int Length)
-        {
-            for (int i = Position; i < Position + Length; i++)
-            {
-                BitsArray[i] = ((Value >> (i - Position)) & 1) != 0;
-            }
-
-            Position += Length;
-        }
-
-        public byte[] ToByteArray()
-        {
-            byte[] RetArray = new byte[(BitsArray.Length + 7) / 8];
-            BitsArray.CopyTo(RetArray, 0);
-            return RetArray;
-        }
-
-        public static int Replicate(int Value, int NumberBits, int ToBit)
-        {
-            if (NumberBits == 0) return 0;
-            if (ToBit == 0) return 0;
-
-            int TempValue = Value & ((1 << NumberBits) - 1);
-            int RetValue  = TempValue;
-            int ResLength = NumberBits;
-
-            while (ResLength < ToBit)
-            {
-                int Comp = 0;
-                if (NumberBits > ToBit - ResLength)
-                {
-                    int NewShift = ToBit - ResLength;
-                    Comp         = NumberBits - NewShift;
-                    NumberBits   = NewShift;
-                }
-                RetValue <<= NumberBits;
-                RetValue  |= TempValue >> Comp;
-                ResLength += NumberBits;
-            }
-            return RetValue;
-        }
-
-        public static int PopCnt(int Number)
-        {
-            int Counter;
-            for (Counter = 0; Number != 0; Counter++)
-            {
-                Number &= Number - 1;
-            }
-            return Counter;
-        }
-
-        public static void Swap<T>(ref T lhs, ref T rhs)
-        {
-            T Temp = lhs;
-            lhs = rhs;
-            rhs = Temp;
-        }
-
-        // Transfers a bit as described in C.2.14
-        public static void BitTransferSigned(ref int a, ref int b)
-        {
-            b >>= 1;
-            b |= a & 0x80;
-            a >>= 1;
-            a &= 0x3F;
-            if ((a & 0x20) != 0) a -= 0x40;
-        }
-    }
-}
+using System;
+using System.Collections;
+
+namespace Ryujinx.Graphics.Texture
+{
+    public class BitArrayStream
+    {
+        public BitArray BitsArray;
+
+        public int Position { get; private set; }
+
+        public BitArrayStream(BitArray BitArray)
+        {
+            BitsArray = BitArray;
+            Position  = 0;
+        }
+
+        public short ReadBits(int Length)
+        {
+            int RetValue = 0;
+            for (int i = Position; i < Position + Length; i++)
+            {
+                if (BitsArray[i])
+                {
+                    RetValue |= 1 << (i - Position);
+                }
+            }
+
+            Position += Length;
+            return (short)RetValue;
+        }
+
+        public int ReadBits(int Start, int End)
+        {
+            int RetValue = 0;
+            for (int i = Start; i <= End; i++)
+            {
+                if (BitsArray[i])
+                {
+                    RetValue |= 1 << (i - Start);
+                }
+            }
+
+            return RetValue;
+        }
+
+        public int ReadBit(int Index)
+        {
+            return Convert.ToInt32(BitsArray[Index]);
+        }
+
+        public void WriteBits(int Value, int Length)
+        {
+            for (int i = Position; i < Position + Length; i++)
+            {
+                BitsArray[i] = ((Value >> (i - Position)) & 1) != 0;
+            }
+
+            Position += Length;
+        }
+
+        public byte[] ToByteArray()
+        {
+            byte[] RetArray = new byte[(BitsArray.Length + 7) / 8];
+            BitsArray.CopyTo(RetArray, 0);
+            return RetArray;
+        }
+
+        public static int Replicate(int Value, int NumberBits, int ToBit)
+        {
+            if (NumberBits == 0) return 0;
+            if (ToBit == 0) return 0;
+
+            int TempValue = Value & ((1 << NumberBits) - 1);
+            int RetValue  = TempValue;
+            int ResLength = NumberBits;
+
+            while (ResLength < ToBit)
+            {
+                int Comp = 0;
+                if (NumberBits > ToBit - ResLength)
+                {
+                    int NewShift = ToBit - ResLength;
+                    Comp         = NumberBits - NewShift;
+                    NumberBits   = NewShift;
+                }
+                RetValue <<= NumberBits;
+                RetValue  |= TempValue >> Comp;
+                ResLength += NumberBits;
+            }
+            return RetValue;
+        }
+
+        public static int PopCnt(int Number)
+        {
+            int Counter;
+            for (Counter = 0; Number != 0; Counter++)
+            {
+                Number &= Number - 1;
+            }
+            return Counter;
+        }
+
+        public static void Swap<T>(ref T lhs, ref T rhs)
+        {
+            T Temp = lhs;
+            lhs = rhs;
+            rhs = Temp;
+        }
+
+        // Transfers a bit as described in C.2.14
+        public static void BitTransferSigned(ref int a, ref int b)
+        {
+            b >>= 1;
+            b |= a & 0x80;
+            a >>= 1;
+            a &= 0x3F;
+            if ((a & 0x20) != 0) a -= 0x40;
+        }
+    }
+}
diff --git a/Ryujinx.HLE/Gpu/Texture/BlockLinearSwizzle.cs b/Ryujinx.Graphics/Texture/BlockLinearSwizzle.cs
similarity index 97%
rename from Ryujinx.HLE/Gpu/Texture/BlockLinearSwizzle.cs
rename to Ryujinx.Graphics/Texture/BlockLinearSwizzle.cs
index e66d76136..9451291e9 100644
--- a/Ryujinx.HLE/Gpu/Texture/BlockLinearSwizzle.cs
+++ b/Ryujinx.Graphics/Texture/BlockLinearSwizzle.cs
@@ -1,6 +1,6 @@
 using System;
 
-namespace Ryujinx.HLE.Gpu.Texture
+namespace Ryujinx.Graphics.Texture
 {
     class BlockLinearSwizzle : ISwizzle
     {
diff --git a/Ryujinx.HLE/Gpu/Texture/ISwizzle.cs b/Ryujinx.Graphics/Texture/ISwizzle.cs
similarity index 69%
rename from Ryujinx.HLE/Gpu/Texture/ISwizzle.cs
rename to Ryujinx.Graphics/Texture/ISwizzle.cs
index 222aab163..583fc20c5 100644
--- a/Ryujinx.HLE/Gpu/Texture/ISwizzle.cs
+++ b/Ryujinx.Graphics/Texture/ISwizzle.cs
@@ -1,4 +1,4 @@
-namespace Ryujinx.HLE.Gpu.Texture
+namespace Ryujinx.Graphics.Texture
 {
     interface ISwizzle
     {
diff --git a/Ryujinx.Graphics/Texture/ImageConverter.cs b/Ryujinx.Graphics/Texture/ImageConverter.cs
new file mode 100644
index 000000000..89529061f
--- /dev/null
+++ b/Ryujinx.Graphics/Texture/ImageConverter.cs
@@ -0,0 +1,24 @@
+namespace Ryujinx.Graphics.Texture
+{
+    static class ImageConverter
+    {
+        public static byte[] G8R8ToR8G8(
+            byte[] Data,
+            int    Width,
+            int    Height,
+            int    Depth)
+        {
+            int Texels = Width * Height * Depth;
+
+            byte[] Output = new byte[Texels * 2];
+
+            for (int Texel = 0; Texel < Texels; Texel++)
+            {
+                Output[Texel * 2 + 0] = Data[Texel * 2 + 1];
+                Output[Texel * 2 + 1] = Data[Texel * 2 + 0];
+            }
+
+            return Output;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Texture/ImageUtils.cs b/Ryujinx.Graphics/Texture/ImageUtils.cs
new file mode 100644
index 000000000..ccea43ec1
--- /dev/null
+++ b/Ryujinx.Graphics/Texture/ImageUtils.cs
@@ -0,0 +1,357 @@
+using Ryujinx.Graphics.Gal;
+using System;
+using System.Collections.Generic;
+
+namespace Ryujinx.Graphics.Texture
+{
+    static class ImageUtils
+    {
+        struct ImageDescriptor
+        {
+            public TextureReaderDelegate Reader;
+
+            public bool HasColor;
+            public bool HasDepth;
+            public bool HasStencil;
+
+            public bool Compressed;
+
+            public ImageDescriptor(
+                TextureReaderDelegate Reader,
+                bool                  HasColor,
+                bool                  HasDepth,
+                bool                  HasStencil,
+                bool                  Compressed)
+            {
+                this.Reader     = Reader;
+                this.HasColor   = HasColor;
+                this.HasDepth   = HasDepth;
+                this.HasStencil = HasStencil;
+                this.Compressed = Compressed;
+            }
+        }
+
+        private const GalImageFormat Snorm  = GalImageFormat.Snorm;
+        private const GalImageFormat Unorm  = GalImageFormat.Unorm;
+        private const GalImageFormat Sint   = GalImageFormat.Sint;
+        private const GalImageFormat Uint   = GalImageFormat.Uint;
+        private const GalImageFormat Sfloat = GalImageFormat.Sfloat;
+
+        private static readonly Dictionary<GalTextureFormat, GalImageFormat> s_TextureTable =
+                            new Dictionary<GalTextureFormat, GalImageFormat>()
+            {
+                { GalTextureFormat.R32G32B32A32, GalImageFormat.R32G32B32A32                 | Sint | Uint | Sfloat },
+                { GalTextureFormat.R16G16B16A16, GalImageFormat.R16G16B16A16 | Snorm | Unorm | Sint | Uint | Sfloat },
+                { GalTextureFormat.R32G32,       GalImageFormat.R32G32                       | Sint | Uint | Sfloat },
+                { GalTextureFormat.A8B8G8R8,     GalImageFormat.A8B8G8R8     | Snorm | Unorm | Sint | Uint          },
+                { GalTextureFormat.A2B10G10R10,  GalImageFormat.A2B10G10R10  | Snorm | Unorm | Sint | Uint          },
+                { GalTextureFormat.G8R8,         GalImageFormat.G8R8         | Snorm | Unorm | Sint | Uint          },
+                { GalTextureFormat.R16,          GalImageFormat.R16          | Snorm | Unorm | Sint | Uint | Sfloat },
+                { GalTextureFormat.R8,           GalImageFormat.R8           | Snorm | Unorm | Sint | Uint          },
+                { GalTextureFormat.R32,          GalImageFormat.R32                          | Sint | Uint | Sfloat },
+                { GalTextureFormat.A4B4G4R4,     GalImageFormat.A4B4G4R4             | Unorm                        },
+                { GalTextureFormat.A1B5G5R5,     GalImageFormat.A1R5G5B5             | Unorm                        },
+                { GalTextureFormat.B5G6R5,       GalImageFormat.B5G6R5               | Unorm                        },
+                { GalTextureFormat.BF10GF11RF11, GalImageFormat.B10G11R11                                  | Sfloat },
+                { GalTextureFormat.Z24S8,        GalImageFormat.D24_S8               | Unorm                        },
+                { GalTextureFormat.ZF32,         GalImageFormat.D32                                        | Sfloat },
+                { GalTextureFormat.ZF32_X24S8,   GalImageFormat.D32_S8               | Unorm                        },
+
+                //Compressed formats
+                { GalTextureFormat.BC6H_SF16,   GalImageFormat.BC6H_SF16  | Unorm         },
+                { GalTextureFormat.BC6H_UF16,   GalImageFormat.BC6H_UF16  | Unorm         },
+                { GalTextureFormat.BC7U,        GalImageFormat.BC7        | Unorm         },
+                { GalTextureFormat.BC1,         GalImageFormat.BC1_RGBA   | Unorm         },
+                { GalTextureFormat.BC2,         GalImageFormat.BC2        | Unorm         },
+                { GalTextureFormat.BC3,         GalImageFormat.BC3        | Unorm         },
+                { GalTextureFormat.BC4,         GalImageFormat.BC4        | Unorm | Snorm },
+                { GalTextureFormat.BC5,         GalImageFormat.BC5        | Unorm | Snorm },
+                { GalTextureFormat.Astc2D4x4,   GalImageFormat.ASTC_4x4   | Unorm         },
+                { GalTextureFormat.Astc2D5x5,   GalImageFormat.ASTC_5x5   | Unorm         },
+                { GalTextureFormat.Astc2D6x6,   GalImageFormat.ASTC_6x6   | Unorm         },
+                { GalTextureFormat.Astc2D8x8,   GalImageFormat.ASTC_8x8   | Unorm         },
+                { GalTextureFormat.Astc2D10x10, GalImageFormat.ASTC_10x10 | Unorm         },
+                { GalTextureFormat.Astc2D12x12, GalImageFormat.ASTC_12x12 | Unorm         },
+                { GalTextureFormat.Astc2D5x4,   GalImageFormat.ASTC_5x4   | Unorm         },
+                { GalTextureFormat.Astc2D6x5,   GalImageFormat.ASTC_6x5   | Unorm         },
+                { GalTextureFormat.Astc2D8x6,   GalImageFormat.ASTC_8x6   | Unorm         },
+                { GalTextureFormat.Astc2D10x8,  GalImageFormat.ASTC_10x8  | Unorm         },
+                { GalTextureFormat.Astc2D12x10, GalImageFormat.ASTC_12x10 | Unorm         },
+                { GalTextureFormat.Astc2D8x5,   GalImageFormat.ASTC_8x5   | Unorm         },
+                { GalTextureFormat.Astc2D10x5,  GalImageFormat.ASTC_10x5  | Unorm         },
+                { GalTextureFormat.Astc2D10x6,  GalImageFormat.ASTC_10x6  | Unorm         }
+            };
+
+        private static readonly Dictionary<GalImageFormat, ImageDescriptor> s_ImageTable =
+                            new Dictionary<GalImageFormat, ImageDescriptor>()
+            {
+                { GalImageFormat.R32G32B32A32,  new ImageDescriptor(TextureReader.Read16Bpp,                       true, false, false, false) },
+                { GalImageFormat.R16G16B16A16,  new ImageDescriptor(TextureReader.Read8Bpp,                        true, false, false, false) },
+                { GalImageFormat.R32G32,        new ImageDescriptor(TextureReader.Read8Bpp,                        true, false, false, false) },
+                { GalImageFormat.A8B8G8R8,      new ImageDescriptor(TextureReader.Read4Bpp,                        true, false, false, false) },
+                { GalImageFormat.A2B10G10R10,   new ImageDescriptor(TextureReader.Read4Bpp,                        true, false, false, false) },
+                { GalImageFormat.R32,           new ImageDescriptor(TextureReader.Read4Bpp,                        true, false, false, false) },
+                { GalImageFormat.A4B4G4R4,      new ImageDescriptor(TextureReader.Read2Bpp,                        true, false, false, false) },
+                { GalImageFormat.BC6H_SF16,     new ImageDescriptor(TextureReader.Read16BptCompressedTexture4x4,   true, false, false, true)  },
+                { GalImageFormat.BC6H_UF16,     new ImageDescriptor(TextureReader.Read16BptCompressedTexture4x4,   true, false, false, true)  },
+                { GalImageFormat.A1R5G5B5,      new ImageDescriptor(TextureReader.Read5551,                        true, false, false, false) },
+                { GalImageFormat.B5G6R5,        new ImageDescriptor(TextureReader.Read565,                         true, false, false, false) },
+                { GalImageFormat.BC7,           new ImageDescriptor(TextureReader.Read16BptCompressedTexture4x4,   true, false, false, true)  },
+                { GalImageFormat.R16G16,        new ImageDescriptor(TextureReader.Read4Bpp,                        true, false, false, false) },
+                { GalImageFormat.R8G8,          new ImageDescriptor(TextureReader.Read2Bpp,                        true, false, false, false) },
+                { GalImageFormat.G8R8,          new ImageDescriptor(TextureReader.Read2Bpp,                        true, false, false, false) },
+                { GalImageFormat.R16,           new ImageDescriptor(TextureReader.Read2Bpp,                        true, false, false, false) },
+                { GalImageFormat.R8,            new ImageDescriptor(TextureReader.Read1Bpp,                        true, false, false, false) },
+                { GalImageFormat.B10G11R11,     new ImageDescriptor(TextureReader.Read4Bpp,                        true, false, false, false) },
+                { GalImageFormat.A8B8G8R8_SRGB, new ImageDescriptor(TextureReader.Read4Bpp,                        true, false, false, false) },
+                { GalImageFormat.BC1_RGBA,      new ImageDescriptor(TextureReader.Read8Bpt4x4,                     true, false, false, true)  },
+                { GalImageFormat.BC2,           new ImageDescriptor(TextureReader.Read16BptCompressedTexture4x4,   true, false, false, true)  },
+                { GalImageFormat.BC3,           new ImageDescriptor(TextureReader.Read16BptCompressedTexture4x4,   true, false, false, true)  },
+                { GalImageFormat.BC4,           new ImageDescriptor(TextureReader.Read8Bpt4x4,                     true, false, false, true)  },
+                { GalImageFormat.BC5,           new ImageDescriptor(TextureReader.Read16BptCompressedTexture4x4,   true, false, false, true)  },
+                { GalImageFormat.ASTC_4x4,      new ImageDescriptor(TextureReader.Read16BptCompressedTexture4x4,   true, false, false, true)  },
+                { GalImageFormat.ASTC_5x5,      new ImageDescriptor(TextureReader.Read16BptCompressedTexture5x5,   true, false, false, true)  },
+                { GalImageFormat.ASTC_6x6,      new ImageDescriptor(TextureReader.Read16BptCompressedTexture6x6,   true, false, false, true)  },
+                { GalImageFormat.ASTC_8x8,      new ImageDescriptor(TextureReader.Read16BptCompressedTexture8x8,   true, false, false, true)  },
+                { GalImageFormat.ASTC_10x10,    new ImageDescriptor(TextureReader.Read16BptCompressedTexture10x10, true, false, false, true)  },
+                { GalImageFormat.ASTC_12x12,    new ImageDescriptor(TextureReader.Read16BptCompressedTexture12x12, true, false, false, true)  },
+                { GalImageFormat.ASTC_5x4,      new ImageDescriptor(TextureReader.Read16BptCompressedTexture5x4,   true, false, false, true)  },
+                { GalImageFormat.ASTC_6x5,      new ImageDescriptor(TextureReader.Read16BptCompressedTexture6x5,   true, false, false, true)  },
+                { GalImageFormat.ASTC_8x6,      new ImageDescriptor(TextureReader.Read16BptCompressedTexture8x6,   true, false, false, true)  },
+                { GalImageFormat.ASTC_10x8,     new ImageDescriptor(TextureReader.Read16BptCompressedTexture10x8,  true, false, false, true)  },
+                { GalImageFormat.ASTC_12x10,    new ImageDescriptor(TextureReader.Read16BptCompressedTexture12x10, true, false, false, true)  },
+                { GalImageFormat.ASTC_8x5,      new ImageDescriptor(TextureReader.Read16BptCompressedTexture8x5,   true, false, false, true)  },
+                { GalImageFormat.ASTC_10x5,     new ImageDescriptor(TextureReader.Read16BptCompressedTexture10x5,  true, false, false, true)  },
+                { GalImageFormat.ASTC_10x6,     new ImageDescriptor(TextureReader.Read16BptCompressedTexture10x6,  true, false, false, true)  },
+
+                { GalImageFormat.D24_S8, new ImageDescriptor(TextureReader.Read4Bpp, false, true, true,  false)  },
+                { GalImageFormat.D32,    new ImageDescriptor(TextureReader.Read4Bpp, false, true, false, false) },
+                { GalImageFormat.D16,    new ImageDescriptor(TextureReader.Read2Bpp, false, true, false, false) },
+                { GalImageFormat.D32_S8, new ImageDescriptor(TextureReader.Read8Bpp, false, true, true,  false)  },
+            };
+
+        public static GalImageFormat ConvertTexture(
+            GalTextureFormat Format,
+            GalTextureType RType,
+            GalTextureType GType,
+            GalTextureType BType,
+            GalTextureType AType)
+        {
+            if (RType != GType || RType != BType || RType != AType)
+            {
+                throw new NotImplementedException("Per component types are not implemented");
+            }
+
+            if (!s_TextureTable.TryGetValue(Format, out GalImageFormat ImageFormat))
+            {
+                throw new NotImplementedException("Texture with format " + ((int)Format).ToString("x2") + " not implemented");
+            }
+
+            GalTextureType Type = RType;
+
+            GalImageFormat FormatType = GetFormatType(RType);
+
+            if (ImageFormat.HasFlag(FormatType))
+            {
+                return (ImageFormat & GalImageFormat.FormatMask) | FormatType;
+            }
+            else
+            {
+                throw new NotImplementedException("Texture with format " + Format +
+                                                  " and component type " + Type + " is not implemented");
+            }
+        }
+
+        public static GalImageFormat ConvertSurface(GalSurfaceFormat Format)
+        {
+            switch (Format)
+            {
+                case GalSurfaceFormat.RGBA32Float:    return GalImageFormat.R32G32B32A32   | Sfloat;
+                case GalSurfaceFormat.RGBA16Float:    return GalImageFormat.R16G16B16A16   | Sfloat;
+                case GalSurfaceFormat.RG32Float:      return GalImageFormat.R32G32         | Sfloat;
+                case GalSurfaceFormat.RG32Sint:       return GalImageFormat.R32G32         | Sint;
+                case GalSurfaceFormat.RG32Uint:       return GalImageFormat.R32G32         | Uint;
+                case GalSurfaceFormat.BGRA8Unorm:     return GalImageFormat.R8G8B8A8       | Unorm; //Is this right?
+                case GalSurfaceFormat.BGRA8Srgb:      return GalImageFormat.A8B8G8R8_SRGB;          //This one might be wrong
+                case GalSurfaceFormat.RGB10A2Unorm:   return GalImageFormat.A2B10G10R10    | Unorm;
+                case GalSurfaceFormat.RGBA8Unorm:     return GalImageFormat.A8B8G8R8       | Unorm;
+                case GalSurfaceFormat.RGBA8Srgb:      return GalImageFormat.A8B8G8R8_SRGB;
+                case GalSurfaceFormat.RGBA8Snorm:     return GalImageFormat.A8B8G8R8       | Snorm;
+                case GalSurfaceFormat.RG16Snorm:      return GalImageFormat.R16G16         | Snorm;
+                case GalSurfaceFormat.RG16Float:      return GalImageFormat.R16G16         | Sfloat;
+                case GalSurfaceFormat.R11G11B10Float: return GalImageFormat.B10G11R11      | Sfloat;
+                case GalSurfaceFormat.R32Float:       return GalImageFormat.R32            | Sfloat;
+                case GalSurfaceFormat.RG8Unorm:       return GalImageFormat.R8G8           | Unorm;
+                case GalSurfaceFormat.RG8Snorm:       return GalImageFormat.R8             | Snorm;
+                case GalSurfaceFormat.R16Float:       return GalImageFormat.R16            | Sfloat;
+                case GalSurfaceFormat.R8Unorm:        return GalImageFormat.R8             | Unorm;
+            }
+
+            throw new NotImplementedException(Format.ToString());
+        }
+
+        public static GalImageFormat ConvertZeta(GalZetaFormat Format)
+        {
+            switch (Format)
+            {
+                case GalZetaFormat.Z32Float:      return GalImageFormat.D32    | Sfloat;
+                case GalZetaFormat.S8Z24Unorm:    return GalImageFormat.D24_S8 | Unorm;
+                case GalZetaFormat.Z16Unorm:      return GalImageFormat.D16    | Unorm;
+                //This one might not be Uint, change when a texture uses this format
+                case GalZetaFormat.Z32S8X24Float: return GalImageFormat.D32_S8 | Uint;
+            }
+
+            throw new NotImplementedException(Format.ToString());
+        }
+
+        public static TextureReaderDelegate GetReader(GalImageFormat Format)
+        {
+            return GetImageDescriptor(Format).Reader;
+        }
+
+        public static int GetSize(GalImage Image)
+        {
+            switch (Image.Format & GalImageFormat.FormatMask)
+            {
+                case GalImageFormat.R32G32B32A32:
+                    return Image.Width * Image.Height * 16;
+
+                case GalImageFormat.R16G16B16A16:
+                case GalImageFormat.D32_S8:
+                case GalImageFormat.R32G32:
+                    return Image.Width * Image.Height * 8;
+
+                case GalImageFormat.A8B8G8R8:
+                case GalImageFormat.A8B8G8R8_SRGB:
+                case GalImageFormat.A2B10G10R10:
+                case GalImageFormat.R16G16:
+                case GalImageFormat.R32:
+                case GalImageFormat.D32:
+                case GalImageFormat.B10G11R11:
+                case GalImageFormat.D24_S8:
+                    return Image.Width * Image.Height * 4;
+
+                case GalImageFormat.B4G4R4A4:
+                case GalImageFormat.A1R5G5B5:
+                case GalImageFormat.B5G6R5:
+                case GalImageFormat.R8G8:
+                case GalImageFormat.R16:
+                case GalImageFormat.D16:
+                    return Image.Width * Image.Height * 2;
+
+                case GalImageFormat.R8:
+                    return Image.Width * Image.Height;
+
+                case GalImageFormat.BC1_RGBA:
+                case GalImageFormat.BC4:
+                {
+                    return CompressedTextureSize(Image.Width, Image.Height, 4, 4, 8);
+                }
+
+                case GalImageFormat.BC6H_SF16:
+                case GalImageFormat.BC6H_UF16:
+                case GalImageFormat.BC7:
+                case GalImageFormat.BC2:
+                case GalImageFormat.BC3:
+                case GalImageFormat.BC5:
+                case GalImageFormat.ASTC_4x4:
+                    return CompressedTextureSize(Image.Width, Image.Height, 4, 4, 16);
+
+                case GalImageFormat.ASTC_5x5:
+                    return CompressedTextureSize(Image.Width, Image.Height, 5, 5, 16);
+
+                case GalImageFormat.ASTC_6x6:
+                    return CompressedTextureSize(Image.Width, Image.Height, 6, 6, 16);
+
+                case GalImageFormat.ASTC_8x8:
+                    return CompressedTextureSize(Image.Width, Image.Height, 8, 8, 16);
+
+                case GalImageFormat.ASTC_10x10:
+                    return CompressedTextureSize(Image.Width, Image.Height, 10, 10, 16);
+
+                case GalImageFormat.ASTC_12x12:
+                    return CompressedTextureSize(Image.Width, Image.Height, 12, 12, 16);
+
+                case GalImageFormat.ASTC_5x4:
+                    return CompressedTextureSize(Image.Width, Image.Height, 5, 4, 16);
+
+                case GalImageFormat.ASTC_6x5:
+                    return CompressedTextureSize(Image.Width, Image.Height, 6, 5, 16);
+
+                case GalImageFormat.ASTC_8x6:
+                    return CompressedTextureSize(Image.Width, Image.Height, 8, 6, 16);
+
+                case GalImageFormat.ASTC_10x8:
+                    return CompressedTextureSize(Image.Width, Image.Height, 10, 8, 16);
+
+                case GalImageFormat.ASTC_12x10:
+                    return CompressedTextureSize(Image.Width, Image.Height, 12, 10, 16);
+
+                case GalImageFormat.ASTC_8x5:
+                    return CompressedTextureSize(Image.Width, Image.Height, 8, 5, 16);
+
+                case GalImageFormat.ASTC_10x5:
+                    return CompressedTextureSize(Image.Width, Image.Height, 10, 5, 16);
+
+                case GalImageFormat.ASTC_10x6:
+                    return CompressedTextureSize(Image.Width, Image.Height, 10, 6, 16);
+            }
+
+            throw new NotImplementedException((Image.Format & GalImageFormat.FormatMask).ToString());
+        }
+
+        public static bool HasColor(GalImageFormat Format)
+        {
+            return GetImageDescriptor(Format).HasColor;
+        }
+
+        public static bool HasDepth(GalImageFormat Format)
+        {
+            return GetImageDescriptor(Format).HasDepth;
+        }
+
+        public static bool HasStencil(GalImageFormat Format)
+        {
+            return GetImageDescriptor(Format).HasStencil;
+        }
+
+        public static bool IsCompressed(GalImageFormat Format)
+        {
+            return GetImageDescriptor(Format).Compressed;
+        }
+
+        private static ImageDescriptor GetImageDescriptor(GalImageFormat Format)
+        {
+            GalImageFormat TypeLess = (Format & GalImageFormat.FormatMask);
+
+            if (s_ImageTable.TryGetValue(TypeLess, out ImageDescriptor Descriptor))
+            {
+                return Descriptor;
+            }
+
+            throw new NotImplementedException("Image with format " + TypeLess.ToString() + " not implemented");
+        }
+
+        private static GalImageFormat GetFormatType(GalTextureType Type)
+        {
+            switch (Type)
+            {
+                case GalTextureType.Snorm: return Snorm;
+                case GalTextureType.Unorm: return Unorm;
+                case GalTextureType.Sint:  return Sint;
+                case GalTextureType.Uint:  return Uint;
+                case GalTextureType.Float: return Sfloat;
+
+                default: throw new NotImplementedException(((int)Type).ToString());
+            }
+        }
+
+        private static int CompressedTextureSize(int TextureWidth, int TextureHeight, int BlockWidth, int BlockHeight, int Bpb)
+        {
+            int W = (TextureWidth + (BlockWidth - 1)) / BlockWidth;
+            int H = (TextureHeight + (BlockHeight - 1)) / BlockHeight;
+
+            return W * H * Bpb;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Texture/IntegerEncoded.cs b/Ryujinx.Graphics/Texture/IntegerEncoded.cs
similarity index 96%
rename from Ryujinx.Graphics/Gal/Texture/IntegerEncoded.cs
rename to Ryujinx.Graphics/Texture/IntegerEncoded.cs
index 0adabe17e..683cb7704 100644
--- a/Ryujinx.Graphics/Gal/Texture/IntegerEncoded.cs
+++ b/Ryujinx.Graphics/Texture/IntegerEncoded.cs
@@ -1,269 +1,269 @@
-using System.Collections;
-using System.Collections.Generic;
-
-namespace Ryujinx.Graphics.Gal.Texture
-{
-    public struct IntegerEncoded
-    {
-        public enum EIntegerEncoding
-        {
-            JustBits,
-            Quint,
-            Trit
-        }
-
-        EIntegerEncoding Encoding;
-        public int NumberBits { get; private set; }
-        public int BitValue   { get; private set; }
-        public int TritValue  { get; private set; }
-        public int QuintValue { get; private set; }
-
-        public IntegerEncoded(EIntegerEncoding _Encoding, int NumBits)
-        {
-            Encoding   = _Encoding;
-            NumberBits = NumBits;
-            BitValue   = 0;
-            TritValue  = 0;
-            QuintValue = 0;
-        }
-
-        public bool MatchesEncoding(IntegerEncoded Other)
-        {
-            return Encoding == Other.Encoding && NumberBits == Other.NumberBits;
-        }
-
-        public EIntegerEncoding GetEncoding()
-        {
-            return Encoding;
-        }
-
-        public int GetBitLength(int NumberVals)
-        {
-            int TotalBits = NumberBits * NumberVals;
-            if (Encoding == EIntegerEncoding.Trit)
-            {
-                TotalBits += (NumberVals * 8 + 4) / 5;
-            }
-            else if (Encoding == EIntegerEncoding.Quint)
-            {
-                TotalBits += (NumberVals * 7 + 2) / 3;
-            }
-            return TotalBits;
-        }
-
-        public static IntegerEncoded CreateEncoding(int MaxVal)
-        {
-            while (MaxVal > 0)
-            {
-                int Check = MaxVal + 1;
-
-                // Is maxVal a power of two?
-                if ((Check & (Check - 1)) == 0)
-                {
-                    return new IntegerEncoded(EIntegerEncoding.JustBits, BitArrayStream.PopCnt(MaxVal));
-                }
-
-                // Is maxVal of the type 3*2^n - 1?
-                if ((Check % 3 == 0) && ((Check / 3) & ((Check / 3) - 1)) == 0)
-                {
-                    return new IntegerEncoded(EIntegerEncoding.Trit, BitArrayStream.PopCnt(Check / 3 - 1));
-                }
-
-                // Is maxVal of the type 5*2^n - 1?
-                if ((Check % 5 == 0) && ((Check / 5) & ((Check / 5) - 1)) == 0)
-                {
-                    return new IntegerEncoded(EIntegerEncoding.Quint, BitArrayStream.PopCnt(Check / 5 - 1));
-                }
-
-                // Apparently it can't be represented with a bounded integer sequence...
-                // just iterate.
-                MaxVal--;
-            }
-
-            return new IntegerEncoded(EIntegerEncoding.JustBits, 0);
-        }
-
-        public static void DecodeTritBlock(
-            BitArrayStream       BitStream, 
-            List<IntegerEncoded> ListIntegerEncoded, 
-            int                  NumberBitsPerValue)
-        {
-            // Implement the algorithm in section C.2.12
-            int[] m = new int[5];
-            int[] t = new int[5];
-            int T;
-
-            // Read the trit encoded block according to
-            // table C.2.14
-            m[0] = BitStream.ReadBits(NumberBitsPerValue);
-            T    = BitStream.ReadBits(2);
-            m[1] = BitStream.ReadBits(NumberBitsPerValue);
-            T   |= BitStream.ReadBits(2) << 2;
-            m[2] = BitStream.ReadBits(NumberBitsPerValue);
-            T   |= BitStream.ReadBits(1) << 4;
-            m[3] = BitStream.ReadBits(NumberBitsPerValue);
-            T   |= BitStream.ReadBits(2) << 5;
-            m[4] = BitStream.ReadBits(NumberBitsPerValue);
-            T   |= BitStream.ReadBits(1) << 7;
-
-            int C = 0;
-
-            BitArrayStream Tb = new BitArrayStream(new BitArray(new int[] { T }));
-            if (Tb.ReadBits(2, 4) == 7)
-            {
-                C    = (Tb.ReadBits(5, 7) << 2) | Tb.ReadBits(0, 1);
-                t[4] = t[3] = 2;
-            }
-            else
-            {
-                C = Tb.ReadBits(0, 4);
-                if (Tb.ReadBits(5, 6) == 3)
-                {
-                    t[4] = 2;
-                    t[3] = Tb.ReadBit(7);
-                }
-                else
-                {
-                    t[4] = Tb.ReadBit(7);
-                    t[3] = Tb.ReadBits(5, 6);
-                }
-            }
-
-            BitArrayStream Cb = new BitArrayStream(new BitArray(new int[] { C }));
-            if (Cb.ReadBits(0, 1) == 3)
-            {
-                t[2] = 2;
-                t[1] = Cb.ReadBit(4);
-                t[0] = (Cb.ReadBit(3) << 1) | (Cb.ReadBit(2) & ~Cb.ReadBit(3));
-            }
-            else if (Cb.ReadBits(2, 3) == 3)
-            {
-                t[2] = 2;
-                t[1] = 2;
-                t[0] = Cb.ReadBits(0, 1);
-            }
-            else
-            {
-                t[2] = Cb.ReadBit(4);
-                t[1] = Cb.ReadBits(2, 3);
-                t[0] = (Cb.ReadBit(1) << 1) | (Cb.ReadBit(0) & ~Cb.ReadBit(1));
-            }
-
-            for (int i = 0; i < 5; i++)
-            {
-                IntegerEncoded IntEncoded = new IntegerEncoded(EIntegerEncoding.Trit, NumberBitsPerValue)
-                {
-                    BitValue  = m[i],
-                    TritValue = t[i]
-                };
-                ListIntegerEncoded.Add(IntEncoded);
-            }
-        }
-
-        public static void DecodeQuintBlock(
-            BitArrayStream       BitStream, 
-            List<IntegerEncoded> ListIntegerEncoded, 
-            int                  NumberBitsPerValue)
-        {
-            // Implement the algorithm in section C.2.12
-            int[] m = new int[3];
-            int[] q = new int[3];
-            int Q;
-
-            // Read the trit encoded block according to
-            // table C.2.15
-            m[0] = BitStream.ReadBits(NumberBitsPerValue);
-            Q    = BitStream.ReadBits(3);
-            m[1] = BitStream.ReadBits(NumberBitsPerValue);
-            Q   |= BitStream.ReadBits(2) << 3;
-            m[2] = BitStream.ReadBits(NumberBitsPerValue);
-            Q   |= BitStream.ReadBits(2) << 5;
-
-            BitArrayStream Qb = new BitArrayStream(new BitArray(new int[] { Q }));
-            if (Qb.ReadBits(1, 2) == 3 && Qb.ReadBits(5, 6) == 0)
-            {
-                q[0] = q[1] = 4;
-                q[2] = (Qb.ReadBit(0) << 2) | ((Qb.ReadBit(4) & ~Qb.ReadBit(0)) << 1) | (Qb.ReadBit(3) & ~Qb.ReadBit(0));
-            }
-            else
-            {
-                int C = 0;
-                if (Qb.ReadBits(1, 2) == 3)
-                {
-                    q[2] = 4;
-                    C    = (Qb.ReadBits(3, 4) << 3) | ((~Qb.ReadBits(5, 6) & 3) << 1) | Qb.ReadBit(0);
-                }
-                else
-                {
-                    q[2] = Qb.ReadBits(5, 6);
-                    C    = Qb.ReadBits(0, 4);
-                }
-
-                BitArrayStream Cb = new BitArrayStream(new BitArray(new int[] { C }));
-                if (Cb.ReadBits(0, 2) == 5)
-                {
-                    q[1] = 4;
-                    q[0] = Cb.ReadBits(3, 4);
-                }
-                else
-                {
-                    q[1] = Cb.ReadBits(3, 4);
-                    q[0] = Cb.ReadBits(0, 2);
-                }
-            }
-
-            for (int i = 0; i < 3; i++)
-            {
-                IntegerEncoded IntEncoded = new IntegerEncoded(EIntegerEncoding.Quint, NumberBitsPerValue)
-                {
-                    BitValue   = m[i],
-                    QuintValue = q[i]
-                };
-                ListIntegerEncoded.Add(IntEncoded);
-            }
-        }
-
-        public static void DecodeIntegerSequence(
-            List<IntegerEncoded> DecodeIntegerSequence, 
-            BitArrayStream       BitStream, 
-            int                  MaxRange, 
-            int                  NumberValues)
-        {
-            // Determine encoding parameters
-            IntegerEncoded IntEncoded = CreateEncoding(MaxRange);
-
-            // Start decoding
-            int NumberValuesDecoded = 0;
-            while (NumberValuesDecoded < NumberValues)
-            {
-                switch (IntEncoded.GetEncoding())
-                {
-                    case EIntegerEncoding.Quint:
-                    {
-                        DecodeQuintBlock(BitStream, DecodeIntegerSequence, IntEncoded.NumberBits);
-                        NumberValuesDecoded += 3;
-
-                        break;
-                    }
-
-                    case EIntegerEncoding.Trit:
-                    {
-                        DecodeTritBlock(BitStream, DecodeIntegerSequence, IntEncoded.NumberBits);
-                        NumberValuesDecoded += 5;
-
-                        break;
-                    }
-
-                    case EIntegerEncoding.JustBits:
-                    {
-                        IntEncoded.BitValue = BitStream.ReadBits(IntEncoded.NumberBits);
-                        DecodeIntegerSequence.Add(IntEncoded);
-                        NumberValuesDecoded++;
-
-                        break;
-                    }
-                }
-            }
-        }
-    }
-}
+using System.Collections;
+using System.Collections.Generic;
+
+namespace Ryujinx.Graphics.Texture
+{
+    public struct IntegerEncoded
+    {
+        public enum EIntegerEncoding
+        {
+            JustBits,
+            Quint,
+            Trit
+        }
+
+        EIntegerEncoding Encoding;
+        public int NumberBits { get; private set; }
+        public int BitValue   { get; private set; }
+        public int TritValue  { get; private set; }
+        public int QuintValue { get; private set; }
+
+        public IntegerEncoded(EIntegerEncoding _Encoding, int NumBits)
+        {
+            Encoding   = _Encoding;
+            NumberBits = NumBits;
+            BitValue   = 0;
+            TritValue  = 0;
+            QuintValue = 0;
+        }
+
+        public bool MatchesEncoding(IntegerEncoded Other)
+        {
+            return Encoding == Other.Encoding && NumberBits == Other.NumberBits;
+        }
+
+        public EIntegerEncoding GetEncoding()
+        {
+            return Encoding;
+        }
+
+        public int GetBitLength(int NumberVals)
+        {
+            int TotalBits = NumberBits * NumberVals;
+            if (Encoding == EIntegerEncoding.Trit)
+            {
+                TotalBits += (NumberVals * 8 + 4) / 5;
+            }
+            else if (Encoding == EIntegerEncoding.Quint)
+            {
+                TotalBits += (NumberVals * 7 + 2) / 3;
+            }
+            return TotalBits;
+        }
+
+        public static IntegerEncoded CreateEncoding(int MaxVal)
+        {
+            while (MaxVal > 0)
+            {
+                int Check = MaxVal + 1;
+
+                // Is maxVal a power of two?
+                if ((Check & (Check - 1)) == 0)
+                {
+                    return new IntegerEncoded(EIntegerEncoding.JustBits, BitArrayStream.PopCnt(MaxVal));
+                }
+
+                // Is maxVal of the type 3*2^n - 1?
+                if ((Check % 3 == 0) && ((Check / 3) & ((Check / 3) - 1)) == 0)
+                {
+                    return new IntegerEncoded(EIntegerEncoding.Trit, BitArrayStream.PopCnt(Check / 3 - 1));
+                }
+
+                // Is maxVal of the type 5*2^n - 1?
+                if ((Check % 5 == 0) && ((Check / 5) & ((Check / 5) - 1)) == 0)
+                {
+                    return new IntegerEncoded(EIntegerEncoding.Quint, BitArrayStream.PopCnt(Check / 5 - 1));
+                }
+
+                // Apparently it can't be represented with a bounded integer sequence...
+                // just iterate.
+                MaxVal--;
+            }
+
+            return new IntegerEncoded(EIntegerEncoding.JustBits, 0);
+        }
+
+        public static void DecodeTritBlock(
+            BitArrayStream       BitStream, 
+            List<IntegerEncoded> ListIntegerEncoded, 
+            int                  NumberBitsPerValue)
+        {
+            // Implement the algorithm in section C.2.12
+            int[] m = new int[5];
+            int[] t = new int[5];
+            int T;
+
+            // Read the trit encoded block according to
+            // table C.2.14
+            m[0] = BitStream.ReadBits(NumberBitsPerValue);
+            T    = BitStream.ReadBits(2);
+            m[1] = BitStream.ReadBits(NumberBitsPerValue);
+            T   |= BitStream.ReadBits(2) << 2;
+            m[2] = BitStream.ReadBits(NumberBitsPerValue);
+            T   |= BitStream.ReadBits(1) << 4;
+            m[3] = BitStream.ReadBits(NumberBitsPerValue);
+            T   |= BitStream.ReadBits(2) << 5;
+            m[4] = BitStream.ReadBits(NumberBitsPerValue);
+            T   |= BitStream.ReadBits(1) << 7;
+
+            int C = 0;
+
+            BitArrayStream Tb = new BitArrayStream(new BitArray(new int[] { T }));
+            if (Tb.ReadBits(2, 4) == 7)
+            {
+                C    = (Tb.ReadBits(5, 7) << 2) | Tb.ReadBits(0, 1);
+                t[4] = t[3] = 2;
+            }
+            else
+            {
+                C = Tb.ReadBits(0, 4);
+                if (Tb.ReadBits(5, 6) == 3)
+                {
+                    t[4] = 2;
+                    t[3] = Tb.ReadBit(7);
+                }
+                else
+                {
+                    t[4] = Tb.ReadBit(7);
+                    t[3] = Tb.ReadBits(5, 6);
+                }
+            }
+
+            BitArrayStream Cb = new BitArrayStream(new BitArray(new int[] { C }));
+            if (Cb.ReadBits(0, 1) == 3)
+            {
+                t[2] = 2;
+                t[1] = Cb.ReadBit(4);
+                t[0] = (Cb.ReadBit(3) << 1) | (Cb.ReadBit(2) & ~Cb.ReadBit(3));
+            }
+            else if (Cb.ReadBits(2, 3) == 3)
+            {
+                t[2] = 2;
+                t[1] = 2;
+                t[0] = Cb.ReadBits(0, 1);
+            }
+            else
+            {
+                t[2] = Cb.ReadBit(4);
+                t[1] = Cb.ReadBits(2, 3);
+                t[0] = (Cb.ReadBit(1) << 1) | (Cb.ReadBit(0) & ~Cb.ReadBit(1));
+            }
+
+            for (int i = 0; i < 5; i++)
+            {
+                IntegerEncoded IntEncoded = new IntegerEncoded(EIntegerEncoding.Trit, NumberBitsPerValue)
+                {
+                    BitValue  = m[i],
+                    TritValue = t[i]
+                };
+                ListIntegerEncoded.Add(IntEncoded);
+            }
+        }
+
+        public static void DecodeQuintBlock(
+            BitArrayStream       BitStream, 
+            List<IntegerEncoded> ListIntegerEncoded, 
+            int                  NumberBitsPerValue)
+        {
+            // Implement the algorithm in section C.2.12
+            int[] m = new int[3];
+            int[] q = new int[3];
+            int Q;
+
+            // Read the trit encoded block according to
+            // table C.2.15
+            m[0] = BitStream.ReadBits(NumberBitsPerValue);
+            Q    = BitStream.ReadBits(3);
+            m[1] = BitStream.ReadBits(NumberBitsPerValue);
+            Q   |= BitStream.ReadBits(2) << 3;
+            m[2] = BitStream.ReadBits(NumberBitsPerValue);
+            Q   |= BitStream.ReadBits(2) << 5;
+
+            BitArrayStream Qb = new BitArrayStream(new BitArray(new int[] { Q }));
+            if (Qb.ReadBits(1, 2) == 3 && Qb.ReadBits(5, 6) == 0)
+            {
+                q[0] = q[1] = 4;
+                q[2] = (Qb.ReadBit(0) << 2) | ((Qb.ReadBit(4) & ~Qb.ReadBit(0)) << 1) | (Qb.ReadBit(3) & ~Qb.ReadBit(0));
+            }
+            else
+            {
+                int C = 0;
+                if (Qb.ReadBits(1, 2) == 3)
+                {
+                    q[2] = 4;
+                    C    = (Qb.ReadBits(3, 4) << 3) | ((~Qb.ReadBits(5, 6) & 3) << 1) | Qb.ReadBit(0);
+                }
+                else
+                {
+                    q[2] = Qb.ReadBits(5, 6);
+                    C    = Qb.ReadBits(0, 4);
+                }
+
+                BitArrayStream Cb = new BitArrayStream(new BitArray(new int[] { C }));
+                if (Cb.ReadBits(0, 2) == 5)
+                {
+                    q[1] = 4;
+                    q[0] = Cb.ReadBits(3, 4);
+                }
+                else
+                {
+                    q[1] = Cb.ReadBits(3, 4);
+                    q[0] = Cb.ReadBits(0, 2);
+                }
+            }
+
+            for (int i = 0; i < 3; i++)
+            {
+                IntegerEncoded IntEncoded = new IntegerEncoded(EIntegerEncoding.Quint, NumberBitsPerValue)
+                {
+                    BitValue   = m[i],
+                    QuintValue = q[i]
+                };
+                ListIntegerEncoded.Add(IntEncoded);
+            }
+        }
+
+        public static void DecodeIntegerSequence(
+            List<IntegerEncoded> DecodeIntegerSequence, 
+            BitArrayStream       BitStream, 
+            int                  MaxRange, 
+            int                  NumberValues)
+        {
+            // Determine encoding parameters
+            IntegerEncoded IntEncoded = CreateEncoding(MaxRange);
+
+            // Start decoding
+            int NumberValuesDecoded = 0;
+            while (NumberValuesDecoded < NumberValues)
+            {
+                switch (IntEncoded.GetEncoding())
+                {
+                    case EIntegerEncoding.Quint:
+                    {
+                        DecodeQuintBlock(BitStream, DecodeIntegerSequence, IntEncoded.NumberBits);
+                        NumberValuesDecoded += 3;
+
+                        break;
+                    }
+
+                    case EIntegerEncoding.Trit:
+                    {
+                        DecodeTritBlock(BitStream, DecodeIntegerSequence, IntEncoded.NumberBits);
+                        NumberValuesDecoded += 5;
+
+                        break;
+                    }
+
+                    case EIntegerEncoding.JustBits:
+                    {
+                        IntEncoded.BitValue = BitStream.ReadBits(IntEncoded.NumberBits);
+                        DecodeIntegerSequence.Add(IntEncoded);
+                        NumberValuesDecoded++;
+
+                        break;
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/Ryujinx.HLE/Gpu/Texture/LinearSwizzle.cs b/Ryujinx.Graphics/Texture/LinearSwizzle.cs
similarity index 90%
rename from Ryujinx.HLE/Gpu/Texture/LinearSwizzle.cs
rename to Ryujinx.Graphics/Texture/LinearSwizzle.cs
index 720f78322..ef468e27b 100644
--- a/Ryujinx.HLE/Gpu/Texture/LinearSwizzle.cs
+++ b/Ryujinx.Graphics/Texture/LinearSwizzle.cs
@@ -1,4 +1,4 @@
-namespace Ryujinx.HLE.Gpu.Texture
+namespace Ryujinx.Graphics.Texture
 {
     class LinearSwizzle : ISwizzle
     {
diff --git a/Ryujinx.HLE/Gpu/Texture/TextureFactory.cs b/Ryujinx.Graphics/Texture/TextureFactory.cs
similarity index 91%
rename from Ryujinx.HLE/Gpu/Texture/TextureFactory.cs
rename to Ryujinx.Graphics/Texture/TextureFactory.cs
index 0ef33d3b7..fa7a0f80e 100644
--- a/Ryujinx.HLE/Gpu/Texture/TextureFactory.cs
+++ b/Ryujinx.Graphics/Texture/TextureFactory.cs
@@ -1,8 +1,8 @@
 using Ryujinx.Graphics.Gal;
-using Ryujinx.HLE.Gpu.Memory;
+using Ryujinx.Graphics.Memory;
 using System;
 
-namespace Ryujinx.HLE.Gpu.Texture
+namespace Ryujinx.Graphics.Texture
 {
     static class TextureFactory
     {
@@ -10,12 +10,7 @@ namespace Ryujinx.HLE.Gpu.Texture
         {
             int[] Tic = ReadWords(Vmm, TicPosition, 8);
 
-            GalTextureType RType = (GalTextureType)((Tic[0] >> 7)  & 7);
-            GalTextureType GType = (GalTextureType)((Tic[0] >> 10) & 7);
-            GalTextureType BType = (GalTextureType)((Tic[0] >> 13) & 7);
-            GalTextureType AType = (GalTextureType)((Tic[0] >> 16) & 7);
-
-            GalImageFormat Format = ImageFormatConverter.ConvertTexture((GalTextureFormat)(Tic[0] & 0x7f), RType, GType, BType, AType);
+            GalImageFormat Format = GetImageFormat(Tic);
 
             GalTextureSource XSource = (GalTextureSource)((Tic[0] >> 19) & 7);
             GalTextureSource YSource = (GalTextureSource)((Tic[0] >> 22) & 7);
@@ -39,7 +34,7 @@ namespace Ryujinx.HLE.Gpu.Texture
         {
             int[] Tic = ReadWords(Vmm, TicPosition, 8);
 
-            GalTextureFormat Format = (GalTextureFormat)(Tic[0] & 0x7f);
+            GalImageFormat Format = GetImageFormat(Tic);
 
             long TextureAddress = (uint)Tic[1];
 
@@ -110,6 +105,18 @@ namespace Ryujinx.HLE.Gpu.Texture
                 BorderColor);
         }
 
+        private static GalImageFormat GetImageFormat(int[] Tic)
+        {
+            GalTextureType RType = (GalTextureType)((Tic[0] >> 7) & 7);
+            GalTextureType GType = (GalTextureType)((Tic[0] >> 10) & 7);
+            GalTextureType BType = (GalTextureType)((Tic[0] >> 13) & 7);
+            GalTextureType AType = (GalTextureType)((Tic[0] >> 16) & 7);
+
+            GalTextureFormat Format = (GalTextureFormat)(Tic[0] & 0x7f);
+
+            return ImageUtils.ConvertTexture(Format, RType, GType, BType, AType);
+        }
+
         private static int[] ReadWords(NvGpuVmm Vmm, long Position, int Count)
         {
             int[] Words = new int[Count];
diff --git a/Ryujinx.Graphics/Texture/TextureHelper.cs b/Ryujinx.Graphics/Texture/TextureHelper.cs
new file mode 100644
index 000000000..8130ab41a
--- /dev/null
+++ b/Ryujinx.Graphics/Texture/TextureHelper.cs
@@ -0,0 +1,45 @@
+using ChocolArm64.Memory;
+using Ryujinx.Graphics.Gal;
+using Ryujinx.Graphics.Memory;
+using System;
+
+namespace Ryujinx.Graphics.Texture
+{
+    static class TextureHelper
+    {
+        public static ISwizzle GetSwizzle(TextureInfo Texture, int BlockWidth, int Bpp)
+        {
+            int Width = (Texture.Width + (BlockWidth - 1)) / BlockWidth;
+
+            int AlignMask = Texture.TileWidth * (64 / Bpp) - 1;
+
+            Width = (Width + AlignMask) & ~AlignMask;
+
+            switch (Texture.Swizzle)
+            {
+                case TextureSwizzle._1dBuffer:
+                case TextureSwizzle.Pitch:
+                case TextureSwizzle.PitchColorKey:
+                     return new LinearSwizzle(Texture.Pitch, Bpp);
+
+                case TextureSwizzle.BlockLinear:
+                case TextureSwizzle.BlockLinearColorKey:
+                    return new BlockLinearSwizzle(Width, Bpp, Texture.BlockHeight);
+            }
+
+            throw new NotImplementedException(Texture.Swizzle.ToString());
+        }
+
+        public static (AMemory Memory, long Position) GetMemoryAndPosition(
+            IAMemory Memory,
+            long     Position)
+        {
+            if (Memory is NvGpuVmm Vmm)
+            {
+                return (Vmm.Memory, Vmm.GetPhysicalAddress(Position));
+            }
+
+            return ((AMemory)Memory, Position);
+        }
+    }
+}
diff --git a/Ryujinx.HLE/Gpu/Texture/TextureInfo.cs b/Ryujinx.Graphics/Texture/TextureInfo.cs
similarity index 86%
rename from Ryujinx.HLE/Gpu/Texture/TextureInfo.cs
rename to Ryujinx.Graphics/Texture/TextureInfo.cs
index 2a98ce00f..66445dcce 100644
--- a/Ryujinx.HLE/Gpu/Texture/TextureInfo.cs
+++ b/Ryujinx.Graphics/Texture/TextureInfo.cs
@@ -1,8 +1,8 @@
 using Ryujinx.Graphics.Gal;
 
-namespace Ryujinx.HLE.Gpu.Texture
+namespace Ryujinx.Graphics.Texture
 {
-    struct TextureInfo
+    public struct TextureInfo
     {
         public long Position { get; private set; }
 
@@ -15,7 +15,7 @@ namespace Ryujinx.HLE.Gpu.Texture
 
         public TextureSwizzle Swizzle { get; private set; }
 
-        public GalTextureFormat Format { get; private set; }
+        public GalImageFormat Format { get; private set; }
 
         public TextureInfo(
             long Position,
@@ -34,7 +34,7 @@ namespace Ryujinx.HLE.Gpu.Texture
 
             Swizzle = TextureSwizzle.BlockLinear;
 
-            Format = GalTextureFormat.A8B8G8R8;
+            Format = GalImageFormat.A8B8G8R8 | GalImageFormat.Unorm;
         }
 
         public TextureInfo(
@@ -45,7 +45,7 @@ namespace Ryujinx.HLE.Gpu.Texture
             int              BlockHeight,
             int              TileWidth,
             TextureSwizzle   Swizzle,
-            GalTextureFormat Format)
+            GalImageFormat   Format)
         {
             this.Position     = Position;
             this.Width        = Width;
diff --git a/Ryujinx.HLE/Gpu/Texture/TextureReader.cs b/Ryujinx.Graphics/Texture/TextureReader.cs
similarity index 63%
rename from Ryujinx.HLE/Gpu/Texture/TextureReader.cs
rename to Ryujinx.Graphics/Texture/TextureReader.cs
index d293bf9f1..dbaed1a8e 100644
--- a/Ryujinx.HLE/Gpu/Texture/TextureReader.cs
+++ b/Ryujinx.Graphics/Texture/TextureReader.cs
@@ -2,58 +2,20 @@ using ChocolArm64.Memory;
 using Ryujinx.Graphics.Gal;
 using System;
 
-namespace Ryujinx.HLE.Gpu.Texture
+namespace Ryujinx.Graphics.Texture
 {
-    static class TextureReader
+    delegate byte[] TextureReaderDelegate(IAMemory Memory, TextureInfo Texture);
+
+    public static class TextureReader
     {
         public static byte[] Read(IAMemory Memory, TextureInfo Texture)
         {
-            switch (Texture.Format)
-            {
-                case GalTextureFormat.R32G32B32A32: return Read16Bpp                 (Memory, Texture);
-                case GalTextureFormat.R16G16B16A16: return Read8Bpp                  (Memory, Texture);
-                case GalTextureFormat.R32G32:       return Read8Bpp                  (Memory, Texture);
-                case GalTextureFormat.A8B8G8R8:     return Read4Bpp                  (Memory, Texture);
-                case GalTextureFormat.A2B10G10R10:  return Read4Bpp                  (Memory, Texture);
-                case GalTextureFormat.R32:          return Read4Bpp                  (Memory, Texture);
-                case GalTextureFormat.BF10GF11RF11: return Read4Bpp                  (Memory, Texture);
-                case GalTextureFormat.Z24S8:        return Read4Bpp                  (Memory, Texture);
-                case GalTextureFormat.A1B5G5R5:     return Read5551                  (Memory, Texture);
-                case GalTextureFormat.B5G6R5:       return Read565                   (Memory, Texture);
-                case GalTextureFormat.A4B4G4R4:     return Read2Bpp                  (Memory, Texture);
-                case GalTextureFormat.G8R8:         return Read2Bpp                  (Memory, Texture);
-                case GalTextureFormat.R16:          return Read2Bpp                  (Memory, Texture);
-                case GalTextureFormat.R8:           return Read1Bpp                  (Memory, Texture);
-                case GalTextureFormat.BC6H_SF16:    return Read16BptCompressedTexture(Memory, Texture, 4, 4);
-                case GalTextureFormat.BC6H_UF16:    return Read16BptCompressedTexture(Memory, Texture, 4, 4);
-                case GalTextureFormat.BC7U:         return Read16BptCompressedTexture(Memory, Texture, 4, 4);
-                case GalTextureFormat.BC1:          return Read8Bpt4x4               (Memory, Texture);
-                case GalTextureFormat.BC2:          return Read16BptCompressedTexture(Memory, Texture, 4, 4);
-                case GalTextureFormat.BC3:          return Read16BptCompressedTexture(Memory, Texture, 4, 4);
-                case GalTextureFormat.BC4:          return Read8Bpt4x4               (Memory, Texture);
-                case GalTextureFormat.BC5:          return Read16BptCompressedTexture(Memory, Texture, 4, 4);
-                case GalTextureFormat.ZF32:         return Read4Bpp                  (Memory, Texture);
-                case GalTextureFormat.ZF32_X24S8:   return Read8Bpp                  (Memory, Texture);
-                case GalTextureFormat.Astc2D4x4:    return Read16BptCompressedTexture(Memory, Texture, 4, 4);
-                case GalTextureFormat.Astc2D5x5:    return Read16BptCompressedTexture(Memory, Texture, 5, 5);
-                case GalTextureFormat.Astc2D6x6:    return Read16BptCompressedTexture(Memory, Texture, 6, 6);
-                case GalTextureFormat.Astc2D8x8:    return Read16BptCompressedTexture(Memory, Texture, 8, 8);
-                case GalTextureFormat.Astc2D10x10:  return Read16BptCompressedTexture(Memory, Texture, 10, 10);
-                case GalTextureFormat.Astc2D12x12:  return Read16BptCompressedTexture(Memory, Texture, 12, 12);
-                case GalTextureFormat.Astc2D5x4:    return Read16BptCompressedTexture(Memory, Texture, 5, 4);
-                case GalTextureFormat.Astc2D6x5:    return Read16BptCompressedTexture(Memory, Texture, 6, 5);
-                case GalTextureFormat.Astc2D8x6:    return Read16BptCompressedTexture(Memory, Texture, 8, 6);
-                case GalTextureFormat.Astc2D10x8:   return Read16BptCompressedTexture(Memory, Texture, 10, 8);
-                case GalTextureFormat.Astc2D12x10:  return Read16BptCompressedTexture(Memory, Texture, 12, 10);
-                case GalTextureFormat.Astc2D8x5:    return Read16BptCompressedTexture(Memory, Texture, 8, 5);
-                case GalTextureFormat.Astc2D10x5:   return Read16BptCompressedTexture(Memory, Texture, 10, 5);
-                case GalTextureFormat.Astc2D10x6:   return Read16BptCompressedTexture(Memory, Texture, 10, 6);
-             }
+            TextureReaderDelegate Reader = ImageUtils.GetReader(Texture.Format);
 
-            throw new NotImplementedException("0x" + ((int)Texture.Format).ToString("x2"));
+            return Reader(Memory, Texture);
         }
 
-        private unsafe static byte[] Read1Bpp(IAMemory Memory, TextureInfo Texture)
+        internal unsafe static byte[] Read1Bpp(IAMemory Memory, TextureInfo Texture)
         {
             int Width  = Texture.Width;
             int Height = Texture.Height;
@@ -86,7 +48,7 @@ namespace Ryujinx.HLE.Gpu.Texture
             return Output;
         }
 
-        private unsafe static byte[] Read5551(IAMemory Memory, TextureInfo Texture)
+        internal unsafe static byte[] Read5551(IAMemory Memory, TextureInfo Texture)
         {
             int Width  = Texture.Width;
             int Height = Texture.Height;
@@ -124,7 +86,7 @@ namespace Ryujinx.HLE.Gpu.Texture
             return Output;
         }
 
-        private unsafe static byte[] Read565(IAMemory Memory, TextureInfo Texture)
+        internal unsafe static byte[] Read565(IAMemory Memory, TextureInfo Texture)
         {
             int Width  = Texture.Width;
             int Height = Texture.Height;
@@ -161,7 +123,7 @@ namespace Ryujinx.HLE.Gpu.Texture
             return Output;
         }
 
-        private unsafe static byte[] Read2Bpp(IAMemory Memory, TextureInfo Texture)
+        internal unsafe static byte[] Read2Bpp(IAMemory Memory, TextureInfo Texture)
         {
             int Width  = Texture.Width;
             int Height = Texture.Height;
@@ -194,7 +156,7 @@ namespace Ryujinx.HLE.Gpu.Texture
             return Output;
         }
 
-        private unsafe static byte[] Read4Bpp(IAMemory Memory, TextureInfo Texture)
+        internal unsafe static byte[] Read4Bpp(IAMemory Memory, TextureInfo Texture)
         {
             int Width  = Texture.Width;
             int Height = Texture.Height;
@@ -227,7 +189,7 @@ namespace Ryujinx.HLE.Gpu.Texture
             return Output;
         }
 
-        private unsafe static byte[] Read8Bpp(IAMemory Memory, TextureInfo Texture)
+        internal unsafe static byte[] Read8Bpp(IAMemory Memory, TextureInfo Texture)
         {
             int Width  = Texture.Width;
             int Height = Texture.Height;
@@ -260,7 +222,7 @@ namespace Ryujinx.HLE.Gpu.Texture
             return Output;
         }
 
-        private unsafe static byte[] Read16Bpp(IAMemory Memory, TextureInfo Texture)
+        internal unsafe static byte[] Read16Bpp(IAMemory Memory, TextureInfo Texture)
         {
             int Width  = Texture.Width;
             int Height = Texture.Height;
@@ -295,7 +257,7 @@ namespace Ryujinx.HLE.Gpu.Texture
             return Output;
         }
 
-        private unsafe static byte[] Read8Bpt4x4(IAMemory Memory, TextureInfo Texture)
+        internal unsafe static byte[] Read8Bpt4x4(IAMemory Memory, TextureInfo Texture)
         {
             int Width  = (Texture.Width  + 3) / 4;
             int Height = (Texture.Height + 3) / 4;
@@ -328,7 +290,7 @@ namespace Ryujinx.HLE.Gpu.Texture
             return Output;
         }
 
-        private unsafe static byte[] Read16BptCompressedTexture(IAMemory Memory, TextureInfo Texture, int BlockWidth, int BlockHeight)
+        internal unsafe static byte[] Read16BptCompressedTexture(IAMemory Memory, TextureInfo Texture, int BlockWidth, int BlockHeight)
         {
             int Width  = (Texture.Width  + (BlockWidth - 1)) / BlockWidth;
             int Height = (Texture.Height + (BlockHeight - 1)) / BlockHeight;
@@ -362,5 +324,75 @@ namespace Ryujinx.HLE.Gpu.Texture
 
             return Output;
         }
+
+        internal static byte[] Read16BptCompressedTexture4x4(IAMemory Memory, TextureInfo Texture)
+        {
+            return Read16BptCompressedTexture(Memory, Texture, 4, 4);
+        }
+
+        internal static byte[] Read16BptCompressedTexture5x5(IAMemory Memory, TextureInfo Texture)
+        {
+            return Read16BptCompressedTexture(Memory, Texture, 5, 5);
+        }
+
+        internal static byte[] Read16BptCompressedTexture6x6(IAMemory Memory, TextureInfo Texture)
+        {
+            return Read16BptCompressedTexture(Memory, Texture, 6, 6);
+        }
+
+        internal static byte[] Read16BptCompressedTexture8x8(IAMemory Memory, TextureInfo Texture)
+        {
+            return Read16BptCompressedTexture(Memory, Texture, 8, 8);
+        }
+
+        internal static byte[] Read16BptCompressedTexture10x10(IAMemory Memory, TextureInfo Texture)
+        {
+            return Read16BptCompressedTexture(Memory, Texture, 10, 10);
+        }
+
+        internal static byte[] Read16BptCompressedTexture12x12(IAMemory Memory, TextureInfo Texture)
+        {
+            return Read16BptCompressedTexture(Memory, Texture, 12, 12);
+        }
+
+        internal static byte[] Read16BptCompressedTexture5x4(IAMemory Memory, TextureInfo Texture)
+        {
+            return Read16BptCompressedTexture(Memory, Texture, 5, 4);
+        }
+
+        internal static byte[] Read16BptCompressedTexture6x5(IAMemory Memory, TextureInfo Texture)
+        {
+            return Read16BptCompressedTexture(Memory, Texture, 6, 5);
+        }
+
+        internal static byte[] Read16BptCompressedTexture8x6(IAMemory Memory, TextureInfo Texture)
+        {
+            return Read16BptCompressedTexture(Memory, Texture, 8, 6);
+        }
+
+        internal static byte[] Read16BptCompressedTexture10x8(IAMemory Memory, TextureInfo Texture)
+        {
+            return Read16BptCompressedTexture(Memory, Texture, 10, 8);
+        }
+
+        internal static byte[] Read16BptCompressedTexture12x10(IAMemory Memory, TextureInfo Texture)
+        {
+            return Read16BptCompressedTexture(Memory, Texture, 12, 10);
+        }
+
+        internal static byte[] Read16BptCompressedTexture8x5(IAMemory Memory, TextureInfo Texture)
+        {
+            return Read16BptCompressedTexture(Memory, Texture, 5, 5);
+        }
+
+        internal static byte[] Read16BptCompressedTexture10x5(IAMemory Memory, TextureInfo Texture)
+        {
+            return Read16BptCompressedTexture(Memory, Texture, 10, 5);
+        }
+
+        internal static byte[] Read16BptCompressedTexture10x6(IAMemory Memory, TextureInfo Texture)
+        {
+            return Read16BptCompressedTexture(Memory, Texture, 10, 6);
+        }
     }
 }
diff --git a/Ryujinx.HLE/Gpu/Texture/TextureSwizzle.cs b/Ryujinx.Graphics/Texture/TextureSwizzle.cs
similarity index 72%
rename from Ryujinx.HLE/Gpu/Texture/TextureSwizzle.cs
rename to Ryujinx.Graphics/Texture/TextureSwizzle.cs
index 076df97ab..c67a5367e 100644
--- a/Ryujinx.HLE/Gpu/Texture/TextureSwizzle.cs
+++ b/Ryujinx.Graphics/Texture/TextureSwizzle.cs
@@ -1,6 +1,6 @@
-namespace Ryujinx.HLE.Gpu.Texture
+namespace Ryujinx.Graphics.Texture
 {
-    enum TextureSwizzle
+    public enum TextureSwizzle
     {
         _1dBuffer           = 0,
         PitchColorKey       = 1,
diff --git a/Ryujinx.HLE/Gpu/Texture/TextureWriter.cs b/Ryujinx.Graphics/Texture/TextureWriter.cs
similarity index 90%
rename from Ryujinx.HLE/Gpu/Texture/TextureWriter.cs
rename to Ryujinx.Graphics/Texture/TextureWriter.cs
index 6c3dda6be..16e78c56f 100644
--- a/Ryujinx.HLE/Gpu/Texture/TextureWriter.cs
+++ b/Ryujinx.Graphics/Texture/TextureWriter.cs
@@ -1,6 +1,8 @@
 using ChocolArm64.Memory;
+using Ryujinx.Graphics.Gal;
+using Ryujinx.Graphics.Memory;
 
-namespace Ryujinx.HLE.Gpu.Texture
+namespace Ryujinx.Graphics.Texture
 {
     static class TextureWriter
     {
diff --git a/Ryujinx.HLE/Gpu/Texture/TextureHelper.cs b/Ryujinx.HLE/Gpu/Texture/TextureHelper.cs
deleted file mode 100644
index 2683174d8..000000000
--- a/Ryujinx.HLE/Gpu/Texture/TextureHelper.cs
+++ /dev/null
@@ -1,204 +0,0 @@
-using ChocolArm64.Memory;
-using Ryujinx.Graphics.Gal;
-using Ryujinx.HLE.Gpu.Memory;
-using System;
-
-namespace Ryujinx.HLE.Gpu.Texture
-{
-    static class TextureHelper
-    {
-        public static ISwizzle GetSwizzle(TextureInfo Texture, int BlockWidth, int Bpp)
-        {
-            int Width = (Texture.Width + (BlockWidth - 1)) / BlockWidth;
-
-            int AlignMask = Texture.TileWidth * (64 / Bpp) - 1;
-
-            Width = (Width + AlignMask) & ~AlignMask;
-
-            switch (Texture.Swizzle)
-            {
-                case TextureSwizzle._1dBuffer:
-                case TextureSwizzle.Pitch:
-                case TextureSwizzle.PitchColorKey:
-                     return new LinearSwizzle(Texture.Pitch, Bpp);
-
-                case TextureSwizzle.BlockLinear:
-                case TextureSwizzle.BlockLinearColorKey:
-                    return new BlockLinearSwizzle(Width, Bpp, Texture.BlockHeight);
-            }
-
-            throw new NotImplementedException(Texture.Swizzle.ToString());
-        }
-
-        public static int GetTextureSize(GalImage Image)
-        {
-            switch (Image.Format)
-            {
-                case GalImageFormat.R32G32B32A32_SFLOAT:
-                case GalImageFormat.R32G32B32A32_SINT:
-                case GalImageFormat.R32G32B32A32_UINT:
-                    return Image.Width * Image.Height * 16;
-
-                case GalImageFormat.R16G16B16A16_SFLOAT:
-                case GalImageFormat.R16G16B16A16_SINT:
-                case GalImageFormat.R16G16B16A16_SNORM:
-                case GalImageFormat.R16G16B16A16_UINT:
-                case GalImageFormat.R16G16B16A16_UNORM:
-                case GalImageFormat.D32_SFLOAT_S8_UINT:
-                case GalImageFormat.R32G32_SFLOAT:
-                case GalImageFormat.R32G32_SINT:
-                case GalImageFormat.R32G32_UINT:
-                    return Image.Width * Image.Height * 8;
-
-                case GalImageFormat.A8B8G8R8_SINT_PACK32:
-                case GalImageFormat.A8B8G8R8_SNORM_PACK32:
-                case GalImageFormat.A8B8G8R8_UINT_PACK32:
-                case GalImageFormat.A8B8G8R8_UNORM_PACK32:
-                case GalImageFormat.A8B8G8R8_SRGB_PACK32:
-                case GalImageFormat.A2B10G10R10_SINT_PACK32:
-                case GalImageFormat.A2B10G10R10_SNORM_PACK32:
-                case GalImageFormat.A2B10G10R10_UINT_PACK32:
-                case GalImageFormat.A2B10G10R10_UNORM_PACK32:
-                case GalImageFormat.R16G16_SFLOAT:
-                case GalImageFormat.R16G16_SINT:
-                case GalImageFormat.R16G16_SNORM:
-                case GalImageFormat.R16G16_UINT:
-                case GalImageFormat.R16G16_UNORM:
-                case GalImageFormat.R32_SFLOAT:
-                case GalImageFormat.R32_SINT:
-                case GalImageFormat.R32_UINT:
-                case GalImageFormat.D32_SFLOAT:
-                case GalImageFormat.B10G11R11_UFLOAT_PACK32:
-                case GalImageFormat.D24_UNORM_S8_UINT:
-                    return Image.Width * Image.Height * 4;
-
-                case GalImageFormat.B4G4R4A4_UNORM_PACK16:
-                case GalImageFormat.A1R5G5B5_UNORM_PACK16:
-                case GalImageFormat.B5G6R5_UNORM_PACK16:
-                case GalImageFormat.R8G8_SINT:
-                case GalImageFormat.R8G8_SNORM:
-                case GalImageFormat.R8G8_UINT:
-                case GalImageFormat.R8G8_UNORM:
-                case GalImageFormat.R16_SFLOAT:
-                case GalImageFormat.R16_SINT:
-                case GalImageFormat.R16_SNORM:
-                case GalImageFormat.R16_UINT:
-                case GalImageFormat.R16_UNORM:
-                case GalImageFormat.D16_UNORM:
-                    return Image.Width * Image.Height * 2;
-
-                case GalImageFormat.R8_SINT:
-                case GalImageFormat.R8_SNORM:
-                case GalImageFormat.R8_UINT:
-                case GalImageFormat.R8_UNORM:
-                    return Image.Width * Image.Height;
-
-                case GalImageFormat.BC1_RGBA_UNORM_BLOCK:
-                case GalImageFormat.BC4_SNORM_BLOCK:
-                case GalImageFormat.BC4_UNORM_BLOCK:
-                {
-                    return CompressedTextureSize(Image.Width, Image.Height, 4, 4, 8);
-                }
-
-                case GalImageFormat.BC6H_SFLOAT_BLOCK:
-                case GalImageFormat.BC6H_UFLOAT_BLOCK:
-                case GalImageFormat.BC7_UNORM_BLOCK:
-                case GalImageFormat.BC2_UNORM_BLOCK:
-                case GalImageFormat.BC3_UNORM_BLOCK:
-                case GalImageFormat.BC5_SNORM_BLOCK:
-                case GalImageFormat.BC5_UNORM_BLOCK:
-                case GalImageFormat.ASTC_4x4_UNORM_BLOCK:
-                {
-                    return CompressedTextureSize(Image.Width, Image.Height, 4, 4, 16);
-                }
-
-                case GalImageFormat.ASTC_5x5_UNORM_BLOCK:
-                {
-                    return CompressedTextureSize(Image.Width, Image.Height, 5, 5, 16);
-                }
-
-                case GalImageFormat.ASTC_6x6_UNORM_BLOCK:
-                {
-                    return CompressedTextureSize(Image.Width, Image.Height, 6, 6, 16);
-                }
-
-                case GalImageFormat.ASTC_8x8_UNORM_BLOCK:
-                {
-                    return CompressedTextureSize(Image.Width, Image.Height, 8, 8, 16);
-                }
-
-                case GalImageFormat.ASTC_10x10_UNORM_BLOCK:
-                {
-                    return CompressedTextureSize(Image.Width, Image.Height, 10, 10, 16);
-                }
-
-                case GalImageFormat.ASTC_12x12_UNORM_BLOCK:
-                {
-                    return CompressedTextureSize(Image.Width, Image.Height, 12, 12, 16);
-                }
-
-                case GalImageFormat.ASTC_5x4_UNORM_BLOCK:
-                {
-                    return CompressedTextureSize(Image.Width, Image.Height, 5, 4, 16);
-                }
-
-                case GalImageFormat.ASTC_6x5_UNORM_BLOCK:
-                {
-                    return CompressedTextureSize(Image.Width, Image.Height, 6, 5, 16);
-                }
-
-                case GalImageFormat.ASTC_8x6_UNORM_BLOCK:
-                {
-                    return CompressedTextureSize(Image.Width, Image.Height, 8, 6, 16);
-                }
-
-                case GalImageFormat.ASTC_10x8_UNORM_BLOCK:
-                {
-                    return CompressedTextureSize(Image.Width, Image.Height, 10, 8, 16);
-                }
-
-                case GalImageFormat.ASTC_12x10_UNORM_BLOCK:
-                {
-                    return CompressedTextureSize(Image.Width, Image.Height, 12, 10, 16);
-                }
-
-                case GalImageFormat.ASTC_8x5_UNORM_BLOCK:
-                {
-                    return CompressedTextureSize(Image.Width, Image.Height, 8, 5, 16);
-                }
-
-                case GalImageFormat.ASTC_10x5_UNORM_BLOCK:
-                {
-                    return CompressedTextureSize(Image.Width, Image.Height, 10, 5, 16);
-                }
-
-                case GalImageFormat.ASTC_10x6_UNORM_BLOCK:
-                {
-                    return CompressedTextureSize(Image.Width, Image.Height, 10, 6, 16);
-                }
-            }
-
-            throw new NotImplementedException(Image.Format.ToString());
-        }
-
-        public static int CompressedTextureSize(int TextureWidth, int TextureHeight, int BlockWidth, int BlockHeight, int Bpb)
-        {
-            int W = (TextureWidth  + (BlockWidth - 1)) / BlockWidth;
-            int H = (TextureHeight + (BlockHeight - 1)) / BlockHeight;
-
-            return W * H * Bpb;
-        }
-
-        public static (AMemory Memory, long Position) GetMemoryAndPosition(
-            IAMemory Memory,
-            long     Position)
-        {
-            if (Memory is NvGpuVmm Vmm)
-            {
-                return (Vmm.Memory, Vmm.GetPhysicalAddress(Position));
-            }
-
-            return ((AMemory)Memory, Position);
-        }
-    }
-}
diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvGpuAS/NvGpuASCtx.cs b/Ryujinx.HLE/HOS/Services/Nv/NvGpuAS/NvGpuASCtx.cs
index 7b6a8676b..70275b2a9 100644
--- a/Ryujinx.HLE/HOS/Services/Nv/NvGpuAS/NvGpuASCtx.cs
+++ b/Ryujinx.HLE/HOS/Services/Nv/NvGpuAS/NvGpuASCtx.cs
@@ -1,4 +1,4 @@
-using Ryujinx.HLE.Gpu.Memory;
+using Ryujinx.Graphics.Memory;
 using System.Collections.Generic;
 
 namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuAS
diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvGpuAS/NvGpuASIoctl.cs b/Ryujinx.HLE/HOS/Services/Nv/NvGpuAS/NvGpuASIoctl.cs
index c9be18ead..95eb5b986 100644
--- a/Ryujinx.HLE/HOS/Services/Nv/NvGpuAS/NvGpuASIoctl.cs
+++ b/Ryujinx.HLE/HOS/Services/Nv/NvGpuAS/NvGpuASIoctl.cs
@@ -1,5 +1,5 @@
 using ChocolArm64.Memory;
-using Ryujinx.HLE.Gpu.Memory;
+using Ryujinx.Graphics.Memory;
 using Ryujinx.HLE.HOS.Services.Nv.NvMap;
 using Ryujinx.HLE.Logging;
 using System;
diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs b/Ryujinx.HLE/HOS/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs
index 842447f3f..f4ed48217 100644
--- a/Ryujinx.HLE/HOS/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs
+++ b/Ryujinx.HLE/HOS/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs
@@ -1,5 +1,5 @@
 using ChocolArm64.Memory;
-using Ryujinx.HLE.Gpu.Memory;
+using Ryujinx.Graphics.Memory;
 using Ryujinx.HLE.HOS.Services.Nv.NvGpuAS;
 using Ryujinx.HLE.Logging;
 using System;
diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvMap/NvMapIoctl.cs b/Ryujinx.HLE/HOS/Services/Nv/NvMap/NvMapIoctl.cs
index 782f7b800..38da2889e 100644
--- a/Ryujinx.HLE/HOS/Services/Nv/NvMap/NvMapIoctl.cs
+++ b/Ryujinx.HLE/HOS/Services/Nv/NvMap/NvMapIoctl.cs
@@ -1,5 +1,5 @@
 using ChocolArm64.Memory;
-using Ryujinx.HLE.Gpu.Memory;
+using Ryujinx.Graphics.Memory;
 using Ryujinx.HLE.Logging;
 using Ryujinx.HLE.Utilities;
 using System.Collections.Concurrent;
diff --git a/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs b/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs
index 12c173113..8f541fbf4 100644
--- a/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs
+++ b/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs
@@ -1,5 +1,5 @@
 using Ryujinx.Graphics.Gal;
-using Ryujinx.HLE.Gpu.Texture;
+using Ryujinx.Graphics.Texture;
 using Ryujinx.HLE.HOS.Kernel;
 using Ryujinx.HLE.HOS.Services.Nv.NvMap;
 using Ryujinx.HLE.Logging;
@@ -303,7 +303,7 @@ namespace Ryujinx.HLE.HOS.Services.Android
             int Right  = Crop.Right;
             int Bottom = Crop.Bottom;
 
-            Renderer.QueueAction(() => Renderer.FrameBuffer.SetTransform(FlipX, FlipY, Top, Left, Right, Bottom));
+            Renderer.QueueAction(() => Renderer.RenderTarget.SetTransform(FlipX, FlipY, Top, Left, Right, Bottom));
 
             //TODO: Support double buffering here aswell, it is broken for GPU
             //frame buffers because it seems to be completely out of sync.
@@ -311,7 +311,7 @@ namespace Ryujinx.HLE.HOS.Services.Android
             {
                 //Frame buffer is rendered to by the GPU, we can just
                 //bind the frame buffer texture, it's not necessary to read anything.
-                Renderer.QueueAction(() => Renderer.FrameBuffer.Set(FbAddr));
+                Renderer.QueueAction(() => Renderer.RenderTarget.Set(FbAddr));
             }
             else
             {
@@ -321,7 +321,7 @@ namespace Ryujinx.HLE.HOS.Services.Android
 
                 byte[] Data = TextureReader.Read(Context.Memory, Texture);
 
-                Renderer.QueueAction(() => Renderer.FrameBuffer.Set(Data, FbWidth, FbHeight));
+                Renderer.QueueAction(() => Renderer.RenderTarget.Set(Data, FbWidth, FbHeight));
             }
 
             Context.Device.Gpu.Renderer.QueueAction(() => ReleaseBuffer(Slot));
diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs
index 03dc44e85..32d5bef68 100644
--- a/Ryujinx.HLE/Switch.cs
+++ b/Ryujinx.HLE/Switch.cs
@@ -1,6 +1,6 @@
 using Ryujinx.Audio;
+using Ryujinx.Graphics;
 using Ryujinx.Graphics.Gal;
-using Ryujinx.HLE.Gpu;
 using Ryujinx.HLE.HOS;
 using Ryujinx.HLE.Input;
 using Ryujinx.HLE.Logging;
diff --git a/Ryujinx/Ui/GLScreen.cs b/Ryujinx/Ui/GLScreen.cs
index 2c683f5ef..e7eb26137 100644
--- a/Ryujinx/Ui/GLScreen.cs
+++ b/Ryujinx/Ui/GLScreen.cs
@@ -73,7 +73,7 @@ namespace Ryujinx
                 {
                     ResizeEvent = false;
 
-                    Renderer.FrameBuffer.SetWindowSize(Width, Height);
+                    Renderer.RenderTarget.SetWindowSize(Width, Height);
                 }
 
                 Ticks += Chrono.ElapsedTicks;
@@ -96,7 +96,7 @@ namespace Ryujinx
 
             Visible = true;
 
-            Renderer.FrameBuffer.SetWindowSize(Width, Height);
+            Renderer.RenderTarget.SetWindowSize(Width, Height);
 
             Context.MakeCurrent(null);
 
@@ -251,7 +251,7 @@ namespace Ryujinx
 
         private new void RenderFrame()
         {
-            Renderer.FrameBuffer.Render();
+            Renderer.RenderTarget.Render();
 
             Device.Statistics.RecordSystemFrameTime();