#pragma once

#include "util/types.hpp"
#include "Common/expected.hpp"
#include "Utilities/StrFmt.h"

namespace gcm
{
	enum
	{
		CELL_GCM_DISPLAY_HSYNC = 1,
		CELL_GCM_DISPLAY_VSYNC = 2,
		CELL_GCM_DISPLAY_HSYNC_WITH_NOISE = 3,
	};

	enum
	{
		CELL_GCM_DEBUG_LEVEL0 = 1,
		CELL_GCM_DEBUG_LEVEL1 = 2,
		CELL_GCM_DEBUG_LEVEL2 = 3,
	};

	enum
	{
		CELL_GCM_DISPLAY_FREQUENCY_59_94HZ = 1,
		CELL_GCM_DISPLAY_FREQUENCY_SCANOUT = 2,
		CELL_GCM_DISPLAY_FREQUENCY_DISABLE = 3,
	};

	enum
	{
		CELL_GCM_DISPLAY_FLIP_STATUS_DONE = 0,
		CELL_GCM_DISPLAY_FLIP_STATUS_WAITING = 1,
	};

	enum
	{
		CELL_GCM_FREQUENCY_MODULO = 1,
		CELL_GCM_FREQUENCY_DIVIDE = 0,
	};

	enum CellRescTableElement
	{
		CELL_RESC_ELEMENT_HALF = 0,
		CELL_RESC_ELEMENT_FLOAT = 1,
	};

	enum
	{
		CELL_GCM_SYSTEM_MODE_IOMAP_512MB = 1,
	};

	enum
	{
		// Index Array Type
		CELL_GCM_DRAW_INDEX_ARRAY_TYPE_32 = 0,
		CELL_GCM_DRAW_INDEX_ARRAY_TYPE_16 = 1,
	};

	enum
	{
		CELL_GCM_PRIMITIVE_POINTS = 1,
		CELL_GCM_PRIMITIVE_LINES = 2,
		CELL_GCM_PRIMITIVE_LINE_LOOP = 3,
		CELL_GCM_PRIMITIVE_LINE_STRIP = 4,
		CELL_GCM_PRIMITIVE_TRIANGLES = 5,
		CELL_GCM_PRIMITIVE_TRIANGLE_STRIP= 6,
		CELL_GCM_PRIMITIVE_TRIANGLE_FAN = 7,
		CELL_GCM_PRIMITIVE_QUADS = 8,
		CELL_GCM_PRIMITIVE_QUAD_STRIP = 9,
		CELL_GCM_PRIMITIVE_POLYGON = 10,
	};

	// GCM Texture
	enum CellGcmTexture : u32
	{
		// Color Flag
		CELL_GCM_TEXTURE_B8 = 0x81,
		CELL_GCM_TEXTURE_A1R5G5B5 = 0x82,
		CELL_GCM_TEXTURE_A4R4G4B4 = 0x83,
		CELL_GCM_TEXTURE_R5G6B5 = 0x84,
		CELL_GCM_TEXTURE_A8R8G8B8 = 0x85,
		CELL_GCM_TEXTURE_COMPRESSED_DXT1 = 0x86,
		CELL_GCM_TEXTURE_COMPRESSED_DXT23 = 0x87,
		CELL_GCM_TEXTURE_COMPRESSED_DXT45 = 0x88,
		CELL_GCM_TEXTURE_G8B8 = 0x8B,
		CELL_GCM_TEXTURE_COMPRESSED_B8R8_G8R8 = 0x8D,
		CELL_GCM_TEXTURE_COMPRESSED_R8B8_R8G8 = 0x8E,
		CELL_GCM_TEXTURE_R6G5B5 = 0x8F,
		CELL_GCM_TEXTURE_DEPTH24_D8 = 0x90,
		CELL_GCM_TEXTURE_DEPTH24_D8_FLOAT = 0x91,
		CELL_GCM_TEXTURE_DEPTH16 = 0x92,
		CELL_GCM_TEXTURE_DEPTH16_FLOAT = 0x93,
		CELL_GCM_TEXTURE_X16 = 0x94,
		CELL_GCM_TEXTURE_Y16_X16 = 0x95,
		CELL_GCM_TEXTURE_R5G5B5A1 = 0x97,
		CELL_GCM_TEXTURE_COMPRESSED_HILO8 = 0x98,
		CELL_GCM_TEXTURE_COMPRESSED_HILO_S8 = 0x99,
		CELL_GCM_TEXTURE_W16_Z16_Y16_X16_FLOAT = 0x9A,
		CELL_GCM_TEXTURE_W32_Z32_Y32_X32_FLOAT = 0x9B,
		CELL_GCM_TEXTURE_X32_FLOAT = 0x9C,
		CELL_GCM_TEXTURE_D1R5G5B5 = 0x9D,
		CELL_GCM_TEXTURE_D8R8G8B8 = 0x9E,
		CELL_GCM_TEXTURE_Y16_X16_FLOAT = 0x9F,

		// Swizzle Flag
		CELL_GCM_TEXTURE_SZ = 0x00,
		CELL_GCM_TEXTURE_LN = 0x20,

		// Normalization Flag
		CELL_GCM_TEXTURE_NR = 0x00,
		CELL_GCM_TEXTURE_UN = 0x40,
	};

	// GCM Surface
	enum
	{
		// Surface type
		CELL_GCM_SURFACE_PITCH = 1,
		CELL_GCM_SURFACE_SWIZZLE = 2,
	};

	// GCM blend equation
	enum
	{
		CELL_GCM_FUNC_ADD = 0x8006,
		CELL_GCM_MIN = 0x8007,
		CELL_GCM_MAX = 0x8008,
		CELL_GCM_FUNC_SUBTRACT = 0x800A,
		CELL_GCM_FUNC_REVERSE_SUBTRACT = 0x800B,
		CELL_GCM_FUNC_REVERSE_SUBTRACT_SIGNED = 0x0000F005,
		CELL_GCM_FUNC_ADD_SIGNED = 0x0000F006,
		CELL_GCM_FUNC_REVERSE_ADD_SIGNED = 0x0000F007,
	};

	// GCM blend factor
	enum
	{
		CELL_GCM_SRC_COLOR = 0x0300,
		CELL_GCM_ONE_MINUS_SRC_COLOR = 0x0301,
		CELL_GCM_SRC_ALPHA = 0x0302,
		CELL_GCM_ONE_MINUS_SRC_ALPHA = 0x0303,
		CELL_GCM_DST_ALPHA = 0x0304,
		CELL_GCM_ONE_MINUS_DST_ALPHA = 0x0305,
		CELL_GCM_DST_COLOR = 0x0306,
		CELL_GCM_ONE_MINUS_DST_COLOR = 0x0307,
		CELL_GCM_SRC_ALPHA_SATURATE = 0x0308,
		CELL_GCM_CONSTANT_COLOR = 0x8001,
		CELL_GCM_ONE_MINUS_CONSTANT_COLOR = 0x8002,
		CELL_GCM_CONSTANT_ALPHA = 0x8003,
		CELL_GCM_ONE_MINUS_CONSTANT_ALPHA = 0x8004,
	};

	enum
	{
		CELL_GCM_TEXTURE_UNSIGNED_REMAP_NORMAL = 0,
		CELL_GCM_TEXTURE_UNSIGNED_REMAP_BIASED = 1,

		CELL_GCM_TEXTURE_SIGNED_REMAP_NORMAL = 0x0,
		CELL_GCM_TEXTURE_SIGNED_REMAP_CLAMPED = 0x3,

		CELL_GCM_TEXTURE_REMAP_FROM_A = 0,
		CELL_GCM_TEXTURE_REMAP_FROM_R = 1,
		CELL_GCM_TEXTURE_REMAP_FROM_G = 2,
		CELL_GCM_TEXTURE_REMAP_FROM_B = 3,

		CELL_GCM_TEXTURE_REMAP_ZERO = 0,
		CELL_GCM_TEXTURE_REMAP_ONE = 1,
		CELL_GCM_TEXTURE_REMAP_REMAP = 2,

		CELL_GCM_TEXTURE_ZFUNC_NEVER = 0,
		CELL_GCM_TEXTURE_ZFUNC_LESS = 1,
		CELL_GCM_TEXTURE_ZFUNC_EQUAL = 2,
		CELL_GCM_TEXTURE_ZFUNC_LEQUAL = 3,
		CELL_GCM_TEXTURE_ZFUNC_GREATER = 4,
		CELL_GCM_TEXTURE_ZFUNC_NOTEQUAL = 5,
		CELL_GCM_TEXTURE_ZFUNC_GEQUAL = 6,
		CELL_GCM_TEXTURE_ZFUNC_ALWAYS = 7,

		CELL_GCM_TEXTURE_GAMMA_R = 1 << 0,
		CELL_GCM_TEXTURE_GAMMA_G = 1 << 1,
		CELL_GCM_TEXTURE_GAMMA_B = 1 << 2,
		CELL_GCM_TEXTURE_GAMMA_A = 1 << 3,

		CELL_GCM_TEXTURE_ANISO_SPREAD_0_50_TEXEL = 0x0,
		CELL_GCM_TEXTURE_ANISO_SPREAD_1_00_TEXEL = 0x1,
		CELL_GCM_TEXTURE_ANISO_SPREAD_1_125_TEXEL = 0x2,
		CELL_GCM_TEXTURE_ANISO_SPREAD_1_25_TEXEL = 0x3,
		CELL_GCM_TEXTURE_ANISO_SPREAD_1_375_TEXEL = 0x4,
		CELL_GCM_TEXTURE_ANISO_SPREAD_1_50_TEXEL = 0x5,
		CELL_GCM_TEXTURE_ANISO_SPREAD_1_75_TEXEL = 0x6,
		CELL_GCM_TEXTURE_ANISO_SPREAD_2_00_TEXEL = 0x7,

		CELL_GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX0_U = 1 << 0,
		CELL_GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX0_V = 1 << 1,
		CELL_GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX0_P = 1 << 2,
		CELL_GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX0_Q = 1 << 3,
		CELL_GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX1_U = 1 << 4,
		CELL_GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX1_V = 1 << 5,
		CELL_GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX1_P = 1 << 6,
		CELL_GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX1_Q = 1 << 7,
		CELL_GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX2_U = 1 << 8,
		CELL_GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX2_V = 1 << 9,
		CELL_GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX2_P = 1 << 10,
		CELL_GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX2_Q = 1 << 11,
		CELL_GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX3_U = 1 << 12,
		CELL_GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX3_V = 1 << 13,
		CELL_GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX3_P = 1 << 14,
		CELL_GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX3_Q = 1 << 15,
		CELL_GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX4_U = 1 << 16,
		CELL_GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX4_V = 1 << 17,
		CELL_GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX4_P = 1 << 18,
		CELL_GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX4_Q = 1 << 19,
		CELL_GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX5_U = 1 << 20,
		CELL_GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX5_V = 1 << 21,
		CELL_GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX5_P = 1 << 22,
		CELL_GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX5_Q = 1 << 23,
		CELL_GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX6_U = 1 << 24,
		CELL_GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX6_V = 1 << 25,
		CELL_GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX6_P = 1 << 26,
		CELL_GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX6_Q = 1 << 27,
		CELL_GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX7_U = 1 << 28,
		CELL_GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX7_V = 1 << 29,
		CELL_GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX7_P = 1 << 30,
		CELL_GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX7_Q = 1u << 31,

		CELL_GCM_COLOR_MASK_B = 1 << 0,
		CELL_GCM_COLOR_MASK_G = 1 << 8,
		CELL_GCM_COLOR_MASK_R = 1 << 16,
		CELL_GCM_COLOR_MASK_A = 1 << 24,

