mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-08-31 17:55:23 +02:00
AddLabel() won't overwrite an existing label. That's deliberate and right for bulk import - a real ELF symbol name shouldn't lose to the analyzer's later z_un_* - but wrong when someone is explicitly naming an address, so a second hle.data.add at the same address silently kept the old name. The response echoed the requested name back either way, so there was no sign anything had been ignored. Force the requested name in with SetLabelName() now, except when a function starts at that address and owns the label - renaming that function isn't what "label this data" should mean, and it would undo the care hle.data.remove takes not to destroy it. Either way the response now reports the name the symbol actually ended up with rather than the one that was asked for. Also, in the ImDebugger memcheck edit form: the Enabled checkbox didn't mark the memcheck as changed, and the condition combo marked it changed on every frame the popup was open rather than when a condition was actually picked (Selectable returns true only on click, BeginCombo stays true while open). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GZq8ZtJmFY7bkX5FVkr3P9
3072 lines
101 KiB
C++
3072 lines
101 KiB
C++
#include <algorithm>
|
|
|
|
|
|
#include "ext/imgui/imgui_internal.h"
|
|
#include "ext/imgui/imgui_extras.h"
|
|
|
|
#include "Common/StringUtils.h"
|
|
#include "Common/File/FileUtil.h"
|
|
#include "Common/Data/Format/IniFile.h"
|
|
#include "Common/Data/Text/StringWriter.h"
|
|
#include "Common/Log/LogManager.h"
|
|
#include "Common/TimeUtil.h"
|
|
#include "Common/Data/Text/I18n.h"
|
|
#include "Core/RetroAchievements.h"
|
|
#include "Core/Config.h"
|
|
#include "Core/System.h"
|
|
#include "Core/SaveState.h"
|
|
#include "Core/WebServer.h"
|
|
#include "Core/Debugger/MemBlockInfo.h"
|
|
#include "Core/Core.h"
|
|
#include "Core/Debugger/DebugInterface.h"
|
|
#include "Core/Debugger/DisassemblyManager.h"
|
|
#include "Core/Debugger/Breakpoints.h"
|
|
#include "Core/MIPS/MIPSDebugInterface.h"
|
|
#include "Core/MIPS/MIPSTables.h"
|
|
#include "Core/HW/SimpleAudioDec.h"
|
|
#include "Core/FileSystems/MetaFileSystem.h"
|
|
#include "Core/Debugger/SymbolMap.h"
|
|
#include "Core/MemMap.h"
|
|
#include "Core/HLE/HLE.h"
|
|
#include "Core/HLE/SocketManager.h"
|
|
#include "Core/HLE/NetInetConstants.h"
|
|
#include "Core/HLE/sceKernelModule.h"
|
|
#include "Core/HLE/sceAac.h"
|
|
#include "Core/HLE/sceMpeg.h"
|
|
#include "Core/HLE/sceNp.h"
|
|
#include "Core/HLE/sceNet.h"
|
|
#include "Core/HLE/sceNetApctl.h"
|
|
#include "Core/HLE/sceNetAdhoc.h"
|
|
#include "Core/HLE/proAdhoc.h"
|
|
#include "Core/HLE/sceNetAdhocMatching.h"
|
|
#include "Core/HLE/NetAdhocCommon.h"
|
|
#include "Common/System/Request.h"
|
|
|
|
#include "Core/Util/AtracTrack.h"
|
|
#include "Core/HLE/sceAtrac.h"
|
|
#include "Core/HLE/sceAudio.h"
|
|
#include "Core/HLE/sceAudiocodec.h"
|
|
#include "Core/HLE/sceMp3.h"
|
|
#include "Core/HLE/AtracCtx.h"
|
|
#include "Core/HLE/sceSas.h"
|
|
#include "Core/HW/SasAudio.h"
|
|
#include "Core/HW/Display.h"
|
|
#include "Core/Dialog/PSPSaveDialog.h"
|
|
|
|
#include "Core/CoreTiming.h"
|
|
// Threads window
|
|
#include "Core/HLE/sceKernelThread.h"
|
|
|
|
// Callstack window
|
|
#include "Core/MIPS/MIPSStackWalk.h"
|
|
|
|
// GPU things
|
|
#include "GPU/GPUCommon.h"
|
|
#include "GPU/Debugger/Stepping.h"
|
|
|
|
#include "UI/ImDebugger/ImDebugger.h"
|
|
#include "UI/ImDebugger/ImGe.h"
|
|
#include "UI/AudioCommon.h"
|
|
#include "UI/GameInfoCache.h"
|
|
#include "UI/PauseScreen.h"
|
|
|
|
extern bool g_TakeScreenshot;
|
|
static ImVec4 g_normalTextColor;
|
|
|
|
void ShowInMemoryViewerMenuItem(uint32_t addr, ImControl &control) {
|
|
if (ImGui::BeginMenu("Show in memory viewer")) {
|
|
for (int i = 0; i < 4; i++) {
|
|
if (ImGui::MenuItem(ImMemWindow::Title(i))) {
|
|
control.command = { ImCmd::SHOW_IN_MEMORY_VIEWER, addr, (u32)i };
|
|
}
|
|
}
|
|
ImGui::EndMenu();
|
|
}
|
|
}
|
|
|
|
void ShowInMemoryDumperMenuItem(uint32_t addr, uint32_t size, MemDumpMode mode, ImControl &control) {
|
|
if (ImGui::MenuItem(mode == MemDumpMode::Raw ? "Dump bytes to file..." : "Disassemble to file...")) {
|
|
control.command = { ImCmd::SHOW_IN_MEMORY_DUMPER, addr, size, (u32)mode};
|
|
}
|
|
}
|
|
|
|
void ShowInWindowMenuItems(uint32_t addr, ImControl &control) {
|
|
// Enable when we implement the memory viewer
|
|
ShowInMemoryViewerMenuItem(addr, control);
|
|
if (ImGui::MenuItem("Show in CPU debugger")) {
|
|
control.command = { ImCmd::SHOW_IN_CPU_DISASM, addr };
|
|
}
|
|
if (ImGui::MenuItem("Show in GE debugger")) {
|
|
control.command = { ImCmd::SHOW_IN_GE_DISASM, addr };
|
|
}
|
|
}
|
|
|
|
void StatusBar(std::string_view status) {
|
|
if (!status.size()) {
|
|
return;
|
|
}
|
|
ImGui::TextUnformatted(status.data(), status.data() + status.length());
|
|
ImGui::SameLine();
|
|
if (ImGui::SmallButton("Copy")) {
|
|
System_CopyStringToClipboard(status);
|
|
}
|
|
}
|
|
|
|
// TODO: Style it more
|
|
// Left click performs the preferred action, if any. Right click opens a menu for more.
|
|
void ImClickableValue(const char *id, uint32_t value, ImControl &control, ImCmd cmd) {
|
|
ImGui::PushID(id);
|
|
|
|
bool validAddr = Memory::IsValidAddress(value);
|
|
|
|
char temp[32];
|
|
snprintf(temp, sizeof(temp), "%08x", value);
|
|
if (ImGui::Selectable(temp) && validAddr) {
|
|
control.command = { cmd, value };
|
|
}
|
|
|
|
// Create a right-click popup menu. Restore the color while it's up. NOTE: can't query the theme, pushcolor modifies it!
|
|
ImGui::PushStyleColor(ImGuiCol_Text, g_normalTextColor);
|
|
if (ImGui::BeginPopupContextItem(temp)) {
|
|
if (ImGui::MenuItem(validAddr ? "Copy address to clipboard" : "Copy value to clipboard")) {
|
|
System_CopyStringToClipboard(temp);
|
|
}
|
|
if (validAddr) {
|
|
ImGui::Separator();
|
|
ShowInWindowMenuItems(value, control);
|
|
}
|
|
ImGui::EndPopup();
|
|
}
|
|
ImGui::PopStyleColor();
|
|
ImGui::PopID();
|
|
}
|
|
|
|
// Left click performs the preferred action, if any. Right click opens a menu for more.
|
|
void ImClickableValueFloat(const char *id, float value) {
|
|
ImGui::PushID(id);
|
|
|
|
char temp[32];
|
|
snprintf(temp, sizeof(temp), "%0.7f", value);
|
|
if (ImGui::Selectable(temp)) {}
|
|
|
|
// Create a right-click popup menu. Restore the color while it's up. NOTE: can't query the theme, pushcolor modifies it!
|
|
ImGui::PushStyleColor(ImGuiCol_Text, g_normalTextColor);
|
|
if (ImGui::BeginPopupContextItem(temp)) {
|
|
if (ImGui::MenuItem("Copy value to clipboard")) {
|
|
System_CopyStringToClipboard(temp);
|
|
}
|
|
ImGui::EndPopup();
|
|
}
|
|
ImGui::PopStyleColor();
|
|
ImGui::PopID();
|
|
}
|
|
|
|
void DrawTimeView(ImConfig &cfg) {
|
|
ImGui::SetNextWindowSize(ImVec2(420, 300), ImGuiCond_FirstUseEver);
|
|
if (!ImGui::Begin("Time", &cfg.timeOpen)) {
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
// Display timing
|
|
if (ImGui::CollapsingHeader("Display Timing", ImGuiTreeNodeFlags_DefaultOpen)) {
|
|
ImGui::Text("Num VBlanks: %d", __DisplayGetNumVblanks());
|
|
ImGui::Text("FlipCount: %d", __DisplayGetFlipCount());
|
|
ImGui::Text("VCount: %d", __DisplayGetVCount());
|
|
ImGui::Text("HCount cur: %d accum: %d", __DisplayGetCurrentHcount(), __DisplayGetAccumulatedHcount());
|
|
ImGui::Text("IsVblank: %d", DisplayIsVblank());
|
|
}
|
|
|
|
// RTC
|
|
if (ImGui::CollapsingHeader("RTC", ImGuiTreeNodeFlags_DefaultOpen)) {
|
|
PSPTimeval tv;
|
|
__RtcTimeOfDay(&tv);
|
|
ImGui::Text("RtcTimeOfDay: %d.%06d", tv.tv_sec, tv.tv_usec);
|
|
ImGui::Text("RtcGetCurrentTick: %lld", (long long)__RtcGetCurrentTick());
|
|
}
|
|
|
|
ImGui::End();
|
|
}
|
|
|
|
void DrawSchedulerView(ImConfig &cfg) {
|
|
ImGui::SetNextWindowSize(ImVec2(420, 300), ImGuiCond_FirstUseEver);
|
|
if (!ImGui::Begin("Event Scheduler", &cfg.schedulerOpen)) {
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
s64 ticks = CoreTiming::GetTicks(currentMIPS);
|
|
if (ImGui::BeginChild("event_list", ImVec2(300.0f, 0.0))) {
|
|
const CoreTiming::Event *event = CoreTiming::GetFirstEvent();
|
|
while (event) {
|
|
ImGui::Text("%s (%lld): %d", CoreTiming::GetEventTypes()[event->type].name, event->time - ticks, (int)event->userdata);
|
|
event = event->next;
|
|
}
|
|
ImGui::EndChild();
|
|
}
|
|
ImGui::SameLine();
|
|
if (ImGui::BeginChild("general")) {
|
|
ImGui::Text("CoreState: %s", CoreStateToString(coreState));
|
|
ImGui::Text("downcount: %d", currentMIPS->downcount);
|
|
ImGui::Text("slicelength: %d", CoreTiming::slicelength);
|
|
ImGui::Text("Ticks: %lld", ticks);
|
|
ImGui::Text("Clock (MHz): %0.1f", (float)CoreTiming::GetClockFrequencyHz() / 1000000.0f);
|
|
ImGui::Text("Global time (us): %lld", CoreTiming::GetGlobalTimeUs());
|
|
ImGui::EndChild();
|
|
}
|
|
ImGui::End();
|
|
}
|
|
|
|
static void DescribeGPRValue(const MIPSDebugInterface *mipsDebug, u32 value, char *buffer, size_t bufferSize) {
|
|
if (Memory::IsValidAddress(value)) {
|
|
DescribeAddress(mipsDebug, value, buffer, bufferSize);
|
|
} else {
|
|
snprintf(buffer, bufferSize, "(value)");
|
|
}
|
|
}
|
|
|
|
static void DrawGPRs(ImConfig &config, ImControl &control, const MIPSDebugInterface *mipsDebug, const ImSnapshotState &prev) {
|
|
ImGui::SetNextWindowSize(ImVec2(320, 600), ImGuiCond_FirstUseEver);
|
|
if (!ImGui::Begin("GPRs", &config.gprOpen)) {
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
bool noDiff = coreState == CORE_RUNNING_CPU || coreState == CORE_STEPPING_GE;
|
|
|
|
if (ImGui::Button("Copy all to clipboard")) {
|
|
char *buffer = new char[20000];
|
|
StringWriter w(buffer, 20000);
|
|
for (int i = 0; i < 32; i++) {
|
|
u32 value = mipsDebug->GetGPR32Value(i);
|
|
w.F("%s: %08x (%d)", mipsDebug->GetRegName(0, i).c_str(), value, value).endl();
|
|
}
|
|
w.F("hi: %08x", mipsDebug->GetHi()).endl();
|
|
w.F("lo: %08x", mipsDebug->GetLo()).endl();
|
|
w.F("pc: %08x", mipsDebug->GetPC()).endl();
|
|
System_CopyStringToClipboard(buffer);
|
|
delete[] buffer;
|
|
}
|
|
|
|
if (ImGui::BeginTable("gpr", 4, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) {
|
|
ImGui::TableSetupColumn("Reg", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Dec", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Desc", ImGuiTableColumnFlags_WidthStretch);
|
|
|
|
ImGui::TableHeadersRow();
|
|
|
|
auto gprLine = [&](int index, const char *regname, int value, int prevValue) {
|
|
bool diff = value != prevValue && !noDiff;
|
|
bool disabled = value == 0xdeadbeef;
|
|
|
|
ImGui::TableNextColumn();
|
|
ImGui::TextUnformatted(regname);
|
|
ImGui::TableNextColumn();
|
|
if (diff) {
|
|
ImGui::PushStyleColor(ImGuiCol_Text, !disabled ? ImDebuggerColor_Diff : ImDebuggerColor_DiffAlpha);
|
|
} else if (disabled) {
|
|
ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(255, 255, 255, 128));
|
|
}
|
|
// TODO: Check if the address is in the code segment to decide default action.
|
|
ImClickableValue(regname, value, control, index == MIPS_REG_RA ? ImCmd::SHOW_IN_CPU_DISASM : ImCmd::SHOW_IN_MEMORY_VIEWER);
|
|
ImGui::TableNextColumn();
|
|
if (value >= -1000000 && value <= 1000000) {
|
|
ImGui::Text("%d", value);
|
|
}
|
|
ImGui::TableNextColumn();
|
|
char temp[256];
|
|
DescribeGPRValue(mipsDebug, value, temp, sizeof(temp));
|
|
ImGui::TextUnformatted(temp);
|
|
if (diff || disabled) {
|
|
ImGui::PopStyleColor();
|
|
}
|
|
};
|
|
for (int i = 0; i < 32; i++) {
|
|
ImGui::TableNextRow();
|
|
gprLine(i, mipsDebug->GetRegName(0, i).c_str(), mipsDebug->GetGPR32Value(i), prev.gpr[i]);
|
|
}
|
|
ImGui::TableNextRow();
|
|
gprLine(32, "hi", mipsDebug->GetHi(), prev.hi);
|
|
ImGui::TableNextRow();
|
|
gprLine(33, "lo", mipsDebug->GetLo(), prev.lo);
|
|
ImGui::TableNextRow();
|
|
gprLine(34, "pc", mipsDebug->GetPC(), prev.pc);
|
|
ImGui::TableNextRow();
|
|
gprLine(35, "ll", mipsDebug->GetLLBit(), prev.ll);
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::End();
|
|
}
|
|
|
|
static void DrawFPRs(ImConfig &config, ImControl &control, const MIPSDebugInterface *mipsDebug, const ImSnapshotState &prev) {
|
|
ImGui::SetNextWindowSize(ImVec2(320, 600), ImGuiCond_FirstUseEver);
|
|
if (!ImGui::Begin("FPRs", &config.fprOpen)) {
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
bool noDiff = coreState == CORE_RUNNING_CPU || coreState == CORE_STEPPING_GE;
|
|
|
|
if (ImGui::BeginTable("fpr", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) {
|
|
ImGui::TableSetupColumn("Reg", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Hex", ImGuiTableColumnFlags_WidthStretch);
|
|
|
|
ImGui::TableHeadersRow();
|
|
|
|
// fpcond
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
ImGui::TextUnformatted("fpcond");
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%08x", mipsDebug->GetFPCond());
|
|
|
|
for (int i = 0; i < 32; i++) {
|
|
float fvalue = mipsDebug->GetFPR32Value(i);
|
|
float prevValue = prev.fpr[i];
|
|
|
|
// NOTE: Using memcmp to avoid NaN problems.
|
|
bool diff = memcmp(&fvalue, &prevValue, 4) != 0 && !noDiff;
|
|
|
|
u32 fivalue;
|
|
memcpy(&fivalue, &fvalue, sizeof(fivalue));
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
if (diff) {
|
|
ImGui::PushStyleColor(ImGuiCol_Text, ImDebuggerColor_Diff);
|
|
}
|
|
ImGui::TextUnformatted(mipsDebug->GetRegName(1, i).c_str());
|
|
ImGui::TableNextColumn();
|
|
ImClickableValueFloat(mipsDebug->GetRegName(1, i).c_str(), fvalue);
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%08x", fivalue);
|
|
if (diff) {
|
|
ImGui::PopStyleColor();
|
|
}
|
|
}
|
|
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::End();
|
|
}
|
|
|
|
static void DrawVFPU(ImConfig &config, ImControl &control, const MIPSDebugInterface *mipsDebug, const ImSnapshotState &prev) {
|
|
ImGui::SetNextWindowSize(ImVec2(320, 600), ImGuiCond_FirstUseEver);
|
|
if (!ImGui::Begin("VFPU", &config.vfpuOpen)) {
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
ImGui::Text("TODO");
|
|
ImGui::End();
|
|
}
|
|
|
|
void WaitIDToString(WaitType waitType, SceUID waitID, char *buffer, size_t bufSize) {
|
|
switch (waitType) {
|
|
case WAITTYPE_AUDIOCHANNEL:
|
|
snprintf(buffer, bufSize, "chan %d", (int)waitID);
|
|
return;
|
|
case WAITTYPE_IO:
|
|
// TODO: More detail
|
|
snprintf(buffer, bufSize, "fd: %d", (int)waitID);
|
|
return;
|
|
case WAITTYPE_ASYNCIO:
|
|
snprintf(buffer, bufSize, "id: %d", (int)waitID);
|
|
return;
|
|
case WAITTYPE_THREADEND:
|
|
case WAITTYPE_MUTEX:
|
|
case WAITTYPE_LWMUTEX:
|
|
case WAITTYPE_MODULE:
|
|
case WAITTYPE_MSGPIPE:
|
|
case WAITTYPE_FPL:
|
|
case WAITTYPE_VPL:
|
|
case WAITTYPE_MBX:
|
|
case WAITTYPE_EVENTFLAG:
|
|
case WAITTYPE_SEMA:
|
|
// Get the name of the thread
|
|
if (kernelObjects.IsValid(waitID)) {
|
|
auto obj = kernelObjects.GetFast<KernelObject>(waitID);
|
|
if (obj && obj->GetName()) {
|
|
truncate_cpy(buffer, bufSize, obj->GetName());
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
case WAITTYPE_DELAY:
|
|
case WAITTYPE_SLEEP:
|
|
case WAITTYPE_HLEDELAY:
|
|
case WAITTYPE_UMD:
|
|
case WAITTYPE_NONE:
|
|
case WAITTYPE_VBLANK:
|
|
case WAITTYPE_MICINPUT:
|
|
truncate_cpy(buffer, bufSize, "-");
|
|
return;
|
|
case WAITTYPE_CTRL:
|
|
snprintf(buffer, bufSize, "ctrl: %d", waitID);
|
|
return;
|
|
default:
|
|
truncate_cpy(buffer, bufSize, "(unimpl)");
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
void DrawThreadView(ImConfig &cfg, ImControl &control) {
|
|
ImGui::SetNextWindowSize(ImVec2(420, 300), ImGuiCond_FirstUseEver);
|
|
if (!ImGui::Begin("Threads", &cfg.threadsOpen)) {
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
std::vector<DebugThreadInfo> info = GetThreadsInfo();
|
|
if (ImGui::BeginTable("threads", 8, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) {
|
|
ImGui::TableSetupColumn("Id", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("PC", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Entry", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Priority", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("State", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Wait Type", ImGuiTableColumnFlags_WidthStretch);
|
|
ImGui::TableSetupColumn("Wait ID", ImGuiTableColumnFlags_WidthStretch);
|
|
// .initialStack, .stackSize, etc
|
|
ImGui::TableHeadersRow();
|
|
|
|
for (int i = 0; i < (int)info.size(); i++) {
|
|
const DebugThreadInfo &thread = info[i];
|
|
ImGui::TableNextRow();
|
|
ImGui::PushID(i);
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%d", thread.id);
|
|
ImGui::TableNextColumn();
|
|
if (ImGui::Selectable(thread.name, cfg.selectedThread == i, ImGuiSelectableFlags_AllowDoubleClick | ImGuiSelectableFlags_SpanAllColumns)) {
|
|
cfg.selectedThread = i;
|
|
}
|
|
if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
|
|
cfg.selectedThread = i;
|
|
ImGui::OpenPopup("threadPopup");
|
|
}
|
|
ImGui::TableNextColumn();
|
|
ImClickableValue("curpc", thread.curPC, control, ImCmd::SHOW_IN_CPU_DISASM);
|
|
ImGui::TableNextColumn();
|
|
ImClickableValue("entry", thread.entrypoint, control, ImCmd::SHOW_IN_CPU_DISASM);
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%d", thread.priority);
|
|
ImGui::TableNextColumn();
|
|
ImGui::TextUnformatted(ThreadStatusToString(thread.status));
|
|
ImGui::TableNextColumn();
|
|
ImGui::TextUnformatted(WaitTypeToString(thread.waitType));
|
|
ImGui::TableNextColumn();
|
|
char temp[64];
|
|
WaitIDToString(thread.waitType, thread.waitID, temp, sizeof(temp));
|
|
ImGui::TextUnformatted(temp);
|
|
if (ImGui::BeginPopup("threadPopup")) {
|
|
DebugThreadInfo &thread = info[i];
|
|
ImGui::Text("Thread: %s", thread.name);
|
|
if (ImGui::MenuItem("Copy entry to clipboard")) {
|
|
char temp[64];
|
|
snprintf(temp, sizeof(temp), "%08x", thread.entrypoint);
|
|
System_CopyStringToClipboard(temp);
|
|
}
|
|
if (ImGui::MenuItem("Copy thread PC to clipboard")) {
|
|
char temp[64];
|
|
snprintf(temp, sizeof(temp), "%08x", thread.curPC);
|
|
System_CopyStringToClipboard(temp);
|
|
}
|
|
if (ImGui::MenuItem("Kill thread")) {
|
|
// Dangerous!
|
|
sceKernelTerminateThread(thread.id);
|
|
}
|
|
if (thread.status == THREADSTATUS_WAIT) {
|
|
if (ImGui::MenuItem("Force run now")) {
|
|
__KernelResumeThreadFromWait(thread.id, 0);
|
|
}
|
|
}
|
|
ImGui::EndPopup();
|
|
}
|
|
ImGui::PopID();
|
|
}
|
|
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::End();
|
|
}
|
|
|
|
void DrawParamSFO(ImConfig &cfg, ImControl &control) {
|
|
ImGui::SetNextWindowSize(ImVec2(420, 300), ImGuiCond_FirstUseEver);
|
|
if (!ImGui::Begin("PARAMS.SFO", &cfg.paramSFOOpen)) {
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
auto renderParamSFOTable = [](ParamSFOData &data) {
|
|
const auto &map = data.Values();
|
|
if (System_GetPropertyBool(SYSPROP_HAS_TEXT_CLIPBOARD) && ImGui::Button("Copy to clipboard")) {
|
|
char buffer[4096];
|
|
StringWriter w(buffer);
|
|
for (auto &[key, value] : map) {
|
|
switch (value.type) {
|
|
case ParamSFOData::VT_UTF8:
|
|
w.F("%s: %s\n", key.c_str(), value.s_value.c_str());
|
|
break;
|
|
case ParamSFOData::VT_UTF8_SPE:
|
|
w.F("%s: %d raw bytes\n", key.c_str(), (int)value.s_value.size());
|
|
break;
|
|
case ParamSFOData::VT_INT:
|
|
w.F("%s: %d\n", key.c_str(), value.i_value);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
System_CopyStringToClipboard(w.as_view());
|
|
}
|
|
|
|
if (ImGui::BeginTable("paramsfo", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH | ImGuiTableFlags_Resizable)) {
|
|
ImGui::TableSetupColumn("Key", ImGuiTableColumnFlags_WidthFixed, 120.0f);
|
|
ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthFixed, 120.f);
|
|
ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed, 50.f);
|
|
ImGui::TableHeadersRow();
|
|
|
|
for (auto &[key, value] : map) {
|
|
ImGui::TableNextRow();
|
|
ImGui::PushID(key.c_str());
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%s", key.c_str());
|
|
ImGui::TableNextColumn();
|
|
switch (value.type) {
|
|
case ParamSFOData::VT_UTF8:
|
|
ImGui::Text("%s", value.s_value.c_str());
|
|
break;
|
|
case ParamSFOData::VT_UTF8_SPE:
|
|
ImGui::Text("%d raw bytes", (int)value.s_value.size());
|
|
break;
|
|
case ParamSFOData::VT_INT:
|
|
ImGui::Text("%d", value.i_value);
|
|
break;
|
|
default:
|
|
ImGui::TextUnformatted("N/A");
|
|
break;
|
|
}
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%s", ParamSFOData::ValueTypeToString(value.type));
|
|
ImGui::PopID();
|
|
}
|
|
|
|
ImGui::EndTable();
|
|
}
|
|
};
|
|
|
|
if (ImGui::BeginTabBar("ParamSFOTabs")) {
|
|
if (ImGui::BeginTabItem("Active")) {
|
|
renderParamSFOTable(g_paramSFO);
|
|
ImGui::EndTabItem();
|
|
}
|
|
if (ImGui::BeginTabItem("Original")) {
|
|
renderParamSFOTable(g_paramSFORaw);
|
|
ImGui::EndTabItem();
|
|
}
|
|
if (ImGui::BeginTabItem("GameInfo")) {
|
|
Path path = PSP_CoreParameter().fileToStart;
|
|
std::shared_ptr<GameInfo> info = g_gameInfoCache->GetInfo(nullptr, path, GameInfoFlags::PARAM_SFO);
|
|
|
|
if (info && ImGui::BeginTable("paramsfo", 2, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH | ImGuiTableFlags_Resizable)) {
|
|
ImGui::TableSetupColumn("Key", ImGuiTableColumnFlags_WidthFixed, 140.0f);
|
|
ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthFixed, 270.0f);
|
|
|
|
ImGui::TableHeadersRow();
|
|
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
ImGui::TextUnformatted("Title");
|
|
ImGui::TableNextColumn();
|
|
ImGui::TextUnformatted(info->GetTitle());
|
|
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
ImGui::TextUnformatted("Path");
|
|
ImGui::TableNextColumn();
|
|
ImGui::TextUnformatted(path.ToVisualString());
|
|
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
ImGui::TextUnformatted("Detected type");
|
|
ImGui::TableNextColumn();
|
|
ImGui::TextUnformatted(IdentifiedFileTypeToString(info->fileType));
|
|
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
ImGui::TextUnformatted("Detected region");
|
|
ImGui::TableNextColumn();
|
|
ImGui::TextUnformatted(GameRegionToString(info->region));
|
|
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
ImGui::TextUnformatted("HasConfig");
|
|
ImGui::TableNextColumn();
|
|
ImGui::TextUnformatted(BoolStr(info->hasConfig));
|
|
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::EndTabItem();
|
|
}
|
|
ImGui::EndTabBar();
|
|
}
|
|
|
|
ImGui::End();
|
|
}
|
|
|
|
// TODO: Add popup menu, export file, export dir, etc...
|
|
static void RecurseFileSystem(IFileSystem *fs, std::string path, RequesterToken token) {
|
|
std::vector<PSPFileInfo> fileInfo = fs->GetDirListing(path);
|
|
for (auto &file : fileInfo) {
|
|
if (file.type == FileType::FILETYPE_DIRECTORY) {
|
|
if (file.name != "." && file.name != ".." && ImGui::TreeNode(file.name.c_str())) {
|
|
std::string fpath = path + "/" + file.name;
|
|
RecurseFileSystem(fs, fpath, token);
|
|
ImGui::TreePop();
|
|
}
|
|
} else {
|
|
ImGui::Selectable(file.name.c_str());
|
|
if (ImGui::BeginPopupContextItem()) {
|
|
if (ImGui::MenuItem("Copy Path")) {
|
|
System_CopyStringToClipboard(path + "/" + file.name);
|
|
}
|
|
if (ImGui::MenuItem("Save file...")) {
|
|
std::string fullPath = path + "/" + file.name;
|
|
int size = file.size;
|
|
// save dialog
|
|
System_BrowseForFileSave(token, "Save file", file.name, BrowseFileType::ANY, [fullPath, fs, size](std::string_view responseString, int) {
|
|
int fd = fs->OpenFile(fullPath, FILEACCESS_READ);
|
|
if (fd >= 0) {
|
|
std::string data;
|
|
data.resize(size);
|
|
fs->ReadFile(fd, (u8 *)data.data(), size);
|
|
fs->CloseFile(fd);
|
|
Path dest(responseString);
|
|
File::WriteDataToFile(false, data.data(), data.size(), dest);
|
|
}
|
|
});
|
|
}
|
|
// your popup code
|
|
ImGui::EndPopup();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void DrawFilesystemBrowser(ImConfig &cfg) {
|
|
ImGui::SetNextWindowSize(ImVec2(420, 500), ImGuiCond_FirstUseEver);
|
|
if (!ImGui::Begin("File System", &cfg.filesystemBrowserOpen)) {
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
for (auto &fs : pspFileSystem.GetMounts()) {
|
|
std::string path;
|
|
char desc[256];
|
|
fs.system->Describe(desc, sizeof(desc));
|
|
char fsTitle[512];
|
|
snprintf(fsTitle, sizeof(fsTitle), "%s - %s", fs.prefix.c_str(), desc);
|
|
if (ImGui::TreeNode(fsTitle)) {
|
|
auto system = fs.system;
|
|
RecurseFileSystem(system.get(), path, cfg.requesterToken);
|
|
ImGui::TreePop();
|
|
}
|
|
}
|
|
|
|
ImGui::End();
|
|
}
|
|
|
|
static void DrawKernelObjects(ImConfig &cfg) {
|
|
if (!ImGui::Begin("Kernel Objects", &cfg.kernelObjectsOpen)) {
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
if (ImGui::BeginTable("kos", 4, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH | ImGuiTableFlags_Resizable)) {
|
|
ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Summary", ImGuiTableColumnFlags_WidthStretch);
|
|
|
|
ImGui::TableHeadersRow();
|
|
|
|
for (int i = 0; i < (int)KernelObjectPool::maxCount; i++) {
|
|
int id = i + KernelObjectPool::handleOffset;
|
|
if (!kernelObjects.IsValid(id)) {
|
|
continue;
|
|
}
|
|
KernelObject *obj = kernelObjects.GetFast<KernelObject>(id);
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
ImGui::PushID(i);
|
|
if (ImGui::Selectable("", cfg.selectedKernelObject == i, ImGuiSelectableFlags_AllowDoubleClick | ImGuiSelectableFlags_SpanAllColumns)) {
|
|
cfg.selectedKernelObject = id;
|
|
}
|
|
ImGui::SameLine();
|
|
/*
|
|
if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
|
|
cfg.selectedThread = i;
|
|
ImGui::OpenPopup("kernelObjectPopup");
|
|
}
|
|
*/
|
|
ImGui::Text("%04x", id);
|
|
ImGui::TableNextColumn();
|
|
ImGui::TextUnformatted(obj->GetTypeName());
|
|
ImGui::TableNextColumn();
|
|
ImGui::TextUnformatted(obj->GetName());
|
|
ImGui::TableNextColumn();
|
|
char qi[128];
|
|
obj->GetQuickInfo(qi, sizeof(qi));
|
|
ImGui::TextUnformatted(qi);
|
|
ImGui::PopID();
|
|
}
|
|
|
|
ImGui::EndTable();
|
|
}
|
|
|
|
if (kernelObjects.IsValid(cfg.selectedKernelObject)) {
|
|
const int id = cfg.selectedKernelObject;
|
|
KernelObject *obj = kernelObjects.GetFast<KernelObject>(id);
|
|
if (obj) {
|
|
ImGui::Text("%08x: %s", id, obj->GetTypeName());
|
|
char longInfo[4096];
|
|
obj->GetLongInfo(longInfo, sizeof(longInfo));
|
|
ImGui::TextUnformatted(longInfo);
|
|
}
|
|
|
|
// TODO: Show details
|
|
}
|
|
ImGui::End();
|
|
}
|
|
|
|
static void DrawNp(ImConfig &cfg) {
|
|
if (!ImGui::Begin("NP", &cfg.npOpen)) {
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
ImGui::Text("Signed in: %d", npSigninState);
|
|
ImGui::Text("Title ID: %s", npTitleId.data);
|
|
|
|
SceNpId id{};
|
|
NpGetNpId(&id);
|
|
ImGui::Text("User Handle: %s", id.handle.data);
|
|
|
|
|
|
ImGui::End();
|
|
}
|
|
|
|
static void DrawApctl(ImConfig &cfg) {
|
|
if (!ImGui::Begin("Apctl", &cfg.apctlOpen)) {
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
ImGui::Text("State: %s", ApctlStateToString(netApctlState));
|
|
if (netApctlState != PSP_NET_APCTL_STATE_DISCONNECTED && ImGui::CollapsingHeader("ApCtl Details")) {
|
|
ImGui::Text("Name: %s", netApctlInfo.name);
|
|
ImGui::Text("IP: %s", netApctlInfo.ip);
|
|
ImGui::Text("SubnetMask: %s", netApctlInfo.ip);
|
|
ImGui::Text("SSID: %.*s", netApctlInfo.ssidLength, netApctlInfo.ssid);
|
|
ImGui::Text("SSID: %.*s", netApctlInfo.ssidLength, netApctlInfo.ssid);
|
|
ImGui::Text("Gateway: %s", netApctlInfo.gateway);
|
|
ImGui::Text("PrimaryDNS: %s", netApctlInfo.primaryDns);
|
|
ImGui::Text("SecondaryDNS: %s", netApctlInfo.secondaryDns);
|
|
}
|
|
|
|
if (g_Config.bInfrastructureAutoDNS) {
|
|
const InfraDNSConfig &dnsConfig = GetInfraDNSConfig();
|
|
if (dnsConfig.loaded) {
|
|
if (!dnsConfig.gameName.empty()) {
|
|
ImGui::Text("Known game: %s", dnsConfig.gameName.c_str());
|
|
}
|
|
ImGui::Text("connectAdhocForGrouping: %s", BoolStr(dnsConfig.connectAdHocForGrouping));
|
|
ImGui::Text("DNS: %s", dnsConfig.dns.c_str());
|
|
if (!dnsConfig.dyn_dns.empty()) {
|
|
ImGui::Text("DynDNS: %s", dnsConfig.dyn_dns.c_str());
|
|
}
|
|
if (!dnsConfig.fixedDNS.empty()) {
|
|
ImGui::TextUnformatted("Fixed DNS");
|
|
for (auto iter : dnsConfig.fixedDNS) {
|
|
ImGui::Text("%s -> %s", iter.first.c_str(), iter.second.c_str());
|
|
}
|
|
}
|
|
} else {
|
|
ImGui::TextUnformatted("(InfraDNSConfig not loaded)");
|
|
}
|
|
}
|
|
|
|
ImGui::End();
|
|
}
|
|
|
|
static void DrawInternals(ImConfig &cfg) {
|
|
if (!ImGui::Begin("PPSSPP Internals", &cfg.internalsOpen)) {
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
struct entry {
|
|
PSPDirectories dir;
|
|
const char *name;
|
|
};
|
|
|
|
static const entry dirs[] = {
|
|
{DIRECTORY_PSP, "PSP"},
|
|
{DIRECTORY_CHEATS, "CHEATS"},
|
|
{DIRECTORY_SCREENSHOT, "SCREENSHOT"},
|
|
{DIRECTORY_SYSTEM, "SYSTEM"},
|
|
{DIRECTORY_GAME, "GAME"},
|
|
{DIRECTORY_SAVEDATA, "SAVEDATA"},
|
|
{DIRECTORY_PAUTH, "PAUTH"},
|
|
{DIRECTORY_DUMP, "DUMP"},
|
|
{DIRECTORY_SAVESTATE, "SAVESTATE"},
|
|
{DIRECTORY_CACHE, "CACHE"},
|
|
{DIRECTORY_TEXTURES, "TEXTURES"},
|
|
{DIRECTORY_PLUGINS, "PLUGINS"},
|
|
{DIRECTORY_APP_CACHE, "APP_CACHE"},
|
|
{DIRECTORY_VIDEO, "VIDEO"},
|
|
{DIRECTORY_AUDIO, "AUDIO"},
|
|
{DIRECTORY_MEMSTICK_ROOT, "MEMSTICK_ROOT"},
|
|
{DIRECTORY_EXDATA, "EXDATA"},
|
|
{DIRECTORY_CUSTOM_SHADERS, "CUSTOM_SHADERS"},
|
|
{DIRECTORY_CUSTOM_THEMES, "CUSTOM_THEMES"},
|
|
};
|
|
|
|
if (ImGui::CollapsingHeader("GetSysDirectory")) {
|
|
for (auto &dir : dirs) {
|
|
ImGui::Text("%s: %s", dir.name, GetSysDirectory(dir.dir).c_str());
|
|
}
|
|
}
|
|
|
|
if (ImGui::CollapsingHeader("Memory")) {
|
|
ImGui::Text("Base pointer: %p", Memory::base);
|
|
ImGui::Text("Main memory size: %08x", Memory::g_MemorySize);
|
|
if (MIPSComp::jit) {
|
|
const JitBlockCache *bc = MIPSComp::jit->GetBlockCache();
|
|
ImGui::Text("JIT block cache: %p", bc->GetBlock(0));
|
|
ImGui::Text("JIT code cache: %p", MIPSComp::jit->GetCodeBase());
|
|
ImGui::Text("MIPS state: %p", currentMIPS);
|
|
}
|
|
if (ImGui::Button("Copy to clipboard")) {
|
|
System_CopyStringToClipboard(StringFromFormat("0x%p", Memory::base));
|
|
}
|
|
}
|
|
|
|
if (ImGui::CollapsingHeader("ImGui state")) {
|
|
const auto &io = ImGui::GetIO();
|
|
ImGui::Text("WantCaptureMouse: %s", BoolStr(io.WantCaptureMouse));
|
|
ImGui::Text("WantCaptureKeyboard: %s", BoolStr(io.WantCaptureKeyboard));
|
|
ImGui::Text("WantCaptureMouseUnlessPopupClose: %s", BoolStr(io.WantCaptureMouseUnlessPopupClose));
|
|
ImGui::Text("WantTextInput: %s", BoolStr(io.WantTextInput));
|
|
}
|
|
|
|
if (ImGui::CollapsingHeader("Save detection")) {
|
|
ImGui::Text("Last in-game save/load: %0.1f seconds ago", SecondsSinceLastGameSave());
|
|
ImGui::Text("Last save/load state: %0.1f seconds ago", SaveState::SecondsSinceLastSavestate());
|
|
}
|
|
|
|
ImGui::End();
|
|
}
|
|
|
|
static void DrawAdhoc(ImConfig &cfg) {
|
|
if (!ImGui::Begin("AdHoc", &cfg.adhocOpen)) {
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
const char *discoverStatusStr = "N/A";
|
|
switch (netAdhocDiscoverStatus) {
|
|
case 0: discoverStatusStr = "NONE"; break;
|
|
case 1: discoverStatusStr = "IN_PROGRESS"; break;
|
|
case 2: discoverStatusStr = "COMPLETED"; break;
|
|
default: break;
|
|
}
|
|
|
|
ImGui::Text("sceNetAdhoc inited: %s", BoolStr(netAdhocInited));
|
|
ImGui::Text("sceNetAdhocctl inited: %s", BoolStr(netAdhocctlInited));
|
|
ImGui::Text("sceNetAdhocctl state: %s", AdhocCtlStateToString(NetAdhocctl_GetState()));
|
|
ImGui::Text("sceNetAdhocMatching inited: %s", BoolStr(netAdhocctlInited));
|
|
ImGui::Text("GameMode entered: %s", BoolStr(netAdhocGameModeEntered));
|
|
ImGui::Text("FriendFinder running: %s", BoolStr(g_adhocServerConnected));
|
|
ImGui::Text("sceNetAdhocDiscover status: %s", discoverStatusStr);
|
|
|
|
if (ImGui::BeginTable("sock", 5, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH | ImGuiTableFlags_Resizable)) {
|
|
ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Non-blocking", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("BufSize", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("IsClient", ImGuiTableColumnFlags_WidthFixed);
|
|
|
|
ImGui::TableHeadersRow();
|
|
|
|
for (int i = 0; i < MAX_SOCKET; i++) {
|
|
const AdhocSocket *socket = adhocSockets[i];
|
|
if (!socket) {
|
|
continue;
|
|
}
|
|
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%d", i + 1);
|
|
ImGui::TableNextColumn();
|
|
switch (socket->type) {
|
|
case SOCK_PDP: ImGui::TextUnformatted("PDP"); break;
|
|
case SOCK_PTP: ImGui::TextUnformatted("PTP"); break;
|
|
default: ImGui::Text("(%d)", socket->type); break;
|
|
}
|
|
ImGui::TableNextColumn();
|
|
ImGui::TextUnformatted(socket->nonblocking ? "Non-blocking" : "Blocking");
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%d", socket->buffer_size);
|
|
ImGui::TableNextColumn();
|
|
ImGui::TextUnformatted(BoolStr(socket->isClient));
|
|
}
|
|
ImGui::EndTable();
|
|
}
|
|
|
|
ImGui::End();
|
|
}
|
|
|
|
static void DrawSockets(ImConfig &cfg) {
|
|
if (!ImGui::Begin("Sockets", &cfg.socketsOpen)) {
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
if (ImGui::BeginTable("sock", 9, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH | ImGuiTableFlags_Resizable)) {
|
|
ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Port", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("IP address", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Non-blocking", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Created by", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Domain", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Protocol", ImGuiTableColumnFlags_WidthStretch);
|
|
ImGui::TableSetupColumn("Host handle", ImGuiTableColumnFlags_WidthFixed);
|
|
|
|
ImGui::TableHeadersRow();
|
|
|
|
for (int i = SocketManager::MIN_VALID_INET_SOCKET; i < SocketManager::VALID_INET_SOCKET_COUNT; i++) {
|
|
InetSocket *inetSocket;
|
|
if (!g_socketManager.GetInetSocket(i, &inetSocket)) {
|
|
continue;
|
|
}
|
|
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%d", i);
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%d", inetSocket->port);
|
|
ImGui::TableNextColumn();
|
|
ImGui::TextUnformatted(inetSocket->addr.c_str());
|
|
ImGui::TableNextColumn();
|
|
ImGui::TextUnformatted(inetSocket->nonblocking ? "Non-blocking" : "Blocking");
|
|
ImGui::TableNextColumn();
|
|
ImGui::TextUnformatted(SocketStateToString(inetSocket->state));
|
|
ImGui::TableNextColumn();
|
|
std::string str = inetSocketDomain2str(inetSocket->domain);
|
|
ImGui::TextUnformatted(str.c_str());
|
|
ImGui::TableNextColumn();
|
|
str = inetSocketType2str(inetSocket->type);
|
|
ImGui::TextUnformatted(str.c_str());
|
|
ImGui::TableNextColumn();
|
|
str = inetSocketProto2str(inetSocket->protocol);
|
|
ImGui::TextUnformatted(str.c_str());
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%d", (int)inetSocket->sock);
|
|
}
|
|
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::End();
|
|
}
|
|
|
|
static const char *MemCheckConditionToString(MemCheckCondition cond) {
|
|
// (int) casting to avoid "case not in enum" warnings
|
|
switch ((int)cond) {
|
|
case (int)MEMCHECK_READ: return "Read";
|
|
case (int)MEMCHECK_WRITE: return "Write";
|
|
case (int)MEMCHECK_READWRITE: return "Read/Write";
|
|
case (int)(MEMCHECK_WRITE | MEMCHECK_WRITE_ONCHANGE): return "Write Change";
|
|
case (int)(MEMCHECK_READWRITE | MEMCHECK_WRITE_ONCHANGE): return "Read/Write Change";
|
|
default:
|
|
return "(bad!)";
|
|
}
|
|
}
|
|
|
|
static void DrawBreakpointsView(MIPSDebugInterface *mipsDebug, ImConfig &cfg) {
|
|
if (!ImGui::Begin("Breakpoints", &cfg.breakpointsOpen)) {
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
if (ImGui::BeginTable("bp_window", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersInnerV)) {
|
|
ImGui::TableSetupColumn("left", ImGuiTableColumnFlags_WidthStretch);
|
|
ImGui::TableSetupColumn("right", ImGuiTableColumnFlags_WidthFixed, 150.0f);
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
auto &bps = g_breakpoints.GetBreakpointRefs();
|
|
auto &mcs = g_breakpoints.GetMemCheckRefs();
|
|
|
|
if (cfg.selectedBreakpoint >= bps.size()) {
|
|
cfg.selectedBreakpoint = -1;
|
|
}
|
|
if (cfg.selectedMemCheck >= mcs.size()) {
|
|
cfg.selectedMemCheck = -1;
|
|
}
|
|
|
|
if (ImGui::Button("Add Breakpoint")) {
|
|
cfg.selectedBreakpoint = g_breakpoints.AddBreakPoint(0);
|
|
cfg.selectedMemCheck = -1;
|
|
}
|
|
ImGui::SameLine();
|
|
if (ImGui::Button("Add MemCheck")) {
|
|
cfg.selectedMemCheck = g_breakpoints.AddMemCheck(0, 0, MemCheckCondition::MEMCHECK_WRITE, BreakAction::BREAK_ACTION_PAUSE);
|
|
cfg.selectedBreakpoint = -1;
|
|
}
|
|
|
|
if (ImGui::BeginTable("breakpoints", 8, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) {
|
|
ImGui::TableSetupColumn("Enabled", ImGuiTableColumnFlags_WidthFixed); // Really means action is to pause
|
|
ImGui::TableSetupColumn("Log", ImGuiTableColumnFlags_WidthFixed); // Really means action is to pause
|
|
ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Address", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Size/Label", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("OpCode", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Cond", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Hits", ImGuiTableColumnFlags_WidthStretch);
|
|
ImGui::TableHeadersRow();
|
|
|
|
for (int i = 0; i < (int)bps.size(); i++) {
|
|
BreakPoint &bp = bps[i];
|
|
bool temp = bp.temporary;
|
|
if (temp) {
|
|
continue;
|
|
}
|
|
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
ImGui::PushID(i);
|
|
// DONE: This clashes with the checkbox!
|
|
// TODO: Test to make sure this works properly
|
|
if (ImGui::Selectable("", cfg.selectedBreakpoint == i, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap) && !bp.temporary) {
|
|
cfg.selectedBreakpoint = i;
|
|
cfg.selectedMemCheck = -1;
|
|
cfg.selectedRegBreakpoint = -1;
|
|
}
|
|
ImGui::SameLine();
|
|
ImGui::CheckboxFlags("##enabled", (int *)&bp.action, (int)BREAK_ACTION_PAUSE);
|
|
ImGui::TableNextColumn();
|
|
ImGui::CheckboxFlags("##log", (int *)&bp.action, (int)BREAK_ACTION_LOG);
|
|
ImGui::TableNextColumn();
|
|
ImGui::TextUnformatted(bp.temporary ? "Temp" : "Exec");
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%08x", bp.addr);
|
|
ImGui::TableNextColumn();
|
|
const std::string sym = g_symbolMap->GetLabelString(bp.addr);
|
|
if (!sym.empty()) {
|
|
ImGui::TextUnformatted(sym.c_str()); // size/label
|
|
} else {
|
|
ImGui::TextUnformatted("-"); // size/label
|
|
}
|
|
ImGui::TableNextColumn();
|
|
// disasm->getOpcodeText(displayedBreakPoints_[index].addr, temp, sizeof(temp));
|
|
ImGui::TextUnformatted("-"); // opcode
|
|
ImGui::TableNextColumn();
|
|
if (bp.hasCond) {
|
|
ImGui::TextUnformatted(bp.cond.expressionString.c_str());
|
|
} else {
|
|
ImGui::TextUnformatted("-"); // condition
|
|
}
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("-"); // hits not available on exec bps yet
|
|
ImGui::PopID();
|
|
}
|
|
|
|
// OK, now list the memchecks.
|
|
for (int i = 0; i < (int)mcs.size(); i++) {
|
|
auto &mc = mcs[i];
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
ImGui::PushID(i + 10000);
|
|
if (ImGui::Selectable("##memcheck", cfg.selectedMemCheck == i, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap)) {
|
|
cfg.selectedBreakpoint = -1;
|
|
cfg.selectedMemCheck = i;
|
|
cfg.selectedRegBreakpoint = -1;
|
|
}
|
|
ImGui::SameLine();
|
|
ImGui::CheckboxFlags("##enabled", (int *)&mc.action, (int)BREAK_ACTION_PAUSE);
|
|
ImGui::TableNextColumn();
|
|
ImGui::CheckboxFlags("##log", (int *)&mc.action, (int)BREAK_ACTION_LOG);
|
|
ImGui::TableNextColumn();
|
|
ImGui::TextUnformatted(MemCheckConditionToString(mc.cond));
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%08x", mc.start);
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%08x", mc.end ? (mc.end - mc.start) : 1);
|
|
ImGui::TableNextColumn();
|
|
ImGui::TextUnformatted("-"); // opcode
|
|
ImGui::TableNextColumn();
|
|
ImGui::TextUnformatted("-"); // cond
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%d", mc.numHits);
|
|
ImGui::PopID();
|
|
}
|
|
|
|
// Finally, list register breakpoints.
|
|
for (auto &rbp : g_breakpoints.GetRegBreakpoints()) {
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
ImGui::PushID(&rbp - &g_breakpoints.GetRegBreakpoints()[0] + 20000);
|
|
if (ImGui::Selectable("##regbp", cfg.selectedRegBreakpoint == &rbp - &g_breakpoints.GetRegBreakpoints()[0], ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap)) {
|
|
cfg.selectedBreakpoint = -1;
|
|
cfg.selectedMemCheck = -1;
|
|
cfg.selectedRegBreakpoint = &rbp - &g_breakpoints.GetRegBreakpoints()[0];
|
|
}
|
|
ImGui::SameLine();
|
|
ImGui::CheckboxFlags("", (int *)&rbp.result, BREAK_ACTION_PAUSE);
|
|
ImGui::TableNextColumn();
|
|
ImGui::TextUnformatted("Reg");
|
|
ImGui::TableNextColumn();
|
|
ImGui::TextUnformatted(mipsDebug->GetRegName(0, rbp.reg).c_str());
|
|
ImGui::TableNextColumn();
|
|
ImGui::TextUnformatted("-"); // size/label
|
|
ImGui::TableNextColumn();
|
|
ImGui::TextUnformatted("-"); // opcode
|
|
ImGui::TableNextColumn();
|
|
if (rbp.hasCond) {
|
|
ImGui::TextUnformatted(rbp.cond.expressionString.c_str());
|
|
} else {
|
|
ImGui::TextUnformatted("-"); // condition
|
|
}
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%d", rbp.numHits);
|
|
ImGui::PopID();
|
|
}
|
|
|
|
ImGui::EndTable();
|
|
}
|
|
|
|
ImGui::TableNextColumn();
|
|
|
|
if (cfg.selectedBreakpoint >= 0) {
|
|
// Add edit form for breakpoints
|
|
if (ImGui::BeginChild("bp_edit")) {
|
|
auto &bp = bps[cfg.selectedBreakpoint];
|
|
bool changed = false;
|
|
ImGui::TextUnformatted("Edit breakpoint");
|
|
if (ImGui::CheckboxFlags("Enabled", (int *)&bp.action, (int)BREAK_ACTION_PAUSE)) {
|
|
changed = true;
|
|
}
|
|
if (ImGui::CheckboxFlags("Log", (int *)&bp.action, (int)BREAK_ACTION_LOG)) {
|
|
changed = true;
|
|
}
|
|
if (ImGui::InputScalar("Address", ImGuiDataType_U32, &bp.addr, nullptr, nullptr, "%08x", ImGuiInputTextFlags_CharsHexadecimal)) {
|
|
changed = true;
|
|
}
|
|
if (changed) {
|
|
// This is a bit hacky, we should probably add a utility function in the breakpoint manager.
|
|
currentMIPS->InvalidateICacheRangeDeferred(bp.addr - 4, 8);
|
|
}
|
|
if (ImGui::Button("Delete")) {
|
|
g_breakpoints.RemoveBreakPoint(bp.addr);
|
|
}
|
|
ImGui::EndChild();
|
|
}
|
|
}
|
|
|
|
if (cfg.selectedMemCheck >= 0) {
|
|
// Add edit form for memchecks
|
|
if (ImGui::BeginChild("mc_edit")) {
|
|
auto &mc = mcs[cfg.selectedMemCheck];
|
|
ImGui::TextUnformatted("Edit memcheck");
|
|
// Anything that edits mc in place has to end up calling NotifyChangedMemchecks(),
|
|
// see GetMemCheckRefs(). Note that Selectable() only returns true on the frame it's
|
|
// actually clicked, unlike BeginCombo(), which is true for every frame the popup
|
|
// stays open - so set this from the Selectables, not from the combo itself.
|
|
bool changed = false;
|
|
if (ImGui::BeginCombo("Condition", MemCheckConditionToString(mc.cond))) {
|
|
if (ImGui::Selectable("Read", mc.cond == MemCheckCondition::MEMCHECK_READ)) {
|
|
mc.cond = MemCheckCondition::MEMCHECK_READ;
|
|
changed = true;
|
|
}
|
|
if (ImGui::Selectable("Write", mc.cond == MemCheckCondition::MEMCHECK_WRITE)) {
|
|
mc.cond = MemCheckCondition::MEMCHECK_WRITE;
|
|
changed = true;
|
|
}
|
|
if (ImGui::Selectable("Read / Write", mc.cond == MemCheckCondition::MEMCHECK_READWRITE)) {
|
|
mc.cond = MemCheckCondition::MEMCHECK_READWRITE;
|
|
changed = true;
|
|
}
|
|
if (ImGui::Selectable("Write On Change", mc.cond == MemCheckCondition::MEMCHECK_WRITE_ONCHANGE)) {
|
|
mc.cond = MemCheckCondition::MEMCHECK_WRITE_ONCHANGE;
|
|
changed = true;
|
|
}
|
|
ImGui::EndCombo();
|
|
}
|
|
// The cached ranges don't carry the action bits (they're read live in ExecMemCheck),
|
|
// so toggling this doesn't strictly need a rebuild - but notify anyway rather than
|
|
// leaving a silent exception to the rule above for the next person to rediscover.
|
|
if (ImGui::CheckboxFlags("Enabled", (int *)&mc.action, (int)BREAK_ACTION_PAUSE)) {
|
|
changed = true;
|
|
}
|
|
if (ImGui::InputScalar("Start", ImGuiDataType_U32, &mc.start, NULL, NULL, "%08x", ImGuiInputTextFlags_CharsHexadecimal)) {
|
|
changed = true;
|
|
}
|
|
if (ImGui::InputScalar("End", ImGuiDataType_U32, &mc.end, NULL, NULL, "%08x", ImGuiInputTextFlags_CharsHexadecimal)) {
|
|
changed = true;
|
|
}
|
|
if (ImGui::Button("Delete")) {
|
|
g_breakpoints.RemoveMemCheck(mcs[cfg.selectedMemCheck].start, mcs[cfg.selectedMemCheck].end);
|
|
}
|
|
if (changed) {
|
|
g_breakpoints.NotifyChangedMemchecks();
|
|
}
|
|
ImGui::EndChild();
|
|
}
|
|
}
|
|
ImGui::EndTable();
|
|
}
|
|
|
|
ImGui::End();
|
|
}
|
|
|
|
void DrawMediaDecodersView(ImConfig &cfg, ImControl &control) {
|
|
if (!ImGui::Begin("Media decoding contexts", &cfg.mediaDecodersOpen)) {
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
const std::map<u32, MpegContext *> &mpegCtxs = __MpegGetContexts();
|
|
if (ImGui::CollapsingHeaderWithCount("sceMpeg", (int)mpegCtxs.size())) {
|
|
if (ImGui::BeginTable("mpegs", 2, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) {
|
|
ImGui::TableSetupColumn("Addr", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("VFrames", ImGuiTableColumnFlags_WidthFixed);
|
|
|
|
ImGui::TableHeadersRow();
|
|
for (auto iter : mpegCtxs) {
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
ImGui::PushID(iter.first);
|
|
ImGui::SetNextItemAllowOverlap();
|
|
char temp[16];
|
|
snprintf(temp, sizeof(temp), "%08x", iter.first);
|
|
if (ImGui::Selectable(temp, iter.first == cfg.selectedMpegCtx, ImGuiSelectableFlags_SpanAllColumns)) {
|
|
cfg.selectedMpegCtx = iter.first;
|
|
}
|
|
ImGui::TableNextColumn();
|
|
const MpegContext *ctx = iter.second;
|
|
if (!ctx) {
|
|
ImGui::TextUnformatted("N/A");
|
|
ImGui::PopID();
|
|
continue;
|
|
}
|
|
ImGui::Text("%d", ctx->videoFrameCount);
|
|
ImGui::PopID();
|
|
}
|
|
ImGui::EndTable();
|
|
}
|
|
|
|
auto iter = mpegCtxs.find(cfg.selectedMpegCtx);
|
|
if (iter != mpegCtxs.end()) {
|
|
const MpegContext *ctx = iter->second;
|
|
char temp[28];
|
|
snprintf(temp, sizeof(temp), "sceMpeg context at %08x", iter->first);
|
|
if (ctx && ImGui::CollapsingHeader(temp, ImGuiTreeNodeFlags_DefaultOpen)) {
|
|
// ImGui::ProgressBar((float)sas->CurPos() / (float)info.fileDataEnd, ImVec2(200.0f, 0.0f));
|
|
ImGui::Text("Mpeg version: %d raw: %08x", ctx->mpegVersion, ctx->mpegRawVersion);
|
|
ImGui::Text("Frame counts: Audio %d, video %d", ctx->audioFrameCount, ctx->videoFrameCount);
|
|
ImGui::Text("Video pixel mode: %d", ctx->videoPixelMode);
|
|
ImGui::Text("AVC status=%d width=%d height=%d result=%d", ctx->avc.avcFrameStatus, ctx->avc.avcDetailFrameWidth, ctx->avc.avcDetailFrameHeight, ctx->avc.avcDecodeResult);
|
|
ImGui::Text("Stream size: %d", ctx->mpegStreamSize);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Count the active atrac contexts so we can display it.
|
|
const int maxAtracContexts = __AtracMaxContexts();
|
|
int atracCount = 0;
|
|
for (int i = 0; i < maxAtracContexts; i++) {
|
|
u32 type;
|
|
if (__AtracGetCtx(i, &type)) {
|
|
atracCount++;
|
|
}
|
|
}
|
|
|
|
if (ImGui::CollapsingHeaderWithCount("sceAtrac", atracCount, ImGuiTreeNodeFlags_DefaultOpen)) {
|
|
ImGui::Checkbox("Force FFMPEG", &g_Config.bForceFfmpegForAudioDec);
|
|
if (ImGui::BeginTable("atracs", 8, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) {
|
|
ImGui::TableSetupColumn("Index", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Mute", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Status", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Channels", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("CurSample", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("RemFrames", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Impl", ImGuiTableColumnFlags_WidthFixed);
|
|
|
|
ImGui::TableHeadersRow();
|
|
for (int i = 0; i < maxAtracContexts; i++) {
|
|
u32 codecType = 0;
|
|
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
ImGui::PushID(i);
|
|
ImGui::SetNextItemAllowOverlap();
|
|
char temp[16];
|
|
snprintf(temp, sizeof(temp), "%d", i);
|
|
if (ImGui::Selectable(temp, i == cfg.selectedAtracCtx, ImGuiSelectableFlags_SpanAllColumns)) {
|
|
cfg.selectedAtracCtx = i;
|
|
}
|
|
ImGui::TableNextColumn();
|
|
bool *mutePtr = __AtracMuteFlag(i);
|
|
if (mutePtr) {
|
|
ImGui::Checkbox("", mutePtr);
|
|
}
|
|
ImGui::TableNextColumn();
|
|
|
|
const AtracBase *ctx = __AtracGetCtx(i, &codecType);
|
|
if (!ctx) {
|
|
// Nothing more we can display about uninitialized contexts.
|
|
ImGui::PopID();
|
|
continue;
|
|
}
|
|
|
|
switch (codecType) {
|
|
case 0:
|
|
ImGui::TextUnformatted("-"); // Uninitialized
|
|
break;
|
|
case PSP_CODEC_AT3PLUS:
|
|
ImGui::TextUnformatted("Atrac3+");
|
|
break;
|
|
case PSP_CODEC_AT3:
|
|
ImGui::TextUnformatted("Atrac3");
|
|
break;
|
|
default:
|
|
ImGui::Text("%04x", codecType);
|
|
break;
|
|
}
|
|
|
|
ImGui::TableNextColumn();
|
|
ImGui::TextUnformatted(AtracStatusToString(ctx->BufferState()));
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("in:%d out:%d", ctx->Channels(), ctx->GetOutputChannels());
|
|
ImGui::TableNextColumn();
|
|
if (AtracStatusIsNormal(ctx->BufferState())) {
|
|
int pos;
|
|
ctx->GetNextDecodePosition(&pos);
|
|
ImGui::Text("%d", pos);
|
|
} else {
|
|
ImGui::TextUnformatted("N/A");
|
|
}
|
|
ImGui::TableNextColumn();
|
|
if (AtracStatusIsNormal(ctx->BufferState())) {
|
|
ImGui::Text("%d", ctx->RemainingFrames());
|
|
} else {
|
|
ImGui::TextUnformatted("N/A");
|
|
}
|
|
ImGui::TableNextColumn();
|
|
ImGui::TextUnformatted(ctx->GetContextVersion() >= 2 ? "NewImpl" : "Legacy");
|
|
ImGui::PopID();
|
|
}
|
|
|
|
ImGui::EndTable();
|
|
}
|
|
|
|
if (cfg.selectedAtracCtx >= 0 && cfg.selectedAtracCtx < PSP_MAX_ATRAC_IDS) {
|
|
u32 type = 0;
|
|
const AtracBase *ctx = __AtracGetCtx(cfg.selectedAtracCtx, &type);
|
|
// Show details about the selected atrac context here.
|
|
char header[32];
|
|
snprintf(header, sizeof(header), "Atrac context %d", cfg.selectedAtracCtx);
|
|
if (ctx && ImGui::CollapsingHeader(header, ImGuiTreeNodeFlags_DefaultOpen)) {
|
|
bool isNormal = AtracStatusIsNormal(ctx->BufferState());
|
|
if (isNormal) {
|
|
int pos;
|
|
ctx->GetNextDecodePosition(&pos);
|
|
int endSample, loopStart, loopEnd;
|
|
ctx->GetSoundSample(&endSample, &loopStart, &loopEnd);
|
|
ImGui::ProgressBar((float)pos / (float)endSample, ImVec2(200.0f, 0.0f));
|
|
ImGui::Text("Status: %s", AtracStatusToString(ctx->BufferState()));
|
|
ImGui::Text("cur/end sample: %d/%d", pos, endSample);
|
|
}
|
|
if (ctx->context_.IsValid()) {
|
|
ImGui::Text("ctx addr: ");
|
|
ImGui::SameLine();
|
|
ImClickableValue("ctx", ctx->context_.ptr, control, ImCmd::SHOW_IN_MEMORY_VIEWER);
|
|
}
|
|
if (ctx->context_.IsValid() && ctx->GetContextVersion() >= 2) {
|
|
const auto &info = ctx->context_->info;
|
|
if (isNormal) {
|
|
ImGui::Text("Buffer: (size: %d / %08x) Frame: %d", info.bufferByte, info.bufferByte, info.sampleSize);
|
|
ImGui::SameLine();
|
|
ImClickableValue("buffer", info.buffer, control, ImCmd::SHOW_IN_MEMORY_VIEWER);
|
|
if (info.secondBuffer || info.secondBufferByte) {
|
|
ImGui::Text("Second: (size: %d / %08x)", info.secondBufferByte, info.secondBufferByte);
|
|
ImGui::SameLine();
|
|
ImClickableValue("second", info.secondBuffer, control, ImCmd::SHOW_IN_MEMORY_VIEWER);
|
|
}
|
|
ImGui::Text("Data: %d/%d", info.dataOff, info.fileDataEnd);
|
|
if (info.state != ATRAC_STATUS_STREAMED_WITHOUT_LOOP) {
|
|
ImGui::Text("LoopNum: %d (%d-%d)", info.loopNum, info.loopStart, info.loopEnd);
|
|
}
|
|
ImGui::Text("DecodePos: %d EndSample: %d", info.decodePos, info.fileDataEnd);
|
|
if (AtracStatusIsStreaming(info.state)) {
|
|
ImGui::Text("Stream: offset %d, streamDataBytes: %d", info.streamOff, info.streamDataByte);
|
|
}
|
|
ImGui::Text("numFrame: %d curBuffer: %d streamOff2: %d", info.numSkipFrames, info.curBuffer, info.secondStreamOff);
|
|
} else if (ctx->BufferState() == ATRAC_STATUS_FOR_SCESAS) {
|
|
// A different set of state!
|
|
const AtracSasStreamState *sas = ctx->StreamStateForSas();
|
|
if (sas) {
|
|
ImGui::ProgressBar((float)sas->CurPos() / (float)info.fileDataEnd, ImVec2(200.0f, 0.0f));
|
|
ImGui::ProgressBar((float)sas->streamOffset / (float)sas->bufSize[sas->curBuffer], ImVec2(200.0f, 0.0f));
|
|
ImGui::Text("Cur pos: %08x File offset: %08x File end: %08x%s", sas->CurPos(), sas->fileOffset, info.fileDataEnd, sas->fileOffset >= info.fileDataEnd ? " (END)" : "");
|
|
ImGui::Text("Second (next buffer): %08x (sz: %08x)", info.secondBuffer, info.secondBufferByte);
|
|
ImGui::Text("Cur buffer: %d (%08x, sz: %08x)", sas->curBuffer, sas->bufPtr[sas->curBuffer], sas->bufSize[sas->curBuffer]);
|
|
ImGui::Text("2nd buffer: %d (%08x, sz: %08x)", sas->curBuffer ^ 1, sas->bufPtr[sas->curBuffer ^ 1], sas->bufSize[sas->curBuffer ^ 1]);
|
|
ImGui::Text("Loop points: %08x, %08x", info.loopStart, info.loopEnd);
|
|
ImGui::TextUnformatted(sas->isStreaming ? "Streaming mode!" : "Non-streaming mode");
|
|
} else {
|
|
ImGui::Text("can't access sas state");
|
|
}
|
|
}
|
|
|
|
if (ctx->BufferState() == ATRAC_STATUS_ALL_DATA_LOADED) {
|
|
if (ImGui::Button("Save to disk...")) {
|
|
System_BrowseForFileSave(cfg.requesterToken, "Save AT3 file", "song.at3", BrowseFileType::ATRAC3, [&info](std::string_view filename, int) {
|
|
if (!Memory::IsValidRange(info.buffer, info.bufferByte)) {
|
|
return;
|
|
}
|
|
const u8 *data = Memory::GetPointerRangeOrException(info.buffer, info.bufferByte);
|
|
if (!data) {
|
|
return;
|
|
}
|
|
FILE *file = File::OpenCFile(Path(filename), "wb");
|
|
if (!file) {
|
|
return;
|
|
}
|
|
fwrite(data, 1, info.bufferByte, file);
|
|
fclose(file);
|
|
});
|
|
}
|
|
}
|
|
} else {
|
|
ImGui::Text("loop: %d", ctx->LoopNum());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ImGui::CollapsingHeaderWithCount("sceMp3", (int)g_mp3Map.size(), ImGuiTreeNodeFlags_DefaultOpen)) {
|
|
if (ImGui::BeginTable("mp3", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) {
|
|
ImGui::TableSetupColumn("Handle", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Channels", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("ReadPos", ImGuiTableColumnFlags_WidthFixed);
|
|
|
|
for (auto &iter : g_mp3Map) {
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
ImGui::PushID(iter.first);
|
|
ImGui::SetNextItemAllowOverlap();
|
|
char temp[16];
|
|
snprintf(temp, sizeof(temp), "%d", iter.first);
|
|
if (ImGui::Selectable(temp, iter.first == cfg.selectedMp3Ctx, ImGuiSelectableFlags_SpanAllColumns)) {
|
|
cfg.selectedMp3Ctx = iter.first;
|
|
}
|
|
if (!iter.second) {
|
|
continue;
|
|
}
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%d", iter.second->Channels);
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%d", (int)iter.second->ReadPos());
|
|
ImGui::PopID();
|
|
}
|
|
ImGui::EndTable();
|
|
}
|
|
|
|
auto iter = g_mp3Map.find(cfg.selectedMp3Ctx);
|
|
if (iter != g_mp3Map.end() && ImGui::CollapsingHeader("MP3 %d", iter->first)) {
|
|
ImGui::Text("MP3 Context %d", iter->first);
|
|
if (iter->second) {
|
|
AuCtx *ctx = iter->second;
|
|
ImGui::Text("%d Hz, %d channels", ctx->SamplingRate, ctx->Channels);
|
|
ImGui::Text("AUBuf: %08x AUSize: %08x", ctx->AuBuf, ctx->AuBufSize);
|
|
ImGui::Text("PCMBuf: %08x PCMSize: %08x", ctx->PCMBuf, ctx->PCMBufSize);
|
|
ImGui::Text("Pos: %d (%d -> %d)", ctx->ReadPos(), (int)ctx->startPos, (int)ctx->endPos);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ImGui::CollapsingHeaderWithCount("sceAac", (int)g_aacMap.size(), ImGuiTreeNodeFlags_DefaultOpen)) {
|
|
if (ImGui::BeginTable("aac", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) {
|
|
ImGui::TableSetupColumn("Handle", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Channels", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("ReadPos", ImGuiTableColumnFlags_WidthFixed);
|
|
|
|
for (auto &iter : g_aacMap) {
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
ImGui::PushID(iter.first);
|
|
ImGui::SetNextItemAllowOverlap();
|
|
char temp[16];
|
|
snprintf(temp, sizeof(temp), "%d", iter.first);
|
|
if (ImGui::Selectable(temp, iter.first == cfg.selectedAacCtx, ImGuiSelectableFlags_SpanAllColumns)) {
|
|
cfg.selectedAacCtx = iter.first;
|
|
}
|
|
if (!iter.second) {
|
|
continue;
|
|
}
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%d", iter.second->Channels);
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%d", (int)iter.second->ReadPos());
|
|
ImGui::PopID();
|
|
}
|
|
ImGui::EndTable();
|
|
}
|
|
|
|
auto iter = g_mp3Map.find(cfg.selectedAacCtx);
|
|
if (iter != g_mp3Map.end() && ImGui::CollapsingHeader("AAC %d", iter->first)) {
|
|
ImGui::Text("AAC Context %d", iter->first);
|
|
if (iter->second) {
|
|
AuCtx *ctx = iter->second;
|
|
ImGui::Text("%d Hz, %d channels", ctx->SamplingRate, ctx->Channels);
|
|
ImGui::Text("AUBuf: %08x AUSize: %08x", ctx->AuBuf, ctx->AuBufSize);
|
|
ImGui::Text("PCMBuf: %08x PCMSize: %08x", ctx->PCMBuf, ctx->PCMBufSize);
|
|
ImGui::Text("Pos: %d (%d -> %d)", ctx->ReadPos(), (int)ctx->startPos, (int)ctx->endPos);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ImGui::CollapsingHeaderWithCount("sceAudiocodec", (int)g_audioDecoderContexts.size(), ImGuiTreeNodeFlags_DefaultOpen)) {
|
|
if (ImGui::BeginTable("codecs", 2, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) {
|
|
ImGui::TableSetupColumn("CtxAddr", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed);
|
|
|
|
for (auto &iter : g_audioDecoderContexts) {
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%08x", iter.first);
|
|
ImGui::TableNextColumn();
|
|
switch (iter.second->GetAudioType()) {
|
|
case PSP_CODEC_AAC: ImGui::TextUnformatted("AAC"); break;
|
|
case PSP_CODEC_MP3: ImGui::TextUnformatted("MP3"); break;
|
|
case PSP_CODEC_AT3PLUS: ImGui::TextUnformatted("Atrac3+"); break;
|
|
case PSP_CODEC_AT3: ImGui::TextUnformatted("Atrac3"); break;
|
|
default: ImGui::Text("%08x", iter.second->GetAudioType()); break;
|
|
}
|
|
}
|
|
ImGui::EndTable();
|
|
}
|
|
}
|
|
|
|
ImGui::End();
|
|
}
|
|
|
|
|
|
void DrawAudioOut(ImConfig &cfg, ImControl &control) {
|
|
if (!ImGui::Begin("Audio output", &cfg.audioOutOpen)) {
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
if (g_Config.iAudioPlaybackMode == (int)AudioSyncMode::GRANULAR) {
|
|
// Show granular stats
|
|
GranularStats stats;
|
|
g_granular.GetStats(&stats);
|
|
ImGui::Text("Granules");
|
|
ImGui::Text("Read size: %0.2f", stats.smoothedReadSize);
|
|
ImGui::Text("Frame time estimate: %0.1f ms", stats.frameTimeEstimate * 1000.0f);
|
|
ImGui::Text("Queued samples target: %d", stats.queuedSamplesTarget);
|
|
ImGui::Text("Queued min/max: %d / %d", stats.queuedGranulesMin, stats.queuedGranulesMax);
|
|
ImGui::Text("Queued (smooth): %0.3f", stats.smoothedQueuedGranules);
|
|
ImGui::Text("Target: %d", stats.targetQueueSize);
|
|
ImGui::Text("Max: %d", stats.maxQueuedGranules);
|
|
ImGui::Text("Computed queue latency: %d ms", (int)(stats.smoothedQueuedGranules * (GranularMixer::GRANULE_SIZE * 1000.0 / 44100.0)));
|
|
ImGui::Text("Fade volume: %0.2f", stats.fadeVolume);
|
|
ImGui::Text("Looping: %s", BoolStr(stats.looping));
|
|
ImGui::Text("Under/Over: %d / %d", stats.underruns, stats.overruns);
|
|
} else {
|
|
ImGui::Text("StereoResampler classic");
|
|
char buf[1024];
|
|
g_resampler.GetAudioDebugStats(buf, sizeof(buf));
|
|
ImGui::Text("%s", buf);
|
|
}
|
|
|
|
ImGui::End();
|
|
}
|
|
|
|
void DrawAudioChannels(ImConfig &cfg, ImControl &control) {
|
|
if (!ImGui::Begin("Raw audio channels", &cfg.audioChannelsOpen)) {
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
if (ImGui::BeginTable("audios", 7, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) {
|
|
ImGui::TableSetupColumn("Index", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Mute", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("SampleAddr", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("SampleCount", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Volume", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Format", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Waiting Thread", ImGuiTableColumnFlags_WidthFixed);
|
|
|
|
ImGui::TableHeadersRow();
|
|
|
|
// vaudio / output2 uses channel 8.
|
|
for (int i = 0; i < PSP_AUDIO_CHANNEL_MAX + 1; i++) {
|
|
if (!g_audioChans[i].reserved) {
|
|
continue;
|
|
}
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
ImGui::PushID(i);
|
|
if (i == 8) {
|
|
ImGui::TextUnformatted("audio2");
|
|
} else {
|
|
ImGui::Text("%d", i);
|
|
}
|
|
ImGui::TableNextColumn();
|
|
ImGui::Checkbox("", &g_audioChans[i].mute);
|
|
ImGui::TableNextColumn();
|
|
char id[2]{};
|
|
id[0] = i + 1;
|
|
ImClickableValue(id, g_audioChans[i].sampleAddress, control, ImCmd::SHOW_IN_MEMORY_VIEWER);
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%08x", g_audioChans[i].sampleCount);
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%d | %d", g_audioChans[i].leftVolume, g_audioChans[i].rightVolume);
|
|
ImGui::TableNextColumn();
|
|
switch (g_audioChans[i].format) {
|
|
case PSP_AUDIO_FORMAT_STEREO:
|
|
ImGui::TextUnformatted("Stereo");
|
|
break;
|
|
case PSP_AUDIO_FORMAT_MONO:
|
|
ImGui::TextUnformatted("Mono");
|
|
break;
|
|
default:
|
|
ImGui::TextUnformatted("UNK: %04x");
|
|
break;
|
|
}
|
|
ImGui::TableNextColumn();
|
|
for (auto t : g_audioChans[i].waitingThreads) {
|
|
KernelObject *thread = kernelObjects.GetFast<KernelObject>(t.threadID);
|
|
if (thread) {
|
|
ImGui::Text("%s: %d", thread->GetName(), t.numSamples);
|
|
}
|
|
}
|
|
ImGui::PopID();
|
|
}
|
|
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::End();
|
|
}
|
|
|
|
void ImLogWindow::Draw(ImConfig &cfg) {
|
|
ImGui::SetNextWindowSize(ImVec2(500, 400), ImGuiCond_FirstUseEver);
|
|
if (!ImGui::Begin("Log", &cfg.logOpen)) {
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
const RingbufferLog &ring = g_logManager.GetRingbuffer();
|
|
|
|
// Options menu
|
|
if (ImGui::BeginPopup("Options")) {
|
|
ImGui::Checkbox("Auto-scroll", &AutoScroll);
|
|
ImGui::EndPopup();
|
|
}
|
|
|
|
// Main window
|
|
if (ImGui::Button("Options"))
|
|
ImGui::OpenPopup("Options");
|
|
ImGui::SameLine();
|
|
bool copy = ImGui::Button("Copy");
|
|
ImGui::Separator();
|
|
|
|
if (ImGui::BeginChild("scrolling", ImVec2(0, 0), ImGuiChildFlags_None, ImGuiWindowFlags_HorizontalScrollbar)) {
|
|
if (copy)
|
|
ImGui::LogToClipboard();
|
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
|
|
|
|
ImGuiListClipper clipper;
|
|
clipper.Begin(ring.GetCount());
|
|
while (clipper.Step()) {
|
|
for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++) {
|
|
int n = ring.GetCount() - 1 - line_no;
|
|
|
|
const std::string line = ring.TextAt(n);
|
|
const LogLevel level = ring.LevelAt(n);
|
|
const u32 color = 0xFF000000 | LogManager::GetLevelColor(level);
|
|
ImGui::PushStyleColor(ImGuiCol_Text, color);
|
|
ImGui::TextUnformatted(line);
|
|
ImGui::PopStyleColor();
|
|
}
|
|
}
|
|
clipper.End();
|
|
ImGui::PopStyleVar();
|
|
|
|
// Keep up at the bottom of the scroll region if we were already at the bottom at the beginning of the frame.
|
|
// Using a scrollbar or mouse-wheel will take away from the bottom edge.
|
|
if (AutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY() - 20)
|
|
ImGui::SetScrollHereY(1.0f);
|
|
}
|
|
ImGui::EndChild();
|
|
ImGui::End();
|
|
}
|
|
|
|
void DrawLogConfig(ImConfig &cfg) {
|
|
if (!ImGui::Begin("Logs", &cfg.logConfigOpen)) {
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
static const char *logLevels[] = {
|
|
"N/A",
|
|
"Notice", // starts at 1 for some reason
|
|
"Error",
|
|
"Warn",
|
|
"Info",
|
|
"Debug",
|
|
"Verb."
|
|
};
|
|
_dbg_assert_(ARRAY_SIZE(logLevels) == (int)LogLevel::LVERBOSE + 1);
|
|
|
|
if (ImGui::BeginTable("logs", 2, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) {
|
|
ImGui::TableSetupColumn("Log", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Level", ImGuiTableColumnFlags_WidthFixed, 150.0f);
|
|
ImGui::TableHeadersRow();
|
|
for (int i = 0; i < (int)Log::NUMBER_OF_LOGS; i++) {
|
|
LogChannel *chan = g_logManager.GetLogChannel((Log)i);
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
|
|
const char *logName = LogManager::GetLogTypeName((Log)i);
|
|
|
|
ImGui::PushID(logName);
|
|
|
|
ImGui::Checkbox(logName, &chan->enabled);
|
|
ImGui::TableNextColumn();
|
|
|
|
if (ImGui::BeginCombo("-", logLevels[(int)chan->level])) {
|
|
for (int i = 1; i < ARRAY_SIZE(logLevels); ++i) {
|
|
LogLevel current = static_cast<LogLevel>(i);
|
|
bool isSelected = (chan->level == current);
|
|
if (ImGui::Selectable(logLevels[(int)current], isSelected)) {
|
|
chan->level = current;
|
|
}
|
|
if (isSelected) {
|
|
ImGui::SetItemDefaultFocus();
|
|
}
|
|
}
|
|
ImGui::EndCombo();
|
|
}
|
|
ImGui::PopID();
|
|
}
|
|
ImGui::EndTable();
|
|
}
|
|
|
|
ImGui::End();
|
|
}
|
|
|
|
static const char *VoiceTypeToString(VoiceType type) {
|
|
switch (type) {
|
|
case VOICETYPE_OFF: return "OFF";
|
|
case VOICETYPE_VAG: return "VAG"; // default
|
|
case VOICETYPE_NOISE: return "NOISE";
|
|
case VOICETYPE_TRIWAVE: return "TRIWAVE"; // are these used? there are functions for them (sceSetTriangularWave)
|
|
case VOICETYPE_PULSEWAVE: return "PULSEWAVE";
|
|
case VOICETYPE_PCM: return "PCM";
|
|
case VOICETYPE_ATRAC3: return "ATRAC3";
|
|
default: return "(unknown!)";
|
|
}
|
|
}
|
|
|
|
void DrawSasAudio(ImConfig &cfg) {
|
|
if (!ImGui::Begin("sasAudio", &cfg.sasAudioOpen)) {
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
const SasInstance *sas = GetSasInstance();
|
|
if (!sas) {
|
|
ImGui::Text("Sas instance not available");
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
ImGui::Checkbox("Mute", __SasGetGlobalMuteFlag());
|
|
ImGui::SameLine();
|
|
ImGui::Checkbox("Show all voices", &cfg.sasShowAllVoices);
|
|
|
|
if (ImGui::BeginTable("saschannels", 9, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) {
|
|
ImGui::TableSetupColumn("Index", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("On", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Loop", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Pause", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Cur", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Addr", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Size", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Volume", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableHeadersRow();
|
|
|
|
for (int i = 0; i < PSP_SAS_VOICES_MAX; i++) {
|
|
const SasVoice &voice = sas->voices[i];
|
|
if (!voice.on && !cfg.sasShowAllVoices) {
|
|
continue;
|
|
}
|
|
if (!voice.on) {
|
|
ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(255, 255, 255, 128));
|
|
}
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%d", i);
|
|
ImGui::TableNextColumn();
|
|
ImGui::TextUnformatted(VoiceTypeToString(voice.type));
|
|
ImGui::TableNextColumn();
|
|
ImGui::TextUnformatted(BoolStr(voice.on));
|
|
ImGui::TableNextColumn();
|
|
ImGui::TextUnformatted(BoolStr(voice.loop));
|
|
ImGui::TableNextColumn();
|
|
ImGui::TextUnformatted(BoolStr(voice.paused));
|
|
ImGui::TableNextColumn();
|
|
switch (voice.type) {
|
|
case VOICETYPE_OFF: ImGui::TextUnformatted("(off)"); break;
|
|
case VOICETYPE_VAG: ImGui::Text("%08x", voice.vag.GetReadPtr()); break;
|
|
case VOICETYPE_PCM: ImGui::Text("%08x", voice.pcmAddr + voice.pcmIndex * 2); break;
|
|
default:
|
|
ImGui::TextUnformatted("N/A");
|
|
break;
|
|
}
|
|
ImGui::TableNextColumn();
|
|
switch (voice.type) {
|
|
case VOICETYPE_OFF: ImGui::TextUnformatted("(off)"); break;
|
|
case VOICETYPE_VAG: ImGui::Text("%08x", voice.vagAddr); break;
|
|
case VOICETYPE_PCM: ImGui::Text("%08x", voice.pcmAddr); break;
|
|
case VOICETYPE_ATRAC3: ImGui::Text("atrac: %d", voice.atrac3.AtracID()); break;
|
|
default:
|
|
ImGui::TextUnformatted("N/A");
|
|
break;
|
|
}
|
|
ImGui::TableNextColumn();
|
|
switch (voice.type) {
|
|
case VOICETYPE_OFF: ImGui::TextUnformatted("(off)"); break;
|
|
case VOICETYPE_VAG: ImGui::Text("%08x", voice.vagSize); break;
|
|
case VOICETYPE_PCM: ImGui::Text("%08x", voice.pcmSize); break;
|
|
case VOICETYPE_ATRAC3: ImGui::Text("atrac: n/a"); break;
|
|
default:
|
|
ImGui::TextUnformatted("N/A");
|
|
break;
|
|
}
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%d | %d", voice.volumeLeft, voice.volumeRight);
|
|
if (!voice.on) {
|
|
ImGui::PopStyleColor();
|
|
}
|
|
}
|
|
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::End();
|
|
}
|
|
|
|
static void DrawCallStacks(const MIPSDebugInterface *debug, ImConfig &config, ImControl &control) {
|
|
if (!ImGui::Begin("Callstacks", &config.callstackOpen)) {
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
std::vector<DebugThreadInfo> info = GetThreadsInfo();
|
|
// TODO: Add dropdown for thread choice, so you can check the callstacks of other threads.
|
|
u32 entry = 0;
|
|
u32 stackTop = 0;
|
|
for (auto &thread : info) {
|
|
if (thread.isCurrent) {
|
|
entry = thread.entrypoint;
|
|
stackTop = thread.initialStack;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (entry != 0 && ImGui::BeginTable("frames", 6, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) {
|
|
ImGui::TableSetupColumn("Entry", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("EntryAddr", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("CurPC", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("CurOpCode", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("CurSP", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Size", ImGuiTableColumnFlags_WidthStretch);
|
|
|
|
ImGui::TableHeadersRow();
|
|
|
|
std::vector<MIPSStackWalk::StackFrame> frames = MIPSStackWalk::Walk(debug->GetPC(), debug->GetRegValue(0, 31), debug->GetRegValue(0, 29), entry, stackTop);
|
|
|
|
// TODO: Add context menu and clickability
|
|
int i = 0;
|
|
for (auto &frame : frames) {
|
|
const std::string entrySym = g_symbolMap->GetLabelString(frame.entry);
|
|
ImGui::PushID(i);
|
|
ImGui::TableNextRow();
|
|
ImGui::TableSetColumnIndex(0);
|
|
ImGui::TextUnformatted(entrySym.c_str());
|
|
ImGui::TableSetColumnIndex(1);
|
|
ImClickableValue("frameentry", frame.entry, control, ImCmd::SHOW_IN_CPU_DISASM);
|
|
ImGui::TableSetColumnIndex(2);
|
|
ImClickableValue("framepc", frame.pc, control, ImCmd::SHOW_IN_CPU_DISASM);
|
|
ImGui::TableSetColumnIndex(3);
|
|
ImGui::TextUnformatted("N/A"); // opcode, see the old debugger
|
|
ImGui::TableSetColumnIndex(4);
|
|
ImClickableValue("framepc", frame.sp, control, ImCmd::SHOW_IN_MEMORY_VIEWER);
|
|
ImGui::TableSetColumnIndex(5);
|
|
ImGui::Text("%d", frame.stackSize);
|
|
// TODO: More fields?
|
|
ImGui::PopID();
|
|
i++;
|
|
}
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::End();
|
|
}
|
|
|
|
static void DrawUtilityModules(ImConfig &cfg, ImControl &control) {
|
|
if (!ImGui::Begin("Utility Modules", &cfg.utilityModulesOpen) || !g_symbolMap) {
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
ImGui::TextUnformatted(
|
|
"These are fake module representations loaded by sceUtilityLoadModule\n"
|
|
"On a real PSP, these would be loaded from the BIOS.\n");
|
|
|
|
const std::map<int, u32> &modules = __UtilityGetLoadedModules();
|
|
if (ImGui::BeginTable("modules", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) {
|
|
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Load Address", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Load Size", ImGuiTableColumnFlags_WidthFixed);
|
|
|
|
ImGui::TableHeadersRow();
|
|
|
|
// TODO: Add context menu and clickability
|
|
int i = 0;
|
|
for (const auto &iter : modules) {
|
|
u32 loadedAddr = iter.second;
|
|
const ModuleLoadInfo *info = __UtilityModuleInfo(iter.first);
|
|
|
|
ImGui::PushID(i);
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
if (ImGui::Selectable(info->name, cfg.selectedUtilityModule == i, ImGuiSelectableFlags_SpanAllColumns)) {
|
|
cfg.selectedUtilityModule = i;
|
|
}
|
|
ImGui::TableNextColumn();
|
|
if (loadedAddr) {
|
|
ImClickableValue("addr", loadedAddr, control, ImCmd::SHOW_IN_MEMORY_VIEWER);
|
|
} else {
|
|
ImGui::TextUnformatted("-");
|
|
}
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%08x", info->size);
|
|
ImGui::PopID();
|
|
i++;
|
|
}
|
|
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::End();
|
|
}
|
|
|
|
static void DrawModules(const MIPSDebugInterface *debug, ImConfig &cfg, ImControl &control) {
|
|
if (!ImGui::Begin("Modules", &cfg.modulesOpen) || !g_symbolMap) {
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
ImGui::TextUnformatted("This shows modules that have been loaded by the game (not plain HLE)");
|
|
|
|
if (ImGui::BeginChild("module_list", ImVec2(170.0f, 0.0), ImGuiChildFlags_ResizeX)) {
|
|
if (ImGui::BeginTable("modules", 2, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) {
|
|
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableHeadersRow();
|
|
|
|
// TODO: Add context menu and clickability
|
|
kernelObjects.Iterate<PSPModule>([&cfg](int id, PSPModule *module) -> bool {
|
|
ImGui::PushID(id);
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
if (ImGui::Selectable(module->GetName(), cfg.selectedModuleId == id, ImGuiSelectableFlags_SpanAllColumns)) {
|
|
cfg.selectedModuleId = id;
|
|
}
|
|
ImGui::TableNextColumn();
|
|
ImGui::TextUnformatted(module->isFake ? "FAKE/HLE" : "normal");
|
|
ImGui::PopID();
|
|
return true;
|
|
});
|
|
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::EndChild();
|
|
}
|
|
ImGui::SameLine();
|
|
|
|
if (ImGui::BeginChild("info")) {
|
|
if (kernelObjects.Is<PSPModule>(cfg.selectedModuleId)) {
|
|
PSPModule *mod = kernelObjects.GetFast<PSPModule>(cfg.selectedModuleId);
|
|
if (mod) {
|
|
if (mod->isFake) {
|
|
ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(255, 255, 255, 170));
|
|
}
|
|
ImGui::Text("%s %d.%d (%s)\n", mod->GetName(), mod->nm.version[1], mod->nm.version[0], mod->isFake ? "FAKE/HLE" : "normal");
|
|
ImGui::Text("Attr: %08x (%s)\n", mod->nm.attribute, (mod->nm.attribute & 0x1000) ? "Kernel" : "User");
|
|
char buf[512];
|
|
mod->GetLongInfo(buf, sizeof(buf));
|
|
ImGui::TextUnformatted(buf);
|
|
if (mod->isFake) {
|
|
ImGui::PopStyleColor();
|
|
}
|
|
if (!mod->impModuleNames.empty() && ImGui::CollapsingHeader("Imported modules")) {
|
|
for (auto &name : mod->impModuleNames) {
|
|
ImGui::TextUnformatted(name);
|
|
}
|
|
}
|
|
if (!mod->expModuleNames.empty() && ImGui::CollapsingHeader("Exported modules")) {
|
|
for (auto &name : mod->expModuleNames) {
|
|
ImGui::TextUnformatted(name);
|
|
}
|
|
}
|
|
if (!mod->importedFuncs.empty() || !mod->importedVars.empty()) {
|
|
if (ImGui::CollapsingHeader("Imports")) {
|
|
if (!mod->importedVars.empty() && ImGui::CollapsingHeader("Vars")) {
|
|
for (auto &var : mod->importedVars) {
|
|
ImGui::TextUnformatted("(some var)"); // TODO
|
|
}
|
|
}
|
|
for (auto &import : mod->importedFuncs) {
|
|
// Look the name up in our HLE database.
|
|
const HLEFunction *func = GetHLEFunc(import.moduleName, import.nid);
|
|
ImGui::TextUnformatted(import.moduleName);
|
|
if (func) {
|
|
ImGui::SameLine();
|
|
ImGui::TextUnformatted(func->name);
|
|
}
|
|
ImGui::SameLine(); ImClickableValue("addr", import.stubAddr, control, ImCmd::SHOW_IN_CPU_DISASM);
|
|
}
|
|
}
|
|
}
|
|
if (!mod->exportedFuncs.empty() || !mod->exportedVars.empty()) {
|
|
if (ImGui::CollapsingHeader("Exports")) {
|
|
if (!mod->exportedVars.empty() && ImGui::CollapsingHeader("Vars")) {
|
|
for (auto &var : mod->importedVars) {
|
|
ImGui::TextUnformatted("(some var)"); // TODO
|
|
}
|
|
}
|
|
for (auto &exportFunc : mod->exportedFuncs) {
|
|
// Look the name up in our HLE database.
|
|
const HLEFunction *func = GetHLEFunc(exportFunc.moduleName, exportFunc.nid);
|
|
ImGui::TextUnformatted(exportFunc.moduleName);
|
|
if (func) {
|
|
ImGui::SameLine();
|
|
ImGui::TextUnformatted(func->name);
|
|
}
|
|
ImGui::SameLine(); ImClickableValue("addr", exportFunc.symAddr, control, ImCmd::SHOW_IN_CPU_DISASM);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
ImGui::TextUnformatted("(no module selected)");
|
|
}
|
|
ImGui::EndChild();
|
|
}
|
|
ImGui::End();
|
|
}
|
|
|
|
// Started as a module browser but really only draws from the symbols database, so let's
|
|
// evolve it to that.
|
|
static void DrawSymbols(const MIPSDebugInterface *debug, ImConfig &cfg, ImControl &control) {
|
|
if (!ImGui::Begin("Symbols", &cfg.symbolsOpen) || !g_symbolMap) {
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
// Reads from the symbol map.
|
|
std::vector<LoadedModuleInfo> modules = g_symbolMap->getAllModules();
|
|
if (ImGui::BeginTable("modules", 4, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) {
|
|
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Address", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Size", ImGuiTableColumnFlags_WidthFixed);
|
|
ImGui::TableSetupColumn("Active", ImGuiTableColumnFlags_WidthFixed);
|
|
|
|
ImGui::TableHeadersRow();
|
|
|
|
// TODO: Add context menu and clickability
|
|
for (int i = 0; i < (int)modules.size(); i++) {
|
|
auto &module = modules[i];
|
|
ImGui::PushID(i);
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
if (ImGui::Selectable(module.name.c_str(), cfg.selectedSymbolModule == i, ImGuiSelectableFlags_SpanAllColumns)) {
|
|
cfg.selectedSymbolModule = i;
|
|
}
|
|
ImGui::TableNextColumn();
|
|
ImClickableValue("addr", module.address, control, ImCmd::SHOW_IN_MEMORY_VIEWER);
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%08x", module.size);
|
|
ImGui::TableNextColumn();
|
|
ImGui::TextUnformatted(module.active ? "yes" : "no");
|
|
ImGui::PopID();
|
|
}
|
|
|
|
ImGui::EndTable();
|
|
}
|
|
|
|
if (cfg.selectedModuleId >= 0 && cfg.selectedModuleId < (int)modules.size()) {
|
|
// TODO: Show details
|
|
}
|
|
ImGui::End();
|
|
}
|
|
|
|
ImWatchWindow::ImWatchWindow() {}
|
|
|
|
void ImWatchWindow::Draw(ImConfig &cfg, ImControl &control, MIPSDebugInterface *mipsDebug) {
|
|
if (!ImGui::Begin("Watch", &cfg.watchOpen) || !g_symbolMap) {
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
// Refresh watches
|
|
int steppingCounter = Core_GetSteppingCounter();
|
|
int changes = false;
|
|
for (auto &watch : watches_) {
|
|
if (watch.steppingCounter != steppingCounter) {
|
|
watch.lastValue = watch.currentValue;
|
|
watch.steppingCounter = steppingCounter;
|
|
}
|
|
|
|
uint32_t prevValue = watch.currentValue;
|
|
watch.evaluateFailed = !parseExpression(mipsDebug, watch.expression, watch.currentValue);
|
|
}
|
|
|
|
if (ImGui::Button("Add Watch")) {
|
|
watches_.push_back(WatchInfo("untitled", "[0x88000000]", mipsDebug));
|
|
}
|
|
|
|
if (ImGui::BeginTable("watches", 4, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH | ImGuiTableFlags_Resizable)) {
|
|
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 120.0f);
|
|
ImGui::TableSetupColumn("Expression", ImGuiTableColumnFlags_WidthFixed, 120.0f);
|
|
ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthFixed, 100.0f);
|
|
ImGui::TableSetupColumn("Actions", ImGuiTableColumnFlags_WidthFixed, 50.0f);
|
|
|
|
ImGui::TableHeadersRow();
|
|
|
|
for (int i = 0; i < (int)watches_.size(); i++) {
|
|
auto &watch = watches_[i];
|
|
ImGui::PushID(i);
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
ImGui::TextUnformatted(watch.name.c_str());
|
|
ImGui::TableNextColumn();
|
|
if (editingWatchIndex_ == i && editingColumn_ == 1) {
|
|
ImGui::SetNextItemWidth(-1.0f); // Full width
|
|
if (setEditFocus_) {
|
|
ImGui::FocusItem();
|
|
setEditFocus_ = false;
|
|
}
|
|
bool confirmed = ImGui::InputText("##edit", editBuffer_, ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll);
|
|
// Filter for only enter presses
|
|
confirmed = confirmed && (ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_KeypadEnter));
|
|
if (confirmed || ImGui::IsItemDeactivated()) {
|
|
watch.SetExpression(editBuffer_, mipsDebug);
|
|
editingWatchIndex_ = -1;
|
|
}
|
|
} else {
|
|
ImGui::SetNextItemWidth(-1.0f); // Full width
|
|
ImGui::TextUnformatted(watch.originalExpression.c_str());
|
|
auto cellRect = ImGui::TableGetCellBgRect(ImGui::GetCurrentTable(), 1);
|
|
bool cellHovered = ImGui::IsMouseHoveringRect(cellRect.Min, cellRect.Max);
|
|
if (cellHovered && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
|
|
editingWatchIndex_ = i;
|
|
editingColumn_ = 1;
|
|
truncate_cpy(editBuffer_, watch.originalExpression.c_str());
|
|
setEditFocus_ = true;
|
|
}
|
|
}
|
|
ImGui::TableNextColumn();
|
|
if (watch.evaluateFailed) {
|
|
ImGui::TextUnformatted("(Error)");
|
|
} else {
|
|
bool changed = watch.currentValue != watch.lastValue;
|
|
if (changed) {
|
|
//ImGui::PushStyleColor(ImGuiCol_Text, ImDebuggerColor_Diff);
|
|
}
|
|
const uint32_t value = watch.currentValue;
|
|
float valuef = 0.0f;
|
|
switch (watch.format) {
|
|
case WatchFormat::HEX:
|
|
ImGui::Text("%08x", value);
|
|
break;
|
|
case WatchFormat::INT:
|
|
ImGui::Text("%d", value);
|
|
break;
|
|
case WatchFormat::FLOAT:
|
|
memcpy(&valuef, &value, sizeof(valuef));
|
|
ImGui::Text("%f", static_cast<float>(value));
|
|
break;
|
|
case WatchFormat::STR:
|
|
if (Memory::IsValidAddress(value)) {
|
|
uint32_t len = Memory::ClampValidSizeAt(value, 255);
|
|
ImGui::Text("%.*s", len, Memory::GetCharPointer(value));
|
|
} else {
|
|
ImGui::Text("%08x", value);
|
|
}
|
|
break;
|
|
}
|
|
if (changed) {
|
|
//ImGui::PopStyleColor(ImGuiCol_Text);
|
|
}
|
|
}
|
|
ImGui::TableNextColumn();
|
|
if (ImGui::SmallButton("X")) {
|
|
watches_.erase(watches_.begin() + i);
|
|
}
|
|
ImGui::SameLine();
|
|
if (ImGui::Button("Type")) {
|
|
ImGui::OpenPopup("watchType");
|
|
}
|
|
if (ImGui::BeginPopup("watchType")) {
|
|
ImGui::Text("Watch type");
|
|
if (ImGui::MenuItem("Hex", nullptr, watch.format == WatchFormat::HEX)) { watch.format = WatchFormat::HEX; }
|
|
if (ImGui::MenuItem("Int", nullptr, watch.format == WatchFormat::INT)) { watch.format = WatchFormat::INT; }
|
|
if (ImGui::MenuItem("Float", nullptr, watch.format == WatchFormat::FLOAT)) { watch.format = WatchFormat::FLOAT; }
|
|
if (ImGui::MenuItem("Str", nullptr, watch.format == WatchFormat::STR)) { watch.format = WatchFormat::STR; }
|
|
ImGui::EndPopup();
|
|
}
|
|
|
|
ImGui::PopID();
|
|
}
|
|
|
|
ImGui::EndTable();
|
|
}
|
|
|
|
ImGui::End();
|
|
}
|
|
|
|
void ImAtracToolWindow::Load() {
|
|
if (File::ReadBinaryFileToString(Path(atracPath_), &data_)) {
|
|
track_.reset(new Track());
|
|
AnalyzeAtracTrack((const u8 *)data_.data(), (u32)data_.size(), track_.get(), &error_);
|
|
} else {
|
|
error_ = "Failed to read file from disk. Bad path?";
|
|
}
|
|
}
|
|
|
|
void ImAtracToolWindow::Draw(ImConfig &cfg) {
|
|
if (!ImGui::Begin("Atrac Tool", &cfg.atracToolOpen) || !g_symbolMap) {
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
ImGui::InputText("File", atracPath_, sizeof(atracPath_));
|
|
ImGui::SameLine();
|
|
if (ImGui::Button("Choose...")) {
|
|
System_BrowseForFile(cfg.requesterToken, "Choose AT3 file", BrowseFileType::ATRAC3, [this](std::string_view filename, int) {
|
|
truncate_cpy(atracPath_, filename);
|
|
Load();
|
|
}, nullptr);
|
|
}
|
|
|
|
if (strlen(atracPath_) > 0) {
|
|
if (ImGui::Button("Load")) {
|
|
Load();
|
|
}
|
|
}
|
|
|
|
if (track_.get() != 0) {
|
|
ImGui::Text("Codec: %s", track_->codecType != PSP_CODEC_AT3 ? "at3+" : "at3");
|
|
ImGui::Text("Bitrate: %d kbps Channels: %d", track_->Bitrate(), track_->channels);
|
|
ImGui::Text("Frame size in bytes: %d (%04x) Output frame in samples: %d", track_->BytesPerFrame(), track_->BytesPerFrame(), track_->SamplesPerFrame());
|
|
ImGui::Text("First valid sample: %08x", track_->FirstSampleOffsetFull());
|
|
ImGui::Text("EndSample: %08x", track_->endSample);
|
|
}
|
|
|
|
if (data_.size()) {
|
|
if (ImGui::Button("Dump 64 raw frames")) {
|
|
std::string firstFrames = data_.substr(track_->dataByteOffset, track_->bytesPerFrame * 64);
|
|
System_BrowseForFileSave(cfg.requesterToken, "Save .at3raw", "at3.raw", BrowseFileType::ANY, [firstFrames](std::string_view filename, int) {
|
|
FILE *f = File::OpenCFile(Path(filename), "wb");
|
|
if (f) {
|
|
fwrite(firstFrames.data(), 1, firstFrames.size(), f);
|
|
fclose(f);
|
|
}
|
|
});
|
|
}
|
|
|
|
if (ImGui::Button("Unload")) {
|
|
data_.clear();
|
|
track_.reset(nullptr);
|
|
}
|
|
}
|
|
|
|
if (!error_.empty()) {
|
|
ImGui::TextUnformatted(error_.c_str());
|
|
}
|
|
|
|
ImGui::End();
|
|
}
|
|
|
|
void DrawHLEModules(ImConfig &config) {
|
|
if (!ImGui::Begin("HLE Modules", &config.hleModulesOpen)) {
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
const int moduleCount = GetNumRegisteredHLEModules();
|
|
std::vector<const HLEModule *> modules;
|
|
modules.reserve(moduleCount);
|
|
for (int i = 0; i < moduleCount; i++) {
|
|
modules.push_back(GetHLEModuleByIndex(i));
|
|
}
|
|
|
|
std::sort(modules.begin(), modules.end(), [](const HLEModule* a, const HLEModule* b) {
|
|
return a->name < b->name;
|
|
});
|
|
std::string label;
|
|
for (auto mod : modules) {
|
|
label = mod->name;
|
|
if (ImGui::TreeNode(label.c_str())) {
|
|
for (int j = 0; j < mod->numFunctions; j++) {
|
|
auto &func = mod->funcTable[j];
|
|
ImGui::Text("%s(%s)", func.name, func.argmask);
|
|
}
|
|
ImGui::TreePop();
|
|
}
|
|
if (ImGui::BeginPopupContextItem(label.c_str())) {
|
|
if (ImGui::MenuItem("Copy as JpscpTrace")) {
|
|
char *buffer = new char[1000000];
|
|
StringWriter w(buffer, 1000000);
|
|
|
|
for (int j = 0; j < mod->numFunctions; j++) {
|
|
auto &func = mod->funcTable[j];
|
|
// Translate the argmask to fit jpscptrace
|
|
std::string amask = func.argmask;
|
|
for (int i = 0; i < amask.size(); i++) {
|
|
switch (amask[i]) {
|
|
case 'i':
|
|
case 'I': amask[i] = 'd'; break;
|
|
case 'f':
|
|
case 'F': amask[i] = 'x'; break;
|
|
default:
|
|
// others go straight through (p)
|
|
break;
|
|
}
|
|
}
|
|
w.F("%s 0x%08x %d %s", func.name, func.ID, (int)strlen(func.argmask), amask.c_str()).endl();
|
|
}
|
|
System_CopyStringToClipboard(w.as_view());
|
|
delete[] buffer;
|
|
}
|
|
if (ImGui::MenuItem("Copy as imports.S")) {
|
|
char *buffer = new char[100000];
|
|
StringWriter w(buffer, 100000);
|
|
|
|
w.C(".set noreorder\n\n#include \"pspimport.s\"\n\n");
|
|
w.F("IMPORT_START \"%.*s\",0x00090011\n", (int)mod->name.size(), mod->name.data());
|
|
for (int j = 0; j < mod->numFunctions; j++) {
|
|
auto &func = mod->funcTable[j];
|
|
w.F("IMPORT_FUNC \"%.*s\",0x%08X,%s\n", (int)mod->name.size(), mod->name.data(), func.ID, func.name);
|
|
}
|
|
w.endl();
|
|
System_CopyStringToClipboard(w.as_view());
|
|
delete[] buffer;
|
|
}
|
|
ImGui::EndPopup();
|
|
}
|
|
}
|
|
|
|
ImGui::End();
|
|
}
|
|
|
|
ImDebugger::ImDebugger() {
|
|
reqToken_ = g_requestManager.GenerateRequesterToken();
|
|
cfg_.LoadConfig(ConfigPath());
|
|
g_normalTextColor = ImGui::GetStyleColorVec4(ImGuiCol_Text);
|
|
|
|
// Just enable ring buffer logging here, it's cheap (but needed for the log window)
|
|
g_logManager.EnableOutput(LogOutput::RingBuffer);
|
|
}
|
|
|
|
ImDebugger::~ImDebugger() {
|
|
cfg_.SaveConfig(ConfigPath());
|
|
}
|
|
|
|
void ImDebugger::Frame(MIPSDebugInterface *mipsDebug, GPUCommon *gpuDebug, Draw::DrawContext *draw) {
|
|
// Snapshot the coreState to avoid inconsistency.
|
|
const CoreState coreState = ::coreState;
|
|
|
|
const bool showDebugger = g_Config.bShowImDebugger && PSP_IsInited(); // This will be separated from showing the IM UI in general.
|
|
const bool allowSaveStates = !Achievements::HardcoreModeActive() && PSP_IsInited() && (!IsNetworkConnected() || g_Config.bAllowSavestateWhileConnected);
|
|
|
|
if (Achievements::HardcoreModeActive()) {
|
|
ImGui::Begin("RetroAchievements hardcore mode");
|
|
ImGui::Text("The debugger may not be used when the\nRetroAchievements hardcore mode is enabled.");
|
|
ImGui::Text("To use the debugger, go into Settings / Tools / RetroAchievements and disable them.");
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
// TODO: Pass mipsDebug in where needed instead.
|
|
g_disassemblyManager.setCpu(mipsDebug);
|
|
disasm_.View().setDebugger(mipsDebug);
|
|
for (int i = 0; i < 4; i++) {
|
|
mem_[i].View().setDebugger(mipsDebug);
|
|
}
|
|
|
|
// Watch the step counters to figure out when to update things.
|
|
|
|
if (showDebugger) {
|
|
if (lastCpuStepCount_ != Core_GetSteppingCounter()) {
|
|
lastCpuStepCount_ = Core_GetSteppingCounter();
|
|
snapshot_ = newSnapshot_; // Compare against the previous snapshot.
|
|
Snapshot(currentMIPS);
|
|
disasm_.NotifyStep();
|
|
}
|
|
|
|
if (gpuDebug && lastGpuStepCount_ != GPUStepping::GetSteppingCounter()) {
|
|
// A GPU step has happened since last time. This means that we should re-center the cursor.
|
|
// Snapshot();
|
|
lastGpuStepCount_ = GPUStepping::GetSteppingCounter();
|
|
SnapshotGPU(gpuDebug);
|
|
geDebugger_.NotifyStep();
|
|
}
|
|
}
|
|
|
|
ImControl control{};
|
|
|
|
if (ImGui::BeginMainMenuBar()) {
|
|
if (ImGui::BeginMenu("File")) {
|
|
if (ImGui::MenuItem("Load")) {
|
|
System_BrowseForFile(reqToken_, "Load", BrowseFileType::SAVE_STATE, [this](std::string_view responseString, int) {
|
|
Path path(responseString);
|
|
System_PostUIMessage(UIMessage::REQUEST_GAME_BOOT, path.ToString());
|
|
});
|
|
}
|
|
if (ImGui::MenuItem("Open directory...")) {
|
|
System_BrowseForFolder(reqToken_, "Load", Path(), [](std::string_view value, int) {
|
|
System_PostUIMessage(UIMessage::REQUEST_GAME_BOOT, value);
|
|
});
|
|
}
|
|
if (System_GetPropertyBool(SYSPROP_HAS_OPEN_DIRECTORY)) {
|
|
if (ImGui::MenuItem("Open memory stick")) {
|
|
System_LaunchUrl(LaunchUrlType::LOCAL_FOLDER, g_Config.memStickDirectory.ToString());
|
|
}
|
|
}
|
|
// TODO: Add "Open new instance"
|
|
if (allowSaveStates) {
|
|
ImGui::Separator();
|
|
if (ImGui::MenuItem(StringFromFormat("Load state (%d)", g_Config.iCurrentStateSlot).c_str(), "F4")) {
|
|
SaveState::LoadSlot(SaveState::GetGamePrefix(g_paramSFO), g_Config.iCurrentStateSlot, ShowMessageAfterSaveStateAction);
|
|
}
|
|
if (ImGui::MenuItem(StringFromFormat("Save state (%d)", g_Config.iCurrentStateSlot).c_str(), "F2")) {
|
|
SaveState::SaveSlot(SaveState::GetGamePrefix(g_paramSFO), g_Config.iCurrentStateSlot, ShowMessageAfterSaveStateAction);
|
|
}
|
|
if (ImGui::MenuItem("Previous slot")) {
|
|
SaveState::PrevSlot();
|
|
}
|
|
if (ImGui::MenuItem("Next slot", "F3")) {
|
|
SaveState::NextSlot();
|
|
}
|
|
ImGui::Separator();
|
|
if (ImGui::MenuItem("Load state from file...")) {
|
|
System_BrowseForFile(reqToken_, "Load", BrowseFileType::SAVE_STATE, [this](std::string_view fn, int) {
|
|
SaveState::Load(Path(fn), -1, ShowMessageAfterSaveStateAction);
|
|
});
|
|
}
|
|
if (ImGui::MenuItem("Save state to file...")) {
|
|
System_BrowseForFile(reqToken_, "Save", BrowseFileType::SAVE_STATE, [this](std::string_view fn, int) {
|
|
SaveState::Save(Path(fn), -1, ShowMessageAfterSaveStateAction);
|
|
});
|
|
}
|
|
} else {
|
|
ImGui::BeginDisabled();
|
|
ImGui::Text("Save states are not available");
|
|
ImGui::EndDisabled();
|
|
}
|
|
ImGui::Separator();
|
|
if (ImGui::MenuItem("Exit")) {
|
|
System_ExitApp();
|
|
}
|
|
ImGui::EndMenu();
|
|
}
|
|
|
|
if (ImGui::BeginMenu("Emulation")) {
|
|
if (ImGui::MenuItem("Pause")) {
|
|
System_PostUIMessage(UIMessage::REQUEST_GAME_PAUSE);
|
|
}
|
|
if (ImGui::MenuItem("Stop")) {
|
|
System_PostUIMessage(UIMessage::REQUEST_GAME_STOP);
|
|
}
|
|
if (ImGui::MenuItem("Reset")) {
|
|
System_PostUIMessage(UIMessage::REQUEST_GAME_RESET);
|
|
}
|
|
ImGui::Separator();
|
|
ImGui::EndMenu();
|
|
}
|
|
|
|
if (ImGui::BeginMenu("View")) {
|
|
if (PSP_IsInited()) {
|
|
ImGui::BeginDisabled(); // Can't change this setting while running.
|
|
}
|
|
ImGui::MenuItem("Show DevMenu button", nullptr, &g_Config.bShowDeveloperMenu);
|
|
if (PSP_IsInited()) {
|
|
ImGui::EndDisabled();
|
|
}
|
|
ImGui::EndMenu();
|
|
}
|
|
|
|
if (ImGui::BeginMenu("Debug")) {
|
|
if (PSP_IsInited()) {
|
|
switch (coreState) {
|
|
case CoreState::CORE_STEPPING_CPU:
|
|
if (ImGui::MenuItem("Run")) {
|
|
Core_Resume();
|
|
}
|
|
ImGui::Separator();
|
|
break;
|
|
case CoreState::CORE_RUNNING_CPU:
|
|
if (ImGui::MenuItem("Break")) {
|
|
Core_Break(BreakReason::DebugBreak);
|
|
}
|
|
ImGui::Separator();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
ImGui::MenuItemInverted("Break on load", nullptr, &g_Config.bAutoRun);
|
|
ImGui::MenuItem("Ignore bad memory accesses", nullptr, &g_Config.bIgnoreBadMemAccess);
|
|
ImGui::MenuItem("Break on frame timeout", nullptr, &g_Config.bBreakOnFrameTimeout);
|
|
ImGui::MenuItem("Fast memory", nullptr, &g_Config.bFastMemory);
|
|
ImGui::Separator();
|
|
if (ImGui::MenuItem("Take screenshot")) {
|
|
g_TakeScreenshot = true;
|
|
}
|
|
ImGui::MenuItem("Save screenshot as .png", nullptr, &g_Config.bScreenshotsAsPNG);
|
|
if (ImGui::MenuItem("Restart graphics")) {
|
|
System_PostUIMessage(UIMessage::RESTART_GRAPHICS);
|
|
}
|
|
if (System_GetPropertyBool(SYSPROP_HAS_TEXT_CLIPBOARD)) {
|
|
if (ImGui::MenuItem("Copy memory base to clipboard")) {
|
|
System_CopyStringToClipboard(StringFromFormat("%016llx", (uint64_t)(uintptr_t)Memory::base));
|
|
}
|
|
}
|
|
ImGui::Separator();
|
|
if (ImGui::MenuItem("Open web debugger")) {
|
|
OpenWebDebugger();
|
|
}
|
|
ImGui::Separator();
|
|
if (ImGui::MenuItem("Close")) {
|
|
g_Config.bShowImDebugger = false;
|
|
}
|
|
ImGui::EndMenu();
|
|
}
|
|
|
|
/*
|
|
if (ImGui::BeginMenu("Options")) {
|
|
ImGui::EndMenu();
|
|
}
|
|
*/
|
|
|
|
if (showDebugger) {
|
|
if (ImGui::BeginMenu("Core")) {
|
|
ImGui::MenuItem("Scheduler", nullptr, &cfg_.schedulerOpen);
|
|
ImGui::MenuItem("Time", nullptr, &cfg_.timeOpen);
|
|
ImGui::EndMenu();
|
|
}
|
|
if (ImGui::BeginMenu("CPU")) {
|
|
ImGui::MenuItem("CPU debugger", nullptr, &cfg_.disasmOpen);
|
|
ImGui::MenuItem("GPR regs", nullptr, &cfg_.gprOpen);
|
|
ImGui::MenuItem("FPR regs", nullptr, &cfg_.fprOpen);
|
|
ImGui::MenuItem("VFPU regs", nullptr, &cfg_.vfpuOpen);
|
|
ImGui::Separator();
|
|
ImGui::MenuItem("Callstacks", nullptr, &cfg_.callstackOpen);
|
|
ImGui::MenuItem("Breakpoints", nullptr, &cfg_.breakpointsOpen);
|
|
ImGui::MenuItem("Watch", nullptr, &cfg_.watchOpen);
|
|
ImGui::MenuItem("JIT viewer", nullptr, &cfg_.jitViewerOpen);
|
|
ImGui::EndMenu();
|
|
}
|
|
if (ImGui::BeginMenu("Symbols")) {
|
|
ImGui::MenuItem("Symbol browser", nullptr, &cfg_.symbolsOpen);
|
|
ImGui::Separator();
|
|
|
|
if (ImGui::MenuItem("Load .ppmap...")) {
|
|
System_BrowseForFile(reqToken_, "Load PPSSPP symbol map", BrowseFileType::SYMBOL_MAP, [this](std::string_view responseString, int) {
|
|
Path path(responseString);
|
|
if (!g_symbolMap->LoadSymbolMap(path)) {
|
|
ERROR_LOG(Log::Common, "Failed to load symbol map");
|
|
}
|
|
disasm_.DirtySymbolMap();
|
|
});
|
|
}
|
|
if (ImGui::MenuItem("Save .ppmap...")) {
|
|
System_BrowseForFileSave(reqToken_, "Save PPSSPP symbol map", "symbols.ppmap", BrowseFileType::SYMBOL_MAP, [](std::string_view responseString, int) {
|
|
Path path(responseString);
|
|
if (!g_symbolMap->SaveSymbolMap(path)) {
|
|
ERROR_LOG(Log::Common, "Failed to save symbol map");
|
|
}
|
|
});
|
|
}
|
|
if (ImGui::MenuItem("Load No$ .sym...")) {
|
|
System_BrowseForFile(reqToken_, "Load No$ symbol map", BrowseFileType::SYMBOL_MAP, [this](std::string_view responseString, int) {
|
|
Path path(responseString);
|
|
if (!g_symbolMap->LoadNocashSym(path)) {
|
|
ERROR_LOG(Log::Common, "Failed to load No$ symbol map");
|
|
}
|
|
disasm_.DirtySymbolMap();
|
|
});
|
|
}
|
|
if (ImGui::MenuItem("Save No$ .sym...")) {
|
|
System_BrowseForFileSave(reqToken_, "Save No$ symbol map", "symbols.sym", BrowseFileType::SYMBOL_MAP, [](std::string_view responseString, int) {
|
|
Path path(responseString);
|
|
if (!g_symbolMap->SaveNocashSym(path)) {
|
|
ERROR_LOG(Log::Common, "Failed to save No$ symbol map");
|
|
}
|
|
});
|
|
}
|
|
ImGui::Separator();
|
|
ImGui::MenuItem("Compress .ppmap files", nullptr, &g_Config.bCompressSymbols);
|
|
if (ImGui::MenuItem("Reset symbol map")) {
|
|
g_symbolMap->Clear();
|
|
disasm_.DirtySymbolMap();
|
|
// NotifyDebuggerMapLoaded();
|
|
}
|
|
ImGui::EndMenu();
|
|
}
|
|
if (ImGui::BeginMenu("Memory")) {
|
|
for (int i = 0; i < 4; i++) {
|
|
char title[64];
|
|
snprintf(title, sizeof(title), "Memory %d", i + 1);
|
|
ImGui::MenuItem(title, nullptr, &cfg_.memViewOpen[i]);
|
|
}
|
|
ImGui::MenuItem("Memory Dumper", nullptr, &cfg_.memDumpOpen);
|
|
ImGui::EndMenu();
|
|
}
|
|
if (ImGui::BeginMenu("OS HLE")) {
|
|
ImGui::MenuItem("HLE module browser", nullptr, &cfg_.hleModulesOpen);
|
|
ImGui::MenuItem("File System Browser", nullptr, &cfg_.filesystemBrowserOpen);
|
|
ImGui::MenuItem("Kernel Objects", nullptr, &cfg_.kernelObjectsOpen);
|
|
ImGui::MenuItem("Threads", nullptr, &cfg_.threadsOpen);
|
|
ImGui::MenuItem("Modules", nullptr, &cfg_.modulesOpen);
|
|
ImGui::MenuItem("Utility Modules", nullptr, &cfg_.utilityModulesOpen);
|
|
ImGui::EndMenu();
|
|
}
|
|
if (ImGui::BeginMenu("Graphics")) {
|
|
ImGui::MenuItem("GE Debugger", nullptr, &cfg_.geDebuggerOpen);
|
|
ImGui::MenuItem("GE State", nullptr, &cfg_.geStateOpen);
|
|
ImGui::MenuItem("GE Vertices", nullptr, &cfg_.geVertsOpen);
|
|
ImGui::MenuItem("Display Output", nullptr, &cfg_.displayOpen);
|
|
ImGui::MenuItem("Textures", nullptr, &cfg_.texturesOpen);
|
|
ImGui::MenuItem("Framebuffers", nullptr, &cfg_.framebuffersOpen);
|
|
ImGui::MenuItem("Pixel Viewer", nullptr, &cfg_.pixelViewerOpen);
|
|
// More to come here...
|
|
ImGui::EndMenu();
|
|
}
|
|
if (ImGui::BeginMenu("Audio/Video")) {
|
|
ImGui::MenuItem("SasAudio mixer", nullptr, &cfg_.sasAudioOpen);
|
|
ImGui::MenuItem("Raw audio channels", nullptr, &cfg_.audioChannelsOpen);
|
|
ImGui::MenuItem("AV Decoder contexts", nullptr, &cfg_.mediaDecodersOpen);
|
|
ImGui::Separator();
|
|
ImGui::MenuItem("Audio output / buffer", nullptr, &cfg_.audioOutOpen);
|
|
ImGui::EndMenu();
|
|
}
|
|
if (ImGui::BeginMenu("Network")) {
|
|
ImGui::MenuItem("ApCtl", nullptr, &cfg_.apctlOpen);
|
|
ImGui::MenuItem("Sockets", nullptr, &cfg_.socketsOpen);
|
|
ImGui::MenuItem("NP", nullptr, &cfg_.npOpen);
|
|
ImGui::MenuItem("AdHoc", nullptr, &cfg_.adhocOpen);
|
|
ImGui::EndMenu();
|
|
}
|
|
}
|
|
|
|
if (ImGui::BeginMenu("Tools")) {
|
|
ImGui::MenuItem("Lua Console", nullptr, &cfg_.luaConsoleOpen);
|
|
ImGui::MenuItem("Log", nullptr, &cfg_.logOpen);
|
|
ImGui::MenuItem("Log channels", nullptr, &cfg_.logConfigOpen);
|
|
ImGui::MenuItem("Debug stats", nullptr, &cfg_.debugStatsOpen);
|
|
ImGui::Separator();
|
|
if (PSP_IsInited()) {
|
|
ImGui::MenuItem("ParamSFO viewer", nullptr, &cfg_.paramSFOOpen);
|
|
}
|
|
ImGui::MenuItem("Struct viewer", nullptr, &cfg_.structViewerOpen);
|
|
ImGui::MenuItem("Atrac Tool", nullptr, &cfg_.atracToolOpen);
|
|
ImGui::EndMenu();
|
|
}
|
|
|
|
if (ImGui::BeginMenu("Misc")) {
|
|
ImGui::MenuItem("PPSSPP Internals", nullptr, &cfg_.internalsOpen);
|
|
ImGui::MenuItem("Dear ImGui Demo", nullptr, &cfg_.demoOpen);
|
|
ImGui::MenuItem("Dear ImGui Style editor", nullptr, &cfg_.styleEditorOpen);
|
|
ImGui::EndMenu();
|
|
}
|
|
|
|
if (ImGui::MenuItem("Close")) {
|
|
g_Config.bShowImDebugger = false;
|
|
}
|
|
switch (coreState) {
|
|
case CoreState::CORE_STEPPING_CPU:
|
|
if (ImGui::MenuItem(">> Run")) {
|
|
Core_Resume();
|
|
}
|
|
break;
|
|
case CoreState::CORE_RUNNING_CPU:
|
|
if (ImGui::MenuItem("|| Break")) {
|
|
Core_Break(BreakReason::DebugBreak);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
ImGui::EndMainMenuBar();
|
|
}
|
|
|
|
if (cfg_.demoOpen) {
|
|
ImGui::ShowDemoWindow(&cfg_.demoOpen);
|
|
}
|
|
|
|
if (cfg_.styleEditorOpen) {
|
|
ImGui::ShowStyleEditor();
|
|
}
|
|
|
|
if (PSP_IsInited()) {
|
|
if (cfg_.disasmOpen) {
|
|
disasm_.Draw(mipsDebug, cfg_, control, coreState);
|
|
}
|
|
|
|
if (cfg_.gprOpen) {
|
|
DrawGPRs(cfg_, control, mipsDebug, snapshot_);
|
|
}
|
|
|
|
if (cfg_.fprOpen) {
|
|
DrawFPRs(cfg_, control, mipsDebug, snapshot_);
|
|
}
|
|
|
|
if (cfg_.vfpuOpen) {
|
|
DrawVFPU(cfg_, control, mipsDebug, snapshot_);
|
|
}
|
|
|
|
if (cfg_.breakpointsOpen) {
|
|
DrawBreakpointsView(mipsDebug, cfg_);
|
|
}
|
|
|
|
if (cfg_.filesystemBrowserOpen) {
|
|
DrawFilesystemBrowser(cfg_);
|
|
}
|
|
|
|
if (cfg_.audioChannelsOpen) {
|
|
DrawAudioChannels(cfg_, control);
|
|
}
|
|
|
|
if (cfg_.audioOutOpen) {
|
|
DrawAudioOut(cfg_, control);
|
|
}
|
|
|
|
if (cfg_.sasAudioOpen) {
|
|
DrawSasAudio(cfg_);
|
|
}
|
|
|
|
if (cfg_.kernelObjectsOpen) {
|
|
DrawKernelObjects(cfg_);
|
|
}
|
|
|
|
if (cfg_.threadsOpen) {
|
|
DrawThreadView(cfg_, control);
|
|
}
|
|
|
|
if (cfg_.callstackOpen) {
|
|
DrawCallStacks(mipsDebug, cfg_, control);
|
|
}
|
|
|
|
if (cfg_.modulesOpen) {
|
|
DrawModules(mipsDebug, cfg_, control);
|
|
}
|
|
|
|
if (cfg_.symbolsOpen) {
|
|
DrawSymbols(mipsDebug, cfg_, control);
|
|
}
|
|
|
|
if (cfg_.utilityModulesOpen) {
|
|
DrawUtilityModules(cfg_, control);
|
|
}
|
|
|
|
if (cfg_.mediaDecodersOpen) {
|
|
DrawMediaDecodersView(cfg_, control);
|
|
}
|
|
|
|
if (cfg_.hleModulesOpen) {
|
|
DrawHLEModules(cfg_);
|
|
}
|
|
|
|
if (cfg_.atracToolOpen) {
|
|
atracToolWindow_.Draw(cfg_);
|
|
}
|
|
|
|
if (gpuDebug) {
|
|
if (cfg_.framebuffersOpen) {
|
|
DrawFramebuffersWindow(cfg_, gpuDebug->GetFramebufferManagerCommon());
|
|
}
|
|
|
|
if (cfg_.texturesOpen) {
|
|
DrawTexturesWindow(cfg_, gpuDebug->GetTextureCacheCommon());
|
|
}
|
|
}
|
|
|
|
if (cfg_.jitViewerOpen) {
|
|
jitViewer_.Draw(cfg_, control);
|
|
}
|
|
|
|
if (cfg_.displayOpen) {
|
|
DrawDisplayWindow(cfg_, gpuDebug->GetFramebufferManagerCommon());
|
|
}
|
|
|
|
if (cfg_.debugStatsOpen) {
|
|
DrawDebugStatsWindow(cfg_);
|
|
}
|
|
|
|
if (cfg_.paramSFOOpen) {
|
|
DrawParamSFO(cfg_, control);
|
|
}
|
|
|
|
if (cfg_.geDebuggerOpen) {
|
|
geDebugger_.Draw(cfg_, control, gpuDebug, draw);
|
|
}
|
|
|
|
if (cfg_.geStateOpen) {
|
|
geStateWindow_.Draw(cfg_, control, gpuDebug);
|
|
}
|
|
|
|
if (cfg_.geVertsOpen) {
|
|
DrawImGeVertsWindow(cfg_, control, gpuDebug);
|
|
}
|
|
|
|
if (cfg_.schedulerOpen) {
|
|
DrawSchedulerView(cfg_);
|
|
}
|
|
|
|
if (cfg_.timeOpen) {
|
|
DrawTimeView(cfg_);
|
|
}
|
|
|
|
if (cfg_.pixelViewerOpen) {
|
|
pixelViewer_.Draw(cfg_, control, gpuDebug, draw);
|
|
}
|
|
|
|
if (cfg_.memDumpOpen) {
|
|
memDumpWindow_.Draw(cfg_, mipsDebug);
|
|
}
|
|
|
|
if (cfg_.watchOpen) {
|
|
watchWindow_.Draw(cfg_, control, mipsDebug);
|
|
}
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
if (cfg_.memViewOpen[i]) {
|
|
mem_[i].Draw(mipsDebug, cfg_, control, i);
|
|
}
|
|
}
|
|
|
|
if (cfg_.socketsOpen) {
|
|
DrawSockets(cfg_);
|
|
}
|
|
|
|
if (cfg_.npOpen) {
|
|
DrawNp(cfg_);
|
|
}
|
|
|
|
if (cfg_.adhocOpen) {
|
|
DrawAdhoc(cfg_);
|
|
}
|
|
|
|
if (cfg_.apctlOpen) {
|
|
DrawApctl(cfg_);
|
|
}
|
|
}
|
|
|
|
if (cfg_.logConfigOpen) {
|
|
DrawLogConfig(cfg_);
|
|
}
|
|
|
|
if (cfg_.logOpen) {
|
|
logWindow_.Draw(cfg_);
|
|
}
|
|
|
|
if (cfg_.structViewerOpen) {
|
|
structViewer_.Draw(cfg_, control, mipsDebug);
|
|
}
|
|
|
|
if (cfg_.internalsOpen) {
|
|
DrawInternals(cfg_);
|
|
}
|
|
|
|
if (externalCommand_.cmd != ImCmd::NONE) {
|
|
control.command = externalCommand_;
|
|
externalCommand_.cmd = ImCmd::NONE;
|
|
}
|
|
|
|
// Process UI commands
|
|
switch (control.command.cmd) {
|
|
case ImCmd::SHOW_IN_CPU_DISASM:
|
|
disasm_.View().gotoAddr(control.command.param);
|
|
cfg_.disasmOpen = true;
|
|
ImGui::SetWindowFocus(disasm_.Title());
|
|
break;
|
|
case ImCmd::SHOW_IN_GE_DISASM:
|
|
geDebugger_.View().GotoAddr(control.command.param);
|
|
cfg_.geDebuggerOpen = true;
|
|
ImGui::SetWindowFocus(geDebugger_.Title());
|
|
break;
|
|
case ImCmd::SHOW_IN_MEMORY_VIEWER:
|
|
{
|
|
u32 index = control.command.param2;
|
|
_dbg_assert_(index < 4);
|
|
mem_[index].GotoAddr(control.command.param);
|
|
cfg_.memViewOpen[index] = true;
|
|
ImGui::SetWindowFocus(ImMemWindow::Title(index));
|
|
break;
|
|
}
|
|
case ImCmd::SHOW_IN_MEMORY_DUMPER:
|
|
{
|
|
cfg_.memDumpOpen = true;
|
|
memDumpWindow_.SetRange(control.command.param, control.command.param2, (MemDumpMode)control.command.param3);
|
|
ImGui::SetWindowFocus(memDumpWindow_.Title());
|
|
break;
|
|
}
|
|
case ImCmd::TRIGGER_FIND_POPUP:
|
|
// TODO
|
|
break;
|
|
case ImCmd::NONE:
|
|
break;
|
|
case ImCmd::SHOW_IN_PIXEL_VIEWER:
|
|
break;
|
|
}
|
|
if (cfg_.luaConsoleOpen) {
|
|
luaConsole_.Draw(cfg_);
|
|
}
|
|
}
|
|
|
|
void ImDebugger::Snapshot(MIPSState *mips) {
|
|
memcpy(newSnapshot_.gpr, mips->r, sizeof(newSnapshot_.gpr));
|
|
memcpy(newSnapshot_.fpr, mips->fs, sizeof(newSnapshot_.fpr));
|
|
memcpy(newSnapshot_.vpr, mips->v, sizeof(newSnapshot_.vpr));
|
|
newSnapshot_.pc = mips->pc;
|
|
newSnapshot_.lo = mips->lo;
|
|
newSnapshot_.hi = mips->hi;
|
|
newSnapshot_.ll = mips->llBit;
|
|
pixelViewer_.Snapshot();
|
|
}
|
|
|
|
void ImDebugger::SnapshotGPU(GPUCommon *gpuDebug) {
|
|
pixelViewer_.Snapshot();
|
|
}
|
|
|
|
void ImDebugger::DeviceLost() {
|
|
pixelViewer_.DeviceLost();
|
|
geDebugger_.DeviceLost();
|
|
}
|
|
|
|
Path ImDebugger::ConfigPath() {
|
|
return GetSysDirectory(DIRECTORY_SYSTEM) / "imdebugger.ini";
|
|
}
|
|
|
|
// TODO: Move this into the main config at some point.
|
|
// But, I don't really want Core to know about the ImDebugger..
|
|
|
|
void ImConfig::LoadConfig(const Path &iniFile) {
|
|
requesterToken = g_requestManager.GenerateRequesterToken();
|
|
|
|
IniFile ini;
|
|
ini.Load(iniFile); // Ignore return value, might not exist yet. In that case we'll end up loading defaults.
|
|
SyncConfig(&ini, false);
|
|
}
|
|
|
|
void ImConfig::SaveConfig(const Path &iniFile) {
|
|
IniFile ini;
|
|
ini.Load(iniFile); // ignore return value, might not exist yet
|
|
SyncConfig(&ini, true);
|
|
ini.Save(iniFile);
|
|
}
|
|
|
|
class Syncer {
|
|
public:
|
|
explicit Syncer(bool save) : save_(save) {}
|
|
void SetSection(Section *section) { section_ = section; }
|
|
template<class T>
|
|
void Sync(std::string_view key, T *value, T defaultValue) {
|
|
if (save_) {
|
|
section_->Set(key, *value);
|
|
} else {
|
|
if (!section_->Get(key, value)) {
|
|
*value = defaultValue;
|
|
}
|
|
}
|
|
}
|
|
private:
|
|
Section *section_ = nullptr;
|
|
bool save_;
|
|
};
|
|
|
|
void ImConfig::SyncConfig(IniFile *ini, bool save) {
|
|
Syncer sync(save);
|
|
sync.SetSection(ini->GetOrCreateSection("Windows"));
|
|
sync.Sync("disasmOpen", &disasmOpen, true);
|
|
sync.Sync("demoOpen", &demoOpen, false);
|
|
sync.Sync("gprOpen", &gprOpen, false);
|
|
sync.Sync("fprOpen", &fprOpen, false);
|
|
sync.Sync("vfpuOpen", &vfpuOpen, false);
|
|
sync.Sync("threadsOpen", &threadsOpen, false);
|
|
sync.Sync("callstackOpen", &callstackOpen, false);
|
|
sync.Sync("breakpointsOpen", &breakpointsOpen, false);
|
|
sync.Sync("symbolsOpen", &symbolsOpen, false);
|
|
sync.Sync("modulesOpen", &modulesOpen, false);
|
|
sync.Sync("hleModulesOpen", &hleModulesOpen, false);
|
|
sync.Sync("mediaDecodersOpen", &mediaDecodersOpen, false);
|
|
sync.Sync("structViewerOpen", &structViewerOpen, false);
|
|
sync.Sync("framebuffersOpen", &framebuffersOpen, false);
|
|
sync.Sync("displayOpen", &displayOpen, true);
|
|
sync.Sync("styleEditorOpen", &styleEditorOpen, false);
|
|
sync.Sync("filesystemBrowserOpen", &filesystemBrowserOpen, false);
|
|
sync.Sync("kernelObjectsOpen", &kernelObjectsOpen, false);
|
|
sync.Sync("audioChannelsOpen", &audioChannelsOpen, false);
|
|
sync.Sync("audioOutOpen", &audioOutOpen, false);
|
|
sync.Sync("texturesOpen", &texturesOpen, false);
|
|
sync.Sync("debugStatsOpen", &debugStatsOpen, false);
|
|
sync.Sync("geDebuggerOpen", &geDebuggerOpen, false);
|
|
sync.Sync("geStateOpen", &geStateOpen, false);
|
|
sync.Sync("geVertsOpen", &geVertsOpen, false);
|
|
sync.Sync("schedulerOpen", &schedulerOpen, false);
|
|
sync.Sync("timeOpen", &timeOpen, false);
|
|
sync.Sync("socketsOpen", &socketsOpen, false);
|
|
sync.Sync("npOpen", &npOpen, false);
|
|
sync.Sync("adhocOpen", &adhocOpen, false);
|
|
sync.Sync("apctlOpen", &apctlOpen, false);
|
|
sync.Sync("pixelViewerOpen", &pixelViewerOpen, false);
|
|
sync.Sync("internalsOpen", &internalsOpen, false);
|
|
sync.Sync("sasAudioOpen", &sasAudioOpen, false);
|
|
sync.Sync("logConfigOpen", &logConfigOpen, false);
|
|
sync.Sync("logOpen", &logOpen, false);
|
|
sync.Sync("luaConsoleOpen", &luaConsoleOpen, false);
|
|
sync.Sync("utilityModulesOpen", &utilityModulesOpen, false);
|
|
sync.Sync("memDumpOpen", &memDumpOpen, false);
|
|
sync.Sync("watchOpen", &watchOpen, false);
|
|
sync.Sync("paramSFOOpen", ¶mSFOOpen, false);
|
|
sync.Sync("atracToolOpen", &atracToolOpen, false);
|
|
sync.Sync("jitViewerOpen", &jitViewerOpen, false);
|
|
for (int i = 0; i < 4; i++) {
|
|
char name[64];
|
|
snprintf(name, sizeof(name), "memory%dOpen", i + 1);
|
|
sync.Sync(name, &memViewOpen[i], false);
|
|
}
|
|
|
|
sync.SetSection(ini->GetOrCreateSection("Settings"));
|
|
sync.Sync("displayLatched", &displayLatched, false);
|
|
sync.Sync("realtimePixelPreview", &realtimePixelPreview, false);
|
|
}
|