// Copyright (c) 2012- 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 "Common/Log.h"
#include "Common/Math/expression_parser.h"
#include "Common/StringUtils.h"
#include "Core/Debugger/SymbolMap.h"
#include "GPU/Common/GPUDebugInterface.h"
#include "GPU/GPUCommon.h"
#include "GPU/Debugger/GECommandTable.h"
#include "GPU/GPUState.h"

enum class GEReferenceIndex : uint32_t {
	VADDR = 0x100,
	IADDR,
	OFFSET,
	PC,
	STALL,
	BFLAG,
	OP,
	DATA,
	CLUTADDR,
	TRANSFERSRC,
	TRANSFERDST,
	PRIMCOUNT,
	LASTPRIMCOUNT,

	TEXADDR0,
	TEXADDR1,
	TEXADDR2,
	TEXADDR3,
	TEXADDR4,
	TEXADDR5,
	TEXADDR6,
	TEXADDR7,

	BONE_MATRIX = 0x200,
	WORLD_MATRIX = 0x260,
	VIEW_MATRIX = 0x26C,
	PROJ_MATRIX = 0x278,
	TGEN_MATRIX = 0x288,
	MATRIX_END = 0x294,

	FIELD_START = 0x1000,
	FIELD_END = 0xFF000,
};
ENUM_CLASS_BITOPS(GEReferenceIndex);

struct ReferenceName {
	GEReferenceIndex index;
	const char *name;
};

static constexpr ReferenceName referenceNames[] = {
	{ GEReferenceIndex::VADDR, "vaddr" },
	{ GEReferenceIndex::IADDR, "iaddr" },
	{ GEReferenceIndex::OFFSET, "offset" },
	{ GEReferenceIndex::PC, "pc" },
	{ GEReferenceIndex::STALL, "stall" },
	{ GEReferenceIndex::BFLAG, "bflag" },
	{ GEReferenceIndex::BFLAG, "boundflag" },
	{ GEReferenceIndex::OP, "op" },
	{ GEReferenceIndex::DATA, "data" },
	{ GEReferenceIndex::CLUTADDR, "clutaddr" },
	{ GEReferenceIndex::TRANSFERSRC, "transfersrc" },
	{ GEReferenceIndex::TRANSFERDST, "transferdst" },
	{ GEReferenceIndex::PRIMCOUNT, "primcount" },
	{ GEReferenceIndex::LASTPRIMCOUNT, "lastprimcount" },
	{ GEReferenceIndex::TEXADDR0, "texaddr0" },
	{ GEReferenceIndex::TEXADDR1, "texaddr1" },
	{ GEReferenceIndex::TEXADDR2, "texaddr2" },
	{ GEReferenceIndex::TEXADDR3, "texaddr3" },
	{ GEReferenceIndex::TEXADDR4, "texaddr4" },
	{ GEReferenceIndex::TEXADDR5, "texaddr5" },
	{ GEReferenceIndex::TEXADDR6, "texaddr6" },
	{ GEReferenceIndex::TEXADDR7, "texaddr7" },
};

enum class GECmdField : uint8_t {
	DATA, // Alias for the entire data.
	LOW_FLAG,
	LOW_U2,
	LOW_U4,
	LOW_U7,
	LOW_U8,
	LOW_U10,
	LOW_U10_P1,
	LOW_U11,
	LOW_U16,
	MID_U8,
	MID_U10, // At 10, 10 bits.
	MID_U10_P1, // At 10, 10 bits (add 1 to value.)
	TOP_U8,
	FLAG_AFTER_1, // At 1, 1 bit.
	FLAG_AFTER_2, // At 2, 1 bit.
	FLAG_AFTER_8, // At 8, 1 bit.
	FLAG_AFTER_9, // At 9, 1 bit.
	FLAG_AFTER_10, // At 10, 1 bit.
	FLAG_AFTER_11, // At 11, 1 bit.
	FLAG_AFTER_16, // At 16, 1 bit.
	FLAG_AFTER_17, // At 17, 1 bit.
	FLAG_AFTER_18, // At 18, 1 bit.
	FLAG_AFTER_19, // At 19, 1 bit.
	FLAG_AFTER_20, // At 20, 1 bit.
	FLAG_AFTER_21, // At 21, 1 bit.
	FLAG_AFTER_22, // At 22, 1 bit.
	FLAG_AFTER_23, // At 23, 1 bit.
	U2_AFTER_8, // At 8, 2 bits.
	U3_AFTER_16, // At 16, 3 bits.
	U12_AFTER_4, // At 4, 12 bits.
	PRIM_TYPE, // At 16, 3 bits.
	SIGNAL_TYPE, // At 16, 8 bits.
	VTYPE_TC, // At 0, 2 bits.
	VTYPE_COL, // At 2, 3 bits.
	VTYPE_NRM, // At 5, 2 bits.
	VTYPE_POS, // At 7, 2 bits.
	VTYPE_WEIGHTTYPE, // At 9, 2 bits.
	VTYPE_INDEX, // At 11, 2 bits.
	VTYPE_WEIGHTCOUNT, // At 14, 3 bits.
	VTYPE_MORPHCOUNT, // At 18, 3 bits.
	PATCH_PRIM_TYPE, // At 0, 2 bits.
	LIGHT_COMP, // At 0, 2 bits.
	LIGHT_TYPE, // At 8, 2 bits.
	LIGHT_TYPE_SPECULAR, // 1 if comp is 1 (but not 3.)
	HIGH_ADDR, // At 16, 8 bits moved left to top 8 bits.
	TEX_W, // At 0, 4 bits - 1 to this power.
	TEX_H, // At 8, 4 bits - 1 to this power.
	UVGEN_TYPE, // At 0, 2 bits.
	UVGEN_PROJ, // At 8, 2 bits.
	TEX_FORMAT, // At 0, 4 bits.
	TEX_MINFILTER, // At 0, 3 bits.
	TEX_MAGFILTER, // At 8, 1 bit.
	TEX_LEVEL_MODE, // At 0, 2 bits.
	LOW_U12_4_FLOAT, // At 0, 12.4 converted to float.
	HIGH_S4_4_FLOAT, // At 16, s.3.4 converted to float.
	TEX_FUNC, // At 0, 3 bits.
	CLUT_BYTES, // At 0, 6 bits, multiplied by 8.
	CLUT_FORMAT, // At 0, 2 bits.
	CLUT_SHIFT, // At 2, 5 bits.
	CLUT_OFFSET, // At 16, 5 bits, multiplied by 16.
	COMPARE_FUNC2, // At 0, 2 bits.
	COMPARE_FUNC3, // At 0, 3 bits.
	STENCIL_OP_AT_0, // At 0, 3 bits.
	STENCIL_OP_AT_8, // At 8, 3 bits.
	STENCIL_OP_AT_16, // At 16, 3 bits.
	BLEND_SRC, // At 0, 4 bits.
	BLEND_DST, // At 4, 4 bits.
	BLEND_EQUATION, // At 8, 3 bits.
	LOGIC_OP, // At 0, 4 bits.
};

struct FieldName {
	GECmdFormat fmt;
	GECmdField field;
	const char *name;
};

