Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/ui/viewmodels/MemoryViewerViewModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include "data\context\EmulatorContext.hh"
#include "data\context\GameContext.hh"
#include "data\context\ConsoleContext.hh"

#include "ui\EditorTheme.hh"
#include "ui\viewmodels\WindowManager.hh"
Expand Down Expand Up @@ -895,6 +896,21 @@ void MemoryViewerViewModel::OnClick(int nX, int nY)
}
}

void MemoryViewerViewModel::OnShiftClick(int nX, int nY)
{
const auto& pEmulatorContext = ra::services::ServiceLocator::Get<ra::data::context::EmulatorContext>();
const auto& pConsoleContext = ra::services::ServiceLocator::Get<ra::data::context::ConsoleContext>();

OnClick(nX, nY);

ra::ByteAddress nAddress = (pEmulatorContext.ReadMemory(GetAddress(), GetSize()));
const auto nConvertedAddress = pConsoleContext.ByteAddressFromRealAddress(nAddress);
if (nConvertedAddress != 0xFFFFFFFF)
{
SetAddress(nConvertedAddress);
}
}

void MemoryViewerViewModel::OnResized(int nWidth, int nHeight)
{
if (s_pFontSurface == nullptr)
Expand Down
1 change: 1 addition & 0 deletions src/ui/viewmodels/MemoryViewerViewModel.hh
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ public:
void SetSize(MemSize value) { SetValue(SizeProperty, ra::etoi(value)); }

void OnClick(int nX, int nY);
void OnShiftClick(int nX, int nY);
void OnResized(int nWidth, int nHeight);
bool OnChar(char c);
void OnGotFocus();
Expand Down
5 changes: 4 additions & 1 deletion src/ui/win32/bindings/MemoryViewerControlBinding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,10 @@ void MemoryViewerControlBinding::OnClick(POINT point)

// multiple properties may change while typing, we'll do a single Invalidate after we're done
m_bSuppressMemoryViewerInvalidate = true;
m_pViewModel.OnClick(point.x, point.y);
if (GetKeyState(VK_SHIFT) < 0)
m_pViewModel.OnShiftClick(point.x, point.y);
else
m_pViewModel.OnClick(point.x, point.y);
m_bSuppressMemoryViewerInvalidate = false;

SetFocus(m_hWnd);
Expand Down
116 changes: 116 additions & 0 deletions tests/ui/viewmodels/MemoryViewerViewModel_Tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "tests\RA_UnitTestHelpers.h"
#include "tests\mocks\MockEmulatorContext.hh"
#include "tests\mocks\MockGameContext.hh"
#include "tests\mocks\MockConsoleContext.hh"
#include "tests\mocks\MockWindowManager.hh"

#undef GetMessage
Expand Down Expand Up @@ -71,6 +72,9 @@ TEST_CLASS(MemoryViewerViewModel_Tests)
bool IsReadOnly() const noexcept { return m_bReadOnly; }
void SetReadOnly(bool value) noexcept { m_bReadOnly = value; }

bool IsAddressFixed() const noexcept { return m_bAddressFixed; }
void SetAddressFixed(bool value) noexcept { m_bAddressFixed = value; }

void InitializeMemory(size_t nSize)
{
unsigned char* pBytes = new unsigned char[nSize];
Expand Down Expand Up @@ -1918,8 +1922,120 @@ TEST_CLASS(MemoryViewerViewModel_Tests)
Assert::IsTrue(viewer.NeedsRedraw());
viewer.MockRender();
}

TEST_METHOD(TestOnShiftClickEightBit)
{
ra::data::context::mocks::MockConsoleContext mockConsole(PlayStation, L"PlayStation");

MemoryViewerViewModelHarness viewer;
viewer.InitializeMemory(0xFFFF);

Assert::AreEqual(MemSize::EightBit, viewer.GetSize());
Assert::AreEqual({ 0U }, viewer.GetAddress());
Assert::AreEqual({ 0U }, viewer.GetSelectedNibble());

// If fixed address, ignore
viewer.mockEmulatorContext.WriteMemoryByte(0U, 0x20);
viewer.SetAddressFixed(true);
viewer.OnShiftClick(10 * CHAR_WIDTH, CHAR_HEIGHT + 4);
Assert::AreEqual({0x0U}, viewer.GetAddress());

// If not fixed address and Shift click on the first byte containing 0x20 should lead to address 0x20
viewer.SetAddressFixed(false);
viewer.OnShiftClick(10 * CHAR_WIDTH, CHAR_HEIGHT + 4);
Assert::AreEqual({ 0x20U }, viewer.GetAddress());

// Shift click on the second byte containing 0x0 should do nothing
viewer.SetAddress(0);
viewer.mockEmulatorContext.WriteMemoryByte(1U, 0x0);
viewer.OnShiftClick(15 * CHAR_WIDTH, CHAR_HEIGHT + 4);
Assert::AreEqual({0x1U}, viewer.GetAddress());
}

TEST_METHOD(TestOnShiftClickSixteenBit)
{
ra::data::context::mocks::MockConsoleContext mockConsole(PlayStation, L"PlayStation");

MemoryViewerViewModelHarness viewer;
viewer.InitializeMemory(0x1FFF);
viewer.SetSize(MemSize::SixteenBit);

Assert::AreEqual(MemSize::SixteenBit, viewer.GetSize());
Assert::AreEqual({0U}, viewer.GetAddress());
Assert::AreEqual({0U}, viewer.GetSelectedNibble());

// If fixed address, ignore
viewer.SetAddressFixed(true);
viewer.OnShiftClick(10 * CHAR_WIDTH, CHAR_HEIGHT + 4);
Assert::AreEqual({0x0U}, viewer.GetAddress());

// If not fixed address and Shift click on the first word containing 0x20 should lead to address 0x20
viewer.SetAddress(0);
viewer.SetAddressFixed(false);
viewer.mockEmulatorContext.WriteMemory(0U, MemSize::SixteenBit, 0x0020);
viewer.OnShiftClick(10 * CHAR_WIDTH, CHAR_HEIGHT + 4);
Assert::AreEqual({0x20U}, viewer.GetAddress());

// Shift click on the first word containing 0x0140 should lead to address 0x0140
viewer.SetAddress(0);
viewer.mockEmulatorContext.WriteMemory(0U, MemSize::SixteenBit, 0x0140);
viewer.OnShiftClick(10 * CHAR_WIDTH, CHAR_HEIGHT + 4);
Assert::AreEqual({0x0140U}, viewer.GetAddress());

// Shift click on the second word containing 0x0 should do nothing
viewer.SetAddress(0x4);
viewer.mockEmulatorContext.WriteMemory(4U, MemSize::SixteenBit, 0x0);
viewer.OnShiftClick(22 * CHAR_WIDTH, CHAR_HEIGHT + 4);
Assert::AreEqual({0x4U}, viewer.GetAddress());
}

TEST_METHOD(TestOnShiftClickThirtyTwoBit)
{
ra::data::context::mocks::MockConsoleContext mockConsole(PlayStation, L"Playstation");

MemoryViewerViewModelHarness viewer;
viewer.InitializeMemory(0x1FFF); // PSX full memory
viewer.SetSize(MemSize::ThirtyTwoBit);

Assert::AreEqual(MemSize::ThirtyTwoBit, viewer.GetSize());
Assert::AreEqual({0U}, viewer.GetAddress());
Assert::AreEqual({0U}, viewer.GetSelectedNibble());

// If fixed address, ignore
viewer.SetAddressFixed(true);
viewer.OnShiftClick(10 * CHAR_WIDTH, CHAR_HEIGHT + 4);
Assert::AreEqual({0x0U}, viewer.GetAddress());

// If not fixed address and Shift click on the first dword containing 0x1234 should lead to address 0x1234
viewer.SetAddress(0);
viewer.SetAddressFixed(false);
viewer.mockEmulatorContext.WriteMemory(0U, MemSize::ThirtyTwoBit, 0x1234);
viewer.OnShiftClick(10 * CHAR_WIDTH, CHAR_HEIGHT + 4);
Assert::AreEqual({0x1234U}, viewer.GetAddress());

// Shift click on the first dword containing 0x80000123 should lead to address 0x00000123 as PSX has mirrored RAM
// on 0x80000000-0x801FFFFF mapped to 0x00000000-0x001FFFFF
viewer.SetAddress(0);
viewer.mockEmulatorContext.WriteMemory(0U, MemSize::ThirtyTwoBit, 0x80000123);
viewer.OnShiftClick(10 * CHAR_WIDTH, CHAR_HEIGHT + 4);
Assert::AreEqual({0x00000123}, viewer.GetAddress());

// Shift click on the second dword containing 0x0 should do nothing
viewer.SetAddress(0x8);
viewer.mockEmulatorContext.WriteMemory(8U, MemSize::ThirtyTwoBit, 0x0);
viewer.OnShiftClick(28 * CHAR_WIDTH, CHAR_HEIGHT + 4);
Assert::AreEqual({0x8U}, viewer.GetAddress());

// Shift click on the first dword containing 0x005FFFFF should do nothing as 0x005FFFFF exceed the
// last address in the current console context (PSX : 0x1FFFFF) and can't be converted to real address
viewer.SetAddress(0);
viewer.mockEmulatorContext.WriteMemory(0U, MemSize::ThirtyTwoBit, 0x005FFFFF);
viewer.OnShiftClick(10 * CHAR_WIDTH, CHAR_HEIGHT + 4);
Assert::AreEqual({0x0}, viewer.GetAddress());
}
};


} // namespace tests
} // namespace viewmodels
} // namespace ui
Expand Down