From 824ed212a9268c9562d56a7886dbb871c34dd5d6 Mon Sep 17 00:00:00 2001 From: Mr-Auto <36127424+Mr-Auto@users.noreply.github.com> Date: Sat, 1 Jun 2024 21:37:15 +0200 Subject: [PATCH] improve performance by using the new EntityList class --- include/Data/EntityList.h | 62 ++++++++++----------- include/Views/ViewEntities.h | 4 +- src/QtHelpers/WidgetSpelunkyLevel.cpp | 13 ++--- src/Views/ViewEntities.cpp | 78 ++++++++++++--------------- src/Views/ViewEntityList.cpp | 9 ++-- 5 files changed, 77 insertions(+), 89 deletions(-) diff --git a/include/Data/EntityList.h b/include/Data/EntityList.h index 3498e3c..2ccad79 100644 --- a/include/Data/EntityList.h +++ b/include/Data/EntityList.h @@ -5,30 +5,33 @@ #include "read_helpers.h" #include #include +#include namespace S2Plugin { - + // this shoudl only ever be used as temporary class EntityList { public: - EntityList(uintptr_t _address) : address(_address){}; + EntityList(uintptr_t address) + { + Script::Memory::Read(address, this, sizeof(EntityList), nullptr); + }; uintptr_t entities() const { - return Script::Memory::ReadQword(address); + return mEntities; } - uintptr_t uids() const { - return Script::Memory::ReadQword(address + sizeof(uintptr_t)); + return mUids; } uint32_t capacity() const { - return Script::Memory::ReadDword(address + sizeof(uintptr_t) * 2); + return mCap; } uint32_t size() const { - return Script::Memory::ReadDword(address + sizeof(uintptr_t) * 2 + sizeof(uint32_t)); + return mSize; } struct Iterator { @@ -37,7 +40,7 @@ namespace S2Plugin { advance(index); } - void advance(int count) noexcept // should probably be int64 ? + void advance(int count) noexcept // should probably be int64 { addr.first += count * sizeof(uintptr_t); addr.second += count * sizeof(uint32_t); @@ -96,16 +99,12 @@ namespace S2Plugin Iterator begin() const { - uintptr_t pointers[2] = {0, 0}; - // slightly faster then reading both thru ReadQword - Script::Memory::Read(address, &pointers, sizeof(uintptr_t) * 2, nullptr); - return {pointers[0], pointers[1]}; + return {entities(), uids()}; } Iterator end() const { - auto full = getFullStruct(); - uintptr_t entitiesEnd = full.entities + full.size * sizeof(uintptr_t); - uintptr_t uidsEnd = full.uids + full.size * sizeof(uint32_t); + uintptr_t entitiesEnd = entities() + size() * sizeof(uintptr_t); + uintptr_t uidsEnd = uids() + size() * sizeof(uint32_t); return {entitiesEnd, uidsEnd}; } const Iterator cbegin() const @@ -136,24 +135,25 @@ namespace S2Plugin } return endIterator; } - - private: - uintptr_t address; - struct TrueEntityList + std::vector getAllEntities() const { - uintptr_t entities{0}; - uintptr_t uids{0}; - uint32_t cap{0}; - uint32_t size{0}; - - private: - TrueEntityList() = delete; - // TrueEntityList(const TrueEntityList&) = delete; - // TrueEntityList& operator=(const TrueEntityList&) = delete; - }; - TrueEntityList getFullStruct() const + std::vector result; + result.resize(size()); + Script::Memory::Read(entities(), result.data(), size() * sizeof(uintptr_t), nullptr); + return result; + } + std::vector getAllUids() const { - return Read(address); + std::vector result; + result.resize(size()); + Script::Memory::Read(uids(), result.data(), size() * sizeof(uint32_t), nullptr); + return result; } + + private: + uintptr_t mEntities{0}; + uintptr_t mUids{0}; + uint32_t mCap{0}; + uint32_t mSize{0}; }; }; // namespace S2Plugin diff --git a/include/Views/ViewEntities.h b/include/Views/ViewEntities.h index 4b65c05..0dd7e45 100644 --- a/include/Views/ViewEntities.h +++ b/include/Views/ViewEntities.h @@ -74,8 +74,8 @@ namespace S2Plugin QLineEdit* mFilterLineEdit; - uintptr_t mLayer0Offset = 0; - uintptr_t mLayer1Offset = 0; + uintptr_t mLayer0Address = 0; + uintptr_t mLayer1Address = 0; uintptr_t mLayerMapOffset = 0; }; } // namespace S2Plugin diff --git a/src/QtHelpers/WidgetSpelunkyLevel.cpp b/src/QtHelpers/WidgetSpelunkyLevel.cpp index dbd5529..54f1ffb 100644 --- a/src/QtHelpers/WidgetSpelunkyLevel.cpp +++ b/src/QtHelpers/WidgetSpelunkyLevel.cpp @@ -1,6 +1,7 @@ #include "QtHelpers/WidgetSpelunkyLevel.h" #include "Configuration.h" +#include "Data/EntityList.h" #include "Data/StdMap.h" #include "Spelunky2.h" #include "pluginmain.h" @@ -154,6 +155,7 @@ void S2Plugin::WidgetSpelunkyLevel::updateLevel() constexpr auto dataSize = (msLevelMaxHeight + 1) * ((msLevelMaxWidth + 1) * sizeof(uintptr_t)); Script::Memory::Read(gridAddr, &mLevelFloors, dataSize, nullptr); } + for (auto& entity : mEntitiesToPaint) entity.pos = entity.ent.abs_position(); @@ -166,14 +168,9 @@ void S2Plugin::WidgetSpelunkyLevel::updateLevel() mEntitiesMaskCoordinates[bit_number].clear(); if ((mEntityMasksToPaint & key) != 0) { - // TODO: change to proper struct when done - auto pointers = Script::Memory::ReadQword(value_ptr); - auto list_count = Script::Memory::ReadDword(value_ptr + 20); - - mEntitiesMaskCoordinates[bit_number].reserve(list_count); - std::vector entities; - entities.resize(list_count); - Script::Memory::Read(pointers, entities.data(), list_count * sizeof(uintptr_t), nullptr); + EntityList entityList{value_ptr}; + mEntitiesMaskCoordinates[bit_number].reserve(entityList.size()); + std::vector entities = entityList.getAllEntities(); for (auto entityAddr : entities) { diff --git a/src/Views/ViewEntities.cpp b/src/Views/ViewEntities.cpp index 5e01e8b..3c29f9f 100644 --- a/src/Views/ViewEntities.cpp +++ b/src/Views/ViewEntities.cpp @@ -2,6 +2,7 @@ #include "Configuration.h" #include "Data/Entity.h" +#include "Data/Entitylist.h" #include "Data/StdMap.h" #include "QtHelpers/TreeViewMemoryFields.h" #include "QtPlugin.h" @@ -20,8 +21,8 @@ S2Plugin::ViewEntities::ViewEntities(QWidget* parent) : QWidget(parent) auto mainLayout = new QVBoxLayout(this); mainLayout->setMargin(5); auto config = Configuration::get(); - mLayer0Offset = config->offsetForField(MemoryFieldType::State, "layer0", Spelunky2::get()->get_StatePtr()); - mLayer1Offset = config->offsetForField(MemoryFieldType::State, "layer1", Spelunky2::get()->get_StatePtr()); + mLayer0Address = config->offsetForField(MemoryFieldType::State, "layer0", Spelunky2::get()->get_StatePtr()); + mLayer1Address = config->offsetForField(MemoryFieldType::State, "layer1", Spelunky2::get()->get_StatePtr()); mLayerMapOffset = config->offsetForField(config->typeFieldsOfDefaultStruct("LayerPointer"), "entities_by_mask"); // initializeRefreshAndFilter @@ -90,6 +91,7 @@ void S2Plugin::ViewEntities::refreshEntities() enteredUID = mFilterLineEdit->text().toUInt(&isUIDlookupSuccess, 0); } + size_t entitiesShown = 0; auto AddEntity = [&](size_t entity_ptr) { auto entity = Entity{Script::Memory::ReadQword(entity_ptr)}; @@ -105,16 +107,16 @@ void S2Plugin::ViewEntities::refreshEntities() field.name = "entity_uid_" + std::to_string(entity.uid()); field.type = MemoryFieldType::EntityPointer; field.isPointer = true; - mMainTreeView->addMemoryField(field, "", entity_ptr, 0); + mMainTreeView->addMemoryField(field, {}, entity_ptr, 0); + ++entitiesShown; }; - size_t totalEntities = 0; - auto layer0 = Script::Memory::ReadQword(mLayer0Offset); - auto layer0Count = Script::Memory::ReadDword(layer0 + 0x1C); - auto layer1 = Script::Memory::ReadQword(mLayer1Offset); - auto layer1Count = Script::Memory::ReadDword(layer1 + 0x1C); - mCheckboxLayer0->setText(QString("Front layer (%1)").arg(layer0Count)); - mCheckboxLayer1->setText(QString("Back layer (%1)").arg(layer1Count)); + auto layer0 = Script::Memory::ReadQword(mLayer0Address); + EntityList entListLayer0{layer0 + 0x8}; + auto layer1 = Script::Memory::ReadQword(mLayer1Address); + EntityList entListLayer1{layer1 + 0x8}; + mCheckboxLayer0->setText(QString("Front layer (%1)").arg(entListLayer0.size())); + mCheckboxLayer1->setText(QString("Back layer (%1)").arg(entListLayer1.size())); auto check_layer0 = mCheckboxLayer0->checkState() == Qt::Checked; auto check_layer1 = mCheckboxLayer1->checkState() == Qt::Checked; @@ -122,30 +124,26 @@ void S2Plugin::ViewEntities::refreshEntities() if (isUIDlookupSuccess) { // loop thru all entities to find the uid - // TODO: change to proper struct when done - auto ent_list = Script::Memory::ReadQword(layer0 + 0x8); - auto uid_list = Script::Memory::ReadQword(layer0 + 0x10); + auto uidList0 = entListLayer0.getAllUids(); bool found_uid = false; - for (uint idx = 0; idx < layer0Count; ++idx) + for (uint idx = 0; idx < entListLayer0.size(); ++idx) { - auto uid = Script::Memory::ReadDword(uid_list + idx * sizeof(uint32_t)); - if (enteredUID == uid) + if (enteredUID == uidList0[idx]) { - AddEntity(ent_list + idx * sizeof(size_t)); + AddEntity(entListLayer0.entities() + idx * sizeof(size_t)); found_uid = true; break; } } - ent_list = Script::Memory::ReadQword(layer1 + 0x8); - uid_list = Script::Memory::ReadQword(layer1 + 0x10); + if (found_uid == false) { - for (uint idx = 0; idx < layer1Count; ++idx) + auto uidList1 = entListLayer1.getAllUids(); + for (uint idx = 0; idx < entListLayer1.size(); ++idx) { - auto uid = Script::Memory::ReadDword(uid_list + idx * sizeof(uint32_t)); - if (enteredUID == uid) + if (enteredUID == uidList1[idx]) { - AddEntity(ent_list + idx * sizeof(size_t)); + AddEntity(entListLayer1.entities() + idx * sizeof(size_t)); break; } } @@ -164,19 +162,14 @@ void S2Plugin::ViewEntities::refreshEntities() auto itr = map0.find(checkbox.mask); if (itr != map0.end()) { - // TODO: change to proper struct when done - auto ent_list = itr.value_ptr(); - auto pointers = Script::Memory::ReadQword(ent_list); - auto list_count = Script::Memory::ReadDword(ent_list + 20); - field_count += list_count; + EntityList maskEntList{itr.value_ptr()}; + + field_count += maskEntList.size(); // loop only if uid was not entered and the mask was choosen - if (!isUIDlookupSuccess && totalEntities < 10000u && checkbox.mCheckbox->checkState() == Qt::Checked) + if (!isUIDlookupSuccess && checkbox.mCheckbox->checkState() == Qt::Checked) { - for (size_t i = 0; i < list_count; ++i) - { - AddEntity(pointers + (i * sizeof(uintptr_t))); - ++totalEntities; - } + for (size_t i = 0; i < maskEntList.size(); ++i) + AddEntity(maskEntList.entities() + i * sizeof(uintptr_t)); } } } @@ -185,23 +178,18 @@ void S2Plugin::ViewEntities::refreshEntities() auto itr = map1.find(checkbox.mask); if (itr != map1.end()) { - auto ent_list = itr.value_ptr(); - auto pointers = Script::Memory::ReadQword(ent_list); - auto list_count = Script::Memory::ReadDword(ent_list + 20); - field_count += list_count; - if (!isUIDlookupSuccess && totalEntities < 10000u && checkbox.mCheckbox->checkState() == Qt::Checked) + EntityList maskEntList{itr.value_ptr()}; + field_count += maskEntList.size(); + if (!isUIDlookupSuccess && checkbox.mCheckbox->checkState() == Qt::Checked) { - for (size_t i = 0; i < list_count; ++i) - { - AddEntity(pointers + (i * sizeof(size_t))); - ++totalEntities; - } + for (size_t i = 0; i < maskEntList.size(); ++i) + AddEntity(maskEntList.entities() + i * sizeof(uintptr_t)); } } } checkbox.mCheckbox->setText(QString(checkbox.name + " (%1)").arg(field_count)); } - setWindowTitle(QString("%1 Entities").arg(totalEntities)); + setWindowTitle(QString("%1 Entities").arg(entitiesShown)); mMainTreeView->updateTableHeader(); mMainTreeView->setColumnWidth(gsColField, 145); mMainTreeView->setColumnWidth(gsColValueHex, 125); diff --git a/src/Views/ViewEntityList.cpp b/src/Views/ViewEntityList.cpp index f90b722..29d4bd3 100644 --- a/src/Views/ViewEntityList.cpp +++ b/src/Views/ViewEntityList.cpp @@ -40,13 +40,16 @@ void S2Plugin::ViewEntityList::refreshEntityListContents() EntityList entityList{mEntityListAddress}; - for (auto entity : entityList) + // TODO using custom view instead of memory view would improve performance + auto entities = entityList.entities(); + auto uids = entityList.getAllUids(); + for (size_t idx = 0; idx < entityList.size(); ++idx) { MemoryField entityField; - entityField.name = "uid_" + std::to_string(entity.second); + entityField.name = "uid_" + std::to_string(uids[idx]); entityField.isPointer = true; entityField.type = MemoryFieldType::EntityPointer; - mMainTreeView->addMemoryField(entityField, {}, entity.first, 0); + mMainTreeView->addMemoryField(entityField, {}, entities + idx * sizeof(uintptr_t), 0); } mMainTreeView->updateTableHeader();