static constexpr FieldName fieldNames[] = {
	{ GECmdFormat::PRIM, GECmdField::LOW_U16, "count" },
	{ GECmdFormat::PRIM, GECmdField::PRIM_TYPE, "type" },
	{ GECmdFormat::BEZIER, GECmdField::LOW_U8, "ucount" },
	{ GECmdFormat::BEZIER, GECmdField::LOW_U8, "u" },
	{ GECmdFormat::BEZIER, GECmdField::MID_U8, "vcount" },
	{ GECmdFormat::BEZIER, GECmdField::MID_U8, "v" },
	{ GECmdFormat::SPLINE, GECmdField::LOW_U8, "ucount" },
	{ GECmdFormat::SPLINE, GECmdField::LOW_U8, "u" },
	{ GECmdFormat::SPLINE, GECmdField::MID_U8, "vcount" },
	{ GECmdFormat::SPLINE, GECmdField::MID_U8, "v" },
	{ GECmdFormat::SPLINE, GECmdField::FLAG_AFTER_16, "ufirstopen" },
	{ GECmdFormat::SPLINE, GECmdField::FLAG_AFTER_17, "ulastopen" },
	{ GECmdFormat::SPLINE, GECmdField::FLAG_AFTER_18, "vfirstopen" },
	{ GECmdFormat::SPLINE, GECmdField::FLAG_AFTER_19, "vlastopen" },
	{ GECmdFormat::SIGNAL, GECmdField::LOW_U16, "data" },
	{ GECmdFormat::SIGNAL, GECmdField::SIGNAL_TYPE, "type" },
	{ GECmdFormat::VERTEX_TYPE, GECmdField::VTYPE_TC, "texcoord" },
	{ GECmdFormat::VERTEX_TYPE, GECmdField::VTYPE_TC, "tc" },
	{ GECmdFormat::VERTEX_TYPE, GECmdField::VTYPE_COL, "col" },
	{ GECmdFormat::VERTEX_TYPE, GECmdField::VTYPE_COL, "color" },
	{ GECmdFormat::VERTEX_TYPE, GECmdField::VTYPE_NRM, "normal" },
	{ GECmdFormat::VERTEX_TYPE, GECmdField::VTYPE_POS, "pos" },
	{ GECmdFormat::VERTEX_TYPE, GECmdField::VTYPE_POS, "position" },
	{ GECmdFormat::VERTEX_TYPE, GECmdField::VTYPE_WEIGHTTYPE, "weighttype" },
	{ GECmdFormat::VERTEX_TYPE, GECmdField::VTYPE_WEIGHTTYPE, "weight" },
	{ GECmdFormat::VERTEX_TYPE, GECmdField::VTYPE_INDEX, "index" },
	{ GECmdFormat::VERTEX_TYPE, GECmdField::VTYPE_WEIGHTCOUNT, "weightcount" },
	{ GECmdFormat::VERTEX_TYPE, GECmdField::VTYPE_MORPHCOUNT, "morphcount" },
	{ GECmdFormat::VERTEX_TYPE, GECmdField::FLAG_AFTER_23, "through" },
	{ GECmdFormat::VERTEX_TYPE, GECmdField::FLAG_AFTER_23, "throughmode" },
	{ GECmdFormat::X10_Y10, GECmdField::LOW_U10, "x" },
	{ GECmdFormat::X10_Y10, GECmdField::MID_U10, "y" },
	{ GECmdFormat::X10_Y10, GECmdField::LOW_U10_P1, "w" },
	{ GECmdFormat::X10_Y10, GECmdField::MID_U10_P1, "h" },
	{ GECmdFormat::FLAG, GECmdField::LOW_FLAG, "flag" },
	{ GECmdFormat::BONE_NUM, GECmdField::LOW_U7, "num" },
	{ GECmdFormat::MATRIX_NUM, GECmdField::LOW_U4, "num" },
	{ GECmdFormat::FLOAT, GECmdField::DATA, "data" },
	{ GECmdFormat::PATCH_DIVISION, GECmdField::LOW_U8, "u" },
	{ GECmdFormat::PATCH_DIVISION, GECmdField::MID_U8, "v" },
	{ GECmdFormat::PATCH_PRIM, GECmdField::PATCH_PRIM_TYPE, "type" },
	{ GECmdFormat::SUBPIXEL_COORD, GECmdField::LOW_U4, "frac" },
	{ GECmdFormat::SUBPIXEL_COORD, GECmdField::LOW_U4, "sub" },
	{ GECmdFormat::SUBPIXEL_COORD, GECmdField::LOW_U4, "subpixels" },
	{ GECmdFormat::SUBPIXEL_COORD, GECmdField::U12_AFTER_4, "int" },
	{ GECmdFormat::SUBPIXEL_COORD, GECmdField::U12_AFTER_4, "integer" },
	{ GECmdFormat::SUBPIXEL_COORD, GECmdField::LOW_U12_4_FLOAT, "pixels" },
	{ GECmdFormat::MATERIAL_UPDATE, GECmdField::LOW_FLAG, "ambient" },
	{ GECmdFormat::MATERIAL_UPDATE, GECmdField::FLAG_AFTER_1, "diffuse" },
	{ GECmdFormat::MATERIAL_UPDATE, GECmdField::FLAG_AFTER_2, "specular" },
	{ GECmdFormat::RGB, GECmdField::LOW_U8, "r" },
	{ GECmdFormat::RGB, GECmdField::LOW_U8, "red" },
	{ GECmdFormat::RGB, GECmdField::MID_U8, "g" },
	{ GECmdFormat::RGB, GECmdField::MID_U8, "green" },
	{ GECmdFormat::RGB, GECmdField::TOP_U8, "b" },
	{ GECmdFormat::RGB, GECmdField::TOP_U8, "blue" },
	{ GECmdFormat::LIGHT_TYPE, GECmdField::LIGHT_COMP, "computation" },
	{ GECmdFormat::LIGHT_TYPE, GECmdField::LIGHT_TYPE, "type" },
	{ GECmdFormat::LIGHT_TYPE, GECmdField::LIGHT_TYPE_SPECULAR, "specular" },
	{ GECmdFormat::STRIDE, GECmdField::LOW_U11, "stride" },
	{ GECmdFormat::STRIDE_HIGH_ADDR, GECmdField::HIGH_ADDR, "highaddr" },
	{ GECmdFormat::HIGH_ADDR, GECmdField::HIGH_ADDR, "highaddr" },
	{ GECmdFormat::HIGH_ADDR_ONLY, GECmdField::HIGH_ADDR, "highaddr" },
	{ GECmdFormat::TEX_SIZE, GECmdField::TEX_W, "w" },
	{ GECmdFormat::TEX_SIZE, GECmdField::TEX_W, "width" },
	{ GECmdFormat::TEX_SIZE, GECmdField::TEX_H, "h" },
	{ GECmdFormat::TEX_SIZE, GECmdField::TEX_H, "height" },
	{ GECmdFormat::TEX_MAP_MODE, GECmdField::UVGEN_TYPE, "type" },
	{ GECmdFormat::TEX_MAP_MODE, GECmdField::UVGEN_TYPE, "uv" },
	{ GECmdFormat::TEX_MAP_MODE, GECmdField::UVGEN_PROJ, "proj" },
	{ GECmdFormat::TEX_MAP_MODE, GECmdField::UVGEN_PROJ, "factor" },
	{ GECmdFormat::TEX_LIGHT_SRC, GECmdField::LOW_U2, "u" },
	{ GECmdFormat::TEX_LIGHT_SRC, GECmdField::U2_AFTER_8, "v" },
	{ GECmdFormat::TEX_MODE, GECmdField::LOW_FLAG, "swizzle" },
	{ GECmdFormat::TEX_MODE, GECmdField::FLAG_AFTER_8, "separateclut" },
	{ GECmdFormat::TEX_MODE, GECmdField::FLAG_AFTER_8, "separate" },
	{ GECmdFormat::TEX_MODE, GECmdField::U3_AFTER_16, "maxlevel" },
	{ GECmdFormat::TEX_MODE, GECmdField::U3_AFTER_16, "level" },
	{ GECmdFormat::TEX_FORMAT, GECmdField::TEX_FORMAT, "format" },
	{ GECmdFormat::TEX_FORMAT, GECmdField::TEX_FORMAT, "fmt" },
	{ GECmdFormat::TEX_FORMAT, GECmdField::FLAG_AFTER_2, "indexed" },
	{ GECmdFormat::TEX_FILTER, GECmdField::TEX_MINFILTER, "min" },
	{ GECmdFormat::TEX_FILTER, GECmdField::TEX_MINFILTER, "minify" },
	{ GECmdFormat::TEX_FILTER, GECmdField::TEX_MAGFILTER, "mag" },
	{ GECmdFormat::TEX_FILTER, GECmdField::TEX_MAGFILTER, "magnify" },
	{ GECmdFormat::TEX_CLAMP, GECmdField::LOW_FLAG, "s" },
	{ GECmdFormat::TEX_CLAMP, GECmdField::FLAG_AFTER_8, "t" },
	{ GECmdFormat::TEX_LEVEL_MODE, GECmdField::TEX_LEVEL_MODE, "mode" },
	{ GECmdFormat::TEX_LEVEL_MODE, GECmdField::HIGH_S4_4_FLOAT, "bias" },
	{ GECmdFormat::TEX_FUNC, GECmdField::TEX_FUNC, "func" },
	{ GECmdFormat::TEX_FUNC, GECmdField::TEX_FUNC, "function" },
	{ GECmdFormat::TEX_FUNC, GECmdField::FLAG_AFTER_8, "alpha" },
	{ GECmdFormat::TEX_FUNC, GECmdField::FLAG_AFTER_8, "a" },
	{ GECmdFormat::TEX_FUNC, GECmdField::FLAG_AFTER_16, "double" },
	{ GECmdFormat::TEX_FUNC, GECmdField::FLAG_AFTER_16, "doubling" },
	{ GECmdFormat::CLUT_FORMAT, GECmdField::CLUT_FORMAT, "format" },
	{ GECmdFormat::CLUT_FORMAT, GECmdField::CLUT_FORMAT, "fmt" },
	{ GECmdFormat::CLUT_FORMAT, GECmdField::CLUT_SHIFT, "shift" },
	{ GECmdFormat::CLUT_FORMAT, GECmdField::MID_U8, "mask" },
	{ GECmdFormat::CLUT_FORMAT, GECmdField::CLUT_OFFSET, "offset" },
	{ GECmdFormat::CLUT_FORMAT, GECmdField::CLUT_OFFSET, "base" },
	{ GECmdFormat::CLEAR_MODE, GECmdField::LOW_FLAG, "on" },
	{ GECmdFormat::CLEAR_MODE, GECmdField::LOW_FLAG, "enable" },
	{ GECmdFormat::CLEAR_MODE, GECmdField::LOW_FLAG, "flag" },
	{ GECmdFormat::CLEAR_MODE, GECmdField::FLAG_AFTER_8, "color" },
	{ GECmdFormat::CLEAR_MODE, GECmdField::FLAG_AFTER_9, "alpha" },
	{ GECmdFormat::CLEAR_MODE, GECmdField::FLAG_AFTER_9, "stencil" },
	{ GECmdFormat::CLEAR_MODE, GECmdField::FLAG_AFTER_10, "depth" },
	{ GECmdFormat::COLOR_TEST_FUNC, GECmdField::COMPARE_FUNC2, "func" },
	{ GECmdFormat::ALPHA_TEST, GECmdField::COMPARE_FUNC3, "func" },
	{ GECmdFormat::ALPHA_TEST, GECmdField::MID_U8, "ref" },
	{ GECmdFormat::ALPHA_TEST, GECmdField::MID_U8, "reference" },
	{ GECmdFormat::ALPHA_TEST, GECmdField::TOP_U8, "mask" },
	{ GECmdFormat::STENCIL_OP, GECmdField::STENCIL_OP_AT_0, "sfail" },
	{ GECmdFormat::STENCIL_OP, GECmdField::STENCIL_OP_AT_8, "zfail" },
	{ GECmdFormat::STENCIL_OP, GECmdField::STENCIL_OP_AT_16, "zpass" },
	{ GECmdFormat::STENCIL_OP, GECmdField::STENCIL_OP_AT_16, "pass" },
	{ GECmdFormat::DEPTH_TEST_FUNC, GECmdField::COMPARE_FUNC3, "func" },
	{ GECmdFormat::BLEND_MODE, GECmdField::BLEND_SRC, "src" },
	{ GECmdFormat::BLEND_MODE, GECmdField::BLEND_SRC, "srcfactor" },
	{ GECmdFormat::BLEND_MODE, GECmdField::BLEND_DST, "dst" },
	{ GECmdFormat::BLEND_MODE, GECmdField::BLEND_DST, "dstfactor" },
	{ GECmdFormat::BLEND_MODE, GECmdField::BLEND_DST, "dest" },
	{ GECmdFormat::BLEND_MODE, GECmdField::BLEND_DST, "destfactor" },
	{ GECmdFormat::BLEND_MODE, GECmdField::BLEND_EQUATION, "eq" },
	{ GECmdFormat::BLEND_MODE, GECmdField::BLEND_EQUATION, "equation" },
	{ GECmdFormat::BLEND_MODE, GECmdField::BLEND_EQUATION, "mode" },
	{ GECmdFormat::LOGIC_OP, GECmdField::LOGIC_OP, "op" },
	{ GECmdFormat::LOGIC_OP, GECmdField::LOGIC_OP, "mode" },
	{ GECmdFormat::ALPHA_PRIM, GECmdField::LOW_U8, "alpha" },
	{ GECmdFormat::ALPHA_PRIM, GECmdField::PRIM_TYPE, "type" },
	{ GECmdFormat::ALPHA_PRIM, GECmdField::PRIM_TYPE, "prim" },
	{ GECmdFormat::ALPHA_PRIM, GECmdField::FLAG_AFTER_11, "antialias" },
	// TODO: Clip bits?
	{ GECmdFormat::ALPHA_PRIM, GECmdField::FLAG_AFTER_18, "shading" },
	{ GECmdFormat::ALPHA_PRIM, GECmdField::FLAG_AFTER_18, "gouraud" },
	{ GECmdFormat::ALPHA_PRIM, GECmdField::FLAG_AFTER_19, "cull" },
	{ GECmdFormat::ALPHA_PRIM, GECmdField::FLAG_AFTER_20, "cullccw" },
	{ GECmdFormat::ALPHA_PRIM, GECmdField::FLAG_AFTER_21, "tex" },
	{ GECmdFormat::ALPHA_PRIM, GECmdField::FLAG_AFTER_22, "fog" },
	{ GECmdFormat::ALPHA_PRIM, GECmdField::FLAG_AFTER_23, "dither" },
};

