// Copyright 2009 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#pragma once

#include <string>

#include "Common/BitField.h"
#include "Common/CommonTypes.h"

#pragma pack(4)

enum
{
  BPMEM_GENMODE = 0x00,
  BPMEM_DISPLAYCOPYFILTER = 0x01,  // 0x01 + 4
  BPMEM_IND_MTXA = 0x06,           // 0x06 + (3 * 3)
  BPMEM_IND_MTXB = 0x07,           // 0x07 + (3 * 3)
  BPMEM_IND_MTXC = 0x08,           // 0x08 + (3 * 3)
  BPMEM_IND_IMASK = 0x0F,
  BPMEM_IND_CMD = 0x10,  // 0x10 + 16
  BPMEM_SCISSORTL = 0x20,
  BPMEM_SCISSORBR = 0x21,
  BPMEM_LINEPTWIDTH = 0x22,
  BPMEM_PERF0_TRI = 0x23,
  BPMEM_PERF0_QUAD = 0x24,
  BPMEM_RAS1_SS0 = 0x25,
  BPMEM_RAS1_SS1 = 0x26,
  BPMEM_IREF = 0x27,
  BPMEM_TREF = 0x28,      // 0x28 + 8
  BPMEM_SU_SSIZE = 0x30,  // 0x30 + (2 * 8)
  BPMEM_SU_TSIZE = 0x31,  // 0x31 + (2 * 8)
  BPMEM_ZMODE = 0x40,
  BPMEM_BLENDMODE = 0x41,
  BPMEM_CONSTANTALPHA = 0x42,
  BPMEM_ZCOMPARE = 0x43,
  BPMEM_FIELDMASK = 0x44,
  BPMEM_SETDRAWDONE = 0x45,
  BPMEM_BUSCLOCK0 = 0x46,
  BPMEM_PE_TOKEN_ID = 0x47,
  BPMEM_PE_TOKEN_INT_ID = 0x48,
  BPMEM_EFB_TL = 0x49,
  BPMEM_EFB_BR = 0x4A,
  BPMEM_EFB_ADDR = 0x4B,
  BPMEM_MIPMAP_STRIDE = 0x4D,
  BPMEM_COPYYSCALE = 0x4E,
  BPMEM_CLEAR_AR = 0x4F,
  BPMEM_CLEAR_GB = 0x50,
  BPMEM_CLEAR_Z = 0x51,
  BPMEM_TRIGGER_EFB_COPY = 0x52,
  BPMEM_COPYFILTER0 = 0x53,
  BPMEM_COPYFILTER1 = 0x54,
  BPMEM_CLEARBBOX1 = 0x55,
  BPMEM_CLEARBBOX2 = 0x56,
  BPMEM_CLEAR_PIXEL_PERF = 0x57,
  BPMEM_REVBITS = 0x58,
  BPMEM_SCISSOROFFSET = 0x59,
  BPMEM_PRELOAD_ADDR = 0x60,
  BPMEM_PRELOAD_TMEMEVEN = 0x61,
  BPMEM_PRELOAD_TMEMODD = 0x62,
  BPMEM_PRELOAD_MODE = 0x63,
  BPMEM_LOADTLUT0 = 0x64,
  BPMEM_LOADTLUT1 = 0x65,
  BPMEM_TEXINVALIDATE = 0x66,
  BPMEM_PERF1 = 0x67,
  BPMEM_FIELDMODE = 0x68,
  BPMEM_BUSCLOCK1 = 0x69,
  BPMEM_TX_SETMODE0 = 0x80,     // 0x80 + 4
  BPMEM_TX_SETMODE1 = 0x84,     // 0x84 + 4
  BPMEM_TX_SETIMAGE0 = 0x88,    // 0x88 + 4
  BPMEM_TX_SETIMAGE1 = 0x8C,    // 0x8C + 4
  BPMEM_TX_SETIMAGE2 = 0x90,    // 0x90 + 4
  BPMEM_TX_SETIMAGE3 = 0x94,    // 0x94 + 4
  BPMEM_TX_SETTLUT = 0x98,      // 0x98 + 4
  BPMEM_TX_SETMODE0_4 = 0xA0,   // 0xA0 + 4
  BPMEM_TX_SETMODE1_4 = 0xA4,   // 0xA4 + 4
  BPMEM_TX_SETIMAGE0_4 = 0xA8,  // 0xA8 + 4
  BPMEM_TX_SETIMAGE1_4 = 0xAC,  // 0xA4 + 4
  BPMEM_TX_SETIMAGE2_4 = 0xB0,  // 0xB0 + 4
  BPMEM_TX_SETIMAGE3_4 = 0xB4,  // 0xB4 + 4
  BPMEM_TX_SETTLUT_4 = 0xB8,    // 0xB8 + 4
  BPMEM_TEV_COLOR_ENV = 0xC0,   // 0xC0 + (2 * 16)
  BPMEM_TEV_ALPHA_ENV = 0xC1,   // 0xC1 + (2 * 16)
  BPMEM_TEV_COLOR_RA = 0xE0,    // 0xE0 + (2 * 4)
  BPMEM_TEV_COLOR_BG = 0xE1,    // 0xE1 + (2 * 4)
  BPMEM_FOGRANGE = 0xE8,        // 0xE8 + 6
  BPMEM_FOGPARAM0 = 0xEE,
  BPMEM_FOGBMAGNITUDE = 0xEF,
  BPMEM_FOGBEXPONENT = 0xF0,
  BPMEM_FOGPARAM3 = 0xF1,
  BPMEM_FOGCOLOR = 0xF2,
  BPMEM_ALPHACOMPARE = 0xF3,
  BPMEM_BIAS = 0xF4,
  BPMEM_ZTEX2 = 0xF5,
  BPMEM_TEV_KSEL = 0xF6,  // 0xF6 + 8
  BPMEM_BP_MASK = 0xFE,
};