		CELL_GCM_COLOR_MASK_MRT1_A = 1 << 4,
		CELL_GCM_COLOR_MASK_MRT1_R = 1 << 5,
		CELL_GCM_COLOR_MASK_MRT1_G = 1 << 6,
		CELL_GCM_COLOR_MASK_MRT1_B = 1 << 7,
		CELL_GCM_COLOR_MASK_MRT2_A = 1 << 8,
		CELL_GCM_COLOR_MASK_MRT2_R = 1 << 9,
		CELL_GCM_COLOR_MASK_MRT2_G = 1 << 10,
		CELL_GCM_COLOR_MASK_MRT2_B = 1 << 11,
		CELL_GCM_COLOR_MASK_MRT3_A = 1 << 12,
		CELL_GCM_COLOR_MASK_MRT3_R = 1 << 13,
		CELL_GCM_COLOR_MASK_MRT3_G = 1 << 14,
		CELL_GCM_COLOR_MASK_MRT3_B = 1 << 15,

		CELL_GCM_NEVER = 0x0200,
		CELL_GCM_LESS = 0x0201,
		CELL_GCM_EQUAL = 0x0202,
		CELL_GCM_LEQUAL = 0x0203,
		CELL_GCM_GREATER = 0x0204,
		CELL_GCM_NOTEQUAL = 0x0205,
		CELL_GCM_GEQUAL = 0x0206,
		CELL_GCM_ALWAYS = 0x0207,

		CELL_GCM_ZERO = 0,
		CELL_GCM_ONE = 1,

		CELL_GCM_FRONT = 0x0404,
		CELL_GCM_BACK = 0x0405,
		CELL_GCM_FRONT_AND_BACK = 0x0408,

		CELL_GCM_CW = 0x0900,
		CELL_GCM_CCW = 0x0901,

		CELL_GCM_INVERT = 0x150A,
		CELL_GCM_KEEP = 0x1E00,
		CELL_GCM_REPLACE = 0x1E01,
		CELL_GCM_INCR = 0x1E02,
		CELL_GCM_DECR = 0x1E03,
		CELL_GCM_INCR_WRAP = 0x8507,
		CELL_GCM_DECR_WRAP = 0x8508,

		CELL_GCM_TRANSFER_LOCAL_TO_LOCAL = 0,
		CELL_GCM_TRANSFER_MAIN_TO_LOCAL = 1,
		CELL_GCM_TRANSFER_LOCAL_TO_MAIN = 2,
		CELL_GCM_TRANSFER_MAIN_TO_MAIN = 3,

		CELL_GCM_INVALIDATE_TEXTURE = 1,
		CELL_GCM_INVALIDATE_VERTEX_TEXTURE = 2,

		CELL_GCM_COMPMODE_DISABLED = 0,
		CELL_GCM_COMPMODE_C32_2X1 = 7,
		CELL_GCM_COMPMODE_C32_2X2 = 8,
		CELL_GCM_COMPMODE_Z32_SEPSTENCIL = 9,
		CELL_GCM_COMPMODE_Z32_SEPSTENCIL_REG = 10,
		CELL_GCM_COMPMODE_Z32_SEPSTENCIL_REGULAR = 10,
		CELL_GCM_COMPMODE_Z32_SEPSTENCIL_DIAGONAL = 11,
		CELL_GCM_COMPMODE_Z32_SEPSTENCIL_ROTATED = 12,

		CELL_GCM_ZCULL_Z16 = 1,
		CELL_GCM_ZCULL_Z24S8 = 2,
		CELL_GCM_ZCULL_MSB = 0,
		CELL_GCM_ZCULL_LONES = 1,
		CELL_GCM_ZCULL_LESS = 0,
		CELL_GCM_ZCULL_GREATER = 1,

		CELL_GCM_SCULL_SFUNC_NEVER = 0,
		CELL_GCM_SCULL_SFUNC_LESS = 1,
		CELL_GCM_SCULL_SFUNC_EQUAL = 2,
		CELL_GCM_SCULL_SFUNC_LEQUAL = 3,
		CELL_GCM_SCULL_SFUNC_GREATER = 4,
		CELL_GCM_SCULL_SFUNC_NOTEQUAL = 5,
		CELL_GCM_SCULL_SFUNC_GEQUAL = 6,
		CELL_GCM_SCULL_SFUNC_ALWAYS = 7,

		CELL_GCM_ATTRIB_OUTPUT_FRONTDIFFUSE = 0,
		CELL_GCM_ATTRIB_OUTPUT_FRONTSPECULAR = 1,
		CELL_GCM_ATTRIB_OUTPUT_BACKDIFFUSE = 2,
		CELL_GCM_ATTRIB_OUTPUT_BACKSPECULAR = 3,
		CELL_GCM_ATTRIB_OUTPUT_FOG = 4,
		CELL_GCM_ATTRIB_OUTPUT_POINTSIZE = 5,
		CELL_GCM_ATTRIB_OUTPUT_UC0 = 6,
		CELL_GCM_ATTRIB_OUTPUT_UC1 = 7,
		CELL_GCM_ATTRIB_OUTPUT_UC2 = 8,
		CELL_GCM_ATTRIB_OUTPUT_UC3 = 9,
		CELL_GCM_ATTRIB_OUTPUT_UC4 = 10,
		CELL_GCM_ATTRIB_OUTPUT_UC5 = 11,
		CELL_GCM_ATTRIB_OUTPUT_TEX8 = 12,
		CELL_GCM_ATTRIB_OUTPUT_TEX9 = 13,
		CELL_GCM_ATTRIB_OUTPUT_TEX0 = 14,
		CELL_GCM_ATTRIB_OUTPUT_TEX1 = 15,
		CELL_GCM_ATTRIB_OUTPUT_TEX2 = 16,
		CELL_GCM_ATTRIB_OUTPUT_TEX3 = 17,
		CELL_GCM_ATTRIB_OUTPUT_TEX4 = 18,
		CELL_GCM_ATTRIB_OUTPUT_TEX5 = 19,
		CELL_GCM_ATTRIB_OUTPUT_TEX6 = 20,
		CELL_GCM_ATTRIB_OUTPUT_TEX7 = 21,

		CELL_GCM_ATTRIB_OUTPUT_MASK_FRONTDIFFUSE = 1 << CELL_GCM_ATTRIB_OUTPUT_FRONTDIFFUSE,
		CELL_GCM_ATTRIB_OUTPUT_MASK_FRONTSPECULAR = 1 << CELL_GCM_ATTRIB_OUTPUT_FRONTSPECULAR,
		CELL_GCM_ATTRIB_OUTPUT_MASK_BACKDIFFUSE = 1 << CELL_GCM_ATTRIB_OUTPUT_BACKDIFFUSE,
		CELL_GCM_ATTRIB_OUTPUT_MASK_BACKSPECULAR = 1 << CELL_GCM_ATTRIB_OUTPUT_BACKSPECULAR,
		CELL_GCM_ATTRIB_OUTPUT_MASK_FOG = 1 << CELL_GCM_ATTRIB_OUTPUT_FOG,
		CELL_GCM_ATTRIB_OUTPUT_MASK_POINTSIZE = 1 << CELL_GCM_ATTRIB_OUTPUT_POINTSIZE,
		CELL_GCM_ATTRIB_OUTPUT_MASK_UC0 = 1 << CELL_GCM_ATTRIB_OUTPUT_UC0,
		CELL_GCM_ATTRIB_OUTPUT_MASK_UC1 = 1 << CELL_GCM_ATTRIB_OUTPUT_UC1,
		CELL_GCM_ATTRIB_OUTPUT_MASK_UC2 = 1 << CELL_GCM_ATTRIB_OUTPUT_UC2,
		CELL_GCM_ATTRIB_OUTPUT_MASK_UC3 = 1 << CELL_GCM_ATTRIB_OUTPUT_UC3,
		CELL_GCM_ATTRIB_OUTPUT_MASK_UC4 = 1 << CELL_GCM_ATTRIB_OUTPUT_UC4,
		CELL_GCM_ATTRIB_OUTPUT_MASK_UC5 = 1 << CELL_GCM_ATTRIB_OUTPUT_UC5,
		CELL_GCM_ATTRIB_OUTPUT_MASK_TEX8 = 1 << CELL_GCM_ATTRIB_OUTPUT_TEX8,
		CELL_GCM_ATTRIB_OUTPUT_MASK_TEX9 = 1 << CELL_GCM_ATTRIB_OUTPUT_TEX9,
		CELL_GCM_ATTRIB_OUTPUT_MASK_TEX0 = 1 << CELL_GCM_ATTRIB_OUTPUT_TEX0,
		CELL_GCM_ATTRIB_OUTPUT_MASK_TEX1 = 1 << CELL_GCM_ATTRIB_OUTPUT_TEX1,
		CELL_GCM_ATTRIB_OUTPUT_MASK_TEX2 = 1 << CELL_GCM_ATTRIB_OUTPUT_TEX2,
		CELL_GCM_ATTRIB_OUTPUT_MASK_TEX3 = 1 << CELL_GCM_ATTRIB_OUTPUT_TEX3,
		CELL_GCM_ATTRIB_OUTPUT_MASK_TEX4 = 1 << CELL_GCM_ATTRIB_OUTPUT_TEX4,
		CELL_GCM_ATTRIB_OUTPUT_MASK_TEX5 = 1 << CELL_GCM_ATTRIB_OUTPUT_TEX5,
		CELL_GCM_ATTRIB_OUTPUT_MASK_TEX6 = 1 << CELL_GCM_ATTRIB_OUTPUT_TEX6,
		CELL_GCM_ATTRIB_OUTPUT_MASK_TEX7 = 1 << CELL_GCM_ATTRIB_OUTPUT_TEX7,

		CELL_GCM_TRUE = 1,
		CELL_GCM_FALSE = 0,
	};

	enum
	{
		CELL_GCM_SHADER_CONTROL_DEPTH_EXPORT = 0xe, ///< shader program exports the depth of the shaded fragment
		CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS = 0x40, ///< shader program exports 32 bits registers values (instead of 16 bits ones)

		// Other known flags
		RSX_SHADER_CONTROL_USED_REGS_MASK = 0xf,
		RSX_SHADER_CONTROL_USED_TEMP_REGS_MASK = 0xff << 24,
		RSX_SHADER_CONTROL_USES_KIL = 0x80,   // program uses KIL op
		RSX_SHADER_CONTROL_UNKNOWN0 = 0x400,  // seemingly always set
		RSX_SHADER_CONTROL_UNKNOWN1 = 0x8000, // seemingly set when srgb packer is used??

		// Custom
		RSX_SHADER_CONTROL_ATTRIBUTE_INTERPOLATION = 0x10000 // Rasterizing triangles and not lines or points
	};

	// GCM Reports
	enum
	{
		CELL_GCM_ZPASS_PIXEL_CNT = 1,
		CELL_GCM_ZCULL_STATS = 2,
		CELL_GCM_ZCULL_STATS1 = 3,
		CELL_GCM_ZCULL_STATS2 = 4,
		CELL_GCM_ZCULL_STATS3 = 5,
	};

	// GPU Class Handles
	enum CellGcmLocation : u32
	{
		CELL_GCM_LOCATION_LOCAL = 0,
		CELL_GCM_LOCATION_MAIN = 1,

