diff --git a/Source/Core/Core/FifoPlayer/FifoAnalyzer.cpp b/Source/Core/Core/FifoPlayer/FifoAnalyzer.cpp
index a2103bd8a8..ca9ed3574d 100644
--- a/Source/Core/Core/FifoPlayer/FifoAnalyzer.cpp
+++ b/Source/Core/Core/FifoPlayer/FifoAnalyzer.cpp
@@ -190,11 +190,11 @@ u32 AnalyzeCommand(const u8* data, DecodeMode mode)
   {
     s_DrawingObject = false;
 
-    int array = 0xc + (cmd - static_cast<u8>(Opcode::GX_LOAD_INDX_A)) / 8;
+    CPArray array = static_cast<CPArray>(0xc + (cmd - static_cast<u8>(Opcode::GX_LOAD_INDX_A)) / 8);
     u32 value = ReadFifo32(data);
 
     if (mode == DecodeMode::Record)
-      FifoRecordAnalyzer::ProcessLoadIndexedXf(value, array);
+      FifoRecordAnalyzer::ProcessLoadIndexedXf(array, value);
     break;
   }
 
@@ -238,8 +238,8 @@ u32 AnalyzeCommand(const u8* data, DecodeMode mode)
       {
         for (size_t i = 0; i < offsets.size(); ++i)
         {
-          FifoRecordAnalyzer::WriteVertexArray(static_cast<int>(i), data + offsets[i], vertexSize,
-                                               numVertices);
+          FifoRecordAnalyzer::WriteVertexArray(static_cast<CPArray>(i), data + offsets[i],
+                                               vertexSize, numVertices);
         }
       }
 
@@ -284,11 +284,11 @@ void LoadCPReg(u32 subCmd, u32 value, CPMemory& cpMem)
     break;
 
   case ARRAY_BASE:
-    cpMem.arrayBases[subCmd & CP_ARRAY_MASK] = value;
+    cpMem.arrayBases[static_cast<CPArray>(subCmd & CP_ARRAY_MASK)] = value;
     break;
 
   case ARRAY_STRIDE:
-    cpMem.arrayStrides[subCmd & CP_ARRAY_MASK] = value & 0xFF;
+    cpMem.arrayStrides[static_cast<CPArray>(subCmd & CP_ARRAY_MASK)] = value & 0xFF;
     break;
   }
 }
diff --git a/Source/Core/Core/FifoPlayer/FifoAnalyzer.h b/Source/Core/Core/FifoPlayer/FifoAnalyzer.h
index 4e167cbc9f..e9604ce918 100644
--- a/Source/Core/Core/FifoPlayer/FifoAnalyzer.h
+++ b/Source/Core/Core/FifoPlayer/FifoAnalyzer.h
@@ -6,6 +6,8 @@
 #include <array>
 
 #include "Common/CommonTypes.h"
+#include "Common/EnumMap.h"
+
 #include "VideoCommon/CPMemory.h"
 
 namespace FifoAnalyzer
@@ -22,8 +24,8 @@ struct CPMemory
 {
   TVtxDesc vtxDesc;
   std::array<VAT, CP_NUM_VAT_REG> vtxAttr;
-  std::array<u32, CP_NUM_ARRAYS> arrayBases{};
-  std::array<u32, CP_NUM_ARRAYS> arrayStrides{};
+  Common::EnumMap<u32, CPArray::XF_D> arrayBases{};
+  Common::EnumMap<u32, CPArray::XF_D> arrayStrides{};
 };
 
 void LoadCPReg(u32 subCmd, u32 value, CPMemory& cpMem);
diff --git a/Source/Core/Core/FifoPlayer/FifoRecordAnalyzer.cpp b/Source/Core/Core/FifoPlayer/FifoRecordAnalyzer.cpp
index 1f9adcc54c..4ada443fcf 100644
--- a/Source/Core/Core/FifoPlayer/FifoRecordAnalyzer.cpp
+++ b/Source/Core/Core/FifoPlayer/FifoRecordAnalyzer.cpp
@@ -30,7 +30,7 @@ void FifoRecordAnalyzer::Initialize(const u32* cpMem)
   std::copy(strides_start, strides_end, s_CpMem.arrayStrides.begin());
 }
 
-void FifoRecordAnalyzer::ProcessLoadIndexedXf(u32 val, int array)
+void FifoRecordAnalyzer::ProcessLoadIndexedXf(CPArray array, u32 val)
 {
   int index = val >> 16;
   int size = ((val >> 12) & 0xF) + 1;
@@ -40,19 +40,19 @@ void FifoRecordAnalyzer::ProcessLoadIndexedXf(u32 val, int array)
   FifoRecorder::GetInstance().UseMemory(address, size * 4, MemoryUpdate::XF_DATA);
 }
 