// Tev/combiner things

// TEV scaling type
enum : u32
{
  TEVSCALE_1 = 0,
  TEVSCALE_2 = 1,
  TEVSCALE_4 = 2,
  TEVDIVIDE_2 = 3
};

enum : u32
{
  TEVCMP_R8 = 0,
  TEVCMP_GR16 = 1,
  TEVCMP_BGR24 = 2,
  TEVCMP_RGB8 = 3
};

// TEV combiner operator
enum : u32
{
  TEVOP_ADD = 0,
  TEVOP_SUB = 1,
  TEVCMP_R8_GT = 8,
  TEVCMP_R8_EQ = 9,
  TEVCMP_GR16_GT = 10,
  TEVCMP_GR16_EQ = 11,
  TEVCMP_BGR24_GT = 12,
  TEVCMP_BGR24_EQ = 13,
  TEVCMP_RGB8_GT = 14,
  TEVCMP_RGB8_EQ = 15,
  TEVCMP_A8_GT = TEVCMP_RGB8_GT,
  TEVCMP_A8_EQ = TEVCMP_RGB8_EQ
};

// TEV color combiner input
enum : u32
{
  TEVCOLORARG_CPREV = 0,
  TEVCOLORARG_APREV = 1,
  TEVCOLORARG_C0 = 2,
  TEVCOLORARG_A0 = 3,
  TEVCOLORARG_C1 = 4,
  TEVCOLORARG_A1 = 5,
  TEVCOLORARG_C2 = 6,
  TEVCOLORARG_A2 = 7,
  TEVCOLORARG_TEXC = 8,
  TEVCOLORARG_TEXA = 9,
  TEVCOLORARG_RASC = 10,
  TEVCOLORARG_RASA = 11,
  TEVCOLORARG_ONE = 12,
  TEVCOLORARG_HALF = 13,
  TEVCOLORARG_KONST = 14,
  TEVCOLORARG_ZERO = 15
};

// TEV alpha combiner input
enum : u32
{
  TEVALPHAARG_APREV = 0,
  TEVALPHAARG_A0 = 1,
  TEVALPHAARG_A1 = 2,
  TEVALPHAARG_A2 = 3,
  TEVALPHAARG_TEXA = 4,
  TEVALPHAARG_RASA = 5,
  TEVALPHAARG_KONST = 6,
  TEVALPHAARG_ZERO = 7
};

// TEV output registers
enum : u32
{
  GX_TEVPREV = 0,
  GX_TEVREG0 = 1,
  GX_TEVREG1 = 2,
  GX_TEVREG2 = 3
};

