#pragma once #include "playerslot.h" #include #include #include #include struct AppliedOverride { uint8_t* address {}; std::vector original; }; enum class OverrideRuleKind { Bytes, VectorFirstFromField, }; struct FieldAddressStep { int32_t offset {}; bool dereference {}; }; struct CompiledOverrideRule { int id {}; int recipientSlot {-1}; int entityIndex {-1}; std::string className; std::string fieldPath; std::string sourceFieldPath; int32_t offset {}; int32_t size {}; int32_t sourceSize {}; std::vector addressSteps; std::vector sourceAddressSteps; std::vector value; OverrideRuleKind kind {OverrideRuleKind::Bytes}; bool enabled {true}; }; class OverrideManager { public: void Clear(); void Compact(); int AddRuleBytes(int recipientSlot, int entityIndex, const char* className, const char* fieldPath, const void* value, int valueSize, std::string& error); int AddRuleVectorFirstFromField(int recipientSlot, int entityIndex, const char* className, const char* vectorFieldPath, const char* sourceFieldPath, std::string& error); bool RemoveRule(int id); bool MarkEntityFieldDirty(int entityIndex, const char* className, const char* fieldPath, std::string& error); bool HasPackedOverrides() const { return !m_rulesByEntity.empty(); } std::vector ApplyForPackedEntity(CPlayerSlot recipient, int entityIndex, void* entityData); void Restore(const std::vector& applied); private: bool ResolveFieldPath(const char* className, const char* fieldPath, int32_t& offset, int32_t& size, std::vector& addressSteps, std::string& error) const; void RebuildIndex(); bool MarkEntityFieldDirtyByOffset(int entityIndex, int32_t offset); bool MarkEntityFullDirty(int entityIndex); std::vector m_rules; std::unordered_map> m_rulesByEntity; int m_nextId {1}; }; extern OverrideManager g_overrides;