From 77ff4eab033a09565b26aba433480b4c7c330dde Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Fri, 24 May 2024 21:07:14 +0200 Subject: [PATCH] add separate combo box for selecting flag in db's comparison, change `themeNameOfOffset` so it returns QString since it was always converted to it anyway --- include/QtHelpers/WidgetDatabaseView.h | 7 +- include/Spelunky2.h | 4 +- src/QtHelpers/TreeViewMemoryFields.cpp | 15 ++-- src/QtHelpers/WidgetDatabaseView.cpp | 120 +++++++++++++++++++------ src/Spelunky2.cpp | 10 +-- 5 files changed, 110 insertions(+), 46 deletions(-) diff --git a/include/QtHelpers/WidgetDatabaseView.h b/include/QtHelpers/WidgetDatabaseView.h index fc6fd72..36dadae 100644 --- a/include/QtHelpers/WidgetDatabaseView.h +++ b/include/QtHelpers/WidgetDatabaseView.h @@ -65,6 +65,7 @@ namespace S2Plugin void fieldUpdated(int row, QStandardItem* parrent); void fieldExpanded(const QModelIndex& index); void comparisonFieldChosen(); + void comparisonFlagChosen(const QString& text); void compareGroupByCheckBoxClicked(int state); void comparisonCellClicked(int row, int column); void groupedComparisonItemClicked(QTreeWidgetItem* item); @@ -73,14 +74,16 @@ namespace S2Plugin QLineEdit* mSearchLineEdit; TreeViewMemoryFields* mMainTreeView; QTableWidget* mCompareTableWidget; + bool mFieldChoosen{false}; private: QTabWidget* mMainTabWidget; QComboBox* mCompareFieldComboBox; + QComboBox* mCompareFlagComboBox; QTreeWidget* mCompareTreeWidget; - void populateComparisonTableWidget(); - void populateComparisonTreeWidget(); + void populateComparisonTableWidget(const QVariant& fieldData); + void populateComparisonTreeWidget(const QVariant& fieldData); size_t populateComparisonCombobox(const std::vector& fields, size_t offset = 0, std::string prefix = {}); static std::pair valueForField(const QVariant& data, uintptr_t addr); }; diff --git a/include/Spelunky2.h b/include/Spelunky2.h index f7d60e3..66284de 100644 --- a/include/Spelunky2.h +++ b/include/Spelunky2.h @@ -7,8 +7,8 @@ #include "Data/StringsTable.h" #include "Data/TextureDB.h" #include "Data/VirtualTableLookup.h" +#include #include -#include namespace S2Plugin { @@ -54,7 +54,7 @@ namespace S2Plugin uintptr_t find(const char* pattern, uintptr_t start = 0, size_t size = 0) const; uintptr_t find_between(const char* pattern, uintptr_t start = 0, uintptr_t end = 0) const; - std::string themeNameOfOffset(uintptr_t offset) const; + const QString& themeNameOfOffset(uintptr_t offset) const; private: static Spelunky2* ptr; diff --git a/src/QtHelpers/TreeViewMemoryFields.cpp b/src/QtHelpers/TreeViewMemoryFields.cpp index 24e8384..d118978 100644 --- a/src/QtHelpers/TreeViewMemoryFields.cpp +++ b/src/QtHelpers/TreeViewMemoryFields.cpp @@ -146,7 +146,7 @@ QStandardItem* S2Plugin::TreeViewMemoryFields::addMemoryField(const MemoryField& auto itemFieldType = new QStandardItem(); itemFieldType->setEditable(false); - QString typeName = field.isPointer ? "P: " : ""; // TODO: add color? + QString typeName = field.isPointer ? "P: " : ""; // add color? if (field.type == MemoryFieldType::EntitySubclass || field.type == MemoryFieldType::DefaultStructType) typeName += QString::fromStdString(field.jsonName); else if (auto str = Configuration::getTypeDisplayName(field.type); !str.empty()) @@ -1688,14 +1688,14 @@ void S2Plugin::TreeViewMemoryFields::updateRow(int row, std::optional if (valueMemoryOffset == 0) itemValue->setData("n/a", Qt::DisplayRole); else - itemValue->setData(QString::fromStdString(Spelunky2::get()->themeNameOfOffset(valueMemoryOffset)), Qt::DisplayRole); + itemValue->setData(Spelunky2::get()->themeNameOfOffset(valueMemoryOffset), Qt::DisplayRole); if (comparisonActive) { if (valueComparisonMemoryOffset == 0) itemComparisonValue->setData("n/a", Qt::DisplayRole); else - itemComparisonValue->setData(QString::fromStdString(Spelunky2::get()->themeNameOfOffset(valueMemoryOffset)), Qt::DisplayRole); + itemComparisonValue->setData(Spelunky2::get()->themeNameOfOffset(valueMemoryOffset), Qt::DisplayRole); itemComparisonValue->setBackground(itemComparisonValueHex->background()); } @@ -1720,7 +1720,7 @@ void S2Plugin::TreeViewMemoryFields::updateRow(int row, std::optional if (comparisonActive) { // we can't show comparison version since it's a tab, not a new window - // TODO: maybe add comparison in the show rooms tab? + // maybe add comparison in the show rooms tab? itemComparisonValue->setData({}, Qt::DisplayRole); itemComparisonValue->setData(0, gsRoleMemoryAddress); @@ -1825,7 +1825,6 @@ void S2Plugin::TreeViewMemoryFields::updateRow(int row, std::optional { std::optional value; // we use the size to check if it was changed - // TODO: don't use updateField, make struct with two size_t values for comparison and hold (will also be needed for std::list) value = updateField(itemField, valueMemoryOffset == 0 ? 0 : valueMemoryOffset + 0x8, itemValue, nullptr, nullptr, true, nullptr, true, !pointerUpdate, highlightColor); if (value.has_value()) { @@ -1842,7 +1841,7 @@ void S2Plugin::TreeViewMemoryFields::updateRow(int row, std::optional { itemComparisonValue->setData("Show contents", Qt::DisplayRole); } - // TODO maybe it should be based on the pointer not size? + // maybe it should be based on the pointer not size? itemComparisonValue->setBackground(value != comparisonValue ? comparisonDifferenceColor : Qt::transparent); if (isPointer == false) itemComparisonValueHex->setBackground(value != comparisonValue ? comparisonDifferenceColor : Qt::transparent); @@ -1858,7 +1857,7 @@ void S2Plugin::TreeViewMemoryFields::updateRow(int row, std::optional } case MemoryFieldType::Skip: { - // TODO + // TODO when setting for skip is done break; } case MemoryFieldType::EntitySubclass: @@ -2146,7 +2145,7 @@ void S2Plugin::TreeViewMemoryFields::cellClicked(const QModelIndex& index) auto rawValue = clickedItem->data(gsRoleMemoryAddress).toULongLong(); if (rawValue != 0) { - // TODO: maybe use the "entity interpret as" for vtable? (it's doable) + // maybe use the "entity interpret as" for vtable? (it's doable) auto vftType = qvariant_cast(getDataFrom(index, gsColField, gsRoleRefName)); if (vftType == "Entity") // in case of Entity, we have to see what the entity is interpreted as, and show those functions { diff --git a/src/QtHelpers/WidgetDatabaseView.cpp b/src/QtHelpers/WidgetDatabaseView.cpp index a4ff08c..34ffdad 100644 --- a/src/QtHelpers/WidgetDatabaseView.cpp +++ b/src/QtHelpers/WidgetDatabaseView.cpp @@ -28,6 +28,15 @@ namespace std }; } // namespace std +struct ComparisonField +{ + S2Plugin::MemoryFieldType type{S2Plugin::MemoryFieldType::None}; + size_t offset{0}; + std::string refName; // for flags + uint8_t flag_index; +}; +Q_DECLARE_METATYPE(ComparisonField) + S2Plugin::WidgetDatabaseView::WidgetDatabaseView(MemoryFieldType type, QWidget* parent) : QWidget(parent) { setWindowIcon(getCavemanIcon()); @@ -93,6 +102,11 @@ S2Plugin::WidgetDatabaseView::WidgetDatabaseView(MemoryFieldType type, QWidget* QObject::connect(mCompareFieldComboBox, &QComboBox::currentTextChanged, this, &WidgetDatabaseView::comparisonFieldChosen); topLayout->addWidget(mCompareFieldComboBox); + mCompareFlagComboBox = new QComboBox(); + QObject::connect(mCompareFlagComboBox, &QComboBox::currentTextChanged, this, &WidgetDatabaseView::comparisonFlagChosen); + mCompareFlagComboBox->hide(); + topLayout->addWidget(mCompareFlagComboBox); + auto groupCheckbox = new QCheckBox("Group by value", this); QObject::connect(groupCheckbox, &QCheckBox::stateChanged, this, &WidgetDatabaseView::compareGroupByCheckBoxClicked); topLayout->addWidget(groupCheckbox); @@ -185,25 +199,93 @@ void S2Plugin::WidgetDatabaseView::compareGroupByCheckBoxClicked(int state) void S2Plugin::WidgetDatabaseView::comparisonFieldChosen() { + mFieldChoosen = true; mCompareTableWidget->clearContents(); mCompareTreeWidget->clear(); auto comboIndex = mCompareFieldComboBox->currentIndex(); if (comboIndex == 0) + return; + + auto comboboxData = mCompareFieldComboBox->currentData(); + if (!comboboxData.isValid()) + return; + + auto type = comboboxData.value().type; + uint8_t flagsCount = 0; + if (type == MemoryFieldType::Flags8) + flagsCount = 8; + else if (type == MemoryFieldType::Flags16) + flagsCount = 16; + else if (type == MemoryFieldType::Flags32) + flagsCount = 32; + + if (flagsCount != 0) + { + mCompareFlagComboBox->clear(); + mCompareFlagComboBox->addItem(""); + std::string refName = comboboxData.value().refName; + for (uint8_t x = 1; x <= flagsCount; ++x) + { + auto flagName = Configuration::get()->flagTitle(refName, x); + // TODO: don't show unknown unless it was chosen in settings + QString realFlagName = QString("%1: ").arg(x) + QString::fromStdString(flagName.empty() ? Configuration::get()->flagTitle("unknown", x) : flagName); + mCompareFlagComboBox->addItem(realFlagName, x - 1); + } + mCompareFlagComboBox->show(); + } + else { + mCompareFlagComboBox->clear(); + mCompareFlagComboBox->hide(); + } + + populateComparisonTableWidget(comboboxData); + populateComparisonTreeWidget(comboboxData); + mFieldChoosen = false; +} + +void S2Plugin::WidgetDatabaseView::comparisonFlagChosen(const QString& text) +{ + // protect againts infinite loops since this slot is called when adding firts element to combo box + if (mFieldChoosen) + return; + + mCompareTableWidget->clearContents(); + mCompareTreeWidget->clear(); + + if (text.isEmpty()) + { + auto comboboxData = mCompareFieldComboBox->currentData(); + if (!comboboxData.isValid()) + return; + + populateComparisonTableWidget(comboboxData); + populateComparisonTreeWidget(comboboxData); + return; } - populateComparisonTableWidget(); - populateComparisonTreeWidget(); + auto fieldData = mCompareFieldComboBox->currentData(); + if (!fieldData.isValid()) + return; + auto flagData = mCompareFlagComboBox->currentData(); + if (!flagData.isValid()) + return; + + ComparisonField comparisonData = fieldData.value(); + comparisonData.type = MemoryFieldType::Flag; + comparisonData.flag_index = flagData.value(); + auto newData = QVariant::fromValue(comparisonData); + + populateComparisonTableWidget(newData); + populateComparisonTreeWidget(newData); } -void S2Plugin::WidgetDatabaseView::populateComparisonTableWidget() +void S2Plugin::WidgetDatabaseView::populateComparisonTableWidget(const QVariant& fieldData) { mCompareTableWidget->setSortingEnabled(false); - auto comboboxData = mCompareFieldComboBox->currentData(); - int row = 0; for (ID_type x = 0; x <= highestRecordID(); ++x) { @@ -216,7 +298,7 @@ void S2Plugin::WidgetDatabaseView::populateComparisonTableWidget() const auto name = recordNameForID(x); mCompareTableWidget->setItem(row, 1, new QTableWidgetItem(QString("%1").arg(name))); - auto [caption, value] = valueForField(comboboxData, addressOfRecordID(x)); + auto [caption, value] = valueForField(fieldData, addressOfRecordID(x)); auto item = new TableWidgetItemNumeric(caption); item->setData(Qt::UserRole, value); mCompareTableWidget->setItem(row, 2, item); @@ -227,11 +309,10 @@ void S2Plugin::WidgetDatabaseView::populateComparisonTableWidget() mCompareTableWidget->sortItems(0); } -void S2Plugin::WidgetDatabaseView::populateComparisonTreeWidget() +void S2Plugin::WidgetDatabaseView::populateComparisonTreeWidget(const QVariant& fieldData) { mCompareTreeWidget->setSortingEnabled(false); - auto comboboxData = mCompareFieldComboBox->currentData(); std::unordered_map rootValues; std::unordered_map> groupedValues; // valueString -> vector for (ID_type x = 0; x <= highestRecordID(); ++x) @@ -239,7 +320,7 @@ void S2Plugin::WidgetDatabaseView::populateComparisonTreeWidget() if (!isValidRecordID(x)) continue; - auto [caption, value] = valueForField(comboboxData, addressOfRecordID(x)); + auto [caption, value] = valueForField(fieldData, addressOfRecordID(x)); rootValues[caption] = value; if (auto it = groupedValues.find(caption); it != groupedValues.end()) @@ -290,16 +371,6 @@ void S2Plugin::WidgetDatabaseView::groupedComparisonItemClicked(QTreeWidgetItem* } } -// TODO: instead of adding all the flags as separate options, add flags and when selected, show another combo box with the flags - -struct ComparisonField -{ - S2Plugin::MemoryFieldType type{S2Plugin::MemoryFieldType::None}; - size_t offset; - uint8_t flag_index; -}; -Q_DECLARE_METATYPE(ComparisonField) - size_t S2Plugin::WidgetDatabaseView::populateComparisonCombobox(const std::vector& fields, size_t offset, std::string prefix) { for (const auto& field : fields) @@ -322,17 +393,8 @@ size_t S2Plugin::WidgetDatabaseView::populateComparisonCombobox(const std::vecto ComparisonField parrentFlag; parrentFlag.type = field.type; parrentFlag.offset = offset; + parrentFlag.refName = field.firstParameterType; mCompareFieldComboBox->addItem(QString::fromStdString(prefix + field.name), QVariant::fromValue(parrentFlag)); - uint8_t flagCount = (field.type == MemoryFieldType::Flags16 ? 16 : (field.type == MemoryFieldType::Flags8 ? 8 : 32)); - for (uint8_t x = 1; x <= flagCount; ++x) - { - ComparisonField flag; - flag.type = MemoryFieldType::Flag; - flag.offset = offset; - flag.flag_index = x - 1; - // TODO: use flag name? - mCompareFieldComboBox->addItem(QString::fromStdString(prefix + field.name + ".flag_" + std::to_string(x)), QVariant::fromValue(flag)); - } break; } case MemoryFieldType::DefaultStructType: diff --git a/src/Spelunky2.cpp b/src/Spelunky2.cpp index 8f04d89..c0cd8d8 100644 --- a/src/Spelunky2.cpp +++ b/src/Spelunky2.cpp @@ -138,12 +138,12 @@ uintptr_t S2Plugin::Spelunky2::get_SaveDataPtr() return heapBaseAddr + heapOffsetSaveGame; } -std::string S2Plugin::Spelunky2::themeNameOfOffset(uintptr_t offset) const // TODO use QString? +const QString& S2Plugin::Spelunky2::themeNameOfOffset(uintptr_t offset) const { auto config = Configuration::get(); uintptr_t firstThemeOffset = config->offsetForField(MemoryFieldType::LevelGen, "theme_dwelling", get_LevelGenPtr()); - const static auto themeNames = { + static const QStringList themeNames = { "DWELLING", "JUNGLE", "VOLCANA", "OLMEC", "TIDE POOL", "TEMPLE", "ICE CAVES", "NEO BABYLON", "SUNKEN CITY", "COSMIC OCEAN", "CITY OF GOLD", "DUAT", "ABZU", "TIAMAT", "EGGPLANT WORLD", "HUNDUN", "BASE CAMP", "ARENA", }; @@ -151,8 +151,8 @@ std::string S2Plugin::Spelunky2::themeNameOfOffset(uintptr_t offset) const // TO { uintptr_t testPtr = Script::Memory::ReadQword(firstThemeOffset + idx * 0x8ull); if (testPtr == offset) - return *(themeNames.begin() + idx); + return themeNames.at(idx); } - - return "UNKNOWN THEME"; + static const QString unknown{"UNKNOWN THEME"}; + return unknown; }