// Z-texture formats
enum : u32
{
  TEV_ZTEX_TYPE_U8 = 0,
  TEV_ZTEX_TYPE_U16 = 1,
  TEV_ZTEX_TYPE_U24 = 2
};

// Z texture operator
enum : u32
{
  ZTEXTURE_DISABLE = 0,
  ZTEXTURE_ADD = 1,
  ZTEXTURE_REPLACE = 2
};

// TEV bias value
enum : u32
{
  TEVBIAS_ZERO = 0,
  TEVBIAS_ADDHALF = 1,
  TEVBIAS_SUBHALF = 2,
  TEVBIAS_COMPARE = 3
};

// Indirect texture format
enum : u32
{
  ITF_8 = 0,
  ITF_5 = 1,
  ITF_4 = 2,
  ITF_3 = 3
};

// Indirect texture bias
enum : u32
{
  ITB_NONE = 0,
  ITB_S = 1,
  ITB_T = 2,
  ITB_ST = 3,
  ITB_U = 4,
  ITB_SU = 5,
  ITB_TU = 6,
  ITB_STU = 7
};

// Indirect texture bump alpha
enum : u32
{
  ITBA_OFF = 0,
  ITBA_S = 1,
  ITBA_T = 2,
  ITBA_U = 3
};

// Indirect texture wrap value
enum : u32
{
  ITW_OFF = 0,
  ITW_256 = 1,
  ITW_128 = 2,
  ITW_64 = 3,
  ITW_32 = 4,
  ITW_16 = 5,
  ITW_0 = 6
};

union IND_MTXA
{
  struct
  {
    s32 ma : 11;
    s32 mb : 11;
    u32 s0 : 2;  // bits 0-1 of scale factor
    u32 rid : 8;
  };
  u32 hex;
};

union IND_MTXB
{
  struct
  {
    s32 mc : 11;
    s32 md : 11;
    u32 s1 : 2;  // bits 2-3 of scale factor
    u32 rid : 8;
  };
  u32 hex;
};

union IND_MTXC
{
  struct
  {
    s32 me : 11;
    s32 mf : 11;
    u32 s2 : 2;  // bits 4-5 of scale factor
    u32 rid : 8;
  };
  u32 hex;
};

struct IND_MTX
{
  IND_MTXA col0;
  IND_MTXB col1;
  IND_MTXC col2;
};

union IND_IMASK
{
  struct
  {
    u32 mask : 24;
    u32 rid : 8;
  };
  u32 hex;
};

struct TevStageCombiner
{
  union ColorCombiner
  {
    struct  // abc=8bit,d=10bit
    {
      u32 d : 4;  // TEVSELCC_X
      u32 c : 4;  // TEVSELCC_X
      u32 b : 4;  // TEVSELCC_X
      u32 a : 4;  // TEVSELCC_X

      u32 bias : 2;
      u32 op : 1;
      u32 clamp : 1;

      u32 shift : 2;
      u32 dest : 2;  // 1,2,3
    };
    u32 hex;
  };
  union AlphaCombiner
  {
    struct
    {
      u32 rswap : 2;
      u32 tswap : 2;
      u32 d : 3;  // TEVSELCA_
      u32 c : 3;  // TEVSELCA_
      u32 b : 3;  // TEVSELCA_
      u32 a : 3;  // TEVSELCA_

      u32 bias : 2;  // GXTevBias
      u32 op : 1;
      u32 clamp : 1;

      u32 shift : 2;
      u32 dest : 2;  // 1,2,3
    };
    u32 hex;
  };

  ColorCombiner colorC;
  AlphaCombiner alphaC;
};

// several discoveries:
// GXSetTevIndBumpST(tevstage, indstage, matrixind)
//  if ( matrix == 2 ) realmat = 6; // 10
//  else if ( matrix == 3 ) realmat = 7; // 11
//  else if ( matrix == 1 ) realmat = 5; // 9
//  GXSetTevIndirect(tevstage, indstage, 0, 3, realmat, 6, 6, 0, 0, 0)
//  GXSetTevIndirect(tevstage+1, indstage, 0, 3, realmat+4, 6, 6, 1, 0, 0)
//  GXSetTevIndirect(tevstage+2, indstage, 0, 0, 0, 0, 0, 1, 0, 0)

