diff --git a/common/MemoryInterface.cpp b/common/MemoryInterface.cpp index 6d08fac226..68298fcca0 100644 --- a/common/MemoryInterface.cpp +++ b/common/MemoryInterface.cpp @@ -34,6 +34,34 @@ Value MemoryInterface::Read(u32 address, bool* valid) return Value(0); } +std::optional MemoryInterface::ReadString(u32 address, u32 max_size, ReadStringFlags flags) +{ + std::string string; + + for (u32 i = 0; i < max_size; i++) + { + bool valid; + char c = Read8(address + i, &valid); + if (!valid) + return std::nullopt; + + if (c == '\0') + return string; + else if (!(flags & ALLOW_NON_PRINTABLE_CHARACTERS) && (c < ' ' || c > '~')) + return std::nullopt; + + string += c; + } + + if (flags & ALLOW_LONG_STRINGS) + { + string += '~'; + return string; + } + + return std::nullopt; +} + template bool MemoryInterface::Write(u32 address, Value value) { @@ -62,6 +90,17 @@ bool MemoryInterface::Write(u32 address, Value value) return false; } +bool MemoryInterface::WriteString(u32 address, std::string_view string) +{ + if (!WriteBytes(address, string.data(), static_cast(string.size()))) + return false; + + if (!Write8(address + static_cast(string.size()), '\0')) + return false; + + return true; +} + bool MemoryInterface::IdempotentWrite8(u32 address, u8 value) { bool valid; diff --git a/common/MemoryInterface.h b/common/MemoryInterface.h index 0ed5500c45..fefe72c4b3 100644 --- a/common/MemoryInterface.h +++ b/common/MemoryInterface.h @@ -5,6 +5,9 @@ #include "Pcsx2Types.h" +#include +#include +#include #include template @@ -31,16 +34,27 @@ public: template Value Read(u32 address, bool* valid = nullptr); + enum ReadStringFlags + { + NO_FLAGS = 0, + ALLOW_LONG_STRINGS = 1 << 0, + ALLOW_NON_PRINTABLE_CHARACTERS = 1 << 1 + }; + + std::optional ReadString(u32 address, u32 max_size = 256, ReadStringFlags flags = NO_FLAGS); + virtual bool Write8(u32 address, u8 value) = 0; virtual bool Write16(u32 address, u16 value) = 0; virtual bool Write32(u32 address, u32 value) = 0; virtual bool Write64(u32 address, u64 value) = 0; virtual bool Write128(u32 address, u128 value) = 0; - virtual bool WriteBytes(u32 address, void* src, u32 size) = 0; + virtual bool WriteBytes(u32 address, const void* src, u32 size) = 0; template bool Write(u32 address, Value value); + bool WriteString(u32 address, std::string_view string); + bool IdempotentWrite8(u32 address, u8 value); bool IdempotentWrite16(u32 address, u16 value); bool IdempotentWrite32(u32 address, u32 value); @@ -51,5 +65,5 @@ public: template bool IdempotentWrite(u32 address, Value value); - virtual bool CompareBytes(u32 address, void* src, u32 size) = 0; + virtual bool CompareBytes(u32 address, const void* src, u32 size) = 0; }; diff --git a/pcsx2-qt/Debugger/SymbolTree/SymbolTreeDelegates.cpp b/pcsx2-qt/Debugger/SymbolTree/SymbolTreeDelegates.cpp index 2df09a5c76..21392f7dee 100644 --- a/pcsx2-qt/Debugger/SymbolTree/SymbolTreeDelegates.cpp +++ b/pcsx2-qt/Debugger/SymbolTree/SymbolTreeDelegates.cpp @@ -7,6 +7,8 @@ #include "Debugger/SymbolTree/SymbolTreeModel.h" #include "Debugger/SymbolTree/TypeString.h" +#include "common/StringUtil.h" + #include #include #include @@ -44,12 +46,28 @@ QWidget* SymbolTreeValueDelegate::createEditor(QWidget* parent, const QStyleOpti const ccc::ast::Node& physical_type = *logical_type->physical_type(database).first; QVariant value = node->readValueAsVariant(physical_type, m_cpu, database); - const ccc::ast::Node& type = *logical_type->physical_type(database).first; - switch (type.descriptor) + switch (physical_type.descriptor) { + case ccc::ast::ARRAY: + { + const ccc::ast::Array& array = physical_type.as(); + const ccc::ast::Node& element_type = *array.element_type->physical_type(database).first;; + + if (array.element_count > 0 && + element_type.name == "char" && + node->location.type == SymbolTreeLocation::MEMORY && + !value.isNull()) + { + QLineEdit* editor = new QLineEdit(parent); + editor->setText(value.toString()); + result = editor; + } + + break; + } case ccc::ast::BUILTIN: { - const ccc::ast::BuiltIn& builtin = type.as(); + const ccc::ast::BuiltIn& builtin = physical_type.as(); switch (builtin.bclass) { @@ -111,7 +129,7 @@ QWidget* SymbolTreeValueDelegate::createEditor(QWidget* parent, const QStyleOpti } case ccc::ast::ENUM: { - const ccc::ast::Enum& enumeration = type.as(); + const ccc::ast::Enum& enumeration = physical_type.as(); QComboBox* combo_box = new QComboBox(parent); bool named = false; @@ -186,12 +204,30 @@ void SymbolTreeValueDelegate::setModelData(QWidget* editor, QAbstractItemModel* if (!logical_type) return; - const ccc::ast::Node& type = *logical_type->physical_type(database).first; - switch (type.descriptor) + const ccc::ast::Node& physical_type = *logical_type->physical_type(database).first; + + switch (physical_type.descriptor) { + case ccc::ast::ARRAY: + { + const ccc::ast::Array& array = physical_type.as(); + const ccc::ast::Node& element_type = *array.element_type->physical_type(database).first; + + QLineEdit* line_edit = qobject_cast(editor); + + if (array.element_count > 0 && + element_type.name == "char" && + node->location.type == SymbolTreeLocation::MEMORY && + line_edit) + { + value = line_edit->text(); + } + + break; + } case ccc::ast::BUILTIN: { - const ccc::ast::BuiltIn& builtin = type.as(); + const ccc::ast::BuiltIn& builtin = physical_type.as(); switch (builtin.bclass) { diff --git a/pcsx2-qt/Debugger/SymbolTree/SymbolTreeNode.cpp b/pcsx2-qt/Debugger/SymbolTree/SymbolTreeNode.cpp index 71aba8990d..6ec0db41ad 100644 --- a/pcsx2-qt/Debugger/SymbolTree/SymbolTreeNode.cpp +++ b/pcsx2-qt/Debugger/SymbolTree/SymbolTreeNode.cpp @@ -67,7 +67,7 @@ bool SymbolTreeNode::writeToVM( if (logical_type) { const ccc::ast::Node& physical_type = *logical_type->physical_type(database).first; - writeValueFromVariant(m_value, physical_type, cpu); + writeValueFromVariant(m_value, physical_type, cpu, database); } data_changed |= updateDisplayString(cpu, database, display_options); @@ -76,10 +76,29 @@ bool SymbolTreeNode::writeToVM( return data_changed; } -QVariant SymbolTreeNode::readValueAsVariant(const ccc::ast::Node& physical_type, DebugInterface& cpu, const ccc::SymbolDatabase& database) const +QVariant SymbolTreeNode::readValueAsVariant( + const ccc::ast::Node& physical_type, + DebugInterface& cpu, + const ccc::SymbolDatabase& database) const { switch (physical_type.descriptor) { + case ccc::ast::ARRAY: + { + const ccc::ast::Array& array = physical_type.as(); + const ccc::ast::Node& element_type = *array.element_type->physical_type(database).first; + + if (array.element_count > 0 && + element_type.name == "char" && + location.type == SymbolTreeLocation::MEMORY) + { + std::optional string = cpu.ReadString(location.address, 256); + if (string.has_value()) + return QString::fromStdString(*string); + } + + break; + } case ccc::ast::BUILTIN: { const ccc::ast::BuiltIn& builtIn = physical_type.as(); @@ -134,10 +153,36 @@ QVariant SymbolTreeNode::readValueAsVariant(const ccc::ast::Node& physical_type, return QVariant(); } -bool SymbolTreeNode::writeValueFromVariant(QVariant value, const ccc::ast::Node& physical_type, DebugInterface& cpu) const +bool SymbolTreeNode::writeValueFromVariant( + QVariant value, + const ccc::ast::Node& physical_type, + DebugInterface& cpu, + const ccc::SymbolDatabase& database) const { + if (value.isNull()) + return false; + switch (physical_type.descriptor) { + case ccc::ast::ARRAY: + { + const ccc::ast::Array& array = physical_type.as(); + const ccc::ast::Node& element_type = *array.element_type->physical_type(database).first; + + if (array.element_count > 0 && + element_type.name == "char" && + location.type == SymbolTreeLocation::MEMORY) + { + QByteArray byte_array = value.toString().toLatin1(); + std::string_view view( + byte_array.data(), + std::min(static_cast(byte_array.size()), array.element_count - 1)); + cpu.WriteString(location.address, view); + break; + } + + return false; + } case ccc::ast::BUILTIN: { const ccc::ast::BuiltIn& built_in = physical_type.as(); @@ -191,6 +236,7 @@ bool SymbolTreeNode::writeValueFromVariant(QVariant value, const ccc::ast::Node& return false; } } + break; } case ccc::ast::ENUM: @@ -266,11 +312,14 @@ QString SymbolTreeNode::generateDisplayString( const ccc::ast::Array& array = physical_type.as(); const ccc::ast::Node& element_type = *array.element_type->physical_type(database).first; - if (element_type.name == "char" && location.type == SymbolTreeLocation::MEMORY) + if (array.element_count > 0 && + element_type.name == "char" && + location.type == SymbolTreeLocation::MEMORY) { - char* string = cpu.stringFromPointer(location.address); - if (string) - return QString("\"%1\"").arg(string); + std::optional string = cpu.ReadString( + location.address, 256, MemoryInterface::ALLOW_LONG_STRINGS); + if (string.has_value()) + return QString("\"%1\"").arg(*string); } QString result; @@ -412,9 +461,10 @@ QString SymbolTreeNode::generateDisplayString( if (pointer_or_reference.is_pointer && value_type.name == "char") { - const char* string = cpu.stringFromPointer(address); - if (string) - result += QString(" \"%1\"").arg(string); + std::optional string = cpu.ReadString( + address, 256, MemoryInterface::ALLOW_LONG_STRINGS); + if (string.has_value()) + result += QString(" \"%1\"").arg(*string); } else if (depth == 0) { diff --git a/pcsx2-qt/Debugger/SymbolTree/SymbolTreeNode.h b/pcsx2-qt/Debugger/SymbolTree/SymbolTreeNode.h index 490ee01dbd..d05e3331ca 100644 --- a/pcsx2-qt/Debugger/SymbolTree/SymbolTreeNode.h +++ b/pcsx2-qt/Debugger/SymbolTree/SymbolTreeNode.h @@ -62,11 +62,20 @@ public: const ccc::SymbolDatabase& database, const SymbolTreeDisplayOptions& display_options); - QVariant readValueAsVariant(const ccc::ast::Node& physical_type, DebugInterface& cpu, const ccc::SymbolDatabase& database) const; - bool writeValueFromVariant(QVariant value, const ccc::ast::Node& physical_type, DebugInterface& cpu) const; + QVariant readValueAsVariant( + const ccc::ast::Node& physical_type, + DebugInterface& cpu, + const ccc::SymbolDatabase& database) const; + + bool writeValueFromVariant( + QVariant value, + const ccc::ast::Node& physical_type, + DebugInterface& cpu, + const ccc::SymbolDatabase& database) const; bool updateDisplayString( DebugInterface& cpu, const ccc::SymbolDatabase& database, const SymbolTreeDisplayOptions& display); + QString generateDisplayString( const ccc::ast::Node& physical_type, DebugInterface& cpu, diff --git a/pcsx2/DebugTools/DebugInterface.cpp b/pcsx2/DebugTools/DebugInterface.cpp index a64b9b348d..8d6552f208 100644 --- a/pcsx2/DebugTools/DebugInterface.cpp +++ b/pcsx2/DebugTools/DebugInterface.cpp @@ -63,37 +63,6 @@ void DebugInterface::resumeCpu() VMManager::SetPaused(false); } -char* DebugInterface::stringFromPointer(u32 p) -{ - const int BUFFER_LEN = 64; - static char buf[BUFFER_LEN] = {0}; - - if (!isValidAddress(p)) - return NULL; - - // This is going to blow up if it hits a TLB miss.. - // Hopefully the checks in isValidAddress() are sufficient. - for (u32 i = 0; i < BUFFER_LEN; i++) - { - char c = Read8(p + i); - buf[i] = c; - - if (c == 0) - { - return i > 0 ? buf : NULL; - } - else if (c < 0x20 || c >= 0x7f) - { - // non printable character - return NULL; - } - } - - buf[BUFFER_LEN - 1] = 0; - buf[BUFFER_LEN - 2] = '~'; - return buf; -} - std::optional DebugInterface::getCallerStackPointer(const ccc::Function& currentFunction) { u32 sp = getRegister(EECAT_GPR, 29); @@ -336,12 +305,12 @@ bool R5900DebugInterface::Write128(u32 address, u128 value) return isValidAddress(address) && vtlb_memSafeWriteBytes(address, &value, sizeof(value)); } -bool R5900DebugInterface::WriteBytes(u32 address, void* src, u32 size) +bool R5900DebugInterface::WriteBytes(u32 address, const void* src, u32 size) { return vtlb_memSafeWriteBytes(address, src, size); } -bool R5900DebugInterface::CompareBytes(u32 address, void* src, u32 size) +bool R5900DebugInterface::CompareBytes(u32 address, const void* src, u32 size) { return vtlb_memSafeCmpBytes(address, src, size) == 0; } @@ -839,12 +808,12 @@ bool R3000DebugInterface::Write128(u32 address, u128 value) return true; } -bool R3000DebugInterface::WriteBytes(u32 address, void* src, u32 size) +bool R3000DebugInterface::WriteBytes(u32 address, const void* src, u32 size) { return iopMemSafeWriteBytes(address, src, size); } -bool R3000DebugInterface::CompareBytes(u32 address, void* src, u32 size) +bool R3000DebugInterface::CompareBytes(u32 address, const void* src, u32 size) { return iopMemSafeCmpBytes(address, src, size) == 0; } @@ -1185,12 +1154,12 @@ bool ElfMemoryReader::Write128(u32 address, u128 value) return false; } -bool ElfMemoryReader::WriteBytes(u32 address, void* src, u32 size) +bool ElfMemoryReader::WriteBytes(u32 address, const void* src, u32 size) { return false; } -bool ElfMemoryReader::CompareBytes(u32 address, void* src, u32 size) +bool ElfMemoryReader::CompareBytes(u32 address, const void* src, u32 size) { std::optional> bytes = m_elf.get_virtual(address, size); if (!bytes.has_value()) diff --git a/pcsx2/DebugTools/DebugInterface.h b/pcsx2/DebugTools/DebugInterface.h index 48b266ba56..7341a317c9 100644 --- a/pcsx2/DebugTools/DebugInterface.h +++ b/pcsx2/DebugTools/DebugInterface.h @@ -80,7 +80,6 @@ public: bool isCpuPaused(); void pauseCpu(); void resumeCpu(); - char* stringFromPointer(u32 p); std::optional getCallerStackPointer(const ccc::Function& currentFunction); std::optional getStackFrameSize(const ccc::Function& currentFunction); @@ -115,9 +114,9 @@ public: bool Write32(u32 address, u32 value) override; bool Write64(u32 address, u64 value) override; bool Write128(u32 address, u128 value) override; - bool WriteBytes(u32 address, void* src, u32 size) override; + bool WriteBytes(u32 address, const void* src, u32 size) override; - bool CompareBytes(u32 address, void* src, u32 size) override; + bool CompareBytes(u32 address, const void* src, u32 size) override; // register stuff int getRegisterCategoryCount() override; @@ -161,9 +160,9 @@ public: bool Write32(u32 address, u32 value) override; bool Write64(u32 address, u64 value) override; bool Write128(u32 address, u128 value) override; - bool WriteBytes(u32 address, void* src, u32 size) override; + bool WriteBytes(u32 address, const void* src, u32 size) override; - bool CompareBytes(u32 address, void* src, u32 size) override; + bool CompareBytes(u32 address, const void* src, u32 size) override; // register stuff int getRegisterCategoryCount() override; @@ -210,9 +209,9 @@ public: bool Write32(u32 address, u32 value) override; bool Write64(u32 address, u64 value) override; bool Write128(u32 address, u128 value) override; - bool WriteBytes(u32 address, void* src, u32 size) override; + bool WriteBytes(u32 address, const void* src, u32 size) override; - bool CompareBytes(u32 address, void* src, u32 size) override; + bool CompareBytes(u32 address, const void* src, u32 size) override; protected: const ccc::ElfFile& m_elf; diff --git a/pcsx2/IopMem.cpp b/pcsx2/IopMem.cpp index fa26382637..3360927614 100644 --- a/pcsx2/IopMem.cpp +++ b/pcsx2/IopMem.cpp @@ -607,12 +607,12 @@ bool IOPMemoryInterface::Write128(u32 address, u128 value) return false; } -bool IOPMemoryInterface::WriteBytes(u32 address, void* src, u32 size) +bool IOPMemoryInterface::WriteBytes(u32 address, const void* src, u32 size) { return iopMemSafeWriteBytes(address, src, size); } -bool IOPMemoryInterface::CompareBytes(u32 address, void* src, u32 size) +bool IOPMemoryInterface::CompareBytes(u32 address, const void* src, u32 size) { return iopMemSafeCmpBytes(address, src, size) == 0; } diff --git a/pcsx2/IopMem.h b/pcsx2/IopMem.h index d89cc449f9..72dabc077b 100644 --- a/pcsx2/IopMem.h +++ b/pcsx2/IopMem.h @@ -136,7 +136,7 @@ public: bool Write32(u32 address, u32 value) override; bool Write64(u32 address, u64 value) override; bool Write128(u32 address, u128 value) override; - bool WriteBytes(u32 address, void* src, u32 size) override; + bool WriteBytes(u32 address, const void* src, u32 size) override; - bool CompareBytes(u32 address, void* src, u32 size) override; + bool CompareBytes(u32 address, const void* src, u32 size) override; }; diff --git a/pcsx2/Memory.cpp b/pcsx2/Memory.cpp index a772ed4689..758ddbc15e 100644 --- a/pcsx2/Memory.cpp +++ b/pcsx2/Memory.cpp @@ -1257,12 +1257,12 @@ bool EEMemoryInterface::Write128(u32 address, u128 value) return true; } -bool EEMemoryInterface::WriteBytes(u32 address, void* src, u32 size) +bool EEMemoryInterface::WriteBytes(u32 address, const void* src, u32 size) { return vtlb_memSafeWriteBytes(address, src, size); } -bool EEMemoryInterface::CompareBytes(u32 address, void* src, u32 size) +bool EEMemoryInterface::CompareBytes(u32 address, const void* src, u32 size) { return vtlb_memSafeCmpBytes(address, src, size) == 0; } diff --git a/pcsx2/Memory.h b/pcsx2/Memory.h index 4efb4db72a..755cbbf772 100644 --- a/pcsx2/Memory.h +++ b/pcsx2/Memory.h @@ -206,7 +206,7 @@ public: bool Write32(u32 address, u32 value) override; bool Write64(u32 address, u64 value) override; bool Write128(u32 address, u128 value) override; - bool WriteBytes(u32 address, void* src, u32 size) override; + bool WriteBytes(u32 address, const void* src, u32 size) override; - bool CompareBytes(u32 address, void* src, u32 size) override; + bool CompareBytes(u32 address, const void* src, u32 size) override; }; diff --git a/tests/ctest/core/MockMemoryInterface.h b/tests/ctest/core/MockMemoryInterface.h index 885549fd43..42e80ff75d 100644 --- a/tests/ctest/core/MockMemoryInterface.h +++ b/tests/ctest/core/MockMemoryInterface.h @@ -21,9 +21,9 @@ public: MOCK_METHOD(bool, Write32, (u32 address, u32 value), (override)); MOCK_METHOD(bool, Write64, (u32 address, u64 value), (override)); MOCK_METHOD(bool, Write128, (u32 address, u128 value), (override)); - MOCK_METHOD(bool, WriteBytes, (u32 address, void* src, u32 size), (override)); + MOCK_METHOD(bool, WriteBytes, (u32 address, const void* src, u32 size), (override)); - MOCK_METHOD(bool, CompareBytes, (u32 address, void* src, u32 size), (override)); + MOCK_METHOD(bool, CompareBytes, (u32 address, const void* src, u32 size), (override)); struct SetValidOutParameterAction {