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