		CELL_GCM_CONTEXT_DMA_MEMORY_FRAME_BUFFER = 0xFEED0000, // Local memory
		CELL_GCM_CONTEXT_DMA_MEMORY_HOST_BUFFER = 0xFEED0001, // Main memory
		CELL_GCM_CONTEXT_DMA_REPORT_LOCATION_LOCAL = 0x66626660,
		CELL_GCM_CONTEXT_DMA_REPORT_LOCATION_MAIN = 0xBAD68000,
		CELL_GCM_CONTEXT_DMA_NOTIFY_MAIN_0 = 0x6660420F,
		CELL_GCM_CONTEXT_DMA_NOTIFY_MAIN_1 = 0x6660420E,
		CELL_GCM_CONTEXT_DMA_NOTIFY_MAIN_2 = 0x6660420D,
		CELL_GCM_CONTEXT_DMA_NOTIFY_MAIN_3 = 0x6660420C,
		CELL_GCM_CONTEXT_DMA_NOTIFY_MAIN_4 = 0x6660420B,
		CELL_GCM_CONTEXT_DMA_NOTIFY_MAIN_5 = 0x6660420A,
		CELL_GCM_CONTEXT_DMA_NOTIFY_MAIN_6 = 0x66604209,
		CELL_GCM_CONTEXT_DMA_NOTIFY_MAIN_7 = 0x66604208,

		CELL_GCM_CONTEXT_DMA_TO_MEMORY_GET_NOTIFY0 = 0x66604207,
		CELL_GCM_CONTEXT_DMA_TO_MEMORY_GET_NOTIFY1 = 0x66604206,
		CELL_GCM_CONTEXT_DMA_TO_MEMORY_GET_NOTIFY2 = 0x66604205,
		CELL_GCM_CONTEXT_DMA_TO_MEMORY_GET_NOTIFY3 = 0x66604204,
		CELL_GCM_CONTEXT_DMA_TO_MEMORY_GET_NOTIFY4 = 0x66604203,
		CELL_GCM_CONTEXT_DMA_TO_MEMORY_GET_NOTIFY5 = 0x66604202,
		CELL_GCM_CONTEXT_DMA_TO_MEMORY_GET_NOTIFY6 = 0x66604201,
		CELL_GCM_CONTEXT_DMA_TO_MEMORY_GET_NOTIFY7 = 0x66604200,

		CELL_GCM_CONTEXT_DMA_SEMAPHORE_RW = 0x66606660,
		CELL_GCM_CONTEXT_DMA_SEMAPHORE_R = 0x66616661,
		CELL_GCM_CONTEXT_DMA_DEVICE_RW = 0x56616660,
		CELL_GCM_CONTEXT_DMA_DEVICE_R = 0x56616661
	};

	enum CellGcmMethod : u16
	{
		// NV40_CHANNEL_DMA (NV406E)
		NV406E_SET_REFERENCE = 0x00000050 >> 2,
		NV406E_SET_CONTEXT_DMA_SEMAPHORE = 0x00000060 >> 2,
		NV406E_SEMAPHORE_OFFSET = 0x00000064 >> 2,
		NV406E_SEMAPHORE_ACQUIRE = 0x00000068 >> 2,
		NV406E_SEMAPHORE_RELEASE = 0x0000006c >> 2,

