1 // Copyright (c) 2012- PPSSPP Project.
2 
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, version 2.0 or later versions.
6 
7 // This program is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 // GNU General Public License 2.0 for more details.
11 
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
14 
15 // Official git repository and contact information can be found at
16 // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17 
18 #include "Common/Common.h"
19 #include "Windows/resource.h"
20 #include "Windows/InputBox.h"
21 #include "Windows/GEDebugger/GEDebugger.h"
22 #include "Windows/GEDebugger/TabState.h"
23 #include "Windows/W32Util/ContextMenu.h"
24 #include "GPU/GPUState.h"
25 #include "GPU/GeDisasm.h"
26 #include "GPU/Common/GPUDebugInterface.h"
27 #include "GPU/Debugger/Breakpoints.h"
28 
29 using namespace GPUBreakpoints;
30 
31 // TODO: Show an icon or something for breakpoints, toggle.
32 static const GenericListViewColumn stateValuesCols[] = {
33 	{ L"Name", 0.50f },
34 	{ L"Value", 0.50f },
35 };
36 
37 GenericListViewDef stateValuesListDef = {
38 	stateValuesCols,	ARRAY_SIZE(stateValuesCols),	NULL,	false
39 };
40 
41 enum StateValuesCols {
42 	STATEVALUES_COL_NAME,
43 	STATEVALUES_COL_VALUE,
44 };
45 
46 enum CmdFormatType {
47 	CMD_FMT_HEX = 0,
48 	CMD_FMT_NUM,
49 	CMD_FMT_FLOAT24,
50 	CMD_FMT_PTRWIDTH,
51 	CMD_FMT_XY,
52 	CMD_FMT_XYXY,
53 	CMD_FMT_XYZ,
54 	CMD_FMT_TEXSIZE,
55 	CMD_FMT_F16_XY,
56 	CMD_FMT_VERTEXTYPE,
57 	CMD_FMT_TEXFMT,
58 	CMD_FMT_CLUTFMT,
59 	CMD_FMT_COLORTEST,
60 	CMD_FMT_ALPHATEST,
61 	CMD_FMT_STENCILTEST,
62 	CMD_FMT_ZTEST,
63 	CMD_FMT_OFFSETADDR,
64 	CMD_FMT_VADDR,
65 	CMD_FMT_IADDR,
66 	CMD_FMT_MATERIALUPDATE,
67 	CMD_FMT_STENCILOP,
68 	CMD_FMT_BLENDMODE,
69 	CMD_FMT_FLAG,
70 	CMD_FMT_CLEARMODE,
71 	CMD_FMT_TEXFUNC,
72 	CMD_FMT_TEXMODE,
73 	CMD_FMT_LOGICOP,
74 	CMD_FMT_TEXWRAP,
75 	CMD_FMT_TEXLEVEL,
76 	CMD_FMT_TEXFILTER,
77 	CMD_FMT_TEXMAPMODE,
78 	CMD_FMT_TEXSHADELS,
79 	CMD_FMT_SHADEMODEL,
80 	CMD_FMT_LIGHTMODE,
81 	CMD_FMT_LIGHTTYPE,
82 	CMD_FMT_CULL,
83 	CMD_FMT_PATCHPRIMITIVE,
84 };
85 
86 struct TabStateRow {
87 	const TCHAR *title;
88 	u8 cmd;
89 	CmdFormatType fmt;
90 	u8 enableCmd;
91 	u8 otherCmd;
92 	u8 otherCmd2;
93 };
94 
95 static const TabStateRow stateFlagsRows[] = {
96 	{ L"Lighting enable",      GE_CMD_LIGHTINGENABLE,          CMD_FMT_FLAG },
97 	{ L"Light 0 enable",       GE_CMD_LIGHTENABLE0,            CMD_FMT_FLAG },
98 	{ L"Light 1 enable",       GE_CMD_LIGHTENABLE1,            CMD_FMT_FLAG },
99 	{ L"Light 2 enable",       GE_CMD_LIGHTENABLE2,            CMD_FMT_FLAG },
100 	{ L"Light 3 enable",       GE_CMD_LIGHTENABLE3,            CMD_FMT_FLAG },
101 	{ L"Depth clamp enable",   GE_CMD_DEPTHCLAMPENABLE,        CMD_FMT_FLAG },
102 	{ L"Cullface enable",      GE_CMD_CULLFACEENABLE,          CMD_FMT_FLAG },
103 	{ L"Texture map enable",   GE_CMD_TEXTUREMAPENABLE,        CMD_FMT_FLAG },
104 	{ L"Fog enable",           GE_CMD_FOGENABLE,               CMD_FMT_FLAG },
105 	{ L"Dither enable",        GE_CMD_DITHERENABLE,            CMD_FMT_FLAG },
106 	{ L"Alpha blend enable",   GE_CMD_ALPHABLENDENABLE,        CMD_FMT_FLAG },
107 	{ L"Alpha test enable",    GE_CMD_ALPHATESTENABLE,         CMD_FMT_FLAG },
108 	{ L"Depth test enable",    GE_CMD_ZTESTENABLE,             CMD_FMT_FLAG },
109 	{ L"Stencil test enable",  GE_CMD_STENCILTESTENABLE,       CMD_FMT_FLAG },
110 	{ L"Antialias enable",     GE_CMD_ANTIALIASENABLE,         CMD_FMT_FLAG },
111 	{ L"Patch cull enable",    GE_CMD_PATCHCULLENABLE,         CMD_FMT_FLAG },
112 	{ L"Color test enable",    GE_CMD_COLORTESTENABLE,         CMD_FMT_FLAG },
113 	{ L"Logic op enable",      GE_CMD_LOGICOPENABLE,           CMD_FMT_FLAG },
114 	{ L"Depth write disable",  GE_CMD_ZWRITEDISABLE,           CMD_FMT_FLAG },
115 };
116 
117 static const TabStateRow stateLightingRows[] = {
118 	{ L"Ambient color",        GE_CMD_AMBIENTCOLOR,            CMD_FMT_HEX },
119 	{ L"Ambient alpha",        GE_CMD_AMBIENTALPHA,            CMD_FMT_HEX },
120 	{ L"Material update",      GE_CMD_MATERIALUPDATE,          CMD_FMT_MATERIALUPDATE },
121 	{ L"Material emissive",    GE_CMD_MATERIALEMISSIVE,        CMD_FMT_HEX },
122 	{ L"Material ambient",     GE_CMD_MATERIALAMBIENT,         CMD_FMT_HEX },
123 	{ L"Material diffuse",     GE_CMD_MATERIALDIFFUSE,         CMD_FMT_HEX },
124 	{ L"Material alpha",       GE_CMD_MATERIALALPHA,           CMD_FMT_HEX },
125 	{ L"Material specular",    GE_CMD_MATERIALSPECULAR,        CMD_FMT_HEX },
126 	{ L"Mat. specular coef",   GE_CMD_MATERIALSPECULARCOEF,    CMD_FMT_FLOAT24 },
127 	{ L"Reverse normals",      GE_CMD_REVERSENORMAL,           CMD_FMT_FLAG },
128 	{ L"Shade model",          GE_CMD_SHADEMODE,               CMD_FMT_SHADEMODEL },
129 	{ L"Light mode",           GE_CMD_LIGHTMODE,               CMD_FMT_LIGHTMODE, GE_CMD_LIGHTINGENABLE },
130 	{ L"Light type 0",         GE_CMD_LIGHTTYPE0,              CMD_FMT_LIGHTTYPE, GE_CMD_LIGHTENABLE0 },
131 	{ L"Light type 1",         GE_CMD_LIGHTTYPE1,              CMD_FMT_LIGHTTYPE, GE_CMD_LIGHTENABLE1 },
132 	{ L"Light type 2",         GE_CMD_LIGHTTYPE2,              CMD_FMT_LIGHTTYPE, GE_CMD_LIGHTENABLE2 },
133 	{ L"Light type 3",         GE_CMD_LIGHTTYPE3,              CMD_FMT_LIGHTTYPE, GE_CMD_LIGHTENABLE3 },
134 	{ L"Light pos 0",          GE_CMD_LX0,                     CMD_FMT_XYZ, GE_CMD_LIGHTENABLE0, GE_CMD_LY0, GE_CMD_LZ0 },
135 	{ L"Light pos 1",          GE_CMD_LX1,                     CMD_FMT_XYZ, GE_CMD_LIGHTENABLE1, GE_CMD_LY1, GE_CMD_LZ1 },
136 	{ L"Light pos 2",          GE_CMD_LX2,                     CMD_FMT_XYZ, GE_CMD_LIGHTENABLE2, GE_CMD_LY2, GE_CMD_LZ2 },
137 	{ L"Light pos 3",          GE_CMD_LX3,                     CMD_FMT_XYZ, GE_CMD_LIGHTENABLE3, GE_CMD_LY3, GE_CMD_LZ3 },
138 	{ L"Light dir 0",          GE_CMD_LDX0,                    CMD_FMT_XYZ, GE_CMD_LIGHTENABLE0, GE_CMD_LDY0, GE_CMD_LDZ0 },
139 	{ L"Light dir 1",          GE_CMD_LDX1,                    CMD_FMT_XYZ, GE_CMD_LIGHTENABLE1, GE_CMD_LDY1, GE_CMD_LDZ1 },
140 	{ L"Light dir 2",          GE_CMD_LDX2,                    CMD_FMT_XYZ, GE_CMD_LIGHTENABLE2, GE_CMD_LDY2, GE_CMD_LDZ2 },
141 	{ L"Light dir 3",          GE_CMD_LDX3,                    CMD_FMT_XYZ, GE_CMD_LIGHTENABLE3, GE_CMD_LDY3, GE_CMD_LDZ3 },
142 	// TODO: Is this a reasonable display format?
143 	{ L"Light att 0",          GE_CMD_LKA0,                    CMD_FMT_XYZ, GE_CMD_LIGHTENABLE0, GE_CMD_LKB0, GE_CMD_LKC0 },
144 	{ L"Light att 1",          GE_CMD_LKA1,                    CMD_FMT_XYZ, GE_CMD_LIGHTENABLE1, GE_CMD_LKB1, GE_CMD_LKC1 },
145 	{ L"Light att 2",          GE_CMD_LKA2,                    CMD_FMT_XYZ, GE_CMD_LIGHTENABLE2, GE_CMD_LKB2, GE_CMD_LKC2 },
146 	{ L"Light att 3",          GE_CMD_LKA3,                    CMD_FMT_XYZ, GE_CMD_LIGHTENABLE3, GE_CMD_LKB3, GE_CMD_LKC3 },
147 	{ L"Lightspot coef 0",     GE_CMD_LKS0,                    CMD_FMT_FLOAT24, GE_CMD_LIGHTENABLE0 },
148 	{ L"Lightspot coef 1",     GE_CMD_LKS1,                    CMD_FMT_FLOAT24, GE_CMD_LIGHTENABLE1 },
149 	{ L"Lightspot coef 2",     GE_CMD_LKS2,                    CMD_FMT_FLOAT24, GE_CMD_LIGHTENABLE2 },
150 	{ L"Lightspot coef 3",     GE_CMD_LKS3,                    CMD_FMT_FLOAT24, GE_CMD_LIGHTENABLE3 },
151 	{ L"Light angle 0",        GE_CMD_LKO0,                    CMD_FMT_FLOAT24, GE_CMD_LIGHTENABLE0 },
152 	{ L"Light angle 1",        GE_CMD_LKO1,                    CMD_FMT_FLOAT24, GE_CMD_LIGHTENABLE1 },
153 	{ L"Light angle 2",        GE_CMD_LKO2,                    CMD_FMT_FLOAT24, GE_CMD_LIGHTENABLE2 },
154 	{ L"Light angle 3",        GE_CMD_LKO3,                    CMD_FMT_FLOAT24, GE_CMD_LIGHTENABLE3 },
155 	{ L"Light ambient 0",      GE_CMD_LAC0,                    CMD_FMT_HEX, GE_CMD_LIGHTENABLE0 },
156 	{ L"Light diffuse 0",      GE_CMD_LDC0,                    CMD_FMT_HEX, GE_CMD_LIGHTENABLE0 },
157 	{ L"Light specular 0",     GE_CMD_LSC0,                    CMD_FMT_HEX, GE_CMD_LIGHTENABLE0 },
158 	{ L"Light ambient 1",      GE_CMD_LAC1,                    CMD_FMT_HEX, GE_CMD_LIGHTENABLE1 },
159 	{ L"Light diffuse 1",      GE_CMD_LDC1,                    CMD_FMT_HEX, GE_CMD_LIGHTENABLE1 },
160 	{ L"Light specular 1",     GE_CMD_LSC1,                    CMD_FMT_HEX, GE_CMD_LIGHTENABLE1 },
161 	{ L"Light ambient 2",      GE_CMD_LAC2,                    CMD_FMT_HEX, GE_CMD_LIGHTENABLE2 },
162 	{ L"Light diffuse 2",      GE_CMD_LDC2,                    CMD_FMT_HEX, GE_CMD_LIGHTENABLE2 },
163 	{ L"Light specular 2",     GE_CMD_LSC2,                    CMD_FMT_HEX, GE_CMD_LIGHTENABLE2 },
164 	{ L"Light ambient 3",      GE_CMD_LAC3,                    CMD_FMT_HEX, GE_CMD_LIGHTENABLE3 },
165 	{ L"Light diffuse 3",      GE_CMD_LDC3,                    CMD_FMT_HEX, GE_CMD_LIGHTENABLE3 },
166 	{ L"Light specular 3",     GE_CMD_LSC3,                    CMD_FMT_HEX, GE_CMD_LIGHTENABLE3 },
167 };
168 
169 static const TabStateRow stateTextureRows[] = {
170 	{ L"Tex U scale",          GE_CMD_TEXSCALEU,               CMD_FMT_FLOAT24, GE_CMD_TEXTUREMAPENABLE },
171 	{ L"Tex V scale",          GE_CMD_TEXSCALEV,               CMD_FMT_FLOAT24, GE_CMD_TEXTUREMAPENABLE },
172 	{ L"Tex U offset",         GE_CMD_TEXOFFSETU,              CMD_FMT_FLOAT24, GE_CMD_TEXTUREMAPENABLE },
173 	{ L"Tex V offset",         GE_CMD_TEXOFFSETV,              CMD_FMT_FLOAT24, GE_CMD_TEXTUREMAPENABLE },
174 	{ L"Tex mapping mode",     GE_CMD_TEXMAPMODE,              CMD_FMT_TEXMAPMODE, GE_CMD_TEXTUREMAPENABLE },
175 	{ L"Tex shade srcs",       GE_CMD_TEXSHADELS,              CMD_FMT_TEXSHADELS, GE_CMD_TEXTUREMAPENABLE },
176 	{ L"Tex mode",             GE_CMD_TEXMODE,                 CMD_FMT_TEXMODE, GE_CMD_TEXTUREMAPENABLE },
177 	{ L"Tex format",           GE_CMD_TEXFORMAT,               CMD_FMT_TEXFMT, GE_CMD_TEXTUREMAPENABLE },
178 	{ L"Tex filtering",        GE_CMD_TEXFILTER,               CMD_FMT_TEXFILTER, GE_CMD_TEXTUREMAPENABLE },
179 	{ L"Tex wrapping",         GE_CMD_TEXWRAP,                 CMD_FMT_TEXWRAP, GE_CMD_TEXTUREMAPENABLE },
180 	{ L"Tex level/bias",       GE_CMD_TEXLEVEL,                CMD_FMT_TEXLEVEL, GE_CMD_TEXTUREMAPENABLE },
181 	{ L"Tex lod slope",        GE_CMD_TEXLODSLOPE,             CMD_FMT_FLOAT24, GE_CMD_TEXTUREMAPENABLE },
182 	{ L"Tex func",             GE_CMD_TEXFUNC,                 CMD_FMT_TEXFUNC, GE_CMD_TEXTUREMAPENABLE },
183 	{ L"Tex env color",        GE_CMD_TEXENVCOLOR,             CMD_FMT_HEX, GE_CMD_TEXTUREMAPENABLE },
184 	{ L"CLUT",                 GE_CMD_CLUTADDR,                CMD_FMT_PTRWIDTH, GE_CMD_TEXTUREMAPENABLE, GE_CMD_CLUTADDRUPPER },
185 	{ L"CLUT format",          GE_CMD_CLUTFORMAT,              CMD_FMT_CLUTFMT, GE_CMD_TEXTUREMAPENABLE },
186 	{ L"Texture L0 addr",      GE_CMD_TEXADDR0,                CMD_FMT_PTRWIDTH, GE_CMD_TEXTUREMAPENABLE, GE_CMD_TEXBUFWIDTH0 },
187 	{ L"Texture L1 addr",      GE_CMD_TEXADDR1,                CMD_FMT_PTRWIDTH, GE_CMD_TEXTUREMAPENABLE, GE_CMD_TEXBUFWIDTH1 },
188 	{ L"Texture L2 addr",      GE_CMD_TEXADDR2,                CMD_FMT_PTRWIDTH, GE_CMD_TEXTUREMAPENABLE, GE_CMD_TEXBUFWIDTH2 },
189 	{ L"Texture L3 addr",      GE_CMD_TEXADDR3,                CMD_FMT_PTRWIDTH, GE_CMD_TEXTUREMAPENABLE, GE_CMD_TEXBUFWIDTH3 },
190 	{ L"Texture L4 addr",      GE_CMD_TEXADDR4,                CMD_FMT_PTRWIDTH, GE_CMD_TEXTUREMAPENABLE, GE_CMD_TEXBUFWIDTH4 },
191 	{ L"Texture L5 addr",      GE_CMD_TEXADDR5,                CMD_FMT_PTRWIDTH, GE_CMD_TEXTUREMAPENABLE, GE_CMD_TEXBUFWIDTH5 },
192 	{ L"Texture L6 addr",      GE_CMD_TEXADDR6,                CMD_FMT_PTRWIDTH, GE_CMD_TEXTUREMAPENABLE, GE_CMD_TEXBUFWIDTH6 },
193 	{ L"Texture L7 addr",      GE_CMD_TEXADDR7,                CMD_FMT_PTRWIDTH, GE_CMD_TEXTUREMAPENABLE, GE_CMD_TEXBUFWIDTH7 },
194 	{ L"Texture L0 size",      GE_CMD_TEXSIZE0,                CMD_FMT_TEXSIZE, GE_CMD_TEXTUREMAPENABLE },
195 	{ L"Texture L1 size",      GE_CMD_TEXSIZE1,                CMD_FMT_TEXSIZE, GE_CMD_TEXTUREMAPENABLE },
196 	{ L"Texture L2 size",      GE_CMD_TEXSIZE2,                CMD_FMT_TEXSIZE, GE_CMD_TEXTUREMAPENABLE },
197 	{ L"Texture L3 size",      GE_CMD_TEXSIZE3,                CMD_FMT_TEXSIZE, GE_CMD_TEXTUREMAPENABLE },
198 	{ L"Texture L4 size",      GE_CMD_TEXSIZE4,                CMD_FMT_TEXSIZE, GE_CMD_TEXTUREMAPENABLE },
199 	{ L"Texture L5 size",      GE_CMD_TEXSIZE5,                CMD_FMT_TEXSIZE, GE_CMD_TEXTUREMAPENABLE },
200 	{ L"Texture L6 size",      GE_CMD_TEXSIZE6,                CMD_FMT_TEXSIZE, GE_CMD_TEXTUREMAPENABLE },
201 	{ L"Texture L7 size",      GE_CMD_TEXSIZE7,                CMD_FMT_TEXSIZE, GE_CMD_TEXTUREMAPENABLE },
202 };
203 
204 static const TabStateRow stateSettingsRows[] = {
205 	{ L"Clear mode",           GE_CMD_CLEARMODE,               CMD_FMT_CLEARMODE },
206 	{ L"Framebuffer",          GE_CMD_FRAMEBUFPTR,             CMD_FMT_PTRWIDTH, 0, GE_CMD_FRAMEBUFWIDTH },
207 	{ L"Framebuffer format",   GE_CMD_FRAMEBUFPIXFORMAT,       CMD_FMT_TEXFMT },
208 	{ L"Depthbuffer",          GE_CMD_ZBUFPTR,                 CMD_FMT_PTRWIDTH, 0, GE_CMD_ZBUFWIDTH },
209 	{ L"Vertex type",          GE_CMD_VERTEXTYPE,              CMD_FMT_VERTEXTYPE },
210 	{ L"Offset addr",          GE_CMD_OFFSETADDR,              CMD_FMT_OFFSETADDR },
211 	{ L"Vertex addr",          GE_CMD_VADDR,                   CMD_FMT_VADDR },
212 	{ L"Index addr",           GE_CMD_IADDR,                   CMD_FMT_IADDR },
213 	{ L"Region",               GE_CMD_REGION1,                 CMD_FMT_XYXY, 0, GE_CMD_REGION2 },
214 	{ L"Scissor",              GE_CMD_SCISSOR1,                CMD_FMT_XYXY, 0, GE_CMD_SCISSOR2 },
215 	{ L"Min Z",                GE_CMD_MINZ,                    CMD_FMT_HEX },
216 	{ L"Max Z",                GE_CMD_MAXZ,                    CMD_FMT_HEX },
217 	{ L"Viewport Scale",       GE_CMD_VIEWPORTXSCALE,          CMD_FMT_XYZ, 0, GE_CMD_VIEWPORTYSCALE, GE_CMD_VIEWPORTZSCALE },
218 	{ L"Viewport Offset",      GE_CMD_VIEWPORTXCENTER,         CMD_FMT_XYZ, 0, GE_CMD_VIEWPORTYCENTER, GE_CMD_VIEWPORTZCENTER },
219 	{ L"Offset",               GE_CMD_OFFSETX,                 CMD_FMT_F16_XY, 0, GE_CMD_OFFSETY },
220 	{ L"Cull mode",            GE_CMD_CULL,                    CMD_FMT_CULL, GE_CMD_CULLFACEENABLE },
221 	{ L"Color test",           GE_CMD_COLORTEST,               CMD_FMT_COLORTEST, GE_CMD_COLORTESTENABLE, GE_CMD_COLORREF, GE_CMD_COLORTESTMASK },
222 	{ L"Alpha test",           GE_CMD_ALPHATEST,               CMD_FMT_ALPHATEST, GE_CMD_ALPHATESTENABLE },
223 	{ L"Stencil test",         GE_CMD_STENCILTEST,             CMD_FMT_STENCILTEST, GE_CMD_STENCILTESTENABLE },
224 	{ L"Stencil test op",      GE_CMD_STENCILOP,               CMD_FMT_STENCILOP, GE_CMD_STENCILTESTENABLE },
225 	{ L"Depth test",           GE_CMD_ZTEST,                   CMD_FMT_ZTEST, GE_CMD_ZTESTENABLE },
226 	{ L"Alpha blend mode",     GE_CMD_BLENDMODE,               CMD_FMT_BLENDMODE, GE_CMD_ALPHABLENDENABLE },
227 	{ L"Blend color A",        GE_CMD_BLENDFIXEDA,             CMD_FMT_HEX, GE_CMD_ALPHABLENDENABLE },
228 	{ L"Blend color B",        GE_CMD_BLENDFIXEDB,             CMD_FMT_HEX, GE_CMD_ALPHABLENDENABLE },
229 	{ L"Logic Op",             GE_CMD_LOGICOP,                 CMD_FMT_LOGICOP, GE_CMD_LOGICOPENABLE },
230 	{ L"Fog 1",                GE_CMD_FOG1,                    CMD_FMT_FLOAT24, GE_CMD_FOGENABLE },
231 	{ L"Fog 2",                GE_CMD_FOG2,                    CMD_FMT_FLOAT24, GE_CMD_FOGENABLE },
232 	{ L"Fog color",            GE_CMD_FOGCOLOR,                CMD_FMT_HEX, GE_CMD_FOGENABLE },
233 	{ L"RGB mask",             GE_CMD_MASKRGB,                 CMD_FMT_HEX },
234 	{ L"Stencil/alpha mask",   GE_CMD_MASKALPHA,               CMD_FMT_HEX },
235 	{ L"Morph Weight 0",       GE_CMD_MORPHWEIGHT0,            CMD_FMT_FLOAT24 },
236 	{ L"Morph Weight 1",       GE_CMD_MORPHWEIGHT1,            CMD_FMT_FLOAT24 },
237 	{ L"Morph Weight 2",       GE_CMD_MORPHWEIGHT2,            CMD_FMT_FLOAT24 },
238 	{ L"Morph Weight 3",       GE_CMD_MORPHWEIGHT3,            CMD_FMT_FLOAT24 },
239 	{ L"Morph Weight 4",       GE_CMD_MORPHWEIGHT4,            CMD_FMT_FLOAT24 },
240 	{ L"Morph Weight 5",       GE_CMD_MORPHWEIGHT5,            CMD_FMT_FLOAT24 },
241 	{ L"Morph Weight 6",       GE_CMD_MORPHWEIGHT6,            CMD_FMT_FLOAT24 },
242 	{ L"Morph Weight 7",       GE_CMD_MORPHWEIGHT7,            CMD_FMT_FLOAT24 },
243 	// TODO: Format?
244 	{ L"Patch division",       GE_CMD_PATCHDIVISION,           CMD_FMT_HEX },
245 	{ L"Patch primitive",      GE_CMD_PATCHPRIMITIVE,          CMD_FMT_PATCHPRIMITIVE },
246 	// TODO: Format?
247 	{ L"Patch facing",         GE_CMD_PATCHFACING,             CMD_FMT_HEX, GE_CMD_PATCHCULLENABLE },
248 	{ L"Dither 0",             GE_CMD_DITH0,                   CMD_FMT_HEX, GE_CMD_DITHERENABLE },
249 	{ L"Dither 1",             GE_CMD_DITH1,                   CMD_FMT_HEX, GE_CMD_DITHERENABLE },
250 	{ L"Dither 2",             GE_CMD_DITH2,                   CMD_FMT_HEX, GE_CMD_DITHERENABLE },
251 	{ L"Dither 3",             GE_CMD_DITH3,                   CMD_FMT_HEX, GE_CMD_DITHERENABLE },
252 	{ L"Transfer src",         GE_CMD_TRANSFERSRC,             CMD_FMT_PTRWIDTH, 0, GE_CMD_TRANSFERSRCW },
253 	{ L"Transfer src pos",     GE_CMD_TRANSFERSRCPOS,          CMD_FMT_XY },
254 	{ L"Transfer dst",         GE_CMD_TRANSFERDST,             CMD_FMT_PTRWIDTH, 0, GE_CMD_TRANSFERDSTW },
255 	{ L"Transfer dst pos",     GE_CMD_TRANSFERDSTPOS,          CMD_FMT_XY },
256 	{ L"Transfer size",        GE_CMD_TRANSFERSIZE,            CMD_FMT_XY },
257 };
258 
259 // TODO: Commands not present in the above lists (some because they don't have meaningful values...):
260 //   GE_CMD_PRIM, GE_CMD_BEZIER, GE_CMD_SPLINE, GE_CMD_BOUNDINGBOX,
261 //   GE_CMD_JUMP, GE_CMD_BJUMP, GE_CMD_CALL, GE_CMD_RET, GE_CMD_END, GE_CMD_SIGNAL, GE_CMD_FINISH,
262 //   GE_CMD_BONEMATRIXNUMBER, GE_CMD_BONEMATRIXDATA, GE_CMD_WORLDMATRIXNUMBER, GE_CMD_WORLDMATRIXDATA,
263 //   GE_CMD_VIEWMATRIXNUMBER, GE_CMD_VIEWMATRIXDATA, GE_CMD_PROJMATRIXNUMBER, GE_CMD_PROJMATRIXDATA,
264 //   GE_CMD_TGENMATRIXNUMBER, GE_CMD_TGENMATRIXDATA,
265 //   GE_CMD_LOADCLUT, GE_CMD_TEXFLUSH, GE_CMD_TEXSYNC,
266 //   GE_CMD_TRANSFERSTART,
267 //   GE_CMD_UNKNOWN_*
268 
269 static std::vector<TabStateRow> watchList;
270 
ToggleWatchList(const TabStateRow & info)271 static void ToggleWatchList(const TabStateRow &info) {
272 	for (size_t i = 0; i < watchList.size(); ++i) {
273 		if (watchList[i].cmd == info.cmd) {
274 			watchList.erase(watchList.begin() + i);
275 			return;
276 		}
277 	}
278 
279 	watchList.push_back(info);
280 }
281 
CtrlStateValues(const TabStateRow * rows,int rowCount,HWND hwnd)282 CtrlStateValues::CtrlStateValues(const TabStateRow *rows, int rowCount, HWND hwnd)
283 	: GenericListControl(hwnd, stateValuesListDef),
284 	  rows_(rows), rowCount_(rowCount) {
285 	Update();
286 }
287 
FormatStateRow(wchar_t * dest,const TabStateRow & info,u32 value,bool enabled,u32 otherValue,u32 otherValue2)288 void FormatStateRow(wchar_t *dest, const TabStateRow &info, u32 value, bool enabled, u32 otherValue, u32 otherValue2) {
289 	switch (info.fmt) {
290 	case CMD_FMT_HEX:
291 		swprintf(dest, 255, L"%06x", value);
292 		break;
293 
294 	case CMD_FMT_NUM:
295 		swprintf(dest, 255, L"%d", value);
296 		break;
297 
298 	case CMD_FMT_FLOAT24:
299 		swprintf(dest, 255, L"%f", getFloat24(value));
300 		break;
301 
302 	case CMD_FMT_PTRWIDTH:
303 		value |= (otherValue & 0x00FF0000) << 8;
304 		otherValue &= 0xFFFF;
305 		swprintf(dest, 255, L"%08x, w=%d", value, otherValue);
306 		break;
307 
308 	case CMD_FMT_XY:
309 		{
310 			int x = value & 0x3FF;
311 			int y = value >> 10;
312 			swprintf(dest, 255, L"%d,%d", x, y);
313 		}
314 		break;
315 
316 	case CMD_FMT_XYXY:
317 		{
318 			int x1 = value & 0x3FF;
319 			int y1 = value >> 10;
320 			int x2 = otherValue & 0x3FF;
321 			int y2 = otherValue >> 10;
322 			swprintf(dest, 255, L"%d,%d - %d,%d", x1, y1, x2, y2);
323 		}
324 		break;
325 
326 	case CMD_FMT_XYZ:
327 		{
328 			float x = getFloat24(value);
329 			float y = getFloat24(otherValue);
330 			float z = getFloat24(otherValue2);
331 			swprintf(dest, 255, L"%f, %f, %f", x, y, z);
332 		}
333 		break;
334 
335 	case CMD_FMT_TEXSIZE:
336 		{
337 			int w = 1 << (value & 0x1f);
338 			int h = 1 << ((value >> 8) & 0x1f);
339 			swprintf(dest, 255, L"%dx%d", w, h);
340 		}
341 		break;
342 
343 	case CMD_FMT_F16_XY:
344 		{
345 			float x = (float)value / 16.0f;
346 			float y = (float)otherValue / 16.0f;
347 			swprintf(dest, 255, L"%fx%f", x, y);
348 		}
349 		break;
350 
351 	case CMD_FMT_VERTEXTYPE:
352 		{
353 			char buffer[256];
354 			GeDescribeVertexType(value, buffer);
355 			swprintf(dest, 255, L"%S", buffer);
356 		}
357 		break;
358 
359 	case CMD_FMT_TEXFMT:
360 		{
361 			static const char *texformats[] = {
362 				"5650",
363 				"5551",
364 				"4444",
365 				"8888",
366 				"CLUT4",
367 				"CLUT8",
368 				"CLUT16",
369 				"CLUT32",
370 				"DXT1",
371 				"DXT3",
372 				"DXT5",
373 			};
374 			if (value < (u32)ARRAY_SIZE(texformats)) {
375 				swprintf(dest, 255, L"%S", texformats[value]);
376 			}
377 			else if ((value & 0xF) < (u32)ARRAY_SIZE(texformats)) {
378 				swprintf(dest, 255, L"%S (extra bits %06x)", texformats[value & 0xF], value);
379 			}
380 			else {
381 				swprintf(dest, 255, L"%06x", value);
382 			}
383 		}
384 		break;
385 
386 	case CMD_FMT_CLUTFMT:
387 		{
388 			const char *clutformats[] = {
389 				"BGR 5650",
390 				"ABGR 1555",
391 				"ABGR 4444",
392 				"ABGR 8888",
393 			};
394 			const u8 palette = (value >> 0) & 3;
395 			const u8 shift = (value >> 2) & 0x3F;
396 			const u8 mask = (value >> 8) & 0xFF;
397 			const u8 offset = (value >> 16) & 0xFF;
398 			if (offset < 0x20 && shift < 0x20) {
399 				if (offset == 0 && shift == 0) {
400 					swprintf(dest, 255, L"%S ind & %02x", clutformats[palette], mask);
401 				} else {
402 					swprintf(dest, 255, L"%S (ind >> %d) & %02x, offset +%d", clutformats[palette], shift, mask, offset);
403 				}
404 			} else {
405 				swprintf(dest, 255, L"%06x", value);
406 			}
407 		}
408 		break;
409 
410 	case CMD_FMT_COLORTEST:
411 		{
412 			static const char *colorTests[] = {"NEVER", "ALWAYS", " == ", " != "};
413 			const u32 mask = otherValue2;
414 			const u32 ref = otherValue;
415 			if (value < (u32)ARRAY_SIZE(colorTests)) {
416 				swprintf(dest, 255, L"pass if (c & %06x) %S (%06x & %06x)", mask, colorTests[value], ref, mask);
417 			} else {
418 				swprintf(dest, 255, L"%06x, ref=%06x, maks=%06x", value, ref, mask);
419 			}
420 		}
421 		break;
422 
423 	case CMD_FMT_ALPHATEST:
424 	case CMD_FMT_STENCILTEST:
425 		{
426 			static const char *alphaTestFuncs[] = { "NEVER", "ALWAYS", "==", "!=", "<", "<=", ">", ">=" };
427 			const u8 mask = (value >> 16) & 0xff;
428 			const u8 ref = (value >> 8) & 0xff;
429 			const u8 func = (value >> 0) & 0xff;
430 			if (func < (u8)ARRAY_SIZE(alphaTestFuncs)) {
431 				if (info.fmt == CMD_FMT_ALPHATEST) {
432 					swprintf(dest, 255, L"pass if (a & %02x) %S (%02x & %02x)", mask, alphaTestFuncs[func], ref, mask);
433 				} else if (info.fmt == CMD_FMT_STENCILTEST) {
434 					// Stencil test is the other way around.
435 					swprintf(dest, 255, L"pass if (%02x & %02x) %S (a & %02x)", ref, mask, alphaTestFuncs[func], mask);
436 				}
437 			} else {
438 				swprintf(dest, 255, L"%06x", value);
439 			}
440 		}
441 		break;
442 
443 	case CMD_FMT_ZTEST:
444 		{
445 			static const char *zTestFuncs[] = { "NEVER", "ALWAYS", "==", "!=", "<", "<=", ">", ">=" };
446 			if (value < (u32)ARRAY_SIZE(zTestFuncs)) {
447 				swprintf(dest, 255, L"pass if src %S dst", zTestFuncs[value]);
448 			} else {
449 				swprintf(dest, 255, L"%06x", value);
450 			}
451 		}
452 		break;
453 
454 	case CMD_FMT_OFFSETADDR:
455 		swprintf(dest, 255, L"%08x", gpuDebug->GetRelativeAddress(0));
456 		break;
457 
458 	case CMD_FMT_VADDR:
459 		swprintf(dest, 255, L"%08x", gpuDebug->GetVertexAddress());
460 		break;
461 
462 	case CMD_FMT_IADDR:
463 		swprintf(dest, 255, L"%08x", gpuDebug->GetIndexAddress());
464 		break;
465 
466 	case CMD_FMT_MATERIALUPDATE:
467 		{
468 			static const char *materialTypes[] = {
469 				"none",
470 				"ambient",
471 				"diffuse",
472 				"ambient, diffuse",
473 				"specular",
474 				"ambient, specular",
475 				"diffuse, specular",
476 				"ambient, diffuse, specular",
477 			};
478 			if (value < (u32)ARRAY_SIZE(materialTypes)) {
479 				swprintf(dest, 255, L"%S", materialTypes[value]);
480 			} else {
481 				swprintf(dest, 255, L"%06x", value);
482 			}
483 		}
484 		break;
485 
486 	case CMD_FMT_SHADEMODEL:
487 		if (value == 0) {
488 			swprintf(dest, 255, L"flat");
489 		} else if (value == 1) {
490 			swprintf(dest, 255, L"gouraud");
491 		} else {
492 			swprintf(dest, 255, L"%06x", value);
493 		}
494 		break;
495 
496 	case CMD_FMT_STENCILOP:
497 		{
498 			static const char *stencilOps[] = { "KEEP", "ZERO", "REPLACE", "INVERT", "INCREMENT", "DECREMENT" };
499 			const u8 sfail = (value >> 0) & 0xFF;
500 			const u8 zfail = (value >> 8) & 0xFF;
501 			const u8 pass = (value >> 16) & 0xFF;
502 			const u8 totalValid = (u8)ARRAY_SIZE(stencilOps);
503 			if (sfail < totalValid && zfail < totalValid && pass < totalValid) {
504 				swprintf(dest, 255, L"fail=%S, pass/depthfail=%S, pass=%S", stencilOps[sfail], stencilOps[zfail], stencilOps[pass]);
505 			} else {
506 				swprintf(dest, 255, L"%06x", value);
507 			}
508 		}
509 		break;
510 
511 	case CMD_FMT_BLENDMODE:
512 		{
513 			const char *blendModes[] = {
514 				"add",
515 				"subtract",
516 				"reverse subtract",
517 				"min",
518 				"max",
519 				"abs subtract",
520 			};
521 			const char *blendFactorsA[] = {
522 				"dst",
523 				"1.0 - dst",
524 				"src.a",
525 				"1.0 - src.a",
526 				"dst.a",
527 				"1.0 - dst.a",
528 				"2.0 * src.a",
529 				"1.0 - 2.0 * src.a",
530 				"2.0 * dst.a",
531 				"1.0 - 2.0 * dst.a",
532 				"fixed",
533 			};
534 			const char *blendFactorsB[] = {
535 				"src",
536 				"1.0 - src",
537 				"src.a",
538 				"1.0 - src.a",
539 				"dst.a",
540 				"1.0 - dst.a",
541 				"2.0 * src.a",
542 				"1.0 - 2.0 * src.a",
543 				"2.0 * dst.a",
544 				"1.0 - 2.0 * dst.a",
545 				"fixed",
546 			};
547 			const u8 blendFactorA = (value >> 0) & 0xF;
548 			const u8 blendFactorB = (value >> 4) & 0xF;
549 			const u32 blendMode = (value >> 8);
550 
551 			if (blendFactorA < (u8)ARRAY_SIZE(blendFactorsA) && blendFactorB < (u8)ARRAY_SIZE(blendFactorsB) && blendMode < (u32)ARRAY_SIZE(blendModes)) {
552 				swprintf(dest, 255, L"%S: %S, %S", blendModes[blendMode], blendFactorsA[blendFactorA], blendFactorsB[blendFactorB]);
553 			} else {
554 				swprintf(dest, 255, L"%06x", value);
555 			}
556 		}
557 		break;
558 
559 	case CMD_FMT_CLEARMODE:
560 		if (value == 0) {
561 			swprintf(dest, 255, L"%d", value);
562 		} else if ((value & ~(GE_CLEARMODE_ALL | 1)) == 0) {
563 			const char *clearmodes[] = {
564 				"1, write disabled",
565 				"1, write color",
566 				"1, write alpha/stencil",
567 				"1, write color, alpha/stencil",
568 				"1, write depth",
569 				"1, write color, depth",
570 				"1, write alpha/stencil, depth",
571 				"1, write color, alpha/stencil, depth",
572 			};
573 			swprintf(dest, 255, L"%S", clearmodes[value >> 8]);
574 		} else {
575 			swprintf(dest, 255, L"%06x", value);
576 		}
577 		break;
578 
579 	case CMD_FMT_TEXFUNC:
580 		{
581 			const char *texfuncs[] = {
582 				"modulate",
583 				"decal",
584 				"blend",
585 				"replace",
586 				"add",
587 			};
588 			const u8 func = (value >> 0) & 0xFF;
589 			const u8 rgba = (value >> 8) & 0xFF;
590 			const u8 colorDouble = (value >> 16) & 0xFF;
591 
592 			if (rgba <= 1 && colorDouble <= 1 && func < (u8)ARRAY_SIZE(texfuncs)) {
593 				swprintf(dest, 255, L"%S, %S%S", texfuncs[func], rgba ? "RGBA" : "RGB", colorDouble ? ", color doubling" : "");
594 			} else {
595 				swprintf(dest, 255, L"%06x", value);
596 			}
597 		}
598 		break;
599 
600 	case CMD_FMT_TEXMODE:
601 		{
602 			const u8 swizzle = (value >> 0) & 0xFF;
603 			const u8 clutLevels = (value >> 8) & 0xFF;
604 			const u8 maxLevel = (value >> 16) & 0xFF;
605 
606 			if (swizzle <= 1 && clutLevels <= 1 && maxLevel <= 7) {
607 				swprintf(dest, 255, L"%S%d levels%S", swizzle ? "swizzled, " : "", maxLevel + 1, clutLevels ? ", CLUT per level" : "");
608 			} else {
609 				swprintf(dest, 255, L"%06x", value);
610 			}
611 		}
612 		break;
613 
614 	case CMD_FMT_LOGICOP:
615 		{
616 			const char *logicOps[] = {
617 				"clear",
618 				"and",
619 				"reverse and",
620 				"copy",
621 				"inverted and",
622 				"noop",
623 				"xor",
624 				"or",
625 				"negated or",
626 				"equivalence",
627 				"inverted",
628 				"reverse or",
629 				"inverted copy",
630 				"inverted or",
631 				"negated and",
632 				"set",
633 			};
634 
635 			if (value < ARRAY_SIZE(logicOps)) {
636 				swprintf(dest, 255, L"%S", logicOps[value]);
637 			} else {
638 				swprintf(dest, 255, L"%06x", value);
639 			}
640 		}
641 		break;
642 
643 	case CMD_FMT_TEXWRAP:
644 		{
645 			if ((value & ~0x0101) == 0) {
646 				const bool clampS = (value & 0x0001) != 0;
647 				const bool clampT = (value & 0x0100) != 0;
648 				swprintf(dest, 255, L"%S s, %S t", clampS ? "clamp" : "wrap", clampT ? "clamp" : "wrap");
649 			} else {
650 				swprintf(dest, 255, L"%06x", value);
651 			}
652 		}
653 		break;
654 
655 	case CMD_FMT_TEXLEVEL:
656 		{
657 			const char *mipLevelModes[] = {
658 				"auto + bias",
659 				"bias",
660 				"slope + bias",
661 			};
662 			const int mipLevel = value & 0xFFFF;
663 			const int biasFixed = (s8)(value >> 16);
664 			const float bias = (float)biasFixed / 16.0f;
665 
666 			if (mipLevel == 0 || mipLevel == 1 || mipLevel == 2) {
667 				swprintf(dest, 255, L"%S: %f", mipLevelModes[mipLevel], bias);
668 			} else {
669 				swprintf(dest, 255, L"%06x", value);
670 			}
671 		}
672 		break;
673 
674 	case CMD_FMT_TEXFILTER:
675 		{
676 			const char *textureFilters[] = {
677 				"nearest",
678 				"linear",
679 				NULL,
680 				NULL,
681 				"nearest, mipmap nearest",
682 				"linear, mipmap nearest",
683 				"nearest, mipmap linear",
684 				"linear, mipmap linear",
685 			};
686 			if ((value & ~0x0107) == 0 && textureFilters[value & 7] != NULL) {
687 				const int min = (value & 0x0007) >> 0;
688 				const int mag = (value & 0x0100) >> 8;
689 				swprintf(dest, 255, L"min: %S, mag: %S", textureFilters[min], textureFilters[mag]);
690 			} else {
691 				swprintf(dest, 255, L"%06x", value);
692 			}
693 		}
694 		break;
695 
696 	case CMD_FMT_TEXMAPMODE:
697 		{
698 			const char *uvGenModes[] = {
699 				"tex coords",
700 				"tex matrix",
701 				"tex env map",
702 				"unknown (tex coords?)",
703 			};
704 			const char *uvProjModes[] = {
705 				"pos",
706 				"uv",
707 				"normalized normal",
708 				"normal",
709 			};
710 			if ((value & ~0x0303) == 0) {
711 				const int uvGen = (value & 0x0003) >> 0;
712 				const int uvProj = (value & 0x0300) >> 8;
713 				swprintf(dest, 255, L"gen: %S, proj: %S", uvGenModes[uvGen], uvProjModes[uvProj]);
714 			} else {
715 				swprintf(dest, 255, L"%06x", value);
716 			}
717 		}
718 		break;
719 
720 	case CMD_FMT_TEXSHADELS:
721 		if ((value & ~0x0303) == 0) {
722 			const int sLight = (value & 0x0003) >> 0;
723 			const int tLight = (value & 0x0300) >> 8;
724 			swprintf(dest, 255, L"s: %d, t: %d", sLight, tLight);
725 		} else {
726 			swprintf(dest, 255, L"%06x", value);
727 		}
728 		break;
729 
730 	case CMD_FMT_LIGHTMODE:
731 		if (value == 0) {
732 			swprintf(dest, 255, L"mixed color");
733 		} else if (value == 1) {
734 			swprintf(dest, 255, L"separate specular");
735 		} else {
736 			swprintf(dest, 255, L"%06x", value);
737 		}
738 		break;
739 
740 	case CMD_FMT_LIGHTTYPE:
741 		{
742 			const char *lightComputations[] = {
743 				"diffuse",
744 				"diffuse + spec",
745 				"pow(diffuse)",
746 				"unknown (diffuse?)",
747 			};
748 			const char *lightTypes[] = {
749 				"directional",
750 				"point",
751 				"spot",
752 				"unknown (directional?)",
753 			};
754 			if ((value & ~0x0303) == 0) {
755 				const int comp = (value & 0x0003) >> 0;
756 				const int type = (value & 0x0300) >> 8;
757 				swprintf(dest, 255, L"type: %S, comp: %S", lightTypes[type], lightComputations[comp]);
758 			} else {
759 				swprintf(dest, 255, L"%06x", value);
760 			}
761 		}
762 		break;
763 
764 	case CMD_FMT_CULL:
765 		if (value == 0) {
766 			swprintf(dest, 255, L"front (CW)");
767 		} else if (value == 1) {
768 			swprintf(dest, 255, L"back (CCW)");
769 		} else {
770 			swprintf(dest, 255, L"%06x", value);
771 		}
772 		break;
773 
774 	case CMD_FMT_PATCHPRIMITIVE:
775 		{
776 			const char *patchPrims[] = {
777 				"triangles",
778 				"lines",
779 				"points",
780 			};
781 			if (value < (u32)ARRAY_SIZE(patchPrims)) {
782 				swprintf(dest, 255, L"%S", patchPrims[value]);
783 			} else {
784 				swprintf(dest, 255, L"%06x", value);
785 			}
786 		}
787 		break;
788 
789 	case CMD_FMT_FLAG:
790 		if ((value & ~1) == 0) {
791 			swprintf(dest, 255, L"%d", value);
792 		} else {
793 			swprintf(dest, 255, L"%06x", value);
794 		}
795 		break;
796 
797 	default:
798 		swprintf(dest, 255, L"BAD FORMAT %06x", value);
799 	}
800 
801 	// TODO: Turn row grey or some such?
802 	if (!enabled) {
803 		wcscat(dest, L" (disabled)");
804 	}
805 }
806 
GetColumnText(wchar_t * dest,int row,int col)807 void CtrlStateValues::GetColumnText(wchar_t *dest, int row, int col) {
808 	if (row < 0 || row >= rowCount_) {
809 		return;
810 	}
811 
812 	switch (col) {
813 	case STATEVALUES_COL_NAME:
814 		wcscpy(dest, rows_[row].title);
815 		break;
816 
817 	case STATEVALUES_COL_VALUE:
818 		{
819 			if (gpuDebug == NULL) {
820 				wcscpy(dest, L"N/A");
821 				break;
822 			}
823 
824 			const auto info = rows_[row];
825 			const auto state = gpuDebug->GetGState();
826 			const bool enabled = info.enableCmd == 0 || (state.cmdmem[info.enableCmd] & 1) == 1;
827 			const u32 value = state.cmdmem[info.cmd] & 0xFFFFFF;
828 			const u32 otherValue = state.cmdmem[info.otherCmd] & 0xFFFFFF;
829 			const u32 otherValue2 = state.cmdmem[info.otherCmd2] & 0xFFFFFF;
830 
831 			FormatStateRow(dest, info, value, enabled, otherValue, otherValue2);
832 			break;
833 		}
834 	}
835 }
836 
OnDoubleClick(int row,int column)837 void CtrlStateValues::OnDoubleClick(int row, int column) {
838 	if (gpuDebug == NULL) {
839 		return;
840 	}
841 
842 	const auto info = rows_[row];
843 	switch (info.fmt) {
844 	case CMD_FMT_FLAG:
845 		{
846 			const auto state = gpuDebug->GetGState();
847 			u32 newValue = state.cmdmem[info.cmd] ^ 1;
848 			SetCmdValue(newValue);
849 		}
850 		break;
851 
852 	default:
853 		{
854 			// TODO: Floats/etc., and things with multiple cmds.
855 			const auto state = gpuDebug->GetGState();
856 			u32 newValue = state.cmdmem[info.cmd] & 0x00FFFFFF;
857 			if (InputBox_GetHex(GetModuleHandle(NULL), GetHandle(), L"New value", newValue, newValue)) {
858 				newValue |= state.cmdmem[info.cmd] & 0xFF000000;
859 				SetCmdValue(newValue);
860 			}
861 		}
862 		break;
863 	}
864 }
865 
OnRightClick(int row,int column,const POINT & point)866 void CtrlStateValues::OnRightClick(int row, int column, const POINT &point) {
867 	if (gpuDebug == nullptr) {
868 		return;
869 	}
870 
871 	const auto info = rows_[row];
872 	const auto state = gpuDebug->GetGState();
873 
874 	POINT screenPt(point);
875 	ClientToScreen(GetHandle(), &screenPt);
876 
877 	HMENU subMenu = GetContextMenu(ContextMenuID::GEDBG_STATE);
878 	SetMenuDefaultItem(subMenu, ID_REGLIST_CHANGE, FALSE);
879 
880 	// Ehh, kinda ugly.
881 	if (!watchList.empty() && rows_ == &watchList[0]) {
882 		ModifyMenu(subMenu, ID_GEDBG_WATCH, MF_BYCOMMAND | MF_STRING, ID_GEDBG_WATCH, L"Remove Watch");
883 	} else {
884 		ModifyMenu(subMenu, ID_GEDBG_WATCH, MF_BYCOMMAND | MF_STRING, ID_GEDBG_WATCH, L"Add Watch");
885 	}
886 
887 	switch (TriggerContextMenu(ContextMenuID::GEDBG_STATE, GetHandle(), ContextPoint::FromClient(point)))
888 	{
889 	case ID_DISASM_TOGGLEBREAKPOINT:
890 		if (IsCmdBreakpoint(info.cmd)) {
891 			RemoveCmdBreakpoint(info.cmd);
892 			RemoveCmdBreakpoint(info.otherCmd);
893 			RemoveCmdBreakpoint(info.otherCmd2);
894 		} else {
895 			AddCmdBreakpoint(info.cmd);
896 			if (info.otherCmd) {
897 				AddCmdBreakpoint(info.otherCmd);
898 			}
899 			if (info.otherCmd2) {
900 				AddCmdBreakpoint(info.otherCmd2);
901 			}
902 		}
903 		break;
904 
905 	case ID_DISASM_COPYINSTRUCTIONHEX: {
906 		char temp[16];
907 		snprintf(temp, sizeof(temp), "%08x", gstate.cmdmem[info.cmd] & 0x00FFFFFF);
908 		W32Util::CopyTextToClipboard(GetHandle(), temp);
909 		break;
910 	}
911 
912 	case ID_DISASM_COPYINSTRUCTIONDISASM: {
913 		const bool enabled = info.enableCmd == 0 || (state.cmdmem[info.enableCmd] & 1) == 1;
914 		const u32 value = state.cmdmem[info.cmd] & 0xFFFFFF;
915 		const u32 otherValue = state.cmdmem[info.otherCmd] & 0xFFFFFF;
916 		const u32 otherValue2 = state.cmdmem[info.otherCmd2] & 0xFFFFFF;
917 
918 		wchar_t dest[512];
919 		FormatStateRow(dest, info, value, enabled, otherValue, otherValue2);
920 		W32Util::CopyTextToClipboard(GetHandle(), dest);
921 		break;
922 	}
923 
924 	case ID_GEDBG_COPYALL:
925 		CopyRows(0, GetRowCount());
926 		break;
927 
928 	case ID_REGLIST_CHANGE:
929 		OnDoubleClick(row, column);
930 		break;
931 
932 	case ID_GEDBG_WATCH:
933 		ToggleWatchList(info);
934 		SendMessage(GetParent(GetParent(GetHandle())), WM_GEDBG_UPDATE_WATCH, 0, 0);
935 		break;
936 	}
937 }
938 
SetCmdValue(u32 op)939 void CtrlStateValues::SetCmdValue(u32 op) {
940 	SendMessage(GetParent(GetParent(GetHandle())), WM_GEDBG_SETCMDWPARAM, op, NULL);
941 	Update();
942 }
943 
TabStateValues(const TabStateRow * rows,int rowCount,LPCSTR dialogID,HINSTANCE _hInstance,HWND _hParent)944 TabStateValues::TabStateValues(const TabStateRow *rows, int rowCount, LPCSTR dialogID, HINSTANCE _hInstance, HWND _hParent)
945 	: Dialog(dialogID, _hInstance, _hParent) {
946 	values = new CtrlStateValues(rows, rowCount, GetDlgItem(m_hDlg, IDC_GEDBG_VALUES));
947 }
948 
~TabStateValues()949 TabStateValues::~TabStateValues() {
950 	delete values;
951 }
952 
UpdateSize(WORD width,WORD height)953 void TabStateValues::UpdateSize(WORD width, WORD height) {
954 	struct Position {
955 		int x,y;
956 		int w,h;
957 	};
958 
959 	Position position;
960 	static const int borderMargin = 5;
961 
962 	position.x = borderMargin;
963 	position.y = borderMargin;
964 	position.w = width - 2 * borderMargin;
965 	position.h = height - 2 * borderMargin;
966 
967 	HWND handle = GetDlgItem(m_hDlg,IDC_GEDBG_VALUES);
968 	MoveWindow(handle, position.x, position.y, position.w, position.h, TRUE);
969 }
970 
DlgProc(UINT message,WPARAM wParam,LPARAM lParam)971 BOOL TabStateValues::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
972 	switch (message) {
973 	case WM_INITDIALOG:
974 		return TRUE;
975 
976 	case WM_SIZE:
977 		UpdateSize(LOWORD(lParam), HIWORD(lParam));
978 		return TRUE;
979 
980 	case WM_NOTIFY:
981 		switch (wParam)
982 		{
983 		case IDC_GEDBG_VALUES:
984 			values->HandleNotify(lParam);
985 			break;
986 		}
987 		break;
988 	}
989 
990 	return FALSE;
991 }
992 
TabStateFlags(HINSTANCE _hInstance,HWND _hParent)993 TabStateFlags::TabStateFlags(HINSTANCE _hInstance, HWND _hParent)
994 	: TabStateValues(stateFlagsRows, ARRAY_SIZE(stateFlagsRows), (LPCSTR)IDD_GEDBG_TAB_VALUES, _hInstance, _hParent) {
995 }
996 
TabStateLighting(HINSTANCE _hInstance,HWND _hParent)997 TabStateLighting::TabStateLighting(HINSTANCE _hInstance, HWND _hParent)
998 	: TabStateValues(stateLightingRows, ARRAY_SIZE(stateLightingRows), (LPCSTR)IDD_GEDBG_TAB_VALUES, _hInstance, _hParent) {
999 }
1000 
TabStateSettings(HINSTANCE _hInstance,HWND _hParent)1001 TabStateSettings::TabStateSettings(HINSTANCE _hInstance, HWND _hParent)
1002 	: TabStateValues(stateSettingsRows, ARRAY_SIZE(stateSettingsRows), (LPCSTR)IDD_GEDBG_TAB_VALUES, _hInstance, _hParent) {
1003 }
1004 
TabStateTexture(HINSTANCE _hInstance,HWND _hParent)1005 TabStateTexture::TabStateTexture(HINSTANCE _hInstance, HWND _hParent)
1006 	: TabStateValues(stateTextureRows, ARRAY_SIZE(stateTextureRows), (LPCSTR)IDD_GEDBG_TAB_VALUES, _hInstance, _hParent) {
1007 }
1008 
TabStateWatch(HINSTANCE _hInstance,HWND _hParent)1009 TabStateWatch::TabStateWatch(HINSTANCE _hInstance, HWND _hParent)
1010 	: TabStateValues(nullptr, 0, (LPCSTR)IDD_GEDBG_TAB_VALUES, _hInstance, _hParent) {
1011 }
1012 
Update()1013 void TabStateWatch::Update() {
1014 	if (watchList.empty()) {
1015 		values->UpdateRows(nullptr, 0);
1016 	} else {
1017 		values->UpdateRows(&watchList[0], (int)watchList.size());
1018 	}
1019 	TabStateValues::Update();
1020 }
1021