Skip to content

Commit

Permalink
implement RobinHoodTableEntry lookup for finding the uid
Browse files Browse the repository at this point in the history
  • Loading branch information
Mr-Auto committed Mar 4, 2024
1 parent b40dbd1 commit 47efa65
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 160 deletions.
1 change: 1 addition & 0 deletions include/Configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ namespace S2Plugin
constexpr uint16_t gsRoleStdContainerFirstParameterType = Qt::UserRole + 8;
constexpr uint16_t gsRoleStdContainerSecondParameterType = Qt::UserRole + 9;
constexpr uint16_t gsRoleSize = Qt::UserRole + 10;
constexpr uint16_t gsRoleEntityOffset = Qt::UserRole + 11; // for entity uid to not look for the uid twice

constexpr char* gsJSONDragDropMemoryField_UID = "uid";
constexpr char* gsJSONDragDropMemoryField_Offset = "offset";
Expand Down
22 changes: 8 additions & 14 deletions include/Data/State.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,17 @@

namespace S2Plugin
{
class Statex
struct State
{
public:
// bool loadState();
State(uintptr_t addr) : mStatePtr(addr){};

////uint32_t heapOffset();
////uint32_t TEBOffset() const;

// std::unordered_map<std::string, size_t>& offsets();
// void refreshOffsets();
// size_t offsetForField(const std::string& fieldName) const;

/*size_t findNextEntity(size_t entityOffset);*/
uintptr_t ptr() const
{
return mStatePtr;
}
uintptr_t findEntitybyUID(uint32_t uid) const;

private:
size_t mStatePtr = 0;
uint32_t mHeapOffset = 0;
// std::unordered_map<std::string, size_t> mMemoryOffsets; // fieldname -> offset of field value in memory
uintptr_t mStatePtr = 0;
};
} // namespace S2Plugin
159 changes: 56 additions & 103 deletions src/Data/State.cpp
Original file line number Diff line number Diff line change
@@ -1,111 +1,64 @@
#include "Data/State.h"
#include "Configuration.h"
#include "Spelunky2.h"
#include "pluginmain.h"

// bool S2Plugin::State::loadState()
//{
// auto spel2 = Spelunky2::get();
// auto afterBundle = spel2->afterBundle;
// auto afterBundleSize = spel2->afterBundleSize;
// if (afterBundle == 0)
// {
// return false;
// }
// if (mStatePtr != 0)
// {
// return true;
// }
//
// auto instructionOffset = Script::Pattern::FindMem(afterBundle, afterBundleSize, "49 0F 44 C0");
// instructionOffset = Script::Pattern::FindMem(instructionOffset + 1, afterBundleSize, "49 0F 44 C0");
// instructionOffset = Script::Pattern::FindMem(instructionOffset - 25, afterBundleSize, "48 8B");
// auto pcOffset = Script::Memory::ReadDword(instructionOffset + 3);
// auto heapOffsetPtr = instructionOffset + pcOffset + 7;
// mHeapOffset = Script::Memory::ReadDword(heapOffsetPtr); // 4A0
//
// THREADLIST threadList;
// DbgGetThreadList(&threadList);
// for (auto x = 0; x < threadList.count; ++x)
// {
// auto threadAllInfo = threadList.list[x];
// if (threadAllInfo.BasicInfo.ThreadNumber == 0)
// {
// auto tebAddress = DbgGetTebAddress(threadAllInfo.BasicInfo.ThreadId);
// auto tebAddress11Ptr = Script::Memory::ReadQword(tebAddress + (11 * sizeof(size_t)));
// auto tebAddress11Value = Script::Memory::ReadQword(tebAddress11Ptr);
// auto heapBase = Script::Memory::ReadQword(tebAddress11Value + 0x120);
// mStatePtr = heapBase + mHeapOffset;
// break;
// }
// }
// return true;
// }
static uint32_t lowbias32(uint32_t x)
{
x ^= x >> 16;
x *= 0x7feb352d;
x ^= x >> 15;
x *= 0x846ca68b;
x ^= x >> 16;
return x;
}

// uint32_t S2Plugin::State::heapOffset()
//{
// loadState();
// return mHeapOffset;
// }
//
// uint32_t S2Plugin::State::TEBOffset() const
// Just for refrence
// struct RobinHoodTableEntry
//{
// return 0x120;
// }
// uint32_t uid_plus_one;
// uint32_t padding;
// Entity* entity;
//};