		// NV40_CURIE_PRIMITIVE	(NV4097)
		NV4097_SET_OBJECT = 0x00000000 >> 2,
		NV4097_NO_OPERATION = 0x00000100 >> 2,
		NV4097_NOTIFY = 0x00000104 >> 2,
		NV4097_WAIT_FOR_IDLE = 0x00000110 >> 2,
		NV4097_PM_TRIGGER = 0x00000140 >> 2,
		NV4097_SET_CONTEXT_DMA_NOTIFIES = 0x00000180 >> 2,
		NV4097_SET_CONTEXT_DMA_A = 0x00000184 >> 2,
		NV4097_SET_CONTEXT_DMA_B = 0x00000188 >> 2,
		NV4097_SET_CONTEXT_DMA_COLOR_B = 0x0000018c >> 2,
		NV4097_SET_CONTEXT_DMA_STATE = 0x00000190 >> 2,
		NV4097_SET_CONTEXT_DMA_COLOR_A = 0x00000194 >> 2,
		NV4097_SET_CONTEXT_DMA_ZETA = 0x00000198 >> 2,
		NV4097_SET_CONTEXT_DMA_VERTEX_A = 0x0000019c >> 2,
		NV4097_SET_CONTEXT_DMA_VERTEX_B = 0x000001a0 >> 2,
		NV4097_SET_CONTEXT_DMA_SEMAPHORE = 0x000001a4 >> 2,
		NV4097_SET_CONTEXT_DMA_REPORT = 0x000001a8 >> 2,
		NV4097_SET_CONTEXT_DMA_CLIP_ID = 0x000001ac >> 2,
		NV4097_SET_CONTEXT_DMA_CULL_DATA = 0x000001b0 >> 2,
		NV4097_SET_CONTEXT_DMA_COLOR_C = 0x000001b4 >> 2,
		NV4097_SET_CONTEXT_DMA_COLOR_D = 0x000001b8 >> 2,
		NV4097_SET_SURFACE_CLIP_HORIZONTAL = 0x00000200 >> 2,
		NV4097_SET_SURFACE_CLIP_VERTICAL = 0x00000204 >> 2,
		NV4097_SET_SURFACE_FORMAT = 0x00000208 >> 2,
		NV4097_SET_SURFACE_PITCH_A = 0x0000020c >> 2,
		NV4097_SET_SURFACE_COLOR_AOFFSET = 0x00000210 >> 2,
		NV4097_SET_SURFACE_ZETA_OFFSET = 0x00000214 >> 2,
		NV4097_SET_SURFACE_COLOR_BOFFSET = 0x00000218 >> 2,
		NV4097_SET_SURFACE_PITCH_B = 0x0000021c >> 2,
		NV4097_SET_SURFACE_COLOR_TARGET = 0x00000220 >> 2,
		NV4097_SET_SURFACE_PITCH_Z = 0x0000022c >> 2,
		NV4097_INVALIDATE_ZCULL = 0x00000234 >> 2,
		NV4097_SET_CYLINDRICAL_WRAP = 0x00000238 >> 2,
		NV4097_SET_CYLINDRICAL_WRAP1 = 0x0000023c >> 2,
		NV4097_SET_SURFACE_PITCH_C = 0x00000280 >> 2,
		NV4097_SET_SURFACE_PITCH_D = 0x00000284 >> 2,
		NV4097_SET_SURFACE_COLOR_COFFSET = 0x00000288 >> 2,
		NV4097_SET_SURFACE_COLOR_DOFFSET = 0x0000028c >> 2,
		NV4097_SET_WINDOW_OFFSET = 0x000002b8 >> 2,
		NV4097_SET_WINDOW_CLIP_TYPE = 0x000002bc >> 2,
		NV4097_SET_WINDOW_CLIP_HORIZONTAL = 0x000002c0 >> 2,
		NV4097_SET_WINDOW_CLIP_VERTICAL = 0x000002c4 >> 2,
		NV4097_SET_DITHER_ENABLE = 0x00000300 >> 2,
		NV4097_SET_ALPHA_TEST_ENABLE = 0x00000304 >> 2,
		NV4097_SET_ALPHA_FUNC = 0x00000308 >> 2,
		NV4097_SET_ALPHA_REF = 0x0000030c >> 2,
		NV4097_SET_BLEND_ENABLE = 0x00000310 >> 2,
		NV4097_SET_BLEND_FUNC_SFACTOR = 0x00000314 >> 2,
		NV4097_SET_BLEND_FUNC_DFACTOR = 0x00000318 >> 2,
		NV4097_SET_BLEND_COLOR = 0x0000031c >> 2,
		NV4097_SET_BLEND_EQUATION = 0x00000320 >> 2,
		NV4097_SET_COLOR_MASK = 0x00000324 >> 2,
		NV4097_SET_STENCIL_TEST_ENABLE = 0x00000328 >> 2,
		NV4097_SET_STENCIL_MASK = 0x0000032c >> 2,
		NV4097_SET_STENCIL_FUNC = 0x00000330 >> 2,
		NV4097_SET_STENCIL_FUNC_REF = 0x00000334 >> 2,
		NV4097_SET_STENCIL_FUNC_MASK = 0x00000338 >> 2,
		NV4097_SET_STENCIL_OP_FAIL = 0x0000033c >> 2,
		NV4097_SET_STENCIL_OP_ZFAIL = 0x00000340 >> 2,
		NV4097_SET_STENCIL_OP_ZPASS = 0x00000344 >> 2,
		NV4097_SET_TWO_SIDED_STENCIL_TEST_ENABLE = 0x00000348 >> 2,
		NV4097_SET_BACK_STENCIL_MASK = 0x0000034c >> 2,
		NV4097_SET_BACK_STENCIL_FUNC = 0x00000350 >> 2,
		NV4097_SET_BACK_STENCIL_FUNC_REF = 0x00000354 >> 2,
		NV4097_SET_BACK_STENCIL_FUNC_MASK = 0x00000358 >> 2,
		NV4097_SET_BACK_STENCIL_OP_FAIL = 0x0000035c >> 2,
		NV4097_SET_BACK_STENCIL_OP_ZFAIL = 0x00000360 >> 2,
		NV4097_SET_BACK_STENCIL_OP_ZPASS = 0x00000364 >> 2,
		NV4097_SET_SHADE_MODE = 0x00000368 >> 2,
		NV4097_SET_BLEND_ENABLE_MRT = 0x0000036c >> 2,
		NV4097_SET_COLOR_MASK_MRT = 0x00000370 >> 2,
		NV4097_SET_LOGIC_OP_ENABLE = 0x00000374 >> 2,
		NV4097_SET_LOGIC_OP = 0x00000378 >> 2,
		NV4097_SET_BLEND_COLOR2 = 0x0000037c >> 2,
		NV4097_SET_DEPTH_BOUNDS_TEST_ENABLE = 0x00000380 >> 2,
		NV4097_SET_DEPTH_BOUNDS_MIN = 0x00000384 >> 2,
		NV4097_SET_DEPTH_BOUNDS_MAX = 0x00000388 >> 2,
		NV4097_SET_CLIP_MIN = 0x00000394 >> 2,
		NV4097_SET_CLIP_MAX = 0x00000398 >> 2,
		NV4097_SET_CONTROL0 = 0x000003b0 >> 2,
		NV4097_SET_LINE_WIDTH = 0x000003b8 >> 2,
		NV4097_SET_LINE_SMOOTH_ENABLE = 0x000003bc >> 2,
		NV4097_SET_ANISO_SPREAD = 0x000003c0 >> 2,
		NV4097_SET_SCISSOR_HORIZONTAL = 0x000008c0 >> 2,
		NV4097_SET_SCISSOR_VERTICAL = 0x000008c4 >> 2,
		NV4097_SET_FOG_MODE = 0x000008cc >> 2,
		NV4097_SET_FOG_PARAMS = 0x000008d0 >> 2,
		NV4097_SET_SHADER_PROGRAM = 0x000008e4 >> 2,
		NV4097_SET_VERTEX_TEXTURE_OFFSET = 0x00000900 >> 2,
		NV4097_SET_VERTEX_TEXTURE_FORMAT = 0x00000904 >> 2,
		NV4097_SET_VERTEX_TEXTURE_ADDRESS = 0x00000908 >> 2,
		NV4097_SET_VERTEX_TEXTURE_CONTROL0 = 0x0000090c >> 2,
		NV4097_SET_VERTEX_TEXTURE_CONTROL3 = 0x00000910 >> 2,
		NV4097_SET_VERTEX_TEXTURE_FILTER = 0x00000914 >> 2,
		NV4097_SET_VERTEX_TEXTURE_IMAGE_RECT = 0x00000918 >> 2,
		NV4097_SET_VERTEX_TEXTURE_BORDER_COLOR = 0x0000091c >> 2,
		NV4097_SET_VIEWPORT_HORIZONTAL = 0x00000a00 >> 2,
		NV4097_SET_VIEWPORT_VERTICAL = 0x00000a04 >> 2,
		NV4097_SET_POINT_CENTER_MODE = 0x00000a0c >> 2,
		NV4097_ZCULL_SYNC = 0x00000a1c >> 2,
		NV4097_SET_VIEWPORT_OFFSET = 0x00000a20 >> 2,
		NV4097_SET_VIEWPORT_SCALE = 0x00000a30 >> 2,
		NV4097_SET_POLY_OFFSET_POINT_ENABLE = 0x00000a60 >> 2,
		NV4097_SET_POLY_OFFSET_LINE_ENABLE = 0x00000a64 >> 2,
		NV4097_SET_POLY_OFFSET_FILL_ENABLE = 0x00000a68 >> 2,
		NV4097_SET_DEPTH_FUNC = 0x00000a6c >> 2,
		NV4097_SET_DEPTH_MASK = 0x00000a70 >> 2,
		NV4097_SET_DEPTH_TEST_ENABLE = 0x00000a74 >> 2,
		NV4097_SET_POLYGON_OFFSET_SCALE_FACTOR = 0x00000a78 >> 2,
		NV4097_SET_POLYGON_OFFSET_BIAS = 0x00000a7c >> 2,
		NV4097_SET_VERTEX_DATA_SCALED4S_M = 0x00000a80 >> 2,
		NV4097_SET_TEXTURE_CONTROL2 = 0x00000b00 >> 2,
		NV4097_SET_TEX_COORD_CONTROL = 0x00000b40 >> 2,
		NV4097_SET_TRANSFORM_PROGRAM = 0x00000b80 >> 2,
		NV4097_SET_SPECULAR_ENABLE = 0x00001428 >> 2,
		NV4097_SET_TWO_SIDE_LIGHT_EN = 0x0000142c >> 2,
		NV4097_CLEAR_ZCULL_SURFACE = 0x00001438 >> 2,
		NV4097_SET_PERFORMANCE_PARAMS = 0x00001450 >> 2,
		NV4097_SET_FLAT_SHADE_OP = 0x00001454 >> 2,
		NV4097_SET_EDGE_FLAG = 0x0000145c >> 2,
		NV4097_SET_USER_CLIP_PLANE_CONTROL = 0x00001478 >> 2,
		NV4097_SET_POLYGON_STIPPLE = 0x0000147c >> 2,
		NV4097_SET_POLYGON_STIPPLE_PATTERN = 0x00001480 >> 2,
		NV4097_SET_VERTEX_DATA3F_M = 0x00001500 >> 2,
		NV4097_SET_VERTEX_DATA_ARRAY_OFFSET = 0x00001680 >> 2,
		NV4097_INVALIDATE_VERTEX_CACHE_FILE = 0x00001710 >> 2,
		NV4097_INVALIDATE_VERTEX_FILE = 0x00001714 >> 2,
		NV4097_PIPE_NOP = 0x00001718 >> 2,
		NV4097_SET_VERTEX_DATA_BASE_OFFSET = 0x00001738 >> 2,
		NV4097_SET_VERTEX_DATA_BASE_INDEX = 0x0000173c >> 2,
		NV4097_SET_VERTEX_DATA_ARRAY_FORMAT = 0x00001740 >> 2,
		NV4097_CLEAR_REPORT_VALUE = 0x000017c8 >> 2,
		NV4097_SET_ZPASS_PIXEL_COUNT_ENABLE = 0x000017cc >> 2,
		NV4097_GET_REPORT = 0x00001800 >> 2,
		NV4097_SET_ZCULL_STATS_ENABLE = 0x00001804 >> 2,
		NV4097_SET_BEGIN_END = 0x00001808 >> 2,
		NV4097_ARRAY_ELEMENT16 = 0x0000180c >> 2,
		NV4097_ARRAY_ELEMENT32 = 0x00001810 >> 2,
		NV4097_DRAW_ARRAYS = 0x00001814 >> 2,
		NV4097_INLINE_ARRAY = 0x00001818 >> 2,
		NV4097_SET_INDEX_ARRAY_ADDRESS = 0x0000181c >> 2,
		NV4097_SET_INDEX_ARRAY_DMA = 0x00001820 >> 2,
		NV4097_DRAW_INDEX_ARRAY = 0x00001824 >> 2,
		NV4097_SET_FRONT_POLYGON_MODE = 0x00001828 >> 2,
		NV4097_SET_BACK_POLYGON_MODE = 0x0000182c >> 2,
		NV4097_SET_CULL_FACE = 0x00001830 >> 2,
		NV4097_SET_FRONT_FACE = 0x00001834 >> 2,
		NV4097_SET_POLY_SMOOTH_ENABLE = 0x00001838 >> 2,
		NV4097_SET_CULL_FACE_ENABLE = 0x0000183c >> 2,
		NV4097_SET_TEXTURE_CONTROL3 = 0x00001840 >> 2,
		NV4097_SET_VERTEX_DATA2F_M = 0x00001880 >> 2,
		NV4097_SET_VERTEX_DATA2S_M = 0x00001900 >> 2,
		NV4097_SET_VERTEX_DATA4UB_M = 0x00001940 >> 2,
		NV4097_SET_VERTEX_DATA4S_M = 0x00001980 >> 2,
		NV4097_SET_TEXTURE_OFFSET = 0x00001a00 >> 2,
		NV4097_SET_TEXTURE_FORMAT = 0x00001a04 >> 2,
		NV4097_SET_TEXTURE_ADDRESS = 0x00001a08 >> 2,
		NV4097_SET_TEXTURE_CONTROL0 = 0x00001a0c >> 2,
		NV4097_SET_TEXTURE_CONTROL1 = 0x00001a10 >> 2,
		NV4097_SET_TEXTURE_FILTER = 0x00001a14 >> 2,
		NV4097_SET_TEXTURE_IMAGE_RECT = 0x00001a18 >> 2,
		NV4097_SET_TEXTURE_BORDER_COLOR = 0x00001a1c >> 2,
		NV4097_SET_VERTEX_DATA4F_M = 0x00001c00 >> 2,
		NV4097_SET_COLOR_KEY_COLOR = 0x00001d00 >> 2,
		NV4097_SET_SHADER_CONTROL = 0x00001d60 >> 2,
		NV4097_SET_INDEXED_CONSTANT_READ_LIMITS = 0x00001d64 >> 2,
		NV4097_SET_SEMAPHORE_OFFSET = 0x00001d6c >> 2,
		NV4097_BACK_END_WRITE_SEMAPHORE_RELEASE = 0x00001d70 >> 2,
		NV4097_TEXTURE_READ_SEMAPHORE_RELEASE = 0x00001d74 >> 2,
		NV4097_SET_ZMIN_MAX_CONTROL = 0x00001d78 >> 2,
		NV4097_SET_ANTI_ALIASING_CONTROL = 0x00001d7c >> 2,
		NV4097_SET_SURFACE_COMPRESSION = 0x00001d80 >> 2,
		NV4097_SET_ZCULL_EN = 0x00001d84 >> 2,
		NV4097_SET_SHADER_WINDOW = 0x00001d88 >> 2,
		NV4097_SET_ZSTENCIL_CLEAR_VALUE = 0x00001d8c >> 2,
		NV4097_SET_COLOR_CLEAR_VALUE = 0x00001d90 >> 2,
		NV4097_CLEAR_SURFACE = 0x00001d94 >> 2,
		NV4097_SET_CLEAR_RECT_HORIZONTAL = 0x00001d98 >> 2,
		NV4097_SET_CLEAR_RECT_VERTICAL = 0x00001d9c >> 2,
		NV4097_SET_CLIP_ID_TEST_ENABLE = 0x00001da4 >> 2,
		NV4097_SET_RESTART_INDEX_ENABLE = 0x00001dac >> 2,
		NV4097_SET_RESTART_INDEX = 0x00001db0 >> 2,
		NV4097_SET_LINE_STIPPLE = 0x00001db4 >> 2,
		NV4097_SET_LINE_STIPPLE_PATTERN = 0x00001db8 >> 2,
		NV4097_SET_VERTEX_DATA1F_M = 0x00001e40 >> 2,
		NV4097_SET_TRANSFORM_EXECUTION_MODE = 0x00001e94 >> 2,
		NV4097_SET_RENDER_ENABLE = 0x00001e98 >> 2,
		NV4097_SET_TRANSFORM_PROGRAM_LOAD = 0x00001e9c >> 2,
		NV4097_SET_TRANSFORM_PROGRAM_START = 0x00001ea0 >> 2,
		NV4097_SET_ZCULL_CONTROL0 = 0x00001ea4 >> 2,
		NV4097_SET_ZCULL_CONTROL1 = 0x00001ea8 >> 2,
		NV4097_SET_SCULL_CONTROL = 0x00001eac >> 2,
		NV4097_SET_POINT_SIZE = 0x00001ee0 >> 2,
		NV4097_SET_POINT_PARAMS_ENABLE = 0x00001ee4 >> 2,
		NV4097_SET_POINT_SPRITE_CONTROL = 0x00001ee8 >> 2,
		NV4097_SET_TRANSFORM_TIMEOUT = 0x00001ef8 >> 2,
		NV4097_SET_TRANSFORM_CONSTANT_LOAD = 0x00001efc >> 2,
		NV4097_SET_TRANSFORM_CONSTANT = 0x00001f00 >> 2,
		NV4097_SET_FREQUENCY_DIVIDER_OPERATION = 0x00001fc0 >> 2,
		NV4097_SET_ATTRIB_COLOR = 0x00001fc4 >> 2,
		NV4097_SET_ATTRIB_TEX_COORD = 0x00001fc8 >> 2,
		NV4097_SET_ATTRIB_TEX_COORD_EX = 0x00001fcc >> 2,
		NV4097_SET_ATTRIB_UCLIP0 = 0x00001fd0 >> 2,
		NV4097_SET_ATTRIB_UCLIP1 = 0x00001fd4 >> 2,
		NV4097_INVALIDATE_L2 = 0x00001fd8 >> 2,
		NV4097_SET_REDUCE_DST_COLOR = 0x00001fe0 >> 2,
		NV4097_SET_NO_PARANOID_TEXTURE_FETCHES = 0x00001fe8 >> 2,
		NV4097_SET_SHADER_PACKER = 0x00001fec >> 2,
		NV4097_SET_VERTEX_ATTRIB_INPUT_MASK = 0x00001ff0 >> 2,
		NV4097_SET_VERTEX_ATTRIB_OUTPUT_MASK = 0x00001ff4 >> 2,
		NV4097_SET_TRANSFORM_BRANCH_BITS = 0x00001ff8 >> 2,

		// NV03_MEMORY_TO_MEMORY_FORMAT	(NV0039)
		NV0039_SET_OBJECT = 0x00002000 >> 2,
		NV0039_SET_CONTEXT_DMA_NOTIFIES = 0x00002180 >> 2,
		NV0039_SET_CONTEXT_DMA_BUFFER_IN = 0x00002184 >> 2,
		NV0039_SET_CONTEXT_DMA_BUFFER_OUT = 0x00002188 >> 2,
		NV0039_OFFSET_IN = 0x0000230C >> 2,
		NV0039_OFFSET_OUT = 0x00002310 >> 2,
		NV0039_PITCH_IN = 0x00002314 >> 2,
		NV0039_PITCH_OUT = 0x00002318 >> 2,
		NV0039_LINE_LENGTH_IN = 0x0000231C >> 2,
		NV0039_LINE_COUNT = 0x00002320 >> 2,
		NV0039_FORMAT = 0x00002324 >> 2,
		NV0039_BUFFER_NOTIFY = 0x00002328 >> 2,

