Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions bindings/2.2074/Cocos2d.bro
Original file line number Diff line number Diff line change
Expand Up @@ -1948,6 +1948,7 @@ class cocos2d::CCFadeTo : cocos2d::CCActionInterval {
class cocos2d::CCFileUtils : cocos2d::TypeInfo {
// CCFileUtils(); // one of these two is 0x15bfa8
// CCFileUtils(cocos2d::CCFileUtils const&);
~CCFileUtils();
virtual void addSearchPath(char const*) = m1 0x3a42d4, imac 0x4294e0, ios 0x155a30;
virtual void addSearchResolutionsOrder(char const*) = imac 0x428f30, m1 0x3a3d24, ios 0x155538;
virtual gd::string addSuffix(gd::string, gd::string) = imac 0x4273f0, m1 0x3a21f4, ios 0x15459c;
Expand Down Expand Up @@ -2224,6 +2225,7 @@ class cocos2d::CCScheduler : cocos2d::CCObject {

// CCScheduler(cocos2d::CCScheduler const&);
// CCScheduler();
~CCScheduler();

void appendIn(cocos2d::_listEntry**, cocos2d::CCObject*, bool);
bool isTargetPaused(cocos2d::CCObject*);
Expand Down
43 changes: 27 additions & 16 deletions codegen/src/BindingGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,21 +93,32 @@ std::string generateAddressDocs(T const& f, PlatformNumber pn) {
for (auto platform : {Platform::MacArm, Platform::MacIntel, Platform::Windows, Platform::iOS, Platform::Android}) {
auto status = codegen::getStatusWithPlatform(platform, f);

if (status == BindStatus::NeedsBinding) {
ret += fmt::format(" * @note[short] {}: 0x{:x}\n",
nameForPlatform(platform),
codegen::platformNumberWithPlatform(platform, pn)
);
}
else if (status == BindStatus::Binded) {
ret += fmt::format(" * @note[short] {}\n",
nameForPlatform(platform)
);
}
else if (status == BindStatus::Inlined) {
ret += fmt::format(" * @note[short] {}: Out of line\n",
nameForPlatform(platform)
);
auto num = codegen::platformNumberWithPlatform(platform, pn);

switch (status) {
case BindStatus::NeedsBinding:
ret += fmt::format(" * @note[short] {}: 0x{:x}\n",
nameForPlatform(platform),
num
);
break;
case BindStatus::NeedsRebinding:
ret += fmt::format(" * @note[short] {}: Rebinded\n",
nameForPlatform(platform)
);
break;
case BindStatus::Binded:
ret += fmt::format(" * @note[short] {}\n",
nameForPlatform(platform)
);
break;
case BindStatus::Inlined:
ret += fmt::format(" * @note[short] {}: Out of line\n",
nameForPlatform(platform)
);
break;
default:
break;
}
}

Expand Down Expand Up @@ -267,7 +278,7 @@ std::string generateBindingHeader(Root const& root, std::filesystem::path const&

fb = &fn->prototype;

if (codegen::platformNumber(fn->binds) == -1 && codegen::getStatus(*fn) != BindStatus::Binded && fb->type != FunctionType::Normal) {
if (codegen::platformNumber(fn->binds) == -1 && codegen::getStatus(*fn) == BindStatus::NeedsBinding) {
continue;
}

Expand Down
2 changes: 1 addition & 1 deletion codegen/src/JsonInterfaceGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ std::string generateTextInterface(Root const& root) {
// skip unbinded functions
continue;
}
else if (status != BindStatus::NeedsBinding && !codegen::shouldAndroidBind(fn)) {
else if (status != BindStatus::NeedsBinding) {
continue;
}
output += fmt::format("{}::{} - {:#x}\n", c.name, fn->prototype.name, codegen::platformNumber(fn->binds));
Expand Down
2 changes: 1 addition & 1 deletion codegen/src/ModifyGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ std::string generateModifyHeader(Root const& root, std::filesystem::path const&
else if (status == BindStatus::Inlined && fn->prototype.type == FunctionType::Normal || codegen::platformNumber(fn->binds) == 0x9999999) {
format_string = format_strings::apply_error_inline;
}
else if ((status == BindStatus::NeedsBinding && codegen::platformNumber(f) > 0) || status == BindStatus::Binded) {
else if ((status == BindStatus::NeedsBinding && codegen::platformNumber(f) > 0) || status == BindStatus::Binded || status == BindStatus::NeedsRebinding) {
// only if has an address
// allow bound functions (including ctors/dtors)
switch (fn->prototype.type) {
Expand Down
97 changes: 58 additions & 39 deletions codegen/src/Shared.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,12 @@ inline bool is_cocos_class(std::string const& str) {
return can_find(str, "cocos2d") || can_find(str, "pugi::") || str == "DS_Dictionary" || str == "ObjectDecoder" || str == "ObjectDecoderDelegate" || str == "CCContentManager";
}

inline bool is_in_extensions_dll(std::string const& str) {
return can_find(str, "cocos2d::extension");
}

inline bool is_in_cocos_dll(std::string const& str) {
return is_cocos_class(str) && !can_find(str, "CCLightning");
return is_cocos_class(str) && !can_find(str, "CCLightning") && !is_in_extensions_dll(str);
}

inline bool is_fmod_class(std::string const& str) {
Expand All @@ -79,6 +83,7 @@ enum class BindStatus {
NeedsBinding,
Unbindable,
Missing,
NeedsRebinding,
};

struct codegen_error : std::runtime_error {
Expand Down Expand Up @@ -207,7 +212,17 @@ namespace codegen {
if (platformNumberWithPlatform(p, fn.binds) == -2) return BindStatus::Inlined;

if ((fn.prototype.attributes.missing & p) != Platform::None || codegen::sdkVersion < fn.prototype.attributes.since) return BindStatus::Missing;
if ((fn.prototype.attributes.links & p) != Platform::None) return BindStatus::Binded;
if ((fn.prototype.attributes.links & p) != Platform::None) {
if (fn.prototype.type != FunctionType::Normal) return BindStatus::NeedsRebinding;

if ((int)p & (int)Platform::Android) {
for (auto& [type, name] : fn.prototype.args) {
if (can_find(type.name, "gd::")) return BindStatus::NeedsRebinding;
}
}

return BindStatus::Binded;
}

if (platformNumberWithPlatform(p, fn.binds) != -1) return BindStatus::NeedsBinding;

Expand All @@ -225,17 +240,6 @@ namespace codegen {
return BindStatus::Unbindable;
}

inline bool shouldAndroidBind(const FunctionBindField* fn) {
if (codegen::platform == Platform::Android32 || codegen::platform == Platform::Android64) {
if (sdkVersion < fn->prototype.attributes.since) return false;
if (fn->prototype.type != FunctionType::Normal) return true;
for (auto& [type, name] : fn->prototype.args) {
if (can_find(type.name, "gd::")) return true;
}
}
return false;
}

inline BindStatus getStatus(FunctionBindField const& fn) {
return getStatusWithPlatform(codegen::platform, fn);
}
Expand Down Expand Up @@ -285,14 +289,14 @@ namespace codegen {
if (codegen::platformArch == PlatformArch::x86) {
// for windows x86, aka versions before 2.206
if (fn->prototype.is_static) {
if (status == BindStatus::Binded) return "Cdecl";
if (status == BindStatus::Binded || status == BindStatus::NeedsRebinding) return "Cdecl";
else return "Optcall";
}
else if (fn->prototype.is_virtual || fn->prototype.is_callback) {
return "Thiscall";
}
else {
if (status == BindStatus::Binded) return "Thiscall";
if (status == BindStatus::Binded || status == BindStatus::NeedsRebinding) return "Thiscall";
else return "Membercall";
}
} else {
Expand Down Expand Up @@ -350,35 +354,50 @@ namespace codegen {

inline std::string getAddressString(Class const& c, Field const& field) {
if (auto fn = field.get_as<FunctionBindField>()) {
const auto isWindowsCocosCtor = [&] {
return codegen::platform == Platform::Windows
&& is_cocos_class(field.parent)
// && codegen::getStatus(field) == BindStatus::Binded
&& fn->prototype.type != FunctionType::Normal;
};

if (codegen::getStatus(*fn) == BindStatus::NeedsBinding || codegen::platformNumber(field) != -1) {
if (is_in_cocos_dll(field.parent) && codegen::platform == Platform::Windows) {
return fmt::format("base::getCocos() + 0x{:x}", codegen::platformNumber(fn->binds));
if (codegen::getStatus(*fn) == BindStatus::NeedsRebinding) {
if ((int)codegen::platform & (int)Platform::Android) {
auto const mangled = generateAndroidSymbol(c, fn);
return fmt::format( // thumb
"reinterpret_cast<uintptr_t>(dlsym(dlopen(\"libcocos2dcpp.so\", RTLD_NOW), \"{}\"))",
mangled
);
}
else if (codegen::platform == Platform::Windows) {
auto const mangled = generateWindowsSymbol(c, fn);
if (is_in_cocos_dll(field.parent)) {
return fmt::format(
"reinterpret_cast<uintptr_t>(GetProcAddress((HMODULE)base::getCocos(), \"{}\"))",
mangled
);
}
else if (is_in_extensions_dll(field.parent)) {
return fmt::format(
"reinterpret_cast<uintptr_t>(GetProcAddress((HMODULE)base::getExtensions(), \"{}\"))",
mangled
);
}
else {
// Uhhhh im not sure
return "(void*)0x9911991122";
}
}
}
else if (codegen::getStatus(*fn) == BindStatus::NeedsBinding || codegen::platformNumber(field) != -1) {
if (codegen::platform == Platform::Windows) {
if (is_in_cocos_dll(field.parent)) {
return fmt::format("base::getCocos() + 0x{:x}", codegen::platformNumber(fn->binds));
}
else if (is_in_extensions_dll(field.parent)) {
return fmt::format("base::getExtensions() + 0x{:x}", codegen::platformNumber(fn->binds));
}
else {
return fmt::format("base::get() + 0x{:x}", codegen::platformNumber(fn->binds));
}
}
else {
return fmt::format("base::get() + 0x{:x}", codegen::platformNumber(fn->binds));
}
}
else if (codegen::shouldAndroidBind(fn)) {
auto const mangled = generateAndroidSymbol(c, fn);
return fmt::format( // thumb
"reinterpret_cast<uintptr_t>(dlsym(dlopen(\"libcocos2dcpp.so\", RTLD_NOW), \"{}\"))",
mangled
);
}
else if (isWindowsCocosCtor()) {
auto const mangled = generateWindowsSymbol(c, fn);
return fmt::format(
"reinterpret_cast<uintptr_t>(GetProcAddress(GetModuleHandleA(\"libcocos2d.dll\"), \"{}\"))",
mangled
);
}
else if (codegen::getStatus(*fn) == BindStatus::Binded && fn->prototype.type == FunctionType::Normal) {
return fmt::format(
"addresser::get{}Virtual(Resolve<{}>::func(&{}::{}))",
Expand Down
79 changes: 34 additions & 45 deletions codegen/src/SourceGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,6 @@ namespace { namespace format_strings {

using namespace geode;
using namespace geode::modifier;
using cocos2d::CCDestructor;

std::unordered_map<void*, bool>& CCDestructor::destructorLock() {{
static thread_local std::unordered_map<void*, bool> ret;
return ret;
}}
bool& CCDestructor::globalLock() {{
static thread_local bool ret = false;
return ret;
}}
bool& CCDestructor::lock(void* self) {
return destructorLock()[self];
}
CCDestructor::~CCDestructor() {{
destructorLock().erase(this);
}}

auto wrapFunction(uintptr_t address, tulip::hook::WrapperMetadata const& metadata) {
auto wrapped = geode::hook::createWrapper(reinterpret_cast<void*>(address), metadata);
Expand All @@ -34,11 +18,6 @@ auto wrapFunction(uintptr_t address, tulip::hook::WrapperMetadata const& metadat
}}
return wrapped.unwrap();
}

// So apparently Clang considers cdecl to return floats through ST0, whereas
// MSVC thinks they are returned through XMM0. This has caused a lot of pain
// and misery for me

)GEN";

constexpr char const* declare_member = R"GEN(
Expand Down Expand Up @@ -79,32 +58,37 @@ auto {class_name}::{function_name}({parameters}){const} -> decltype({function_na
{class_name}::{function_name}({parameters}) {{
// basically we destruct it once by calling the gd function,
// then lock it, so that other gd destructors dont get called
if (CCDestructor::lock(this)) return;
using FunctionType = void(*)({class_name}*{parameter_comma}{parameter_types});
static auto func = wrapFunction({address_inline}, tulip::hook::WrapperMetadata{{
.m_convention = geode::hook::createConvention(tulip::hook::TulipConvention::{convention}),
.m_abstract = tulip::hook::AbstractFunction::from(FunctionType(nullptr)),
}});
reinterpret_cast<FunctionType>(func)(this{parameter_comma}{arguments});
// we need to construct it back so that it uhhh ummm doesnt crash
// while going to the child destructors
auto thing = new (this) {class_name}(geode::CutoffConstructor, sizeof({class_name}));
CCDestructor::lock(this) = true;
if (!geode::DestructorLock::isLocked(this)) {{
using FunctionType = void(*)({class_name}*{parameter_comma}{parameter_types});
static auto func = wrapFunction({address_inline}, tulip::hook::WrapperMetadata{{
.m_convention = geode::hook::createConvention(tulip::hook::TulipConvention::{convention}),
.m_abstract = tulip::hook::AbstractFunction::from(FunctionType(nullptr)),
}});
reinterpret_cast<FunctionType>(func)(this{parameter_comma}{arguments});

// we need to construct it back so that it uhhh ummm doesnt crash
// while going to the child destructors
auto thing = new (this) {class_name}(geode::CutoffConstructor, sizeof({class_name}));
geode::DestructorLock::addLock(this);
}}
}}
)GEN";

constexpr char const* declare_destructor_baseless = R"GEN(
{class_name}::{function_name}({parameters}) {{
// basically we destruct it once by calling the gd function,
// then lock it, so that other gd destructors dont get called
if (CCDestructor::lock(this)) return;
using FunctionType = void(*)({class_name}*{parameter_comma}{parameter_types});
static auto func = wrapFunction({address_inline}, tulip::hook::WrapperMetadata{{
.m_convention = geode::hook::createConvention(tulip::hook::TulipConvention::{convention}),
.m_abstract = tulip::hook::AbstractFunction::from(FunctionType(nullptr)),
}});
reinterpret_cast<FunctionType>(func)(this{parameter_comma}{arguments});
CCDestructor::lock(this) = true;
// then we release the lock because there are no other destructors after this
if (!geode::DestructorLock::isLocked(this)) {{
using FunctionType = void(*)({class_name}*{parameter_comma}{parameter_types});
static auto func = wrapFunction({address_inline}, tulip::hook::WrapperMetadata{{
.m_convention = geode::hook::createConvention(tulip::hook::TulipConvention::{convention}),
.m_abstract = tulip::hook::AbstractFunction::from(FunctionType(nullptr)),
}});
reinterpret_cast<FunctionType>(func)(this{parameter_comma}{arguments});
}}
else {{
geode::DestructorLock::removeLock(this);
}}
}}
)GEN";

Expand All @@ -113,8 +97,9 @@ auto {class_name}::{function_name}({parameters}){const} -> decltype({function_na
// here we construct it as normal as we can, then destruct it
// using the generated functions. this ensures no memory gets leaked
// no crashes :pray:
CCDestructor::lock(this) = true;
geode::DestructorLock::addLock(this);
{class_name}::~{unqualified_class_name}();

using FunctionType = void(*)({class_name}*{parameter_comma}{parameter_types});
static auto func = wrapFunction({address_inline}, tulip::hook::WrapperMetadata{{
.m_convention = geode::hook::createConvention(tulip::hook::TulipConvention::{convention}),
Expand Down Expand Up @@ -165,6 +150,10 @@ auto {class_name}::{function_name}({parameters}){const} -> decltype({function_na
)GEN";
}}

bool areSuperclassesEmpty(Class const& c) {
return c.superclasses.empty() || (c.superclasses.size() == 1 && c.superclasses[0].find("CCCopying") != std::string::npos);
}

std::string generateBindingSource(Root const& root, bool skipPugixml) {
std::string output(format_strings::source_start);

Expand Down Expand Up @@ -237,7 +226,7 @@ std::string generateBindingSource(Root const& root, bool skipPugixml) {
if (codegen::platformNumber(fn->binds) == 0x9999999) {
used_declare_format = format_strings::declare_unimplemented_error;
}
else if (codegen::getStatus(*fn) != BindStatus::NeedsBinding && !codegen::shouldAndroidBind(fn)) {
else if (codegen::getStatus(*fn) != BindStatus::NeedsBinding && codegen::getStatus(*fn) != BindStatus::NeedsRebinding) {
continue;
}

Expand All @@ -247,15 +236,15 @@ std::string generateBindingSource(Root const& root, bool skipPugixml) {
used_declare_format = format_strings::declare_member;
break;
case FunctionType::Ctor:
if (c.superclasses.empty()) {
if (areSuperclassesEmpty(c)) {
used_declare_format = format_strings::declare_constructor_begin;
}
else {
used_declare_format = format_strings::declare_constructor;
}
break;
case FunctionType::Dtor:
used_declare_format = c.superclasses.empty() ? format_strings::declare_destructor_baseless : format_strings::declare_destructor;
used_declare_format = areSuperclassesEmpty(c) ? format_strings::declare_destructor_baseless : format_strings::declare_destructor;
break;
}

Expand Down
Loading
Loading