struct GECmdConstant {
	const char *name;
	uint32_t value;
};

// TODO: It would be nice if these were somehow typed...
static constexpr GECmdConstant constantNames[] = {
	{ "GE_PRIM_POINTS", GE_PRIM_POINTS },
	{ "GE_PRIM_LINES", GE_PRIM_LINES },
	{ "GE_PRIM_LINE_STRIP", GE_PRIM_LINE_STRIP },
	{ "GE_PRIM_TRIANGLES", GE_PRIM_TRIANGLES },
	{ "GE_PRIM_TRIANGLE_STRIP", GE_PRIM_TRIANGLE_STRIP },
	{ "GE_PRIM_TRIANGLE_FAN", GE_PRIM_TRIANGLE_FAN },
	{ "GE_PRIM_RECTANGLES", GE_PRIM_RECTANGLES },
	{ "GE_PRIM_KEEP_PREVIOUS", GE_PRIM_KEEP_PREVIOUS },
	{ "POINTS", GE_PRIM_POINTS },
	{ "LINES", GE_PRIM_LINES },
	{ "LINE_STRIP", GE_PRIM_LINE_STRIP },
	{ "TRIANGLES", GE_PRIM_TRIANGLES },
	{ "TRIANGLE_STRIP", GE_PRIM_TRIANGLE_STRIP },
	{ "TRIANGLE_FAN", GE_PRIM_TRIANGLE_FAN },
	{ "RECTANGLES", GE_PRIM_RECTANGLES },
	{ "SPRITES", GE_PRIM_RECTANGLES },
	{ "KEEP_PREVIOUS", GE_PRIM_KEEP_PREVIOUS },
	{ "CONTINUE", GE_PRIM_KEEP_PREVIOUS },
	{ "GE_PATCHPRIM_TRIANGLES", GE_PATCHPRIM_TRIANGLES, },
	{ "GE_PATCHPRIM_LINES", GE_PATCHPRIM_LINES, },
	{ "GE_PATCHPRIM_POINTS", GE_PATCHPRIM_POINTS, },
	{ "GE_SIGNAL_NONE", PSP_GE_SIGNAL_NONE },
	{ "GE_SIGNAL_HANDLER_SUSPEND", PSP_GE_SIGNAL_HANDLER_SUSPEND },
	{ "GE_SIGNAL_HANDLER_CONTINUE", PSP_GE_SIGNAL_HANDLER_CONTINUE },
	{ "GE_SIGNAL_HANDLER_PAUSE", PSP_GE_SIGNAL_HANDLER_PAUSE },
	{ "GE_SIGNAL_SYNC", PSP_GE_SIGNAL_SYNC },
	{ "GE_SIGNAL_JUMP", PSP_GE_SIGNAL_JUMP },
	{ "GE_SIGNAL_CALL", PSP_GE_SIGNAL_CALL },
	{ "GE_SIGNAL_RET", PSP_GE_SIGNAL_RET },
	{ "GE_SIGNAL_RJUMP", PSP_GE_SIGNAL_RJUMP },
	{ "GE_SIGNAL_RCALL", PSP_GE_SIGNAL_RCALL },
	{ "GE_SIGNAL_OJUMP", PSP_GE_SIGNAL_OJUMP },
	{ "GE_SIGNAL_OCALL", PSP_GE_SIGNAL_OCALL },
	{ "GE_VTYPE_TC_NONE", GE_VTYPE_TC_NONE >> GE_VTYPE_TC_SHIFT },
	{ "GE_VTYPE_TC_8BIT", GE_VTYPE_TC_8BIT >> GE_VTYPE_TC_SHIFT },
	{ "GE_VTYPE_TC_16BIT", GE_VTYPE_TC_16BIT >> GE_VTYPE_TC_SHIFT },
	{ "GE_VTYPE_TC_FLOAT", GE_VTYPE_TC_FLOAT >> GE_VTYPE_TC_SHIFT },
	{ "GE_VTYPE_COL_NONE", GE_VTYPE_COL_NONE >> GE_VTYPE_COL_SHIFT },
	{ "GE_VTYPE_COL_565", GE_VTYPE_COL_565 >> GE_VTYPE_COL_SHIFT },
	{ "GE_VTYPE_COL_5650", GE_VTYPE_COL_565 >> GE_VTYPE_COL_SHIFT },
	{ "GE_VTYPE_COL_5551", GE_VTYPE_COL_5551 >> GE_VTYPE_COL_SHIFT },
	{ "GE_VTYPE_COL_4444", GE_VTYPE_COL_4444 >> GE_VTYPE_COL_SHIFT },
	{ "GE_VTYPE_COL_8888", GE_VTYPE_COL_8888 >> GE_VTYPE_COL_SHIFT },
	{ "GE_VTYPE_NRM_NONE", GE_VTYPE_NRM_NONE >> GE_VTYPE_NRM_SHIFT },
	{ "GE_VTYPE_NRM_8BIT", GE_VTYPE_NRM_8BIT >> GE_VTYPE_NRM_SHIFT },
	{ "GE_VTYPE_NRM_16BIT", GE_VTYPE_NRM_16BIT >> GE_VTYPE_NRM_SHIFT },
	{ "GE_VTYPE_NRM_FLOAT", GE_VTYPE_NRM_FLOAT >> GE_VTYPE_NRM_SHIFT },
	{ "GE_VTYPE_POS_8BIT", GE_VTYPE_POS_8BIT >> GE_VTYPE_POS_SHIFT },
	{ "GE_VTYPE_POS_16BIT", GE_VTYPE_POS_16BIT >> GE_VTYPE_POS_SHIFT },
	{ "GE_VTYPE_POS_FLOAT", GE_VTYPE_POS_FLOAT >> GE_VTYPE_POS_SHIFT },
	{ "GE_VTYPE_WEIGHT_NONE", GE_VTYPE_WEIGHT_NONE >> GE_VTYPE_WEIGHT_SHIFT },
	{ "GE_VTYPE_WEIGHT_8BIT", GE_VTYPE_WEIGHT_8BIT >> GE_VTYPE_WEIGHT_SHIFT },
	{ "GE_VTYPE_WEIGHT_16BIT", GE_VTYPE_WEIGHT_16BIT >> GE_VTYPE_WEIGHT_SHIFT },
	{ "GE_VTYPE_WEIGHT_FLOAT", GE_VTYPE_WEIGHT_FLOAT >> GE_VTYPE_WEIGHT_SHIFT },
	{ "GE_VTYPE_IDX_NONE", GE_VTYPE_IDX_NONE >> GE_VTYPE_IDX_SHIFT },
	{ "GE_VTYPE_IDX_8BIT", GE_VTYPE_IDX_8BIT >> GE_VTYPE_IDX_SHIFT },
	{ "GE_VTYPE_IDX_16BIT", GE_VTYPE_IDX_16BIT >> GE_VTYPE_IDX_SHIFT },
	{ "GE_VTYPE_IDX_32BIT", GE_VTYPE_IDX_32BIT >> GE_VTYPE_IDX_SHIFT },
	{ "GE_LIGHTCOMP_ONLYDIFFUSE", GE_LIGHTCOMP_ONLYDIFFUSE },
	{ "GE_LIGHTCOMP_BOTH", GE_LIGHTCOMP_BOTH },
	{ "GE_LIGHTCOMP_ONLYPOWDIFFUSE", GE_LIGHTCOMP_ONLYPOWDIFFUSE },
	{ "GE_LIGHTTYPE_DIRECTIONAL", GE_LIGHTTYPE_DIRECTIONAL },
	{ "GE_LIGHTTYPE_POINT", GE_LIGHTTYPE_POINT },
	{ "GE_LIGHTTYPE_SPOT", GE_LIGHTTYPE_SPOT },
	{ "GE_TEXMAP_TEXTURE_COORDS", GE_TEXMAP_TEXTURE_COORDS },
	{ "GE_TEXMAP_TEXTURE_MATRIX", GE_TEXMAP_TEXTURE_MATRIX },
	{ "GE_TEXMAP_ENVIRONMENT_MAP", GE_TEXMAP_ENVIRONMENT_MAP },
	{ "GE_PROJMAP_POSITION", GE_PROJMAP_POSITION },
	{ "GE_PROJMAP_UV", GE_PROJMAP_UV },
	{ "GE_PROJMAP_NORMALIZED_NORMAL", GE_PROJMAP_NORMALIZED_NORMAL },
	{ "GE_PROJMAP_NORMAL", GE_PROJMAP_NORMAL },
	{ "GE_TFMT_565", GE_TFMT_5650 },
	{ "GE_TFMT_5650", GE_TFMT_5650 },
	{ "GE_TFMT_5551", GE_TFMT_5551 },
	{ "GE_TFMT_4444", GE_TFMT_4444 },
	{ "GE_TFMT_8888", GE_TFMT_8888 },
	{ "GE_TFMT_CLUT4", GE_TFMT_CLUT4 },
	{ "GE_TFMT_CLUT8", GE_TFMT_CLUT8 },
	{ "GE_TFMT_CLUT16", GE_TFMT_CLUT16 },
	{ "GE_TFMT_CLUT32", GE_TFMT_CLUT32 },
	{ "GE_TFMT_DXT1", GE_TFMT_DXT1 },
	{ "GE_TFMT_DXT3", GE_TFMT_DXT3 },
	{ "GE_TFMT_DXT5", GE_TFMT_DXT5 },
	{ "GE_CMODE_16BIT_BGR5650", GE_CMODE_16BIT_BGR5650 },
	{ "GE_CMODE_16BIT_ABGR5551", GE_CMODE_16BIT_ABGR5551 },
	{ "GE_CMODE_16BIT_ABGR4444", GE_CMODE_16BIT_ABGR4444 },
	{ "GE_CMODE_32BIT_ABGR8888", GE_CMODE_32BIT_ABGR8888 },
	{ "GE_TFILT_NEAREST", GE_TFILT_NEAREST },
	{ "GE_TFILT_LINEAR", GE_TFILT_LINEAR },
	{ "GE_TFILT_NEAREST_MIPMAP_NEAREST", GE_TFILT_NEAREST_MIPMAP_NEAREST },
	{ "GE_TFILT_LINEAR_MIPMAP_NEAREST", GE_TFILT_LINEAR_MIPMAP_NEAREST },
	{ "GE_TFILT_NEAREST_MIPMAP_LINEAR", GE_TFILT_NEAREST_MIPMAP_LINEAR },
	{ "GE_TFILT_LINEAR_MIPMAP_LINEAR", GE_TFILT_LINEAR_MIPMAP_LINEAR },
	{ "NEAREST", GE_TFILT_NEAREST },
	{ "LINEAR", GE_TFILT_LINEAR },
	{ "GE_TEXLEVEL_MODE_AUTO", GE_TEXLEVEL_MODE_AUTO },
	{ "GE_TEXLEVEL_MODE_CONST", GE_TEXLEVEL_MODE_CONST },
	{ "GE_TEXLEVEL_MODE_SLOPE", GE_TEXLEVEL_MODE_SLOPE },
	{ "GE_TEXFUNC_MODULATE", GE_TEXFUNC_MODULATE },
	{ "GE_TEXFUNC_DECAL", GE_TEXFUNC_DECAL },
	{ "GE_TEXFUNC_BLEND", GE_TEXFUNC_BLEND },
	{ "GE_TEXFUNC_REPLACE", GE_TEXFUNC_REPLACE },
	{ "GE_TEXFUNC_ADD", GE_TEXFUNC_ADD },
	{ "GE_COMP_NEVER", GE_COMP_NEVER },
	{ "GE_COMP_ALWAYS", GE_COMP_ALWAYS },
	{ "GE_COMP_EQUAL", GE_COMP_EQUAL },
	{ "GE_COMP_NOTEQUAL", GE_COMP_NOTEQUAL },
	{ "GE_COMP_LESS", GE_COMP_LESS },
	{ "GE_COMP_LEQUAL", GE_COMP_LEQUAL },
	{ "GE_COMP_GREATER", GE_COMP_GREATER },
	{ "GE_COMP_GEQUAL", GE_COMP_GEQUAL },
	{ "NEVER", GE_COMP_NEVER },
	{ "ALWAYS", GE_COMP_ALWAYS },
	{ "EQUAL", GE_COMP_EQUAL },
	{ "NOTEQUAL", GE_COMP_NOTEQUAL },
	{ "LESS", GE_COMP_LESS },
	{ "LEQUAL", GE_COMP_LEQUAL },
	{ "LESSEQUAL", GE_COMP_LEQUAL },
	{ "GREATER", GE_COMP_GREATER },
	{ "GEQUAL", GE_COMP_GEQUAL },
	{ "GREATEREQUAL", GE_COMP_GEQUAL },
	{ "GE_STENCILOP_KEEP", GE_STENCILOP_KEEP },
	{ "GE_STENCILOP_ZERO", GE_STENCILOP_ZERO },
	{ "GE_STENCILOP_REPLACE", GE_STENCILOP_REPLACE },
	{ "GE_STENCILOP_INVERT", GE_STENCILOP_INVERT },
	{ "GE_STENCILOP_INCR", GE_STENCILOP_INCR },
	{ "GE_STENCILOP_DECR", GE_STENCILOP_DECR },
	{ "GE_BLENDMODE_MUL_AND_ADD", GE_BLENDMODE_MUL_AND_ADD },
	{ "GE_BLENDMODE_MUL_AND_SUBTRACT", GE_BLENDMODE_MUL_AND_SUBTRACT },
	{ "GE_BLENDMODE_MUL_AND_SUBTRACT_REVERSE", GE_BLENDMODE_MUL_AND_SUBTRACT_REVERSE },
	{ "GE_BLENDMODE_MIN", GE_BLENDMODE_MIN },
	{ "GE_BLENDMODE_MAX", GE_BLENDMODE_MAX },
	{ "GE_BLENDMODE_ABSDIFF", GE_BLENDMODE_ABSDIFF },
	{ "ADD", GE_BLENDMODE_MUL_AND_ADD },
	{ "SUBTRACT", GE_BLENDMODE_MUL_AND_SUBTRACT },
	{ "SUBTRACT_REVERSE", GE_BLENDMODE_MUL_AND_SUBTRACT_REVERSE },
	{ "MIN", GE_BLENDMODE_MIN },
	{ "MAX", GE_BLENDMODE_MAX },
	{ "ABSDIFF", GE_BLENDMODE_ABSDIFF },
	{ "GE_SRCBLEND_DSTCOLOR", GE_SRCBLEND_DSTCOLOR },
	{ "GE_SRCBLEND_INVDSTCOLOR", GE_SRCBLEND_INVDSTCOLOR },
	{ "GE_SRCBLEND_SRCALPHA", GE_SRCBLEND_SRCALPHA },
	{ "GE_SRCBLEND_INVSRCALPHA", GE_SRCBLEND_INVSRCALPHA },
	{ "GE_SRCBLEND_DSTALPHA", GE_SRCBLEND_DSTALPHA },
	{ "GE_SRCBLEND_INVDSTALPHA", GE_SRCBLEND_INVDSTALPHA },
	{ "GE_SRCBLEND_DOUBLESRCALPHA", GE_SRCBLEND_DOUBLESRCALPHA },
	{ "GE_SRCBLEND_DOUBLEINVSRCALPHA", GE_SRCBLEND_DOUBLEINVSRCALPHA },
	{ "GE_SRCBLEND_DOUBLEDSTALPHA", GE_SRCBLEND_DOUBLEDSTALPHA },
	{ "GE_SRCBLEND_DOUBLEINVDSTALPHA", GE_SRCBLEND_DOUBLEINVDSTALPHA },
	{ "GE_SRCBLEND_FIXA", GE_SRCBLEND_FIXA },
	{ "GE_DSTBLEND_SRCCOLOR", GE_DSTBLEND_SRCCOLOR },
	{ "GE_DSTBLEND_INVSRCCOLOR", GE_DSTBLEND_INVSRCCOLOR },
	{ "GE_DSTBLEND_SRCALPHA", GE_DSTBLEND_SRCALPHA },
	{ "GE_DSTBLEND_INVSRCALPHA", GE_DSTBLEND_INVSRCALPHA },
	{ "GE_DSTBLEND_DSTALPHA", GE_DSTBLEND_DSTALPHA },
	{ "GE_DSTBLEND_INVDSTALPHA", GE_DSTBLEND_INVDSTALPHA },
	{ "GE_DSTBLEND_DOUBLESRCALPHA", GE_DSTBLEND_DOUBLESRCALPHA },
	{ "GE_DSTBLEND_DOUBLEINVSRCALPHA", GE_DSTBLEND_DOUBLEINVSRCALPHA },
	{ "GE_DSTBLEND_DOUBLEDSTALPHA", GE_DSTBLEND_DOUBLEDSTALPHA },
	{ "GE_DSTBLEND_DOUBLEINVDSTALPHA", GE_DSTBLEND_DOUBLEINVDSTALPHA },
	{ "GE_DSTBLEND_FIXB", GE_DSTBLEND_FIXB },
	{ "SRCALPHA", GE_SRCBLEND_SRCALPHA },
	{ "INVSRCALPHA", GE_SRCBLEND_INVSRCALPHA },
	{ "INVERSESRCALPHA", GE_SRCBLEND_INVSRCALPHA },
	{ "DSTALPHA", GE_DSTBLEND_DSTALPHA },
	{ "INVDSTALPHA", GE_DSTBLEND_INVDSTALPHA },
	{ "DOUBLESRCALPHA", GE_DSTBLEND_DOUBLESRCALPHA },
	{ "DOUBLEINVSRCALPHA", GE_DSTBLEND_DOUBLEINVSRCALPHA },
	{ "DOUBLEDSTALPHA", GE_DSTBLEND_DOUBLEDSTALPHA },
	{ "DOUBLEINVDSTALPHA", GE_DSTBLEND_DOUBLEINVDSTALPHA },
	{ "FIXED", GE_DSTBLEND_FIXB },
	{ "GE_LOGIC_CLEAR", GE_LOGIC_CLEAR },
	{ "GE_LOGIC_AND", GE_LOGIC_AND },
	{ "GE_LOGIC_AND_REVERSE", GE_LOGIC_AND_REVERSE },
	{ "GE_LOGIC_COPY", GE_LOGIC_COPY },
	{ "GE_LOGIC_AND_INVERTED", GE_LOGIC_AND_INVERTED },
	{ "GE_LOGIC_NOOP", GE_LOGIC_NOOP },
	{ "GE_LOGIC_XOR", GE_LOGIC_XOR },
	{ "GE_LOGIC_OR", GE_LOGIC_OR },
	{ "GE_LOGIC_NOR", GE_LOGIC_NOR },
	{ "GE_LOGIC_EQUIV", GE_LOGIC_EQUIV },
	{ "GE_LOGIC_INVERTED", GE_LOGIC_INVERTED },
	{ "GE_LOGIC_OR_REVERSE", GE_LOGIC_OR_REVERSE },
	{ "GE_LOGIC_COPY_INVERTED", GE_LOGIC_COPY_INVERTED },
	{ "GE_LOGIC_OR_INVERTED", GE_LOGIC_OR_INVERTED },
	{ "GE_LOGIC_NAND", GE_LOGIC_NAND },
	{ "GE_LOGIC_SET", GE_LOGIC_SET },
};