union TevStageIndirect
{
  struct
  {
    u32 bt : 2;          // Indirect tex stage ID
    u32 fmt : 2;         // Format: ITF_X
    u32 bias : 3;        // ITB_X
    u32 bs : 2;          // ITBA_X, indicates which coordinate will become the 'bump alpha'
    u32 mid : 4;         // Matrix ID to multiply offsets with
    u32 sw : 3;          // ITW_X, wrapping factor for S of regular coord
    u32 tw : 3;          // ITW_X, wrapping factor for T of regular coord
    u32 lb_utclod : 1;   // Use modified or unmodified texture coordinates for LOD computation
    u32 fb_addprev : 1;  // 1 if the texture coordinate results from the previous TEV stage should
                         // be added
    u32 pad0 : 3;
    u32 rid : 8;
  };
  struct
  {
    u32 hex : 21;
    u32 unused : 11;
  };

  // If bs and mid are zero, the result of the stage is independent of
  // the texture sample data, so we can skip sampling the texture.
  bool IsActive() const { return bs != ITBA_OFF || mid != 0; }
};

union TwoTevStageOrders
{
  struct
  {
    u32 texmap0 : 3;  // Indirect tex stage texmap
    u32 texcoord0 : 3;
    u32 enable0 : 1;     // 1 if should read from texture
    u32 colorchan0 : 3;  // RAS1_CC_X

    u32 pad0 : 2;

    u32 texmap1 : 3;
    u32 texcoord1 : 3;
    u32 enable1 : 1;     // 1 if should read from texture
    u32 colorchan1 : 3;  // RAS1_CC_X

    u32 pad1 : 2;
    u32 rid : 8;
  };
  u32 hex;
  int getTexMap(int i) const { return i ? texmap1 : texmap0; }
  int getTexCoord(int i) const { return i ? texcoord1 : texcoord0; }
  int getEnable(int i) const { return i ? enable1 : enable0; }
  int getColorChan(int i) const { return i ? colorchan1 : colorchan0; }
};

union TEXSCALE
{
  struct
  {
    u32 ss0 : 4;  // Indirect tex stage 0, 2^(-ss0)
    u32 ts0 : 4;  // Indirect tex stage 0
    u32 ss1 : 4;  // Indirect tex stage 1
    u32 ts1 : 4;  // Indirect tex stage 1
    u32 pad : 8;
    u32 rid : 8;
  };
  u32 hex;
};

union RAS1_IREF
{
  struct
  {
    u32 bi0 : 3;  // Indirect tex stage 0 ntexmap
    u32 bc0 : 3;  // Indirect tex stage 0 ntexcoord
    u32 bi1 : 3;
    u32 bc1 : 3;
    u32 bi2 : 3;
    u32 bc3 : 3;
    u32 bi4 : 3;
    u32 bc4 : 3;
    u32 rid : 8;
  };
  u32 hex;

  u32 getTexCoord(int i) const { return (hex >> (6 * i + 3)) & 7; }
  u32 getTexMap(int i) const { return (hex >> (6 * i)) & 7; }
};

// Texture structs

union TexMode0
{
  enum TextureFilter : u32
  {
    TEXF_NONE = 0,
    TEXF_POINT = 1,
    TEXF_LINEAR = 2
  };

  struct
  {
    u32 wrap_s : 2;
    u32 wrap_t : 2;
    u32 mag_filter : 1;
    u32 min_filter : 3;
    u32 diag_lod : 1;
    s32 lod_bias : 8;
    u32 pad0 : 2;
    u32 max_aniso : 2;
    u32 lod_clamp : 1;
  };
  u32 hex;
};
union TexMode1
{
  struct
  {
    u32 min_lod : 8;
    u32 max_lod : 8;
  };
  u32 hex;
};
union TexImage0
{
  struct
  {
    u32 width : 10;   // Actually w-1
    u32 height : 10;  // Actually h-1
    u32 format : 4;
  };
  u32 hex;
};
union TexImage1
{
  struct
  {
    u32 tmem_even : 15;  // TMEM line index for even LODs
    u32 cache_width : 3;
    u32 cache_height : 3;
    u32 image_type : 1;  // 1 if this texture is managed manually (0 means we'll autofetch the
                         // texture data whenever it changes)
  };
  u32 hex;
};

