duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

memoryscannerwindow.cpp (19754B)


      1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com> and contributors.
      2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
      3 
      4 #include "memoryscannerwindow.h"
      5 #include "cheatcodeeditordialog.h"
      6 #include "common/assert.h"
      7 #include "common/string_util.h"
      8 #include "core/bus.h"
      9 #include "core/cpu_core.h"
     10 #include "core/host.h"
     11 #include "core/system.h"
     12 #include "qthost.h"
     13 #include "qtutils.h"
     14 #include <QtCore/QFileInfo>
     15 #include <QtGui/QColor>
     16 #include <QtWidgets/QFileDialog>
     17 #include <QtWidgets/QInputDialog>
     18 #include <QtWidgets/QMenu>
     19 #include <QtWidgets/QMessageBox>
     20 #include <QtWidgets/QTreeWidgetItemIterator>
     21 #include <array>
     22 #include <utility>
     23 
     24 static constexpr std::array<const char*, 6> s_size_strings = {
     25   {TRANSLATE_NOOP("MemoryScannerWindow", "Byte"), TRANSLATE_NOOP("MemoryScannerWindow", "Halfword"),
     26    TRANSLATE_NOOP("MemoryScannerWindow", "Word"), TRANSLATE_NOOP("MemoryScannerWindow", "Signed Byte"),
     27    TRANSLATE_NOOP("MemoryScannerWindow", "Signed Halfword"), TRANSLATE_NOOP("MemoryScannerWindow", "Signed Word")}};
     28 
     29 static QString formatHexValue(u32 value, u8 size)
     30 {
     31   return QStringLiteral("0x%1").arg(static_cast<uint>(value), size, 16, QChar('0'));
     32 }
     33 
     34 static QString formatHexAndDecValue(u32 value, u8 size, bool is_signed)
     35 {
     36 
     37   if (is_signed)
     38   {
     39     u32 value_raw = value;
     40     if (size == 2)
     41       value_raw &= 0xFF;
     42     else if (size == 4)
     43       value_raw &= 0xFFFF;
     44     return QStringLiteral("0x%1 (%2)")
     45       .arg(static_cast<u32>(value_raw), size, 16, QChar('0'))
     46       .arg(static_cast<int>(value));
     47   }
     48   else
     49     return QStringLiteral("0x%1 (%2)").arg(static_cast<u32>(value), size, 16, QChar('0')).arg(static_cast<uint>(value));
     50 }
     51 
     52 static QString formatCheatCode(u32 address, u32 value, const MemoryAccessSize size)
     53 {
     54 
     55   if (size == MemoryAccessSize::Byte && address <= 0x00200000)
     56     return QStringLiteral("CHEAT CODE: %1 %2")
     57       .arg(static_cast<u32>(address) + 0x30000000, 8, 16, QChar('0'))
     58       .toUpper()
     59       .arg(static_cast<u16>(value), 4, 16, QChar('0'))
     60       .toUpper();
     61   else if (size == MemoryAccessSize::HalfWord && address <= 0x001FFFFE)
     62     return QStringLiteral("CHEAT CODE: %1 %2")
     63       .arg(static_cast<u32>(address) + 0x80000000, 8, 16, QChar('0'))
     64       .toUpper()
     65       .arg(static_cast<u16>(value), 4, 16, QChar('0'))
     66       .toUpper();
     67   else if (size == MemoryAccessSize::Word && address <= 0x001FFFFC)
     68     return QStringLiteral("CHEAT CODE: %1 %2")
     69       .arg(static_cast<u32>(address) + 0x90000000, 8, 16, QChar('0'))
     70       .toUpper()
     71       .arg(static_cast<u32>(value), 8, 16, QChar('0'))
     72       .toUpper();
     73   else
     74     return QStringLiteral("OUTSIDE RAM RANGE. POKE %1 with %2")
     75       .arg(static_cast<u32>(address), 8, 16, QChar('0'))
     76       .toUpper()
     77       .arg(static_cast<u16>(value), 8, 16, QChar('0'))
     78       .toUpper();
     79 }
     80 
     81 static QString formatValue(u32 value, bool is_signed)
     82 {
     83   if (is_signed)
     84     return QString::number(static_cast<int>(value));
     85   else
     86     return QString::number(static_cast<uint>(value));
     87 }
     88 
     89 MemoryScannerWindow::MemoryScannerWindow() : QWidget()
     90 {
     91   m_ui.setupUi(this);
     92   connectUi();
     93 
     94   m_ui.cheatEngineAddress->setText(tr("Address of RAM for HxD Usage: 0x%1").arg(reinterpret_cast<qulonglong>(Bus::g_unprotected_ram), 16, 16, QChar('0')));
     95 }
     96 
     97 MemoryScannerWindow::~MemoryScannerWindow() = default;
     98 
     99 void MemoryScannerWindow::connectUi()
    100 {
    101   m_ui.scanStartAddress->setText(formatHexValue(m_scanner.GetStartAddress(), 8));
    102   m_ui.scanEndAddress->setText(formatHexValue(m_scanner.GetEndAddress(), 8));
    103 
    104   connect(m_ui.scanValue, &QLineEdit::textChanged, this, &MemoryScannerWindow::updateScanValue);
    105   connect(m_ui.scanValueBase, QOverload<int>::of(&QComboBox::currentIndexChanged),
    106           [this](int index) { updateScanValue(); });
    107   connect(m_ui.scanSize, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int index) {
    108     m_scanner.SetSize(static_cast<MemoryAccessSize>(index));
    109     m_scanner.ResetSearch();
    110     updateResults();
    111   });
    112   connect(m_ui.scanValueSigned, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int index) {
    113     m_scanner.SetValueSigned(index == 0);
    114     m_scanner.ResetSearch();
    115     updateResults();
    116   });
    117   connect(m_ui.scanOperator, QOverload<int>::of(&QComboBox::currentIndexChanged),
    118           [this](int index) { m_scanner.SetOperator(static_cast<MemoryScan::Operator>(index)); });
    119   connect(m_ui.scanStartAddress, &QLineEdit::textChanged, [this](const QString& value) {
    120     uint address;
    121     if (value.startsWith(QStringLiteral("0x")) && value.length() > 2)
    122       address = value.mid(2).toUInt(nullptr, 16);
    123     else
    124       address = value.toUInt(nullptr, 16);
    125     m_scanner.SetStartAddress(static_cast<PhysicalMemoryAddress>(address));
    126   });
    127   connect(m_ui.scanEndAddress, &QLineEdit::textChanged, [this](const QString& value) {
    128     uint address;
    129     if (value.startsWith(QStringLiteral("0x")) && value.length() > 2)
    130       address = value.mid(2).toUInt(nullptr, 16);
    131     else
    132       address = value.toUInt(nullptr, 16);
    133     m_scanner.SetEndAddress(static_cast<PhysicalMemoryAddress>(address));
    134   });
    135   connect(m_ui.scanPresetRange, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int index) {
    136     if (index == 0)
    137     {
    138       m_ui.scanStartAddress->setText(formatHexValue(0, 8));
    139       m_ui.scanEndAddress->setText(formatHexValue(Bus::g_ram_size, 8));
    140     }
    141     else if (index == 1)
    142     {
    143       m_ui.scanStartAddress->setText(formatHexValue(CPU::SCRATCHPAD_ADDR, 8));
    144       m_ui.scanEndAddress->setText(formatHexValue(CPU::SCRATCHPAD_ADDR + CPU::SCRATCHPAD_SIZE, 8));
    145     }
    146     else
    147     {
    148       m_ui.scanStartAddress->setText(formatHexValue(Bus::BIOS_BASE, 8));
    149       m_ui.scanEndAddress->setText(formatHexValue(Bus::BIOS_BASE + Bus::BIOS_SIZE, 8));
    150     }
    151   });
    152   connect(m_ui.scanNewSearch, &QPushButton::clicked, [this]() {
    153     m_scanner.Search();
    154     updateResults();
    155   });
    156   connect(m_ui.scanSearchAgain, &QPushButton::clicked, [this]() {
    157     m_scanner.SearchAgain();
    158     updateResults();
    159   });
    160   connect(m_ui.scanResetSearch, &QPushButton::clicked, [this]() {
    161     m_scanner.ResetSearch();
    162     updateResults();
    163   });
    164   connect(m_ui.scanAddWatch, &QPushButton::clicked, this, &MemoryScannerWindow::addToWatchClicked);
    165   connect(m_ui.scanAddManualAddress, &QPushButton::clicked, this, &MemoryScannerWindow::addManualWatchAddressClicked);
    166   connect(m_ui.scanRemoveWatch, &QPushButton::clicked, this, &MemoryScannerWindow::removeWatchClicked);
    167   connect(m_ui.scanTable, &QTableWidget::currentItemChanged, this, &MemoryScannerWindow::scanCurrentItemChanged);
    168   connect(m_ui.watchTable, &QTableWidget::currentItemChanged, this, &MemoryScannerWindow::watchCurrentItemChanged);
    169   connect(m_ui.scanTable, &QTableWidget::itemChanged, this, &MemoryScannerWindow::scanItemChanged);
    170   connect(m_ui.watchTable, &QTableWidget::itemChanged, this, &MemoryScannerWindow::watchItemChanged);
    171 
    172   m_update_timer = new QTimer(this);
    173   connect(m_update_timer, &QTimer::timeout, this, &MemoryScannerWindow::updateScanUi);
    174 
    175   connect(g_emu_thread, &EmuThread::systemStarted, this, &MemoryScannerWindow::onSystemStarted);
    176   connect(g_emu_thread, &EmuThread::systemDestroyed, this, &MemoryScannerWindow::onSystemDestroyed);
    177 
    178   if (QtHost::IsSystemValid())
    179     onSystemStarted();
    180   else
    181     enableUi(false);
    182 }
    183 
    184 void MemoryScannerWindow::enableUi(bool enabled)
    185 {
    186   const bool has_results = (m_scanner.GetResultCount() > 0);
    187 
    188   m_ui.scanValue->setEnabled(enabled);
    189   m_ui.scanValueBase->setEnabled(enabled);
    190   m_ui.scanValueSigned->setEnabled(enabled);
    191   m_ui.scanSize->setEnabled(enabled);
    192   m_ui.scanOperator->setEnabled(enabled);
    193   m_ui.scanStartAddress->setEnabled(enabled);
    194   m_ui.scanEndAddress->setEnabled(enabled);
    195   m_ui.scanPresetRange->setEnabled(enabled);
    196   m_ui.scanResultCount->setEnabled(enabled);
    197   m_ui.scanNewSearch->setEnabled(enabled);
    198   m_ui.scanSearchAgain->setEnabled(enabled && has_results);
    199   m_ui.scanResetSearch->setEnabled(enabled && has_results);
    200   m_ui.scanAddWatch->setEnabled(enabled && !m_ui.scanTable->selectedItems().empty());
    201   m_ui.watchTable->setEnabled(enabled);
    202   m_ui.scanAddManualAddress->setEnabled(enabled);
    203   m_ui.scanRemoveWatch->setEnabled(enabled && !m_ui.watchTable->selectedItems().empty());
    204 }
    205 
    206 void MemoryScannerWindow::showEvent(QShowEvent* event)
    207 {
    208   QWidget::showEvent(event);
    209   resizeColumns();
    210 }
    211 
    212 void MemoryScannerWindow::closeEvent(QCloseEvent* event)
    213 {
    214   QWidget::closeEvent(event);
    215   emit closed();
    216 }
    217 
    218 void MemoryScannerWindow::resizeEvent(QResizeEvent* event)
    219 {
    220   QWidget::resizeEvent(event);
    221   resizeColumns();
    222 }
    223 
    224 void MemoryScannerWindow::resizeColumns()
    225 {
    226   QtUtils::ResizeColumnsForTableView(m_ui.scanTable, {-1, 130, 130});
    227   QtUtils::ResizeColumnsForTableView(m_ui.watchTable, {-1, 100, 100, 100, 40});
    228 }
    229 
    230 int MemoryScannerWindow::getSelectedResultIndexFirst() const
    231 {
    232   QList<QTableWidgetSelectionRange> sel = m_ui.scanTable->selectedRanges();
    233   if (sel.isEmpty())
    234     return -1;
    235 
    236   return sel.front().topRow();
    237 }
    238 
    239 int MemoryScannerWindow::getSelectedResultIndexLast() const
    240 {
    241   QList<QTableWidgetSelectionRange> sel = m_ui.scanTable->selectedRanges();
    242   if (sel.isEmpty())
    243     return -1;
    244 
    245   return sel.front().bottomRow();
    246 }
    247 
    248 int MemoryScannerWindow::getSelectedWatchIndexFirst() const
    249 {
    250   QList<QTableWidgetSelectionRange> sel = m_ui.watchTable->selectedRanges();
    251   if (sel.isEmpty())
    252     return -1;
    253 
    254   return sel.front().topRow();
    255 }
    256 
    257 int MemoryScannerWindow::getSelectedWatchIndexLast() const
    258 {
    259   QList<QTableWidgetSelectionRange> sel = m_ui.watchTable->selectedRanges();
    260   if (sel.isEmpty())
    261     return -1;
    262 
    263   return sel.front().bottomRow();
    264 }
    265 
    266 void MemoryScannerWindow::onSystemStarted()
    267 {
    268   if (!m_update_timer->isActive())
    269     m_update_timer->start(SCAN_INTERVAL);
    270 
    271   enableUi(true);
    272 }
    273 
    274 void MemoryScannerWindow::onSystemDestroyed()
    275 {
    276   if (m_update_timer->isActive())
    277     m_update_timer->stop();
    278 
    279   enableUi(false);
    280 }
    281 
    282 void MemoryScannerWindow::addToWatchClicked()
    283 {
    284   const int indexFirst = getSelectedResultIndexFirst();
    285   const int indexLast = getSelectedResultIndexLast();
    286   if (indexFirst < 0)
    287     return;
    288 
    289   for (int index = indexFirst; index <= indexLast; index++)
    290   {
    291     const MemoryScan::Result& res = m_scanner.GetResults()[static_cast<u32>(index)];
    292     m_watch.AddEntry(fmt::format("0x{:08x}", res.address), res.address, m_scanner.GetSize(), m_scanner.GetValueSigned(),
    293                      false);
    294     updateWatch();
    295   }
    296 }
    297 
    298 void MemoryScannerWindow::addManualWatchAddressClicked()
    299 {
    300   std::optional<unsigned> address = QtUtils::PromptForAddress(this, windowTitle(), tr("Enter manual address:"), false);
    301   if (!address.has_value())
    302     return;
    303 
    304   QStringList items;
    305   for (const char* title : s_size_strings)
    306     items.append(tr(title));
    307 
    308   bool ok = false;
    309   QString selected_item(QInputDialog::getItem(this, windowTitle(), tr("Select data size:"), items, 0, false, &ok));
    310   int index = items.indexOf(selected_item);
    311   if (index < 0 || !ok)
    312     return;
    313 
    314   if (index == 1 || index == 4)
    315     address.value() &= 0xFFFFFFFE;
    316   else if (index == 2 || index == 5)
    317     address.value() &= 0xFFFFFFFC;
    318 
    319   m_watch.AddEntry(fmt::format("0x{:08x}", address.value()), address.value(), static_cast<MemoryAccessSize>(index % 3),
    320                    (index > 3), false);
    321   updateWatch();
    322 }
    323 
    324 void MemoryScannerWindow::removeWatchClicked()
    325 {
    326   const int indexFirst = getSelectedWatchIndexFirst();
    327   const int indexLast = getSelectedWatchIndexLast();
    328   if (indexFirst < 0)
    329     return;
    330 
    331   for (int index = indexLast; index >= indexFirst; index--)
    332   {
    333     m_watch.RemoveEntry(static_cast<u32>(index));
    334     updateWatch();
    335   }
    336 }
    337 
    338 void MemoryScannerWindow::scanCurrentItemChanged(QTableWidgetItem* current, QTableWidgetItem* previous)
    339 {
    340   m_ui.scanAddWatch->setEnabled((current != nullptr));
    341 }
    342 
    343 void MemoryScannerWindow::watchCurrentItemChanged(QTableWidgetItem* current, QTableWidgetItem* previous)
    344 {
    345   m_ui.scanRemoveWatch->setEnabled((current != nullptr));
    346 }
    347 
    348 void MemoryScannerWindow::scanItemChanged(QTableWidgetItem* item)
    349 {
    350   const u32 index = static_cast<u32>(item->row());
    351   switch (item->column())
    352   {
    353     case 1:
    354     {
    355       bool value_ok = false;
    356       if (m_scanner.GetValueSigned())
    357       {
    358         int value = item->text().toInt(&value_ok);
    359         if (value_ok)
    360           m_scanner.SetResultValue(index, static_cast<u32>(value));
    361       }
    362       else
    363       {
    364         uint value = item->text().toUInt(&value_ok);
    365         if (value_ok)
    366           m_scanner.SetResultValue(index, static_cast<u32>(value));
    367       }
    368     }
    369     break;
    370 
    371     default:
    372       break;
    373   }
    374 }
    375 
    376 void MemoryScannerWindow::watchItemChanged(QTableWidgetItem* item)
    377 {
    378   const u32 index = static_cast<u32>(item->row());
    379   if (index >= m_watch.GetEntryCount())
    380     return;
    381 
    382   switch (item->column())
    383   {
    384     case 4:
    385     {
    386       m_watch.SetEntryFreeze(index, (item->checkState() == Qt::Checked));
    387     }
    388     break;
    389 
    390     case 0:
    391     {
    392       m_watch.SetEntryDescription(index, item->text().toStdString());
    393     }
    394     break;
    395 
    396     case 3:
    397     {
    398       const MemoryWatchList::Entry& entry = m_watch.GetEntry(index);
    399       bool value_ok = false;
    400       if (entry.is_signed)
    401       {
    402         int value = item->text().toInt(&value_ok);
    403         if (value_ok)
    404           m_watch.SetEntryValue(index, static_cast<u32>(value));
    405       }
    406       else
    407       {
    408         uint value;
    409         if (item->text()[1] == 'x' || item->text()[1] == 'X')
    410           value = item->text().toUInt(&value_ok, 16);
    411         else
    412           value = item->text().toUInt(&value_ok);
    413         if (value_ok)
    414           m_watch.SetEntryValue(index, static_cast<u32>(value));
    415       }
    416     }
    417     break;
    418 
    419     default:
    420       break;
    421   }
    422 }
    423 
    424 void MemoryScannerWindow::updateScanValue()
    425 {
    426   QString value = m_ui.scanValue->text();
    427   if (value.startsWith(QStringLiteral("0x")))
    428     value.remove(0, 2);
    429 
    430   bool ok = false;
    431   uint uint_value = value.toUInt(&ok, (m_ui.scanValueBase->currentIndex() > 0) ? 16 : 10);
    432   if (ok)
    433     m_scanner.SetValue(uint_value);
    434 }
    435 
    436 void MemoryScannerWindow::updateResults()
    437 {
    438   QSignalBlocker sb(m_ui.scanTable);
    439   m_ui.scanTable->setRowCount(0);
    440 
    441   int row = 0;
    442   const MemoryScan::ResultVector& results = m_scanner.GetResults();
    443   for (const MemoryScan::Result& res : results)
    444   {
    445     if (row == MAX_DISPLAYED_SCAN_RESULTS)
    446       break;
    447 
    448     m_ui.scanTable->insertRow(row);
    449 
    450     QTableWidgetItem* address_item = new QTableWidgetItem(formatHexValue(res.address, 8));
    451     address_item->setFlags(address_item->flags() & ~(Qt::ItemIsEditable));
    452     m_ui.scanTable->setItem(row, 0, address_item);
    453 
    454     QTableWidgetItem* value_item;
    455     if (m_ui.scanValueBase->currentIndex() == 0)
    456       value_item = new QTableWidgetItem(formatValue(res.value, m_scanner.GetValueSigned()));
    457     else if (m_scanner.GetSize() == MemoryAccessSize::Byte)
    458       value_item = new QTableWidgetItem(formatHexValue(res.value, 2));
    459     else if (m_scanner.GetSize() == MemoryAccessSize::HalfWord)
    460       value_item = new QTableWidgetItem(formatHexValue(res.value, 4));
    461     else
    462       value_item = new QTableWidgetItem(formatHexValue(res.value, 8));
    463     m_ui.scanTable->setItem(row, 1, value_item);
    464 
    465     QTableWidgetItem* previous_item;
    466     if (m_ui.scanValueBase->currentIndex() == 0)
    467       previous_item = new QTableWidgetItem(formatValue(res.last_value, m_scanner.GetValueSigned()));
    468     else if (m_scanner.GetSize() == MemoryAccessSize::Byte)
    469       previous_item = new QTableWidgetItem(formatHexValue(res.last_value, 2));
    470     else if (m_scanner.GetSize() == MemoryAccessSize::HalfWord)
    471       previous_item = new QTableWidgetItem(formatHexValue(res.last_value, 4));
    472     else
    473       previous_item = new QTableWidgetItem(formatHexValue(res.last_value, 8));
    474 
    475     previous_item->setFlags(address_item->flags() & ~(Qt::ItemIsEditable));
    476     m_ui.scanTable->setItem(row, 2, previous_item);
    477     row++;
    478   }
    479 
    480   m_ui.scanResultCount->setText((row < static_cast<int>(results.size())) ?
    481                                   tr("%1 (only showing first %2)").arg(results.size()).arg(row) :
    482                                   QString::number(m_scanner.GetResultCount()));
    483 
    484   m_ui.scanResetSearch->setEnabled(!results.empty());
    485   m_ui.scanSearchAgain->setEnabled(!results.empty());
    486   m_ui.scanAddWatch->setEnabled(false);
    487 }
    488 
    489 void MemoryScannerWindow::updateResultsValues()
    490 {
    491   QSignalBlocker sb(m_ui.scanTable);
    492 
    493   int row = 0;
    494   for (const MemoryScan::Result& res : m_scanner.GetResults())
    495   {
    496     if (res.value_changed)
    497     {
    498       QTableWidgetItem* item = m_ui.scanTable->item(row, 1);
    499       if (m_ui.scanValueBase->currentIndex() == 0)
    500         item->setText(formatValue(res.value, m_scanner.GetValueSigned()));
    501       else if (m_scanner.GetSize() == MemoryAccessSize::Byte)
    502         item->setText(formatHexValue(res.value, 2));
    503       else if (m_scanner.GetSize() == MemoryAccessSize::HalfWord)
    504         item->setText(formatHexValue(res.value, 4));
    505       else
    506         item->setText(formatHexValue(res.value, 8));
    507       item->setForeground(Qt::red);
    508     }
    509 
    510     row++;
    511     if (row == MAX_DISPLAYED_SCAN_RESULTS)
    512       break;
    513   }
    514 }
    515 
    516 void MemoryScannerWindow::updateWatch()
    517 {
    518   m_watch.UpdateValues();
    519 
    520   QSignalBlocker sb(m_ui.watchTable);
    521   m_ui.watchTable->setRowCount(0);
    522 
    523   const MemoryWatchList::EntryVector& entries = m_watch.GetEntries();
    524   if (!entries.empty())
    525   {
    526     int row = 0;
    527     for (const MemoryWatchList::Entry& res : entries)
    528     {
    529       m_ui.watchTable->insertRow(row);
    530 
    531       QTableWidgetItem* description_item = new QTableWidgetItem(formatCheatCode(res.address, res.value, res.size));
    532       m_ui.watchTable->setItem(row, 0, description_item);
    533 
    534       QTableWidgetItem* address_item = new QTableWidgetItem(formatHexValue(res.address, 8));
    535       address_item->setFlags(address_item->flags() & ~(Qt::ItemIsEditable));
    536       m_ui.watchTable->setItem(row, 1, address_item);
    537 
    538       QTableWidgetItem* size_item =
    539         new QTableWidgetItem(tr(s_size_strings[static_cast<u32>(res.size) + (res.is_signed ? 3 : 0)]));
    540       size_item->setFlags(address_item->flags() & ~(Qt::ItemIsEditable));
    541       m_ui.watchTable->setItem(row, 2, size_item);
    542 
    543       QTableWidgetItem* value_item;
    544       if (res.size == MemoryAccessSize::Byte)
    545         value_item = new QTableWidgetItem(formatHexAndDecValue(res.value, 2, res.is_signed));
    546       else if (res.size == MemoryAccessSize::HalfWord)
    547         value_item = new QTableWidgetItem(formatHexAndDecValue(res.value, 4, res.is_signed));
    548       else
    549         value_item = new QTableWidgetItem(formatHexAndDecValue(res.value, 8, res.is_signed));
    550 
    551       m_ui.watchTable->setItem(row, 3, value_item);
    552 
    553       QTableWidgetItem* freeze_item = new QTableWidgetItem();
    554       freeze_item->setFlags(freeze_item->flags() | (Qt::ItemIsEditable | Qt::ItemIsUserCheckable));
    555       freeze_item->setCheckState(res.freeze ? Qt::Checked : Qt::Unchecked);
    556       m_ui.watchTable->setItem(row, 4, freeze_item);
    557 
    558       row++;
    559     }
    560   }
    561 
    562   m_ui.scanSaveWatch->setEnabled(!entries.empty());
    563   m_ui.scanRemoveWatch->setEnabled(false);
    564 }
    565 
    566 void MemoryScannerWindow::updateWatchValues()
    567 {
    568   QSignalBlocker sb(m_ui.watchTable);
    569   int row = 0;
    570   for (const MemoryWatchList::Entry& res : m_watch.GetEntries())
    571   {
    572     if (res.changed)
    573     {
    574       if (m_ui.scanValueBase->currentIndex() == 0)
    575         m_ui.watchTable->item(row, 3)->setText(formatValue(res.value, res.is_signed));
    576       else if (m_scanner.GetSize() == MemoryAccessSize::Byte)
    577         m_ui.watchTable->item(row, 3)->setText(formatHexValue(res.value, 2));
    578       else if (m_scanner.GetSize() == MemoryAccessSize::HalfWord)
    579         m_ui.watchTable->item(row, 3)->setText(formatHexValue(res.value, 4));
    580       else
    581         m_ui.watchTable->item(row, 3)->setText(formatHexValue(res.value, 8));
    582     }
    583     row++;
    584   }
    585 }
    586 
    587 void MemoryScannerWindow::updateScanUi()
    588 {
    589   m_scanner.UpdateResultsValues();
    590   m_watch.UpdateValues();
    591 
    592   updateResultsValues();
    593   updateWatchValues();
    594 }