mirror of
https://github.com/RetroAchievements/RAIntegration.git
synced 2026-10-12 02:09:25 +00:00
661 lines
22 KiB
C++
661 lines
22 KiB
C++
#include "MemoryBookmarksViewModel.hh"
|
|
|
|
#include "RA_Defs.h"
|
|
#include "RA_Json.h"
|
|
#include "util\Strings.hh"
|
|
|
|
#include "data\Types.hh"
|
|
#include "data\context\EmulatorContext.hh"
|
|
#include "data\models\TriggerValidation.hh"
|
|
|
|
#include "services\AchievementLogicSerializer.hh"
|
|
#include "services\FrameEventQueue.hh"
|
|
#include "services\IConfiguration.hh"
|
|
#include "services\IFileSystem.hh"
|
|
#include "services\ILocalStorage.hh"
|
|
#include "services\ServiceLocator.hh"
|
|
|
|
#include "ui\viewmodels\FileDialogViewModel.hh"
|
|
#include "ui\viewmodels\MessageBoxViewModel.hh"
|
|
|
|
#include <rcheevos/src/rcheevos/rc_internal.h>
|
|
#include <rcheevos/src/rc_client_internal.h>
|
|
|
|
namespace ra {
|
|
namespace ui {
|
|
namespace viewmodels {
|
|
|
|
const IntModelProperty MemoryBookmarksViewModel::MemoryBookmarkViewModel::BehaviorProperty("MemoryBookmarkViewModel", "Behavior", ra::etoi(BookmarkBehavior::None));
|
|
const StringModelProperty MemoryBookmarksViewModel::FreezeButtonTextProperty("MemoryBookmarksViewModel", "FreezeButtonText", L"Freeze");
|
|
const StringModelProperty MemoryBookmarksViewModel::PauseButtonTextProperty("MemoryBookmarksViewModel", "PauseButtonText", L"Pause");
|
|
|
|
MemoryBookmarksViewModel::MemoryBookmarksViewModel() noexcept
|
|
{
|
|
SetWindowTitle(L"Memory Bookmarks");
|
|
|
|
m_vBehaviors.Add(ra::etoi(BookmarkBehavior::None), L"");
|
|
m_vBehaviors.Add(ra::etoi(BookmarkBehavior::Frozen), L"Frozen");
|
|
m_vBehaviors.Add(ra::etoi(BookmarkBehavior::PauseOnChange), L"Pause");
|
|
|
|
m_vmMemoryWatchList.Items().AddNotifyTarget(*this);
|
|
}
|
|
|
|
void MemoryBookmarksViewModel::InitializeNotifyTargets()
|
|
{
|
|
auto& pGameContext = ra::services::ServiceLocator::GetMutable<ra::data::context::GameContext>();
|
|
pGameContext.AddNotifyTarget(*this);
|
|
|
|
m_vmMemoryWatchList.InitializeNotifyTargets();
|
|
}
|
|
|
|
GSL_SUPPRESS_F6
|
|
void MemoryBookmarksViewModel::MemoryBookmarkViewModel::OnValueChanged(const IntModelProperty::ChangeArgs& args)
|
|
{
|
|
if (args.Property == MemoryBookmarkViewModel::BehaviorProperty)
|
|
{
|
|
switch (ra::itoe<BookmarkBehavior>(args.tNewValue))
|
|
{
|
|
case BookmarkBehavior::Frozen:
|
|
SetValue(RowColorProperty, 0xFFFFFFC0);
|
|
break;
|
|
|
|
default:
|
|
SetValue(RowColorProperty, MemoryBookmarkViewModel::RowColorProperty.GetDefaultValue());
|
|
break;
|
|
}
|
|
}
|
|
|
|
MemoryWatchViewModel::OnValueChanged(args);
|
|
}
|
|
|
|
bool MemoryBookmarksViewModel::MemoryBookmarkViewModel::ChangeValue(uint32_t nNewValue)
|
|
{
|
|
const auto nBehavior = GetBehavior();
|
|
|
|
if (nNewValue == GetCurrentValueRaw())
|
|
{
|
|
// if the value didn't change, remove any highlighting from previous pause events
|
|
if (nBehavior == BookmarkBehavior::PauseOnChange)
|
|
SetRowColor(ra::ui::Color(ra::to_unsigned(RowColorProperty.GetDefaultValue())));
|
|
|
|
return false;
|
|
}
|
|
|
|
// value changed. if it's frozen, write the frozen value back into memory
|
|
if (nBehavior == BookmarkBehavior::Frozen)
|
|
{
|
|
const auto& pEmulatorContext = ra::services::ServiceLocator::Get<ra::data::context::EmulatorContext>();
|
|
pEmulatorContext.WriteMemory(GetAddress(), GetSize(), GetCurrentValueRaw());
|
|
return false;
|
|
}
|
|
|
|
if (!MemoryWatchViewModel::ChangeValue(nNewValue))
|
|
return false;
|
|
|
|
if (nBehavior == BookmarkBehavior::PauseOnChange)
|
|
HandlePauseOnChange();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool MemoryBookmarksViewModel::MemoryBookmarkViewModel::IgnoreValueChange(uint32_t nNewValue)
|
|
{
|
|
// if there's an invalid pointer in the indirect chain, don't try to write the frozen value back
|
|
if (!IsIndirectAddressChainValid() && GetBehavior() == BookmarkBehavior::Frozen)
|
|
return true;
|
|
|
|
return MemoryWatchViewModel::IgnoreValueChange(nNewValue);
|
|
}
|
|
|
|
void MemoryBookmarksViewModel::MemoryBookmarkViewModel::HandlePauseOnChange()
|
|
{
|
|
SetRowColor(ra::ui::Color(0xFFFFC0C0));
|
|
|
|
auto sMessage =
|
|
ra::StringPrintf(L"%s %s", ra::data::MemSizeString(GetSize()), ra::ByteAddressToString(GetAddress()));
|
|
|
|
const auto& pDescription = GetRealNote();
|
|
if (!pDescription.empty())
|
|
{
|
|
auto nDescriptionLength = pDescription.find(L'\n');
|
|
if (nDescriptionLength == std::string::npos)
|
|
{
|
|
if (pDescription.length() < 40)
|
|
{
|
|
nDescriptionLength = pDescription.length();
|
|
}
|
|
else
|
|
{
|
|
nDescriptionLength = pDescription.find_last_of(L' ', 40);
|
|
if (nDescriptionLength == std::string::npos)
|
|
nDescriptionLength = 40;
|
|
}
|
|
}
|
|
sMessage.append(L": ");
|
|
sMessage.append(pDescription, 0, nDescriptionLength);
|
|
}
|
|
|
|
auto& pFrameEventQueue = ra::services::ServiceLocator::GetMutable<ra::services::FrameEventQueue>();
|
|
pFrameEventQueue.QueuePauseOnChange(sMessage);
|
|
}
|
|
|
|
bool MemoryBookmarksViewModel::IsModified() const noexcept
|
|
{
|
|
if (m_vmMemoryWatchList.Items().Count() != m_nUnmodifiedBookmarkCount)
|
|
return true;
|
|
|
|
for (const auto& vmBookmark : m_vmMemoryWatchList.Items())
|
|
{
|
|
if (vmBookmark.IsModified())
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void MemoryBookmarksViewModel::OnActiveGameChanged()
|
|
{
|
|
const auto& pGameContext = ra::services::ServiceLocator::Get<ra::data::context::GameContext>();
|
|
if (m_nLoadedGameId != pGameContext.GameId())
|
|
{
|
|
if (m_nLoadedGameId != 0 && IsModified())
|
|
{
|
|
auto& pLocalStorage = ra::services::ServiceLocator::GetMutable<ra::services::ILocalStorage>();
|
|
auto pWriter = pLocalStorage.WriteText(ra::services::StorageItemType::Bookmarks, std::to_wstring(m_nLoadedGameId));
|
|
if (pWriter != nullptr)
|
|
SaveBookmarks(*pWriter);
|
|
}
|
|
|
|
m_vmMemoryWatchList.Items().Clear();
|
|
m_nUnmodifiedBookmarkCount = 0;
|
|
|
|
m_nLoadedGameId = pGameContext.GameId();
|
|
|
|
if (m_nLoadedGameId != 0)
|
|
{
|
|
DispatchMemoryRead([this]()
|
|
{
|
|
auto& pLocalStorage = ra::services::ServiceLocator::GetMutable<ra::services::ILocalStorage>();
|
|
auto pReader = pLocalStorage.ReadText(ra::services::StorageItemType::Bookmarks, std::to_wstring(m_nLoadedGameId));
|
|
if (pReader != nullptr)
|
|
LoadBookmarks(*pReader);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
void MemoryBookmarksViewModel::OnEndGameLoad()
|
|
{
|
|
for (auto& vmBookmark : m_vmMemoryWatchList.Items())
|
|
vmBookmark.UpdateRealNote();
|
|
}
|
|
|
|
void MemoryBookmarksViewModel::LoadBookmarks(ra::services::TextReader& sBookmarksFile)
|
|
{
|
|
gsl::index nIndex = 0;
|
|
|
|
m_vmMemoryWatchList.Items().BeginUpdate();
|
|
|
|
rapidjson::Document document;
|
|
if (LoadDocument(document, sBookmarksFile))
|
|
{
|
|
if (document.HasMember("Bookmarks"))
|
|
{
|
|
const auto& bookmarks = document["Bookmarks"];
|
|
for (const auto& bookmark : bookmarks.GetArray())
|
|
{
|
|
auto* vmBookmark = m_vmMemoryWatchList.Items().GetItemAt<MemoryBookmarkViewModel>(nIndex);
|
|
if (vmBookmark == nullptr)
|
|
{
|
|
vmBookmark = &m_vmMemoryWatchList.Items().Add<MemoryBookmarkViewModel>();
|
|
Ensures(vmBookmark != nullptr);
|
|
}
|
|
++nIndex;
|
|
|
|
vmBookmark->BeginInitialization();
|
|
|
|
if (bookmark.HasMember("MemAddr"))
|
|
{
|
|
// third bookmark format uses the memref serializer
|
|
const char* memaddr = bookmark["MemAddr"].GetString();
|
|
InitializeBookmark(*vmBookmark, memaddr);
|
|
|
|
if (bookmark.HasMember("Size"))
|
|
{
|
|
switch (bookmark["Size"].GetInt())
|
|
{
|
|
case 15: vmBookmark->SetSize(MemSize::Text); break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MemSize nSize = MemSize::EightBit;
|
|
|
|
if (bookmark.HasMember("Type"))
|
|
{
|
|
// original bookmark format used Type for the three supported sizes.
|
|
switch (bookmark["Type"].GetInt())
|
|
{
|
|
case 1: nSize = MemSize::EightBit; break;
|
|
case 2: nSize = MemSize::SixteenBit; break;
|
|
case 3: nSize = MemSize::ThirtyTwoBit; break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// second bookmark format used the raw enum values, which was fragile.
|
|
// this enumerates the mapping for backwards compatibility.
|
|
switch (bookmark["Size"].GetInt())
|
|
{
|
|
case 0: nSize = MemSize::Bit_0; break;
|
|
case 1: nSize = MemSize::Bit_1; break;
|
|
case 2: nSize = MemSize::Bit_2; break;
|
|
case 3: nSize = MemSize::Bit_3; break;
|
|
case 4: nSize = MemSize::Bit_4; break;
|
|
case 5: nSize = MemSize::Bit_5; break;
|
|
case 6: nSize = MemSize::Bit_6; break;
|
|
case 7: nSize = MemSize::Bit_7; break;
|
|
case 8: nSize = MemSize::Nibble_Lower; break;
|
|
case 9: nSize = MemSize::Nibble_Upper; break;
|
|
case 10: nSize = MemSize::EightBit; break;
|
|
case 11: nSize = MemSize::SixteenBit; break;
|
|
case 12: nSize = MemSize::TwentyFourBit; break;
|
|
case 13: nSize = MemSize::ThirtyTwoBit; break;
|
|
case 14: nSize = MemSize::BitCount; break;
|
|
case 15: nSize = MemSize::Text; break;
|
|
}
|
|
}
|
|
|
|
vmBookmark->SetSize(nSize);
|
|
vmBookmark->SetAddress(bookmark["Address"].GetUint());
|
|
}
|
|
|
|
if (bookmark.HasMember("Decimal") && bookmark["Decimal"].GetBool())
|
|
vmBookmark->SetFormat(ra::MemFormat::Dec);
|
|
else
|
|
vmBookmark->SetFormat(ra::MemFormat::Hex);
|
|
|
|
if (!vmBookmark->IsIndirectAddress()) // Indirect note already called UpdateRealNote
|
|
vmBookmark->UpdateRealNote();
|
|
|
|
if (bookmark.HasMember("Description"))
|
|
{
|
|
const auto sDescription = ra::Widen(bookmark["Description"].GetString());
|
|
vmBookmark->SetDescription(sDescription);
|
|
}
|
|
|
|
vmBookmark->SetBehavior(MemoryBookmarksViewModel::BookmarkBehavior::None);
|
|
|
|
vmBookmark->EndInitialization();
|
|
}
|
|
}
|
|
}
|
|
|
|
while (m_vmMemoryWatchList.Items().Count() > ra::to_unsigned(nIndex))
|
|
m_vmMemoryWatchList.Items().RemoveAt(m_vmMemoryWatchList.Items().Count() - 1);
|
|
|
|
m_vmMemoryWatchList.Items().EndUpdate();
|
|
|
|
m_nUnmodifiedBookmarkCount = m_vmMemoryWatchList.Items().Count();
|
|
}
|
|
|
|
void MemoryBookmarksViewModel::SaveBookmarks(ra::services::TextWriter& sBookmarksFile)
|
|
{
|
|
std::string sSerialized;
|
|
|
|
rapidjson::Document document;
|
|
auto& allocator = document.GetAllocator();
|
|
document.SetObject();
|
|
|
|
rapidjson::Value bookmarks(rapidjson::kArrayType);
|
|
for (auto& vmBookmark : m_vmMemoryWatchList.Items())
|
|
{
|
|
rapidjson::Value item(rapidjson::kObjectType);
|
|
|
|
const auto nSize = vmBookmark.GetSize();
|
|
switch (nSize)
|
|
{
|
|
case MemSize::Text:
|
|
item.AddMember("Size", 15, allocator);
|
|
if (vmBookmark.IsIndirectAddress())
|
|
item.AddMember("MemAddr", vmBookmark.GetIndirectAddress(), allocator);
|
|
else
|
|
item.AddMember("Address", vmBookmark.GetAddress(), allocator);
|
|
break;
|
|
|
|
default:
|
|
if (vmBookmark.IsIndirectAddress())
|
|
{
|
|
item.AddMember("MemAddr", vmBookmark.GetIndirectAddress(), allocator);
|
|
}
|
|
else
|
|
{
|
|
sSerialized.clear();
|
|
ra::services::AchievementLogicSerializer::AppendOperand(
|
|
sSerialized, ra::services::TriggerOperandType::Address, nSize, vmBookmark.GetAddress());
|
|
item.AddMember("MemAddr", sSerialized, allocator);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (vmBookmark.GetFormat() != MemFormat::Hex)
|
|
item.AddMember("Decimal", true, allocator);
|
|
|
|
if (vmBookmark.IsCustomDescription())
|
|
item.AddMember("Description", ra::Narrow(vmBookmark.GetDescription()), allocator);
|
|
|
|
bookmarks.PushBack(item, allocator);
|
|
vmBookmark.ResetModified();
|
|
}
|
|
|
|
document.AddMember("Bookmarks", bookmarks, allocator);
|
|
SaveDocument(document, sBookmarksFile);
|
|
}
|
|
|
|
void MemoryBookmarksViewModel::DoFrame()
|
|
{
|
|
m_vmMemoryWatchList.DoFrame();
|
|
}
|
|
|
|
bool MemoryBookmarksViewModel::HasBookmark(ra::ByteAddress nAddress) const noexcept
|
|
{
|
|
for (const auto& pBookmark : m_vmMemoryWatchList.Items())
|
|
{
|
|
if (pBookmark.GetAddress() == nAddress)
|
|
{
|
|
if (!pBookmark.IsIndirectAddress() || pBookmark.IsIndirectAddressChainValid())
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool MemoryBookmarksViewModel::HasFrozenBookmark(ra::ByteAddress nAddress) const
|
|
{
|
|
for (const auto& vmBookmark : m_vmMemoryWatchList.Items())
|
|
{
|
|
auto* pBookmark = dynamic_cast<const MemoryBookmarkViewModel*>(&vmBookmark);
|
|
if (pBookmark != nullptr && pBookmark->GetBehavior() == BookmarkBehavior::Frozen && pBookmark->GetAddress() == nAddress)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void MemoryBookmarksViewModel::AddBookmark(ra::ByteAddress nAddress, MemSize nSize)
|
|
{
|
|
auto vmBookmark = std::make_unique<MemoryBookmarkViewModel>();
|
|
vmBookmark->BeginInitialization();
|
|
|
|
vmBookmark->SetAddress(nAddress);
|
|
vmBookmark->SetSize(nSize);
|
|
InitializeBookmark(*vmBookmark);
|
|
|
|
vmBookmark->EndInitialization();
|
|
|
|
m_vmMemoryWatchList.Items().Append(std::move(vmBookmark));
|
|
}
|
|
|
|
void MemoryBookmarksViewModel::AddBookmark(const std::string& sSerialized)
|
|
{
|
|
auto vmBookmark = std::make_unique<MemoryBookmarkViewModel>();
|
|
vmBookmark->BeginInitialization();
|
|
|
|
InitializeBookmark(*vmBookmark, sSerialized);
|
|
InitializeBookmark(*vmBookmark);
|
|
|
|
vmBookmark->EndInitialization();
|
|
|
|
m_vmMemoryWatchList.Items().Append(std::move(vmBookmark));
|
|
}
|
|
|
|
void MemoryBookmarksViewModel::InitializeBookmark(MemoryWatchViewModel& vmBookmark)
|
|
{
|
|
vmBookmark.SetFormat(MemFormat::Unknown);
|
|
|
|
vmBookmark.UpdateRealNote();
|
|
}
|
|
|
|
void MemoryBookmarksViewModel::InitializeBookmark(MemoryWatchViewModel& vmBookmark, const std::string& sSerialized)
|
|
{
|
|
// if there's no condition separator, it's a simple memref
|
|
if (sSerialized.find('_') == std::string::npos)
|
|
{
|
|
uint8_t size = 0;
|
|
uint32_t address = 0;
|
|
const char* memaddr = sSerialized.c_str();
|
|
if (ra::StringStartsWith(sSerialized, "M:"))
|
|
memaddr += 2;
|
|
|
|
if (rc_parse_memref(&memaddr, &size, &address) == RC_OK)
|
|
{
|
|
vmBookmark.SetAddress(address);
|
|
vmBookmark.SetSize(ra::data::models::TriggerValidation::MapRcheevosMemSize(size));
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// complex memref.
|
|
vmBookmark.SetIndirectAddress(sSerialized);
|
|
}
|
|
|
|
int MemoryBookmarksViewModel::RemoveSelectedBookmarks()
|
|
{
|
|
m_vmMemoryWatchList.Items().BeginUpdate();
|
|
|
|
int nRemoved = 0;
|
|
for (gsl::index nIndex = m_vmMemoryWatchList.Items().Count() - 1; nIndex >= 0; --nIndex)
|
|
{
|
|
if (m_vmMemoryWatchList.Items().GetItemAt(nIndex)->IsSelected())
|
|
{
|
|
m_vmMemoryWatchList.Items().RemoveAt(nIndex);
|
|
++nRemoved;
|
|
}
|
|
}
|
|
|
|
m_vmMemoryWatchList.Items().EndUpdate();
|
|
|
|
return nRemoved;
|
|
}
|
|
|
|
void MemoryBookmarksViewModel::OnViewModelBoolValueChanged(gsl::index, const BoolModelProperty::ChangeArgs& args)
|
|
{
|
|
if (args.Property == LookupItemViewModel::IsSelectedProperty)
|
|
{
|
|
if (!m_vmMemoryWatchList.Items().IsUpdating())
|
|
{
|
|
UpdateFreezeButtonText();
|
|
UpdatePauseButtonText();
|
|
}
|
|
}
|
|
}
|
|
|
|
void MemoryBookmarksViewModel::OnViewModelIntValueChanged(gsl::index, const IntModelProperty::ChangeArgs& args)
|
|
{
|
|
if (args.Property == MemoryBookmarkViewModel::BehaviorProperty)
|
|
{
|
|
if (!m_vmMemoryWatchList.Items().IsUpdating())
|
|
{
|
|
UpdateFreezeButtonText();
|
|
UpdatePauseButtonText();
|
|
}
|
|
}
|
|
}
|
|
|
|
void MemoryBookmarksViewModel::OnEndViewModelCollectionUpdate()
|
|
{
|
|
UpdateFreezeButtonText();
|
|
UpdatePauseButtonText();
|
|
}
|
|
|
|
bool MemoryBookmarksViewModel::ShouldFreeze() const
|
|
{
|
|
for (const auto& vmBookmark : m_vmMemoryWatchList.Items())
|
|
{
|
|
auto* pBookmark = dynamic_cast<const MemoryBookmarkViewModel*>(&vmBookmark);
|
|
if (pBookmark != nullptr && pBookmark->IsSelected() && pBookmark->GetBehavior() != BookmarkBehavior::Frozen)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void MemoryBookmarksViewModel::UpdateFreezeButtonText()
|
|
{
|
|
if (!m_vmMemoryWatchList.HasSelection() || ShouldFreeze())
|
|
SetValue(FreezeButtonTextProperty, FreezeButtonTextProperty.GetDefaultValue());
|
|
else
|
|
SetValue(FreezeButtonTextProperty, L"Unfreeze");
|
|
}
|
|
|
|
void MemoryBookmarksViewModel::ToggleFreezeSelected()
|
|
{
|
|
m_vmMemoryWatchList.Items().BeginUpdate();
|
|
|
|
if (ShouldFreeze())
|
|
{
|
|
for (auto& vmItem : m_vmMemoryWatchList.Items())
|
|
{
|
|
auto* pItem = dynamic_cast<MemoryBookmarkViewModel*>(&vmItem);
|
|
if (pItem && pItem->IsSelected())
|
|
pItem->SetBehavior(BookmarkBehavior::Frozen);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (auto& vmItem : m_vmMemoryWatchList.Items())
|
|
{
|
|
auto* pItem = dynamic_cast<MemoryBookmarkViewModel*>(&vmItem);
|
|
if (pItem && pItem->IsSelected() && pItem->GetBehavior() == BookmarkBehavior::Frozen)
|
|
pItem->SetBehavior(BookmarkBehavior::None);
|
|
}
|
|
}
|
|
|
|
m_vmMemoryWatchList.Items().EndUpdate();
|
|
}
|
|
|
|
bool MemoryBookmarksViewModel::ShouldPause() const
|
|
{
|
|
for (const auto& vmItem : m_vmMemoryWatchList.Items())
|
|
{
|
|
const auto* pBookmark = dynamic_cast<const MemoryBookmarkViewModel*>(&vmItem);
|
|
if (pBookmark != nullptr && pBookmark->IsSelected() && pBookmark->GetBehavior() != BookmarkBehavior::PauseOnChange)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void MemoryBookmarksViewModel::UpdatePauseButtonText()
|
|
{
|
|
if (!m_vmMemoryWatchList.HasSelection() || ShouldPause())
|
|
SetValue(PauseButtonTextProperty, PauseButtonTextProperty.GetDefaultValue());
|
|
else
|
|
SetValue(PauseButtonTextProperty, L"Stop Pausing");
|
|
}
|
|
|
|
void MemoryBookmarksViewModel::TogglePauseSelected()
|
|
{
|
|
m_vmMemoryWatchList.Items().BeginUpdate();
|
|
|
|
if (ShouldPause())
|
|
{
|
|
for (auto& vmItem : m_vmMemoryWatchList.Items())
|
|
{
|
|
auto* pItem = dynamic_cast<MemoryBookmarkViewModel*>(&vmItem);
|
|
if (pItem && pItem->IsSelected())
|
|
pItem->SetBehavior(BookmarkBehavior::PauseOnChange);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (auto& vmItem : m_vmMemoryWatchList.Items())
|
|
{
|
|
auto* pItem = dynamic_cast<MemoryBookmarkViewModel*>(&vmItem);
|
|
if (pItem && pItem->IsSelected() && pItem->GetBehavior() == BookmarkBehavior::PauseOnChange)
|
|
pItem->SetBehavior(BookmarkBehavior::None);
|
|
}
|
|
}
|
|
|
|
m_vmMemoryWatchList.Items().EndUpdate();
|
|
}
|
|
|
|
void MemoryBookmarksViewModel::MoveSelectedBookmarksUp()
|
|
{
|
|
m_vmMemoryWatchList.Items().ShiftItemsUp(MemoryBookmarkViewModel::IsSelectedProperty);
|
|
}
|
|
|
|
void MemoryBookmarksViewModel::MoveSelectedBookmarksDown()
|
|
{
|
|
m_vmMemoryWatchList.Items().ShiftItemsDown(MemoryBookmarkViewModel::IsSelectedProperty);
|
|
}
|
|
|
|
void MemoryBookmarksViewModel::ClearBehaviors()
|
|
{
|
|
for (auto& vmBookmark : m_vmMemoryWatchList.Items())
|
|
{
|
|
auto* pBookmark = dynamic_cast<MemoryBookmarkViewModel*>(&vmBookmark);
|
|
if (pBookmark)
|
|
pBookmark->SetBehavior(BookmarkBehavior::None);
|
|
}
|
|
}
|
|
|
|
void MemoryBookmarksViewModel::LoadBookmarkFile()
|
|
{
|
|
ra::ui::viewmodels::FileDialogViewModel vmFileDialog;
|
|
vmFileDialog.SetWindowTitle(L"Import Bookmark File");
|
|
vmFileDialog.AddFileType(L"JSON File", L"*.json");
|
|
vmFileDialog.AddFileType(L"Text File", L"*.txt");
|
|
vmFileDialog.SetDefaultExtension(L"json");
|
|
|
|
const auto& pGameContext = ra::services::ServiceLocator::Get<ra::data::context::GameContext>();
|
|
vmFileDialog.SetFileName(ra::StringPrintf(L"%u-Bookmarks.json", pGameContext.GameId()));
|
|
|
|
if (vmFileDialog.ShowOpenFileDialog() == ra::ui::DialogResult::OK)
|
|
{
|
|
const auto& pFileSystem = ra::services::ServiceLocator::Get<ra::services::IFileSystem>();
|
|
auto pTextReader = pFileSystem.OpenTextFile(vmFileDialog.GetFileName());
|
|
if (pTextReader == nullptr)
|
|
{
|
|
ra::ui::viewmodels::MessageBoxViewModel::ShowErrorMessage(
|
|
ra::StringPrintf(L"Could not open %s", vmFileDialog.GetFileName()));
|
|
}
|
|
else
|
|
{
|
|
LoadBookmarks(*pTextReader);
|
|
}
|
|
}}
|
|
|
|
void MemoryBookmarksViewModel::SaveBookmarkFile()
|
|
{
|
|
ra::ui::viewmodels::FileDialogViewModel vmFileDialog;
|
|
vmFileDialog.SetWindowTitle(L"Export Bookmark File");
|
|
vmFileDialog.AddFileType(L"JSON File", L"*.json");
|
|
vmFileDialog.SetDefaultExtension(L"json");
|
|
|
|
const auto& pGameContext = ra::services::ServiceLocator::Get<ra::data::context::GameContext>();
|
|
vmFileDialog.SetFileName(ra::StringPrintf(L"%u-Bookmarks.json", pGameContext.GameId()));
|
|
|
|
if (vmFileDialog.ShowSaveFileDialog() == ra::ui::DialogResult::OK)
|
|
{
|
|
const auto& pFileSystem = ra::services::ServiceLocator::Get<ra::services::IFileSystem>();
|
|
auto pTextWriter = pFileSystem.CreateTextFile(vmFileDialog.GetFileName());
|
|
if (pTextWriter == nullptr)
|
|
{
|
|
ra::ui::viewmodels::MessageBoxViewModel::ShowErrorMessage(
|
|
ra::StringPrintf(L"Could not create %s", vmFileDialog.GetFileName()));
|
|
}
|
|
else
|
|
{
|
|
SaveBookmarks(*pTextWriter);
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace viewmodels
|
|
} // namespace ui
|
|
} // namespace ra
|