union TexImage2
{
  struct
  {
    u32 tmem_odd : 15;  // tmem line index for odd LODs
    u32 cache_width : 3;
    u32 cache_height : 3;
  };
  u32 hex;
};

union TexImage3
{
  struct
  {
    u32 image_base : 24;  // address in memory >> 5 (was 20 for GC)
  };
  u32 hex;
};
union TexTLUT
{
  struct
  {
    u32 tmem_offset : 10;
    u32 tlut_format : 2;
  };
  u32 hex;
};

union ZTex1
{
  struct
  {
    u32 bias : 24;
  };
  u32 hex;
};

union ZTex2
{
  struct
  {
    u32 type : 2;  // TEV_Z_TYPE_X
    u32 op : 2;    // GXZTexOp
  };
  u32 hex;
};

struct FourTexUnits
{
  TexMode0 texMode0[4];
  TexMode1 texMode1[4];
  TexImage0 texImage0[4];
  TexImage1 texImage1[4];
  TexImage2 texImage2[4];
  TexImage3 texImage3[4];
  TexTLUT texTlut[4];
  u32 unknown[4];
};

// Geometry/other structs

union GenMode
{
  enum CullMode : u32
  {
    CULL_NONE = 0,
    CULL_BACK = 1,   // cull back-facing primitives
    CULL_FRONT = 2,  // cull front-facing primitives
    CULL_ALL = 3,    // cull all primitives
  };

  BitField<0, 4, u32> numtexgens;
  BitField<4, 3, u32> numcolchans;
  // 1 bit unused?
  BitField<8, 1, u32> flat_shading;  // unconfirmed
  BitField<9, 1, u32> multisampling;
  BitField<10, 4, u32> numtevstages;
  BitField<14, 2, CullMode> cullmode;
  BitField<16, 3, u32> numindstages;
  BitField<19, 1, u32> zfreeze;

  u32 hex;
};

union LPSize
{
  struct
  {
    u32 linesize : 8;   // in 1/6th pixels
    u32 pointsize : 8;  // in 1/6th pixels
    u32 lineoff : 3;
    u32 pointoff : 3;
    u32 lineaspect : 1;  // interlacing: adjust for pixels having AR of 1/2
    u32 padding : 1;
  };
  u32 hex;
};

union X12Y12
{
  struct
  {
    u32 y : 12;
    u32 x : 12;
  };
  u32 hex;
};
union X10Y10
{
  struct
  {
    u32 x : 10;
    u32 y : 10;
  };
  u32 hex;
};

// Framebuffer/pixel stuff (incl fog)

union BlendMode
{
  enum BlendFactor : u32
  {
    ZERO = 0,
    ONE = 1,
    SRCCLR = 2,             // for dst factor
    INVSRCCLR = 3,          // for dst factor
    DSTCLR = SRCCLR,        // for src factor
    INVDSTCLR = INVSRCCLR,  // for src factor
    SRCALPHA = 4,
    INVSRCALPHA = 5,
    DSTALPHA = 6,
    INVDSTALPHA = 7
  };

  enum LogicOp : u32
  {
    CLEAR = 0,
    AND = 1,
    AND_REVERSE = 2,
    COPY = 3,
    AND_INVERTED = 4,
    NOOP = 5,
    XOR = 6,
    OR = 7,
    NOR = 8,
    EQUIV = 9,
    INVERT = 10,
    OR_REVERSE = 11,
    COPY_INVERTED = 12,
    OR_INVERTED = 13,
    NAND = 14,
    SET = 15
  };

  BitField<0, 1, u32> blendenable;
  BitField<1, 1, u32> logicopenable;
  BitField<2, 1, u32> dither;
  BitField<3, 1, u32> colorupdate;
  BitField<4, 1, u32> alphaupdate;
  BitField<5, 3, BlendFactor> dstfactor;
  BitField<8, 3, BlendFactor> srcfactor;
  BitField<11, 1, u32> subtract;
  BitField<12, 4, LogicOp> logicmode;

  u32 hex;
};

union FogParam0
{
  struct
  {
    u32 mantissa : 11;
    u32 exponent : 8;
    u32 sign : 1;
  };

  float GetA() const;

  u32 hex;
};