class GEExpressionFunctions : public IExpressionFunctions {
public:
	GEExpressionFunctions(GPUDebugInterface *gpu) : gpu_(gpu) {}

	bool parseReference(char *str, uint32_t &referenceIndex) override;
	bool parseSymbol(char *str, uint32_t &symbolValue) override;
	uint32_t getReferenceValue(uint32_t referenceIndex) override;
	ExpressionType getReferenceType(uint32_t referenceIndex) override;
	bool getMemoryValue(uint32_t address, int size, uint32_t &dest, std::string *error) override;

private:
	bool parseFieldReference(const char *ref, const char *field, uint32_t &referenceIndex);
	static uint32_t getFieldValue(GECmdFormat fmt, GECmdField field, uint32_t value);
	static ExpressionType getFieldType(GECmdFormat fmt, GECmdField field);

	GPUDebugInterface *gpu_;
};

bool GEExpressionFunctions::parseReference(char *str, uint32_t &referenceIndex) {
	// TODO: Support formats and a form of fields (i.e. vtype.throughmode.)
	// For now, let's just support the register bits directly.
	GECmdInfo info;
	if (GECmdInfoByName(str, info)) {
		referenceIndex = info.cmd;
		return true;
	}

	char *dot = strchr(str, '.');
	if (dot != nullptr) {
		*dot = '\0';
		bool success = parseFieldReference(str, dot + 1, referenceIndex);
		*dot = '.';
		if (success)
			return true;
	}

	// Also allow non-register references.
	for (const auto &entry : referenceNames) {
		if (strcasecmp(str, entry.name) == 0) {
			referenceIndex = (uint32_t)entry.index;
			return true;
		}
	}

	// And matrix data.  Maybe should allow column/row specification.
	int subindex = -1;
	int len = -1;

	if (sscanf(str, "bone%i%n", &subindex, &len) == 1) {
		if (len == strlen(str) && subindex < 96) {
			referenceIndex = (uint32_t)GEReferenceIndex::BONE_MATRIX + subindex;
			return true;
		}
	}
	if (sscanf(str, "world%i%n", &subindex, &len) == 1) {
		if (len == strlen(str) && subindex < 12) {
			referenceIndex = (uint32_t)GEReferenceIndex::WORLD_MATRIX + subindex;
			return true;
		}
	}
	if (sscanf(str, "view%i%n", &subindex, &len) == 1) {
		if (len == strlen(str) && subindex < 12) {
			referenceIndex = (uint32_t)GEReferenceIndex::VIEW_MATRIX + subindex;
			return true;
		}
	}
	if (sscanf(str, "proj%i%n", &subindex, &len) == 1) {
		if (len == strlen(str) && subindex < 16) {
			referenceIndex = (uint32_t)GEReferenceIndex::PROJ_MATRIX + subindex;
			return true;
		}
	}
	if (sscanf(str, "tgen%i%n", &subindex, &len) == 1 || sscanf(str, "texgen%i%n", &subindex, &len) == 1) {
		if (len == strlen(str) && subindex < 12) {
			referenceIndex = (uint32_t)GEReferenceIndex::TGEN_MATRIX + subindex;
			return true;
		}
	}

	return false;
}