// void S2Plugin::State::refreshOffsets()
//{
// mMemoryOffsets.clear();
// auto offset = mStatePtr;
// auto config = Configuration::get();
// for (const auto& field : config->typeFields(MemoryFieldType::State))
// {
// offset = config->setOffsetForField(field, "State." + field.name, offset, mMemoryOffsets);
// }
// }
//
// size_t S2Plugin::State::offsetForField(const std::string& fieldName) const
//{
// auto full = "State." + fieldName;
// auto r = mMemoryOffsets.find(full);
// if (r == mMemoryOffsets.end())
// {
// return 0;
// }
// return r->second;
// }
uintptr_t S2Plugin::State::findEntitybyUID(uint32_t uid) const
{
// ported from overlunky
if (uid == ~0)
{
return 0;
}

// size_t S2Plugin::State::findNextEntity(size_t entityOffset)
//{
// size_t nextOffset = (std::numeric_limits<size_t>::max)();
//
// auto loopEntities = [&nextOffset, entityOffset](size_t entities, uint32_t entityCount)
// {
// for (auto x = 0; x < (std::min)(10000u, entityCount); ++x)
// {
// auto entityPtr = Script::Memory::ReadQword(entities + (x * sizeof(size_t)));
// if (entityPtr <= entityOffset)
// {
// continue;
// }
// if (entityPtr < nextOffset)
// {
// nextOffset = entityPtr;
// }
// }
// };
//
// // auto layer0Entities = Script::Memory::ReadQword(offsetForField("layer0.first_entity*"));
// // auto layer0EntityCount = Script::Memory::ReadDword(offsetForField("layer0.size"));
// // loopEntities(layer0Entities, layer0EntityCount);
//
// // auto layer1Entities = Script::Memory::ReadQword(offsetForField("layer1.first_entity*"));
// // auto layer1EntityCount = Script::Memory::ReadDword(offsetForField("layer1.size"));
// // loopEntities(layer1Entities, layer1EntityCount);
//
// if (nextOffset == (std::numeric_limits<size_t>::max)())
// {
// return 0;
// }
// return nextOffset;
// }
static size_t mask_offset = Configuration::get()->offsetForField(MemoryFieldType::State, "uid_to_entity_mask");
const uint32_t mask = Script::Memory::ReadDword(mStatePtr + mask_offset);
const uint32_t target_uid_plus_one = lowbias32(uid + 1u);
uint32_t cur_index = target_uid_plus_one & mask;
const uintptr_t uid_to_entity_data = Script::Memory::ReadQword(mStatePtr + mask_offset + 0x8u);

auto getEntry = [uid_to_entity_data](size_t index)
{
constexpr size_t robinHoodTableEntrySize = 0x10u;
return uid_to_entity_data + index * robinHoodTableEntrySize;
};

while (true)
{
auto entry = getEntry(cur_index);
auto uid_plus_one = Script::Memory::ReadDword(entry);
if (uid_plus_one == target_uid_plus_one)
{
return Script::Memory::ReadQword(entry + 0x8u);
}

if (uid_plus_one == 0)
{
return 0;
}

if (((cur_index - target_uid_plus_one) & mask) > ((cur_index - uid_plus_one) & mask))
{
return 0;
}

cur_index = (cur_index + 1u) & mask;
}
}
63 changes: 20 additions & 43 deletions src/QtHelpers/TreeViewMemoryFields.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "Data/Entity.h"
#include "Data/EntityDB.h"
#include "Data/ParticleDB.h"
#include "Data/State.h"
#include "Data/StdString.h"
#include "Data/StringsTable.h"
#include "Data/TextureDB.h"
Expand Down Expand Up @@ -1324,19 +1325,22 @@ void S2Plugin::TreeViewMemoryFields::updateRow(int row, std::optional<uintptr_t>
{
if (value.value() < 0)
{
itemValue->setData("Nothing", Qt::DisplayRole); // TODO: maybe display the uid value as well?
itemValue->setData(QString::asprintf("(%d) Nothing", value.value()), Qt::DisplayRole);
itemValue->setData(0, gsRoleEntityOffset);
}
else
{
uintptr_t entityOffset = 0 /*Entity::findEntityByUID(value.value(), mToolbar->state())*/; // TODO
uintptr_t entityOffset = S2Plugin::State{Spelunky2::get()->get_StatePtr()}.findEntitybyUID(value.value());
if (entityOffset != 0)
{
auto entityName = Configuration::get()->getEntityName(Entity{entityOffset}.entityTypeID());
itemValue->setData(QString::asprintf("<font color='blue'><u>UID %u (%s)</u></font>", value.value(), entityName.c_str()), Qt::DisplayRole);
itemValue->setData(entityOffset, gsRoleEntityOffset);
}
else
{
itemValue->setData("UNKNOWN ENTITY", Qt::DisplayRole); // TODO: display the uid anyway
itemValue->setData(QString::asprintf("(%d) UNKNOWN ENTITY", value.value()), Qt::DisplayRole);
itemValue->setData(0, gsRoleEntityOffset);
}
}
}
Expand All @@ -1349,21 +1353,22 @@ void S2Plugin::TreeViewMemoryFields::updateRow(int row, std::optional<uintptr_t>
{
if (comparisonValue.value() < 0)
{
itemComparisonValue->setData("Nothing", Qt::DisplayRole);
itemComparisonValue->setData(QVariant{}, gsRoleRawValue);
itemComparisonValue->setData(QString::asprintf("(%d) Nothing", comparisonValue.value()), Qt::DisplayRole);
itemComparisonValue->setData(0, gsRoleEntityOffset);
}
else
{
uintptr_t comparisonEntityOffset = 0 /*Entity::findEntityByUID(comparisonValue.value(), mToolbar->state())*/; // TODO
uintptr_t comparisonEntityOffset = S2Plugin::State{Spelunky2::get()->get_StatePtr()}.findEntitybyUID(comparisonValue.value());
if (comparisonEntityOffset != 0)
{
auto entityName = Configuration::get()->getEntityName(Entity{comparisonEntityOffset}.entityTypeID());
itemComparisonValue->setData(QString::asprintf("<font color='blue'><u>UID %u (%s)</u></font>", comparisonValue.value(), entityName.c_str()), Qt::DisplayRole);
itemComparisonValue->setData(comparisonEntityOffset, gsRoleEntityOffset);
}
else
{
itemComparisonValue->setData("UNKNOWN ENTITY", Qt::DisplayRole); // TODO: display the uid anyway
itemComparisonValue->setData(QVariant{}, gsRoleRawValue);
itemComparisonValue->setData(QString::asprintf("(%d) UNKNOWN ENTITY", comparisonValue.value()), Qt::DisplayRole);
itemComparisonValue->setData(0, gsRoleEntityOffset);
}
}
}
Expand Down Expand Up @@ -2031,8 +2036,6 @@ void S2Plugin::TreeViewMemoryFields::cellClicked(const QModelIndex& index)
GuiShowCpu();
break;
}
// case MemoryFieldType::ConstCharPointerPointer:
// case MemoryFieldType::ConstCharPointer:
case MemoryFieldType::DataPointer:
{
auto rawValue = clickedItem->data(gsRoleMemoryOffset).toULongLong();
Expand All @@ -2043,28 +2046,6 @@ void S2Plugin::TreeViewMemoryFields::cellClicked(const QModelIndex& index)
}
break;
}
// case MemoryFieldType::UndeterminedThemeInfoPointer:
// case MemoryFieldType::ThemeInfoName:
//{
// if (rawValue != 0)
// {
// GuiDumpAt(rawValue);
// GuiShowCpu();
// }
// break;
// }
/*case MemoryFieldType::DefaultStructType:
{
if (getDataFrom(index, gsColField, gsRoleIsPointer).toBool())
{
if (rawValue != 0)
{
GuiDumpAt(rawValue);
GuiShowCpu();
}
}
break;
}*/
case MemoryFieldType::EntityPointer:
{
auto rawValue = clickedItem->data(gsRoleRawValue).toULongLong(); // TODO check if valid ptr here or in update
Expand All @@ -2077,15 +2058,10 @@ void S2Plugin::TreeViewMemoryFields::cellClicked(const QModelIndex& index)
case MemoryFieldType::EntityUID:
case MemoryFieldType::EntityUIDPointer:
{
auto uid = clickedItem->data(gsRoleRawValue);
if (!uid.isNull())
auto offset = clickedItem->data(gsRoleEntityOffset).toULongLong();
if (offset != 0)
{
// TODO save offset on update to
auto offset = 0 /*Entity::findEntityByUID(uid.toUInt(), mToolbar->state())*/;
if (offset != 0)
{
mToolbar->showEntity(offset);
}
mToolbar->showEntity(offset);
}
break;
}
Expand Down Expand Up @@ -2218,7 +2194,7 @@ void S2Plugin::TreeViewMemoryFields::cellClicked(const QModelIndex& index)
auto rawValue = clickedItem->data(gsRoleMemoryOffset).toULongLong();
if (rawValue != 0)
{
auto id = Script::Memory::ReadDword(rawValue); // use pointer
auto id = Script::Memory::ReadDword(rawValue); // TODO: use pointer
auto view = mToolbar->showParticleDB();
if (view != nullptr)
{
Expand All @@ -2232,11 +2208,12 @@ void S2Plugin::TreeViewMemoryFields::cellClicked(const QModelIndex& index)
auto rawValue = clickedItem->data(gsRoleMemoryOffset).toULongLong();
if (rawValue != 0)
{
// TODO: maybe use the interpret as vtable? (it's doable)
// TODO: maybe use the "entity interpret as" for vtable? (it's doable)
auto vftType = qvariant_cast<std::string>(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
{
auto ent = Entity{getDataFrom(index, gsColMemoryOffset, gsRoleRawValue).toULongLong()}; // rare case, we need the address not the pointer to get entity
// rare case, we need the address not the pointer value to get entity
auto ent = Entity{getDataFrom(index, gsColMemoryOffset, gsRoleRawValue).toULongLong()};
mToolbar->showVirtualFunctions(rawValue, ent.entityClassName());
}
else
Expand Down

0 comments on commit 47efa65

Please sign in to comment.