union FogParam3
{
  struct
  {
    u32 c_mant : 11;
    u32 c_exp : 8;
    u32 c_sign : 1;
    u32 proj : 1;  // 0 - perspective, 1 - orthographic
    u32 fsel : 3;  // 0 - off, 2 - linear, 4 - exp, 5 - exp2, 6 - backward exp, 7 - backward exp2
  };

  // amount to subtract from eyespacez after range adjustment
  float GetC() const;

  u32 hex;
};

union FogRangeKElement
{
  struct
  {
    u32 HI : 12;
    u32 LO : 12;
    u32 regid : 8;
  };

  // TODO: Which scaling coefficient should we use here? This is just a guess!
  float GetValue(int i) const { return (i ? HI : LO) / 256.f; }
  u32 HEX;
};

struct FogRangeParams
{
  union RangeBase
  {
    struct
    {
      u32 Center : 10;  // viewport center + 342
      u32 Enabled : 1;
      u32 unused : 13;
      u32 regid : 8;
    };
    u32 hex;
  };
  RangeBase Base;
  FogRangeKElement K[5];
};
// final eq: ze = A/(B_MAG - (Zs>>B_SHF));
struct FogParams
{
  FogParam0 a;
  u32 b_magnitude;
  u32 b_shift;  // b's exp + 1?
  FogParam3 c_proj_fsel;

  union FogColor
  {
    struct
    {
      u32 b : 8;
      u32 g : 8;
      u32 r : 8;
    };
    u32 hex;
  };

  FogColor color;  // 0:b 8:g 16:r - nice!
};

union ZMode
{
  enum CompareMode : u32
  {
    NEVER = 0,
    LESS = 1,
    EQUAL = 2,
    LEQUAL = 3,
    GREATER = 4,
    NEQUAL = 5,
    GEQUAL = 6,
    ALWAYS = 7
  };

  BitField<0, 1, u32> testenable;
  BitField<1, 3, CompareMode> func;
  BitField<4, 1, u32> updateenable;

  u32 hex;
};

union ConstantAlpha
{
  struct
  {
    u32 alpha : 8;
    u32 enable : 1;
  };
  u32 hex;
};

union FieldMode
{
  struct
  {
    u32 texLOD : 1;  // adjust vert tex LOD computation to account for interlacing
  };
  u32 hex;
};

union FieldMask
{
  struct
  {
    // If bit is not set, do not write field to EFB
    u32 odd : 1;
    u32 even : 1;
  };
  u32 hex;
};

union PEControl
{
  enum PixelFormat : u32
  {
    RGB8_Z24 = 0,
    RGBA6_Z24 = 1,
    RGB565_Z16 = 2,
    Z24 = 3,
    Y8 = 4,
    U8 = 5,
    V8 = 6,
    YUV420 = 7,
    INVALID_FMT = 0xffffffff,  // Used by Dolphin to represent a missing value.
  };

  enum DepthFormat : u32
  {
    ZLINEAR = 0,
    ZNEAR = 1,
    ZMID = 2,
    ZFAR = 3,

    // It seems these Z formats aren't supported/were removed ?
    ZINV_LINEAR = 4,
    ZINV_NEAR = 5,
    ZINV_MID = 6,
    ZINV_FAR = 7
  };

  BitField<0, 3, PixelFormat> pixel_format;
  BitField<3, 3, DepthFormat> zformat;
  BitField<6, 1, u32> early_ztest;

  u32 hex;
};

// Texture coordinate stuff

union TCInfo
{
  struct
  {
    u32 scale_minus_1 : 16;
    u32 range_bias : 1;
    u32 cylindric_wrap : 1;
    // These bits only have effect in the s field of TCoordInfo
    u32 line_offset : 1;
    u32 point_offset : 1;
  };
  u32 hex;
};
struct TCoordInfo
{
  TCInfo s;
  TCInfo t;
};

union TevReg
{
  u64 hex;

  // Access to individual registers
  BitField<0, 32, u64> low;
  BitField<32, 32, u64> high;

  // TODO: Check if Konst uses all 11 bits or just 8

  // Low register
  BitField<0, 11, s64> red;

  BitField<12, 11, s64> alpha;
  BitField<23, 1, u64> type_ra;

  // High register
  BitField<32, 11, s64> blue;

  BitField<44, 11, s64> green;
  BitField<55, 1, u64> type_bg;
};