bool GEExpressionFunctions::parseFieldReference(const char *ref, const char *field, uint32_t &referenceIndex) {
	GECmdInfo info;
	if (!GECmdInfoByName(ref, info)) {
		return false;
	}

	for (const auto &entry : fieldNames) {
		if (entry.fmt == info.cmdFmt && strcasecmp(field, entry.name) == 0) {
			referenceIndex = (info.cmd << 12) | (uint32_t)entry.field;
			return true;
		}
	}

	return false;
}

bool GEExpressionFunctions::parseSymbol(char *str, uint32_t &symbolValue) {
	// Mainly useful for checking memory addresses and constants.

	for (const auto &entry : constantNames) {
		if (strcasecmp(str, entry.name) == 0) {
			symbolValue = entry.value;
			return true;
		}
	}

	return g_symbolMap->GetLabelValue(str, symbolValue);
}

uint32_t GEExpressionFunctions::getReferenceValue(uint32_t referenceIndex) {
	GPUgstate state = gpu_->GetGState();
	if (referenceIndex < 0x100) {
		GECmdFormat fmt = GECmdInfoByCmd(GECommand(referenceIndex)).cmdFmt;
		uint32_t value = state.cmdmem[referenceIndex];
		if (fmt == GECmdFormat::FLOAT)
			return value << 8;
		return value & 0x00FFFFFF;
	}

	if (referenceIndex >= (uint32_t)GEReferenceIndex::FIELD_START && referenceIndex <= (uint32_t)GEReferenceIndex::FIELD_END) {
		uint32_t value = state.cmdmem[referenceIndex >> 12] & 0x00FFFFFF;
		GECmdFormat fmt = GECmdInfoByCmd(GECommand(referenceIndex >> 12)).cmdFmt;
		return getFieldValue(fmt, GECmdField(referenceIndex & 0xFF), value);
	}

	// We return the matrix value as float bits, which gets interpreted correctly in the parser.
	if (referenceIndex >= (uint32_t)GEReferenceIndex::BONE_MATRIX && referenceIndex < (uint32_t)GEReferenceIndex::MATRIX_END) {
		float value;
		if (referenceIndex >= (uint32_t)GEReferenceIndex::TGEN_MATRIX) {
			value = state.tgenMatrix[referenceIndex - (uint32_t)GEReferenceIndex::TGEN_MATRIX];
		} else if (referenceIndex >= (uint32_t)GEReferenceIndex::PROJ_MATRIX) {
			value = state.projMatrix[referenceIndex - (uint32_t)GEReferenceIndex::PROJ_MATRIX];
		} else if (referenceIndex >= (uint32_t)GEReferenceIndex::VIEW_MATRIX) {
			value = state.viewMatrix[referenceIndex - (uint32_t)GEReferenceIndex::VIEW_MATRIX];
		} else if (referenceIndex >= (uint32_t)GEReferenceIndex::WORLD_MATRIX) {
			value = state.worldMatrix[referenceIndex - (uint32_t)GEReferenceIndex::WORLD_MATRIX];
		} else {
			value = state.boneMatrix[referenceIndex - (uint32_t)GEReferenceIndex::BONE_MATRIX];
		}

		uint32_t result;
		memcpy(&result, &value, sizeof(result));
		return result;
	}

	GEReferenceIndex ref = (GEReferenceIndex)referenceIndex;
	DisplayList list;
	switch (ref) {
	case GEReferenceIndex::VADDR:
		return gpu_->GetVertexAddress();
	case GEReferenceIndex::IADDR:
		return gpu_->GetIndexAddress();
	case GEReferenceIndex::OFFSET:
		// TODO: Should use an interface method, probably.
		return gstate_c.offsetAddr;
	case GEReferenceIndex::PC:
		if (gpu_->GetCurrentDisplayList(list)) {
			return list.pc;
		}
		return 0;
	case GEReferenceIndex::STALL:
		if (gpu_->GetCurrentDisplayList(list)) {
			return list.stall;
		}
		return 0;
	case GEReferenceIndex::BFLAG:
		if (gpu_->GetCurrentDisplayList(list)) {
			return list.bboxResult ? 1 : 0;
		}
		return 0;
	case GEReferenceIndex::OP:
		if (gpu_->GetCurrentDisplayList(list)) {
			return Memory::Read_U32(list.pc);
		}
		return 0;
	case GEReferenceIndex::DATA:
		if (gpu_->GetCurrentDisplayList(list)) {
			return Memory::Read_U32(list.pc) & 0x00FFFFFF;
		}
		return 0;

	case GEReferenceIndex::CLUTADDR:
		return state.getClutAddress();

	case GEReferenceIndex::TRANSFERSRC:
		return state.getTransferSrcAddress();

	case GEReferenceIndex::TRANSFERDST:
		return state.getTransferDstAddress();

	case GEReferenceIndex::PRIMCOUNT:
		return gpu_->PrimsThisFrame();

	case GEReferenceIndex::LASTPRIMCOUNT:
		return gpu_->PrimsLastFrame();

	case GEReferenceIndex::TEXADDR0:
	case GEReferenceIndex::TEXADDR1:
	case GEReferenceIndex::TEXADDR2:
	case GEReferenceIndex::TEXADDR3:
	case GEReferenceIndex::TEXADDR4:
	case GEReferenceIndex::TEXADDR5:
	case GEReferenceIndex::TEXADDR6:
	case GEReferenceIndex::TEXADDR7:
		return state.getTextureAddress((int)ref - (int)GEReferenceIndex::TEXADDR0);

	case GEReferenceIndex::BONE_MATRIX:
	case GEReferenceIndex::WORLD_MATRIX:
	case GEReferenceIndex::VIEW_MATRIX:
	case GEReferenceIndex::PROJ_MATRIX:
	case GEReferenceIndex::TGEN_MATRIX:
	case GEReferenceIndex::MATRIX_END:
	case GEReferenceIndex::FIELD_START:
	case GEReferenceIndex::FIELD_END:
		// Shouldn't have gotten here.
		break;
	}

	_assert_msg_(false, "Invalid reference index");
	return 0;
}

