geode/loader/include/Geode/modify/Field.hpp

107 lines
3.6 KiB
C++
Raw Normal View History

2022-07-30 12:24:03 -04:00
#pragma once
2022-07-30 12:24:03 -04:00
#include "Traits.hpp"
2022-10-08 05:59:06 -04:00
#include <Geode/loader/Loader.hpp>
#include <cocos2d.h>
2022-07-30 12:24:03 -04:00
#include <vector>
2022-10-13 05:56:23 -04:00
namespace cocos2d {
2022-10-30 14:59:20 -04:00
class CCNode;
2022-10-13 05:56:23 -04:00
}
namespace geode {
template <class, class>
class Modify;
}
2022-07-30 12:24:03 -04:00
namespace geode::modifier {
2022-10-30 14:59:20 -04:00
class FieldContainer {
private:
std::vector<void*> m_containedFields;
2024-11-04 12:42:09 -05:00
std::vector<std::function<void(void*)>> m_destructorFunctions;
2022-10-30 14:59:20 -04:00
public:
~FieldContainer() {
for (auto i = 0u; i < m_containedFields.size(); i++) {
if (m_destructorFunctions[i] && m_containedFields[i]) {
m_destructorFunctions[i](m_containedFields[i]);
operator delete(m_containedFields[i]);
}
2022-10-30 14:59:20 -04:00
}
}
void* getField(size_t index) {
2023-07-30 15:21:17 -04:00
while (m_containedFields.size() <= index) {
m_containedFields.push_back(nullptr);
m_destructorFunctions.push_back(nullptr);
2022-10-30 14:59:20 -04:00
}
return m_containedFields.at(index);
}
2024-11-04 12:42:09 -05:00
void* setField(size_t index, size_t size, std::function<void(void*)> destructor) {
2022-10-30 14:59:20 -04:00
m_containedFields.at(index) = operator new(size);
2024-11-04 12:42:09 -05:00
m_destructorFunctions.at(index) = std::move(destructor);
2022-10-30 14:59:20 -04:00
return m_containedFields.at(index);
}
static FieldContainer* from(cocos2d::CCNode* node, char const* forClass) {
return node->getFieldContainer(forClass);
2022-10-30 14:59:20 -04:00
}
};
GEODE_DLL size_t getFieldIndexForClass(char const* name);
2022-10-30 14:59:20 -04:00
2022-11-09 12:11:50 -05:00
template <class Parent, class Base>
2022-10-30 14:59:20 -04:00
class FieldIntermediate {
using Intermediate = Modify<Parent, Base>;
2022-10-30 14:59:20 -04:00
// Padding used for guaranteeing any member of parents
// will be in between sizeof(Intermediate) and sizeof(Parent)
2024-06-20 13:06:00 -04:00
std::aligned_storage_t<std::alignment_of_v<Base>, std::alignment_of_v<Base>> m_padding;
2022-10-30 14:59:20 -04:00
public:
// the constructor that constructs the fields.
// we construct the Parent first,
static void fieldConstructor(void* offsetField) {
(void) new (offsetField) typename Parent::Fields();
2022-10-30 14:59:20 -04:00
}
static void fieldDestructor(void* offsetField) {
static_cast<typename Parent::Fields*>(offsetField)->~Fields();
}
auto self() {
// get the this pointer of the base
// field intermediate is the first member of Modify
// meaning we can get the base from ourself
auto node = reinterpret_cast<Parent*>(reinterpret_cast<std::byte*>(this) - sizeof(Base));
2024-06-20 07:16:37 -04:00
// static_assert(sizeof(Base) + sizeof() == sizeof(Intermediate), "offsetof not correct");
// generating the container if it doesn't exist
auto container = FieldContainer::from(node, typeid(Base).name());
// the index is global across all mods, so the
// function is defined in the loader source
static size_t index = getFieldIndexForClass(typeid(Base).name());
// the fields are actually offset from their original
// offset, this is done to save on allocation and space
auto offsetField = container->getField(index);
if (!offsetField) {
offsetField = container->setField(
index, sizeof(typename Parent::Fields), &FieldIntermediate::fieldDestructor
);
FieldIntermediate::fieldConstructor(offsetField);
}
return reinterpret_cast<typename Parent::Fields*>(offsetField);
}
auto operator->() {
return this->self();
}
2022-10-30 14:59:20 -04:00
};
2022-07-30 12:24:03 -04:00
}