union TevKSel
{
  struct
  {
    u32 swap1 : 2;
    u32 swap2 : 2;
    u32 kcsel0 : 5;
    u32 kasel0 : 5;
    u32 kcsel1 : 5;
    u32 kasel1 : 5;
  };
  u32 hex;

  int getKC(int i) const { return i ? kcsel1 : kcsel0; }
  int getKA(int i) const { return i ? kasel1 : kasel0; }
};

union AlphaTest
{
  enum CompareMode : u32
  {
    NEVER = 0,
    LESS = 1,
    EQUAL = 2,
    LEQUAL = 3,
    GREATER = 4,
    NEQUAL = 5,
    GEQUAL = 6,
    ALWAYS = 7
  };

  enum Op : u32
  {
    AND = 0,
    OR = 1,
    XOR = 2,
    XNOR = 3
  };

  BitField<0, 8, u32> ref0;
  BitField<8, 8, u32> ref1;
  BitField<16, 3, CompareMode> comp0;
  BitField<19, 3, CompareMode> comp1;
  BitField<22, 2, Op> logic;

  u32 hex;

  enum TEST_RESULT
  {
    UNDETERMINED = 0,
    FAIL = 1,
    PASS = 2,
  };

  __forceinline TEST_RESULT TestResult() const
  {
    switch (logic)
    {
    case AND:
      if (comp0 == ALWAYS && comp1 == ALWAYS)
        return PASS;
      if (comp0 == NEVER || comp1 == NEVER)
        return FAIL;
      break;

    case OR:
      if (comp0 == ALWAYS || comp1 == ALWAYS)
        return PASS;
      if (comp0 == NEVER && comp1 == NEVER)
        return FAIL;
      break;

    case XOR:
      if ((comp0 == ALWAYS && comp1 == NEVER) || (comp0 == NEVER && comp1 == ALWAYS))
        return PASS;
      if ((comp0 == ALWAYS && comp1 == ALWAYS) || (comp0 == NEVER && comp1 == NEVER))
        return FAIL;
      break;

    case XNOR:
      if ((comp0 == ALWAYS && comp1 == NEVER) || (comp0 == NEVER && comp1 == ALWAYS))
        return FAIL;
      if ((comp0 == ALWAYS && comp1 == ALWAYS) || (comp0 == NEVER && comp1 == NEVER))
        return PASS;
      break;

    default:
      return UNDETERMINED;
    }
    return UNDETERMINED;
  }
};

union UPE_Copy
{
  u32 Hex;

  BitField<0, 1, u32> clamp0;               // if set clamp top
  BitField<1, 1, u32> clamp1;               // if set clamp bottom
  BitField<2, 1, u32> yuv;                  // if set, color conversion from RGB to YUV
  BitField<3, 4, u32> target_pixel_format;  // realformat is (fmt/2)+((fmt&1)*8).... for some reason
                                            // the msb is the lsb (pattern: cycling right shift)
  BitField<7, 2, u32> gamma;  // gamma correction.. 0 = 1.0 ; 1 = 1.7 ; 2 = 2.2 ; 3 is reserved
  BitField<9, 1, u32>
      half_scale;  // "mipmap" filter... 0 = no filter (scale 1:1) ; 1 = box filter (scale 2:1)
  BitField<10, 1, u32> scale_invert;  // if set vertical scaling is on
  BitField<11, 1, u32> clear;
  BitField<12, 2, u32> frame_to_field;  // 0 progressive ; 1 is reserved ; 2 = interlaced (even
                                        // lines) ; 3 = interlaced 1 (odd lines)
  BitField<14, 1, u32> copy_to_xfb;
  BitField<15, 1, u32> intensity_fmt;  // if set, is an intensity format (I4,I8,IA4,IA8)
  BitField<16, 1, u32>
      auto_conv;  // if 0 automatic color conversion by texture format and pixel type

  u32 tp_realFormat() const { return target_pixel_format / 2 + (target_pixel_format & 1) * 8; }
};

union BPU_PreloadTileInfo
{
  u32 hex;
  struct
  {
    u32 count : 15;
    u32 type : 2;
  };
};

struct BPS_TmemConfig
{
  u32 preload_addr;
  u32 preload_tmem_even;
  u32 preload_tmem_odd;
  BPU_PreloadTileInfo preload_tile_info;
  u32 tlut_src;
  u32 tlut_dest;
  u32 texinvalidate;
};

