2022-07-30 12:24:03 -04:00
|
|
|
#pragma once
|
2022-10-06 13:46:07 -04:00
|
|
|
|
2023-10-15 12:20:47 -04:00
|
|
|
#include "../utils/MiniFunction.hpp"
|
2022-07-30 12:24:03 -04:00
|
|
|
#include "Traits.hpp"
|
2022-12-24 13:13:53 -05:00
|
|
|
|
2022-10-08 05:59:06 -04:00
|
|
|
#include <Geode/loader/Loader.hpp>
|
2022-12-24 13:13:53 -05:00
|
|
|
#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
|
|
|
}
|
|
|
|
|
2022-12-24 13:13:53 -05: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;
|
2023-02-08 10:25:07 -05:00
|
|
|
std::vector<utils::MiniFunction<void(void*)>> m_destructorFunctions;
|
2022-10-30 14:59:20 -04:00
|
|
|
|
|
|
|
public:
|
|
|
|
~FieldContainer() {
|
|
|
|
for (auto i = 0u; i < m_containedFields.size(); i++) {
|
2024-05-15 16:59:42 -04:00
|
|
|
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) {
|
2023-03-01 17:02:09 -05:00
|
|
|
m_containedFields.push_back(nullptr);
|
|
|
|
m_destructorFunctions.push_back(nullptr);
|
2022-10-30 14:59:20 -04:00
|
|
|
}
|
|
|
|
return m_containedFields.at(index);
|
|
|
|
}
|
|
|
|
|
2023-02-08 10:25:07 -05:00
|
|
|
void* setField(size_t index, size_t size, utils::MiniFunction<void(void*)> destructor) {
|
2022-10-30 14:59:20 -04:00
|
|
|
m_containedFields.at(index) = operator new(size);
|
|
|
|
m_destructorFunctions.at(index) = destructor;
|
|
|
|
return m_containedFields.at(index);
|
|
|
|
}
|
|
|
|
|
2024-04-12 15:56:05 -04:00
|
|
|
static FieldContainer* from(cocos2d::CCNode* node, char const* forClass) {
|
|
|
|
return node->getFieldContainer(forClass);
|
2022-10-30 14:59:20 -04:00
|
|
|
}
|
|
|
|
};
|
2022-12-24 13:13:53 -05:00
|
|
|
|
2023-03-01 17:02:09 -05: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 {
|
2022-12-24 13:13:53 -05:00
|
|
|
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)
|
2023-10-15 12:20:47 -04:00
|
|
|
alignas(std::max(alignof(Base), alignof(uintptr_t))) uintptr_t m_padding;
|
2022-10-30 14:59:20 -04:00
|
|
|
|
|
|
|
public:
|
2022-12-24 13:13:53 -05:00
|
|
|
// the constructor that constructs the fields.
|
|
|
|
// we construct the Parent first,
|
2024-04-22 08:34:57 -04:00
|
|
|
static void fieldConstructor(void* offsetField) {
|
2024-06-20 04:48:15 -04:00
|
|
|
(void) new (offsetField) typename Parent::Fields();
|
2022-10-30 14:59:20 -04:00
|
|
|
}
|
|
|
|
|
2024-04-22 08:34:57 -04:00
|
|
|
static void fieldDestructor(void* offsetField) {
|
2024-06-20 04:48:15 -04:00
|
|
|
static_cast<typename Parent::Fields*>(offsetField)->~Fields();
|
2024-04-21 17:09:49 -04:00
|
|
|
}
|
|
|
|
|
2024-04-22 08:34:57 -04:00
|
|
|
auto self() {
|
2024-06-20 04:48:15 -04:00
|
|
|
// 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));
|
|
|
|
static_assert(sizeof(Base) == offsetof(Parent, m_fields), "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
|
|
|
|
);
|
2024-04-22 08:34:57 -04:00
|
|
|
|
2024-06-20 04:48:15 -04:00
|
|
|
FieldIntermediate::fieldConstructor(offsetField);
|
2024-04-21 17:09:49 -04:00
|
|
|
}
|
2024-06-20 04:48:15 -04:00
|
|
|
|
|
|
|
return reinterpret_cast<typename Parent::Fields*>(offsetField);
|
2024-04-21 17:09:49 -04:00
|
|
|
}
|
|
|
|
|
2024-04-22 08:34:57 -04:00
|
|
|
auto operator->() {
|
2024-04-21 17:09:49 -04:00
|
|
|
// workaround for "static assertion is not an integral constant expression" in CLion
|
|
|
|
// while the solution in https://github.com/microsoft/STL/issues/3311 works, you can't provide
|
|
|
|
// cli args to clang-tidy in clion, so we use this workaround instead
|
|
|
|
// https://youtrack.jetbrains.com/issue/CPP-27446/spurious-offsetof-in-staticassert-error-from-clangd#focus=Comments-27-8172811.0-0
|
|
|
|
// update: that workaround didn't work,
|
|
|
|
// undefining and re-defining offsetof caused another error further down
|
|
|
|
// so we're doing this now
|
|
|
|
#ifdef __CLION_IDE__
|
2024-04-21 17:22:40 -04:00
|
|
|
return reinterpret_cast<typename Parent::Fields*>(69420);
|
2024-04-21 17:09:49 -04:00
|
|
|
#else
|
|
|
|
return this->self();
|
2024-01-13 14:12:20 -05:00
|
|
|
#endif
|
2023-03-01 17:02:09 -05:00
|
|
|
}
|
2022-10-30 14:59:20 -04:00
|
|
|
};
|
2022-07-30 12:24:03 -04:00
|
|
|
|
|
|
|
}
|