mirror of
https://github.com/geode-sdk/geode.git
synced 2024-12-04 13:11:09 -05:00
268 lines
8.7 KiB
C++
268 lines
8.7 KiB
C++
#include "Shared.hpp"
|
|
#include <iostream>
|
|
#include <set>
|
|
|
|
namespace { namespace format_strings {
|
|
// requires: base_classes, class_name
|
|
char const* binding_include = R"GEN(#include "binding/{file_name}"
|
|
)GEN";
|
|
|
|
char const* class_includes = R"GEN(#pragma once
|
|
#include <stdexcept>
|
|
#include <Geode/platform/platform.hpp>
|
|
#include <Geode/c++stl/gdstdlib.hpp>
|
|
#include <cocos2d.h>
|
|
#include <cocos-ext.h>
|
|
#include <Geode/GeneratedPredeclare.hpp>
|
|
#include <Geode/Enums.hpp>
|
|
#include <Geode/utils/SeedValue.hpp>
|
|
|
|
)GEN";
|
|
|
|
char const* class_no_includes = R"GEN(#pragma once
|
|
#include <Geode/platform/platform.hpp>
|
|
#include <stdexcept>
|
|
|
|
)GEN";
|
|
|
|
char const* class_include_prereq = R"GEN(#include "{file_name}"
|
|
)GEN";
|
|
|
|
char const* class_start = R"GEN(
|
|
class {class_name}{base_classes} {{
|
|
public:
|
|
static constexpr auto CLASS_NAME = "{class_name}";
|
|
)GEN";
|
|
|
|
char const* custom_constructor = R"GEN( GEODE_CUSTOM_CONSTRUCTOR_GD({class_name}, {first_base})
|
|
)GEN";
|
|
|
|
char const* custom_constructor_cutoff = R"GEN( GEODE_CUSTOM_CONSTRUCTOR_CUTOFF({class_name}, {first_base})
|
|
)GEN";
|
|
|
|
char const* function_definition = R"GEN(
|
|
/**
|
|
{docs}{docs_addresses} */
|
|
{static}{virtual}{return_type} {function_name}({parameters}){const};
|
|
)GEN";
|
|
|
|
char const* error_definition = R"GEN(
|
|
private:
|
|
[[deprecated("{class_name}::{function_name} not implemented")]]
|
|
/**
|
|
{docs}{docs_addresses} */
|
|
{static}{virtual}{return_type} {function_name}({parameters}){const};
|
|
public:
|
|
)GEN";
|
|
|
|
char const* structor_definition = R"GEN(
|
|
/**
|
|
{docs}{docs_addresses} */
|
|
{function_name}({parameters});
|
|
)GEN";
|
|
|
|
// requires: type, member_name, array
|
|
char const* member_definition = R"GEN({private} {type} {member_name};{public}
|
|
)GEN";
|
|
|
|
char const* pad_definition = R"GEN( GEODE_PAD({hardcode});
|
|
)GEN";
|
|
|
|
char const* class_end = R"GEN(};
|
|
)GEN";
|
|
}}
|
|
|
|
inline std::string nameForPlatform(Platform platform) {
|
|
switch (platform) {
|
|
case Platform::Mac: return "MacOS";
|
|
case Platform::Windows: return "Windows";
|
|
case Platform::iOS: return "iOS";
|
|
case Platform::Android: return "Android";
|
|
default: // unreachable
|
|
return "Windows";
|
|
}
|
|
}
|
|
|
|
template <class T>
|
|
std::string generateAddressDocs(T const& f, PlatformNumber pn) {
|
|
std::string ret;
|
|
|
|
for (auto platform : {Platform::Mac, 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)
|
|
);
|
|
}
|
|
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
std::string generateDocs(std::string const& docs) {
|
|
if (docs.size() < 7) return "";
|
|
auto ret = docs.substr(1, docs.size() - 6); // i hate this but idk how to generalize
|
|
|
|
for (auto next = ret.find(" "); next != std::string::npos; next = ret.find(" ")) {
|
|
ret.replace(next, 8, " * ");
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
std::string generateBindingHeader(Root const& root, ghc::filesystem::path const& singleFolder) {
|
|
std::string output;
|
|
|
|
{
|
|
std::string filename = "Standalones.hpp";
|
|
output += fmt::format(format_strings::binding_include,
|
|
fmt::arg("file_name", filename)
|
|
);
|
|
|
|
std::string single_output;
|
|
single_output += format_strings::class_includes;
|
|
|
|
for (auto& f : root.functions) {
|
|
FunctionProto const* fb = &f.prototype;
|
|
char const* used_format = format_strings::function_definition;
|
|
|
|
std::string addressDocs = generateAddressDocs(f, f.binds);
|
|
std::string docs = generateDocs(fb->docs);
|
|
|
|
single_output += fmt::format(used_format,
|
|
fmt::arg("virtual", ""),
|
|
fmt::arg("static", ""),
|
|
fmt::arg("class_name", ""),
|
|
fmt::arg("const", ""),
|
|
fmt::arg("function_name", fb->name),
|
|
fmt::arg("parameters", codegen::getParameters(*fb)),
|
|
fmt::arg("return_type", fb->ret.name),
|
|
fmt::arg("docs_addresses", addressDocs),
|
|
fmt::arg("docs", docs)
|
|
);
|
|
|
|
}
|
|
|
|
writeFile(singleFolder / filename, single_output);
|
|
}
|
|
|
|
for (auto& cls : root.classes) {
|
|
if (is_cocos_class(cls.name))
|
|
continue;
|
|
|
|
std::string filename = (codegen::getUnqualifiedClassName(cls.name) + ".hpp");
|
|
output += fmt::format(format_strings::binding_include,
|
|
fmt::arg("file_name", filename)
|
|
);
|
|
|
|
std::string single_output;
|
|
if (cls.name != "GDString") {
|
|
single_output += format_strings::class_includes;
|
|
} else {
|
|
single_output += format_strings::class_no_includes;
|
|
}
|
|
|
|
for (auto dep : cls.depends) {
|
|
if (can_find(dep, "cocos2d::")) continue;
|
|
|
|
std::string depfilename = (codegen::getUnqualifiedClassName(dep) + ".hpp");
|
|
|
|
single_output += fmt::format(format_strings::class_include_prereq, fmt::arg("file_name", depfilename));
|
|
}
|
|
|
|
std::string supers = str_if(
|
|
fmt::format(" : public {}", fmt::join(cls.superclasses, ", public ")),
|
|
!cls.superclasses.empty()
|
|
);
|
|
|
|
single_output += fmt::format(::format_strings::class_start,
|
|
fmt::arg("class_name", cls.name),
|
|
fmt::arg("base_classes", supers)
|
|
);
|
|
|
|
// what.
|
|
if (!cls.superclasses.empty()) {
|
|
single_output += fmt::format(
|
|
is_cocos_class(cls.superclasses[0])
|
|
? format_strings::custom_constructor_cutoff
|
|
: format_strings::custom_constructor,
|
|
fmt::arg("class_name", cls.name),
|
|
fmt::arg("first_base", cls.superclasses[0])
|
|
);
|
|
}
|
|
|
|
bool unimplementedField = false;
|
|
for (auto field : cls.fields) {
|
|
MemberFunctionProto* fb;
|
|
char const* used_format = format_strings::function_definition;
|
|
|
|
std::string addressDocs;
|
|
|
|
if (auto i = field.get_as<InlineField>()) {
|
|
single_output += "\t" + i->inner + "\n";
|
|
continue;
|
|
} else if (auto m = field.get_as<MemberField>()) {
|
|
single_output += fmt::format(format_strings::member_definition,
|
|
fmt::arg("private", unimplementedField ? "private:\n" : ""),
|
|
fmt::arg("public", unimplementedField ? "\npublic:" : ""),
|
|
fmt::arg("type", m->type.name),
|
|
fmt::arg("member_name", m->name + str_if(fmt::format("[{}]", m->count), m->count))
|
|
);
|
|
continue;
|
|
} else if (auto p = field.get_as<PadField>()) {
|
|
auto hardcode = codegen::platformNumber(p->amount);
|
|
|
|
if (hardcode) {
|
|
single_output += fmt::format(format_strings::pad_definition, fmt::arg("hardcode", hardcode));
|
|
} else {
|
|
unimplementedField = true;
|
|
}
|
|
continue;
|
|
} else if (auto fn = field.get_as<OutOfLineField>()) {
|
|
fb = &fn->prototype;
|
|
addressDocs = " * @note[short] Out of line\n";
|
|
|
|
} else if (auto fn = field.get_as<FunctionBindField>()) {
|
|
fb = &fn->prototype;
|
|
|
|
if (!codegen::platformNumber(fn->binds)) {
|
|
used_format = format_strings::error_definition;
|
|
|
|
if (fb->type != FunctionType::Normal)
|
|
continue;
|
|
}
|
|
|
|
addressDocs = generateAddressDocs(field, fn->binds);
|
|
}
|
|
|
|
std::string docs = generateDocs(fb->docs);
|
|
|
|
single_output += fmt::format(used_format,
|
|
fmt::arg("virtual", str_if("virtual ", fb->is_virtual)),
|
|
fmt::arg("static", str_if("static ", fb->is_static)),
|
|
fmt::arg("class_name", cls.name),
|
|
fmt::arg("const", str_if(" const ", fb->is_const)),
|
|
fmt::arg("function_name", fb->name),
|
|
fmt::arg("parameters", codegen::getParameters(*fb)),
|
|
fmt::arg("return_type", fb->ret.name),
|
|
fmt::arg("docs_addresses", addressDocs),
|
|
fmt::arg("docs", docs)
|
|
);
|
|
}
|
|
|
|
// if (hasClass)
|
|
single_output += ::format_strings::class_end;
|
|
|
|
writeFile(singleFolder / filename, single_output);
|
|
}
|
|
|
|
return output;
|
|
}
|