uint32_t GEExpressionFunctions::getFieldValue(GECmdFormat fmt, GECmdField field, uint32_t value) {
	switch (field) {
	case GECmdField::DATA:
		return value;
	case GECmdField::LOW_FLAG:
		return value & 1;
	case GECmdField::LOW_U2:
		return value & 3;
	case GECmdField::LOW_U4:
		return value & 0xF;
	case GECmdField::LOW_U7:
		return value & 0x7F;
	case GECmdField::LOW_U8:
		return value & 0xFF;
	case GECmdField::LOW_U10:
		return value & 0x03FF;
	case GECmdField::LOW_U10_P1:
		return (value & 0x03FF) + 1;
	case GECmdField::LOW_U11:
		return value & 0x07FF;
	case GECmdField::LOW_U16:
		return value & 0xFFFF;
	case GECmdField::MID_U8:
		return (value >> 8) & 0xFF;
	case GECmdField::MID_U10:
		return (value >> 10) & 0x03FF;
	case GECmdField::MID_U10_P1:
		return ((value >> 10) & 0x03FF) + 1;
	case GECmdField::TOP_U8:
		return (value >> 16) & 0xFF;
	case GECmdField::FLAG_AFTER_1:
		return (value >> 1) & 1;
	case GECmdField::FLAG_AFTER_2:
		return (value >> 2) & 1;
	case GECmdField::FLAG_AFTER_8:
		return (value >> 8) & 1;
	case GECmdField::FLAG_AFTER_9:
		return (value >> 9) & 1;
	case GECmdField::FLAG_AFTER_10:
		return (value >> 10) & 1;
	case GECmdField::FLAG_AFTER_11:
		return (value >> 11) & 1;
	case GECmdField::FLAG_AFTER_16:
		return (value >> 16) & 1;
	case GECmdField::FLAG_AFTER_17:
		return (value >> 17) & 1;
	case GECmdField::FLAG_AFTER_18:
		return (value >> 18) & 1;
	case GECmdField::FLAG_AFTER_19:
		return (value >> 19) & 1;
	case GECmdField::FLAG_AFTER_20:
		return (value >> 20) & 1;
	case GECmdField::FLAG_AFTER_21:
		return (value >> 21) & 1;
	case GECmdField::FLAG_AFTER_22:
		return (value >> 22) & 1;
	case GECmdField::FLAG_AFTER_23:
		return (value >> 23) & 1;
	case GECmdField::U2_AFTER_8:
		return (value >> 8) & 3;
	case GECmdField::U3_AFTER_16:
		return (value >> 16) & 7;
	case GECmdField::U12_AFTER_4:
		return (value >> 4) & 0x0FFF;

	// Below here are "typed" values, maybe they'll be exposed differently than integers someday.

	case GECmdField::PRIM_TYPE:
		return (value >> 16) & 7;
	case GECmdField::SIGNAL_TYPE:
		return (value >> 16) & 0xFF;
	case GECmdField::VTYPE_TC:
		return value & 3;
	case GECmdField::VTYPE_COL:
		return (value >> 2) & 7;
	case GECmdField::VTYPE_NRM:
		return (value >> 5) & 3;
	case GECmdField::VTYPE_POS:
		return (value >> 7) & 3;
	case GECmdField::VTYPE_WEIGHTTYPE:
		return (value >> 9) & 3;
	case GECmdField::VTYPE_INDEX:
		return (value >> 11) & 3;
	case GECmdField::VTYPE_WEIGHTCOUNT:
		return ((value >> 14) & 7) + 1;
	case GECmdField::VTYPE_MORPHCOUNT:
		return ((value >> 18) & 7) + 1;
	case GECmdField::PATCH_PRIM_TYPE:
		return value & 3;
	case GECmdField::LIGHT_COMP:
		return value & 3;
	case GECmdField::LIGHT_TYPE:
		return (value >> 8) & 3;
	case GECmdField::LIGHT_TYPE_SPECULAR:
		return (value & 3) == 1;
	case GECmdField::HIGH_ADDR:
		return (value << 8) & 0xFF000000;
	case GECmdField::TEX_W:
		return 1 << (value & 0xF);
	case GECmdField::TEX_H:
		return 1 << ((value >> 8) & 0xF);
	case GECmdField::UVGEN_TYPE:
		return value & 3;
	case GECmdField::UVGEN_PROJ:
		return (value >> 8) & 3;
	case GECmdField::TEX_FORMAT:
		return value & 0xF;
	case GECmdField::TEX_MINFILTER:
		return value & 7;
	case GECmdField::TEX_MAGFILTER:
		return (value >> 8) & 1;
	case GECmdField::TEX_LEVEL_MODE:
		return value & 3;
	case GECmdField::LOW_U12_4_FLOAT:
	{
		float f = (value & 0xFFFF) / 16.0f;
		uint32_t result;
		memcpy(&result, &f, sizeof(result));
		return result;
	}
	case GECmdField::HIGH_S4_4_FLOAT: // At 16, s.3.4 converted to float.
	{
		int value8 = (int)(s8)((value >> 16) & 0xFF);
		float f = value8 / 16.0f;
		uint32_t result;
		memcpy(&result, &f, sizeof(result));
		return result;
	}
	case GECmdField::TEX_FUNC:
		return value & 7;
	case GECmdField::CLUT_BYTES:
		return (value & 0x3F) * 8;
	case GECmdField::CLUT_FORMAT:
		return value & 3;
	case GECmdField::CLUT_SHIFT:
		return (value >> 2) & 0x1F;
	case GECmdField::CLUT_OFFSET:
		return ((value >> 16) & 0x1F) << 4;
	case GECmdField::COMPARE_FUNC2:
		return value & 3;
	case GECmdField::COMPARE_FUNC3:
		return value & 7;
	case GECmdField::STENCIL_OP_AT_0:
		return value & 7;
	case GECmdField::STENCIL_OP_AT_8:
		return (value >> 8) & 7;
	case GECmdField::STENCIL_OP_AT_16:
		return (value >> 16) & 7;
	case GECmdField::BLEND_SRC:
		return value & 0xF;
	case GECmdField::BLEND_DST:
		return (value >> 4) & 0xF;
	case GECmdField::BLEND_EQUATION:
		return (value >> 8) & 7;
	case GECmdField::LOGIC_OP:
		return value & 0xF;
	}

	_assert_msg_(false, "Invalid field type");
	return 0;
}