		// NV30_CONTEXT_SURFACES_2D	(NV3062)
		NV3062_SET_OBJECT = 0x00006000 >> 2,
		NV3062_SET_CONTEXT_DMA_NOTIFIES = 0x00006180 >> 2,
		NV3062_SET_CONTEXT_DMA_IMAGE_SOURCE = 0x00006184 >> 2,
		NV3062_SET_CONTEXT_DMA_IMAGE_DESTIN = 0x00006188 >> 2,
		NV3062_SET_COLOR_FORMAT = 0x00006300 >> 2,
		NV3062_SET_PITCH = 0x00006304 >> 2,
		NV3062_SET_OFFSET_SOURCE = 0x00006308 >> 2,
		NV3062_SET_OFFSET_DESTIN = 0x0000630C >> 2,

		// NV30_CONTEXT_SURFACE_SWIZZLED (NV309E)
		NV309E_SET_OBJECT = 0x00008000 >> 2,
		NV309E_SET_CONTEXT_DMA_NOTIFIES = 0x00008180 >> 2,
		NV309E_SET_CONTEXT_DMA_IMAGE = 0x00008184 >> 2,
		NV309E_SET_FORMAT = 0x00008300 >> 2,
		NV309E_SET_OFFSET = 0x00008304 >> 2,

		// NV30_IMAGE_FROM_CPU (NV308A)
		NV308A_SET_OBJECT = 0x0000A000 >> 2,
		NV308A_SET_CONTEXT_DMA_NOTIFIES = 0x0000A180 >> 2,
		NV308A_SET_CONTEXT_COLOR_KEY = 0x0000A184 >> 2,
		NV308A_SET_CONTEXT_CLIP_RECTANGLE = 0x0000A188 >> 2,
		NV308A_SET_CONTEXT_PATTERN = 0x0000A18C >> 2,
		NV308A_SET_CONTEXT_ROP = 0x0000A190 >> 2,
		NV308A_SET_CONTEXT_BETA1 = 0x0000A194 >> 2,
		NV308A_SET_CONTEXT_BETA4 = 0x0000A198 >> 2,
		NV308A_SET_CONTEXT_SURFACE = 0x0000A19C >> 2,
		NV308A_SET_COLOR_CONVERSION = 0x0000A2F8 >> 2,
		NV308A_SET_OPERATION = 0x0000A2FC >> 2,
		NV308A_SET_COLOR_FORMAT = 0x0000A300 >> 2,
		NV308A_POINT = 0x0000A304 >> 2,
		NV308A_SIZE_OUT = 0x0000A308 >> 2,
		NV308A_SIZE_IN = 0x0000A30C >> 2,
		NV308A_COLOR = 0x0000A400 >> 2,

		// NV30_SCALED_IMAGE_FROM_MEMORY (NV3089)
		NV3089_SET_OBJECT = 0x0000C000 >> 2,
		NV3089_SET_CONTEXT_DMA_NOTIFIES = 0x0000C180 >> 2,
		NV3089_SET_CONTEXT_DMA_IMAGE = 0x0000C184 >> 2,
		NV3089_SET_CONTEXT_PATTERN = 0x0000C188 >> 2,
		NV3089_SET_CONTEXT_ROP = 0x0000C18C >> 2,
		NV3089_SET_CONTEXT_BETA1 = 0x0000C190 >> 2,
		NV3089_SET_CONTEXT_BETA4 = 0x0000C194 >> 2,
		NV3089_SET_CONTEXT_SURFACE = 0x0000C198 >> 2,
		NV3089_SET_COLOR_CONVERSION = 0x0000C2FC >> 2,
		NV3089_SET_COLOR_FORMAT = 0x0000C300 >> 2,
		NV3089_SET_OPERATION = 0x0000C304 >> 2,
		NV3089_CLIP_POINT = 0x0000C308 >> 2,
		NV3089_CLIP_SIZE = 0x0000C30C >> 2,
		NV3089_IMAGE_OUT_POINT = 0x0000C310 >> 2,
		NV3089_IMAGE_OUT_SIZE = 0x0000C314 >> 2,
		NV3089_DS_DX = 0x0000C318 >> 2,
		NV3089_DT_DY = 0x0000C31C >> 2,
		NV3089_IMAGE_IN_SIZE = 0x0000C400 >> 2,
		NV3089_IMAGE_IN_FORMAT = 0x0000C404 >> 2,
		NV3089_IMAGE_IN_OFFSET = 0x0000C408 >> 2,
		NV3089_IMAGE_IN = 0x0000C40C >> 2,

		//lv1 hypervisor commands
		GCM_SET_DRIVER_OBJECT = 0x0000E000 >> 2,
		GCM_FLIP_HEAD = 0X0000E920 >> 2,          //0xE920:0xE924: Flip head 0 or 1
		GCM_DRIVER_QUEUE = 0X0000E940 >> 2,       //0XE940:0xE95C: First two indices prepare display buffers, rest unknown
		GCM_SET_USER_COMMAND = 0x0000EB00 >> 2,   //0xEB00:0xEB04: User interrupt

		GCM_FLIP_COMMAND = 0x0000FEAC >> 2
	};


	enum Method : u32
	{
		/*
		CELL_GCM_METHOD_FLAG_NON_INCREMENT = 0x40000000,
		CELL_GCM_METHOD_FLAG_JUMP = 0x20000000,
		CELL_GCM_METHOD_FLAG_CALL = 0x00000002,
		CELL_GCM_METHOD_FLAG_RETURN = 0x00020000,
		*/
		RSX_METHOD_OLD_JUMP_CMD_MASK = 0xe0000003,
		RSX_METHOD_OLD_JUMP_CMD = 0x20000000,
		RSX_METHOD_OLD_JUMP_OFFSET_MASK = 0x1ffffffc,

		RSX_METHOD_INCREMENT_CMD_MASK = 0xe0030003,
		RSX_METHOD_INCREMENT_CMD = 0,
		RSX_METHOD_NON_INCREMENT_CMD_MASK = 0xe0030003,
		RSX_METHOD_NON_INCREMENT_CMD = 0x40000000,
		RSX_METHOD_COUNT_MASK = 0x1ffc0000,
		RSX_METHOD_COUNT_SHIFT = 18,
		RSX_METHOD_METHOD_MASK = 0x0000fffc,

		RSX_METHOD_NEW_JUMP_CMD_MASK = 0xe0000003,
		RSX_METHOD_NEW_JUMP_CMD = 0x00000001,
		RSX_METHOD_NEW_JUMP_OFFSET_MASK = 0xfffffffc,

		RSX_METHOD_CALL_CMD_MASK = 0x00000003,
		RSX_METHOD_CALL_CMD = 0x00000002,
		RSX_METHOD_CALL_OFFSET_MASK = 0x1ffffffc,

		RSX_METHOD_NON_METHOD_CMD_MASK = 0xa0030003,
		RSX_METHOD_RETURN_CMD = 0x00020000,
		RSX_METHOD_RETURN_MASK = 0xffff0003,

		RSX_METHOD_NOP_CMD = 0x00000000,
		RSX_METHOD_NOP_MASK = 0xbfff0003,

		// Stack is empty (invalid value)
		RSX_CALL_STACK_EMPTY = 0x00000003,
	};

	//Fog
	enum
	{
		CELL_GCM_FOG_MODE_LINEAR = 0x2601,
		CELL_GCM_FOG_MODE_EXP = 0x0800,
		CELL_GCM_FOG_MODE_EXP2 = 0x0801,
		CELL_GCM_FOG_MODE_EXP_ABS = 0x0802,
		CELL_GCM_FOG_MODE_EXP2_ABS = 0x0803,
		CELL_GCM_FOG_MODE_LINEAR_ABS = 0x0804,
	};

	// Surface clear bitfields (aggregates)
	enum
	{
		RSX_GCM_CLEAR_DEPTH_BIT = 0x01,
		RSX_GCM_CLEAR_STENCIL_BIT = 0x02,
		RSX_GCM_CLEAR_RED_BIT = 0x10,
		RSX_GCM_CLEAR_GREEN_BIT = 0x20,
		RSX_GCM_CLEAR_BLUE_BIT = 0x40,
		RSX_GCM_CLEAR_ALPHA_BIT = 0x80,

		RSX_GCM_CLEAR_COLOR_RG_MASK = (RSX_GCM_CLEAR_RED_BIT | RSX_GCM_CLEAR_GREEN_BIT),
		RSX_GCM_CLEAR_COLOR_RGB_MASK = (RSX_GCM_CLEAR_RED_BIT | RSX_GCM_CLEAR_GREEN_BIT | RSX_GCM_CLEAR_BLUE_BIT),
		RSX_GCM_CLEAR_COLOR_RGBA_MASK = (RSX_GCM_CLEAR_COLOR_RGB_MASK | RSX_GCM_CLEAR_ALPHA_BIT),
		RSX_GCM_CLEAR_DEPTH_STENCIL_MASK = (RSX_GCM_CLEAR_DEPTH_BIT | RSX_GCM_CLEAR_STENCIL_BIT),
		RSX_GCM_CLEAR_ANY_MASK = (RSX_GCM_CLEAR_COLOR_RGBA_MASK | RSX_GCM_CLEAR_DEPTH_STENCIL_MASK)
	};

	enum
	{
		// Surface Target
		CELL_GCM_SURFACE_TARGET_NONE = 0,
		CELL_GCM_SURFACE_TARGET_0 = 1,
		CELL_GCM_SURFACE_TARGET_1 = 2,
		CELL_GCM_SURFACE_TARGET_MRT1 = 0x13,
		CELL_GCM_SURFACE_TARGET_MRT2 = 0x17,
		CELL_GCM_SURFACE_TARGET_MRT3 = 0x1f,

		// Surface Depth
		CELL_GCM_SURFACE_Z16 = 1,
		CELL_GCM_SURFACE_Z24S8 = 2,

		// Surface Antialias
		CELL_GCM_SURFACE_CENTER_1 = 0,
		CELL_GCM_SURFACE_DIAGONAL_CENTERED_2 = 3,
		CELL_GCM_SURFACE_SQUARE_CENTERED_4 = 4,
		CELL_GCM_SURFACE_SQUARE_ROTATED_4 = 5,

		// Surface format
		CELL_GCM_SURFACE_X1R5G5B5_Z1R5G5B5 = 1,
		CELL_GCM_SURFACE_X1R5G5B5_O1R5G5B5 = 2,
		CELL_GCM_SURFACE_R5G6B5 = 3,
		CELL_GCM_SURFACE_X8R8G8B8_Z8R8G8B8 = 4,
		CELL_GCM_SURFACE_X8R8G8B8_O8R8G8B8 = 5,
		CELL_GCM_SURFACE_A8R8G8B8 = 8,
		CELL_GCM_SURFACE_B8 = 9,
		CELL_GCM_SURFACE_G8B8 = 10,
		CELL_GCM_SURFACE_F_W16Z16Y16X16 = 11,
		CELL_GCM_SURFACE_F_W32Z32Y32X32 = 12,
		CELL_GCM_SURFACE_F_X32 = 13,
		CELL_GCM_SURFACE_X8B8G8R8_Z8B8G8R8 = 14,
		CELL_GCM_SURFACE_X8B8G8R8_O8B8G8R8 = 15,
		CELL_GCM_SURFACE_A8B8G8R8 = 16,

		// Wrap
		CELL_GCM_TEXTURE_WRAP = 1,
		CELL_GCM_TEXTURE_MIRROR = 2,
		CELL_GCM_TEXTURE_CLAMP_TO_EDGE = 3,
		CELL_GCM_TEXTURE_BORDER = 4,
		CELL_GCM_TEXTURE_CLAMP = 5,
		CELL_GCM_TEXTURE_MIRROR_ONCE_CLAMP_TO_EDGE = 6,
		CELL_GCM_TEXTURE_MIRROR_ONCE_BORDER = 7,
		CELL_GCM_TEXTURE_MIRROR_ONCE_CLAMP = 8,