-void FifoRecordAnalyzer::WriteVertexArray(int arrayIndex, const u8* vertexData, int vertexSize,
+void FifoRecordAnalyzer::WriteVertexArray(CPArray arrayIndex, const u8* vertexData, int vertexSize,
                                           int numVertices)
 {
   // Skip if not indexed array
   VertexComponentFormat arrayType;
-  if (arrayIndex == ARRAY_POSITION)
+  if (arrayIndex == CPArray::Position)
     arrayType = s_CpMem.vtxDesc.low.Position;
-  else if (arrayIndex == ARRAY_NORMAL)
+  else if (arrayIndex == CPArray::Normal)
     arrayType = s_CpMem.vtxDesc.low.Normal;
-  else if (arrayIndex >= ARRAY_COLOR0 && arrayIndex < ARRAY_COLOR0 + NUM_COLOR_ARRAYS)
-    arrayType = s_CpMem.vtxDesc.low.Color[arrayIndex - ARRAY_COLOR0];
-  else if (arrayIndex >= ARRAY_TEXCOORD0 && arrayIndex < ARRAY_TEXCOORD0 + NUM_TEXCOORD_ARRAYS)
-    arrayType = s_CpMem.vtxDesc.high.TexCoord[arrayIndex - ARRAY_TEXCOORD0];
+  else if (arrayIndex >= CPArray::Color0 && arrayIndex <= CPArray::Color1)
+    arrayType = s_CpMem.vtxDesc.low.Color[u8(arrayIndex) - u8(CPArray::Color0)];
+  else if (arrayIndex >= CPArray::TexCoord0 && arrayIndex <= CPArray::TexCoord7)
+    arrayType = s_CpMem.vtxDesc.high.TexCoord[u8(arrayIndex) - u8(CPArray::TexCoord0)];
   else
   {
     PanicAlertFmt("Invalid arrayIndex {}", arrayIndex);
diff --git a/Source/Core/Core/FifoPlayer/FifoRecordAnalyzer.h b/Source/Core/Core/FifoPlayer/FifoRecordAnalyzer.h
index 8c3bd00a86..d1ac21c09b 100644
--- a/Source/Core/Core/FifoPlayer/FifoRecordAnalyzer.h
+++ b/Source/Core/Core/FifoPlayer/FifoRecordAnalyzer.h
@@ -5,11 +5,13 @@
 
 #include "Common/CommonTypes.h"
 
+enum class CPArray : u8;
+
 namespace FifoRecordAnalyzer
 {
 // Must call this before analyzing Fifo commands with FifoAnalyzer::AnalyzeCommand()
 void Initialize(const u32* cpMem);
 
-void ProcessLoadIndexedXf(u32 val, int array);
-void WriteVertexArray(int arrayIndex, const u8* vertexData, int vertexSize, int numVertices);
+void ProcessLoadIndexedXf(CPArray array, u32 val);
+void WriteVertexArray(CPArray arrayIndex, const u8* vertexData, int vertexSize, int numVertices);
 }  // namespace FifoRecordAnalyzer
diff --git a/Source/Core/DolphinQt/FIFO/FIFOAnalyzer.cpp b/Source/Core/DolphinQt/FIFO/FIFOAnalyzer.cpp
index 21a4572d38..73ba823ec1 100644
--- a/Source/Core/DolphinQt/FIFO/FIFOAnalyzer.cpp
+++ b/Source/Core/DolphinQt/FIFO/FIFOAnalyzer.cpp
@@ -287,7 +287,7 @@ void FIFOAnalyzer::UpdateDetails()
     case Opcode::GX_LOAD_INDX_A:
     {
       const auto [desc, written] =
-          GetXFIndexedLoadInfo(ARRAY_XF_A, Common::swap32(&object[object_offset]));
+          GetXFIndexedLoadInfo(CPArray::XF_A, Common::swap32(&object[object_offset]));
       object_offset += 4;
       new_label = QStringLiteral("LOAD INDX A   %1").arg(QString::fromStdString(desc));
     }
@@ -295,7 +295,7 @@ void FIFOAnalyzer::UpdateDetails()
     case Opcode::GX_LOAD_INDX_B:
     {
       const auto [desc, written] =
-          GetXFIndexedLoadInfo(ARRAY_XF_B, Common::swap32(&object[object_offset]));
+          GetXFIndexedLoadInfo(CPArray::XF_B, Common::swap32(&object[object_offset]));
       object_offset += 4;
       new_label = QStringLiteral("LOAD INDX B   %1").arg(QString::fromStdString(desc));
     }
@@ -303,7 +303,7 @@ void FIFOAnalyzer::UpdateDetails()
     case Opcode::GX_LOAD_INDX_C:
     {
       const auto [desc, written] =
-          GetXFIndexedLoadInfo(ARRAY_XF_C, Common::swap32(&object[object_offset]));
+          GetXFIndexedLoadInfo(CPArray::XF_C, Common::swap32(&object[object_offset]));
       object_offset += 4;
       new_label = QStringLiteral("LOAD INDX C   %1").arg(QString::fromStdString(desc));
     }
@@ -311,7 +311,7 @@ void FIFOAnalyzer::UpdateDetails()
     case Opcode::GX_LOAD_INDX_D:
     {
       const auto [desc, written] =
-          GetXFIndexedLoadInfo(ARRAY_XF_D, Common::swap32(&object[object_offset]));
+          GetXFIndexedLoadInfo(CPArray::XF_D, Common::swap32(&object[object_offset]));
       object_offset += 4;
       new_label = QStringLiteral("LOAD INDX D   %1").arg(QString::fromStdString(desc));
     }
@@ -619,7 +619,7 @@ void FIFOAnalyzer::UpdateDescription()
   }
   else if (opcode == Opcode::GX_LOAD_INDX_A)
   {
-    const auto [desc, written] = GetXFIndexedLoadInfo(ARRAY_XF_A, Common::swap32(cmddata + 1));
+    const auto [desc, written] = GetXFIndexedLoadInfo(CPArray::XF_A, Common::swap32(cmddata + 1));
 
     text = QString::fromStdString(desc);
     text += QLatin1Char{'\n'};
@@ -629,7 +629,7 @@ void FIFOAnalyzer::UpdateDescription()
   }
   else if (opcode == Opcode::GX_LOAD_INDX_B)
   {
-    const auto [desc, written] = GetXFIndexedLoadInfo(ARRAY_XF_B, Common::swap32(cmddata + 1));
+    const auto [desc, written] = GetXFIndexedLoadInfo(CPArray::XF_B, Common::swap32(cmddata + 1));
 
     text = QString::fromStdString(desc);
     text += QLatin1Char{'\n'};
@@ -641,7 +641,7 @@ void FIFOAnalyzer::UpdateDescription()
   }
   else if (opcode == Opcode::GX_LOAD_INDX_C)
   {
-    const auto [desc, written] = GetXFIndexedLoadInfo(ARRAY_XF_C, Common::swap32(cmddata + 1));
+    const auto [desc, written] = GetXFIndexedLoadInfo(CPArray::XF_C, Common::swap32(cmddata + 1));
 
     text = QString::fromStdString(desc);
     text += QLatin1Char{'\n'};
@@ -652,7 +652,7 @@ void FIFOAnalyzer::UpdateDescription()
   }
   else if (opcode == Opcode::GX_LOAD_INDX_D)
   {
-    const auto [desc, written] = GetXFIndexedLoadInfo(ARRAY_XF_D, Common::swap32(cmddata + 1));
+    const auto [desc, written] = GetXFIndexedLoadInfo(CPArray::XF_D, Common::swap32(cmddata + 1));
 
     text = QString::fromStdString(desc);
     text += QLatin1Char{'\n'};
diff --git a/Source/Core/VideoCommon/CPMemory.cpp b/Source/Core/VideoCommon/CPMemory.cpp
index 1184e10ca3..afa354b4e4 100644
--- a/Source/Core/VideoCommon/CPMemory.cpp
+++ b/Source/Core/VideoCommon/CPMemory.cpp
@@ -62,11 +62,13 @@ std::pair<std::string, std::string> GetCPRegInfo(u8 cmd, u32 value)
     return std::make_pair(fmt::format("CP_VAT_REG_C - Format {}", cmd & CP_VAT_MASK),
                           fmt::to_string(UVAT_group2{.Hex = value}));
   case ARRAY_BASE:
-    return std::make_pair(fmt::format("ARRAY_BASE Array {}", cmd & CP_ARRAY_MASK),
-                          fmt::format("Base address {:08x}", value));
+    return std::make_pair(
+        fmt::format("ARRAY_BASE Array {}", static_cast<CPArray>(cmd & CP_ARRAY_MASK)),
+        fmt::format("Base address {:08x}", value));
   case ARRAY_STRIDE:
-    return std::make_pair(fmt::format("ARRAY_STRIDE Array {}", cmd - ARRAY_STRIDE),
-                          fmt::format("Stride {:02x}", value & 0xff));
+    return std::make_pair(
+        fmt::format("ARRAY_STRIDE Array {}", static_cast<CPArray>(cmd & CP_ARRAY_MASK)),
+        fmt::format("Stride {:02x}", value & 0xff));
   default:
     return std::make_pair(fmt::format("Invalid CP register {:02x} = {:08x}", cmd, value), "");
   }
diff --git a/Source/Core/VideoCommon/CPMemory.h b/Source/Core/VideoCommon/CPMemory.h
index 81d0316cec..1a937d15c9 100644
--- a/Source/Core/VideoCommon/CPMemory.h
+++ b/Source/Core/VideoCommon/CPMemory.h
@@ -11,6 +11,7 @@
 #include "Common/BitSet.h"
 #include "Common/CommonTypes.h"
 #include "Common/EnumFormatter.h"
+#include "Common/EnumMap.h"
 #include "Common/MsgHandler.h"
 
 enum
@@ -53,24 +54,46 @@ enum
 };
 
 // Vertex array numbers
-enum
+enum class CPArray : u8
 {
-  ARRAY_POSITION = 0,
-  ARRAY_NORMAL = 1,
-  ARRAY_COLOR0 = 2,
-  NUM_COLOR_ARRAYS = 2,
-  ARRAY_TEXCOORD0 = 4,
-  NUM_TEXCOORD_ARRAYS = 8,
+  Position = 0,
+  Normal = 1,
 
-  ARRAY_XF_A = 12,  // Usually used for position matrices
-  ARRAY_XF_B = 13,  // Usually used for normal matrices
-  ARRAY_XF_C = 14,  // Usually used for tex coord matrices
-  ARRAY_XF_D = 15,  // Usually used for light objects
+  Color0 = 2,
+  Color1 = 3,
 
-  // Number of arrays related to vertex components (position, normal, color, tex coord)
-  // Excludes the 4 arrays used for indexed XF loads
-  NUM_VERTEX_COMPONENT_ARRAYS = 12,
+  TexCoord0 = 4,
+  TexCoord1 = 5,
+  TexCoord2 = 6,
+  TexCoord3 = 7,
+  TexCoord4 = 8,
+  TexCoord5 = 9,
+  TexCoord6 = 10,
+  TexCoord7 = 11,
+
+  XF_A = 12,  // Usually used for position matrices
+  XF_B = 13,  // Usually used for normal matrices
+  XF_C = 14,  // Usually used for tex coord matrices
+  XF_D = 15,  // Usually used for light objects
 };
+template <>
+struct fmt::formatter<CPArray> : EnumFormatter<CPArray::XF_D>
+{
+  static constexpr array_type names = {"Position",    "Normal",      "Color 0",     "Color 1",
+                                       "Tex Coord 0", "Tex Coord 1", "Tex Coord 2", "Tex Coord 3",
+                                       "Tex Coord 4", "Tex Coord 5", "Tex Coord 6", "Tex Coord 7",
+                                       "XF A",        "XF B",        "XF C",        "XF D"};
+  formatter() : EnumFormatter(names) {}
+};
+// Intended for offsetting from Color0/TexCoord0
+constexpr CPArray operator+(CPArray array, u8 offset)
+{
+  return static_cast<CPArray>(static_cast<u8>(array) + offset);
+}
+
+// Number of arrays related to vertex components (position, normal, color, tex coord)
+// Excludes the 4 arrays used for indexed XF loads
+constexpr u8 NUM_VERTEX_COMPONENT_ARRAYS = 12;
 
 // Vertex components
 enum class VertexComponentFormat
@@ -607,8 +630,8 @@ class VertexLoaderBase;
 // STATE_TO_SAVE
 struct CPState final
 {
-  u32 array_bases[CP_NUM_ARRAYS]{};
-  u32 array_strides[CP_NUM_ARRAYS]{};
+  Common::EnumMap<u32, CPArray::XF_D> array_bases;
+  Common::EnumMap<u32, CPArray::XF_D> array_strides;
   TMatrixIndexA matrix_index_a{};
   TMatrixIndexB matrix_index_b{};
   TVtxDesc vtx_desc;
diff --git a/Source/Core/VideoCommon/OpcodeDecoding.cpp b/Source/Core/VideoCommon/OpcodeDecoding.cpp
index 362afd6a7a..be879ddfbc 100644
--- a/Source/Core/VideoCommon/OpcodeDecoding.cpp
+++ b/Source/Core/VideoCommon/OpcodeDecoding.cpp
@@ -166,12 +166,12 @@ u8* Run(DataReader src, u32* cycles, bool in_display_list)
       // GX_LOAD_INDX_B (40) -> 0xD
       // GX_LOAD_INDX_C (48) -> 0xE
       // GX_LOAD_INDX_D (56) -> 0xF
-      const int ref_array = (cmd_byte / 8) + 8;
+      const auto array = static_cast<CPArray>((cmd_byte / 8) + 8);
 
       if constexpr (is_preprocess)
-        PreprocessIndexedXF(src.Read<u32>(), ref_array);
+        PreprocessIndexedXF(array, src.Read<u32>());
       else
-        LoadIndexedXF(src.Read<u32>(), ref_array);
+        LoadIndexedXF(array, src.Read<u32>());
     }
     break;
 
diff --git a/Source/Core/VideoCommon/VertexLoaderARM64.cpp b/Source/Core/VideoCommon/VertexLoaderARM64.cpp
index 71fc9e054c..6663e6c8ae 100644
--- a/Source/Core/VideoCommon/VertexLoaderARM64.cpp
+++ b/Source/Core/VideoCommon/VertexLoaderARM64.cpp
@@ -6,6 +6,7 @@
 #include <array>
 
 #include "Common/CommonTypes.h"
+#include "VideoCommon/CPMemory.h"
 #include "VideoCommon/DataReader.h"
 #include "VideoCommon/VertexLoaderManager.h"
 
@@ -59,7 +60,7 @@ VertexLoaderARM64::VertexLoaderARM64(const TVtxDesc& vtx_desc, const VAT& vtx_at
   WriteProtect();
 }
 
-void VertexLoaderARM64::GetVertexAddr(int array, VertexComponentFormat attribute, ARM64Reg reg)
+void VertexLoaderARM64::GetVertexAddr(CPArray array, VertexComponentFormat attribute, ARM64Reg reg)
 {
   if (IsIndexed(attribute))
   {
@@ -95,7 +96,7 @@ void VertexLoaderARM64::GetVertexAddr(int array, VertexComponentFormat attribute
       REV16(scratch1_reg, scratch1_reg);
     }
 
-    if (array == ARRAY_POSITION)
+    if (array == CPArray::Position)
     {
       EOR(scratch2_reg, scratch1_reg,
           attribute == VertexComponentFormat::Index8 ? LogicalImm(0xFF, 32) :
@@ -103,17 +104,18 @@ void VertexLoaderARM64::GetVertexAddr(int array, VertexComponentFormat attribute
       m_skip_vertex = CBZ(scratch2_reg);
     }
 
-    LDR(IndexType::Unsigned, scratch2_reg, stride_reg, array * 4);
+    LDR(IndexType::Unsigned, scratch2_reg, stride_reg, static_cast<u8>(array) * 4);
     MUL(scratch1_reg, scratch1_reg, scratch2_reg);
 
-    LDR(IndexType::Unsigned, EncodeRegTo64(scratch2_reg), arraybase_reg, array * 8);
+    LDR(IndexType::Unsigned, EncodeRegTo64(scratch2_reg), arraybase_reg,
+        static_cast<u8>(array) * 8);
     ADD(EncodeRegTo64(reg), EncodeRegTo64(scratch1_reg), EncodeRegTo64(scratch2_reg));
   }
   else
     ADD(reg, src_reg, m_src_ofs);
 }
 
-s32 VertexLoaderARM64::GetAddressImm(int array, VertexComponentFormat attribute,
+s32 VertexLoaderARM64::GetAddressImm(CPArray array, VertexComponentFormat attribute,
                                      Arm64Gen::ARM64Reg reg, u32 align)
 {
   if (IsIndexed(attribute) || (m_src_ofs > 255 && (m_src_ofs & (align - 1))))
@@ -448,8 +450,8 @@ void VertexLoaderARM64::GenerateVertexLoader()
     int load_size = GetLoadSize(load_bytes);
     load_size <<= 3;
 
-    s32 offset = GetAddressImm(ARRAY_POSITION, m_VtxDesc.low.Position, EncodeRegTo64(scratch1_reg),
-                               load_size);
+    s32 offset = GetAddressImm(CPArray::Position, m_VtxDesc.low.Position,
+                               EncodeRegTo64(scratch1_reg), load_size);
     ReadVertex(m_VtxDesc.low.Position, m_VtxAttr.g0.PosFormat, pos_elements, pos_elements,
                m_VtxAttr.g0.ByteDequant, m_VtxAttr.g0.PosFrac, &m_native_vtx_decl.position, offset);
   }
@@ -470,7 +472,7 @@ void VertexLoaderARM64::GenerateVertexLoader()
         int load_bytes = elem_size * 3;
         int load_size = GetLoadSize(load_bytes);
 
-        offset = GetAddressImm(ARRAY_NORMAL, m_VtxDesc.low.Normal, EncodeRegTo64(scratch1_reg),
+        offset = GetAddressImm(CPArray::Normal, m_VtxDesc.low.Normal, EncodeRegTo64(scratch1_reg),
                                load_size << 3);
 
         if (offset == -1)
@@ -488,7 +490,7 @@ void VertexLoaderARM64::GenerateVertexLoader()
     }
   }
 
-  for (size_t i = 0; i < m_VtxDesc.low.Color.Size(); i++)
+  for (u8 i = 0; i < m_VtxDesc.low.Color.Size(); i++)
   {
     m_native_vtx_decl.colors[i].components = 4;
     m_native_vtx_decl.colors[i].type = VAR_UNSIGNED_BYTE;
@@ -501,7 +503,7 @@ void VertexLoaderARM64::GenerateVertexLoader()
           m_VtxAttr.GetColorFormat(i) == ColorFormat::RGBA4444)
         align = 2;
 
-      s32 offset = GetAddressImm(ARRAY_COLOR0 + int(i), m_VtxDesc.low.Color[i],
+      s32 offset = GetAddressImm(CPArray::Color0 + i, m_VtxDesc.low.Color[i],
                                  EncodeRegTo64(scratch1_reg), align);
       ReadColor(m_VtxDesc.low.Color[i], m_VtxAttr.GetColorFormat(i), offset);
       m_native_vtx_decl.colors[i].components = 4;
@@ -513,7 +515,7 @@ void VertexLoaderARM64::GenerateVertexLoader()
     }
   }
 
-  for (size_t i = 0; i < m_VtxDesc.high.TexCoord.Size(); i++)
+  for (u8 i = 0; i < m_VtxDesc.high.TexCoord.Size(); i++)
   {
     m_native_vtx_decl.texcoords[i].offset = m_dst_ofs;
     m_native_vtx_decl.texcoords[i].type = VAR_FLOAT;
@@ -527,7 +529,7 @@ void VertexLoaderARM64::GenerateVertexLoader()
       int load_size = GetLoadSize(load_bytes);
       load_size <<= 3;
 
-      s32 offset = GetAddressImm(ARRAY_TEXCOORD0 + int(i), m_VtxDesc.high.TexCoord[i],
+      s32 offset = GetAddressImm(CPArray::TexCoord0 + i, m_VtxDesc.high.TexCoord[i],
                                  EncodeRegTo64(scratch1_reg), load_size);
       u8 scaling_exponent = m_VtxAttr.GetTexFrac(i);
       ReadVertex(m_VtxDesc.high.TexCoord[i], m_VtxAttr.GetTexFormat(i), elements,
diff --git a/Source/Core/VideoCommon/VertexLoaderARM64.h b/Source/Core/VideoCommon/VertexLoaderARM64.h
index a2190a6965..eccf3f0ad8 100644
--- a/Source/Core/VideoCommon/VertexLoaderARM64.h
+++ b/Source/Core/VideoCommon/VertexLoaderARM64.h
@@ -11,6 +11,7 @@ class DataReader;
 enum class VertexComponentFormat;
 enum class ComponentFormat;
 enum class ColorFormat;
+enum class CPArray : u8;
 
 class VertexLoaderARM64 : public VertexLoaderBase, public Arm64Gen::ARM64CodeBlock
 {
@@ -25,8 +26,9 @@ private:
   u32 m_dst_ofs = 0;
   Arm64Gen::FixupBranch m_skip_vertex;
   Arm64Gen::ARM64FloatEmitter m_float_emit;
-  void GetVertexAddr(int array, VertexComponentFormat attribute, Arm64Gen::ARM64Reg reg);
-  s32 GetAddressImm(int array, VertexComponentFormat attribute, Arm64Gen::ARM64Reg reg, u32 align);
+  void GetVertexAddr(CPArray array, VertexComponentFormat attribute, Arm64Gen::ARM64Reg reg);
+  s32 GetAddressImm(CPArray array, VertexComponentFormat attribute, Arm64Gen::ARM64Reg reg,
+                    u32 align);
   int ReadVertex(VertexComponentFormat attribute, ComponentFormat format, int count_in,
                  int count_out, bool dequantize, u8 scaling_exponent,
                  AttributeFormat* native_format, s32 offset = -1);
diff --git a/Source/Core/VideoCommon/VertexLoaderManager.cpp b/Source/Core/VideoCommon/VertexLoaderManager.cpp
index b794c5dfc0..e2475d666f 100644
--- a/Source/Core/VideoCommon/VertexLoaderManager.cpp
+++ b/Source/Core/VideoCommon/VertexLoaderManager.cpp
@@ -14,6 +14,7 @@
 
 #include "Common/Assert.h"
 #include "Common/CommonTypes.h"
+#include "Common/EnumMap.h"
 #include "Common/Logging/Log.h"
 
 #include "Core/DolphinAnalytics.h"
@@ -48,7 +49,14 @@ static std::mutex s_vertex_loader_map_lock;
 static VertexLoaderMap s_vertex_loader_map;
 // TODO - change into array of pointers. Keep a map of all seen so far.
 
-u8* cached_arraybases[NUM_VERTEX_COMPONENT_ARRAYS];
+Common::EnumMap<u8*, CPArray::TexCoord7> cached_arraybases;
+
+BitSet8 g_main_vat_dirty;
+BitSet8 g_preprocess_vat_dirty;
+bool g_bases_dirty;  // Main only
+u8 g_current_vat;    // Main only
+std::array<VertexLoaderBase*, CP_NUM_VAT_REG> g_main_vertex_loaders;
+std::array<VertexLoaderBase*, CP_NUM_VAT_REG> g_preprocess_vertex_loaders;
 
 void Init()
 {
@@ -80,24 +88,25 @@ void UpdateVertexArrayPointers()
   //       12 through 15 are used for loading data into xfmem.
   // We also only update the array base if the vertex description states we are going to use it.
   if (IsIndexed(g_main_cp_state.vtx_desc.low.Position))
-    cached_arraybases[ARRAY_POSITION] =
-        Memory::GetPointer(g_main_cp_state.array_bases[ARRAY_POSITION]);
+    cached_arraybases[CPArray::Position] =
+        Memory::GetPointer(g_main_cp_state.array_bases[CPArray::Position]);
 
   if (IsIndexed(g_main_cp_state.vtx_desc.low.Normal))
-    cached_arraybases[ARRAY_NORMAL] = Memory::GetPointer(g_main_cp_state.array_bases[ARRAY_NORMAL]);
+    cached_arraybases[CPArray::Normal] =
+        Memory::GetPointer(g_main_cp_state.array_bases[CPArray::Normal]);
 
-  for (size_t i = 0; i < g_main_cp_state.vtx_desc.low.Color.Size(); i++)
+  for (u8 i = 0; i < g_main_cp_state.vtx_desc.low.Color.Size(); i++)
   {
     if (IsIndexed(g_main_cp_state.vtx_desc.low.Color[i]))
-      cached_arraybases[ARRAY_COLOR0 + i] =
-          Memory::GetPointer(g_main_cp_state.array_bases[ARRAY_COLOR0 + i]);
+      cached_arraybases[CPArray::Color0 + i] =
+          Memory::GetPointer(g_main_cp_state.array_bases[CPArray::Color0 + i]);
   }
 
-  for (size_t i = 0; i < g_main_cp_state.vtx_desc.high.TexCoord.Size(); i++)
+  for (u8 i = 0; i < g_main_cp_state.vtx_desc.high.TexCoord.Size(); i++)
   {
     if (IsIndexed(g_main_cp_state.vtx_desc.high.TexCoord[i]))
-      cached_arraybases[ARRAY_TEXCOORD0 + i] =
-          Memory::GetPointer(g_main_cp_state.array_bases[ARRAY_TEXCOORD0 + i]);
+      cached_arraybases[CPArray::TexCoord0 + i] =
+          Memory::GetPointer(g_main_cp_state.array_bases[CPArray::TexCoord0 + i]);
   }
 
   g_main_cp_state.bases_dirty = false;
@@ -398,13 +407,13 @@ void LoadCPReg(u32 sub_cmd, u32 value, bool is_preprocess)
 
   // Pointers to vertex arrays in GC RAM
   case ARRAY_BASE:
-    state->array_bases[sub_cmd & CP_ARRAY_MASK] =
+    state->array_bases[static_cast<CPArray>(sub_cmd & CP_ARRAY_MASK)] =
         value & CommandProcessor::GetPhysicalAddressMask();
     state->bases_dirty = true;
     break;
 
   case ARRAY_STRIDE:
-    state->array_strides[sub_cmd & CP_ARRAY_MASK] = value & 0xFF;
+    state->array_strides[static_cast<CPArray>(sub_cmd & CP_ARRAY_MASK)] = value & 0xFF;
     break;
 
   default:
@@ -427,9 +436,9 @@ void FillCPMemoryArray(u32* memory)
     memory[CP_VAT_REG_C + i] = g_main_cp_state.vtx_attr[i].g2.Hex;
   }
 
-  for (int i = 0; i < CP_NUM_ARRAYS; ++i)
+  for (u8 i = 0; i < CP_NUM_ARRAYS; ++i)
   {
-    memory[ARRAY_BASE + i] = g_main_cp_state.array_bases[i];
-    memory[ARRAY_STRIDE + i] = g_main_cp_state.array_strides[i];
+    memory[ARRAY_BASE + i] = g_main_cp_state.array_bases[static_cast<CPArray>(i)];
+    memory[ARRAY_STRIDE + i] = g_main_cp_state.array_strides[static_cast<CPArray>(i)];
   }
 }
diff --git a/Source/Core/VideoCommon/VertexLoaderManager.h b/Source/Core/VideoCommon/VertexLoaderManager.h
index bd9066c5bb..4eeae12742 100644
--- a/Source/Core/VideoCommon/VertexLoaderManager.h
+++ b/Source/Core/VideoCommon/VertexLoaderManager.h
@@ -8,6 +8,7 @@
 #include <unordered_map>
 
 #include "Common/CommonTypes.h"
+#include "Common/EnumMap.h"
 #include "VideoCommon/CPMemory.h"
 
 class DataReader;
@@ -46,7 +47,7 @@ int RunVertices(int vtx_attr_group, OpcodeDecoder::Primitive primitive, int coun
 NativeVertexFormat* GetCurrentVertexFormat();
 
 // Resolved pointers to array bases. Used by vertex loaders.
-extern u8* cached_arraybases[NUM_VERTEX_COMPONENT_ARRAYS];
+extern Common::EnumMap<u8*, CPArray::TexCoord7> cached_arraybases;
 void UpdateVertexArrayPointers();
 
 // Position cache for zfreeze (3 vertices, 4 floats each to allow SIMD overwrite).
diff --git a/Source/Core/VideoCommon/VertexLoaderX64.cpp b/Source/Core/VideoCommon/VertexLoaderX64.cpp
index b204e131d7..4022863e59 100644
--- a/Source/Core/VideoCommon/VertexLoaderX64.cpp
+++ b/Source/Core/VideoCommon/VertexLoaderX64.cpp
@@ -15,6 +15,7 @@
 #include "Common/JitRegister.h"
 #include "Common/x64ABI.h"
 #include "Common/x64Emitter.h"
+#include "VideoCommon/CPMemory.h"
 #include "VideoCommon/DataReader.h"
 #include "VideoCommon/VertexLoaderManager.h"
 
@@ -54,7 +55,7 @@ VertexLoaderX64::VertexLoaderX64(const TVtxDesc& vtx_desc, const VAT& vtx_att)
   JitRegister::Register(region, GetCodePtr(), name.c_str());
 }
 
-OpArg VertexLoaderX64::GetVertexAddr(int array, VertexComponentFormat attribute)
+OpArg VertexLoaderX64::GetVertexAddr(CPArray array, VertexComponentFormat attribute)
 {
   OpArg data = MDisp(src_reg, m_src_ofs);
   if (IsIndexed(attribute))
@@ -62,7 +63,7 @@ OpArg VertexLoaderX64::GetVertexAddr(int array, VertexComponentFormat attribute)
     int bits = attribute == VertexComponentFormat::Index8 ? 8 : 16;
     LoadAndSwap(bits, scratch1, data);
     m_src_ofs += bits / 8;
-    if (array == ARRAY_POSITION)
+    if (array == CPArray::Position)
     {
       CMP(bits, R(scratch1), Imm8(-1));
       m_skip_vertex = J_CC(CC_E, true);
@@ -433,7 +434,7 @@ void VertexLoaderX64::GenerateVertexLoader()
       texmatidx_ofs[i] = m_src_ofs++;
   }
 
-  OpArg data = GetVertexAddr(ARRAY_POSITION, m_VtxDesc.low.Position);
+  OpArg data = GetVertexAddr(CPArray::Position, m_VtxDesc.low.Position);
   int pos_elements = m_VtxAttr.g0.PosElements == CoordComponentCount::XY ? 2 : 3;
   ReadVertex(data, m_VtxDesc.low.Position, m_VtxAttr.g0.PosFormat, pos_elements, pos_elements,
              m_VtxAttr.g0.ByteDequant, m_VtxAttr.g0.PosFrac, &m_native_vtx_decl.position);
@@ -448,7 +449,7 @@ void VertexLoaderX64::GenerateVertexLoader()
     {
       if (!i || m_VtxAttr.g0.NormalIndex3)
       {
-        data = GetVertexAddr(ARRAY_NORMAL, m_VtxDesc.low.Normal);
+        data = GetVertexAddr(CPArray::Normal, m_VtxDesc.low.Normal);
         int elem_size = GetElementSize(m_VtxAttr.g0.NormalFormat);
         data.AddMemOffset(i * elem_size * 3);
       }
@@ -457,11 +458,11 @@ void VertexLoaderX64::GenerateVertexLoader()
     }
   }
 
-  for (size_t i = 0; i < m_VtxDesc.low.Color.Size(); i++)
+  for (u8 i = 0; i < m_VtxDesc.low.Color.Size(); i++)
   {
     if (m_VtxDesc.low.Color[i] != VertexComponentFormat::NotPresent)
     {
-      data = GetVertexAddr(ARRAY_COLOR0 + int(i), m_VtxDesc.low.Color[i]);
+      data = GetVertexAddr(CPArray::Color0 + i, m_VtxDesc.low.Color[i]);
       ReadColor(data, m_VtxDesc.low.Color[i], m_VtxAttr.GetColorFormat(i));
       m_native_vtx_decl.colors[i].components = 4;
       m_native_vtx_decl.colors[i].enable = true;
@@ -472,12 +473,12 @@ void VertexLoaderX64::GenerateVertexLoader()
     }
   }
 
-  for (size_t i = 0; i < m_VtxDesc.high.TexCoord.Size(); i++)
+  for (u8 i = 0; i < m_VtxDesc.high.TexCoord.Size(); i++)
   {
     int elements = m_VtxAttr.GetTexElements(i) == TexComponentCount::ST ? 2 : 1;
     if (m_VtxDesc.high.TexCoord[i] != VertexComponentFormat::NotPresent)
     {
-      data = GetVertexAddr(ARRAY_TEXCOORD0 + int(i), m_VtxDesc.high.TexCoord[i]);
+      data = GetVertexAddr(CPArray::TexCoord0 + i, m_VtxDesc.high.TexCoord[i]);
       u8 scaling_exponent = m_VtxAttr.GetTexFrac(i);
       ReadVertex(data, m_VtxDesc.high.TexCoord[i], m_VtxAttr.GetTexFormat(i), elements,
                  m_VtxDesc.low.TexMatIdx[i] ? 2 : elements, m_VtxAttr.g0.ByteDequant,
diff --git a/Source/Core/VideoCommon/VertexLoaderX64.h b/Source/Core/VideoCommon/VertexLoaderX64.h
index 8a3fd5aa6b..6a0cf7b785 100644
--- a/Source/Core/VideoCommon/VertexLoaderX64.h
+++ b/Source/Core/VideoCommon/VertexLoaderX64.h
@@ -10,6 +10,7 @@
 enum class VertexComponentFormat;
 enum class ComponentFormat;
 enum class ColorFormat;
+enum class CPArray : u8;
 
 class VertexLoaderX64 : public VertexLoaderBase, public Gen::X64CodeBlock
 {
@@ -23,7 +24,7 @@ private:
   u32 m_src_ofs = 0;
   u32 m_dst_ofs = 0;
   Gen::FixupBranch m_skip_vertex;
-  Gen::OpArg GetVertexAddr(int array, VertexComponentFormat attribute);
+  Gen::OpArg GetVertexAddr(CPArray array, VertexComponentFormat attribute);
   int ReadVertex(Gen::OpArg data, VertexComponentFormat attribute, ComponentFormat format,
                  int count_in, int count_out, bool dequantize, u8 scaling_exponent,
                  AttributeFormat* native_format);
diff --git a/Source/Core/VideoCommon/VertexLoader_Color.cpp b/Source/Core/VideoCommon/VertexLoader_Color.cpp
index 4e71889bbd..36939557b8 100644
--- a/Source/Core/VideoCommon/VertexLoader_Color.cpp
+++ b/Source/Core/VideoCommon/VertexLoader_Color.cpp
@@ -79,8 +79,8 @@ void Color_ReadIndex_16b_565(VertexLoader* loader)
 {
   const auto index = DataRead<I>();
   const u8* const address =
-      VertexLoaderManager::cached_arraybases[ARRAY_COLOR0 + loader->m_colIndex] +
-      (index * g_main_cp_state.array_strides[ARRAY_COLOR0 + loader->m_colIndex]);
+      VertexLoaderManager::cached_arraybases[CPArray::Color0 + loader->m_colIndex] +
+      (index * g_main_cp_state.array_strides[CPArray::Color0 + loader->m_colIndex]);
 
   u16 value;
   std::memcpy(&value, address, sizeof(u16));
@@ -92,8 +92,8 @@ template <typename I>
 void Color_ReadIndex_24b_888(VertexLoader* loader)
 {
   const auto index = DataRead<I>();
-  const u8* address = VertexLoaderManager::cached_arraybases[ARRAY_COLOR0 + loader->m_colIndex] +
-                      (index * g_main_cp_state.array_strides[ARRAY_COLOR0 + loader->m_colIndex]);
+  const u8* address = VertexLoaderManager::cached_arraybases[CPArray::Color0 + loader->m_colIndex] +
+                      (index * g_main_cp_state.array_strides[CPArray::Color0 + loader->m_colIndex]);
   SetCol(loader, Read24(address));
 }
 
@@ -101,18 +101,18 @@ template <typename I>
 void Color_ReadIndex_32b_888x(VertexLoader* loader)
 {
   const auto index = DataRead<I>();
-  const u8* address = VertexLoaderManager::cached_arraybases[ARRAY_COLOR0 + loader->m_colIndex] +
-                      (index * g_main_cp_state.array_strides[ARRAY_COLOR0 + loader->m_colIndex]);
+  const u8* address = VertexLoaderManager::cached_arraybases[CPArray::Color0 + loader->m_colIndex] +
+                      (index * g_main_cp_state.array_strides[CPArray::Color0 + loader->m_colIndex]);
   SetCol(loader, Read24(address));
 }
 
 template <typename I>
 void Color_ReadIndex_16b_4444(VertexLoader* loader)
 {
-  auto const index = DataRead<I>();
+  const auto index = DataRead<I>();
   const u8* const address =
-      VertexLoaderManager::cached_arraybases[ARRAY_COLOR0 + loader->m_colIndex] +
-      (index * g_main_cp_state.array_strides[ARRAY_COLOR0 + loader->m_colIndex]);
+      VertexLoaderManager::cached_arraybases[CPArray::Color0 + loader->m_colIndex] +
+      (index * g_main_cp_state.array_strides[CPArray::Color0 + loader->m_colIndex]);
 
   u16 value;
   std::memcpy(&value, address, sizeof(u16));
@@ -124,8 +124,8 @@ template <typename I>
 void Color_ReadIndex_24b_6666(VertexLoader* loader)
 {
   const auto index = DataRead<I>();
-  const u8* data = VertexLoaderManager::cached_arraybases[ARRAY_COLOR0 + loader->m_colIndex] +
-                   (index * g_main_cp_state.array_strides[ARRAY_COLOR0 + loader->m_colIndex]) - 1;
+  const u8* data = VertexLoaderManager::cached_arraybases[CPArray::Color0 + loader->m_colIndex] +
+                   (index * g_main_cp_state.array_strides[CPArray::Color0 + loader->m_colIndex]) - 1;
   const u32 val = Common::swap32(data);
   SetCol6666(loader, val);
 }
@@ -134,8 +134,8 @@ template <typename I>
 void Color_ReadIndex_32b_8888(VertexLoader* loader)
 {
   const auto index = DataRead<I>();
-  const u8* address = VertexLoaderManager::cached_arraybases[ARRAY_COLOR0 + loader->m_colIndex] +
-                      (index * g_main_cp_state.array_strides[ARRAY_COLOR0 + loader->m_colIndex]);
+  const u8* address = VertexLoaderManager::cached_arraybases[CPArray::Color0 + loader->m_colIndex] +
+                      (index * g_main_cp_state.array_strides[CPArray::Color0 + loader->m_colIndex]);
   SetCol(loader, Read32(address));
 }
 
diff --git a/Source/Core/VideoCommon/VertexLoader_Normal.cpp b/Source/Core/VideoCommon/VertexLoader_Normal.cpp
index f19f27eda3..254bcacff3 100644
--- a/Source/Core/VideoCommon/VertexLoader_Normal.cpp
+++ b/Source/Core/VideoCommon/VertexLoader_Normal.cpp
@@ -59,7 +59,7 @@ struct Normal_Direct
 {
   static void function([[maybe_unused]] VertexLoader* loader)
   {
-    auto const source = reinterpret_cast<const T*>(DataGetPosition());
+    const auto source = reinterpret_cast<const T*>(DataGetPosition());
     ReadIndirect<T, N * 3>(source);
     DataSkip<N * 3 * sizeof(T)>();
   }
@@ -72,10 +72,10 @@ void Normal_Index_Offset()
 {
   static_assert(std::is_unsigned_v<I>, "Only unsigned I is sane!");
 
-  auto const index = DataRead<I>();
-  auto const data = reinterpret_cast<const T*>(
-      VertexLoaderManager::cached_arraybases[ARRAY_NORMAL] +
-      (index * g_main_cp_state.array_strides[ARRAY_NORMAL]) + sizeof(T) * 3 * Offset);
+  const auto index = DataRead<I>();
+  const auto data = reinterpret_cast<const T*>(
+      VertexLoaderManager::cached_arraybases[CPArray::Normal] +
+      (index * g_main_cp_state.array_strides[CPArray::Normal]) + sizeof(T) * 3 * Offset);
   ReadIndirect<T, N * 3>(data);
 }
 
diff --git a/Source/Core/VideoCommon/VertexLoader_Position.cpp b/Source/Core/VideoCommon/VertexLoader_Position.cpp
index 37b15de53c..0fe8e7ba72 100644
--- a/Source/Core/VideoCommon/VertexLoader_Position.cpp
+++ b/Source/Core/VideoCommon/VertexLoader_Position.cpp
@@ -60,8 +60,8 @@ void Pos_ReadIndex(VertexLoader* loader)
   const auto index = DataRead<I>();
   loader->m_vertexSkip = index == std::numeric_limits<I>::max();
   const auto data =
-      reinterpret_cast<const T*>(VertexLoaderManager::cached_arraybases[ARRAY_POSITION] +
-                                 (index * g_main_cp_state.array_strides[ARRAY_POSITION]));
+      reinterpret_cast<const T*>(VertexLoaderManager::cached_arraybases[CPArray::Position] +
+                                 (index * g_main_cp_state.array_strides[CPArray::Position]));
   const auto scale = loader->m_posScale;
   DataReader dst(g_vertex_manager_write_ptr, nullptr);
 
diff --git a/Source/Core/VideoCommon/VertexLoader_TextCoord.cpp b/Source/Core/VideoCommon/VertexLoader_TextCoord.cpp
index f5741f6423..89891df5a8 100644
--- a/Source/Core/VideoCommon/VertexLoader_TextCoord.cpp
+++ b/Source/Core/VideoCommon/VertexLoader_TextCoord.cpp
@@ -55,8 +55,8 @@ void TexCoord_ReadIndex(VertexLoader* loader)
 
   const auto index = DataRead<I>();
   const auto data = reinterpret_cast<const T*>(
-      VertexLoaderManager::cached_arraybases[ARRAY_TEXCOORD0 + loader->m_tcIndex] +
-      (index * g_main_cp_state.array_strides[ARRAY_TEXCOORD0 + loader->m_tcIndex]));
+      VertexLoaderManager::cached_arraybases[CPArray::TexCoord0 + loader->m_tcIndex] +
+      (index * g_main_cp_state.array_strides[CPArray::TexCoord0 + loader->m_tcIndex]));
   const auto scale = loader->m_tcScale[loader->m_tcIndex];
   DataReader dst(g_vertex_manager_write_ptr, nullptr);
 
diff --git a/Source/Core/VideoCommon/XFMemory.h b/Source/Core/VideoCommon/XFMemory.h
index 2a44497ec7..32c5dbb607 100644
--- a/Source/Core/VideoCommon/XFMemory.h
+++ b/Source/Core/VideoCommon/XFMemory.h
@@ -459,5 +459,5 @@ static_assert(sizeof(XFMemory) == sizeof(u32) * 0x1058);
 extern XFMemory xfmem;
 
 void LoadXFReg(u32 transferSize, u32 address, DataReader src);
-void LoadIndexedXF(u32 val, int array);
-void PreprocessIndexedXF(u32 val, int refarray);
+void LoadIndexedXF(CPArray array, u32 val);
+void PreprocessIndexedXF(CPArray array, u32 val);
diff --git a/Source/Core/VideoCommon/XFStructs.cpp b/Source/Core/VideoCommon/XFStructs.cpp
index ebf9e8efab..c31b8cbb54 100644
--- a/Source/Core/VideoCommon/XFStructs.cpp
+++ b/Source/Core/VideoCommon/XFStructs.cpp
@@ -274,7 +274,7 @@ constexpr std::tuple<u32, u32, u32> ExtractIndexedXF(u32 val)
 }
 
 // TODO - verify that it is correct. Seems to work, though.
-void LoadIndexedXF(u32 val, int refarray)
+void LoadIndexedXF(CPArray array, u32 val)
 {
   const auto [index, address, size] = ExtractIndexedXF(val);
   // load stuff from array to address in xf mem
@@ -287,8 +287,8 @@ void LoadIndexedXF(u32 val, int refarray)
   }
   else
   {
-    newData = (u32*)Memory::GetPointer(g_main_cp_state.array_bases[refarray] +
-                                       g_main_cp_state.array_strides[refarray] * index);
+    newData = (u32*)Memory::GetPointer(g_main_cp_state.array_bases[array] +
+                                       g_main_cp_state.array_strides[array] * index);
   }
   bool changed = false;
   for (u32 i = 0; i < size; ++i)
@@ -307,12 +307,12 @@ void LoadIndexedXF(u32 val, int refarray)
   }
 }
 
-void PreprocessIndexedXF(u32 val, int refarray)
+void PreprocessIndexedXF(CPArray array, u32 val)
 {
   const auto [index, address, size] = ExtractIndexedXF(val);
 
-  const u8* new_data = Memory::GetPointer(g_preprocess_cp_state.array_bases[refarray] +
-                                          g_preprocess_cp_state.array_strides[refarray] * index);
+  const u8* new_data = Memory::GetPointer(g_preprocess_cp_state.array_bases[array] +
+                                          g_preprocess_cp_state.array_strides[array] * index);
 
   const size_t buf_size = size * sizeof(u32);
   Fifo::PushFifoAuxBuffer(new_data, buf_size);
@@ -655,7 +655,7 @@ std::pair<std::string, std::string> GetXFTransferInfo(const u8* data)
   return std::make_pair(fmt::to_string(name), fmt::to_string(desc));
 }
 
-std::pair<std::string, std::string> GetXFIndexedLoadInfo(u8 array, u32 value)
+std::pair<std::string, std::string> GetXFIndexedLoadInfo(CPArray array, u32 value)
 {
   const auto [index, address, size] = ExtractIndexedXF(value);
 
diff --git a/Source/Core/VideoCommon/XFStructs.h b/Source/Core/VideoCommon/XFStructs.h
index 8f839baf97..7e1cc2c49f 100644
--- a/Source/Core/VideoCommon/XFStructs.h
+++ b/Source/Core/VideoCommon/XFStructs.h
@@ -12,4 +12,4 @@ std::pair<std::string, std::string> GetXFRegInfo(u32 address, u32 value);
 std::string GetXFMemName(u32 address);
 std::string GetXFMemDescription(u32 address, u32 value);
 std::pair<std::string, std::string> GetXFTransferInfo(const u8* data);
-std::pair<std::string, std::string> GetXFIndexedLoadInfo(u8 array, u32 value);
+std::pair<std::string, std::string> GetXFIndexedLoadInfo(CPArray array, u32 value);
diff --git a/Source/UnitTests/VideoCommon/VertexLoaderTest.cpp b/Source/UnitTests/VideoCommon/VertexLoaderTest.cpp
index 81b3e5ee53..e72fe28c29 100644
--- a/Source/UnitTests/VideoCommon/VertexLoaderTest.cpp
+++ b/Source/UnitTests/VideoCommon/VertexLoaderTest.cpp
@@ -174,8 +174,8 @@ TEST_P(VertexLoaderParamTest, PositionAll)
         Input<u8>(i);
       else
         Input<u16>(i);
-    VertexLoaderManager::cached_arraybases[ARRAY_POSITION] = m_src.GetPointer();
-    g_main_cp_state.array_strides[ARRAY_POSITION] = elem_count * elem_size;
+    VertexLoaderManager::cached_arraybases[CPArray::Position] = m_src.GetPointer();
+    g_main_cp_state.array_strides[CPArray::Position] = elem_count * elem_size;
   }
   CreateAndCheckSizes(input_size, elem_count * sizeof(float));
   for (float value : values)
@@ -243,8 +243,8 @@ TEST_F(VertexLoaderTest, PositionIndex16FloatXY)
   CreateAndCheckSizes(sizeof(u16), 2 * sizeof(float));
   Input<u16>(1);
   Input<u16>(0);
-  VertexLoaderManager::cached_arraybases[ARRAY_POSITION] = m_src.GetPointer();
-  g_main_cp_state.array_strides[ARRAY_POSITION] = sizeof(float);  // ;)
+  VertexLoaderManager::cached_arraybases[CPArray::Position] = m_src.GetPointer();
+  g_main_cp_state.array_strides[CPArray::Position] = sizeof(float);  // ;)
   Input(1.f);
   Input(2.f);
   Input(3.f);
@@ -357,8 +357,8 @@ TEST_F(VertexLoaderTest, LargeFloatVertexSpeed)
 
   for (int i = 0; i < NUM_VERTEX_COMPONENT_ARRAYS; i++)
   {
-    VertexLoaderManager::cached_arraybases[i] = m_src.GetPointer();
-    g_main_cp_state.array_strides[i] = 129;
+    VertexLoaderManager::cached_arraybases[static_cast<CPArray>(i)] = m_src.GetPointer();
+    g_main_cp_state.array_strides[static_cast<CPArray>(i)] = 129;
   }
 
   // This test is only done 100x in a row since it's ~20x slower using the