ExpressionType GEExpressionFunctions::getReferenceType(uint32_t referenceIndex) {
	if (referenceIndex < 0x100) {
		GECmdFormat fmt = GECmdInfoByCmd(GECommand(referenceIndex)).cmdFmt;
		if (fmt == GECmdFormat::FLOAT)
			return EXPR_TYPE_FLOAT;
		return EXPR_TYPE_UINT;
	}

	if (referenceIndex >= (uint32_t)GEReferenceIndex::FIELD_START && referenceIndex <= (uint32_t)GEReferenceIndex::FIELD_END) {
		GECmdFormat fmt = GECmdInfoByCmd(GECommand(referenceIndex >> 12)).cmdFmt;
		return getFieldType(fmt, GECmdField(referenceIndex & 0xFF));
	}

	if (referenceIndex >= (uint32_t)GEReferenceIndex::BONE_MATRIX && referenceIndex < (uint32_t)GEReferenceIndex::MATRIX_END)
		return EXPR_TYPE_FLOAT;
	return EXPR_TYPE_UINT;
}

ExpressionType GEExpressionFunctions::getFieldType(GECmdFormat fmt, GECmdField field) {
	switch (field) {
	case GECmdField::LOW_U12_4_FLOAT:
	case GECmdField::HIGH_S4_4_FLOAT:
		return EXPR_TYPE_FLOAT;

	default:
		// Almost all of these are uint, so not listing each one.
		break;
	}

	return EXPR_TYPE_UINT;
}