// All of BP memory

struct BPCmd
{
  int address;
  int changes;
  int newvalue;
};

struct BPMemory
{
  GenMode genMode;
  u32 display_copy_filter[4];  // 01-04
  u32 unknown;                 // 05
  // indirect matrices (set by GXSetIndTexMtx, selected by TevStageIndirect::mid)
  // abc form a 2x3 offset matrix, there's 3 such matrices
  // the 3 offset matrices can either be indirect type, S-type, or T-type
  // 6bit scale factor s is distributed across IND_MTXA/B/C.
  // before using matrices scale by 2^-(s-17)
  IND_MTX indmtx[3];               // 06-0e GXSetIndTexMtx, 2x3 matrices
  IND_IMASK imask;                 // 0f
  TevStageIndirect tevind[16];     // 10 GXSetTevIndirect
  X12Y12 scissorTL;                // 20
  X12Y12 scissorBR;                // 21
  LPSize lineptwidth;              // 22 line and point width
  u32 sucounter;                   // 23
  u32 rascounter;                  // 24
  TEXSCALE texscale[2];            // 25-26 GXSetIndTexCoordScale
  RAS1_IREF tevindref;             // 27 GXSetIndTexOrder
  TwoTevStageOrders tevorders[8];  // 28-2F
  TCoordInfo texcoords[8];         // 0x30 s,t,s,t,s,t,s,t...
  ZMode zmode;                     // 40
  BlendMode blendmode;             // 41
  ConstantAlpha dstalpha;          // 42
  PEControl zcontrol;              // 43 GXSetZCompLoc, GXPixModeSync
  FieldMask fieldmask;             // 44
  u32 drawdone;                    // 45, bit1=1 if end of list
  u32 unknown5;                    // 46 clock?
  u32 petoken;                     // 47
  u32 petokenint;                  // 48
  X10Y10 copyTexSrcXY;             // 49
  X10Y10 copyTexSrcWH;             // 4a
  u32 copyTexDest;                 // 4b// 4b == CopyAddress (GXDispCopy and GXTexCopy use it)
  u32 unknown6;                    // 4c
  u32 copyMipMapStrideChannels;  // 4d usually set to 4 when dest is single channel, 8 when dest is
                                 // 2 channel, 16 when dest is RGBA
  // also, doubles whenever mipmap box filter option is set (excent on RGBA). Probably to do with
  // number of bytes to look at when smoothing
  u32 dispcopyyscale;              // 4e
  u32 clearcolorAR;                // 4f
  u32 clearcolorGB;                // 50
  u32 clearZValue;                 // 51
  UPE_Copy triggerEFBCopy;         // 52
  u32 copyfilter[2];               // 53,54
  u32 boundbox0;                   // 55
  u32 boundbox1;                   // 56
  u32 unknown7[2];                 // 57,58
  X10Y10 scissorOffset;            // 59
  u32 unknown8[6];                 // 5a,5b,5c,5d, 5e,5f
  BPS_TmemConfig tmem_config;      // 60-66
  u32 metric;                      // 67
  FieldMode fieldmode;             // 68
  u32 unknown10[7];                // 69-6F
  u32 unknown11[16];               // 70-7F
  FourTexUnits tex[2];             // 80-bf
  TevStageCombiner combiners[16];  // 0xC0-0xDF
  TevReg tevregs[4];               // 0xE0
  FogRangeParams fogRange;         // 0xE8
  FogParams fog;                   // 0xEE,0xEF,0xF0,0xF1,0xF2
  AlphaTest alpha_test;            // 0xF3
  ZTex1 ztex1;                     // 0xf4,0xf5
  ZTex2 ztex2;
  TevKSel tevksel[8];  // 0xf6,0xf7,f8,f9,fa,fb,fc,fd
  u32 bpMask;          // 0xFE
  u32 unknown18;       // ff

  bool UseEarlyDepthTest() const { return zcontrol.early_ztest && zmode.testenable; }
  bool UseLateDepthTest() const { return !zcontrol.early_ztest && zmode.testenable; }
};

#pragma pack()

extern BPMemory bpmem;

void LoadBPReg(u32 value0);
void LoadBPRegPreprocess(u32 value0);

void GetBPRegInfo(const u8* data, std::string* name, std::string* desc);