		// Max Anisotropy
		CELL_GCM_TEXTURE_MAX_ANISO_1 = 0,
		CELL_GCM_TEXTURE_MAX_ANISO_2 = 1,
		CELL_GCM_TEXTURE_MAX_ANISO_4 = 2,
		CELL_GCM_TEXTURE_MAX_ANISO_6 = 3,
		CELL_GCM_TEXTURE_MAX_ANISO_8 = 4,
		CELL_GCM_TEXTURE_MAX_ANISO_10 = 5,
		CELL_GCM_TEXTURE_MAX_ANISO_12 = 6,
		CELL_GCM_TEXTURE_MAX_ANISO_16 = 7,

		// Texture Filter
		CELL_GCM_TEXTURE_NEAREST = 1,
		CELL_GCM_TEXTURE_LINEAR = 2,
		CELL_GCM_TEXTURE_NEAREST_NEAREST = 3,
		CELL_GCM_TEXTURE_LINEAR_NEAREST = 4,
		CELL_GCM_TEXTURE_NEAREST_LINEAR = 5,
		CELL_GCM_TEXTURE_LINEAR_LINEAR = 6,
		CELL_GCM_TEXTURE_CONVOLUTION_MIN = 7,
		CELL_GCM_TEXTURE_CONVOLUTION_MAG = 4,
	};

	enum
	{
		CELL_GCM_CLEAR = 0x1500,
		CELL_GCM_AND = 0x1501,
		CELL_GCM_AND_REVERSE = 0x1502,
		CELL_GCM_COPY = 0x1503,
		CELL_GCM_AND_INVERTED = 0x1504,
		CELL_GCM_NOOP = 0x1505,
		CELL_GCM_XOR = 0x1506,
		CELL_GCM_OR = 0x1507,
		CELL_GCM_NOR = 0x1508,
		CELL_GCM_EQUIV = 0x1509,
		CELL_GCM_OR_REVERSE = 0x150B,
		CELL_GCM_COPY_INVERTED = 0x150C,
		CELL_GCM_OR_INVERTED = 0x150D,
		CELL_GCM_NAND = 0x150E,
		CELL_GCM_SET = 0x150F,
	};

	enum
	{
		CELL_GCM_TRANSFER_ORIGIN_CENTER = 1,
		CELL_GCM_TRANSFER_ORIGIN_CORNER = 2,

		CELL_GCM_TRANSFER_INTERPOLATOR_ZOH = 0,
		CELL_GCM_TRANSFER_INTERPOLATOR_FOH = 1,
	};

	enum
	{
		CELL_GCM_TRANSFER_OPERATION_SRCCOPY_AND = 0,
		CELL_GCM_TRANSFER_OPERATION_ROP_AND = 1,
		CELL_GCM_TRANSFER_OPERATION_BLEND_AND = 2,
		CELL_GCM_TRANSFER_OPERATION_SRCCOPY = 3,
		CELL_GCM_TRANSFER_OPERATION_SRCCOPY_PREMULT = 4,
		CELL_GCM_TRANSFER_OPERATION_BLEND_PREMULT = 5,
	};

	enum
	{
		CELL_GCM_TRANSFER_SCALE_FORMAT_A1R5G5B5 = 1,
		CELL_GCM_TRANSFER_SCALE_FORMAT_X1R5G5B5 = 2,
		CELL_GCM_TRANSFER_SCALE_FORMAT_A8R8G8B8 = 3,
		CELL_GCM_TRANSFER_SCALE_FORMAT_X8R8G8B8 = 4,
		CELL_GCM_TRANSFER_SCALE_FORMAT_CR8YB8CB8YA8 = 5,
		CELL_GCM_TRANSFER_SCALE_FORMAT_YB8CR8YA8CB8 = 6,
		CELL_GCM_TRANSFER_SCALE_FORMAT_R5G6B5 = 7,
		CELL_GCM_TRANSFER_SCALE_FORMAT_Y8 = 8,
		CELL_GCM_TRANSFER_SCALE_FORMAT_AY8 = 9,
		CELL_GCM_TRANSFER_SCALE_FORMAT_EYB8ECR8EYA8ECB8 = 10,
		CELL_GCM_TRANSFER_SCALE_FORMAT_ECR8EYB8ECB8EYA8 = 11,
		CELL_GCM_TRANSFER_SCALE_FORMAT_A8B8G8R8 = 12,
		CELL_GCM_TRANSFER_SCALE_FORMAT_X8B8G8R8 = 13,
	};

	enum
	{
		// Destination Format conversions
		CELL_GCM_TRANSFER_SURFACE_FORMAT_R5G6B5 = 4,
		CELL_GCM_TRANSFER_SURFACE_FORMAT_A8R8G8B8 = 10,
		CELL_GCM_TRANSFER_SURFACE_FORMAT_Y32 = 11,
	};

	enum
	{
		CELL_GCM_CONTEXT_SURFACE2D = 0x313371C3,
		CELL_GCM_CONTEXT_SWIZZLE2D = 0x31337A73,
	};

	enum
	{
		CELL_GCM_USER_CLIP_PLANE_DISABLE = 0,
		CELL_GCM_USER_CLIP_PLANE_ENABLE_LT = 1,
		CELL_GCM_USER_CLIP_PLANE_ENABLE_GE = 2,
	};

	enum
	{
		CELL_GCM_FLAT = 0x1D00,
		CELL_GCM_SMOOTH = 0x1D01,
	};

	enum
	{
		CELL_GCM_POLYGON_MODE_POINT = 0x1B00,
		CELL_GCM_POLYGON_MODE_LINE = 0x1B01,
		CELL_GCM_POLYGON_MODE_FILL = 0x1B02,
	};

	enum
	{
		CELL_GCM_WINDOW_ORIGIN_TOP = 0,
		CELL_GCM_WINDOW_ORIGIN_BOTTOM = 1,
		CELL_GCM_WINDOW_PIXEL_CENTER_HALF = 0,
		CELL_GCM_WINDOW_PIXEL_CENTER_INTEGER = 1,
	};

	enum
	{
		RSX_VERTEX_BASE_TYPE_UNDEFINED = 0,
		RSX_VERTEX_BASE_TYPE_SNORM16,
		RSX_VERTEX_BASE_TYPE_FLOAT,
		RSX_VERTEX_BASE_TYPE_HALF_FLOAT,
		RSX_VERTEX_BASE_TYPE_UNORM8,
		RSX_VERTEX_BASE_TYPE_SINT16,
		RSX_VERTEX_BASE_TYPE_CMP32,
		RSX_VERTEX_BASE_TYPE_UINT8,
	};
}

// Public export
// TODO: Don't leak namespaces
using namespace gcm;

namespace rsx
{
	template <typename T, u32 min_val, u32 max_val>
	expected<T> gcm_enum_cast(u32 value)
	{
		if (value >= min_val && value <= max_val)
		{
			return static_cast<T>(value);
		}

		return exception_utils::soft_exception_t{ rsx::exception_utils::invalid_enum };
	}

	template <typename T>
	expected<T> gcm_enum_cast(u32 value, std::initializer_list<u32> allowed)
	{
		for (const auto v : allowed)
		{
			if (value == v)
			{
				return static_cast<T>(value);
			}
		}

		return exception_utils::soft_exception_t{ rsx::exception_utils::invalid_enum };
	}

	template <typename T>
	expected<T> gcm_enum_cast(u32 value, std::initializer_list<const std::array<u32, 2>> allowed)
	{
		for (const auto& range : allowed)
		{
			if (value >= range[0] && value <= range[1])
			{
				return static_cast<T>(value);
			}
		}

		return exception_utils::soft_exception_t{ rsx::exception_utils::invalid_enum };
	}

	enum class vertex_base_type : u8
	{
		s1 = RSX_VERTEX_BASE_TYPE_SNORM16,    ///< signed normalized 16-bit int
		f  = RSX_VERTEX_BASE_TYPE_FLOAT,      ///< float
		sf = RSX_VERTEX_BASE_TYPE_HALF_FLOAT, ///< half float
		ub = RSX_VERTEX_BASE_TYPE_UNORM8,     ///< unsigned byte interpreted as 0.f and 1.f
		s32k = RSX_VERTEX_BASE_TYPE_SINT16,   ///< signed 16bits int
		cmp  = RSX_VERTEX_BASE_TYPE_CMP32,    ///< compressed aka X11G11Z10 and always 1. W.
		ub256 = RSX_VERTEX_BASE_TYPE_UINT8,   ///< unsigned byte interpreted as between 0 and 255.
	};

	static inline auto to_vertex_base_type(u32 in)
	{
		return in
			? gcm_enum_cast<
				vertex_base_type,
				RSX_VERTEX_BASE_TYPE_SNORM16,
				RSX_VERTEX_BASE_TYPE_UINT8>(in)
			: expected(vertex_base_type::ub256);
	}

	enum class index_array_type : u8
	{
		u32 = CELL_GCM_DRAW_INDEX_ARRAY_TYPE_32,
		u16 = CELL_GCM_DRAW_INDEX_ARRAY_TYPE_16
	};

	enum class primitive_type : u8
	{
		points = CELL_GCM_PRIMITIVE_POINTS,
		lines  = CELL_GCM_PRIMITIVE_LINES,
		line_loop = CELL_GCM_PRIMITIVE_LINE_LOOP, // line strip with last end being joined with first end.
		line_strip = CELL_GCM_PRIMITIVE_LINE_STRIP,
		triangles  = CELL_GCM_PRIMITIVE_TRIANGLES,
		triangle_strip = CELL_GCM_PRIMITIVE_TRIANGLE_STRIP,
		triangle_fan = CELL_GCM_PRIMITIVE_TRIANGLE_FAN, // like strip except that every triangle share the first vertex and one instead of 2 from previous triangle.
		quads = CELL_GCM_PRIMITIVE_QUADS,
		quad_strip = CELL_GCM_PRIMITIVE_QUAD_STRIP,
		polygon = CELL_GCM_PRIMITIVE_POLYGON, // convex polygon
	};

	static inline auto to_primitive_type(u32 in)
	{
		return gcm_enum_cast<
			primitive_type,
			CELL_GCM_PRIMITIVE_POINTS,
			CELL_GCM_PRIMITIVE_POLYGON>(in);
	}

	enum class surface_target : u8
	{
		none = CELL_GCM_SURFACE_TARGET_NONE,
		surface_a = CELL_GCM_SURFACE_TARGET_0,
		surface_b = CELL_GCM_SURFACE_TARGET_1,
		surfaces_a_b = CELL_GCM_SURFACE_TARGET_MRT1,
		surfaces_a_b_c = CELL_GCM_SURFACE_TARGET_MRT2,
		surfaces_a_b_c_d = CELL_GCM_SURFACE_TARGET_MRT3,
	};

	static inline auto to_surface_target(u32 in)
	{
		return gcm_enum_cast<surface_target>(in, {
			CELL_GCM_SURFACE_TARGET_0,
			CELL_GCM_SURFACE_TARGET_MRT1,
			CELL_GCM_SURFACE_TARGET_NONE,
			CELL_GCM_SURFACE_TARGET_1,
			CELL_GCM_SURFACE_TARGET_MRT2,
			CELL_GCM_SURFACE_TARGET_MRT3
		});
	}

	enum class surface_depth_format : u8
	{
		z16 = CELL_GCM_SURFACE_Z16,     // typeless 16 bits depth
		z24s8 = CELL_GCM_SURFACE_Z24S8, // typeless 24 bits depth + 8 bits stencil
	};

	enum class surface_depth_format2 : u8
	{
		z16_uint = CELL_GCM_SURFACE_Z16,      // unsigned 16 bits depth
		z24s8_uint = CELL_GCM_SURFACE_Z24S8,  // unsigned 24 bits depth + 8 bits stencil
		z16_float,   // floating point 16 bits depth
		z24s8_float, // floating point 24 bits depth + 8 bits stencil
	};