bool GEExpressionFunctions::getMemoryValue(uint32_t address, int size, uint32_t &dest, std::string *error) {
	// We allow, but ignore, bad access.
	// If we didn't, log/condition statements that reference registers couldn't be configured.
	uint32_t valid = Memory::ClampValidSizeAt(address, size);
	uint8_t buf[4]{};
	if (valid != 0)
		memcpy(buf, Memory::GetPointerUnchecked(address), valid);

	switch (size) {
	case 1:
		dest = buf[0];
		return true;
	case 2:
		dest = (buf[1] << 8) | buf[0];
		return true;
	case 4:
		dest = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
		return true;
	}

	*error = StringFromFormat("Unexpected memory access size %d", size);
	return false;
}

bool GPUDebugInitExpression(GPUDebugInterface *g, const char *str, PostfixExpression &exp) {
	GEExpressionFunctions funcs(g);
	return initPostfixExpression(str, &funcs, exp);
}

bool GPUDebugExecExpression(GPUDebugInterface *g, PostfixExpression &exp, uint32_t &result) {
	GEExpressionFunctions funcs(g);
	return parsePostfixExpression(exp, &funcs, result);
}

bool GPUDebugExecExpression(GPUDebugInterface *g, const char *str, uint32_t &result) {
	GEExpressionFunctions funcs(g);
	return parseExpression(str, &funcs, result);
}

void GPUDebugBuffer::Allocate(u32 stride, u32 height, GEBufferFormat fmt, bool flipped, bool reversed) {
	GPUDebugBufferFormat actualFmt = GPUDebugBufferFormat(fmt);
	if (reversed && actualFmt < GPU_DBG_FORMAT_8888) {
		actualFmt |= GPU_DBG_FORMAT_REVERSE_FLAG;
	}
	Allocate(stride, height, actualFmt, flipped);
}

void GPUDebugBuffer::Allocate(u32 stride, u32 height, GPUDebugBufferFormat fmt, bool flipped) {
	if (alloc_ && stride_ == stride && height_ == height && fmt_ == fmt) {
		// Already allocated the right size.
		flipped_ = flipped;
		return;
	}

	Free();
	alloc_ = true;
	height_ = height;
	stride_ = stride;
	fmt_ = fmt;
	flipped_ = flipped;

	u32 pixelSize = PixelSize();
	data_ = new u8[pixelSize * stride * height];
}

void GPUDebugBuffer::Free() {
	if (alloc_ && data_ != NULL) {
		delete [] data_;
	}
	data_ = NULL;
}

void GPUDebugBuffer::ZeroBytes() {
	_dbg_assert_(data_);
	memset(data_, 0, PixelSize() * stride_ * height_);
}

u32 GPUDebugBuffer::PixelSize() const {
	switch (fmt_) {
	case GPU_DBG_FORMAT_8888:
	case GPU_DBG_FORMAT_8888_BGRA:
	case GPU_DBG_FORMAT_FLOAT:
	case GPU_DBG_FORMAT_24BIT_8X:
	case GPU_DBG_FORMAT_24X_8BIT:
	case GPU_DBG_FORMAT_FLOAT_DIV_256:
	case GPU_DBG_FORMAT_24BIT_8X_DIV_256:
		return 4;

	case GPU_DBG_FORMAT_888_RGB:
		return 3;

	case GPU_DBG_FORMAT_8BIT:
		return 1;

	default:
		return 2;
	}
}

u32 GPUDebugBuffer::GetRawPixel(int x, int y) const {
	if (data_ == nullptr) {
		return 0;
	}

	if (flipped_) {
		y = height_ - y - 1;
	}

	u32 pixelSize = PixelSize();
	u32 byteOffset = pixelSize * (stride_ * y + x);
	const u8 *ptr = &data_[byteOffset];

	switch (pixelSize) {
	case 4:
		return *(const u32 *)ptr;
	case 3:
		return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16);
	case 2:
		return *(const u16 *)ptr;
	case 1:
		return *(const u8 *)ptr;
	default:
		return 0;
	}
}

void GPUDebugBuffer::SetRawPixel(int x, int y, u32 c) {
	if (data_ == nullptr) {
		return;
	}

	if (flipped_) {
		y = height_ - y - 1;
	}

	u32 pixelSize = PixelSize();
	u32 byteOffset = pixelSize * (stride_ * y + x);
	u8 *ptr = &data_[byteOffset];

	switch (pixelSize) {
	case 4:
		*(u32 *)ptr = c;
		break;
	case 3:
		ptr[0] = (c >> 0) & 0xFF;
		ptr[1] = (c >> 8) & 0xFF;
		ptr[2] = (c >> 16) & 0xFF;
		break;
	case 2:
		*(u16 *)ptr = (u16)c;
		break;
	case 1:
		*ptr = (u8)c;
		break;
	default:
		break;
	}
}
