Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/UI/ImDebugger/ImDebugger.cpp
4779 views
1
#include <algorithm>
2
3
4
#include "ext/imgui/imgui_internal.h"
5
#include "ext/imgui/imgui_extras.h"
6
7
#include "Common/StringUtils.h"
8
#include "Common/File/FileUtil.h"
9
#include "Common/Data/Format/IniFile.h"
10
#include "Common/Data/Text/Parsers.h"
11
#include "Common/Log/LogManager.h"
12
#include "Common/TimeUtil.h"
13
#include "Core/Config.h"
14
#include "Core/System.h"
15
#include "Core/SaveState.h"
16
#include "Core/Debugger/MemBlockInfo.h"
17
#include "Core/RetroAchievements.h"
18
#include "Core/Core.h"
19
#include "Core/Debugger/DebugInterface.h"
20
#include "Core/Debugger/DisassemblyManager.h"
21
#include "Core/Debugger/Breakpoints.h"
22
#include "Core/MIPS/MIPSDebugInterface.h"
23
#include "Core/MIPS/MIPSTables.h"
24
#include "Core/HW/SimpleAudioDec.h"
25
#include "Core/FileSystems/MetaFileSystem.h"
26
#include "Core/Debugger/SymbolMap.h"
27
#include "Core/MemMap.h"
28
#include "Core/HLE/HLE.h"
29
#include "Core/HLE/SocketManager.h"
30
#include "Core/HLE/NetInetConstants.h"
31
#include "Core/HLE/sceKernelModule.h"
32
#include "Core/HLE/sceAac.h"
33
#include "Core/HLE/sceMpeg.h"
34
#include "Core/HLE/sceNp.h"
35
#include "Core/HLE/sceNet.h"
36
#include "Core/HLE/sceNetApctl.h"
37
#include "Core/HLE/sceNetAdhoc.h"
38
#include "Core/HLE/proAdhoc.h"
39
#include "Core/HLE/sceNetAdhocMatching.h"
40
#include "Core/HLE/NetAdhocCommon.h"
41
#include "Common/System/Request.h"
42
43
#include "Core/Util/AtracTrack.h"
44
#include "Core/HLE/sceAtrac.h"
45
#include "Core/HLE/sceAudio.h"
46
#include "Core/HLE/sceAudiocodec.h"
47
#include "Core/HLE/sceMp3.h"
48
#include "Core/HLE/AtracCtx.h"
49
#include "Core/HLE/sceSas.h"
50
#include "Core/HW/SasAudio.h"
51
#include "Core/HW/Display.h"
52
#include "Core/Dialog/PSPSaveDialog.h"
53
54
#include "Core/CoreTiming.h"
55
// Threads window
56
#include "Core/HLE/sceKernelThread.h"
57
58
// Callstack window
59
#include "Core/MIPS/MIPSStackWalk.h"
60
61
// GPU things
62
#include "GPU/Common/GPUDebugInterface.h"
63
#include "GPU/Debugger/Stepping.h"
64
65
#include "UI/ImDebugger/ImDebugger.h"
66
#include "UI/ImDebugger/ImGe.h"
67
#include "UI/AudioCommon.h"
68
#include "UI/GameInfoCache.h"
69
70
extern bool g_TakeScreenshot;
71
static ImVec4 g_normalTextColor;
72
73
void ShowInMemoryViewerMenuItem(uint32_t addr, ImControl &control) {
74
if (ImGui::BeginMenu("Show in memory viewer")) {
75
for (int i = 0; i < 4; i++) {
76
if (ImGui::MenuItem(ImMemWindow::Title(i))) {
77
control.command = { ImCmd::SHOW_IN_MEMORY_VIEWER, addr, (u32)i };
78
}
79
}
80
ImGui::EndMenu();
81
}
82
}
83
84
void ShowInMemoryDumperMenuItem(uint32_t addr, uint32_t size, MemDumpMode mode, ImControl &control) {
85
if (ImGui::MenuItem(mode == MemDumpMode::Raw ? "Dump bytes to file..." : "Disassemble to file...")) {
86
control.command = { ImCmd::SHOW_IN_MEMORY_DUMPER, addr, size, (u32)mode};
87
}
88
}
89
90
void ShowInWindowMenuItems(uint32_t addr, ImControl &control) {
91
// Enable when we implement the memory viewer
92
ShowInMemoryViewerMenuItem(addr, control);
93
if (ImGui::MenuItem("Show in CPU debugger")) {
94
control.command = { ImCmd::SHOW_IN_CPU_DISASM, addr };
95
}
96
if (ImGui::MenuItem("Show in GE debugger")) {
97
control.command = { ImCmd::SHOW_IN_GE_DISASM, addr };
98
}
99
}
100
101
void StatusBar(std::string_view status) {
102
if (!status.size()) {
103
return;
104
}
105
ImGui::TextUnformatted(status.data(), status.data() + status.length());
106
ImGui::SameLine();
107
if (ImGui::SmallButton("Copy")) {
108
System_CopyStringToClipboard(status);
109
}
110
}
111
112
// TODO: Style it more
113
// Left click performs the preferred action, if any. Right click opens a menu for more.
114
void ImClickableValue(const char *id, uint32_t value, ImControl &control, ImCmd cmd) {
115
ImGui::PushID(id);
116
117
bool validAddr = Memory::IsValidAddress(value);
118
119
char temp[32];
120
snprintf(temp, sizeof(temp), "%08x", value);
121
if (ImGui::Selectable(temp) && validAddr) {
122
control.command = { cmd, value };
123
}
124
125
// Create a right-click popup menu. Restore the color while it's up. NOTE: can't query the theme, pushcolor modifies it!
126
ImGui::PushStyleColor(ImGuiCol_Text, g_normalTextColor);
127
if (ImGui::BeginPopupContextItem(temp)) {
128
if (ImGui::MenuItem(validAddr ? "Copy address to clipboard" : "Copy value to clipboard")) {
129
System_CopyStringToClipboard(temp);
130
}
131
if (validAddr) {
132
ImGui::Separator();
133
ShowInWindowMenuItems(value, control);
134
}
135
ImGui::EndPopup();
136
}
137
ImGui::PopStyleColor();
138
ImGui::PopID();
139
}
140
141
// Left click performs the preferred action, if any. Right click opens a menu for more.
142
void ImClickableValueFloat(const char *id, float value) {
143
ImGui::PushID(id);
144
145
char temp[32];
146
snprintf(temp, sizeof(temp), "%0.7f", value);
147
if (ImGui::Selectable(temp)) {}
148
149
// Create a right-click popup menu. Restore the color while it's up. NOTE: can't query the theme, pushcolor modifies it!
150
ImGui::PushStyleColor(ImGuiCol_Text, g_normalTextColor);
151
if (ImGui::BeginPopupContextItem(temp)) {
152
if (ImGui::MenuItem("Copy value to clipboard")) {
153
System_CopyStringToClipboard(temp);
154
}
155
ImGui::EndPopup();
156
}
157
ImGui::PopStyleColor();
158
ImGui::PopID();
159
}
160
161
void DrawTimeView(ImConfig &cfg) {
162
ImGui::SetNextWindowSize(ImVec2(420, 300), ImGuiCond_FirstUseEver);
163
if (!ImGui::Begin("Time", &cfg.timeOpen)) {
164
ImGui::End();
165
return;
166
}
167
168
// Display timing
169
if (ImGui::CollapsingHeader("Display Timing", ImGuiTreeNodeFlags_DefaultOpen)) {
170
ImGui::Text("Num VBlanks: %d", __DisplayGetNumVblanks());
171
ImGui::Text("FlipCount: %d", __DisplayGetFlipCount());
172
ImGui::Text("VCount: %d", __DisplayGetVCount());
173
ImGui::Text("HCount cur: %d accum: %d", __DisplayGetCurrentHcount(), __DisplayGetAccumulatedHcount());
174
ImGui::Text("IsVblank: %d", DisplayIsVblank());
175
}
176
177
// RTC
178
if (ImGui::CollapsingHeader("RTC", ImGuiTreeNodeFlags_DefaultOpen)) {
179
PSPTimeval tv;
180
__RtcTimeOfDay(&tv);
181
ImGui::Text("RtcTimeOfDay: %d.%06d", tv.tv_sec, tv.tv_usec);
182
ImGui::Text("RtcGetCurrentTick: %lld", (long long)__RtcGetCurrentTick());
183
}
184
185
ImGui::End();
186
}
187
188
void DrawSchedulerView(ImConfig &cfg) {
189
ImGui::SetNextWindowSize(ImVec2(420, 300), ImGuiCond_FirstUseEver);
190
if (!ImGui::Begin("Event Scheduler", &cfg.schedulerOpen)) {
191
ImGui::End();
192
return;
193
}
194
s64 ticks = CoreTiming::GetTicks();
195
if (ImGui::BeginChild("event_list", ImVec2(300.0f, 0.0))) {
196
const CoreTiming::Event *event = CoreTiming::GetFirstEvent();
197
while (event) {
198
ImGui::Text("%s (%lld): %d", CoreTiming::GetEventTypes()[event->type].name, event->time - ticks, (int)event->userdata);
199
event = event->next;
200
}
201
ImGui::EndChild();
202
}
203
ImGui::SameLine();
204
if (ImGui::BeginChild("general")) {
205
ImGui::Text("CoreState: %s", CoreStateToString(coreState));
206
ImGui::Text("downcount: %d", currentMIPS->downcount);
207
ImGui::Text("slicelength: %d", CoreTiming::slicelength);
208
ImGui::Text("Ticks: %lld", ticks);
209
ImGui::Text("Clock (MHz): %0.1f", (float)CoreTiming::GetClockFrequencyHz() / 1000000.0f);
210
ImGui::Text("Global time (us): %lld", CoreTiming::GetGlobalTimeUs());
211
ImGui::EndChild();
212
}
213
ImGui::End();
214
}
215
216
static void DrawGPRs(ImConfig &config, ImControl &control, const MIPSDebugInterface *mipsDebug, const ImSnapshotState &prev) {
217
ImGui::SetNextWindowSize(ImVec2(320, 600), ImGuiCond_FirstUseEver);
218
if (!ImGui::Begin("GPRs", &config.gprOpen)) {
219
ImGui::End();
220
return;
221
}
222
223
bool noDiff = coreState == CORE_RUNNING_CPU || coreState == CORE_STEPPING_GE;
224
225
if (ImGui::Button("Copy all to clipboard")) {
226
char *buffer = new char[20000];
227
StringWriter w(buffer, 20000);
228
for (int i = 0; i < 32; i++) {
229
u32 value = mipsDebug->GetGPR32Value(i);
230
w.F("%s: %08x (%d)", mipsDebug->GetRegName(0, i).c_str(), value, value).endl();
231
}
232
w.F("hi: %08x", mipsDebug->GetHi()).endl();
233
w.F("lo: %08x", mipsDebug->GetLo()).endl();
234
w.F("pc: %08x", mipsDebug->GetPC()).endl();
235
System_CopyStringToClipboard(buffer);
236
delete[] buffer;
237
}
238
239
if (ImGui::BeginTable("gpr", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) {
240
ImGui::TableSetupColumn("Reg", ImGuiTableColumnFlags_WidthFixed);
241
ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthFixed);
242
ImGui::TableSetupColumn("Decimal", ImGuiTableColumnFlags_WidthStretch);
243
244
ImGui::TableHeadersRow();
245
246
auto gprLine = [&](int index, const char *regname, int value, int prevValue) {
247
bool diff = value != prevValue && !noDiff;
248
bool disabled = value == 0xdeadbeef;
249
250
ImGui::TableNextColumn();
251
ImGui::TextUnformatted(regname);
252
ImGui::TableNextColumn();
253
if (diff) {
254
ImGui::PushStyleColor(ImGuiCol_Text, !disabled ? ImDebuggerColor_Diff : ImDebuggerColor_DiffAlpha);
255
} else if (disabled) {
256
ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(255, 255, 255, 128));
257
}
258
// TODO: Check if the address is in the code segment to decide default action.
259
ImClickableValue(regname, value, control, index == MIPS_REG_RA ? ImCmd::SHOW_IN_CPU_DISASM : ImCmd::SHOW_IN_MEMORY_VIEWER);
260
ImGui::TableNextColumn();
261
if (value >= -1000000 && value <= 1000000) {
262
ImGui::Text("%d", value);
263
}
264
if (diff || disabled) {
265
ImGui::PopStyleColor();
266
}
267
};
268
for (int i = 0; i < 32; i++) {
269
ImGui::TableNextRow();
270
gprLine(i, mipsDebug->GetRegName(0, i).c_str(), mipsDebug->GetGPR32Value(i), prev.gpr[i]);
271
}
272
ImGui::TableNextRow();
273
gprLine(32, "hi", mipsDebug->GetHi(), prev.hi);
274
ImGui::TableNextRow();
275
gprLine(33, "lo", mipsDebug->GetLo(), prev.lo);
276
ImGui::TableNextRow();
277
gprLine(34, "pc", mipsDebug->GetPC(), prev.pc);
278
ImGui::TableNextRow();
279
gprLine(35, "ll", mipsDebug->GetLLBit(), prev.ll);
280
ImGui::EndTable();
281
}
282
ImGui::End();
283
}
284
285
static void DrawFPRs(ImConfig &config, ImControl &control, const MIPSDebugInterface *mipsDebug, const ImSnapshotState &prev) {
286
ImGui::SetNextWindowSize(ImVec2(320, 600), ImGuiCond_FirstUseEver);
287
if (!ImGui::Begin("FPRs", &config.fprOpen)) {
288
ImGui::End();
289
return;
290
}
291
292
bool noDiff = coreState == CORE_RUNNING_CPU || coreState == CORE_STEPPING_GE;
293
294
if (ImGui::BeginTable("fpr", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) {
295
ImGui::TableSetupColumn("Reg", ImGuiTableColumnFlags_WidthFixed);
296
ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthFixed);
297
ImGui::TableSetupColumn("Hex", ImGuiTableColumnFlags_WidthStretch);
298
299
ImGui::TableHeadersRow();
300
301
// fpcond
302
ImGui::TableNextRow();
303
ImGui::TableNextColumn();
304
ImGui::TextUnformatted("fpcond");
305
ImGui::TableNextColumn();
306
ImGui::Text("%08x", mipsDebug->GetFPCond());
307
308
for (int i = 0; i < 32; i++) {
309
float fvalue = mipsDebug->GetFPR32Value(i);
310
float prevValue = prev.fpr[i];
311
312
// NOTE: Using memcmp to avoid NaN problems.
313
bool diff = memcmp(&fvalue, &prevValue, 4) != 0 && !noDiff;
314
315
u32 fivalue;
316
memcpy(&fivalue, &fvalue, sizeof(fivalue));
317
ImGui::TableNextRow();
318
ImGui::TableNextColumn();
319
if (diff) {
320
ImGui::PushStyleColor(ImGuiCol_Text, ImDebuggerColor_Diff);
321
}
322
ImGui::TextUnformatted(mipsDebug->GetRegName(1, i).c_str());
323
ImGui::TableNextColumn();
324
ImClickableValueFloat(mipsDebug->GetRegName(1, i).c_str(), fvalue);
325
ImGui::TableNextColumn();
326
ImGui::Text("%08x", fivalue);
327
if (diff) {
328
ImGui::PopStyleColor();
329
}
330
}
331
332
ImGui::EndTable();
333
}
334
ImGui::End();
335
}
336
337
static void DrawVFPU(ImConfig &config, ImControl &control, const MIPSDebugInterface *mipsDebug, const ImSnapshotState &prev) {
338
ImGui::SetNextWindowSize(ImVec2(320, 600), ImGuiCond_FirstUseEver);
339
if (!ImGui::Begin("VFPU", &config.vfpuOpen)) {
340
ImGui::End();
341
return;
342
}
343
ImGui::Text("TODO");
344
ImGui::End();
345
}
346
347
void WaitIDToString(WaitType waitType, SceUID waitID, char *buffer, size_t bufSize) {
348
switch (waitType) {
349
case WAITTYPE_AUDIOCHANNEL:
350
snprintf(buffer, bufSize, "chan %d", (int)waitID);
351
return;
352
case WAITTYPE_IO:
353
// TODO: More detail
354
snprintf(buffer, bufSize, "fd: %d", (int)waitID);
355
return;
356
case WAITTYPE_ASYNCIO:
357
snprintf(buffer, bufSize, "id: %d", (int)waitID);
358
return;
359
case WAITTYPE_THREADEND:
360
case WAITTYPE_MUTEX:
361
case WAITTYPE_LWMUTEX:
362
case WAITTYPE_MODULE:
363
case WAITTYPE_MSGPIPE:
364
case WAITTYPE_FPL:
365
case WAITTYPE_VPL:
366
case WAITTYPE_MBX:
367
case WAITTYPE_EVENTFLAG:
368
case WAITTYPE_SEMA:
369
// Get the name of the thread
370
if (kernelObjects.IsValid(waitID)) {
371
auto obj = kernelObjects.GetFast<KernelObject>(waitID);
372
if (obj && obj->GetName()) {
373
truncate_cpy(buffer, bufSize, obj->GetName());
374
return;
375
}
376
}
377
break;
378
case WAITTYPE_DELAY:
379
case WAITTYPE_SLEEP:
380
case WAITTYPE_HLEDELAY:
381
case WAITTYPE_UMD:
382
case WAITTYPE_NONE:
383
case WAITTYPE_VBLANK:
384
case WAITTYPE_MICINPUT:
385
truncate_cpy(buffer, bufSize, "-");
386
return;
387
case WAITTYPE_CTRL:
388
snprintf(buffer, bufSize, "ctrl: %d", waitID);
389
return;
390
default:
391
truncate_cpy(buffer, bufSize, "(unimpl)");
392
return;
393
}
394
395
}
396
397
void DrawThreadView(ImConfig &cfg, ImControl &control) {
398
ImGui::SetNextWindowSize(ImVec2(420, 300), ImGuiCond_FirstUseEver);
399
if (!ImGui::Begin("Threads", &cfg.threadsOpen)) {
400
ImGui::End();
401
return;
402
}
403
404
std::vector<DebugThreadInfo> info = GetThreadsInfo();
405
if (ImGui::BeginTable("threads", 8, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) {
406
ImGui::TableSetupColumn("Id", ImGuiTableColumnFlags_WidthFixed);
407
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed);
408
ImGui::TableSetupColumn("PC", ImGuiTableColumnFlags_WidthFixed);
409
ImGui::TableSetupColumn("Entry", ImGuiTableColumnFlags_WidthFixed);
410
ImGui::TableSetupColumn("Priority", ImGuiTableColumnFlags_WidthFixed);
411
ImGui::TableSetupColumn("State", ImGuiTableColumnFlags_WidthFixed);
412
ImGui::TableSetupColumn("Wait Type", ImGuiTableColumnFlags_WidthStretch);
413
ImGui::TableSetupColumn("Wait ID", ImGuiTableColumnFlags_WidthStretch);
414
// .initialStack, .stackSize, etc
415
ImGui::TableHeadersRow();
416
417
for (int i = 0; i < (int)info.size(); i++) {
418
const DebugThreadInfo &thread = info[i];
419
ImGui::TableNextRow();
420
ImGui::PushID(i);
421
ImGui::TableNextColumn();
422
ImGui::Text("%d", thread.id);
423
ImGui::TableNextColumn();
424
if (ImGui::Selectable(thread.name, cfg.selectedThread == i, ImGuiSelectableFlags_AllowDoubleClick | ImGuiSelectableFlags_SpanAllColumns)) {
425
cfg.selectedThread = i;
426
}
427
if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
428
cfg.selectedThread = i;
429
ImGui::OpenPopup("threadPopup");
430
}
431
ImGui::TableNextColumn();
432
ImClickableValue("curpc", thread.curPC, control, ImCmd::SHOW_IN_CPU_DISASM);
433
ImGui::TableNextColumn();
434
ImClickableValue("entry", thread.entrypoint, control, ImCmd::SHOW_IN_CPU_DISASM);
435
ImGui::TableNextColumn();
436
ImGui::Text("%d", thread.priority);
437
ImGui::TableNextColumn();
438
ImGui::TextUnformatted(ThreadStatusToString(thread.status));
439
ImGui::TableNextColumn();
440
ImGui::TextUnformatted(WaitTypeToString(thread.waitType));
441
ImGui::TableNextColumn();
442
char temp[64];
443
WaitIDToString(thread.waitType, thread.waitID, temp, sizeof(temp));
444
ImGui::TextUnformatted(temp);
445
if (ImGui::BeginPopup("threadPopup")) {
446
DebugThreadInfo &thread = info[i];
447
ImGui::Text("Thread: %s", thread.name);
448
if (ImGui::MenuItem("Copy entry to clipboard")) {
449
char temp[64];
450
snprintf(temp, sizeof(temp), "%08x", thread.entrypoint);
451
System_CopyStringToClipboard(temp);
452
}
453
if (ImGui::MenuItem("Copy thread PC to clipboard")) {
454
char temp[64];
455
snprintf(temp, sizeof(temp), "%08x", thread.curPC);
456
System_CopyStringToClipboard(temp);
457
}
458
if (ImGui::MenuItem("Kill thread")) {
459
// Dangerous!
460
sceKernelTerminateThread(thread.id);
461
}
462
if (thread.status == THREADSTATUS_WAIT) {
463
if (ImGui::MenuItem("Force run now")) {
464
__KernelResumeThreadFromWait(thread.id, 0);
465
}
466
}
467
ImGui::EndPopup();
468
}
469
ImGui::PopID();
470
}
471
472
ImGui::EndTable();
473
}
474
ImGui::End();
475
}
476
477
void DrawParamSFO(ImConfig &cfg, ImControl &control) {
478
ImGui::SetNextWindowSize(ImVec2(420, 300), ImGuiCond_FirstUseEver);
479
if (!ImGui::Begin("PARAMS.SFO", &cfg.paramSFOOpen)) {
480
ImGui::End();
481
return;
482
}
483
484
auto renderParamSFOTable = [](ParamSFOData &data) {
485
const auto &map = data.Values();
486
if (System_GetPropertyBool(SYSPROP_HAS_TEXT_CLIPBOARD) && ImGui::Button("Copy to clipboard")) {
487
char buffer[4096];
488
StringWriter w(buffer);
489
for (auto &[key, value] : map) {
490
switch (value.type) {
491
case ParamSFOData::VT_UTF8:
492
w.F("%s: %s\n", key.c_str(), value.s_value.c_str());
493
break;
494
case ParamSFOData::VT_UTF8_SPE:
495
w.F("%s: %d raw bytes\n", key.c_str(), (int)value.s_value.size());
496
break;
497
case ParamSFOData::VT_INT:
498
w.F("%s: %d\n", key.c_str(), value.i_value);
499
break;
500
default:
501
break;
502
}
503
}
504
System_CopyStringToClipboard(w.as_view());
505
}
506
507
if (ImGui::BeginTable("paramsfo", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH | ImGuiTableFlags_Resizable)) {
508
ImGui::TableSetupColumn("Key", ImGuiTableColumnFlags_WidthFixed, 120.0f);
509
ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthFixed, 120.f);
510
ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed, 50.f);
511
ImGui::TableHeadersRow();
512
513
for (auto &[key, value] : map) {
514
ImGui::TableNextRow();
515
ImGui::PushID(key.c_str());
516
ImGui::TableNextColumn();
517
ImGui::Text("%s", key.c_str());
518
ImGui::TableNextColumn();
519
switch (value.type) {
520
case ParamSFOData::VT_UTF8:
521
ImGui::Text("%s", value.s_value.c_str());
522
break;
523
case ParamSFOData::VT_UTF8_SPE:
524
ImGui::Text("%d raw bytes", (int)value.s_value.size());
525
break;
526
case ParamSFOData::VT_INT:
527
ImGui::Text("%d", value.i_value);
528
break;
529
default:
530
ImGui::TextUnformatted("N/A");
531
break;
532
}
533
ImGui::TableNextColumn();
534
ImGui::Text("%s", ParamSFOData::ValueTypeToString(value.type));
535
ImGui::PopID();
536
}
537
538
ImGui::EndTable();
539
}
540
};
541
542
if (ImGui::BeginTabBar("ParamSFOTabs")) {
543
if (ImGui::BeginTabItem("Active")) {
544
renderParamSFOTable(g_paramSFO);
545
ImGui::EndTabItem();
546
}
547
if (ImGui::BeginTabItem("Original")) {
548
renderParamSFOTable(g_paramSFORaw);
549
ImGui::EndTabItem();
550
}
551
if (ImGui::BeginTabItem("GameInfo")) {
552
Path path = PSP_CoreParameter().fileToStart;
553
std::shared_ptr<GameInfo> info = g_gameInfoCache->GetInfo(nullptr, path, GameInfoFlags::PARAM_SFO);
554
555
if (info && ImGui::BeginTable("paramsfo", 2, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH | ImGuiTableFlags_Resizable)) {
556
ImGui::TableSetupColumn("Key", ImGuiTableColumnFlags_WidthFixed, 140.0f);
557
ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthFixed, 270.0f);
558
559
ImGui::TableHeadersRow();
560
561
ImGui::TableNextRow();
562
ImGui::TableNextColumn();
563
ImGui::TextUnformatted("Title");
564
ImGui::TableNextColumn();
565
ImGui::TextUnformatted(info->GetTitle());
566
567
ImGui::TableNextRow();
568
ImGui::TableNextColumn();
569
ImGui::TextUnformatted("Path");
570
ImGui::TableNextColumn();
571
ImGui::TextUnformatted(path.ToVisualString());
572
573
ImGui::TableNextRow();
574
ImGui::TableNextColumn();
575
ImGui::TextUnformatted("Detected type");
576
ImGui::TableNextColumn();
577
ImGui::TextUnformatted(IdentifiedFileTypeToString(info->fileType));
578
579
ImGui::TableNextRow();
580
ImGui::TableNextColumn();
581
ImGui::TextUnformatted("Detected region");
582
ImGui::TableNextColumn();
583
ImGui::TextUnformatted(GameRegionToString(info->region));
584
585
ImGui::TableNextRow();
586
ImGui::TableNextColumn();
587
ImGui::TextUnformatted("HasConfig");
588
ImGui::TableNextColumn();
589
ImGui::TextUnformatted(BoolStr(info->hasConfig));
590
591
ImGui::EndTable();
592
}
593
ImGui::EndTabItem();
594
}
595
ImGui::EndTabBar();
596
}
597
598
ImGui::End();
599
}
600
601
// TODO: Add popup menu, export file, export dir, etc...
602
static void RecurseFileSystem(IFileSystem *fs, std::string path, RequesterToken token) {
603
std::vector<PSPFileInfo> fileInfo = fs->GetDirListing(path);
604
for (auto &file : fileInfo) {
605
if (file.type == FileType::FILETYPE_DIRECTORY) {
606
if (file.name != "." && file.name != ".." && ImGui::TreeNode(file.name.c_str())) {
607
std::string fpath = path + "/" + file.name;
608
RecurseFileSystem(fs, fpath, token);
609
ImGui::TreePop();
610
}
611
} else {
612
ImGui::Selectable(file.name.c_str());
613
if (ImGui::BeginPopupContextItem()) {
614
if (ImGui::MenuItem("Copy Path")) {
615
System_CopyStringToClipboard(path + "/" + file.name);
616
}
617
if (ImGui::MenuItem("Save file...")) {
618
std::string fullPath = path + "/" + file.name;
619
int size = file.size;
620
// save dialog
621
System_BrowseForFileSave(token, "Save file", file.name, BrowseFileType::ANY, [fullPath, fs, size](const char *responseString, int) {
622
int fd = fs->OpenFile(fullPath, FILEACCESS_READ);
623
if (fd >= 0) {
624
std::string data;
625
data.resize(size);
626
fs->ReadFile(fd, (u8 *)data.data(), size);
627
fs->CloseFile(fd);
628
Path dest(responseString);
629
File::WriteDataToFile(false, data.data(), data.size(), dest);
630
}
631
});
632
}
633
// your popup code
634
ImGui::EndPopup();
635
}
636
}
637
}
638
}
639
640
static void DrawFilesystemBrowser(ImConfig &cfg) {
641
ImGui::SetNextWindowSize(ImVec2(420, 500), ImGuiCond_FirstUseEver);
642
if (!ImGui::Begin("File System", &cfg.filesystemBrowserOpen)) {
643
ImGui::End();
644
return;
645
}
646
647
for (auto &fs : pspFileSystem.GetMounts()) {
648
std::string path;
649
char desc[256];
650
fs.system->Describe(desc, sizeof(desc));
651
char fsTitle[512];
652
snprintf(fsTitle, sizeof(fsTitle), "%s - %s", fs.prefix.c_str(), desc);
653
if (ImGui::TreeNode(fsTitle)) {
654
auto system = fs.system;
655
RecurseFileSystem(system.get(), path, cfg.requesterToken);
656
ImGui::TreePop();
657
}
658
}
659
660
ImGui::End();
661
}
662
663
static void DrawKernelObjects(ImConfig &cfg) {
664
if (!ImGui::Begin("Kernel Objects", &cfg.kernelObjectsOpen)) {
665
ImGui::End();
666
return;
667
}
668
if (ImGui::BeginTable("kos", 4, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH | ImGuiTableFlags_Resizable)) {
669
ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_WidthFixed);
670
ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed);
671
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed);
672
ImGui::TableSetupColumn("Summary", ImGuiTableColumnFlags_WidthStretch);
673
674
ImGui::TableHeadersRow();
675
676
for (int i = 0; i < (int)KernelObjectPool::maxCount; i++) {
677
int id = i + KernelObjectPool::handleOffset;
678
if (!kernelObjects.IsValid(id)) {
679
continue;
680
}
681
KernelObject *obj = kernelObjects.GetFast<KernelObject>(id);
682
ImGui::TableNextRow();
683
ImGui::TableNextColumn();
684
ImGui::PushID(i);
685
if (ImGui::Selectable("", cfg.selectedKernelObject == i, ImGuiSelectableFlags_AllowDoubleClick | ImGuiSelectableFlags_SpanAllColumns)) {
686
cfg.selectedKernelObject = id;
687
}
688
ImGui::SameLine();
689
/*
690
if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
691
cfg.selectedThread = i;
692
ImGui::OpenPopup("kernelObjectPopup");
693
}
694
*/
695
ImGui::Text("%04x", id);
696
ImGui::TableNextColumn();
697
ImGui::TextUnformatted(obj->GetTypeName());
698
ImGui::TableNextColumn();
699
ImGui::TextUnformatted(obj->GetName());
700
ImGui::TableNextColumn();
701
char qi[128];
702
obj->GetQuickInfo(qi, sizeof(qi));
703
ImGui::TextUnformatted(qi);
704
ImGui::PopID();
705
}
706
707
ImGui::EndTable();
708
}
709
710
if (kernelObjects.IsValid(cfg.selectedKernelObject)) {
711
const int id = cfg.selectedKernelObject;
712
KernelObject *obj = kernelObjects.GetFast<KernelObject>(id);
713
if (obj) {
714
ImGui::Text("%08x: %s", id, obj->GetTypeName());
715
char longInfo[4096];
716
obj->GetLongInfo(longInfo, sizeof(longInfo));
717
ImGui::TextUnformatted(longInfo);
718
}
719
720
// TODO: Show details
721
}
722
ImGui::End();
723
}
724
725
static void DrawNp(ImConfig &cfg) {
726
if (!ImGui::Begin("NP", &cfg.npOpen)) {
727
ImGui::End();
728
return;
729
}
730
731
ImGui::Text("Signed in: %d", npSigninState);
732
ImGui::Text("Title ID: %s", npTitleId.data);
733
734
SceNpId id{};
735
NpGetNpId(&id);
736
ImGui::Text("User Handle: %s", id.handle.data);
737
738
739
ImGui::End();
740
}
741
742
static void DrawApctl(ImConfig &cfg) {
743
if (!ImGui::Begin("Apctl", &cfg.apctlOpen)) {
744
ImGui::End();
745
return;
746
}
747
748
ImGui::Text("State: %s", ApctlStateToString(netApctlState));
749
if (netApctlState != PSP_NET_APCTL_STATE_DISCONNECTED && ImGui::CollapsingHeader("ApCtl Details")) {
750
ImGui::Text("Name: %s", netApctlInfo.name);
751
ImGui::Text("IP: %s", netApctlInfo.ip);
752
ImGui::Text("SubnetMask: %s", netApctlInfo.ip);
753
ImGui::Text("SSID: %.*s", netApctlInfo.ssidLength, netApctlInfo.ssid);
754
ImGui::Text("SSID: %.*s", netApctlInfo.ssidLength, netApctlInfo.ssid);
755
ImGui::Text("Gateway: %s", netApctlInfo.gateway);
756
ImGui::Text("PrimaryDNS: %s", netApctlInfo.primaryDns);
757
ImGui::Text("SecondaryDNS: %s", netApctlInfo.secondaryDns);
758
}
759
760
if (g_Config.bInfrastructureAutoDNS) {
761
const InfraDNSConfig &dnsConfig = GetInfraDNSConfig();
762
if (dnsConfig.loaded) {
763
if (!dnsConfig.gameName.empty()) {
764
ImGui::Text("Known game: %s", dnsConfig.gameName.c_str());
765
}
766
ImGui::Text("connectAdhocForGrouping: %s", BoolStr(dnsConfig.connectAdHocForGrouping));
767
ImGui::Text("DNS: %s", dnsConfig.dns.c_str());
768
if (!dnsConfig.dyn_dns.empty()) {
769
ImGui::Text("DynDNS: %s", dnsConfig.dyn_dns.c_str());
770
}
771
if (!dnsConfig.fixedDNS.empty()) {
772
ImGui::TextUnformatted("Fixed DNS");
773
for (auto iter : dnsConfig.fixedDNS) {
774
ImGui::Text("%s -> %s", iter.first.c_str(), iter.second.c_str());
775
}
776
}
777
} else {
778
ImGui::TextUnformatted("(InfraDNSConfig not loaded)");
779
}
780
}
781
782
ImGui::End();
783
}
784
785
static void DrawInternals(ImConfig &cfg) {
786
if (!ImGui::Begin("PPSSPP Internals", &cfg.internalsOpen)) {
787
ImGui::End();
788
return;
789
}
790
791
struct entry {
792
PSPDirectories dir;
793
const char *name;
794
};
795
796
static const entry dirs[] = {
797
{DIRECTORY_PSP, "PSP"},
798
{DIRECTORY_CHEATS, "CHEATS"},
799
{DIRECTORY_SCREENSHOT, "SCREENSHOT"},
800
{DIRECTORY_SYSTEM, "SYSTEM"},
801
{DIRECTORY_GAME, "GAME"},
802
{DIRECTORY_SAVEDATA, "SAVEDATA"},
803
{DIRECTORY_PAUTH, "PAUTH"},
804
{DIRECTORY_DUMP, "DUMP"},
805
{DIRECTORY_SAVESTATE, "SAVESTATE"},
806
{DIRECTORY_CACHE, "CACHE"},
807
{DIRECTORY_TEXTURES, "TEXTURES"},
808
{DIRECTORY_PLUGINS, "PLUGINS"},
809
{DIRECTORY_APP_CACHE, "APP_CACHE"},
810
{DIRECTORY_VIDEO, "VIDEO"},
811
{DIRECTORY_AUDIO, "AUDIO"},
812
{DIRECTORY_MEMSTICK_ROOT, "MEMSTICK_ROOT"},
813
{DIRECTORY_EXDATA, "EXDATA"},
814
{DIRECTORY_CUSTOM_SHADERS, "CUSTOM_SHADERS"},
815
{DIRECTORY_CUSTOM_THEMES, "CUSTOM_THEMES"},
816
};
817
818
if (ImGui::CollapsingHeader("GetSysDirectory")) {
819
for (auto &dir : dirs) {
820
ImGui::Text("%s: %s", dir.name, GetSysDirectory(dir.dir).c_str());
821
}
822
}
823
824
if (ImGui::CollapsingHeader("Memory")) {
825
ImGui::Text("Base pointer: %p", Memory::base);
826
ImGui::Text("Main memory size: %08x", Memory::g_MemorySize);
827
if (ImGui::Button("Copy to clipboard")) {
828
System_CopyStringToClipboard(StringFromFormat("0x%p", Memory::base));
829
}
830
}
831
832
if (ImGui::CollapsingHeader("ImGui state")) {
833
const auto &io = ImGui::GetIO();
834
ImGui::Text("WantCaptureMouse: %s", BoolStr(io.WantCaptureMouse));
835
ImGui::Text("WantCaptureKeyboard: %s", BoolStr(io.WantCaptureKeyboard));
836
ImGui::Text("WantCaptureMouseUnlessPopupClose: %s", BoolStr(io.WantCaptureMouseUnlessPopupClose));
837
ImGui::Text("WantTextInput: %s", BoolStr(io.WantTextInput));
838
}
839
840
if (ImGui::CollapsingHeader("Save detection")) {
841
ImGui::Text("Last in-game save/load: %0.1f seconds ago", SecondsSinceLastGameSave());
842
ImGui::Text("Last save/load state: %0.1f seconds ago", SaveState::SecondsSinceLastSavestate());
843
}
844
845
ImGui::End();
846
}
847
848
static void DrawAdhoc(ImConfig &cfg) {
849
if (!ImGui::Begin("AdHoc", &cfg.adhocOpen)) {
850
ImGui::End();
851
return;
852
}
853
854
const char *discoverStatusStr = "N/A";
855
switch (netAdhocDiscoverStatus) {
856
case 0: discoverStatusStr = "NONE"; break;
857
case 1: discoverStatusStr = "IN_PROGRESS"; break;
858
case 2: discoverStatusStr = "COMPLETED"; break;
859
default: break;
860
}
861
862
ImGui::Text("sceNetAdhoc inited: %s", BoolStr(netAdhocInited));
863
ImGui::Text("sceNetAdhocctl inited: %s", BoolStr(netAdhocctlInited));
864
ImGui::Text("sceNetAdhocctl state: %s", AdhocCtlStateToString(NetAdhocctl_GetState()));
865
ImGui::Text("sceNetAdhocMatching inited: %s", BoolStr(netAdhocctlInited));
866
ImGui::Text("GameMode entered: %s", BoolStr(netAdhocGameModeEntered));
867
ImGui::Text("FriendFinder running: %s", BoolStr(g_adhocServerConnected));
868
ImGui::Text("sceNetAdhocDiscover status: %s", discoverStatusStr);
869
870
if (ImGui::BeginTable("sock", 5, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH | ImGuiTableFlags_Resizable)) {
871
ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_WidthFixed);
872
ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed);
873
ImGui::TableSetupColumn("Non-blocking", ImGuiTableColumnFlags_WidthFixed);
874
ImGui::TableSetupColumn("BufSize", ImGuiTableColumnFlags_WidthFixed);
875
ImGui::TableSetupColumn("IsClient", ImGuiTableColumnFlags_WidthFixed);
876
877
ImGui::TableHeadersRow();
878
879
for (int i = 0; i < MAX_SOCKET; i++) {
880
const AdhocSocket *socket = adhocSockets[i];
881
if (!socket) {
882
continue;
883
}
884
885
ImGui::TableNextRow();
886
ImGui::TableNextColumn();
887
ImGui::Text("%d", i + 1);
888
ImGui::TableNextColumn();
889
switch (socket->type) {
890
case SOCK_PDP: ImGui::TextUnformatted("PDP"); break;
891
case SOCK_PTP: ImGui::TextUnformatted("PTP"); break;
892
default: ImGui::Text("(%d)", socket->type); break;
893
}
894
ImGui::TableNextColumn();
895
ImGui::TextUnformatted(socket->nonblocking ? "Non-blocking" : "Blocking");
896
ImGui::TableNextColumn();
897
ImGui::Text("%d", socket->buffer_size);
898
ImGui::TableNextColumn();
899
ImGui::TextUnformatted(BoolStr(socket->isClient));
900
}
901
ImGui::EndTable();
902
}
903
904
ImGui::End();
905
}
906
907
static void DrawSockets(ImConfig &cfg) {
908
if (!ImGui::Begin("Sockets", &cfg.socketsOpen)) {
909
ImGui::End();
910
return;
911
}
912
if (ImGui::BeginTable("sock", 9, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH | ImGuiTableFlags_Resizable)) {
913
ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_WidthFixed);
914
ImGui::TableSetupColumn("Port", ImGuiTableColumnFlags_WidthFixed);
915
ImGui::TableSetupColumn("IP address", ImGuiTableColumnFlags_WidthFixed);
916
ImGui::TableSetupColumn("Non-blocking", ImGuiTableColumnFlags_WidthFixed);
917
ImGui::TableSetupColumn("Created by", ImGuiTableColumnFlags_WidthFixed);
918
ImGui::TableSetupColumn("Domain", ImGuiTableColumnFlags_WidthFixed);
919
ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed);
920
ImGui::TableSetupColumn("Protocol", ImGuiTableColumnFlags_WidthStretch);
921
ImGui::TableSetupColumn("Host handle", ImGuiTableColumnFlags_WidthFixed);
922
923
ImGui::TableHeadersRow();
924
925
for (int i = SocketManager::MIN_VALID_INET_SOCKET; i < SocketManager::VALID_INET_SOCKET_COUNT; i++) {
926
InetSocket *inetSocket;
927
if (!g_socketManager.GetInetSocket(i, &inetSocket)) {
928
continue;
929
}
930
931
ImGui::TableNextRow();
932
ImGui::TableNextColumn();
933
ImGui::Text("%d", i);
934
ImGui::TableNextColumn();
935
ImGui::Text("%d", inetSocket->port);
936
ImGui::TableNextColumn();
937
ImGui::TextUnformatted(inetSocket->addr.c_str());
938
ImGui::TableNextColumn();
939
ImGui::TextUnformatted(inetSocket->nonblocking ? "Non-blocking" : "Blocking");
940
ImGui::TableNextColumn();
941
ImGui::TextUnformatted(SocketStateToString(inetSocket->state));
942
ImGui::TableNextColumn();
943
std::string str = inetSocketDomain2str(inetSocket->domain);
944
ImGui::TextUnformatted(str.c_str());
945
ImGui::TableNextColumn();
946
str = inetSocketType2str(inetSocket->type);
947
ImGui::TextUnformatted(str.c_str());
948
ImGui::TableNextColumn();
949
str = inetSocketProto2str(inetSocket->protocol);
950
ImGui::TextUnformatted(str.c_str());
951
ImGui::TableNextColumn();
952
ImGui::Text("%d", (int)inetSocket->sock);
953
}
954
955
ImGui::EndTable();
956
}
957
ImGui::End();
958
}
959
960
static const char *MemCheckConditionToString(MemCheckCondition cond) {
961
// (int) casting to avoid "case not in enum" warnings
962
switch ((int)cond) {
963
case (int)MEMCHECK_READ: return "Read";
964
case (int)MEMCHECK_WRITE: return "Write";
965
case (int)MEMCHECK_READWRITE: return "Read/Write";
966
case (int)(MEMCHECK_WRITE | MEMCHECK_WRITE_ONCHANGE): return "Write Change";
967
case (int)(MEMCHECK_READWRITE | MEMCHECK_WRITE_ONCHANGE): return "Read/Write Change";
968
default:
969
return "(bad!)";
970
}
971
}
972
973
static void DrawBreakpointsView(MIPSDebugInterface *mipsDebug, ImConfig &cfg) {
974
if (!ImGui::Begin("Breakpoints", &cfg.breakpointsOpen)) {
975
ImGui::End();
976
return;
977
}
978
if (ImGui::BeginTable("bp_window", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersInnerV)) {
979
ImGui::TableSetupColumn("left", ImGuiTableColumnFlags_WidthStretch);
980
ImGui::TableSetupColumn("right", ImGuiTableColumnFlags_WidthFixed, 150.0f);
981
ImGui::TableNextRow();
982
ImGui::TableNextColumn();
983
auto &bps = g_breakpoints.GetBreakpointRefs();
984
auto &mcs = g_breakpoints.GetMemCheckRefs();
985
986
if (cfg.selectedBreakpoint >= bps.size()) {
987
cfg.selectedBreakpoint = -1;
988
}
989
if (cfg.selectedMemCheck >= mcs.size()) {
990
cfg.selectedMemCheck = -1;
991
}
992
993
if (ImGui::Button("Add Breakpoint")) {
994
cfg.selectedBreakpoint = g_breakpoints.AddBreakPoint(0);
995
cfg.selectedMemCheck = -1;
996
}
997
ImGui::SameLine();
998
if (ImGui::Button("Add MemCheck")) {
999
cfg.selectedMemCheck = g_breakpoints.AddMemCheck(0, 0, MemCheckCondition::MEMCHECK_WRITE, BreakAction::BREAK_ACTION_PAUSE);
1000
cfg.selectedBreakpoint = -1;
1001
}
1002
1003
if (ImGui::BeginTable("breakpoints", 8, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) {
1004
ImGui::TableSetupColumn("Enabled", ImGuiTableColumnFlags_WidthFixed);
1005
ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed);
1006
ImGui::TableSetupColumn("Address", ImGuiTableColumnFlags_WidthFixed);
1007
ImGui::TableSetupColumn("Size/Label", ImGuiTableColumnFlags_WidthFixed);
1008
ImGui::TableSetupColumn("OpCode", ImGuiTableColumnFlags_WidthFixed);
1009
ImGui::TableSetupColumn("Cond", ImGuiTableColumnFlags_WidthFixed);
1010
ImGui::TableSetupColumn("Hits", ImGuiTableColumnFlags_WidthStretch);
1011
ImGui::TableHeadersRow();
1012
1013
for (int i = 0; i < (int)bps.size(); i++) {
1014
auto &bp = bps[i];
1015
bool temp = bp.temporary;
1016
if (temp) {
1017
continue;
1018
}
1019
1020
ImGui::TableNextRow();
1021
ImGui::TableNextColumn();
1022
ImGui::PushID(i);
1023
// DONE: This clashes with the checkbox!
1024
// TODO: Test to make sure this works properly
1025
if (ImGui::Selectable("", cfg.selectedBreakpoint == i, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap) && !bp.temporary) {
1026
cfg.selectedBreakpoint = i;
1027
cfg.selectedMemCheck = -1;
1028
}
1029
ImGui::SameLine();
1030
ImGui::CheckboxFlags("##enabled", (int *)&bp.result, (int)BREAK_ACTION_PAUSE);
1031
ImGui::TableNextColumn();
1032
ImGui::TextUnformatted("Exec");
1033
ImGui::TableNextColumn();
1034
ImGui::Text("%08x", bp.addr);
1035
ImGui::TableNextColumn();
1036
const std::string sym = g_symbolMap->GetLabelString(bp.addr);
1037
if (!sym.empty()) {
1038
ImGui::TextUnformatted(sym.c_str()); // size/label
1039
} else {
1040
ImGui::TextUnformatted("-"); // size/label
1041
}
1042
ImGui::TableNextColumn();
1043
// disasm->getOpcodeText(displayedBreakPoints_[index].addr, temp, sizeof(temp));
1044
ImGui::TextUnformatted("-"); // opcode
1045
ImGui::TableNextColumn();
1046
if (bp.hasCond) {
1047
ImGui::TextUnformatted(bp.cond.expressionString.c_str());
1048
} else {
1049
ImGui::TextUnformatted("-"); // condition
1050
}
1051
ImGui::TableNextColumn();
1052
ImGui::Text("-"); // hits not available on exec bps yet
1053
ImGui::PopID();
1054
}
1055
1056
// OK, now list the memchecks.
1057
for (int i = 0; i < (int)mcs.size(); i++) {
1058
auto &mc = mcs[i];
1059
ImGui::TableNextRow();
1060
ImGui::TableNextColumn();
1061
ImGui::PushID(i + 10000);
1062
if (ImGui::Selectable("##memcheck", cfg.selectedMemCheck == i, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap)) {
1063
cfg.selectedBreakpoint = -1;
1064
cfg.selectedMemCheck = i;
1065
}
1066
ImGui::SameLine();
1067
ImGui::CheckboxFlags("", (int *)&mc.result, BREAK_ACTION_PAUSE);
1068
ImGui::TableNextColumn();
1069
ImGui::TextUnformatted(MemCheckConditionToString(mc.cond));
1070
ImGui::TableNextColumn();
1071
ImGui::Text("%08x", mc.start);
1072
ImGui::TableNextColumn();
1073
ImGui::Text("%08x", mc.end ? (mc.end - mc.start) : 1);
1074
ImGui::TableNextColumn();
1075
ImGui::TextUnformatted("-"); // opcode
1076
ImGui::TableNextColumn();
1077
ImGui::TextUnformatted("-"); // cond
1078
ImGui::TableNextColumn();
1079
ImGui::Text("%d", mc.numHits);
1080
ImGui::PopID();
1081
}
1082
1083
ImGui::EndTable();
1084
}
1085
1086
ImGui::TableNextColumn();
1087
1088
if (cfg.selectedBreakpoint >= 0) {
1089
// Add edit form for breakpoints
1090
if (ImGui::BeginChild("bp_edit")) {
1091
auto &bp = bps[cfg.selectedBreakpoint];
1092
ImGui::TextUnformatted("Edit breakpoint");
1093
ImGui::CheckboxFlags("Enabled", (int *)&bp.result, (int)BREAK_ACTION_PAUSE);
1094
ImGui::InputScalar("Address", ImGuiDataType_U32, &bp.addr, nullptr, nullptr, "%08x", ImGuiInputTextFlags_CharsHexadecimal);
1095
if (ImGui::Button("Delete")) {
1096
g_breakpoints.RemoveBreakPoint(bp.addr);
1097
}
1098
ImGui::EndChild();
1099
}
1100
}
1101
1102
if (cfg.selectedMemCheck >= 0) {
1103
// Add edit form for memchecks
1104
if (ImGui::BeginChild("mc_edit")) {
1105
auto &mc = mcs[cfg.selectedMemCheck];
1106
ImGui::TextUnformatted("Edit memcheck");
1107
if (ImGui::BeginCombo("Condition", MemCheckConditionToString(mc.cond))) {
1108
if (ImGui::Selectable("Read", mc.cond == MemCheckCondition::MEMCHECK_READ)) {
1109
mc.cond = MemCheckCondition::MEMCHECK_READ;
1110
}
1111
if (ImGui::Selectable("Write", mc.cond == MemCheckCondition::MEMCHECK_WRITE)) {
1112
mc.cond = MemCheckCondition::MEMCHECK_WRITE;
1113
}
1114
if (ImGui::Selectable("Read / Write", mc.cond == MemCheckCondition::MEMCHECK_READWRITE)) {
1115
mc.cond = MemCheckCondition::MEMCHECK_READWRITE;
1116
}
1117
if (ImGui::Selectable("Write On Change", mc.cond == MemCheckCondition::MEMCHECK_WRITE_ONCHANGE)) {
1118
mc.cond = MemCheckCondition::MEMCHECK_WRITE_ONCHANGE;
1119
}
1120
ImGui::EndCombo();
1121
}
1122
ImGui::CheckboxFlags("Enabled", (int *)&mc.result, (int)BREAK_ACTION_PAUSE);
1123
ImGui::InputScalar("Start", ImGuiDataType_U32, &mc.start, NULL, NULL, "%08x", ImGuiInputTextFlags_CharsHexadecimal);
1124
ImGui::InputScalar("End", ImGuiDataType_U32, &mc.end, NULL, NULL, "%08x", ImGuiInputTextFlags_CharsHexadecimal);
1125
if (ImGui::Button("Delete")) {
1126
g_breakpoints.RemoveMemCheck(mcs[cfg.selectedMemCheck].start, mcs[cfg.selectedMemCheck].end);
1127
}
1128
ImGui::EndChild();
1129
}
1130
}
1131
ImGui::EndTable();
1132
}
1133
1134
ImGui::End();
1135
}
1136
1137
void DrawMediaDecodersView(ImConfig &cfg, ImControl &control) {
1138
if (!ImGui::Begin("Media decoding contexts", &cfg.mediaDecodersOpen)) {
1139
ImGui::End();
1140
return;
1141
}
1142
1143
const std::map<u32, MpegContext *> &mpegCtxs = __MpegGetContexts();
1144
if (ImGui::CollapsingHeaderWithCount("sceMpeg", (int)mpegCtxs.size())) {
1145
if (ImGui::BeginTable("mpegs", 2, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) {
1146
ImGui::TableSetupColumn("Addr", ImGuiTableColumnFlags_WidthFixed);
1147
ImGui::TableSetupColumn("VFrames", ImGuiTableColumnFlags_WidthFixed);
1148
1149
ImGui::TableHeadersRow();
1150
for (auto iter : mpegCtxs) {
1151
ImGui::TableNextRow();
1152
ImGui::TableNextColumn();
1153
ImGui::PushID(iter.first);
1154
ImGui::SetNextItemAllowOverlap();
1155
char temp[16];
1156
snprintf(temp, sizeof(temp), "%08x", iter.first);
1157
if (ImGui::Selectable(temp, iter.first == cfg.selectedMpegCtx, ImGuiSelectableFlags_SpanAllColumns)) {
1158
cfg.selectedMpegCtx = iter.first;
1159
}
1160
ImGui::TableNextColumn();
1161
const MpegContext *ctx = iter.second;
1162
if (!ctx) {
1163
ImGui::TextUnformatted("N/A");
1164
ImGui::PopID();
1165
continue;
1166
}
1167
ImGui::Text("%d", ctx->videoFrameCount);
1168
ImGui::PopID();
1169
}
1170
ImGui::EndTable();
1171
}
1172
1173
auto iter = mpegCtxs.find(cfg.selectedMpegCtx);
1174
if (iter != mpegCtxs.end()) {
1175
const MpegContext *ctx = iter->second;
1176
char temp[28];
1177
snprintf(temp, sizeof(temp), "sceMpeg context at %08x", iter->first);
1178
if (ctx && ImGui::CollapsingHeader(temp, ImGuiTreeNodeFlags_DefaultOpen)) {
1179
// ImGui::ProgressBar((float)sas->CurPos() / (float)info.fileDataEnd, ImVec2(200.0f, 0.0f));
1180
ImGui::Text("Mpeg version: %d raw: %08x", ctx->mpegVersion, ctx->mpegRawVersion);
1181
ImGui::Text("Frame counts: Audio %d, video %d", ctx->audioFrameCount, ctx->videoFrameCount);
1182
ImGui::Text("Video pixel mode: %d", ctx->videoPixelMode);
1183
ImGui::Text("AVC status=%d width=%d height=%d result=%d", ctx->avc.avcFrameStatus, ctx->avc.avcDetailFrameWidth, ctx->avc.avcDetailFrameHeight, ctx->avc.avcDecodeResult);
1184
ImGui::Text("Stream size: %d", ctx->mpegStreamSize);
1185
}
1186
}
1187
}
1188
1189
// Count the active atrac contexts so we can display it.
1190
const int maxAtracContexts = __AtracMaxContexts();
1191
int atracCount = 0;
1192
for (int i = 0; i < maxAtracContexts; i++) {
1193
u32 type;
1194
if (__AtracGetCtx(i, &type)) {
1195
atracCount++;
1196
}
1197
}
1198
1199
if (ImGui::CollapsingHeaderWithCount("sceAtrac", atracCount, ImGuiTreeNodeFlags_DefaultOpen)) {
1200
ImGui::Checkbox("Force FFMPEG", &g_Config.bForceFfmpegForAudioDec);
1201
if (ImGui::BeginTable("atracs", 8, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) {
1202
ImGui::TableSetupColumn("Index", ImGuiTableColumnFlags_WidthFixed);
1203
ImGui::TableSetupColumn("Mute", ImGuiTableColumnFlags_WidthFixed);
1204
ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed);
1205
ImGui::TableSetupColumn("Status", ImGuiTableColumnFlags_WidthFixed);
1206
ImGui::TableSetupColumn("Channels", ImGuiTableColumnFlags_WidthFixed);
1207
ImGui::TableSetupColumn("CurSample", ImGuiTableColumnFlags_WidthFixed);
1208
ImGui::TableSetupColumn("RemFrames", ImGuiTableColumnFlags_WidthFixed);
1209
ImGui::TableSetupColumn("Impl", ImGuiTableColumnFlags_WidthFixed);
1210
1211
ImGui::TableHeadersRow();
1212
for (int i = 0; i < maxAtracContexts; i++) {
1213
u32 codecType = 0;
1214
1215
ImGui::TableNextRow();
1216
ImGui::TableNextColumn();
1217
ImGui::PushID(i);
1218
ImGui::SetNextItemAllowOverlap();
1219
char temp[16];
1220
snprintf(temp, sizeof(temp), "%d", i);
1221
if (ImGui::Selectable(temp, i == cfg.selectedAtracCtx, ImGuiSelectableFlags_SpanAllColumns)) {
1222
cfg.selectedAtracCtx = i;
1223
}
1224
ImGui::TableNextColumn();
1225
bool *mutePtr = __AtracMuteFlag(i);
1226
if (mutePtr) {
1227
ImGui::Checkbox("", mutePtr);
1228
}
1229
ImGui::TableNextColumn();
1230
1231
const AtracBase *ctx = __AtracGetCtx(i, &codecType);
1232
if (!ctx) {
1233
// Nothing more we can display about uninitialized contexts.
1234
ImGui::PopID();
1235
continue;
1236
}
1237
1238
switch (codecType) {
1239
case 0:
1240
ImGui::TextUnformatted("-"); // Uninitialized
1241
break;
1242
case PSP_CODEC_AT3PLUS:
1243
ImGui::TextUnformatted("Atrac3+");
1244
break;
1245
case PSP_CODEC_AT3:
1246
ImGui::TextUnformatted("Atrac3");
1247
break;
1248
default:
1249
ImGui::Text("%04x", codecType);
1250
break;
1251
}
1252
1253
ImGui::TableNextColumn();
1254
ImGui::TextUnformatted(AtracStatusToString(ctx->BufferState()));
1255
ImGui::TableNextColumn();
1256
ImGui::Text("in:%d out:%d", ctx->Channels(), ctx->GetOutputChannels());
1257
ImGui::TableNextColumn();
1258
if (AtracStatusIsNormal(ctx->BufferState())) {
1259
int pos;
1260
ctx->GetNextDecodePosition(&pos);
1261
ImGui::Text("%d", pos);
1262
} else {
1263
ImGui::TextUnformatted("N/A");
1264
}
1265
ImGui::TableNextColumn();
1266
if (AtracStatusIsNormal(ctx->BufferState())) {
1267
ImGui::Text("%d", ctx->RemainingFrames());
1268
} else {
1269
ImGui::TextUnformatted("N/A");
1270
}
1271
ImGui::TableNextColumn();
1272
ImGui::TextUnformatted(ctx->GetContextVersion() >= 2 ? "NewImpl" : "Legacy");
1273
ImGui::PopID();
1274
}
1275
1276
ImGui::EndTable();
1277
}
1278
1279
if (cfg.selectedAtracCtx >= 0 && cfg.selectedAtracCtx < PSP_MAX_ATRAC_IDS) {
1280
u32 type = 0;
1281
const AtracBase *ctx = __AtracGetCtx(cfg.selectedAtracCtx, &type);
1282
// Show details about the selected atrac context here.
1283
char header[32];
1284
snprintf(header, sizeof(header), "Atrac context %d", cfg.selectedAtracCtx);
1285
if (ctx && ImGui::CollapsingHeader(header, ImGuiTreeNodeFlags_DefaultOpen)) {
1286
bool isNormal = AtracStatusIsNormal(ctx->BufferState());
1287
if (isNormal) {
1288
int pos;
1289
ctx->GetNextDecodePosition(&pos);
1290
int endSample, loopStart, loopEnd;
1291
ctx->GetSoundSample(&endSample, &loopStart, &loopEnd);
1292
ImGui::ProgressBar((float)pos / (float)endSample, ImVec2(200.0f, 0.0f));
1293
ImGui::Text("Status: %s", AtracStatusToString(ctx->BufferState()));
1294
ImGui::Text("cur/end sample: %d/%d", pos, endSample);
1295
}
1296
if (ctx->context_.IsValid()) {
1297
ImGui::Text("ctx addr: ");
1298
ImGui::SameLine();
1299
ImClickableValue("ctx", ctx->context_.ptr, control, ImCmd::SHOW_IN_MEMORY_VIEWER);
1300
}
1301
if (ctx->context_.IsValid() && ctx->GetContextVersion() >= 2) {
1302
const auto &info = ctx->context_->info;
1303
if (isNormal) {
1304
ImGui::Text("Buffer: (size: %d / %08x) Frame: %d", info.bufferByte, info.bufferByte, info.sampleSize);
1305
ImGui::SameLine();
1306
ImClickableValue("buffer", info.buffer, control, ImCmd::SHOW_IN_MEMORY_VIEWER);
1307
if (info.secondBuffer || info.secondBufferByte) {
1308
ImGui::Text("Second: (size: %d / %08x)", info.secondBufferByte, info.secondBufferByte);
1309
ImGui::SameLine();
1310
ImClickableValue("second", info.secondBuffer, control, ImCmd::SHOW_IN_MEMORY_VIEWER);
1311
}
1312
ImGui::Text("Data: %d/%d", info.dataOff, info.fileDataEnd);
1313
if (info.state != ATRAC_STATUS_STREAMED_WITHOUT_LOOP) {
1314
ImGui::Text("LoopNum: %d (%d-%d)", info.loopNum, info.loopStart, info.loopEnd);
1315
}
1316
ImGui::Text("DecodePos: %d EndSample: %d", info.decodePos, info.fileDataEnd);
1317
if (AtracStatusIsStreaming(info.state)) {
1318
ImGui::Text("Stream: offset %d, streamDataBytes: %d", info.streamOff, info.streamDataByte);
1319
}
1320
ImGui::Text("numFrame: %d curBuffer: %d streamOff2: %d", info.numSkipFrames, info.curBuffer, info.secondStreamOff);
1321
} else if (ctx->BufferState() == ATRAC_STATUS_FOR_SCESAS) {
1322
// A different set of state!
1323
const AtracSasStreamState *sas = ctx->StreamStateForSas();
1324
if (sas) {
1325
ImGui::ProgressBar((float)sas->CurPos() / (float)info.fileDataEnd, ImVec2(200.0f, 0.0f));
1326
ImGui::ProgressBar((float)sas->streamOffset / (float)sas->bufSize[sas->curBuffer], ImVec2(200.0f, 0.0f));
1327
ImGui::Text("Cur pos: %08x File offset: %08x File end: %08x%s", sas->CurPos(), sas->fileOffset, info.fileDataEnd, sas->fileOffset >= info.fileDataEnd ? " (END)" : "");
1328
ImGui::Text("Second (next buffer): %08x (sz: %08x)", info.secondBuffer, info.secondBufferByte);
1329
ImGui::Text("Cur buffer: %d (%08x, sz: %08x)", sas->curBuffer, sas->bufPtr[sas->curBuffer], sas->bufSize[sas->curBuffer]);
1330
ImGui::Text("2nd buffer: %d (%08x, sz: %08x)", sas->curBuffer ^ 1, sas->bufPtr[sas->curBuffer ^ 1], sas->bufSize[sas->curBuffer ^ 1]);
1331
ImGui::Text("Loop points: %08x, %08x", info.loopStart, info.loopEnd);
1332
ImGui::TextUnformatted(sas->isStreaming ? "Streaming mode!" : "Non-streaming mode");
1333
} else {
1334
ImGui::Text("can't access sas state");
1335
}
1336
}
1337
1338
if (ctx->BufferState() == ATRAC_STATUS_ALL_DATA_LOADED) {
1339
if (ImGui::Button("Save to disk...")) {
1340
System_BrowseForFileSave(cfg.requesterToken, "Save AT3 file", "song.at3", BrowseFileType::ATRAC3, [=](const std::string &filename, int) {
1341
const u8 *data = Memory::GetPointerRange(info.buffer, info.bufferByte);
1342
if (!data) {
1343
return;
1344
}
1345
FILE *file = File::OpenCFile(Path(filename), "wb");
1346
if (!file) {
1347
return;
1348
}
1349
fwrite(data, 1, info.bufferByte, file);
1350
fclose(file);
1351
});
1352
}
1353
}
1354
} else {
1355
ImGui::Text("loop: %d", ctx->LoopNum());
1356
}
1357
}
1358
}
1359
}
1360
1361
if (ImGui::CollapsingHeaderWithCount("sceMp3", (int)g_mp3Map.size(), ImGuiTreeNodeFlags_DefaultOpen)) {
1362
if (ImGui::BeginTable("mp3", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) {
1363
ImGui::TableSetupColumn("Handle", ImGuiTableColumnFlags_WidthFixed);
1364
ImGui::TableSetupColumn("Channels", ImGuiTableColumnFlags_WidthFixed);
1365
ImGui::TableSetupColumn("ReadPos", ImGuiTableColumnFlags_WidthFixed);
1366
1367
for (auto &iter : g_mp3Map) {
1368
ImGui::TableNextRow();
1369
ImGui::TableNextColumn();
1370
ImGui::PushID(iter.first);
1371
ImGui::SetNextItemAllowOverlap();
1372
char temp[16];
1373
snprintf(temp, sizeof(temp), "%d", iter.first);
1374
if (ImGui::Selectable(temp, iter.first == cfg.selectedMp3Ctx, ImGuiSelectableFlags_SpanAllColumns)) {
1375
cfg.selectedMp3Ctx = iter.first;
1376
}
1377
if (!iter.second) {
1378
continue;
1379
}
1380
ImGui::TableNextColumn();
1381
ImGui::Text("%d", iter.second->Channels);
1382
ImGui::TableNextColumn();
1383
ImGui::Text("%d", (int)iter.second->ReadPos());
1384
ImGui::PopID();
1385
}
1386
ImGui::EndTable();
1387
}
1388
1389
auto iter = g_mp3Map.find(cfg.selectedMp3Ctx);
1390
if (iter != g_mp3Map.end() && ImGui::CollapsingHeader("MP3 %d", iter->first)) {
1391
ImGui::Text("MP3 Context %d", iter->first);
1392
if (iter->second) {
1393
AuCtx *ctx = iter->second;
1394
ImGui::Text("%d Hz, %d channels", ctx->SamplingRate, ctx->Channels);
1395
ImGui::Text("AUBuf: %08x AUSize: %08x", ctx->AuBuf, ctx->AuBufSize);
1396
ImGui::Text("PCMBuf: %08x PCMSize: %08x", ctx->PCMBuf, ctx->PCMBufSize);
1397
ImGui::Text("Pos: %d (%d -> %d)", ctx->ReadPos(), (int)ctx->startPos, (int)ctx->endPos);
1398
}
1399
}
1400
}
1401
1402
if (ImGui::CollapsingHeaderWithCount("sceAac", (int)g_aacMap.size(), ImGuiTreeNodeFlags_DefaultOpen)) {
1403
if (ImGui::BeginTable("aac", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) {
1404
ImGui::TableSetupColumn("Handle", ImGuiTableColumnFlags_WidthFixed);
1405
ImGui::TableSetupColumn("Channels", ImGuiTableColumnFlags_WidthFixed);
1406
ImGui::TableSetupColumn("ReadPos", ImGuiTableColumnFlags_WidthFixed);
1407
1408
for (auto &iter : g_aacMap) {
1409
ImGui::TableNextRow();
1410
ImGui::TableNextColumn();
1411
ImGui::PushID(iter.first);
1412
ImGui::SetNextItemAllowOverlap();
1413
char temp[16];
1414
snprintf(temp, sizeof(temp), "%d", iter.first);
1415
if (ImGui::Selectable(temp, iter.first == cfg.selectedAacCtx, ImGuiSelectableFlags_SpanAllColumns)) {
1416
cfg.selectedAacCtx = iter.first;
1417
}
1418
if (!iter.second) {
1419
continue;
1420
}
1421
ImGui::TableNextColumn();
1422
ImGui::Text("%d", iter.second->Channels);
1423
ImGui::TableNextColumn();
1424
ImGui::Text("%d", (int)iter.second->ReadPos());
1425
ImGui::PopID();
1426
}
1427
ImGui::EndTable();
1428
}
1429
1430
auto iter = g_mp3Map.find(cfg.selectedAacCtx);
1431
if (iter != g_mp3Map.end() && ImGui::CollapsingHeader("AAC %d", iter->first)) {
1432
ImGui::Text("AAC Context %d", iter->first);
1433
if (iter->second) {
1434
AuCtx *ctx = iter->second;
1435
ImGui::Text("%d Hz, %d channels", ctx->SamplingRate, ctx->Channels);
1436
ImGui::Text("AUBuf: %08x AUSize: %08x", ctx->AuBuf, ctx->AuBufSize);
1437
ImGui::Text("PCMBuf: %08x PCMSize: %08x", ctx->PCMBuf, ctx->PCMBufSize);
1438
ImGui::Text("Pos: %d (%d -> %d)", ctx->ReadPos(), (int)ctx->startPos, (int)ctx->endPos);
1439
}
1440
}
1441
}
1442
1443
if (ImGui::CollapsingHeaderWithCount("sceAudiocodec", (int)g_audioDecoderContexts.size(), ImGuiTreeNodeFlags_DefaultOpen)) {
1444
if (ImGui::BeginTable("codecs", 2, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) {
1445
ImGui::TableSetupColumn("CtxAddr", ImGuiTableColumnFlags_WidthFixed);
1446
ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed);
1447
1448
for (auto &iter : g_audioDecoderContexts) {
1449
ImGui::TableNextRow();
1450
ImGui::TableNextColumn();
1451
ImGui::Text("%08x", iter.first);
1452
ImGui::TableNextColumn();
1453
switch (iter.second->GetAudioType()) {
1454
case PSP_CODEC_AAC: ImGui::TextUnformatted("AAC"); break;
1455
case PSP_CODEC_MP3: ImGui::TextUnformatted("MP3"); break;
1456
case PSP_CODEC_AT3PLUS: ImGui::TextUnformatted("Atrac3+"); break;
1457
case PSP_CODEC_AT3: ImGui::TextUnformatted("Atrac3"); break;
1458
default: ImGui::Text("%08x", iter.second->GetAudioType()); break;
1459
}
1460
}
1461
ImGui::EndTable();
1462
}
1463
}
1464
1465
ImGui::End();
1466
}
1467
1468
1469
void DrawAudioOut(ImConfig &cfg, ImControl &control) {
1470
if (!ImGui::Begin("Audio output", &cfg.audioOutOpen)) {
1471
ImGui::End();
1472
return;
1473
}
1474
1475
if (g_Config.iAudioPlaybackMode == (int)AudioSyncMode::GRANULAR) {
1476
// Show granular stats
1477
GranularStats stats;
1478
g_granular.GetStats(&stats);
1479
ImGui::Text("Granules");
1480
ImGui::Text("Read size: %0.2f", stats.smoothedReadSize);
1481
ImGui::Text("Frame time estimate: %0.1f ms", stats.frameTimeEstimate * 1000.0f);
1482
ImGui::Text("Queued samples target: %d", stats.queuedSamplesTarget);
1483
ImGui::Text("Queued min/max: %d / %d", stats.queuedGranulesMin, stats.queuedGranulesMax);
1484
ImGui::Text("Queued (smooth): %0.3f", stats.smoothedQueuedGranules);
1485
ImGui::Text("Target: %d", stats.targetQueueSize);
1486
ImGui::Text("Max: %d", stats.maxQueuedGranules);
1487
ImGui::Text("Computed queue latency: %d ms", (int)(stats.smoothedQueuedGranules * (GranularMixer::GRANULE_SIZE * 1000.0 / 44100.0)));
1488
ImGui::Text("Fade volume: %0.2f", stats.fadeVolume);
1489
ImGui::Text("Looping: %s", BoolStr(stats.looping));
1490
ImGui::Text("Under/Over: %d / %d", stats.underruns, stats.overruns);
1491
} else {
1492
ImGui::Text("StereoResampler classic");
1493
char buf[1024];
1494
g_resampler.GetAudioDebugStats(buf, sizeof(buf));
1495
ImGui::Text("%s", buf);
1496
}
1497
1498
ImGui::End();
1499
}
1500
1501
void DrawAudioChannels(ImConfig &cfg, ImControl &control) {
1502
if (!ImGui::Begin("Raw audio channels", &cfg.audioChannelsOpen)) {
1503
ImGui::End();
1504
return;
1505
}
1506
1507
if (ImGui::BeginTable("audios", 7, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) {
1508
ImGui::TableSetupColumn("Index", ImGuiTableColumnFlags_WidthFixed);
1509
ImGui::TableSetupColumn("Mute", ImGuiTableColumnFlags_WidthFixed);
1510
ImGui::TableSetupColumn("SampleAddr", ImGuiTableColumnFlags_WidthFixed);
1511
ImGui::TableSetupColumn("SampleCount", ImGuiTableColumnFlags_WidthFixed);
1512
ImGui::TableSetupColumn("Volume", ImGuiTableColumnFlags_WidthFixed);
1513
ImGui::TableSetupColumn("Format", ImGuiTableColumnFlags_WidthFixed);
1514
ImGui::TableSetupColumn("Waiting Thread", ImGuiTableColumnFlags_WidthFixed);
1515
1516
ImGui::TableHeadersRow();
1517
1518
// vaudio / output2 uses channel 8.
1519
for (int i = 0; i < PSP_AUDIO_CHANNEL_MAX + 1; i++) {
1520
if (!g_audioChans[i].reserved) {
1521
continue;
1522
}
1523
ImGui::TableNextRow();
1524
ImGui::TableNextColumn();
1525
ImGui::PushID(i);
1526
if (i == 8) {
1527
ImGui::TextUnformatted("audio2");
1528
} else {
1529
ImGui::Text("%d", i);
1530
}
1531
ImGui::TableNextColumn();
1532
ImGui::Checkbox("", &g_audioChans[i].mute);
1533
ImGui::TableNextColumn();
1534
char id[2]{};
1535
id[0] = i + 1;
1536
ImClickableValue(id, g_audioChans[i].sampleAddress, control, ImCmd::SHOW_IN_MEMORY_VIEWER);
1537
ImGui::TableNextColumn();
1538
ImGui::Text("%08x", g_audioChans[i].sampleCount);
1539
ImGui::TableNextColumn();
1540
ImGui::Text("%d | %d", g_audioChans[i].leftVolume, g_audioChans[i].rightVolume);
1541
ImGui::TableNextColumn();
1542
switch (g_audioChans[i].format) {
1543
case PSP_AUDIO_FORMAT_STEREO:
1544
ImGui::TextUnformatted("Stereo");
1545
break;
1546
case PSP_AUDIO_FORMAT_MONO:
1547
ImGui::TextUnformatted("Mono");
1548
break;
1549
default:
1550
ImGui::TextUnformatted("UNK: %04x");
1551
break;
1552
}
1553
ImGui::TableNextColumn();
1554
for (auto t : g_audioChans[i].waitingThreads) {
1555
KernelObject *thread = kernelObjects.GetFast<KernelObject>(t.threadID);
1556
if (thread) {
1557
ImGui::Text("%s: %d", thread->GetName(), t.numSamples);
1558
}
1559
}
1560
ImGui::PopID();
1561
}
1562
1563
ImGui::EndTable();
1564
}
1565
ImGui::End();
1566
}
1567
1568
void ImLogWindow::Draw(ImConfig &cfg) {
1569
ImGui::SetNextWindowSize(ImVec2(500, 400), ImGuiCond_FirstUseEver);
1570
if (!ImGui::Begin("Log", &cfg.logOpen)) {
1571
ImGui::End();
1572
return;
1573
}
1574
1575
const RingbufferLog &ring = g_logManager.GetRingbuffer();
1576
1577
// Options menu
1578
if (ImGui::BeginPopup("Options")) {
1579
ImGui::Checkbox("Auto-scroll", &AutoScroll);
1580
ImGui::EndPopup();
1581
}
1582
1583
// Main window
1584
if (ImGui::Button("Options"))
1585
ImGui::OpenPopup("Options");
1586
ImGui::SameLine();
1587
bool copy = ImGui::Button("Copy");
1588
ImGui::Separator();
1589
1590
if (ImGui::BeginChild("scrolling", ImVec2(0, 0), ImGuiChildFlags_None, ImGuiWindowFlags_HorizontalScrollbar)) {
1591
if (copy)
1592
ImGui::LogToClipboard();
1593
1594
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
1595
1596
ImGuiListClipper clipper;
1597
clipper.Begin(ring.GetCount());
1598
while (clipper.Step()) {
1599
for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++) {
1600
int n = ring.GetCount() - 1 - line_no;
1601
1602
const std::string_view line = ring.TextAt(n);
1603
const LogLevel level = ring.LevelAt(n);
1604
const u32 color = 0xFF000000 | LogManager::GetLevelColor(level);
1605
ImGui::PushStyleColor(ImGuiCol_Text, color);
1606
ImGui::TextUnformatted(line);
1607
ImGui::PopStyleColor();
1608
}
1609
}
1610
clipper.End();
1611
ImGui::PopStyleVar();
1612
1613
// Keep up at the bottom of the scroll region if we were already at the bottom at the beginning of the frame.
1614
// Using a scrollbar or mouse-wheel will take away from the bottom edge.
1615
if (AutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY() - 20)
1616
ImGui::SetScrollHereY(1.0f);
1617
}
1618
ImGui::EndChild();
1619
ImGui::End();
1620
}
1621
1622
void DrawLogConfig(ImConfig &cfg) {
1623
if (!ImGui::Begin("Logs", &cfg.logConfigOpen)) {
1624
ImGui::End();
1625
return;
1626
}
1627
1628
static const char *logLevels[] = {
1629
"N/A",
1630
"Notice", // starts at 1 for some reason
1631
"Error",
1632
"Warn",
1633
"Info",
1634
"Debug",
1635
"Verb."
1636
};
1637
_dbg_assert_(ARRAY_SIZE(logLevels) == (int)LogLevel::LVERBOSE + 1);
1638
1639
if (ImGui::BeginTable("logs", 2, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) {
1640
ImGui::TableSetupColumn("Log", ImGuiTableColumnFlags_WidthFixed);
1641
ImGui::TableSetupColumn("Level", ImGuiTableColumnFlags_WidthFixed, 150.0f);
1642
ImGui::TableHeadersRow();
1643
for (int i = 0; i < (int)Log::NUMBER_OF_LOGS; i++) {
1644
LogChannel *chan = g_logManager.GetLogChannel((Log)i);
1645
ImGui::TableNextRow();
1646
ImGui::TableNextColumn();
1647
1648
const char *logName = LogManager::GetLogTypeName((Log)i);
1649
1650
ImGui::PushID(logName);
1651
1652
ImGui::Checkbox(logName, &chan->enabled);
1653
ImGui::TableNextColumn();
1654
1655
if (ImGui::BeginCombo("-", logLevels[(int)chan->level])) {
1656
for (int i = 1; i < ARRAY_SIZE(logLevels); ++i) {
1657
LogLevel current = static_cast<LogLevel>(i);
1658
bool isSelected = (chan->level == current);
1659
if (ImGui::Selectable(logLevels[(int)current], isSelected)) {
1660
chan->level = current;
1661
}
1662
if (isSelected) {
1663
ImGui::SetItemDefaultFocus();
1664
}
1665
}
1666
ImGui::EndCombo();
1667
}
1668
ImGui::PopID();
1669
}
1670
ImGui::EndTable();
1671
}
1672
1673
ImGui::End();
1674
}
1675
1676
static const char *VoiceTypeToString(VoiceType type) {
1677
switch (type) {
1678
case VOICETYPE_OFF: return "OFF";
1679
case VOICETYPE_VAG: return "VAG"; // default
1680
case VOICETYPE_NOISE: return "NOISE";
1681
case VOICETYPE_TRIWAVE: return "TRIWAVE"; // are these used? there are functions for them (sceSetTriangularWave)
1682
case VOICETYPE_PULSEWAVE: return "PULSEWAVE";
1683
case VOICETYPE_PCM: return "PCM";
1684
case VOICETYPE_ATRAC3: return "ATRAC3";
1685
default: return "(unknown!)";
1686
}
1687
}
1688
1689
void DrawSasAudio(ImConfig &cfg) {
1690
if (!ImGui::Begin("sasAudio", &cfg.sasAudioOpen)) {
1691
ImGui::End();
1692
return;
1693
}
1694
1695
const SasInstance *sas = GetSasInstance();
1696
if (!sas) {
1697
ImGui::Text("Sas instance not available");
1698
ImGui::End();
1699
return;
1700
}
1701
1702
ImGui::Checkbox("Mute", __SasGetGlobalMuteFlag());
1703
ImGui::SameLine();
1704
ImGui::Checkbox("Show all voices", &cfg.sasShowAllVoices);
1705
1706
if (ImGui::BeginTable("saschannels", 9, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) {
1707
ImGui::TableSetupColumn("Index", ImGuiTableColumnFlags_WidthFixed);
1708
ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed);
1709
ImGui::TableSetupColumn("On", ImGuiTableColumnFlags_WidthFixed);
1710
ImGui::TableSetupColumn("Loop", ImGuiTableColumnFlags_WidthFixed);
1711
ImGui::TableSetupColumn("Pause", ImGuiTableColumnFlags_WidthFixed);
1712
ImGui::TableSetupColumn("Cur", ImGuiTableColumnFlags_WidthFixed);
1713
ImGui::TableSetupColumn("Addr", ImGuiTableColumnFlags_WidthFixed);
1714
ImGui::TableSetupColumn("Size", ImGuiTableColumnFlags_WidthFixed);
1715
ImGui::TableSetupColumn("Volume", ImGuiTableColumnFlags_WidthFixed);
1716
ImGui::TableHeadersRow();
1717
1718
for (int i = 0; i < PSP_SAS_VOICES_MAX; i++) {
1719
const SasVoice &voice = sas->voices[i];
1720
if (!voice.on && !cfg.sasShowAllVoices) {
1721
continue;
1722
}
1723
if (!voice.on) {
1724
ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(255, 255, 255, 128));
1725
}
1726
ImGui::TableNextRow();
1727
ImGui::TableNextColumn();
1728
ImGui::Text("%d", i);
1729
ImGui::TableNextColumn();
1730
ImGui::TextUnformatted(VoiceTypeToString(voice.type));
1731
ImGui::TableNextColumn();
1732
ImGui::TextUnformatted(BoolStr(voice.on));
1733
ImGui::TableNextColumn();
1734
ImGui::TextUnformatted(BoolStr(voice.loop));
1735
ImGui::TableNextColumn();
1736
ImGui::TextUnformatted(BoolStr(voice.paused));
1737
ImGui::TableNextColumn();
1738
switch (voice.type) {
1739
case VOICETYPE_OFF: ImGui::TextUnformatted("(off)"); break;
1740
case VOICETYPE_VAG: ImGui::Text("%08x", voice.vag.GetReadPtr()); break;
1741
case VOICETYPE_PCM: ImGui::Text("%08x", voice.pcmAddr + voice.pcmIndex * 2); break;
1742
default:
1743
ImGui::TextUnformatted("N/A");
1744
break;
1745
}
1746
ImGui::TableNextColumn();
1747
switch (voice.type) {
1748
case VOICETYPE_OFF: ImGui::TextUnformatted("(off)"); break;
1749
case VOICETYPE_VAG: ImGui::Text("%08x", voice.vagAddr); break;
1750
case VOICETYPE_PCM: ImGui::Text("%08x", voice.pcmAddr); break;
1751
case VOICETYPE_ATRAC3: ImGui::Text("atrac: %d", voice.atrac3.AtracID()); break;
1752
default:
1753
ImGui::TextUnformatted("N/A");
1754
break;
1755
}
1756
ImGui::TableNextColumn();
1757
switch (voice.type) {
1758
case VOICETYPE_OFF: ImGui::TextUnformatted("(off)"); break;
1759
case VOICETYPE_VAG: ImGui::Text("%08x", voice.vagSize); break;
1760
case VOICETYPE_PCM: ImGui::Text("%08x", voice.pcmSize); break;
1761
case VOICETYPE_ATRAC3: ImGui::Text("atrac: n/a"); break;
1762
default:
1763
ImGui::TextUnformatted("N/A");
1764
break;
1765
}
1766
ImGui::TableNextColumn();
1767
ImGui::Text("%d | %d", voice.volumeLeft, voice.volumeRight);
1768
if (!voice.on) {
1769
ImGui::PopStyleColor();
1770
}
1771
}
1772
1773
ImGui::EndTable();
1774
}
1775
ImGui::End();
1776
}
1777
1778
static void DrawCallStacks(const MIPSDebugInterface *debug, ImConfig &config, ImControl &control) {
1779
if (!ImGui::Begin("Callstacks", &config.callstackOpen)) {
1780
ImGui::End();
1781
return;
1782
}
1783
1784
std::vector<DebugThreadInfo> info = GetThreadsInfo();
1785
// TODO: Add dropdown for thread choice, so you can check the callstacks of other threads.
1786
u32 entry = 0;
1787
u32 stackTop = 0;
1788
for (auto &thread : info) {
1789
if (thread.isCurrent) {
1790
entry = thread.entrypoint;
1791
stackTop = thread.initialStack;
1792
break;
1793
}
1794
}
1795
1796
if (entry != 0 && ImGui::BeginTable("frames", 6, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) {
1797
ImGui::TableSetupColumn("Entry", ImGuiTableColumnFlags_WidthFixed);
1798
ImGui::TableSetupColumn("EntryAddr", ImGuiTableColumnFlags_WidthFixed);
1799
ImGui::TableSetupColumn("CurPC", ImGuiTableColumnFlags_WidthFixed);
1800
ImGui::TableSetupColumn("CurOpCode", ImGuiTableColumnFlags_WidthFixed);
1801
ImGui::TableSetupColumn("CurSP", ImGuiTableColumnFlags_WidthFixed);
1802
ImGui::TableSetupColumn("Size", ImGuiTableColumnFlags_WidthStretch);
1803
1804
ImGui::TableHeadersRow();
1805
1806
std::vector<MIPSStackWalk::StackFrame> frames = MIPSStackWalk::Walk(debug->GetPC(), debug->GetRegValue(0, 31), debug->GetRegValue(0, 29), entry, stackTop);
1807
1808
// TODO: Add context menu and clickability
1809
int i = 0;
1810
for (auto &frame : frames) {
1811
const std::string entrySym = g_symbolMap->GetLabelString(frame.entry);
1812
ImGui::PushID(i);
1813
ImGui::TableNextRow();
1814
ImGui::TableSetColumnIndex(0);
1815
ImGui::TextUnformatted(entrySym.c_str());
1816
ImGui::TableSetColumnIndex(1);
1817
ImClickableValue("frameentry", frame.entry, control, ImCmd::SHOW_IN_CPU_DISASM);
1818
ImGui::TableSetColumnIndex(2);
1819
ImClickableValue("framepc", frame.pc, control, ImCmd::SHOW_IN_CPU_DISASM);
1820
ImGui::TableSetColumnIndex(3);
1821
ImGui::TextUnformatted("N/A"); // opcode, see the old debugger
1822
ImGui::TableSetColumnIndex(4);
1823
ImClickableValue("framepc", frame.sp, control, ImCmd::SHOW_IN_MEMORY_VIEWER);
1824
ImGui::TableSetColumnIndex(5);
1825
ImGui::Text("%d", frame.stackSize);
1826
// TODO: More fields?
1827
ImGui::PopID();
1828
i++;
1829
}
1830
ImGui::EndTable();
1831
}
1832
ImGui::End();
1833
}
1834
1835
static void DrawUtilityModules(ImConfig &cfg, ImControl &control) {
1836
if (!ImGui::Begin("Utility Modules", &cfg.utilityModulesOpen) || !g_symbolMap) {
1837
ImGui::End();
1838
return;
1839
}
1840
1841
ImGui::TextUnformatted(
1842
"These are fake module representations loaded by sceUtilityLoadModule\n"
1843
"On a real PSP, these would be loaded from the BIOS.\n");
1844
1845
const std::map<int, u32> &modules = __UtilityGetLoadedModules();
1846
if (ImGui::BeginTable("modules", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) {
1847
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed);
1848
ImGui::TableSetupColumn("Load Address", ImGuiTableColumnFlags_WidthFixed);
1849
ImGui::TableSetupColumn("Load Size", ImGuiTableColumnFlags_WidthFixed);
1850
1851
ImGui::TableHeadersRow();
1852
1853
// TODO: Add context menu and clickability
1854
int i = 0;
1855
for (const auto &iter : modules) {
1856
u32 loadedAddr = iter.second;
1857
const ModuleLoadInfo *info = __UtilityModuleInfo(iter.first);
1858
1859
ImGui::PushID(i);
1860
ImGui::TableNextRow();
1861
ImGui::TableNextColumn();
1862
if (ImGui::Selectable(info->name, cfg.selectedUtilityModule == i, ImGuiSelectableFlags_SpanAllColumns)) {
1863
cfg.selectedUtilityModule = i;
1864
}
1865
ImGui::TableNextColumn();
1866
if (loadedAddr) {
1867
ImClickableValue("addr", loadedAddr, control, ImCmd::SHOW_IN_MEMORY_VIEWER);
1868
} else {
1869
ImGui::TextUnformatted("-");
1870
}
1871
ImGui::TableNextColumn();
1872
ImGui::Text("%08x", info->size);
1873
ImGui::PopID();
1874
i++;
1875
}
1876
1877
ImGui::EndTable();
1878
}
1879
ImGui::End();
1880
}
1881
1882
static void DrawModules(const MIPSDebugInterface *debug, ImConfig &cfg, ImControl &control) {
1883
if (!ImGui::Begin("Modules", &cfg.modulesOpen) || !g_symbolMap) {
1884
ImGui::End();
1885
return;
1886
}
1887
1888
ImGui::TextUnformatted("This shows modules that have been loaded by the game (not plain HLE)");
1889
1890
if (ImGui::BeginChild("module_list", ImVec2(170.0f, 0.0), ImGuiChildFlags_ResizeX)) {
1891
if (ImGui::BeginTable("modules", 2, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) {
1892
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed);
1893
ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed);
1894
ImGui::TableHeadersRow();
1895
1896
// TODO: Add context menu and clickability
1897
kernelObjects.Iterate<PSPModule>([&cfg](int id, PSPModule *module) -> bool {
1898
ImGui::PushID(id);
1899
ImGui::TableNextRow();
1900
ImGui::TableNextColumn();
1901
if (ImGui::Selectable(module->GetName(), cfg.selectedModuleId == id, ImGuiSelectableFlags_SpanAllColumns)) {
1902
cfg.selectedModuleId = id;
1903
}
1904
ImGui::TableNextColumn();
1905
ImGui::TextUnformatted(module->isFake ? "FAKE/HLE" : "normal");
1906
ImGui::PopID();
1907
return true;
1908
});
1909
1910
ImGui::EndTable();
1911
}
1912
ImGui::EndChild();
1913
}
1914
ImGui::SameLine();
1915
1916
if (ImGui::BeginChild("info")) {
1917
if (kernelObjects.Is<PSPModule>(cfg.selectedModuleId)) {
1918
PSPModule *mod = kernelObjects.GetFast<PSPModule>(cfg.selectedModuleId);
1919
if (mod) {
1920
if (mod->isFake) {
1921
ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(255, 255, 255, 170));
1922
}
1923
ImGui::Text("%s %d.%d (%s)\n", mod->GetName(), mod->nm.version[1], mod->nm.version[0], mod->isFake ? "FAKE/HLE" : "normal");
1924
ImGui::Text("Attr: %08x (%s)\n", mod->nm.attribute, (mod->nm.attribute & 0x1000) ? "Kernel" : "User");
1925
char buf[512];
1926
mod->GetLongInfo(buf, sizeof(buf));
1927
ImGui::TextUnformatted(buf);
1928
if (mod->isFake) {
1929
ImGui::PopStyleColor();
1930
}
1931
if (!mod->impModuleNames.empty() && ImGui::CollapsingHeader("Imported modules")) {
1932
for (auto &name : mod->impModuleNames) {
1933
ImGui::TextUnformatted(name);
1934
}
1935
}
1936
if (!mod->expModuleNames.empty() && ImGui::CollapsingHeader("Exported modules")) {
1937
for (auto &name : mod->expModuleNames) {
1938
ImGui::TextUnformatted(name);
1939
}
1940
}
1941
if (!mod->importedFuncs.empty() || !mod->importedVars.empty()) {
1942
if (ImGui::CollapsingHeader("Imports")) {
1943
if (!mod->importedVars.empty() && ImGui::CollapsingHeader("Vars")) {
1944
for (auto &var : mod->importedVars) {
1945
ImGui::TextUnformatted("(some var)"); // TODO
1946
}
1947
}
1948
for (auto &import : mod->importedFuncs) {
1949
// Look the name up in our HLE database.
1950
const HLEFunction *func = GetHLEFunc(import.moduleName, import.nid);
1951
ImGui::TextUnformatted(import.moduleName);
1952
if (func) {
1953
ImGui::SameLine();
1954
ImGui::TextUnformatted(func->name);
1955
}
1956
ImGui::SameLine(); ImClickableValue("addr", import.stubAddr, control, ImCmd::SHOW_IN_CPU_DISASM);
1957
}
1958
}
1959
}
1960
if (!mod->exportedFuncs.empty() || !mod->exportedVars.empty()) {
1961
if (ImGui::CollapsingHeader("Exports")) {
1962
if (!mod->exportedVars.empty() && ImGui::CollapsingHeader("Vars")) {
1963
for (auto &var : mod->importedVars) {
1964
ImGui::TextUnformatted("(some var)"); // TODO
1965
}
1966
}
1967
for (auto &exportFunc : mod->exportedFuncs) {
1968
// Look the name up in our HLE database.
1969
const HLEFunction *func = GetHLEFunc(exportFunc.moduleName, exportFunc.nid);
1970
ImGui::TextUnformatted(exportFunc.moduleName);
1971
if (func) {
1972
ImGui::SameLine();
1973
ImGui::TextUnformatted(func->name);
1974
}
1975
ImGui::SameLine(); ImClickableValue("addr", exportFunc.symAddr, control, ImCmd::SHOW_IN_CPU_DISASM);
1976
}
1977
}
1978
}
1979
}
1980
} else {
1981
ImGui::TextUnformatted("(no module selected)");
1982
}
1983
ImGui::EndChild();
1984
}
1985
ImGui::End();
1986
}
1987
1988
// Started as a module browser but really only draws from the symbols database, so let's
1989
// evolve it to that.
1990
static void DrawSymbols(const MIPSDebugInterface *debug, ImConfig &cfg, ImControl &control) {
1991
if (!ImGui::Begin("Symbols", &cfg.symbolsOpen) || !g_symbolMap) {
1992
ImGui::End();
1993
return;
1994
}
1995
1996
// Reads from the symbol map.
1997
std::vector<LoadedModuleInfo> modules = g_symbolMap->getAllModules();
1998
if (ImGui::BeginTable("modules", 4, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) {
1999
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed);
2000
ImGui::TableSetupColumn("Address", ImGuiTableColumnFlags_WidthFixed);
2001
ImGui::TableSetupColumn("Size", ImGuiTableColumnFlags_WidthFixed);
2002
ImGui::TableSetupColumn("Active", ImGuiTableColumnFlags_WidthFixed);
2003
2004
ImGui::TableHeadersRow();
2005
2006
// TODO: Add context menu and clickability
2007
for (int i = 0; i < (int)modules.size(); i++) {
2008
auto &module = modules[i];
2009
ImGui::PushID(i);
2010
ImGui::TableNextRow();
2011
ImGui::TableNextColumn();
2012
if (ImGui::Selectable(module.name.c_str(), cfg.selectedSymbolModule == i, ImGuiSelectableFlags_SpanAllColumns)) {
2013
cfg.selectedSymbolModule = i;
2014
}
2015
ImGui::TableNextColumn();
2016
ImClickableValue("addr", module.address, control, ImCmd::SHOW_IN_MEMORY_VIEWER);
2017
ImGui::TableNextColumn();
2018
ImGui::Text("%08x", module.size);
2019
ImGui::TableNextColumn();
2020
ImGui::TextUnformatted(module.active ? "yes" : "no");
2021
ImGui::PopID();
2022
}
2023
2024
ImGui::EndTable();
2025
}
2026
2027
if (cfg.selectedModuleId >= 0 && cfg.selectedModuleId < (int)modules.size()) {
2028
// TODO: Show details
2029
}
2030
ImGui::End();
2031
}
2032
2033
ImWatchWindow::ImWatchWindow() {}
2034
2035
void ImWatchWindow::Draw(ImConfig &cfg, ImControl &control, MIPSDebugInterface *mipsDebug) {
2036
if (!ImGui::Begin("Watch", &cfg.watchOpen) || !g_symbolMap) {
2037
ImGui::End();
2038
return;
2039
}
2040
2041
// Refresh watches
2042
int steppingCounter = Core_GetSteppingCounter();
2043
int changes = false;
2044
for (auto &watch : watches_) {
2045
if (watch.steppingCounter != steppingCounter) {
2046
watch.lastValue = watch.currentValue;
2047
watch.steppingCounter = steppingCounter;
2048
}
2049
2050
uint32_t prevValue = watch.currentValue;
2051
watch.evaluateFailed = !parseExpression(mipsDebug, watch.expression, watch.currentValue);
2052
}
2053
2054
if (ImGui::Button("Add Watch")) {
2055
watches_.push_back(WatchInfo("untitled", "[0x88000000]", mipsDebug));
2056
}
2057
2058
if (ImGui::BeginTable("watches", 4, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH | ImGuiTableFlags_Resizable)) {
2059
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 120.0f);
2060
ImGui::TableSetupColumn("Expression", ImGuiTableColumnFlags_WidthFixed, 120.0f);
2061
ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthFixed, 100.0f);
2062
ImGui::TableSetupColumn("Actions", ImGuiTableColumnFlags_WidthFixed, 50.0f);
2063
2064
ImGui::TableHeadersRow();
2065
2066
for (int i = 0; i < (int)watches_.size(); i++) {
2067
auto &watch = watches_[i];
2068
ImGui::PushID(i);
2069
ImGui::TableNextRow();
2070
ImGui::TableNextColumn();
2071
ImGui::TextUnformatted(watch.name.c_str());
2072
ImGui::TableNextColumn();
2073
if (editingWatchIndex_ == i && editingColumn_ == 1) {
2074
ImGui::SetNextItemWidth(-1.0f); // Full width
2075
if (setEditFocus_) {
2076
ImGui::FocusItem();
2077
setEditFocus_ = false;
2078
}
2079
bool confirmed = ImGui::InputText("##edit", editBuffer_, ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll);
2080
// Filter for only enter presses
2081
confirmed = confirmed && (ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_KeypadEnter));
2082
if (confirmed || ImGui::IsItemDeactivated()) {
2083
watch.SetExpression(editBuffer_, mipsDebug);
2084
editingWatchIndex_ = -1;
2085
}
2086
} else {
2087
ImGui::SetNextItemWidth(-1.0f); // Full width
2088
ImGui::TextUnformatted(watch.originalExpression.c_str());
2089
auto cellRect = ImGui::TableGetCellBgRect(ImGui::GetCurrentTable(), 1);
2090
bool cellHovered = ImGui::IsMouseHoveringRect(cellRect.Min, cellRect.Max);
2091
if (cellHovered && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
2092
editingWatchIndex_ = i;
2093
editingColumn_ = 1;
2094
truncate_cpy(editBuffer_, watch.originalExpression.c_str());
2095
setEditFocus_ = true;
2096
}
2097
}
2098
ImGui::TableNextColumn();
2099
if (watch.evaluateFailed) {
2100
ImGui::TextUnformatted("(Error)");
2101
} else {
2102
bool changed = watch.currentValue != watch.lastValue;
2103
if (changed) {
2104
//ImGui::PushStyleColor(ImGuiCol_Text, ImDebuggerColor_Diff);
2105
}
2106
const uint32_t value = watch.currentValue;
2107
float valuef = 0.0f;
2108
switch (watch.format) {
2109
case WatchFormat::HEX:
2110
ImGui::Text("%08x", value);
2111
break;
2112
case WatchFormat::INT:
2113
ImGui::Text("%d", value);
2114
break;
2115
case WatchFormat::FLOAT:
2116
memcpy(&valuef, &value, sizeof(valuef));
2117
ImGui::Text("%f", static_cast<float>(value));
2118
break;
2119
case WatchFormat::STR:
2120
if (Memory::IsValidAddress(value)) {
2121
uint32_t len = Memory::ValidSize(value, 255);
2122
ImGui::Text("%.*s", len, Memory::GetCharPointer(value));
2123
} else {
2124
ImGui::Text("%08x", value);
2125
}
2126
break;
2127
}
2128
if (changed) {
2129
//ImGui::PopStyleColor(ImGuiCol_Text);
2130
}
2131
}
2132
ImGui::TableNextColumn();
2133
if (ImGui::SmallButton("X")) {
2134
watches_.erase(watches_.begin() + i);
2135
}
2136
ImGui::SameLine();
2137
if (ImGui::Button("Type")) {
2138
ImGui::OpenPopup("watchType");
2139
}
2140
if (ImGui::BeginPopup("watchType")) {
2141
ImGui::Text("Watch type");
2142
if (ImGui::MenuItem("Hex", nullptr, watch.format == WatchFormat::HEX)) { watch.format = WatchFormat::HEX; }
2143
if (ImGui::MenuItem("Int", nullptr, watch.format == WatchFormat::INT)) { watch.format = WatchFormat::INT; }
2144
if (ImGui::MenuItem("Float", nullptr, watch.format == WatchFormat::FLOAT)) { watch.format = WatchFormat::FLOAT; }
2145
if (ImGui::MenuItem("Str", nullptr, watch.format == WatchFormat::STR)) { watch.format = WatchFormat::STR; }
2146
ImGui::EndPopup();
2147
}
2148
2149
ImGui::PopID();
2150
}
2151
2152
ImGui::EndTable();
2153
}
2154
2155
ImGui::End();
2156
}
2157
2158
void ImAtracToolWindow::Load() {
2159
if (File::ReadBinaryFileToString(Path(atracPath_), &data_)) {
2160
track_.reset(new Track());
2161
AnalyzeAtracTrack((const u8 *)data_.data(), (u32)data_.size(), track_.get(), &error_);
2162
} else {
2163
error_ = "Failed to read file from disk. Bad path?";
2164
}
2165
}
2166
2167
void ImAtracToolWindow::Draw(ImConfig &cfg) {
2168
if (!ImGui::Begin("Atrac Tool", &cfg.atracToolOpen) || !g_symbolMap) {
2169
ImGui::End();
2170
return;
2171
}
2172
2173
ImGui::InputText("File", atracPath_, sizeof(atracPath_));
2174
ImGui::SameLine();
2175
if (ImGui::Button("Choose...")) {
2176
System_BrowseForFile(cfg.requesterToken, "Choose AT3 file", BrowseFileType::ATRAC3, [this](const std::string &filename, int) {
2177
truncate_cpy(atracPath_, filename);
2178
Load();
2179
}, nullptr);
2180
}
2181
2182
if (strlen(atracPath_) > 0) {
2183
if (ImGui::Button("Load")) {
2184
Load();
2185
}
2186
}
2187
2188
if (track_.get() != 0) {
2189
ImGui::Text("Codec: %s", track_->codecType != PSP_CODEC_AT3 ? "at3+" : "at3");
2190
ImGui::Text("Bitrate: %d kbps Channels: %d", track_->Bitrate(), track_->channels);
2191
ImGui::Text("Frame size in bytes: %d (%04x) Output frame in samples: %d", track_->BytesPerFrame(), track_->BytesPerFrame(), track_->SamplesPerFrame());
2192
ImGui::Text("First valid sample: %08x", track_->FirstSampleOffsetFull());
2193
ImGui::Text("EndSample: %08x", track_->endSample);
2194
}
2195
2196
if (data_.size()) {
2197
if (ImGui::Button("Dump 64 raw frames")) {
2198
std::string firstFrames = data_.substr(track_->dataByteOffset, track_->bytesPerFrame * 64);
2199
System_BrowseForFileSave(cfg.requesterToken, "Save .at3raw", "at3.raw", BrowseFileType::ANY, [firstFrames](const std::string &filename, int) {
2200
FILE *f = File::OpenCFile(Path(filename), "wb");
2201
if (f) {
2202
fwrite(firstFrames.data(), 1, firstFrames.size(), f);
2203
fclose(f);
2204
}
2205
});
2206
}
2207
2208
if (ImGui::Button("Unload")) {
2209
data_.clear();
2210
track_.reset(nullptr);
2211
}
2212
}
2213
2214
if (!error_.empty()) {
2215
ImGui::TextUnformatted(error_.c_str());
2216
}
2217
2218
ImGui::End();
2219
}
2220
2221
void DrawHLEModules(ImConfig &config) {
2222
if (!ImGui::Begin("HLE Modules", &config.hleModulesOpen)) {
2223
ImGui::End();
2224
return;
2225
}
2226
2227
const int moduleCount = GetNumRegisteredHLEModules();
2228
std::vector<const HLEModule *> modules;
2229
modules.reserve(moduleCount);
2230
for (int i = 0; i < moduleCount; i++) {
2231
modules.push_back(GetHLEModuleByIndex(i));
2232
}
2233
2234
std::sort(modules.begin(), modules.end(), [](const HLEModule* a, const HLEModule* b) {
2235
return a->name < b->name;
2236
});
2237
std::string label;
2238
for (auto mod : modules) {
2239
label = mod->name;
2240
if (ImGui::TreeNode(label.c_str())) {
2241
for (int j = 0; j < mod->numFunctions; j++) {
2242
auto &func = mod->funcTable[j];
2243
ImGui::Text("%s(%s)", func.name, func.argmask);
2244
}
2245
ImGui::TreePop();
2246
}
2247
if (ImGui::BeginPopupContextItem(label.c_str())) {
2248
if (ImGui::MenuItem("Copy as JpscpTrace")) {
2249
char *buffer = new char[1000000];
2250
StringWriter w(buffer, 1000000);
2251
2252
for (int j = 0; j < mod->numFunctions; j++) {
2253
auto &func = mod->funcTable[j];
2254
// Translate the argmask to fit jpscptrace
2255
std::string amask = func.argmask;
2256
for (int i = 0; i < amask.size(); i++) {
2257
switch (amask[i]) {
2258
case 'i':
2259
case 'I': amask[i] = 'd'; break;
2260
case 'f':
2261
case 'F': amask[i] = 'x'; break;
2262
default:
2263
// others go straight through (p)
2264
break;
2265
}
2266
}
2267
w.F("%s 0x%08x %d %s", func.name, func.ID, strlen(func.argmask), amask.c_str()).endl();
2268
}
2269
System_CopyStringToClipboard(w.as_view());
2270
delete[] buffer;
2271
}
2272
if (ImGui::MenuItem("Copy as imports.S")) {
2273
char *buffer = new char[100000];
2274
StringWriter w(buffer, 100000);
2275
2276
w.C(".set noreorder\n\n#include \"pspimport.s\"\n\n");
2277
w.F("IMPORT_START \"%.*s\",0x00090011\n", (int)mod->name.size(), mod->name.data());
2278
for (int j = 0; j < mod->numFunctions; j++) {
2279
auto &func = mod->funcTable[j];
2280
w.F("IMPORT_FUNC \"%.*s\",0x%08X,%s\n", (int)mod->name.size(), mod->name.data(), func.ID, func.name);
2281
}
2282
w.endl();
2283
System_CopyStringToClipboard(w.as_view());
2284
delete[] buffer;
2285
}
2286
ImGui::EndPopup();
2287
}
2288
}
2289
2290
ImGui::End();
2291
}
2292
2293
ImDebugger::ImDebugger() {
2294
reqToken_ = g_requestManager.GenerateRequesterToken();
2295
cfg_.LoadConfig(ConfigPath());
2296
g_normalTextColor = ImGui::GetStyleColorVec4(ImGuiCol_Text);
2297
2298
// Just enable ring buffer logging here, it's cheap (but needed for the log window)
2299
g_logManager.EnableOutput(LogOutput::RingBuffer);
2300
}
2301
2302
ImDebugger::~ImDebugger() {
2303
cfg_.SaveConfig(ConfigPath());
2304
}
2305
2306
void ImDebugger::Frame(MIPSDebugInterface *mipsDebug, GPUDebugInterface *gpuDebug, Draw::DrawContext *draw) {
2307
// Snapshot the coreState to avoid inconsistency.
2308
const CoreState coreState = ::coreState;
2309
2310
if (Achievements::HardcoreModeActive()) {
2311
ImGui::Begin("RetroAchievements hardcore mode");
2312
ImGui::Text("The debugger may not be used when the\nRetroAchievements hardcore mode is enabled.");
2313
ImGui::Text("To use the debugger, go into Settings / Tools / RetroAchievements and disable them.");
2314
ImGui::End();
2315
return;
2316
}
2317
2318
// TODO: Pass mipsDebug in where needed instead.
2319
g_disassemblyManager.setCpu(mipsDebug);
2320
disasm_.View().setDebugger(mipsDebug);
2321
for (int i = 0; i < 4; i++) {
2322
mem_[i].View().setDebugger(mipsDebug);
2323
}
2324
2325
// Watch the step counters to figure out when to update things.
2326
2327
if (lastCpuStepCount_ != Core_GetSteppingCounter()) {
2328
lastCpuStepCount_ = Core_GetSteppingCounter();
2329
snapshot_ = newSnapshot_; // Compare against the previous snapshot.
2330
Snapshot(currentMIPS);
2331
disasm_.NotifyStep();
2332
}
2333
2334
if (lastGpuStepCount_ != GPUStepping::GetSteppingCounter()) {
2335
// A GPU step has happened since last time. This means that we should re-center the cursor.
2336
// Snapshot();
2337
lastGpuStepCount_ = GPUStepping::GetSteppingCounter();
2338
SnapshotGPU(gpuDebug);
2339
geDebugger_.NotifyStep();
2340
}
2341
2342
ImControl control{};
2343
2344
if (ImGui::BeginMainMenuBar()) {
2345
if (ImGui::BeginMenu("Debug")) {
2346
switch (coreState) {
2347
case CoreState::CORE_STEPPING_CPU:
2348
if (ImGui::MenuItem("Run")) {
2349
Core_Resume();
2350
}
2351
break;
2352
case CoreState::CORE_RUNNING_CPU:
2353
if (ImGui::MenuItem("Break")) {
2354
Core_Break(BreakReason::DebugBreak);
2355
}
2356
break;
2357
default:
2358
break;
2359
}
2360
ImGui::Separator();
2361
ImGui::MenuItem("Ignore bad memory accesses", nullptr, &g_Config.bIgnoreBadMemAccess);
2362
ImGui::MenuItem("Break on frame timeout", nullptr, &g_Config.bBreakOnFrameTimeout);
2363
ImGui::MenuItem("Don't break on start", nullptr, &g_Config.bAutoRun); // should really invert this bool!
2364
ImGui::MenuItem("Fast memory", nullptr, &g_Config.bFastMemory);
2365
ImGui::Separator();
2366
if (ImGui::MenuItem("Take screenshot")) {
2367
g_TakeScreenshot = true;
2368
}
2369
ImGui::MenuItem("Save screenshot as .png", nullptr, &g_Config.bScreenshotsAsPNG);
2370
if (ImGui::MenuItem("Restart graphics")) {
2371
System_PostUIMessage(UIMessage::RESTART_GRAPHICS);
2372
}
2373
if (System_GetPropertyBool(SYSPROP_HAS_TEXT_CLIPBOARD)) {
2374
if (ImGui::MenuItem("Copy memory base to clipboard")) {
2375
System_CopyStringToClipboard(StringFromFormat("%016llx", (uint64_t)(uintptr_t)Memory::base));
2376
}
2377
}
2378
ImGui::Separator();
2379
if (ImGui::MenuItem("Close")) {
2380
g_Config.bShowImDebugger = false;
2381
}
2382
ImGui::EndMenu();
2383
}
2384
if (ImGui::BeginMenu("Core")) {
2385
ImGui::MenuItem("Scheduler", nullptr, &cfg_.schedulerOpen);
2386
ImGui::MenuItem("Time", nullptr, &cfg_.timeOpen);
2387
ImGui::EndMenu();
2388
}
2389
if (ImGui::BeginMenu("CPU")) {
2390
ImGui::MenuItem("CPU debugger", nullptr, &cfg_.disasmOpen);
2391
ImGui::MenuItem("GPR regs", nullptr, &cfg_.gprOpen);
2392
ImGui::MenuItem("FPR regs", nullptr, &cfg_.fprOpen);
2393
ImGui::MenuItem("VFPU regs", nullptr, &cfg_.vfpuOpen);
2394
ImGui::Separator();
2395
ImGui::MenuItem("Callstacks", nullptr, &cfg_.callstackOpen);
2396
ImGui::MenuItem("Breakpoints", nullptr, &cfg_.breakpointsOpen);
2397
ImGui::MenuItem("Watch", nullptr, &cfg_.watchOpen);
2398
ImGui::MenuItem("JIT viewer", nullptr, &cfg_.jitViewerOpen);
2399
ImGui::EndMenu();
2400
}
2401
if (ImGui::BeginMenu("Symbols")) {
2402
ImGui::MenuItem("Symbol browser", nullptr, &cfg_.symbolsOpen);
2403
ImGui::Separator();
2404
2405
if (ImGui::MenuItem("Load .ppmap...")) {
2406
System_BrowseForFile(reqToken_, "Load PPSSPP symbol map", BrowseFileType::SYMBOL_MAP, [&](const char *responseString, int) {
2407
Path path(responseString);
2408
if (!g_symbolMap->LoadSymbolMap(path)) {
2409
ERROR_LOG(Log::Common, "Failed to load symbol map");
2410
}
2411
disasm_.DirtySymbolMap();
2412
});
2413
}
2414
if (ImGui::MenuItem("Save .ppmap...")) {
2415
System_BrowseForFileSave(reqToken_, "Save PPSSPP symbol map", "symbols.ppmap", BrowseFileType::SYMBOL_MAP, [](const char *responseString, int) {
2416
Path path(responseString);
2417
if (!g_symbolMap->SaveSymbolMap(path)) {
2418
ERROR_LOG(Log::Common, "Failed to save symbol map");
2419
}
2420
});
2421
}
2422
if (ImGui::MenuItem("Load No$ .sym...")) {
2423
System_BrowseForFile(reqToken_, "Load No$ symbol map", BrowseFileType::SYMBOL_MAP, [&](const char *responseString, int) {
2424
Path path(responseString);
2425
if (!g_symbolMap->LoadNocashSym(path)) {
2426
ERROR_LOG(Log::Common, "Failed to load No$ symbol map");
2427
}
2428
disasm_.DirtySymbolMap();
2429
});
2430
}
2431
if (ImGui::MenuItem("Save No$ .sym...")) {
2432
System_BrowseForFileSave(reqToken_, "Save No$ symbol map", "symbols.sym", BrowseFileType::SYMBOL_MAP, [](const char *responseString, int) {
2433
Path path(responseString);
2434
if (!g_symbolMap->SaveNocashSym(path)) {
2435
ERROR_LOG(Log::Common, "Failed to save No$ symbol map");
2436
}
2437
});
2438
}
2439
ImGui::Separator();
2440
ImGui::MenuItem("Compress .ppmap files", nullptr, &g_Config.bCompressSymbols);
2441
if (ImGui::MenuItem("Reset symbol map")) {
2442
g_symbolMap->Clear();
2443
disasm_.DirtySymbolMap();
2444
// NotifyDebuggerMapLoaded();
2445
}
2446
ImGui::EndMenu();
2447
}
2448
if (ImGui::BeginMenu("Memory")) {
2449
for (int i = 0; i < 4; i++) {
2450
char title[64];
2451
snprintf(title, sizeof(title), "Memory %d", i + 1);
2452
ImGui::MenuItem(title, nullptr, &cfg_.memViewOpen[i]);
2453
}
2454
ImGui::MenuItem("Memory Dumper", nullptr, &cfg_.memDumpOpen);
2455
ImGui::EndMenu();
2456
}
2457
if (ImGui::BeginMenu("OS HLE")) {
2458
ImGui::MenuItem("HLE module browser", nullptr, &cfg_.hleModulesOpen);
2459
ImGui::MenuItem("File System Browser", nullptr, &cfg_.filesystemBrowserOpen);
2460
ImGui::MenuItem("Kernel Objects", nullptr, &cfg_.kernelObjectsOpen);
2461
ImGui::MenuItem("Threads", nullptr, &cfg_.threadsOpen);
2462
ImGui::MenuItem("Modules", nullptr, &cfg_.modulesOpen);
2463
ImGui::MenuItem("Utility Modules",nullptr, &cfg_.utilityModulesOpen);
2464
ImGui::EndMenu();
2465
}
2466
if (ImGui::BeginMenu("Graphics")) {
2467
ImGui::MenuItem("GE Debugger", nullptr, &cfg_.geDebuggerOpen);
2468
ImGui::MenuItem("GE State", nullptr, &cfg_.geStateOpen);
2469
ImGui::MenuItem("GE Vertices", nullptr, &cfg_.geVertsOpen);
2470
ImGui::MenuItem("Display Output", nullptr, &cfg_.displayOpen);
2471
ImGui::MenuItem("Textures", nullptr, &cfg_.texturesOpen);
2472
ImGui::MenuItem("Framebuffers", nullptr, &cfg_.framebuffersOpen);
2473
ImGui::MenuItem("Pixel Viewer", nullptr, &cfg_.pixelViewerOpen);
2474
// More to come here...
2475
ImGui::EndMenu();
2476
}
2477
if (ImGui::BeginMenu("Audio/Video")) {
2478
ImGui::MenuItem("SasAudio mixer", nullptr, &cfg_.sasAudioOpen);
2479
ImGui::MenuItem("Raw audio channels", nullptr, &cfg_.audioChannelsOpen);
2480
ImGui::MenuItem("AV Decoder contexts", nullptr, &cfg_.mediaDecodersOpen);
2481
ImGui::Separator();
2482
ImGui::MenuItem("Audio output / buffer", nullptr, &cfg_.audioOutOpen);
2483
ImGui::EndMenu();
2484
}
2485
if (ImGui::BeginMenu("Network")) {
2486
ImGui::MenuItem("ApCtl", nullptr, &cfg_.apctlOpen);
2487
ImGui::MenuItem("Sockets", nullptr, &cfg_.socketsOpen);
2488
ImGui::MenuItem("NP", nullptr, &cfg_.npOpen);
2489
ImGui::MenuItem("AdHoc", nullptr, &cfg_.adhocOpen);
2490
ImGui::EndMenu();
2491
}
2492
if (ImGui::BeginMenu("Tools")) {
2493
ImGui::MenuItem("Lua Console", nullptr, &cfg_.luaConsoleOpen);
2494
ImGui::MenuItem("Log", nullptr, &cfg_.logOpen);
2495
ImGui::MenuItem("Log channels", nullptr, &cfg_.logConfigOpen);
2496
ImGui::MenuItem("Debug stats", nullptr, &cfg_.debugStatsOpen);
2497
ImGui::Separator();
2498
ImGui::MenuItem("ParamSFO viewer", nullptr, &cfg_.paramSFOOpen);
2499
ImGui::MenuItem("Struct viewer", nullptr, &cfg_.structViewerOpen);
2500
ImGui::MenuItem("Atrac Tool", nullptr, &cfg_.atracToolOpen);
2501
ImGui::EndMenu();
2502
}
2503
if (ImGui::BeginMenu("Misc")) {
2504
ImGui::MenuItem("PPSSPP Internals", nullptr, &cfg_.internalsOpen);
2505
ImGui::MenuItem("Dear ImGui Demo", nullptr, &cfg_.demoOpen);
2506
ImGui::MenuItem("Dear ImGui Style editor", nullptr, &cfg_.styleEditorOpen);
2507
ImGui::EndMenu();
2508
}
2509
if (ImGui::MenuItem("Close")) {
2510
g_Config.bShowImDebugger = false;
2511
}
2512
switch (coreState) {
2513
case CoreState::CORE_STEPPING_CPU:
2514
if (ImGui::MenuItem(">> Run")) {
2515
Core_Resume();
2516
}
2517
break;
2518
case CoreState::CORE_RUNNING_CPU:
2519
if (ImGui::MenuItem("|| Break")) {
2520
Core_Break(BreakReason::DebugBreak);
2521
}
2522
break;
2523
default:
2524
break;
2525
}
2526
ImGui::EndMainMenuBar();
2527
}
2528
2529
if (cfg_.demoOpen) {
2530
ImGui::ShowDemoWindow(&cfg_.demoOpen);
2531
}
2532
2533
if (cfg_.styleEditorOpen) {
2534
ImGui::ShowStyleEditor();
2535
}
2536
2537
if (cfg_.disasmOpen) {
2538
disasm_.Draw(mipsDebug, cfg_, control, coreState);
2539
}
2540
2541
if (cfg_.gprOpen) {
2542
DrawGPRs(cfg_, control, mipsDebug, snapshot_);
2543
}
2544
2545
if (cfg_.fprOpen) {
2546
DrawFPRs(cfg_, control, mipsDebug, snapshot_);
2547
}
2548
2549
if (cfg_.vfpuOpen) {
2550
DrawVFPU(cfg_, control, mipsDebug, snapshot_);
2551
}
2552
2553
if (cfg_.breakpointsOpen) {
2554
DrawBreakpointsView(mipsDebug, cfg_);
2555
}
2556
2557
if (cfg_.filesystemBrowserOpen) {
2558
DrawFilesystemBrowser(cfg_);
2559
}
2560
2561
if (cfg_.audioChannelsOpen) {
2562
DrawAudioChannels(cfg_, control);
2563
}
2564
2565
if (cfg_.audioOutOpen) {
2566
DrawAudioOut(cfg_, control);
2567
}
2568
2569
if (cfg_.sasAudioOpen) {
2570
DrawSasAudio(cfg_);
2571
}
2572
2573
if (cfg_.kernelObjectsOpen) {
2574
DrawKernelObjects(cfg_);
2575
}
2576
2577
if (cfg_.threadsOpen) {
2578
DrawThreadView(cfg_, control);
2579
}
2580
2581
if (cfg_.callstackOpen) {
2582
DrawCallStacks(mipsDebug, cfg_, control);
2583
}
2584
2585
if (cfg_.modulesOpen) {
2586
DrawModules(mipsDebug, cfg_, control);
2587
}
2588
2589
if (cfg_.symbolsOpen) {
2590
DrawSymbols(mipsDebug, cfg_, control);
2591
}
2592
2593
if (cfg_.utilityModulesOpen) {
2594
DrawUtilityModules(cfg_, control);
2595
}
2596
2597
if (cfg_.mediaDecodersOpen) {
2598
DrawMediaDecodersView(cfg_, control);
2599
}
2600
2601
if (cfg_.hleModulesOpen) {
2602
DrawHLEModules(cfg_);
2603
}
2604
2605
if (cfg_.atracToolOpen) {
2606
atracToolWindow_.Draw(cfg_);
2607
}
2608
2609
if (cfg_.framebuffersOpen) {
2610
DrawFramebuffersWindow(cfg_, gpuDebug->GetFramebufferManagerCommon());
2611
}
2612
2613
if (cfg_.texturesOpen) {
2614
DrawTexturesWindow(cfg_, gpuDebug->GetTextureCacheCommon());
2615
}
2616
2617
if (cfg_.logConfigOpen) {
2618
DrawLogConfig(cfg_);
2619
}
2620
2621
if (cfg_.logOpen) {
2622
logWindow_.Draw(cfg_);
2623
}
2624
2625
if (cfg_.jitViewerOpen) {
2626
jitViewer_.Draw(cfg_, control);
2627
}
2628
2629
if (cfg_.displayOpen) {
2630
DrawDisplayWindow(cfg_, gpuDebug->GetFramebufferManagerCommon());
2631
}
2632
2633
if (cfg_.debugStatsOpen) {
2634
DrawDebugStatsWindow(cfg_);
2635
}
2636
2637
if (cfg_.structViewerOpen) {
2638
structViewer_.Draw(cfg_, control, mipsDebug);
2639
}
2640
2641
if (cfg_.paramSFOOpen) {
2642
DrawParamSFO(cfg_, control);
2643
}
2644
2645
if (cfg_.geDebuggerOpen) {
2646
geDebugger_.Draw(cfg_, control, gpuDebug, draw);
2647
}
2648
2649
if (cfg_.geStateOpen) {
2650
geStateWindow_.Draw(cfg_, control, gpuDebug);
2651
}
2652
2653
if (cfg_.geVertsOpen) {
2654
DrawImGeVertsWindow(cfg_, control, gpuDebug);
2655
}
2656
2657
if (cfg_.schedulerOpen) {
2658
DrawSchedulerView(cfg_);
2659
}
2660
2661
if (cfg_.timeOpen) {
2662
DrawTimeView(cfg_);
2663
}
2664
2665
if (cfg_.pixelViewerOpen) {
2666
pixelViewer_.Draw(cfg_, control, gpuDebug, draw);
2667
}
2668
2669
if (cfg_.memDumpOpen) {
2670
memDumpWindow_.Draw(cfg_, mipsDebug);
2671
}
2672
2673
if (cfg_.watchOpen) {
2674
watchWindow_.Draw(cfg_, control, mipsDebug);
2675
}
2676
2677
for (int i = 0; i < 4; i++) {
2678
if (cfg_.memViewOpen[i]) {
2679
mem_[i].Draw(mipsDebug, cfg_, control, i);
2680
}
2681
}
2682
2683
if (cfg_.socketsOpen) {
2684
DrawSockets(cfg_);
2685
}
2686
2687
if (cfg_.npOpen) {
2688
DrawNp(cfg_);
2689
}
2690
2691
if (cfg_.adhocOpen) {
2692
DrawAdhoc(cfg_);
2693
}
2694
2695
if (cfg_.apctlOpen) {
2696
DrawApctl(cfg_);
2697
}
2698
2699
if (cfg_.internalsOpen) {
2700
DrawInternals(cfg_);
2701
}
2702
2703
if (externalCommand_.cmd != ImCmd::NONE) {
2704
control.command = externalCommand_;
2705
externalCommand_.cmd = ImCmd::NONE;
2706
}
2707
2708
// Process UI commands
2709
switch (control.command.cmd) {
2710
case ImCmd::SHOW_IN_CPU_DISASM:
2711
disasm_.View().gotoAddr(control.command.param);
2712
cfg_.disasmOpen = true;
2713
ImGui::SetWindowFocus(disasm_.Title());
2714
break;
2715
case ImCmd::SHOW_IN_GE_DISASM:
2716
geDebugger_.View().GotoAddr(control.command.param);
2717
cfg_.geDebuggerOpen = true;
2718
ImGui::SetWindowFocus(geDebugger_.Title());
2719
break;
2720
case ImCmd::SHOW_IN_MEMORY_VIEWER:
2721
{
2722
u32 index = control.command.param2;
2723
_dbg_assert_(index < 4);
2724
mem_[index].GotoAddr(control.command.param);
2725
cfg_.memViewOpen[index] = true;
2726
ImGui::SetWindowFocus(ImMemWindow::Title(index));
2727
break;
2728
}
2729
case ImCmd::SHOW_IN_MEMORY_DUMPER:
2730
{
2731
cfg_.memDumpOpen = true;
2732
memDumpWindow_.SetRange(control.command.param, control.command.param2, (MemDumpMode)control.command.param3);
2733
ImGui::SetWindowFocus(memDumpWindow_.Title());
2734
break;
2735
}
2736
case ImCmd::TRIGGER_FIND_POPUP:
2737
// TODO
2738
break;
2739
case ImCmd::NONE:
2740
break;
2741
case ImCmd::SHOW_IN_PIXEL_VIEWER:
2742
break;
2743
}
2744
if (cfg_.luaConsoleOpen) {
2745
luaConsole_.Draw(cfg_);
2746
}
2747
}
2748
2749
void ImDebugger::Snapshot(MIPSState *mips) {
2750
memcpy(newSnapshot_.gpr, mips->r, sizeof(newSnapshot_.gpr));
2751
memcpy(newSnapshot_.fpr, mips->fs, sizeof(newSnapshot_.fpr));
2752
memcpy(newSnapshot_.vpr, mips->v, sizeof(newSnapshot_.vpr));
2753
newSnapshot_.pc = mips->pc;
2754
newSnapshot_.lo = mips->lo;
2755
newSnapshot_.hi = mips->hi;
2756
newSnapshot_.ll = mips->llBit;
2757
pixelViewer_.Snapshot();
2758
}
2759
2760
void ImDebugger::SnapshotGPU(GPUDebugInterface *gpuDebug) {
2761
pixelViewer_.Snapshot();
2762
}
2763
2764
void ImDebugger::DeviceLost() {
2765
pixelViewer_.DeviceLost();
2766
geDebugger_.DeviceLost();
2767
}
2768
2769
Path ImDebugger::ConfigPath() {
2770
return GetSysDirectory(DIRECTORY_SYSTEM) / "imdebugger.ini";
2771
}
2772
2773
// TODO: Move this into the main config at some point.
2774
// But, I don't really want Core to know about the ImDebugger..
2775
2776
void ImConfig::LoadConfig(const Path &iniFile) {
2777
requesterToken = g_requestManager.GenerateRequesterToken();
2778
2779
IniFile ini;
2780
ini.Load(iniFile); // Ignore return value, might not exist yet. In that case we'll end up loading defaults.
2781
SyncConfig(&ini, false);
2782
}
2783
2784
void ImConfig::SaveConfig(const Path &iniFile) {
2785
IniFile ini;
2786
ini.Load(iniFile); // ignore return value, might not exist yet
2787
SyncConfig(&ini, true);
2788
ini.Save(iniFile);
2789
}
2790
2791
class Syncer {
2792
public:
2793
explicit Syncer(bool save) : save_(save) {}
2794
void SetSection(Section *section) { section_ = section; }
2795
template<class T>
2796
void Sync(std::string_view key, T *value, T defaultValue) {
2797
if (save_) {
2798
section_->Set(key, *value);
2799
} else {
2800
if (!section_->Get(key, value)) {
2801
*value = defaultValue;
2802
}
2803
}
2804
}
2805
private:
2806
Section *section_ = nullptr;
2807
bool save_;
2808
};
2809
2810
void ImConfig::SyncConfig(IniFile *ini, bool save) {
2811
Syncer sync(save);
2812
sync.SetSection(ini->GetOrCreateSection("Windows"));
2813
sync.Sync("disasmOpen", &disasmOpen, true);
2814
sync.Sync("demoOpen", &demoOpen, false);
2815
sync.Sync("gprOpen", &gprOpen, false);
2816
sync.Sync("fprOpen", &fprOpen, false);
2817
sync.Sync("vfpuOpen", &vfpuOpen, false);
2818
sync.Sync("threadsOpen", &threadsOpen, false);
2819
sync.Sync("callstackOpen", &callstackOpen, false);
2820
sync.Sync("breakpointsOpen", &breakpointsOpen, false);
2821
sync.Sync("symbolsOpen", &symbolsOpen, false);
2822
sync.Sync("modulesOpen", &modulesOpen, false);
2823
sync.Sync("hleModulesOpen", &hleModulesOpen, false);
2824
sync.Sync("mediaDecodersOpen", &mediaDecodersOpen, false);
2825
sync.Sync("structViewerOpen", &structViewerOpen, false);
2826
sync.Sync("framebuffersOpen", &framebuffersOpen, false);
2827
sync.Sync("displayOpen", &displayOpen, true);
2828
sync.Sync("styleEditorOpen", &styleEditorOpen, false);
2829
sync.Sync("filesystemBrowserOpen", &filesystemBrowserOpen, false);
2830
sync.Sync("kernelObjectsOpen", &kernelObjectsOpen, false);
2831
sync.Sync("audioChannelsOpen", &audioChannelsOpen, false);
2832
sync.Sync("audioOutOpen", &audioOutOpen, false);
2833
sync.Sync("texturesOpen", &texturesOpen, false);
2834
sync.Sync("debugStatsOpen", &debugStatsOpen, false);
2835
sync.Sync("geDebuggerOpen", &geDebuggerOpen, false);
2836
sync.Sync("geStateOpen", &geStateOpen, false);
2837
sync.Sync("geVertsOpen", &geVertsOpen, false);
2838
sync.Sync("schedulerOpen", &schedulerOpen, false);
2839
sync.Sync("timeOpen", &timeOpen, false);
2840
sync.Sync("socketsOpen", &socketsOpen, false);
2841
sync.Sync("npOpen", &npOpen, false);
2842
sync.Sync("adhocOpen", &adhocOpen, false);
2843
sync.Sync("apctlOpen", &apctlOpen, false);
2844
sync.Sync("pixelViewerOpen", &pixelViewerOpen, false);
2845
sync.Sync("internalsOpen", &internalsOpen, false);
2846
sync.Sync("sasAudioOpen", &sasAudioOpen, false);
2847
sync.Sync("logConfigOpen", &logConfigOpen, false);
2848
sync.Sync("logOpen", &logOpen, false);
2849
sync.Sync("luaConsoleOpen", &luaConsoleOpen, false);
2850
sync.Sync("utilityModulesOpen", &utilityModulesOpen, false);
2851
sync.Sync("memDumpOpen", &memDumpOpen, false);
2852
sync.Sync("watchOpen", &watchOpen, false);
2853
sync.Sync("paramSFOOpen", &paramSFOOpen, false);
2854
sync.Sync("atracToolOpen", &atracToolOpen, false);
2855
sync.Sync("jitViewerOpen", &jitViewerOpen, false);
2856
for (int i = 0; i < 4; i++) {
2857
char name[64];
2858
snprintf(name, sizeof(name), "memory%dOpen", i + 1);
2859
sync.Sync(name, &memViewOpen[i], false);
2860
}
2861
2862
sync.SetSection(ini->GetOrCreateSection("Settings"));
2863
sync.Sync("displayLatched", &displayLatched, false);
2864
sync.Sync("realtimePixelPreview", &realtimePixelPreview, false);
2865
}
2866
2867