// Copyright (c) 2022- PPSSPP Project.

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License 2.0 for more details.

// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/

// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.

#include <cstring>
#include "Common/Common.h"
#include "Common/Log.h"
#include "GPU/Debugger/GECommandTable.h"
#include "GPU/Debugger/Breakpoints.h"
#include "GPU/ge_constants.h"
#include "StringUtils.h"

struct GECmdAlias {
	GECommand reg;
	const char *aliases[3];
};

// TODO: Merge this and State.cpp with GeDisasm. So many representations of the same thing!

static constexpr GECmdInfo geCmdInfo[] = {
	{ GE_CMD_NOP, "nop", GECmdFormat::NONE, "Nop" },
	{ GE_CMD_VADDR, "setvaddr", GECmdFormat::RELATIVE_ADDR, "Vertex addr" },
	{ GE_CMD_IADDR, "setiaddr", GECmdFormat::RELATIVE_ADDR, "Index addr" },
	{ GE_CMD_UNKNOWN_03, "unknown03", GECmdFormat::NONE, "Unknown03" },
	{ GE_CMD_PRIM, "prim", GECmdFormat::PRIM, "Prim" },
	{ GE_CMD_BEZIER, "bezier", GECmdFormat::BEZIER, "Bezier" },
	{ GE_CMD_SPLINE, "spline", GECmdFormat::SPLINE, "Spline" },
	{ GE_CMD_BOUNDINGBOX, "btest", GECmdFormat::PRIM, "BBox" },
	{ GE_CMD_JUMP, "jump", GECmdFormat::JUMP, "Jump" },
	{ GE_CMD_BJUMP, "bjump", GECmdFormat::JUMP, "BJump" },
	{ GE_CMD_CALL, "call", GECmdFormat::JUMP, "Call" },
	{ GE_CMD_RET, "ret", GECmdFormat::NONE, "Return" },
	{ GE_CMD_END, "end", GECmdFormat::DATA16, "End", CMD_FMT_HEX },
	{ GE_CMD_UNKNOWN_0D, "unknown0d", GECmdFormat::NONE, "Unknown0D" },
	{ GE_CMD_SIGNAL, "signal", GECmdFormat::SIGNAL, "Signal" },
	{ GE_CMD_FINISH, "finish", GECmdFormat::NONE, "Finish" },
	{ GE_CMD_BASE, "base", GECmdFormat::HIGH_ADDR_ONLY, "Base" },
	{ GE_CMD_UNKNOWN_11, "unknown11", GECmdFormat::NONE, "Unknown 11" },
	{ GE_CMD_VERTEXTYPE, "vtype", GECmdFormat::VERTEX_TYPE, "Vertex type", CMD_FMT_VERTEXTYPE},
	{ GE_CMD_OFFSETADDR, "setoffset", GECmdFormat::OFFSET_ADDR, "Offset addr", CMD_FMT_OFFSETADDR},
	{ GE_CMD_ORIGIN, "origin", GECmdFormat::NONE, "Origin" },
	{ GE_CMD_REGION1, "regionrate", GECmdFormat::X10_Y10, "Region", CMD_FMT_XYXY, 0, GE_CMD_REGION2},
	{ GE_CMD_REGION2, "regionstop", GECmdFormat::X10_Y10, "Region2", CMD_FMT_XYXY, 0 },
	{ GE_CMD_LIGHTINGENABLE, "lighting_on", GECmdFormat::FLAG, "Lighting enable", CMD_FMT_FLAG},
	{ GE_CMD_LIGHTENABLE0, "light0_on", GECmdFormat::FLAG, "Light 0 enable", CMD_FMT_FLAG, GE_CMD_LIGHTINGENABLE },
	{ GE_CMD_LIGHTENABLE1, "light1_on", GECmdFormat::FLAG, "Light 1 enable", CMD_FMT_FLAG, GE_CMD_LIGHTINGENABLE },
	{ GE_CMD_LIGHTENABLE2, "light2_on", GECmdFormat::FLAG, "Light 2 enable", CMD_FMT_FLAG, GE_CMD_LIGHTINGENABLE },
	{ GE_CMD_LIGHTENABLE3, "light3_on", GECmdFormat::FLAG, "Light 3 enable", CMD_FMT_FLAG, GE_CMD_LIGHTINGENABLE },
	{ GE_CMD_DEPTHCLAMPENABLE, "zclamp_on", GECmdFormat::FLAG, "Depth clamp enable", CMD_FMT_FLAG},
	{ GE_CMD_CULLFACEENABLE, "cull_on", GECmdFormat::FLAG, "Cullface enable", CMD_FMT_FLAG},
	{ GE_CMD_TEXTUREMAPENABLE, "tex_on", GECmdFormat::FLAG, "Texture enable", CMD_FMT_FLAG},
	{ GE_CMD_FOGENABLE, "fog_on", GECmdFormat::FLAG, "Fog enable", CMD_FMT_FLAG},
	{ GE_CMD_DITHERENABLE, "dither_on", GECmdFormat::FLAG, "Dither enable", CMD_FMT_FLAG},
	{ GE_CMD_ALPHABLENDENABLE, "ablend_on", GECmdFormat::FLAG, "Alpha blend enable", CMD_FMT_FLAG},
	{ GE_CMD_ALPHATESTENABLE, "atest_on", GECmdFormat::FLAG, "Alpha test enable", CMD_FMT_FLAG},
	{ GE_CMD_ZTESTENABLE, "ztest_on", GECmdFormat::FLAG, "Depth test enable", CMD_FMT_FLAG},
	{ GE_CMD_STENCILTESTENABLE, "stest_on", GECmdFormat::FLAG, "Stencil test enable", CMD_FMT_FLAG},
	{ GE_CMD_ANTIALIASENABLE, "antialias_on", GECmdFormat::FLAG, "Antialias enable", CMD_FMT_FLAG},
	{ GE_CMD_PATCHCULLENABLE, "patchcull_on", GECmdFormat::FLAG, "Patch cull enable", CMD_FMT_FLAG},
	{ GE_CMD_COLORTESTENABLE, "ctest_on", GECmdFormat::FLAG, "Color test enable", CMD_FMT_FLAG},
	{ GE_CMD_LOGICOPENABLE, "logicop_on", GECmdFormat::FLAG, "Logic op enable", CMD_FMT_FLAG},
	{ GE_CMD_UNKNOWN_29, "unknown29", GECmdFormat::NONE },
	{ GE_CMD_BONEMATRIXNUMBER, "bonemtxnum", GECmdFormat::BONE_NUM, "Bone matrix number", CMD_FMT_INTEGER},
	{ GE_CMD_BONEMATRIXDATA, "bonemtxdata", GECmdFormat::FLOAT, "Bone matrix data", CMD_FMT_FLOAT24},
	{ GE_CMD_MORPHWEIGHT0, "morph0", GECmdFormat::FLOAT, "Morph weight 0", CMD_FMT_FLOAT24 },
	{ GE_CMD_MORPHWEIGHT1, "morph1", GECmdFormat::FLOAT, "Morph weight 1", CMD_FMT_FLOAT24 },
	{ GE_CMD_MORPHWEIGHT2, "morph2", GECmdFormat::FLOAT, "Morph weight 2", CMD_FMT_FLOAT24 },
	{ GE_CMD_MORPHWEIGHT3, "morph3", GECmdFormat::FLOAT, "Morph weight 3", CMD_FMT_FLOAT24 },
	{ GE_CMD_MORPHWEIGHT4, "morph4", GECmdFormat::FLOAT, "Morph weight 4", CMD_FMT_FLOAT24 },
	{ GE_CMD_MORPHWEIGHT5, "morph5", GECmdFormat::FLOAT, "Morph weight 5", CMD_FMT_FLOAT24 },
	{ GE_CMD_MORPHWEIGHT6, "morph6", GECmdFormat::FLOAT, "Morph weight 6", CMD_FMT_FLOAT24 },
	{ GE_CMD_MORPHWEIGHT7, "morph7", GECmdFormat::FLOAT, "Morph weight 7", CMD_FMT_FLOAT24 },
	{ GE_CMD_UNKNOWN_34, "unknown34", GECmdFormat::NONE, "Unknown34" },
	{ GE_CMD_UNKNOWN_35, "unknown35", GECmdFormat::NONE, "Unknown35" },
	{ GE_CMD_PATCHDIVISION, "patchdivision", GECmdFormat::PATCH_DIVISION, "Patch division", CMD_FMT_HEX },
	{ GE_CMD_PATCHPRIMITIVE, "patchprim", GECmdFormat::PATCH_PRIM, "Patch primitive", CMD_FMT_PATCHPRIMITIVE },
	{ GE_CMD_PATCHFACING, "patchreversenormals", GECmdFormat::FLAG, "Patch facing", CMD_FMT_HEX, GE_CMD_PATCHCULLENABLE },
	{ GE_CMD_UNKNOWN_39, "unknown39", GECmdFormat::NONE, "Unknown39" },
	{ GE_CMD_WORLDMATRIXNUMBER, "worldmtxnum", GECmdFormat::MATRIX_NUM, "World matrix number" },
	{ GE_CMD_WORLDMATRIXDATA, "worldmtxdata", GECmdFormat::FLOAT, "World matrix data" },
	{ GE_CMD_VIEWMATRIXNUMBER, "viewmtxnum", GECmdFormat::MATRIX_NUM, "View matrix number"},
	{ GE_CMD_VIEWMATRIXDATA, "viewmtxdata", GECmdFormat::FLOAT, "View matrix data" },
	{ GE_CMD_PROJMATRIXNUMBER, "projmtxnum", GECmdFormat::MATRIX_NUM, "Proj matrix number" },
	{ GE_CMD_PROJMATRIXDATA, "projmtxdata", GECmdFormat::FLOAT, "Proj matrix data" },
	{ GE_CMD_TGENMATRIXNUMBER, "texgenmtxnum", GECmdFormat::MATRIX_NUM, "Texgen matrix number" },
	{ GE_CMD_TGENMATRIXDATA, "texgenmtxdata", GECmdFormat::FLOAT, "Texgen matrix data" },
	{ GE_CMD_VIEWPORTXSCALE, "vpxscale", GECmdFormat::FLOAT, "Viewport Scale", CMD_FMT_XYZ, 0, GE_CMD_VIEWPORTYSCALE, GE_CMD_VIEWPORTZSCALE},
	{ GE_CMD_VIEWPORTYSCALE, "vpyscale", GECmdFormat::FLOAT },
	{ GE_CMD_VIEWPORTZSCALE, "vpzscale", GECmdFormat::FLOAT },
	{ GE_CMD_VIEWPORTXCENTER, "vpxcenter", GECmdFormat::FLOAT, "Viewport Offset", CMD_FMT_XYZ, 0, GE_CMD_VIEWPORTYCENTER, GE_CMD_VIEWPORTZCENTER},
	{ GE_CMD_VIEWPORTYCENTER, "vpycenter", GECmdFormat::FLOAT },
	{ GE_CMD_VIEWPORTZCENTER, "vpzcenter", GECmdFormat::FLOAT },
	{ GE_CMD_TEXSCALEU, "texscaleu", GECmdFormat::FLOAT, "Tex scale U", CMD_FMT_FLOAT24, GE_CMD_TEXTUREMAPENABLE},
	{ GE_CMD_TEXSCALEV, "texscalev", GECmdFormat::FLOAT, "Tex scale V", CMD_FMT_FLOAT24, GE_CMD_TEXTUREMAPENABLE },
	{ GE_CMD_TEXOFFSETU, "texoffsetu", GECmdFormat::FLOAT, "Tex offset U", CMD_FMT_FLOAT24, GE_CMD_TEXTUREMAPENABLE },
	{ GE_CMD_TEXOFFSETV, "texoffsetv", GECmdFormat::FLOAT, "Tex offset V", CMD_FMT_FLOAT24, GE_CMD_TEXTUREMAPENABLE},
	{ GE_CMD_OFFSETX, "offsetx", GECmdFormat::SUBPIXEL_COORD, "Offset", CMD_FMT_F16_XY, 0, GE_CMD_OFFSETY},
	{ GE_CMD_OFFSETY, "offsety", GECmdFormat::SUBPIXEL_COORD },
	{ GE_CMD_UNKNOWN_4E, "unknown4e", GECmdFormat::NONE, "Unknown4e" },
	{ GE_CMD_UNKNOWN_4F, "unknown4f", GECmdFormat::NONE, "Unknown4f" },
	// Really shade mode, but using gouraud as the default so it can be 1/0.
	{ GE_CMD_SHADEMODE, "gouraud", GECmdFormat::FLAG, "Shade model", CMD_FMT_SHADEMODEL},
	{ GE_CMD_REVERSENORMAL, "reversenormals", GECmdFormat::FLAG, "Reverse normals", CMD_FMT_FLAG},
	{ GE_CMD_UNKNOWN_52, "unknown52", GECmdFormat::NONE, "Unknown52" },
	{ GE_CMD_MATERIALUPDATE, "materialupdate", GECmdFormat::MATERIAL_UPDATE, "Material update", CMD_FMT_MATERIALUPDATE},
	{ GE_CMD_MATERIALEMISSIVE, "materialemissive", GECmdFormat::RGB, "Material emissive", CMD_FMT_HEX},
	{ GE_CMD_MATERIALAMBIENT, "materialambient", GECmdFormat::RGB, "Material ambient", CMD_FMT_HEX},
	{ GE_CMD_MATERIALDIFFUSE, "materialdiffuse", GECmdFormat::RGB, "Material diffuse", CMD_FMT_HEX},
	{ GE_CMD_MATERIALSPECULAR, "materialspecular", GECmdFormat::RGB, "Material specular", CMD_FMT_HEX},
	{ GE_CMD_MATERIALALPHA, "materialambienta", GECmdFormat::DATA8, "Material amb. alpha", CMD_FMT_HEX8},
	{ GE_CMD_UNKNOWN_59, "unknown59", GECmdFormat::NONE, "Unknown59"},
	{ GE_CMD_UNKNOWN_5A, "unknown5a", GECmdFormat::NONE, "Unknown5a"},
	{ GE_CMD_MATERIALSPECULARCOEF, "specularcoef", GECmdFormat::FLOAT, "Specular coef", CMD_FMT_FLOAT24},
	{ GE_CMD_AMBIENTCOLOR, "ambient", GECmdFormat::RGB, "Ambient color", CMD_FMT_HEX},
	{ GE_CMD_AMBIENTALPHA, "ambienta", GECmdFormat::DATA8, "Ambient alpha", CMD_FMT_HEX},
	{ GE_CMD_LIGHTMODE, "lightseparate", GECmdFormat::FLAG, "Light mode", CMD_FMT_LIGHTMODE, GE_CMD_LIGHTINGENABLE},  // or separate specular
	{ GE_CMD_LIGHTTYPE0, "ltype0", GECmdFormat::LIGHT_TYPE, "Light type 0", CMD_FMT_LIGHTTYPE, GE_CMD_LIGHTENABLE0 },
	{ GE_CMD_LIGHTTYPE1, "ltype1", GECmdFormat::LIGHT_TYPE, "Light type 1", CMD_FMT_LIGHTTYPE, GE_CMD_LIGHTENABLE1 },
	{ GE_CMD_LIGHTTYPE2, "ltype2", GECmdFormat::LIGHT_TYPE, "Light type 2", CMD_FMT_LIGHTTYPE, GE_CMD_LIGHTENABLE2 },
	{ GE_CMD_LIGHTTYPE3, "ltype3", GECmdFormat::LIGHT_TYPE, "Light type 3", CMD_FMT_LIGHTTYPE, GE_CMD_LIGHTENABLE3 },
	{ GE_CMD_LX0, "light0posx", GECmdFormat::FLOAT, "Light pos 0", CMD_FMT_XYZ, GE_CMD_LIGHTENABLE0, GE_CMD_LY0, GE_CMD_LZ0 },
	{ GE_CMD_LY0, "light0posy", GECmdFormat::FLOAT },
	{ GE_CMD_LZ0, "light0posz", GECmdFormat::FLOAT },
	{ GE_CMD_LX1, "light1posx", GECmdFormat::FLOAT, "Light pos 1", CMD_FMT_XYZ, GE_CMD_LIGHTENABLE1, GE_CMD_LY1, GE_CMD_LZ1 },
	{ GE_CMD_LY1, "light1posy", GECmdFormat::FLOAT },
	{ GE_CMD_LZ1, "light1posz", GECmdFormat::FLOAT },
	{ GE_CMD_LX2, "light2posx", GECmdFormat::FLOAT, "Light pos 2", CMD_FMT_XYZ, GE_CMD_LIGHTENABLE2, GE_CMD_LY2, GE_CMD_LZ2 },
	{ GE_CMD_LY2, "light2posy", GECmdFormat::FLOAT },
	{ GE_CMD_LZ2, "light2posz", GECmdFormat::FLOAT },
	{ GE_CMD_LX3, "light3posx", GECmdFormat::FLOAT, "Light pos 3", CMD_FMT_XYZ, GE_CMD_LIGHTENABLE3, GE_CMD_LY3, GE_CMD_LZ3 },
	{ GE_CMD_LY3, "light3posy", GECmdFormat::FLOAT },
	{ GE_CMD_LZ3, "light3posz", GECmdFormat::FLOAT },
	{ GE_CMD_LDX0, "light0dirx", GECmdFormat::FLOAT, "Light dir 0", CMD_FMT_XYZ, GE_CMD_LIGHTENABLE0, GE_CMD_LDY0, GE_CMD_LDZ0 },
	{ GE_CMD_LDY0, "light0diry", GECmdFormat::FLOAT },
	{ GE_CMD_LDZ0, "light0dirz", GECmdFormat::FLOAT },
	{ GE_CMD_LDX1, "light1dirx", GECmdFormat::FLOAT, "Light dir 1", CMD_FMT_XYZ, GE_CMD_LIGHTENABLE1, GE_CMD_LDY1, GE_CMD_LDZ1 },
	{ GE_CMD_LDY1, "light1diry", GECmdFormat::FLOAT },
	{ GE_CMD_LDZ1, "light1dirz", GECmdFormat::FLOAT },
	{ GE_CMD_LDX2, "light2dirx", GECmdFormat::FLOAT, "Light dir 2", CMD_FMT_XYZ, GE_CMD_LIGHTENABLE2, GE_CMD_LDY2, GE_CMD_LDZ2 },
	{ GE_CMD_LDY2, "light2diry", GECmdFormat::FLOAT },
	{ GE_CMD_LDZ2, "light2dirz", GECmdFormat::FLOAT },
	{ GE_CMD_LDX3, "light3dirx", GECmdFormat::FLOAT, "Light dir 3", CMD_FMT_XYZ, GE_CMD_LIGHTENABLE3, GE_CMD_LDY3, GE_CMD_LDZ3 },
	{ GE_CMD_LDY3, "light3diry", GECmdFormat::FLOAT },
	{ GE_CMD_LDZ3, "light3dirz", GECmdFormat::FLOAT },

	{ GE_CMD_LKA0, "light0attpow0", GECmdFormat::FLOAT, "Light att 0", CMD_FMT_XYZ, GE_CMD_LIGHTENABLE0, GE_CMD_LKB0, GE_CMD_LKC0 },
	{ GE_CMD_LKB0, "light0attpow1", GECmdFormat::FLOAT },
	{ GE_CMD_LKC0, "light0attpow2", GECmdFormat::FLOAT },
	{ GE_CMD_LKA1, "light1attpow0", GECmdFormat::FLOAT, "Light att 1", CMD_FMT_XYZ, GE_CMD_LIGHTENABLE1, GE_CMD_LKB1, GE_CMD_LKC1 },
	{ GE_CMD_LKB1, "light1attpow1", GECmdFormat::FLOAT },
	{ GE_CMD_LKC1, "light1attpow2", GECmdFormat::FLOAT },
	{ GE_CMD_LKA2, "light2attpow0", GECmdFormat::FLOAT, "Light att 2", CMD_FMT_XYZ, GE_CMD_LIGHTENABLE2, GE_CMD_LKB2, GE_CMD_LKC2 },
	{ GE_CMD_LKB2, "light2attpow1", GECmdFormat::FLOAT },
	{ GE_CMD_LKC2, "light2attpow2", GECmdFormat::FLOAT },
	{ GE_CMD_LKA3, "light3attpow0", GECmdFormat::FLOAT, "Light att 3", CMD_FMT_XYZ, GE_CMD_LIGHTENABLE3, GE_CMD_LKB3, GE_CMD_LKC3 },
	{ GE_CMD_LKB3, "light3attpow1", GECmdFormat::FLOAT },
	{ GE_CMD_LKC3, "light3attpow2", GECmdFormat::FLOAT },

	{ GE_CMD_LKS0, "light0spotexp", GECmdFormat::FLOAT, "Light spec. exp 0", CMD_FMT_FLOAT24, GE_CMD_LIGHTENABLE0 },
	{ GE_CMD_LKS1, "light1spotexp", GECmdFormat::FLOAT, "Light spec. exp 1", CMD_FMT_FLOAT24, GE_CMD_LIGHTENABLE1 },
	{ GE_CMD_LKS2, "light2spotexp", GECmdFormat::FLOAT, "Light spec. exp 2", CMD_FMT_FLOAT24, GE_CMD_LIGHTENABLE2 },
	{ GE_CMD_LKS3, "light3spotexp", GECmdFormat::FLOAT, "Light spec. exp 3", CMD_FMT_FLOAT24, GE_CMD_LIGHTENABLE3 },
	{ GE_CMD_LKO0, "light0spotcutoff", GECmdFormat::FLOAT, "Spotlight cutoff 0", CMD_FMT_FLOAT24, GE_CMD_LIGHTENABLE0 },
	{ GE_CMD_LKO1, "light1spotcutoff", GECmdFormat::FLOAT, "Spotlight cutoff 1", CMD_FMT_FLOAT24, GE_CMD_LIGHTENABLE1 },
	{ GE_CMD_LKO2, "light2spotcutoff", GECmdFormat::FLOAT, "Spotlight cutoff 2", CMD_FMT_FLOAT24, GE_CMD_LIGHTENABLE2 },
	{ GE_CMD_LKO3, "light3spotcutoff", GECmdFormat::FLOAT, "Spotlight cutoff 3", CMD_FMT_FLOAT24, GE_CMD_LIGHTENABLE3 },
	{ GE_CMD_LAC0, "light0ambient",  GECmdFormat::RGB, "Light ambient 0", CMD_FMT_HEX, GE_CMD_LIGHTENABLE0},
	{ GE_CMD_LDC0, "light0diffuse",  GECmdFormat::RGB, "Light diffuse 0", CMD_FMT_HEX, GE_CMD_LIGHTENABLE0},
	{ GE_CMD_LSC0, "light0specular", GECmdFormat::RGB, "Light specular 0", CMD_FMT_HEX, GE_CMD_LIGHTENABLE0},
	{ GE_CMD_LAC1, "light1ambient",  GECmdFormat::RGB, "Light ambient 1", CMD_FMT_HEX, GE_CMD_LIGHTENABLE1 },
	{ GE_CMD_LDC1, "light1diffuse",  GECmdFormat::RGB, "Light diffuse 1", CMD_FMT_HEX, GE_CMD_LIGHTENABLE1 },
	{ GE_CMD_LSC1, "light1specular", GECmdFormat::RGB, "Light specular 1", CMD_FMT_HEX, GE_CMD_LIGHTENABLE1 },
	{ GE_CMD_LAC2, "light2ambient",  GECmdFormat::RGB, "Light ambient 2", CMD_FMT_HEX, GE_CMD_LIGHTENABLE2 },
	{ GE_CMD_LDC2, "light2diffuse",  GECmdFormat::RGB, "Light diffuse 2", CMD_FMT_HEX, GE_CMD_LIGHTENABLE2 },
	{ GE_CMD_LSC2, "light2specular", GECmdFormat::RGB, "Light specular 2", CMD_FMT_HEX, GE_CMD_LIGHTENABLE2 },
	{ GE_CMD_LAC3, "light3ambient",  GECmdFormat::RGB, "Light ambient 3", CMD_FMT_HEX, GE_CMD_LIGHTENABLE3 },
	{ GE_CMD_LDC3, "light3diffuse",  GECmdFormat::RGB, "Light diffuse 3", CMD_FMT_HEX, GE_CMD_LIGHTENABLE3 },
	{ GE_CMD_LSC3, "light3specular", GECmdFormat::RGB, "Light specular 3", CMD_FMT_HEX, GE_CMD_LIGHTENABLE3 },
	{ GE_CMD_CULL, "cullccw", GECmdFormat::FLAG, "Cull mode", CMD_FMT_CULL, GE_CMD_CULLFACEENABLE},
	{ GE_CMD_FRAMEBUFPTR, "fbptr", GECmdFormat::LOW_ADDR_ONLY, "Framebuffer", CMD_FMT_PTRWIDTH, 0, GE_CMD_FRAMEBUFWIDTH},
	{ GE_CMD_FRAMEBUFWIDTH, "fbstride", GECmdFormat::STRIDE, "Framebuffer stride" },
	{ GE_CMD_ZBUFPTR, "zbptr", GECmdFormat::LOW_ADDR_ONLY, "Depth buffer", CMD_FMT_PTRWIDTH, 0, GE_CMD_ZBUFWIDTH},
	{ GE_CMD_ZBUFWIDTH, "zbstride", GECmdFormat::STRIDE, "Depth buffer stride" },
	{ GE_CMD_TEXADDR0, "texaddr0low", GECmdFormat::LOW_ADDR, "Texture addr 0", CMD_FMT_PTRWIDTH, GE_CMD_TEXTUREMAPENABLE, GE_CMD_TEXBUFWIDTH0 },
	{ GE_CMD_TEXADDR1, "texaddr1low", GECmdFormat::LOW_ADDR, "Texture addr 1", CMD_FMT_PTRWIDTH, GE_CMD_TEXTUREMAPENABLE, GE_CMD_TEXBUFWIDTH1 },
	{ GE_CMD_TEXADDR2, "texaddr2low", GECmdFormat::LOW_ADDR, "Texture addr 2", CMD_FMT_PTRWIDTH, GE_CMD_TEXTUREMAPENABLE, GE_CMD_TEXBUFWIDTH2 },
	{ GE_CMD_TEXADDR3, "texaddr3low", GECmdFormat::LOW_ADDR, "Texture addr 3", CMD_FMT_PTRWIDTH, GE_CMD_TEXTUREMAPENABLE, GE_CMD_TEXBUFWIDTH3 },
	{ GE_CMD_TEXADDR4, "texaddr4low", GECmdFormat::LOW_ADDR, "Texture addr 4", CMD_FMT_PTRWIDTH, GE_CMD_TEXTUREMAPENABLE, GE_CMD_TEXBUFWIDTH4 },
	{ GE_CMD_TEXADDR5, "texaddr5low", GECmdFormat::LOW_ADDR, "Texture addr 5", CMD_FMT_PTRWIDTH, GE_CMD_TEXTUREMAPENABLE, GE_CMD_TEXBUFWIDTH5 },
	{ GE_CMD_TEXADDR6, "texaddr6low", GECmdFormat::LOW_ADDR, "Texture addr 6", CMD_FMT_PTRWIDTH, GE_CMD_TEXTUREMAPENABLE, GE_CMD_TEXBUFWIDTH6 },
	{ GE_CMD_TEXADDR7, "texaddr7low", GECmdFormat::LOW_ADDR, "Texture addr 7", CMD_FMT_PTRWIDTH, GE_CMD_TEXTUREMAPENABLE, GE_CMD_TEXBUFWIDTH7 },
	{ GE_CMD_TEXBUFWIDTH0, "texbufw0", GECmdFormat::STRIDE_HIGH_ADDR, "Texture bufw 0" },
	{ GE_CMD_TEXBUFWIDTH1, "texbufw1", GECmdFormat::STRIDE_HIGH_ADDR, "Texture bufw 1" },
	{ GE_CMD_TEXBUFWIDTH2, "texbufw2", GECmdFormat::STRIDE_HIGH_ADDR, "Texture bufw 2" },
	{ GE_CMD_TEXBUFWIDTH3, "texbufw3", GECmdFormat::STRIDE_HIGH_ADDR, "Texture bufw 3" },
	{ GE_CMD_TEXBUFWIDTH4, "texbufw4", GECmdFormat::STRIDE_HIGH_ADDR, "Texture bufw 4" },
	{ GE_CMD_TEXBUFWIDTH5, "texbufw5", GECmdFormat::STRIDE_HIGH_ADDR, "Texture bufw 5" },
	{ GE_CMD_TEXBUFWIDTH6, "texbufw6", GECmdFormat::STRIDE_HIGH_ADDR, "Texture bufw 6" },
	{ GE_CMD_TEXBUFWIDTH7, "texbufw7", GECmdFormat::STRIDE_HIGH_ADDR, "Texture bufw 7" },
	{ GE_CMD_CLUTADDR, "clutaddrlow", GECmdFormat::LOW_ADDR, "Tex CLUT", CMD_FMT_PTRWIDTH, GE_CMD_TEXTUREMAPENABLE, GE_CMD_CLUTADDRUPPER},
	{ GE_CMD_CLUTADDRUPPER, "clutaddrhigh", GECmdFormat::HIGH_ADDR },
	{ GE_CMD_TRANSFERSRC, "transfersrclow", GECmdFormat::LOW_ADDR, "Transfer src", CMD_FMT_PTRWIDTH, 0, GE_CMD_TRANSFERSRCW},
	{ GE_CMD_TRANSFERSRCW, "transfersrcstride", GECmdFormat::STRIDE_HIGH_ADDR },
	{ GE_CMD_TRANSFERDST, "transferdstlow", GECmdFormat::LOW_ADDR, "Transfer dst", CMD_FMT_PTRWIDTH, 0, GE_CMD_TRANSFERDSTW},
	{ GE_CMD_TRANSFERDSTW, "transferdststride", GECmdFormat::STRIDE_HIGH_ADDR },
	{ GE_CMD_UNKNOWN_B6, "unknownb6", GECmdFormat::NONE, "UnknownB6" },
	{ GE_CMD_UNKNOWN_B7, "unknownb7", GECmdFormat::NONE, "UnknownB7" },
	{ GE_CMD_TEXSIZE0, "texsize0", GECmdFormat::TEX_SIZE, "Tex size 0", CMD_FMT_TEXSIZE, GE_CMD_TEXTUREMAPENABLE },
	{ GE_CMD_TEXSIZE1, "texsize1", GECmdFormat::TEX_SIZE, "Tex size 1", CMD_FMT_TEXSIZE, GE_CMD_TEXTUREMAPENABLE },
	{ GE_CMD_TEXSIZE2, "texsize2", GECmdFormat::TEX_SIZE, "Tex size 2", CMD_FMT_TEXSIZE, GE_CMD_TEXTUREMAPENABLE },
	{ GE_CMD_TEXSIZE3, "texsize3", GECmdFormat::TEX_SIZE, "Tex size 3", CMD_FMT_TEXSIZE, GE_CMD_TEXTUREMAPENABLE },
	{ GE_CMD_TEXSIZE4, "texsize4", GECmdFormat::TEX_SIZE, "Tex size 4", CMD_FMT_TEXSIZE, GE_CMD_TEXTUREMAPENABLE },
	{ GE_CMD_TEXSIZE5, "texsize5", GECmdFormat::TEX_SIZE, "Tex size 5", CMD_FMT_TEXSIZE, GE_CMD_TEXTUREMAPENABLE },
	{ GE_CMD_TEXSIZE6, "texsize6", GECmdFormat::TEX_SIZE, "Tex size 6", CMD_FMT_TEXSIZE, GE_CMD_TEXTUREMAPENABLE },
	{ GE_CMD_TEXSIZE7, "texsize7", GECmdFormat::TEX_SIZE, "Tex size 7", CMD_FMT_TEXSIZE, GE_CMD_TEXTUREMAPENABLE },
	{ GE_CMD_TEXMAPMODE, "texmapmode", GECmdFormat::TEX_MAP_MODE, "Tex mapping mode", CMD_FMT_TEXMAPMODE, GE_CMD_TEXTUREMAPENABLE},
	{ GE_CMD_TEXSHADELS, "texlightsrc", GECmdFormat::TEX_LIGHT_SRC, "Tex shade srcs", CMD_FMT_TEXSHADELS, GE_CMD_TEXTUREMAPENABLE},
	{ GE_CMD_TEXMODE, "texmode", GECmdFormat::TEX_MODE, "Tex mode", CMD_FMT_TEXMAPMODE, GE_CMD_TEXTUREMAPENABLE},
	{ GE_CMD_TEXFORMAT, "texformat", GECmdFormat::TEX_FORMAT, "Tex format", CMD_FMT_TEXFMT, GE_CMD_TEXTUREMAPENABLE},
	{ GE_CMD_LOADCLUT, "loadclut", GECmdFormat::CLUT_BLOCKS, "Load CLUT" },
	{ GE_CMD_CLUTFORMAT, "clutformat", GECmdFormat::CLUT_FORMAT, "CLUT format", CMD_FMT_CLUTFMT, GE_CMD_TEXTUREMAPENABLE},
	{ GE_CMD_TEXFILTER, "texfilter", GECmdFormat::TEX_FILTER, "Tex filter", CMD_FMT_TEXFILTER, GE_CMD_TEXTUREMAPENABLE},
	{ GE_CMD_TEXWRAP, "texclamp", GECmdFormat::TEX_CLAMP, "Tex wrap mode", CMD_FMT_TEXWRAP, GE_CMD_TEXTUREMAPENABLE},
	{ GE_CMD_TEXLEVEL, "texlevelmode", GECmdFormat::TEX_LEVEL_MODE, "Tex mip level mode", CMD_FMT_TEXLEVEL, GE_CMD_TEXTUREMAPENABLE},
	{ GE_CMD_TEXFUNC, "texfunc", GECmdFormat::TEX_FUNC, "Tex func", CMD_FMT_TEXFUNC, GE_CMD_TEXTUREMAPENABLE},
	{ GE_CMD_TEXENVCOLOR, "texenv", GECmdFormat::RGB, "Tex env color", CMD_FMT_HEX, GE_CMD_TEXTUREMAPENABLE},
	{ GE_CMD_TEXFLUSH, "texflush", GECmdFormat::NONE, "Tex flush", },
	{ GE_CMD_TEXSYNC, "texsync", GECmdFormat::NONE, "Tex sync" },
	{ GE_CMD_FOG1, "fogend", GECmdFormat::FLOAT, "Fog end", CMD_FMT_FLOAT24, GE_CMD_FOGENABLE},
	{ GE_CMD_FOG2, "fogslope", GECmdFormat::FLOAT, "Fog slope", CMD_FMT_FLOAT24, GE_CMD_FOGENABLE},
	{ GE_CMD_FOGCOLOR, "fogcolor", GECmdFormat::RGB, "Fog color", CMD_FMT_HEX, GE_CMD_FOGENABLE,},
	{ GE_CMD_TEXLODSLOPE, "texlodslope", GECmdFormat::FLOAT, "Tex lod slope", CMD_FMT_FLOAT24, GE_CMD_TEXTUREMAPENABLE},
	{ GE_CMD_UNKNOWN_D1, "unknownd1", GECmdFormat::NONE, "UnknownD1" },
	{ GE_CMD_FRAMEBUFPIXFORMAT, "fbformat", GECmdFormat::TEX_FORMAT, "Framebuffer format", CMD_FMT_TEXFMT},
	{ GE_CMD_CLEARMODE, "clearmode", GECmdFormat::CLEAR_MODE, "Clear mode", CMD_FMT_CLEARMODE},
	{ GE_CMD_SCISSOR1, "scissor1", GECmdFormat::X10_Y10, "Scissor rectangle", CMD_FMT_XYXY, 0, GE_CMD_SCISSOR2},
	{ GE_CMD_SCISSOR2, "scissor2", GECmdFormat::X10_Y10, "Scissor2", },
	{ GE_CMD_MINZ, "minz", GECmdFormat::DATA16, "Min Z", CMD_FMT_HEX},
	{ GE_CMD_MAXZ, "maxz", GECmdFormat::DATA16, "Max Z", CMD_FMT_HEX},
	{ GE_CMD_COLORTEST, "ctestfunc", GECmdFormat::COLOR_TEST_FUNC, "Color test", CMD_FMT_COLORTEST, GE_CMD_COLORTESTENABLE, GE_CMD_COLORREF, GE_CMD_COLORTESTMASK},
	{ GE_CMD_COLORREF, "ctestref", GECmdFormat::RGB, "Color test ref", CMD_FMT_HEX, },
	{ GE_CMD_COLORTESTMASK, "ctestmask", GECmdFormat::RGB, "Color test mask", CMD_FMT_HEX, GE_CMD_COLORTESTENABLE},
	{ GE_CMD_ALPHATEST, "atest", GECmdFormat::ALPHA_TEST, "Alpha test mask", CMD_FMT_ALPHATEST, GE_CMD_ALPHATESTENABLE},
	{ GE_CMD_STENCILTEST, "stest", GECmdFormat::ALPHA_TEST, "Stencil test", CMD_FMT_STENCILTEST, GE_CMD_STENCILTESTENABLE},
	{ GE_CMD_STENCILOP, "stencilop", GECmdFormat::STENCIL_OP, "Stencil op", CMD_FMT_STENCILOP, GE_CMD_STENCILTESTENABLE},
	{ GE_CMD_ZTEST, "ztest", GECmdFormat::DEPTH_TEST_FUNC, "Depth test", CMD_FMT_ZTEST, GE_CMD_ZTESTENABLE},
	{ GE_CMD_BLENDMODE, "blendmode", GECmdFormat::BLEND_MODE, "Blend mode", CMD_FMT_BLENDMODE, GE_CMD_ALPHABLENDENABLE},
	{ GE_CMD_BLENDFIXEDA, "blendfixa", GECmdFormat::RGB, "Blend color A", CMD_FMT_HEX, GE_CMD_ALPHABLENDENABLE},
	{ GE_CMD_BLENDFIXEDB, "blendfixb", GECmdFormat::RGB, "Blend color B", CMD_FMT_HEX, GE_CMD_ALPHABLENDENABLE },
	{ GE_CMD_DITH0, "dither0", GECmdFormat::DITHER_ROW, "Dither pattern 0", CMD_FMT_HEX, GE_CMD_DITHERENABLE },
	{ GE_CMD_DITH1, "dither1", GECmdFormat::DITHER_ROW, "Dither pattern 1", CMD_FMT_HEX, GE_CMD_DITHERENABLE },
	{ GE_CMD_DITH2, "dither2", GECmdFormat::DITHER_ROW, "Dither pattern 2", CMD_FMT_HEX, GE_CMD_DITHERENABLE },
	{ GE_CMD_DITH3, "dither3", GECmdFormat::DITHER_ROW, "Dither pattern 3", CMD_FMT_HEX, GE_CMD_DITHERENABLE },
	{ GE_CMD_LOGICOP, "logicop", GECmdFormat::LOGIC_OP, "Logic op", CMD_FMT_LOGICOP, GE_CMD_LOGICOPENABLE},
	{ GE_CMD_ZWRITEDISABLE, "zwrite_off", GECmdFormat::FLAG, "Z write disable", CMD_FMT_FLAG, GE_CMD_ZTESTENABLE},
	{ GE_CMD_MASKRGB, "rgbmask_block", GECmdFormat::RGB, "Mask RGB", CMD_FMT_HEX},
	{ GE_CMD_MASKALPHA, "swritemask_block", GECmdFormat::DATA8, "Mask alpha", CMD_FMT_HEX8},
	{ GE_CMD_TRANSFERSTART, "transferstart_bpp", GECmdFormat::FLAG, "Transfer start", CMD_FMT_FLAG},
	{ GE_CMD_TRANSFERSRCPOS, "transfersrcpos", GECmdFormat::X10_Y10, "Transfer src pos", CMD_FMT_XY},
	{ GE_CMD_TRANSFERDSTPOS, "transferdstpos", GECmdFormat::X10_Y10, "Transfer dst pos", CMD_FMT_XY},
	{ GE_CMD_UNKNOWN_ED, "unknowned", GECmdFormat::NONE, "UnknownED" },
	{ GE_CMD_TRANSFERSIZE, "transfersize", GECmdFormat::X10_Y10, "Transfer size", CMD_FMT_XY},
	{ GE_CMD_UNKNOWN_EF, "unknownef", GECmdFormat::NONE, "UnknownEF" },
	{ GE_CMD_VSCX, "immx", GECmdFormat::SUBPIXEL_COORD, "Imm vertex XY", CMD_FMT_F16_XY, 0, GE_CMD_VSCY},
	{ GE_CMD_VSCY, "immy", GECmdFormat::SUBPIXEL_COORD, "Imm vertex Y" },
	{ GE_CMD_VSCZ, "immz", GECmdFormat::DATA16, "Imm vertex Z", CMD_FMT_HEX},
	{ GE_CMD_VTCS, "imms", GECmdFormat::FLOAT, "Imm vertex STQ", CMD_FMT_XYZ, 0, GE_CMD_VTCT, GE_CMD_VTCQ},
	{ GE_CMD_VTCT, "immt", GECmdFormat::FLOAT, "Imm vertex T", CMD_FMT_FLOAT24 },
	{ GE_CMD_VTCQ, "immq", GECmdFormat::FLOAT, "Imm vertex Q", CMD_FMT_FLOAT24 },
	{ GE_CMD_VCV, "immrgb", GECmdFormat::RGB, "Imm vertex RGB", CMD_FMT_HEX},
	{ GE_CMD_VAP, "imma_prim", GECmdFormat::ALPHA_PRIM, "Imm vertex alpha", CMD_FMT_HEX},  // TODO: format?
	{ GE_CMD_VFC, "immfog", GECmdFormat::DATA8, "Imm vertex fog", CMD_FMT_HEX8},
	{ GE_CMD_VSCV, "immrgb1", GECmdFormat::RGB, "Imm vertex spec RGB", CMD_FMT_HEX},
	{ GE_CMD_UNKNOWN_FA, "unknownfa", GECmdFormat::NONE, "UnknownFA" },
	{ GE_CMD_UNKNOWN_FB, "unknownfb", GECmdFormat::NONE, "UnknownFB" },
	{ GE_CMD_UNKNOWN_FC, "unknownfc", GECmdFormat::NONE, "UnknownFC" },
	{ GE_CMD_UNKNOWN_FD, "unknownfd", GECmdFormat::NONE, "UnknownFD" },
	{ GE_CMD_UNKNOWN_FE, "unknownfe", GECmdFormat::NONE, "UnknownFE" },
	{ GE_CMD_NOP_FF, "nopff", GECmdFormat::NONE, "NopFF" },
};