	static inline auto to_surface_depth_format(u32 in)
	{
		return gcm_enum_cast<
			surface_depth_format,
			CELL_GCM_SURFACE_Z16,
			CELL_GCM_SURFACE_Z24S8>(in);
	}

	constexpr bool operator ==(surface_depth_format2 rhs, surface_depth_format lhs)
	{
		switch (lhs)
		{
		case surface_depth_format::z16:
			return (rhs == surface_depth_format2::z16_uint || rhs == surface_depth_format2::z16_float);
		case surface_depth_format::z24s8:
			return (rhs == surface_depth_format2::z24s8_uint || rhs == surface_depth_format2::z24s8_float);
		[[unlikely]] default:
			return false;
		}
	}

	enum class surface_raster_type : u8
	{
		undefined = CELL_GCM_ZERO, // TODO: Drop this (used in surface cache for optional args)
		linear = CELL_GCM_SURFACE_PITCH,
		swizzle = CELL_GCM_SURFACE_SWIZZLE,
	};

	static inline auto to_surface_raster_type(u32 in)
	{
		return gcm_enum_cast<
			surface_raster_type,
			CELL_GCM_SURFACE_PITCH,
			CELL_GCM_SURFACE_SWIZZLE>(in);
	}

	enum class surface_antialiasing : u8
	{
		center_1_sample = CELL_GCM_SURFACE_CENTER_1,
		diagonal_centered_2_samples = CELL_GCM_SURFACE_DIAGONAL_CENTERED_2,
		square_centered_4_samples = CELL_GCM_SURFACE_SQUARE_CENTERED_4,
		square_rotated_4_samples = CELL_GCM_SURFACE_SQUARE_ROTATED_4,
	};

	static inline auto to_surface_antialiasing(u32 in)
	{
		return gcm_enum_cast<surface_antialiasing>(in,
		{
			{ CELL_GCM_SURFACE_CENTER_1, CELL_GCM_SURFACE_CENTER_1 },
			{ CELL_GCM_SURFACE_DIAGONAL_CENTERED_2, CELL_GCM_SURFACE_SQUARE_ROTATED_4 }
		});
	}

	enum class surface_color_format : u8
	{
		x1r5g5b5_z1r5g5b5 = CELL_GCM_SURFACE_X1R5G5B5_Z1R5G5B5,
		x1r5g5b5_o1r5g5b5 = CELL_GCM_SURFACE_X1R5G5B5_O1R5G5B5,
		r5g6b5 = CELL_GCM_SURFACE_R5G6B5,
		x8r8g8b8_z8r8g8b8 = CELL_GCM_SURFACE_X8R8G8B8_Z8R8G8B8,
		x8r8g8b8_o8r8g8b8 = CELL_GCM_SURFACE_X8R8G8B8_O8R8G8B8,
		a8r8g8b8 = CELL_GCM_SURFACE_A8R8G8B8,
		b8 = CELL_GCM_SURFACE_B8,
		g8b8 = CELL_GCM_SURFACE_G8B8,
		x8b8g8r8_z8b8g8r8 = CELL_GCM_SURFACE_X8B8G8R8_Z8B8G8R8,
		x8b8g8r8_o8b8g8r8 = CELL_GCM_SURFACE_X8B8G8R8_O8B8G8R8,
		a8b8g8r8 = CELL_GCM_SURFACE_A8B8G8R8,
		w16z16y16x16 = CELL_GCM_SURFACE_F_W16Z16Y16X16,
		w32z32y32x32 = CELL_GCM_SURFACE_F_W32Z32Y32X32,
		x32 = CELL_GCM_SURFACE_F_X32
	};

	static inline auto to_surface_color_format(u32 in)
	{
		return gcm_enum_cast<
			surface_color_format,
			CELL_GCM_SURFACE_X1R5G5B5_Z1R5G5B5,
			CELL_GCM_SURFACE_A8B8G8R8>(in);
	}

	enum class window_origin : u8
	{
		top = CELL_GCM_WINDOW_ORIGIN_TOP,
		bottom = CELL_GCM_WINDOW_ORIGIN_BOTTOM
	};

	static inline auto to_window_origin(u32 in)
	{
		return gcm_enum_cast<
			window_origin,
			CELL_GCM_WINDOW_ORIGIN_TOP,
			CELL_GCM_WINDOW_ORIGIN_BOTTOM>(in);
	}

	enum class window_pixel_center : u8
	{
		half = CELL_GCM_WINDOW_PIXEL_CENTER_HALF,
		integer = CELL_GCM_WINDOW_PIXEL_CENTER_INTEGER
	};

	static inline auto to_window_pixel_center(u32 in)
	{
		return gcm_enum_cast<
			window_pixel_center,
			CELL_GCM_WINDOW_PIXEL_CENTER_HALF,
			CELL_GCM_WINDOW_PIXEL_CENTER_INTEGER>(in);
	}

	enum class comparison_function : u16
	{
		never = CELL_GCM_NEVER,
		less = CELL_GCM_LESS,
		equal = CELL_GCM_EQUAL,
		less_or_equal = CELL_GCM_LEQUAL,
		greater = CELL_GCM_GREATER,
		not_equal = CELL_GCM_NOTEQUAL,
		greater_or_equal = CELL_GCM_GEQUAL,
		always = CELL_GCM_ALWAYS,
	};

	static inline auto to_comparison_function(u32 in)
	{
		return gcm_enum_cast<
			comparison_function,
			CELL_GCM_NEVER,
			CELL_GCM_ALWAYS>(in | 0x200);
	}

	enum class fog_mode : u16
	{
		linear = CELL_GCM_FOG_MODE_LINEAR,
		exponential = CELL_GCM_FOG_MODE_EXP,
		exponential2 = CELL_GCM_FOG_MODE_EXP2,
		exponential_abs = CELL_GCM_FOG_MODE_EXP_ABS,
		exponential2_abs = CELL_GCM_FOG_MODE_EXP2_ABS,
		linear_abs = CELL_GCM_FOG_MODE_LINEAR_ABS
	};

	static inline auto to_fog_mode(u32 in)
	{
		if (in == CELL_GCM_FOG_MODE_LINEAR)
		{
			return expected(fog_mode::linear);
		}

		return gcm_enum_cast<
			fog_mode,
			CELL_GCM_FOG_MODE_EXP,
			CELL_GCM_FOG_MODE_LINEAR_ABS>(in);
	}

	/**
	* Use an extra cubemap format
	*/
	enum class texture_dimension_extended : u8
	{
		texture_dimension_1d = 0,
		texture_dimension_2d = 1,
		texture_dimension_cubemap = 2,
		texture_dimension_3d = 3,
	};

	enum class texture_dimension : u8
	{
		dimension1d = 1,
		dimension2d = 2,
		dimension3d = 3,
	};

	static inline auto to_texture_dimension(u32 in)
	{
		return gcm_enum_cast<texture_dimension, 1, 3>(in);
	}

	enum class texture_wrap_mode : u8
	{
		wrap = CELL_GCM_TEXTURE_WRAP,
		mirror = CELL_GCM_TEXTURE_MIRROR,
		clamp_to_edge = CELL_GCM_TEXTURE_CLAMP_TO_EDGE,
		border = CELL_GCM_TEXTURE_BORDER,
		clamp = CELL_GCM_TEXTURE_CLAMP,
		mirror_once_clamp_to_edge = CELL_GCM_TEXTURE_MIRROR_ONCE_CLAMP_TO_EDGE,
		mirror_once_border = CELL_GCM_TEXTURE_MIRROR_ONCE_BORDER,
		mirror_once_clamp = CELL_GCM_TEXTURE_MIRROR_ONCE_CLAMP,
	};

	static inline auto to_texture_wrap_mode(u32 in)
	{
		return gcm_enum_cast<
			texture_wrap_mode,
			CELL_GCM_TEXTURE_WRAP,
			CELL_GCM_TEXTURE_MIRROR_ONCE_CLAMP>(in);
	}

	enum class texture_max_anisotropy : u8
	{
		x1 = CELL_GCM_TEXTURE_MAX_ANISO_1,
		x2 = CELL_GCM_TEXTURE_MAX_ANISO_2,
		x4 = CELL_GCM_TEXTURE_MAX_ANISO_4,
		x6 = CELL_GCM_TEXTURE_MAX_ANISO_6,
		x8 = CELL_GCM_TEXTURE_MAX_ANISO_8,
		x10 = CELL_GCM_TEXTURE_MAX_ANISO_10,
		x12 = CELL_GCM_TEXTURE_MAX_ANISO_12,
		x16 = CELL_GCM_TEXTURE_MAX_ANISO_16,
	};

	static inline auto to_texture_max_anisotropy(u32 in)
	{
		return gcm_enum_cast<
			texture_max_anisotropy,
			CELL_GCM_TEXTURE_MAX_ANISO_1,
			CELL_GCM_TEXTURE_MAX_ANISO_16>(in);
	}

	enum class texture_minify_filter : u8
	{
		nearest = CELL_GCM_TEXTURE_NEAREST, ///< no filtering, mipmap base level
		linear  = CELL_GCM_TEXTURE_LINEAR, ///< linear filtering, mipmap base level
		nearest_nearest = CELL_GCM_TEXTURE_NEAREST_NEAREST, ///< no filtering, closest mipmap level
		linear_nearest = CELL_GCM_TEXTURE_LINEAR_NEAREST, ///< linear filtering, closest mipmap level
		nearest_linear = CELL_GCM_TEXTURE_NEAREST_LINEAR, ///< no filtering, linear mix between closest mipmap levels
		linear_linear  = CELL_GCM_TEXTURE_LINEAR_LINEAR, ///< linear filtering, linear mix between closest mipmap levels
		convolution_min = CELL_GCM_TEXTURE_CONVOLUTION_MIN, ///< Unknown mode but looks close to linear_linear
	};

	static inline auto to_texture_minify_filter(u32 in)
	{
		return gcm_enum_cast<
			texture_minify_filter,
			CELL_GCM_TEXTURE_NEAREST,
			CELL_GCM_TEXTURE_CONVOLUTION_MIN>(in);
	}

	enum class texture_magnify_filter : u8
	{
		nearest = CELL_GCM_TEXTURE_NEAREST, ///< no filtering
		linear = CELL_GCM_TEXTURE_LINEAR, ///< linear filtering
		convolution_mag = CELL_GCM_TEXTURE_CONVOLUTION_MAG, ///< Unknown mode but looks close to linear
	};

	static inline auto to_texture_magnify_filter(u32 in)
	{
		return gcm_enum_cast<texture_magnify_filter>(in, { CELL_GCM_TEXTURE_LINEAR, CELL_GCM_TEXTURE_NEAREST, CELL_GCM_TEXTURE_CONVOLUTION_MAG });
	}

	enum class stencil_op : u16
	{
		keep = CELL_GCM_KEEP,
		zero = CELL_GCM_ZERO,
		replace = CELL_GCM_REPLACE,
		incr = CELL_GCM_INCR,
		decr = CELL_GCM_DECR,
		invert = CELL_GCM_INVERT,
		incr_wrap = CELL_GCM_INCR_WRAP,
		decr_wrap = CELL_GCM_DECR_WRAP,
	};

	static inline auto to_stencil_op(u32 in)
	{
		return gcm_enum_cast<stencil_op>(in,
		{
			CELL_GCM_KEEP, CELL_GCM_ZERO, CELL_GCM_REPLACE,
			CELL_GCM_INCR, CELL_GCM_DECR, CELL_GCM_INVERT,
			CELL_GCM_INCR_WRAP, CELL_GCM_DECR_WRAP
		});
	}

