mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
895 lines
25 KiB
C++
895 lines
25 KiB
C++
#include <cctype>
|
|
#include <cmath>
|
|
#include <iomanip>
|
|
#include <cstdio>
|
|
#include <sstream>
|
|
|
|
#include "ext/imgui/imgui.h"
|
|
#include "ext/imgui/imgui_internal.h"
|
|
#include "ext/imgui/imgui_impl_thin3d.h"
|
|
|
|
#include "ext/xxhash.h"
|
|
#include "Common/StringUtils.h"
|
|
#include "Common/File/FileUtil.h"
|
|
#include "Core/Config.h"
|
|
#include "Core/MemMap.h"
|
|
#include "Core/Reporting.h"
|
|
#include "Core/RetroAchievements.h"
|
|
#include "Common/System/Display.h"
|
|
|
|
#include "UI/ImDebugger/ImDebugger.h"
|
|
#include "UI/ImDebugger/ImMemView.h"
|
|
|
|
ImMemView::ImMemView() {
|
|
windowStart_ = curAddress_;
|
|
selectRangeStart_ = curAddress_;
|
|
selectRangeEnd_ = curAddress_ + 1;
|
|
lastSelectReset_ = curAddress_;
|
|
}
|
|
|
|
ImMemView::~ImMemView() {}
|
|
|
|
static uint32_t pickTagColor(std::string_view tag) {
|
|
uint32_t colors[6] = { 0xFF301010, 0xFF103030, 0xFF403010, 0xFF103000, 0xFF301030, 0xFF101030 };
|
|
int which = XXH3_64bits(tag.data(), tag.length()) % ARRAY_SIZE(colors);
|
|
return colors[which];
|
|
}
|
|
|
|
void ImMemView::Draw(ImDrawList *drawList) {
|
|
if (!debugger_->isAlive()) {
|
|
return;
|
|
}
|
|
|
|
ImGui_PushFixedFont();
|
|
|
|
rowHeight_ = ImGui::GetTextLineHeightWithSpacing();
|
|
charWidth_ = ImGui::CalcTextSize("W", nullptr, false, -1.0f).x;
|
|
charHeight_ = ImGui::GetTextLineHeight();
|
|
|
|
offsetPositionY_ = offsetLine * rowHeight_;
|
|
|
|
addressStartX_ = charWidth_;
|
|
hexStartX_ = addressStartX_ + 9 * charWidth_;
|
|
asciiStartX_ = hexStartX_ + (rowSize_ * 3 + 1) * charWidth_;
|
|
|
|
const ImVec2 canvas_p0 = ImGui::GetCursorScreenPos(); // ImDrawList API uses screen coordinates!
|
|
const ImVec2 canvas_sz = ImGui::GetContentRegionAvail(); // Resize canvas to what's available
|
|
const ImVec2 canvas_p1 = ImVec2(canvas_p0.x + canvas_sz.x, canvas_p0.y + canvas_sz.y);
|
|
|
|
// This will catch our interactions
|
|
bool pressed = ImGui::InvisibleButton("canvas", canvas_sz, ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight);
|
|
const bool is_hovered = ImGui::IsItemHovered(); // Hovered
|
|
const bool is_active = ImGui::IsItemActive(); // Held
|
|
|
|
if (pressed) {
|
|
// INFO_LOG(Log::System, "Pressed");
|
|
}
|
|
ImGui::SetItemKeyOwner(ImGuiKey_MouseWheelY);
|
|
|
|
visibleRows_ = canvas_sz.y / rowHeight_;
|
|
|
|
if (displayOffsetScale_) {
|
|
// visibleRows_ is calculated based on the size of the control, but X rows have already been used for the offsets and are no longer usable
|
|
visibleRows_ -= offsetSpace;
|
|
}
|
|
|
|
Bounds bounds;
|
|
bounds.x = canvas_p0.x;
|
|
bounds.y = canvas_p0.y;
|
|
bounds.w = canvas_p1.x - canvas_p0.x;
|
|
bounds.h = canvas_p1.y - canvas_p0.y;
|
|
|
|
drawList->PushClipRect(canvas_p0, canvas_p1, true);
|
|
drawList->AddRectFilled(canvas_p0, canvas_p1, IM_COL32(25, 25, 25, 255));
|
|
|
|
if (displayOffsetScale_)
|
|
drawOffsetScale(drawList);
|
|
|
|
std::vector<MemBlockInfo> memRangeInfo = FindMemInfoByFlag(highlightFlags_, windowStart_, (visibleRows_ + 1) * rowSize_);
|
|
|
|
_assert_msg_(((windowStart_ | rowSize_) & 3) == 0, "readMemory() can't handle unaligned reads");
|
|
|
|
// draw one extra row that may be partially visible
|
|
for (int i = 0; i < visibleRows_ + 1; i++) {
|
|
int rowY = rowHeight_ * i;
|
|
// Skip the first X rows to make space for the offsets.
|
|
if (displayOffsetScale_)
|
|
rowY += rowHeight_ * offsetSpace;
|
|
|
|
char temp[32];
|
|
uint32_t address = windowStart_ + i * rowSize_;
|
|
snprintf(temp, sizeof(temp), "%08x", address);
|
|
drawList->AddText(ImVec2(canvas_p0.x + addressStartX_, canvas_p0.y + rowY), IM_COL32(0xE0, 0xE0, 0xE0, 0xFF), temp);
|
|
|
|
union {
|
|
uint32_t words[4];
|
|
uint8_t bytes[16];
|
|
} memory;
|
|
int valid = debugger_ != nullptr && debugger_->isAlive() ? Memory::ValidSize(address, 16) / 4 : 0;
|
|
for (int i = 0; i < valid; ++i) {
|
|
memory.words[i] = debugger_->readMemory(address + i * 4);
|
|
}
|
|
|
|
for (int j = 0; j < rowSize_; j++) {
|
|
const uint32_t byteAddress = (address + j) & ~0xC0000000;
|
|
std::string_view tag;
|
|
bool tagContinues = false;
|
|
for (const auto &info : memRangeInfo) {
|
|
if (info.start <= byteAddress && info.start + info.size > byteAddress) {
|
|
tag = info.tag; // doesn't take ownership!
|
|
tagContinues = byteAddress + 1 < info.start + info.size;
|
|
}
|
|
}
|
|
|
|
int hexX = hexStartX_ + j * 3 * charWidth_;
|
|
int hexLen = 2;
|
|
int asciiX = asciiStartX_ + j * (charWidth_ + 2);
|
|
|
|
char c;
|
|
if (valid) {
|
|
snprintf(temp, sizeof(temp), "%02x ", memory.bytes[j]);
|
|
c = (char)memory.bytes[j];
|
|
if (memory.bytes[j] < 32 || memory.bytes[j] >= 128)
|
|
c = '.';
|
|
} else {
|
|
truncate_cpy(temp, "??");
|
|
c = '.';
|
|
}
|
|
|
|
ImColor standardBG = 0xFF000000;
|
|
bool continueRect = false;
|
|
|
|
ImColor hexBGCol = 0;
|
|
ImColor hexTextCol = 0xFFFFFFFF;
|
|
ImColor asciiBGCol = 0;
|
|
ImColor asciiTextCol = 0xFFFFFFFF;
|
|
int underline = -1;
|
|
|
|
const ImColor primarySelFg = 0xFFFFFFFF;
|
|
const ImColor primarySelBg = 0xFFFF9933;
|
|
const ImColor secondarySelFg = 0xFFFFFFFF;
|
|
const ImColor secondarySelBg = 0xFF808080;
|
|
|
|
if (address + j >= selectRangeStart_ && address + j < selectRangeEnd_ && !searching_) {
|
|
if (asciiSelected_) {
|
|
hexBGCol = secondarySelBg;
|
|
hexTextCol = secondarySelFg;
|
|
asciiBGCol = primarySelBg;
|
|
asciiTextCol = primarySelFg;
|
|
} else {
|
|
hexBGCol = primarySelBg;
|
|
hexTextCol = primarySelFg;
|
|
asciiBGCol = secondarySelBg;
|
|
asciiTextCol = secondarySelFg;
|
|
if (address + j == curAddress_)
|
|
underline = selectedNibble_;
|
|
}
|
|
if (!tag.empty() && tagContinues) {
|
|
continueRect = true;
|
|
}
|
|
} else if (!tag.empty()) {
|
|
hexBGCol = pickTagColor(tag);
|
|
continueRect = tagContinues;
|
|
asciiBGCol = hexBGCol;
|
|
}
|
|
|
|
ImColor fg = hexTextCol;
|
|
ImColor bg = hexBGCol;
|
|
// SelectObject(hdc, underline == 0 ? (HGDIOBJ)underlineFont : (HGDIOBJ)font);
|
|
if (bg != 0) {
|
|
int bgWidth = 2; // continueRect ? 3 : 2;
|
|
drawList->AddRectFilled(ImVec2(canvas_p0.x + hexX - 1, canvas_p0.y + rowY), ImVec2(canvas_p0.x + hexX + charWidth_ * bgWidth, canvas_p0.y + rowY + charHeight_), bg);
|
|
}
|
|
drawList->AddText(ImVec2(canvas_p0.x + hexX, canvas_p0.y + rowY), fg, &temp[0], &temp[2]);
|
|
if (underline >= 0) {
|
|
float x = canvas_p0.x + hexX + underline * charWidth_;
|
|
drawList->AddRectFilled(ImVec2(x, canvas_p0.y + rowY + charHeight_ - 2), ImVec2(x + charWidth_, canvas_p0.y + rowY + charHeight_), IM_COL32(0xFF, 0xFF, 0xFF, 0xFF));
|
|
}
|
|
|
|
fg = asciiTextCol;
|
|
bg = asciiBGCol;
|
|
if (bg) {
|
|
drawList->AddRectFilled(ImVec2(canvas_p0.x + asciiX, canvas_p0.y + rowY), ImVec2(canvas_p0.x + asciiX + charWidth_, canvas_p0.y + rowY + charHeight_), bg);
|
|
}
|
|
drawList->AddText(ImVec2(canvas_p0.x + asciiX, canvas_p0.y + rowY), fg, &c, &c + 1);
|
|
}
|
|
}
|
|
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
ImVec2 mousePos = ImVec2(io.MousePos.x - canvas_p0.x, io.MousePos.y - canvas_p0.y);
|
|
if (is_hovered && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
|
|
// INFO_LOG(Log::System, "Mousedown %f,%f active:%d hover:%d", mousePos.x, mousePos.y, is_active, is_hovered);
|
|
onMouseDown(mousePos.x, mousePos.y, 1);
|
|
}
|
|
if (is_hovered && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
|
|
// INFO_LOG(Log::CPU, "Mousedown %f,%f active:%d hover:%d", mousePos.x, mousePos.y, is_active, is_hovered);
|
|
onMouseDown(mousePos.x, mousePos.y, 2);
|
|
}
|
|
if (ImGui::IsMouseReleased(ImGuiMouseButton_Left)) {
|
|
// INFO_LOG(Log::System, "Mouseup %f,%f active:%d hover:%d", mousePos.x, mousePos.y, is_active, is_hovered);
|
|
if (is_hovered) {
|
|
onMouseUp(mousePos.x, mousePos.y, 1);
|
|
}
|
|
}
|
|
if (ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
|
|
// INFO_LOG(Log::System, "Mousedrag %f,%f active:%d hover:%d", mousePos.x, mousePos.y, is_active, is_hovered);
|
|
if (is_hovered) {
|
|
onMouseMove(mousePos.x, mousePos.y, 1);
|
|
}
|
|
}
|
|
|
|
if (is_hovered) {
|
|
if (io.MouseWheel > 0.0f) { // TODO: Scale steps by the value.
|
|
windowStart_ -= rowSize_ * 4;
|
|
} else if (io.MouseWheel < 0.0f) {
|
|
windowStart_ += rowSize_ * 4;
|
|
}
|
|
}
|
|
|
|
ProcessKeyboardShortcuts(ImGui::IsItemFocused());
|
|
|
|
ImGui_PopFont();
|
|
|
|
ImGui::OpenPopupOnItemClick("memcontext", ImGuiPopupFlags_MouseButtonRight);
|
|
PopupMenu();
|
|
|
|
drawList->PopClipRect();
|
|
}
|
|
|
|
// We don't have a scroll bar - though maybe we should build one.
|
|
/*
|
|
void ImMemView::onVScroll(WPARAM wParam, LPARAM lParam) {
|
|
switch (wParam & 0xFFFF) {
|
|
case SB_LINEDOWN:
|
|
ScrollWindow(1, GotoModeFromModifiers(false));
|
|
break;
|
|
case SB_LINEUP:
|
|
ScrollWindow(-1, GotoModeFromModifiers(false));
|
|
break;
|
|
case SB_PAGEDOWN:
|
|
ScrollWindow(visibleRows_, GotoModeFromModifiers(false));
|
|
break;
|
|
case SB_PAGEUP:
|
|
ScrollWindow(-visibleRows_, GotoModeFromModifiers(false));
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
*/
|
|
|
|
void ImMemView::ProcessKeyboardShortcuts(bool focused) {
|
|
if (!focused) {
|
|
return;
|
|
}
|
|
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
|
|
if (io.KeyMods & ImGuiMod_Ctrl) {
|
|
if (ImGui::IsKeyPressed(ImGuiKey_G)) {
|
|
/*
|
|
u32 addr;
|
|
if (executeExpressionWindow(wnd, debugger_, addr) == false)
|
|
return;
|
|
gotoAddr(addr);
|
|
return;
|
|
*/
|
|
}
|
|
if (ImGui::IsKeyPressed(ImGuiKey_F)) {
|
|
search(false);
|
|
}
|
|
if (ImGui::IsKeyPressed(ImGuiKey_C)) {
|
|
search(true);
|
|
}
|
|
} else {
|
|
if (ImGui::IsKeyPressed(ImGuiKey_DownArrow)) {
|
|
ScrollCursor(rowSize_, GotoModeFromModifiers(false));
|
|
}
|
|
if (ImGui::IsKeyPressed(ImGuiKey_UpArrow)) {
|
|
ScrollCursor(-rowSize_, GotoModeFromModifiers(false));
|
|
}
|
|
if (ImGui::IsKeyPressed(ImGuiKey_LeftArrow)) {
|
|
ScrollCursor(-1, GotoModeFromModifiers(false));
|
|
}
|
|
if (ImGui::IsKeyPressed(ImGuiKey_RightArrow)) {
|
|
ScrollCursor(1, GotoModeFromModifiers(false));
|
|
}
|
|
if (ImGui::IsKeyPressed(ImGuiKey_PageDown)) {
|
|
ScrollWindow(visibleRows_, GotoModeFromModifiers(false));
|
|
}
|
|
if (ImGui::IsKeyPressed(ImGuiKey_PageUp)) {
|
|
ScrollWindow(-visibleRows_, GotoModeFromModifiers(false));
|
|
}
|
|
}
|
|
}
|
|
|
|
void ImMemView::onChar(int c) {
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
|
|
if (io.KeyMods & ImGuiMod_Ctrl) {
|
|
return;
|
|
}
|
|
|
|
if (!Memory::IsValidAddress(curAddress_)) {
|
|
ScrollCursor(1, GotoMode::RESET);
|
|
return;
|
|
}
|
|
|
|
if (asciiSelected_) {
|
|
Memory::WriteUnchecked_U8((u8)c, curAddress_);
|
|
ScrollCursor(1, GotoMode::RESET);
|
|
} else {
|
|
c = tolower(c);
|
|
int inputValue = -1;
|
|
|
|
if (c >= '0' && c <= '9') inputValue = c - '0';
|
|
if (c >= 'a' && c <= 'f') inputValue = c - 'a' + 10;
|
|
|
|
if (inputValue >= 0) {
|
|
int shiftAmount = (1 - selectedNibble_) * 4;
|
|
|
|
u8 oldValue = Memory::ReadUnchecked_U8(curAddress_);
|
|
oldValue &= ~(0xF << shiftAmount);
|
|
u8 newValue = oldValue | (inputValue << shiftAmount);
|
|
Memory::WriteUnchecked_U8(newValue, curAddress_);
|
|
ScrollCursor(1, GotoMode::RESET);
|
|
}
|
|
}
|
|
|
|
Reporting::NotifyDebugger();
|
|
}
|
|
|
|
ImMemView::GotoMode ImMemView::GotoModeFromModifiers(bool isRightClick) {
|
|
GotoMode mode = GotoMode::RESET;
|
|
auto &io = ImGui::GetIO();
|
|
if (isRightClick) {
|
|
mode = GotoMode::RESET_IF_OUTSIDE;
|
|
} else if (io.KeyMods & ImGuiMod_Shift) {
|
|
if (io.KeyMods & ImGuiMod_Ctrl)
|
|
mode = GotoMode::EXTEND;
|
|
else
|
|
mode = GotoMode::FROM_CUR;
|
|
}
|
|
return mode;
|
|
}
|
|
|
|
void ImMemView::onMouseDown(float x, float y, int button) {
|
|
if (Achievements::HardcoreModeActive())
|
|
return;
|
|
|
|
GotoPoint(x, y, GotoModeFromModifiers(button == 2));
|
|
}
|
|
|
|
void ImMemView::PopupMenu() {
|
|
if (ImGui::BeginPopup("memcontext")) {
|
|
|
|
int32_t selectedSize = selectRangeEnd_ - selectRangeStart_;
|
|
bool enable16 = !asciiSelected_ && (selectedSize == 1 || (selectedSize & 1) == 0);
|
|
bool enable32 = !asciiSelected_ && (selectedSize == 1 || (selectedSize & 3) == 0);
|
|
/*
|
|
if (ImGui::MenuItem("Dump memory")) {
|
|
DumpMemoryWindow dump(wnd, debugger_);
|
|
dump.exec();
|
|
break;
|
|
}
|
|
*/
|
|
|
|
if (ImGui::MenuItem("Copy value (8-bit)")) {
|
|
size_t tempSize = 3 * selectedSize + 1;
|
|
char *temp = new char[tempSize];
|
|
memset(temp, 0, tempSize);
|
|
|
|
// it's admittedly not really useful like this
|
|
if (asciiSelected_) {
|
|
for (uint32_t p = selectRangeStart_; p != selectRangeEnd_; ++p) {
|
|
uint8_t c = Memory::IsValidAddress(p) ? Memory::ReadUnchecked_U8(p) : '.';
|
|
if (c < 32 || c >= 128)
|
|
c = '.';
|
|
temp[p - selectRangeStart_] = c;
|
|
}
|
|
} else {
|
|
char *pos = temp;
|
|
for (uint32_t p = selectRangeStart_; p != selectRangeEnd_; ++p) {
|
|
uint8_t c = Memory::IsValidAddress(p) ? Memory::ReadUnchecked_U8(p) : 0xFF;
|
|
pos += snprintf(pos, tempSize - (pos - temp + 1), "%02X ", c);
|
|
}
|
|
// Clear the last space.
|
|
if (pos > temp)
|
|
*(pos - 1) = '\0';
|
|
}
|
|
|
|
System_CopyStringToClipboard(temp);
|
|
delete[] temp;
|
|
}
|
|
|
|
if (ImGui::MenuItem("Copy value (16-bit)")) {
|
|
size_t tempSize = 5 * ((selectedSize + 1) / 2) + 1;
|
|
char *temp = new char[tempSize];
|
|
memset(temp, 0, tempSize);
|
|
|
|
char *pos = temp;
|
|
for (uint32_t p = selectRangeStart_; p < selectRangeEnd_; p += 2) {
|
|
uint16_t c = Memory::IsValidRange(p, 2) ? Memory::ReadUnchecked_U16(p) : 0xFFFF;
|
|
pos += snprintf(pos, tempSize - (pos - temp + 1), "%04X ", c);
|
|
}
|
|
// Clear the last space.
|
|
if (pos > temp)
|
|
*(pos - 1) = '\0';
|
|
|
|
System_CopyStringToClipboard(temp);
|
|
delete[] temp;
|
|
}
|
|
|
|
if (ImGui::MenuItem("Copy value (32-bit)")) {
|
|
size_t tempSize = 9 * ((selectedSize + 3) / 4) + 1;
|
|
char *temp = new char[tempSize];
|
|
memset(temp, 0, tempSize);
|
|
|
|
char *pos = temp;
|
|
for (uint32_t p = selectRangeStart_; p < selectRangeEnd_; p += 4) {
|
|
uint32_t c = Memory::IsValidRange(p, 4) ? Memory::ReadUnchecked_U32(p) : 0xFFFFFFFF;
|
|
pos += snprintf(pos, tempSize - (pos - temp + 1), "%08X ", c);
|
|
}
|
|
// Clear the last space.
|
|
if (pos > temp)
|
|
*(pos - 1) = '\0';
|
|
|
|
System_CopyStringToClipboard(temp);
|
|
delete[] temp;
|
|
}
|
|
|
|
if (ImGui::MenuItem("Copy value (float32)")) {
|
|
char temp[64];
|
|
snprintf(temp, sizeof(temp), "%f", Memory::IsValidAddress(curAddress_) ? Memory::Read_Float(curAddress_) : NAN);
|
|
System_CopyStringToClipboard(temp);
|
|
}
|
|
/*
|
|
case ID_MEMVIEW_EXTENTBEGIN:
|
|
{
|
|
std::vector<MemBlockInfo> memRangeInfo = FindMemInfoByFlag(highlightFlags_, curAddress_, 1);
|
|
uint32_t addr = curAddress_;
|
|
for (MemBlockInfo info : memRangeInfo) {
|
|
addr = info.start;
|
|
}
|
|
gotoAddr(addr);
|
|
break;
|
|
}
|
|
|
|
case ID_MEMVIEW_EXTENTEND:
|
|
{
|
|
std::vector<MemBlockInfo> memRangeInfo = FindMemInfoByFlag(highlightFlags_, curAddress_, 1);
|
|
uint32_t addr = curAddress_;
|
|
for (MemBlockInfo info : memRangeInfo) {
|
|
addr = info.start + info.size - 1;
|
|
}
|
|
gotoAddr(addr);
|
|
break;
|
|
}
|
|
*/
|
|
if (ImGui::MenuItem("Copy address")) {
|
|
char temp[24];
|
|
snprintf(temp, sizeof(temp), "0x%08X", curAddress_);
|
|
System_CopyStringToClipboard(temp);
|
|
}
|
|
|
|
if (ImGui::MenuItem("Goto in disasm")) {
|
|
/*
|
|
if (disasmWindow) {
|
|
disasmWindow->Goto(curAddress_);
|
|
disasmWindow->Show(true);
|
|
}
|
|
*/
|
|
}
|
|
ImGui::EndPopup();
|
|
}
|
|
}
|
|
|
|
void ImMemView::onMouseUp(float x, float y, int button) {
|
|
if (button == 2) {
|
|
|
|
// HMENU menu = GetContextMenu(ContextMenuID::MEMVIEW);
|
|
// EnableMenuItem(menu, ID_MEMVIEW_COPYVALUE_16, enable16 ? MF_ENABLED : MF_GRAYED);
|
|
// EnableMenuItem(menu, ID_MEMVIEW_COPYVALUE_32, enable32 ? MF_ENABLED : MF_GRAYED);
|
|
// EnableMenuItem(menu, ID_MEMVIEW_COPYFLOAT_32, enable32 ? MF_ENABLED : MF_GRAYED);
|
|
|
|
}
|
|
|
|
/*
|
|
int x = LOWORD(lParam);
|
|
int y = HIWORD(lParam);
|
|
ReleaseCapture();
|
|
*/
|
|
GotoPoint(x, y, GotoModeFromModifiers(button == 2));
|
|
}
|
|
|
|
void ImMemView::onMouseMove(float x, float y, int button) {
|
|
if (Achievements::HardcoreModeActive())
|
|
return;
|
|
|
|
if (button & 1) {
|
|
GotoPoint(x, y, GotoModeFromModifiers(button == 2));
|
|
}
|
|
}
|
|
|
|
void ImMemView::updateStatusBarText() {
|
|
std::vector<MemBlockInfo> memRangeInfo = FindMemInfoByFlag(highlightFlags_, curAddress_, 1);
|
|
char text[512];
|
|
snprintf(text, sizeof(text), "%08x", curAddress_);
|
|
// There should only be one.
|
|
for (MemBlockInfo info : memRangeInfo) {
|
|
snprintf(text, sizeof(text), "%08x - %s %08x-%08x (PC %08x / %lld ticks)", curAddress_, info.tag.c_str(), info.start, info.start + info.size, info.pc, info.ticks);
|
|
}
|
|
statusMessage_ = text;
|
|
}
|
|
|
|
void ImMemView::UpdateSelectRange(uint32_t target, GotoMode mode) {
|
|
if (mode == GotoMode::FROM_CUR && lastSelectReset_ == 0) {
|
|
lastSelectReset_ = curAddress_;
|
|
}
|
|
|
|
switch (mode) {
|
|
case GotoMode::RESET:
|
|
selectRangeStart_ = target;
|
|
selectRangeEnd_ = target + 1;
|
|
lastSelectReset_ = target;
|
|
break;
|
|
|
|
case GotoMode::RESET_IF_OUTSIDE:
|
|
if (target < selectRangeStart_ || target >= selectRangeEnd_) {
|
|
selectRangeStart_ = target;
|
|
selectRangeEnd_ = target + 1;
|
|
lastSelectReset_ = target;
|
|
}
|
|
break;
|
|
|
|
case GotoMode::FROM_CUR:
|
|
selectRangeStart_ = lastSelectReset_ > target ? target : lastSelectReset_;
|
|
selectRangeEnd_ = selectRangeStart_ == lastSelectReset_ ? target + 1 : lastSelectReset_ + 1;
|
|
break;
|
|
|
|
case GotoMode::EXTEND:
|
|
if (target < selectRangeStart_)
|
|
selectRangeStart_ = target;
|
|
if (target > selectRangeEnd_)
|
|
selectRangeEnd_ = target;
|
|
break;
|
|
}
|
|
curAddress_ = target;
|
|
}
|
|
|
|
void ImMemView::GotoPoint(int x, int y, GotoMode mode) {
|
|
int line = y / rowHeight_;
|
|
int lineAddress = windowStart_ + line * rowSize_;
|
|
|
|
if (displayOffsetScale_) {
|
|
// ignore clicks on the offset space
|
|
if (line < offsetSpace) {
|
|
updateStatusBarText();
|
|
return;
|
|
}
|
|
// since each row has been written X rows down from where the window expected it to be written the target of the clicks must be adjusted
|
|
lineAddress -= rowSize_ * offsetSpace;
|
|
}
|
|
|
|
uint32_t target = curAddress_;
|
|
uint32_t targetNibble = selectedNibble_;
|
|
bool targetAscii = asciiSelected_;
|
|
if (x >= asciiStartX_) {
|
|
int col = (x - asciiStartX_) / (charWidth_ + 2);
|
|
if (col >= rowSize_)
|
|
return;
|
|
|
|
targetAscii = true;
|
|
target = lineAddress + col;
|
|
targetNibble = 0;
|
|
} else if (x >= hexStartX_) {
|
|
int col = (x - hexStartX_) / charWidth_;
|
|
if ((col / 3) >= rowSize_)
|
|
return;
|
|
|
|
switch (col % 3) {
|
|
case 0: targetNibble = 0; break;
|
|
case 1: targetNibble = 1; break;
|
|
// case 2: return; // don't change position when clicking on the space
|
|
case 2: targetNibble = 0; break; // TODO: split the difference? Anyway, this feels better.
|
|
}
|
|
|
|
targetAscii = false;
|
|
target = lineAddress + col / 3;
|
|
}
|
|
|
|
if (target != curAddress_ || targetNibble != selectedNibble_ || targetAscii != asciiSelected_) {
|
|
selectedNibble_ = targetNibble;
|
|
asciiSelected_ = targetAscii;
|
|
UpdateSelectRange(target, mode);
|
|
updateStatusBarText();
|
|
}
|
|
}
|
|
|
|
void ImMemView::gotoAddr(unsigned int addr) {
|
|
int lines = visibleRows_;
|
|
u32 windowEnd = windowStart_ + lines * rowSize_;
|
|
|
|
curAddress_ = addr;
|
|
lastSelectReset_ = curAddress_;
|
|
selectRangeStart_ = curAddress_;
|
|
selectRangeEnd_ = curAddress_ + 1;
|
|
selectedNibble_ = 0;
|
|
|
|
if (curAddress_ < windowStart_ || curAddress_ >= windowEnd) {
|
|
windowStart_ = curAddress_ & ~15;
|
|
}
|
|
|
|
updateStatusBarText();
|
|
}
|
|
|
|
void ImMemView::ScrollWindow(int lines, GotoMode mode) {
|
|
windowStart_ += lines * rowSize_;
|
|
|
|
UpdateSelectRange(curAddress_ + lines * rowSize_, mode);
|
|
|
|
updateStatusBarText();
|
|
}
|
|
|
|
void ImMemView::ScrollCursor(int bytes, GotoMode mode) {
|
|
if (!asciiSelected_ && bytes == 1) {
|
|
if (selectedNibble_ == 0) {
|
|
selectedNibble_ = 1;
|
|
bytes = 0;
|
|
} else {
|
|
selectedNibble_ = 0;
|
|
}
|
|
} else if (!asciiSelected_ && bytes == -1) {
|
|
if (selectedNibble_ == 0) {
|
|
selectedNibble_ = 1;
|
|
} else {
|
|
selectedNibble_ = 0;
|
|
bytes = 0;
|
|
}
|
|
}
|
|
|
|
UpdateSelectRange(curAddress_ + bytes, mode);
|
|
|
|
u32 windowEnd = windowStart_ + visibleRows_ * rowSize_;
|
|
if (curAddress_ < windowStart_) {
|
|
windowStart_ = curAddress_ & ~15;
|
|
} else if (curAddress_ >= windowEnd) {
|
|
windowStart_ = (curAddress_ - (visibleRows_ - 1) * rowSize_) & ~15;
|
|
}
|
|
|
|
updateStatusBarText();
|
|
}
|
|
|
|
bool ImMemView::ParseSearchString(const std::string &query, bool asHex, std::vector<uint8_t> &data) {
|
|
data.clear();
|
|
if (!asHex) {
|
|
for (size_t i = 0; i < query.length(); i++) {
|
|
data.push_back(query[i]);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
for (size_t index = 0; index < query.size(); ) {
|
|
if (isspace(query[index])) {
|
|
index++;
|
|
continue;
|
|
}
|
|
|
|
u8 value = 0;
|
|
for (int i = 0; i < 2 && index < query.size(); i++) {
|
|
char c = tolower(query[index++]);
|
|
if (c >= 'a' && c <= 'f') {
|
|
value |= (c - 'a' + 10) << (1 - i) * 4;
|
|
} else if (c >= '0' && c <= '9') {
|
|
value |= (c - '0') << (1 - i) * 4;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
data.push_back(value);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
std::vector<u32> ImMemView::searchString(const std::string &searchQuery) {
|
|
std::vector<u32> searchResAddrs;
|
|
|
|
std::vector<u8> searchData;
|
|
if (!ParseSearchString(searchQuery, false, searchData))
|
|
return searchResAddrs;
|
|
|
|
if (searchData.empty())
|
|
return searchResAddrs;
|
|
|
|
std::vector<std::pair<u32, u32>> memoryAreas;
|
|
memoryAreas.emplace_back(PSP_GetScratchpadMemoryBase(), PSP_GetScratchpadMemoryEnd());
|
|
// Ignore the video memory mirrors.
|
|
memoryAreas.emplace_back(PSP_GetVidMemBase(), 0x04200000);
|
|
memoryAreas.emplace_back(PSP_GetKernelMemoryBase(), PSP_GetUserMemoryEnd());
|
|
|
|
for (const auto &area : memoryAreas) {
|
|
const u32 segmentStart = area.first;
|
|
const u32 segmentEnd = area.second - (u32)searchData.size();
|
|
|
|
for (u32 pos = segmentStart; pos < segmentEnd; pos++) {
|
|
if ((pos % 256) == 0 && ImGui::IsKeyDown(ImGuiKey_Escape)) {
|
|
return searchResAddrs;
|
|
}
|
|
|
|
const u8 *ptr = Memory::GetPointerUnchecked(pos);
|
|
if (memcmp(ptr, searchData.data(), searchData.size()) == 0) {
|
|
searchResAddrs.push_back(pos);
|
|
}
|
|
}
|
|
}
|
|
|
|
return searchResAddrs;
|
|
};
|
|
|
|
void ImMemView::search(bool continueSearch) {
|
|
/*
|
|
if (!PSP_IsInited())
|
|
return;
|
|
|
|
u32 searchAddress = 0;
|
|
u32 segmentStart = 0;
|
|
u32 segmentEnd = 0;
|
|
if (continueSearch == false || searchQuery_.empty()) {
|
|
if (InputBox_GetString(GetModuleHandle(NULL), wnd, L"Search for", searchQuery_, searchQuery_) == false) {
|
|
SetFocus(wnd);
|
|
return;
|
|
}
|
|
SetFocus(wnd);
|
|
searchAddress = curAddress_ + 1;
|
|
} else {
|
|
searchAddress = matchAddress_ + 1;
|
|
}
|
|
|
|
std::vector<u8> searchData;
|
|
if (!ParseSearchString(searchQuery_, !asciiSelected_, searchData)) {
|
|
statusMessage_ = "Invalid search text.";
|
|
return;
|
|
}
|
|
|
|
std::vector<std::pair<u32, u32>> memoryAreas;
|
|
// Ignore the video memory mirrors.
|
|
memoryAreas.emplace_back(PSP_GetVidMemBase(), 0x04200000);
|
|
memoryAreas.emplace_back(PSP_GetKernelMemoryBase(), PSP_GetUserMemoryEnd());
|
|
memoryAreas.emplace_back(PSP_GetScratchpadMemoryBase(), PSP_GetScratchpadMemoryEnd());
|
|
|
|
searching_ = true;
|
|
redraw(); // so the cursor is disabled
|
|
|
|
for (size_t i = 0; i < memoryAreas.size(); i++) {
|
|
segmentStart = memoryAreas[i].first;
|
|
segmentEnd = memoryAreas[i].second;
|
|
|
|
// better safe than sorry, I guess
|
|
if (!Memory::IsValidAddress(segmentStart))
|
|
continue;
|
|
const u8 *dataPointer = Memory::GetPointerUnchecked(segmentStart);
|
|
|
|
if (searchAddress < segmentStart)
|
|
searchAddress = segmentStart;
|
|
if (searchAddress >= segmentEnd)
|
|
continue;
|
|
|
|
int index = searchAddress - segmentStart;
|
|
int endIndex = segmentEnd - segmentStart - (int)searchData.size();
|
|
|
|
while (index < endIndex) {
|
|
// cancel search
|
|
if ((index % 256) == 0 && ImGui::IsKeyDown(ImGuiKey_Escape)) {
|
|
searching_ = false;
|
|
return;
|
|
}
|
|
if (memcmp(&dataPointer[index], searchData.data(), searchData.size()) == 0) {
|
|
matchAddress_ = index + segmentStart;
|
|
searching_ = false;
|
|
gotoAddr(matchAddress_);
|
|
return;
|
|
}
|
|
index++;
|
|
}
|
|
}
|
|
|
|
statusMessage_ = "Not found";
|
|
searching_ = false;
|
|
redraw();
|
|
*/
|
|
}
|
|
|
|
void ImMemView::drawOffsetScale(ImDrawList *drawList) {
|
|
int currentX = addressStartX_;
|
|
|
|
drawList->AddText(ImVec2(currentX, offsetPositionY_), 0xFF600000, "Offset");
|
|
// SetTextColor(hdc, 0x600000);
|
|
// TextOutA(hdc, currentX, offsetPositionY_, "Offset", 6);
|
|
|
|
// the start offset, the size of the hex addresses and one space
|
|
currentX = addressStartX_ + ((8 + 1) * charWidth_);
|
|
|
|
char temp[64];
|
|
for (int i = 0; i < 16; i++) {
|
|
snprintf(temp, sizeof(temp), "%02X", i);
|
|
drawList->AddText(ImVec2(currentX, offsetPositionY_), 0xFF600000, temp);
|
|
currentX += 3 * charWidth_; // hex and space
|
|
}
|
|
}
|
|
|
|
void ImMemView::toggleOffsetScale(CommonToggles toggle) {
|
|
if (toggle == On)
|
|
displayOffsetScale_ = true;
|
|
else if (toggle == Off)
|
|
displayOffsetScale_ = false;
|
|
|
|
updateStatusBarText();
|
|
}
|
|
|
|
void ImMemView::setHighlightType(MemBlockFlags flags) {
|
|
if (highlightFlags_ != flags) {
|
|
highlightFlags_ = flags;
|
|
updateStatusBarText();
|
|
}
|
|
}
|
|
|
|
void ImMemDumpWindow::Draw(ImConfig &cfg, MIPSDebugInterface *debug) {
|
|
ImGui::SetNextWindowSize(ImVec2(200, 300), ImGuiCond_FirstUseEver);
|
|
|
|
if (!ImGui::Begin(Title(), &cfg.memDumpOpen)) {
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
if (ImGui::Button("User RAM (0x08800000)")) {
|
|
address_ = 0x08800000;
|
|
size_ = 0x01800000; // 24MB
|
|
}
|
|
|
|
ImGui::InputScalar("Starting address", ImGuiDataType_U32, &address_, NULL, NULL, "%08X");
|
|
ImGui::InputScalar("Size", ImGuiDataType_U32, &size_, NULL, NULL, "%08X");
|
|
|
|
ImGui::InputText("Filename", filename_, ARRAY_SIZE(filename_));
|
|
|
|
const char* modes[] = { "Raw", "Disassembly" };
|
|
int modeIndex = static_cast<int>(mode_);
|
|
if (ImGui::Combo("Memory Dump Mode", &modeIndex, modes, IM_ARRAYSIZE(modes))) {
|
|
// Update the current mode if the user selects a new one
|
|
mode_ = static_cast<MemDumpMode>(modeIndex);
|
|
}
|
|
|
|
if (ImGui::Button(mode_ == MemDumpMode::Raw ? "Dump to file" : "Disassemble to file")) {
|
|
uint32_t validSize = Memory::ValidSize(address_, size_);
|
|
if (validSize != size_) {
|
|
errorMsg_ = "Address range out of bounds";
|
|
if (Memory::IsValidAddress(address_)) {
|
|
size_ = validSize;
|
|
}
|
|
} else if (strlen(filename_) == 0) {
|
|
errorMsg_ = "Please specify a valid filename";
|
|
} else {
|
|
FILE *file = File::OpenCFile(Path(filename_), "wb");
|
|
if (!file) {
|
|
errorMsg_ = "Couldn't open file for writing";
|
|
} else {
|
|
if (mode_ == MemDumpMode::Raw) {
|
|
const uint8_t *ptr = Memory::GetPointer(address_);
|
|
fwrite(ptr, 1, size_, file);
|
|
} else {
|
|
std::string disassembly = DisassembleRange(address_, size_, true, debug);
|
|
fprintf(file, "%s", disassembly.c_str());
|
|
}
|
|
errorMsg_.clear();
|
|
fclose(file);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!errorMsg_.empty()) {
|
|
ImGui::TextUnformatted(errorMsg_.data(), errorMsg_.data() + errorMsg_.size());
|
|
}
|
|
|
|
ImGui::End();
|
|
}
|