static constexpr GECmdAlias geCmdAliases[] = {
	{ GE_CMD_VADDR, { "vertexaddr" } },
	{ GE_CMD_IADDR, { "indexaddr" } },
	{ GE_CMD_BOUNDINGBOX, { "boundingbox", "boundtest" } },
	{ GE_CMD_BJUMP, { "boundjump" } },
	{ GE_CMD_BASE, { "baseaddr" } },
	{ GE_CMD_VERTEXTYPE, { "vertextype" } },
	{ GE_CMD_OFFSETADDR, { "offsetaddr" } },
	{ GE_CMD_REGION2, { "region2" } },
	{ GE_CMD_LIGHTINGENABLE, { "lightingenable", "lighting" } },
	{ GE_CMD_LIGHTENABLE0, { "light0enable" } },
	{ GE_CMD_LIGHTENABLE1, { "light1enable" } },
	{ GE_CMD_LIGHTENABLE2, { "light2enable" } },
	{ GE_CMD_LIGHTENABLE3, { "light3enable" } },
	{ GE_CMD_DEPTHCLAMPENABLE, { "zclampenable", "depthclamp_on", "depthclampenable" } },
	{ GE_CMD_CULLFACEENABLE, { "cullenable", "cullface_on", "cullfaceenable" } },
	{ GE_CMD_TEXTUREMAPENABLE, { "texenable", "texture_on", "textureenable" } },
	{ GE_CMD_FOGENABLE, { "fogenable" } },
	{ GE_CMD_DITHERENABLE, { "ditherenable" } },
	{ GE_CMD_ALPHABLENDENABLE, { "ablendenable", "alphablend_on", "alphablendenable" } },
	{ GE_CMD_ALPHABLENDENABLE, { "atestenable", "alphatest_on", "alphatestenable" } },
	{ GE_CMD_ZTESTENABLE, { "ztestenable", "depthtest_on", "depthtest_enable" } },
	{ GE_CMD_STENCILTESTENABLE, { "stestenable", "stenciltest_on", "stenciltestenable" } },
	{ GE_CMD_ANTIALIASENABLE, { "antialiasenable", "antialias" } },
	{ GE_CMD_PATCHCULLENABLE, { "patchcullenable" } },
	{ GE_CMD_COLORTESTENABLE, { "ctestenable", "colortest_on", "colortestenable" } },
	{ GE_CMD_LOGICOPENABLE, { "logicopenable" } },
	{ GE_CMD_BONEMATRIXNUMBER, { "bonematrixnum" } },
	{ GE_CMD_BONEMATRIXDATA, { "bonematrixdata" } },
	{ GE_CMD_MORPHWEIGHT0, { "morphweight0" } },
	{ GE_CMD_MORPHWEIGHT1, { "morphweight1" } },
	{ GE_CMD_MORPHWEIGHT2, { "morphweight2" } },
	{ GE_CMD_MORPHWEIGHT3, { "morphweight3" } },
	{ GE_CMD_MORPHWEIGHT4, { "morphweight4" } },
	{ GE_CMD_MORPHWEIGHT5, { "morphweight5" } },
	{ GE_CMD_MORPHWEIGHT6, { "morphweight6" } },
	{ GE_CMD_MORPHWEIGHT7, { "morphweight7" } },
	{ GE_CMD_PATCHDIVISION, { "patchdiv" } },
	{ GE_CMD_PATCHFACING, { "patchreversenormal" } },
	{ GE_CMD_WORLDMATRIXNUMBER, { "worldmatrixnum" } },
	{ GE_CMD_WORLDMATRIXDATA, { "worldmatrixdata" } },
	{ GE_CMD_VIEWMATRIXNUMBER, { "viewmatrixnum" } },
	{ GE_CMD_VIEWMATRIXDATA, { "viewmatrixdata" } },
	{ GE_CMD_PROJMATRIXNUMBER, { "projmatrixnum" } },
	{ GE_CMD_PROJMATRIXDATA, { "projmatrixdata" } },
	{ GE_CMD_TGENMATRIXNUMBER, { "texgenmatrixnum", "tgenmtxnum", "tgenmatrixnum" } },
	{ GE_CMD_TGENMATRIXDATA, { "texgenmatrixdata", "tgenmtxdata", "tgenmatrixdata" } },
	{ GE_CMD_VIEWPORTXSCALE, { "viewportxscale" } },
	{ GE_CMD_VIEWPORTYSCALE, { "viewportyscale" } },
	{ GE_CMD_VIEWPORTZSCALE, { "viewportzscale" } },
	{ GE_CMD_VIEWPORTXCENTER, { "viewportxcenter" } },
	{ GE_CMD_VIEWPORTYCENTER, { "viewportycenter" } },
	{ GE_CMD_VIEWPORTZCENTER, { "viewportzcenter" } },
	{ GE_CMD_SHADEMODE, { "shademode", "shading" } },
	{ GE_CMD_REVERSENORMAL, { "reversenormal" } },
	{ GE_CMD_MATERIALAMBIENT, { "materialambientrgb" } },
	{ GE_CMD_MATERIALALPHA, { "materialambientalpha" } },
	{ GE_CMD_MATERIALSPECULARCOEF, { "materialspecularcoef" } },
	{ GE_CMD_AMBIENTCOLOR, { "ambientrgb" } },
	{ GE_CMD_AMBIENTALPHA, { "ambientalpha" } },
	{ GE_CMD_LIGHTMODE, { "lmode", "secondarycolor" } },
	{ GE_CMD_LIGHTTYPE0, { "lighttype0" } },
	{ GE_CMD_LIGHTTYPE1, { "lighttype1" } },
	{ GE_CMD_LIGHTTYPE2, { "lighttype2" } },
	{ GE_CMD_LIGHTTYPE3, { "lighttype3" } },
	{ GE_CMD_FRAMEBUFPTR, { "framebufptr" } },
	{ GE_CMD_FRAMEBUFWIDTH, { "fbwidth", "framebufstride", "framebufwidth" } },
	{ GE_CMD_ZBUFPTR, { "depthbufptr" } },
	{ GE_CMD_ZBUFWIDTH, { "zbwidth", "depthbufstride", "depthbufwidth" } },
	{ GE_CMD_TEXBUFWIDTH0, { "texbufwidth0", "texstride0" } },
	{ GE_CMD_TEXBUFWIDTH1, { "texbufwidth1", "texstride1" } },
	{ GE_CMD_TEXBUFWIDTH2, { "texbufwidth2", "texstride2" } },
	{ GE_CMD_TEXBUFWIDTH3, { "texbufwidth3", "texstride3" } },
	{ GE_CMD_TEXBUFWIDTH4, { "texbufwidth4", "texstride4" } },
	{ GE_CMD_TEXBUFWIDTH5, { "texbufwidth5", "texstride5" } },
	{ GE_CMD_TEXBUFWIDTH6, { "texbufwidth6", "texstride6" } },
	{ GE_CMD_TEXBUFWIDTH7, { "texbufwidth7", "texstride7" } },
	{ GE_CMD_CLUTADDRUPPER, { "clutaddrupper" } },
	{ GE_CMD_TEXSHADELS, { "texshadels" } },
	{ GE_CMD_TEXWRAP, { "texwrap" } },
	{ GE_CMD_FOGCOLOR, { "fogrgb" } },
	{ GE_CMD_FRAMEBUFPIXFORMAT, { "framebufformat" } },
	{ GE_CMD_CLEARMODE, { "clear" } },
	{ GE_CMD_SCISSOR1, { "scissortl" } },
	{ GE_CMD_SCISSOR2, { "scissorbr" } },
	{ GE_CMD_COLORTEST, { "colortestfunc" } },
	{ GE_CMD_COLORREF, { "colortestref" } },
	{ GE_CMD_COLORTESTMASK, { "colortestmask" } },
	{ GE_CMD_ALPHATEST, { "alphatest" } },
	{ GE_CMD_STENCILTEST, { "stenciltest" } },
	{ GE_CMD_ZTEST, { "depthtest" } },
	{ GE_CMD_BLENDFIXEDA, { "blendfixsrc" } },
	{ GE_CMD_BLENDFIXEDB, { "blendfixdst" } },
	{ GE_CMD_ZWRITEDISABLE, { "depthwrite_off", "zwritedisable", "depthwritedisable" } },
	{ GE_CMD_MASKRGB, { "rgbmask" } },
	{ GE_CMD_MASKALPHA, { "swritemask", "amask", "amask_block" } },
	{ GE_CMD_TRANSFERSTART, { "transferstart" } },
	{ GE_CMD_VCV, { "immrgb0" } },
	{ GE_CMD_VSCV, { "immsecondaryrgb" } },
};

bool GECmdInfoByName(const char *name, GECmdInfo &result) {
	for (const GECmdInfo &info : geCmdInfo) {
		if (equalsNoCase(info.name, name)) {
			result = info;
			return true;
		}
	}

	for (const GECmdAlias &entry : geCmdAliases) {
		for (const char *alias : entry.aliases) {
			if (alias && equalsNoCase(alias, name)) {
				result = GECmdInfoByCmd(entry.reg);
				return true;
			}
		}
	}

	return false;
}

const GECmdInfo &GECmdInfoByCmd(GECommand reg) {
	_assert_msg_((reg & 0xFF) == reg, "Invalid reg");
	return geCmdInfo[reg & 0xFF];
}