	enum class blend_equation : u16
	{
		add = CELL_GCM_FUNC_ADD,
		min = CELL_GCM_MIN,
		max = CELL_GCM_MAX,
		subtract = CELL_GCM_FUNC_SUBTRACT,
		reverse_subtract = CELL_GCM_FUNC_REVERSE_SUBTRACT,
		reverse_subtract_signed = CELL_GCM_FUNC_REVERSE_SUBTRACT_SIGNED,
		add_signed = CELL_GCM_FUNC_ADD_SIGNED,
		reverse_add_signed = CELL_GCM_FUNC_REVERSE_ADD_SIGNED,
	};

	static inline auto to_blend_equation(u32 in)
	{
		return gcm_enum_cast<blend_equation>(in,
		{
			{ CELL_GCM_FUNC_ADD, CELL_GCM_FUNC_REVERSE_SUBTRACT },
			{ CELL_GCM_FUNC_REVERSE_SUBTRACT_SIGNED, CELL_GCM_FUNC_REVERSE_ADD_SIGNED }
		});
	}

	enum class blend_factor : u16
	{
		zero = CELL_GCM_ZERO,
		one = CELL_GCM_ONE,
		src_color = CELL_GCM_SRC_COLOR,
		one_minus_src_color = CELL_GCM_ONE_MINUS_SRC_COLOR,
		dst_color = CELL_GCM_DST_COLOR,
		one_minus_dst_color = CELL_GCM_ONE_MINUS_DST_COLOR,
		src_alpha = CELL_GCM_SRC_ALPHA,
		one_minus_src_alpha = CELL_GCM_ONE_MINUS_SRC_ALPHA,
		dst_alpha = CELL_GCM_DST_ALPHA,
		one_minus_dst_alpha = CELL_GCM_ONE_MINUS_DST_ALPHA,
		src_alpha_saturate = CELL_GCM_SRC_ALPHA_SATURATE,
		constant_color = CELL_GCM_CONSTANT_COLOR,
		one_minus_constant_color = CELL_GCM_ONE_MINUS_CONSTANT_COLOR,
		constant_alpha = CELL_GCM_CONSTANT_ALPHA,
		one_minus_constant_alpha = CELL_GCM_ONE_MINUS_CONSTANT_ALPHA,
	};

	static inline auto to_blend_factor(u32 in)
	{
		return gcm_enum_cast<blend_factor>(in,
		{
			{ CELL_GCM_SRC_COLOR, CELL_GCM_ONE_MINUS_CONSTANT_ALPHA },
			{ CELL_GCM_ZERO, CELL_GCM_ONE }
		});
	}

	enum class logic_op : u16
	{
		logic_clear = CELL_GCM_CLEAR,
		logic_and = CELL_GCM_AND,
		logic_and_reverse = CELL_GCM_AND_REVERSE,
		logic_copy = CELL_GCM_COPY,
		logic_and_inverted = CELL_GCM_AND_INVERTED,
		logic_noop = CELL_GCM_NOOP,
		logic_xor = CELL_GCM_XOR,
		logic_or = CELL_GCM_OR,
		logic_nor = CELL_GCM_NOR,
		logic_equiv = CELL_GCM_EQUIV,
		logic_invert = CELL_GCM_INVERT,
		logic_or_reverse = CELL_GCM_OR_REVERSE,
		logic_copy_inverted = CELL_GCM_COPY_INVERTED,
		logic_or_inverted = CELL_GCM_OR_INVERTED,
		logic_nand = CELL_GCM_NAND,
		logic_set = CELL_GCM_SET,
	};

	static inline auto to_logic_op(u32 in)
	{
		return gcm_enum_cast<
			logic_op,
			CELL_GCM_CLEAR,
			CELL_GCM_SET>(in);
	}

	enum class front_face : u16
	{
		cw = CELL_GCM_CW, /// clockwise
		ccw = CELL_GCM_CCW, /// counter clockwise
	};

	static inline auto to_front_face(u32 in)
	{
		return gcm_enum_cast<
			front_face,
			CELL_GCM_CW,
			CELL_GCM_CCW>(in);
	}

	enum class cull_face : u16
	{
		front = CELL_GCM_FRONT,
		back = CELL_GCM_BACK,
		front_and_back = CELL_GCM_FRONT_AND_BACK
	};

	static inline auto to_cull_face(u32 in)
	{
		return gcm_enum_cast<cull_face>(in,
		{
			CELL_GCM_FRONT,
			CELL_GCM_BACK,
			CELL_GCM_FRONT_AND_BACK
		});
	}

	enum class user_clip_plane_op : u8
	{
		disable = CELL_GCM_USER_CLIP_PLANE_DISABLE,
		less_than = CELL_GCM_USER_CLIP_PLANE_ENABLE_LT,
		greater_or_equal = CELL_GCM_USER_CLIP_PLANE_ENABLE_GE,
	};

	static inline auto to_user_clip_plane_op(u32 in)
	{
		return gcm_enum_cast<
			user_clip_plane_op,
			CELL_GCM_USER_CLIP_PLANE_DISABLE,
			CELL_GCM_USER_CLIP_PLANE_ENABLE_GE>(in);
	}

	enum class shading_mode : u16
	{
		smooth = CELL_GCM_SMOOTH,
		flat = CELL_GCM_FLAT,
	};

	static inline auto to_shading_mode(u32 in)
	{
		return gcm_enum_cast<
			shading_mode,
			CELL_GCM_FLAT,
			CELL_GCM_SMOOTH>(in);
	}

	enum class polygon_mode : u16
	{
		point = CELL_GCM_POLYGON_MODE_POINT,
		line = CELL_GCM_POLYGON_MODE_LINE,
		fill = CELL_GCM_POLYGON_MODE_FILL,
	};

	static inline auto to_polygon_mode(u32 in)
	{
		return gcm_enum_cast<
			polygon_mode,
			CELL_GCM_POLYGON_MODE_POINT,
			CELL_GCM_POLYGON_MODE_FILL>(in);
	}

	namespace blit_engine
	{
		enum class transfer_origin : u8
		{
			center = CELL_GCM_TRANSFER_ORIGIN_CENTER,
			corner = CELL_GCM_TRANSFER_ORIGIN_CORNER,
		};

		static inline auto to_transfer_origin(u32 in)
		{
			return gcm_enum_cast<
				transfer_origin,
				CELL_GCM_TRANSFER_ORIGIN_CENTER,
				CELL_GCM_TRANSFER_ORIGIN_CORNER>(in);
		}

		enum class transfer_interpolator : u8
		{
			zoh = CELL_GCM_TRANSFER_INTERPOLATOR_ZOH,
			foh = CELL_GCM_TRANSFER_INTERPOLATOR_FOH,
		};

		static inline auto to_transfer_interpolator(u32 in)
		{
			return gcm_enum_cast<
				transfer_interpolator,
				CELL_GCM_TRANSFER_INTERPOLATOR_ZOH,
				CELL_GCM_TRANSFER_INTERPOLATOR_FOH>(in);
		}

		enum class transfer_operation : u8
		{
			srccopy_and = CELL_GCM_TRANSFER_OPERATION_SRCCOPY_AND,
			rop_and = CELL_GCM_TRANSFER_OPERATION_ROP_AND,
			blend_and = CELL_GCM_TRANSFER_OPERATION_BLEND_AND,
			srccopy = CELL_GCM_TRANSFER_OPERATION_SRCCOPY,
			srccopy_premult = CELL_GCM_TRANSFER_OPERATION_SRCCOPY_PREMULT,
			blend_premult = CELL_GCM_TRANSFER_OPERATION_BLEND_PREMULT,
		};

		static inline auto to_transfer_operation(u32 in)
		{
			return gcm_enum_cast<
				transfer_operation,
				CELL_GCM_TRANSFER_OPERATION_SRCCOPY_AND,
				CELL_GCM_TRANSFER_OPERATION_BLEND_PREMULT>(in);
		}

		enum class transfer_source_format : u8
		{
			a1r5g5b5 = CELL_GCM_TRANSFER_SCALE_FORMAT_A1R5G5B5,
			x1r5g5b5 = CELL_GCM_TRANSFER_SCALE_FORMAT_X1R5G5B5,
			a8r8g8b8 = CELL_GCM_TRANSFER_SCALE_FORMAT_A8R8G8B8,
			x8r8g8b8 = CELL_GCM_TRANSFER_SCALE_FORMAT_X8R8G8B8,
			cr8yb8cb8ya8 = CELL_GCM_TRANSFER_SCALE_FORMAT_CR8YB8CB8YA8,
			yb8cr8ya8cb8 = CELL_GCM_TRANSFER_SCALE_FORMAT_YB8CR8YA8CB8,
			r5g6b5 = CELL_GCM_TRANSFER_SCALE_FORMAT_R5G6B5,
			y8 = CELL_GCM_TRANSFER_SCALE_FORMAT_Y8,
			ay8 = CELL_GCM_TRANSFER_SCALE_FORMAT_AY8,
			eyb8ecr8eya8ecb8 = CELL_GCM_TRANSFER_SCALE_FORMAT_EYB8ECR8EYA8ECB8,
			ecr8eyb8ecb8eya8 = CELL_GCM_TRANSFER_SCALE_FORMAT_ECR8EYB8ECB8EYA8,
			a8b8g8r8 = CELL_GCM_TRANSFER_SCALE_FORMAT_A8B8G8R8,
			x8b8g8r8 = CELL_GCM_TRANSFER_SCALE_FORMAT_X8B8G8R8,
		};

		static inline auto to_transfer_source_format(u32 in)
		{
			return gcm_enum_cast<
				transfer_source_format,
				CELL_GCM_TRANSFER_SCALE_FORMAT_A1R5G5B5,
				CELL_GCM_TRANSFER_SCALE_FORMAT_X8B8G8R8>(in);
		}

		enum class transfer_destination_format : u8
		{
			r5g6b5 = CELL_GCM_TRANSFER_SURFACE_FORMAT_R5G6B5,
			a8r8g8b8 = CELL_GCM_TRANSFER_SURFACE_FORMAT_A8R8G8B8,
			y32 = CELL_GCM_TRANSFER_SURFACE_FORMAT_Y32,
		};

		static inline auto to_transfer_destination_format(u32 in)
		{
			return gcm_enum_cast<transfer_destination_format>(in,
			{
				CELL_GCM_TRANSFER_SURFACE_FORMAT_A8R8G8B8,
				CELL_GCM_TRANSFER_SURFACE_FORMAT_R5G6B5,
				CELL_GCM_TRANSFER_SURFACE_FORMAT_Y32
			});
		}

		enum class context_surface : u32
		{
			surface2d = CELL_GCM_CONTEXT_SURFACE2D,
			swizzle2d = CELL_GCM_CONTEXT_SWIZZLE2D,
		};

		static inline auto to_context_surface(u32 in)
		{
			return gcm_enum_cast<context_surface>(in,
			{
				CELL_GCM_CONTEXT_SURFACE2D,
				CELL_GCM_CONTEXT_SWIZZLE2D
			});
		}

		enum class context_dma : u32
		{
			to_memory_get_report = CELL_GCM_CONTEXT_DMA_REPORT_LOCATION_LOCAL,
			report_location_main = CELL_GCM_CONTEXT_DMA_REPORT_LOCATION_MAIN,
			memory_host_buffer = CELL_GCM_CONTEXT_DMA_MEMORY_HOST_BUFFER,
		};

		static inline auto to_context_dma(u32 in)
		{
			return gcm_enum_cast<context_dma>(in,
			{
				CELL_GCM_CONTEXT_DMA_REPORT_LOCATION_LOCAL,
				CELL_GCM_CONTEXT_DMA_REPORT_LOCATION_MAIN,
				CELL_GCM_CONTEXT_DMA_MEMORY_HOST_BUFFER
			});
		}
	}
}
