Merge branch 'geode-sdk:main' into main

This commit is contained in:
alk 2022-10-22 20:20:09 +03:00 committed by GitHub
commit 8f4e117325
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 801 additions and 241 deletions

View file

@ -1,5 +1,28 @@
# Geode Changelog
## v0.6.0
- Mod resource loading has been reworked again, with the intent of adding support for texture pack loaders
- Added `Loader::addTexturePath` and `Loader::removeTexturePath` to work with additional resource paths
- Mods that work with Cocos2d search paths directly should convert to using the above functions
- New header `Geode/ui/LayerBG.hpp` with `createLayerBG` function for creating the blue gradient background GD layers have
- All Cocos2d include paths have been changed to be relative
- `cocos2dx` folder has been removed, cocos2d is now directly inside `Geode/cocos`
- Loader resources updating will no longer check for plist file differences due to problems with CLI on Mac
- More codegen optimizations, Geode mods should now compile faster
- Added `NewResult` class, which will eventually replace the old `Result`
- Add `deprecate` and `nodiscard` attributes to the old `Result` class
- Many more bindings on Mac and Windows
## v0.5.0
- Added `CCFileUtils::get`
- Fix crashes related to setting IDs in `MenuLayer`
- Remove `Loader::updateModResourcePaths` and `Loader::updateResourcePaths`. The minimum mod target version is still v0.4.0 however, as you should never have been using these functions.
- Rework how mod resources are added
- Deprecate `geode::utils::vector` and `geode::utils::container` namespaces and replace them with `geode::utils::ranges`
- Finally added a license to Geode! The framework is now licensed under BSL v1.0.
## v0.4.8
- CLI issues fixed in v1.0.6 so loader again verifies if loader resources are missing / corrupt on startup

View file

@ -39,14 +39,23 @@ target_sources(${PROJECT_NAME} INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/entry.cpp)
add_subdirectory(codegen)
add_custom_target(CodegenRun ALL
message(STATUS ${GEODE_CODEGEN_PATH}/Geode/GeneratedSource.cpp)
add_custom_command(
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/bindings/GeometryDash.bro
${CMAKE_CURRENT_SOURCE_DIR}/bindings/Cocos2d.bro
${CMAKE_CURRENT_SOURCE_DIR}/bindings/Entry.bro
COMMAND Codegen ${GEODE_TARGET_PLATFORM} bindings ${GEODE_CODEGEN_PATH}
COMMAND echo codegen > ${GEODE_CODEGEN_PATH}/.stamp
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Run Codegen"
BYPRODUCTS ${GEODE_CODEGEN_PATH}/Geode/GeneratedSource.cpp
OUTPUT ${GEODE_CODEGEN_PATH}/Geode/GeneratedSource.cpp ${GEODE_CODEGEN_PATH}/.stamp
)
add_custom_target(CodegenRun
DEPENDS ${GEODE_CODEGEN_PATH}/.stamp
)
add_dependencies(${PROJECT_NAME} CodegenRun)
add_dependencies(CodegenRun Codegen)
# Hacky way to supress the not generated error
if (NOT EXISTS ${GEODE_CODEGEN_PATH}/Geode/GeneratedSource.cpp)

View file

@ -1 +1 @@
0.5.0
0.6.0

View file

@ -17,6 +17,7 @@ class AchievementManager : cocos2d::CCNode {
PAD = win 0x4;
}
class AchievementNotifier : cocos2d::CCNode {
void notifyAchievement(const char* title, const char* desc, const char* icon, bool quest) {
m_queue->addObject(AchievementBar::create(title, desc, icon, quest));
@ -1205,6 +1206,7 @@ class EditorUI : cocos2d::CCLayer, FLAlertLayerProtocol, ColorSelectDelegate, GJ
void onGoToBaseLayer(cocos2d::CCObject* sender) = win 0x88790;
void editColor(cocos2d::CCObject* sender) = mac 0x19190, win 0x8d3c0;
void alignObjects(cocos2d::CCArray* objs, bool alignY) = win 0x8f320;
virtual void scrollWheel(float vertical, float horizontal) = win 0x921d0, mac 0x31370, ios 0x2c4884;
EditButtonBar* m_buttonBar;
PAD = mac 0x8, win 0x4;
@ -3917,7 +3919,7 @@ class MenuGameLayer {
void update(float) = mac 0x28fa70, win 0x18f190;
}
class MenuLayer : cocos2d::CCLayer, FLAlertLayerProtocol {
class MenuLayer : cocos2d::CCLayer, FLAlertLayerProtocol, GooglePlayDelegate {
inline MenuLayer() {}
virtual ~MenuLayer() = mac 0x1d1230, win 0x190620;
virtual bool init() = mac 0x1d14b0, win 0x1907b0;
@ -3942,6 +3944,11 @@ class MenuLayer : cocos2d::CCLayer, FLAlertLayerProtocol {
void onYouTube(cocos2d::CCObject*) = win 0x1919A0;
static cocos2d::CCScene* scene(bool) = mac 0x1d12d0, win 0x190720, ios 0x19e57c;
MenuLayer* node() = win 0x190550;
cocos2d::CCSprite* m_googlePlaySprite;
cocos2d::CCSprite* m_viewProfileInfoText;
cocos2d::CCLabelBMFont* m_playerNameLabel;
CCMenuItemSpriteExtra* m_profileBtn;
}
class MessageListDelegate {}

View file

@ -1,4 +1,5 @@
#include "Shared.hpp"
#include "TypeOpt.hpp"
namespace { namespace format_strings {
@ -14,6 +15,9 @@ GEODE_INLINE GEODE_HIDDEN static uintptr_t address{index}() {{
std::string generateAddressHeader(Root& root) {
std::string output;
TypeBank bank;
bank.loadFrom(root);
for (auto& c : root.classes) {
for (auto& field : c.fields) {
@ -26,18 +30,16 @@ std::string generateAddressHeader(Root& root) {
}
if (codegen::getStatus(field) == BindStatus::Binded) {
const auto ids = bank.getIDs(fn->beginning, c.name);
address_str = fmt::format("addresser::get{}Virtual((types::member{})(&{}::{}))",
str_if("Non", !fn->beginning.is_virtual),
field.field_id,
ids.member,
field.parent,
fn->beginning.name
);
} else if (codegen::getStatus(field) == BindStatus::NeedsBinding) {
address_str = fmt::format("base::get() + 0x{:x}", codegen::platformNumber(fn->binds));
} else {
continue;
}

View file

@ -32,9 +32,9 @@ int main(int argc, char** argv) try {
}
writeFile(writeDir / "GeneratedAddress.hpp", generateAddressHeader(root));
writeFile(writeDir / "GeneratedModify.hpp", generateModifyHeader(root, writeDir / "modify")); // pretty much obsolete with a custom compiler
writeFile(writeDir / "GeneratedWrapper.hpp", generateWrapperHeader(root)); // pretty much obsolete with a custom compiler
writeFile(writeDir / "GeneratedType.hpp", generateTypeHeader(root)); // pretty much obsolete with a custom compiler
writeFile(writeDir / "GeneratedModify.hpp", generateModifyHeader(root, writeDir / "modify"));
writeFile(writeDir / "GeneratedWrapper.hpp", generateWrapperHeader(root));
writeFile(writeDir / "GeneratedType.hpp", generateTypeHeader(root));
writeFile(writeDir / "GeneratedBinding.hpp", generateBindingHeader(root, writeDir / "binding"));
writeFile(writeDir / "GeneratedPredeclare.hpp", generatePredeclareHeader(root));
writeFile(writeDir / "GeneratedSource.cpp", generateBindingSource(root));

View file

@ -1,4 +1,5 @@
#include "Shared.hpp"
#include "TypeOpt.hpp"
#include <iostream>
#include <set>
@ -36,7 +37,7 @@ namespace geode::modifier {{
// requires: index, class_name, arg_types, function_name, raw_arg_types, non_virtual
char const* apply_function = R"GEN(
GEODE_APPLY_MODIFY_FOR_FUNCTION({index}, {function_convention}, {class_name}, {function_name}))GEN";
GEODE_APPLY_MODIFY_FOR_FUNCTION({addr_index}, {pure_index}, {function_convention}, {class_name}, {function_name}))GEN";
char const* modify_end = R"GEN(
}
@ -52,6 +53,9 @@ namespace geode::modifier {{
std::string generateModifyHeader(Root& root, ghc::filesystem::path const& singleFolder) {
std::string output;
TypeBank bank;
bank.loadFrom(root);
for (auto c : root.classes) {
if (c.name == "cocos2d")
continue;
@ -104,7 +108,8 @@ std::string generateModifyHeader(Root& root, ghc::filesystem::path const& single
}
single_output += fmt::format(format_strings::apply_function,
fmt::arg("index", f.field_id),
fmt::arg("addr_index", f.field_id),
fmt::arg("pure_index", bank.getPure(*begin, c.name)),
fmt::arg("class_name", c.name),
fmt::arg("function_name", function_name),
fmt::arg("function_convention", codegen::getConvention(f))

View file

@ -175,113 +175,4 @@ namespace codegen {
if (index == std::string::npos) return s;
return s.substr(index + 2);
}
/*
// defined : we have a symbol that is usable
inline bool isFunctionDefined(Function const& f) {
// basically this is true for
// fmod for all platforms
// cocos for windows
// all funcs without stl parameter for android
// destructors for no platforms
if (f.function_type == kDestructor) return false;
if (f.function_type == kConstructor) return false;
if (f.parent_class->name.rfind("fmod::", 0) == 0) return true;
if (f.parent_class->name.rfind("cocos2d::", 0) == 0 && codegen::platform == kWindows) return true;
if (getParameterTypes(f).find("gd::", 0) != string::npos && codegen::platform == kAndroid) return false;
if (codegen::platform == kAndroid) return true;
return false;
}
// definable : we can define it and hook it
inline bool isFunctionDefinable(Function const& f) {
// basically this is true for
// all funcs that we have the offset for
// all funcs with stl parameter for android
if (getParameterTypes(f).find("gd::", 0) != string::npos && codegen::platform == kAndroid) return true;
if (getBind(f) != "") return true;
return false;
}
inline std::string getUnqualifiedClassName(Function const& f) {
auto index = f.parent_class->name.rfind("::");
if (index == std::string::npos) return f.parent_class->name;
return f.parent_class->name.substr(index + 2);
}
inline std::string getFunctionName(Function const& f) {
return f.name;
}
inline std::string getConst(Function const& f) {
return f.is_const ? "const" : "";
}
inline std::string getConstWhitespace(Function const& f) {
return f.is_const ? " " : "";
}
inline std::string getParameterTypes(Function const& f) { //int, float
return fmt::format("{}", fmt::join(f.args, ", "));
}
inline std::string getArguments(Function const& f) { // p0, p1
return fmt::format("{}", fmt::join(f.argnames, ", "));
}
inline std::string getParameterComma(Function const& f) { // int p0, float p1
return f.args.size() > 0 ? ", " : "";
}
inline std::string getParameterTypeComma(Function const& f) { //int, float
return f.args.size() > 0 ? ", " : "";
}
inline std::string getArgumentComma(Function const& f) { // p0, p1
return f.argnames.size() > 0 ? ", " : "";
}
// defined : we have a symbol that is usable
inline bool isFunctionDefined(Function const& f) {
// basically this is true for
// fmod for all platforms
// cocos for windows
// all funcs without stl parameter for android
// destructors for no platforms
if (f.function_type == kDestructor) return false;
if (f.function_type == kConstructor) return false;
if (f.parent_class->name.rfind("fmod::", 0) == 0) return true;
if (f.parent_class->name.rfind("cocos2d::", 0) == 0 && codegen::platform == kWindows) return true;
if (getParameterTypes(f).find("gd::", 0) != string::npos && codegen::platform == kAndroid) return false;
if (codegen::platform == kAndroid) return true;
return false;
}
// definable : we can define it and hook it
inline bool isFunctionDefinable(Function const& f) {
// basically this is true for
// all funcs that we have the offset for
// all funcs with stl parameter for android
if (getParameterTypes(f).find("gd::", 0) != string::npos && codegen::platform == kAndroid) return true;
if (getBind(f) != "") return true;
return false;
}
inline std::string getConvention(Function const& f) {
if (codegen::platform != kWindows) return "DefaultConv";
switch (f.function_type) {
case kConstructor: [[fallthrough]];
case kDestructor: [[fallthrough]];
case kRegularFunction:
if (isFunctionDefined(f)) return "x86::Thiscall";
return "x86::Membercall";
case kVirtualFunction:
return "x86::Thiscall";
case kStaticFunction:
if (isFunctionDefined(f)) return "x86::Cdecl";
return "x86::Optcall";
}
return "x86::Membercall";
}*/
}

View file

@ -1,4 +1,5 @@
#include "Shared.hpp"
#include "TypeOpt.hpp"
namespace { namespace format_strings {
char const* source_start = R"CAC(
@ -16,23 +17,23 @@ using namespace geode::modifier; // types
)CAC";
char const* declare_member = R"GEN(
types::ret{index} {class_name}::{function_name}({parameters}){const} {{
auto func = Function<types::meta{index}, {convention}>({{addresses::address{index}()}});
types::ret{ret_index} {class_name}::{function_name}({parameters}){const} {{
auto func = Function<types::meta{meta_index}, {convention}>({{addresses::address{addr_index}()}});
return func(this{parameter_comma}{arguments});
}}
)GEN";
char const* declare_virtual = R"GEN(
types::ret{index} {class_name}::{function_name}({parameters}){const} {{
auto self = addresser::thunkAdjust((types::member{index})(&{class_name}::{function_name}), this);
auto func = Function<types::meta{index}, {convention}>({{addresses::address{index}()}});
types::ret{ret_index} {class_name}::{function_name}({parameters}){const} {{
auto self = addresser::thunkAdjust((types::member{member_index})(&{class_name}::{function_name}), this);
auto func = Function<types::meta{meta_index}, {convention}>({{addresses::address{addr_index}()}});
return func(self{parameter_comma}{arguments});
}}
)GEN";
char const* declare_static = R"GEN(
types::ret{index} {class_name}::{function_name}({parameters}){const} {{
auto func = Function<types::meta{index}, {convention}>({{addresses::address{index}()}});
types::ret{ret_index} {class_name}::{function_name}({parameters}){const} {{
auto func = Function<types::meta{meta_index}, {convention}>({{addresses::address{addr_index}()}});
return func({arguments});
}}
)GEN";
@ -42,7 +43,7 @@ types::ret{index} {class_name}::{function_name}({parameters}){const} {{
// 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;
auto func = Function<types::meta{index}, {convention}>({{addresses::address{index}()}});
auto func = Function<types::meta{meta_index}, {convention}>({{addresses::address{addr_index}()}});
func(this{parameter_comma}{arguments});
// we need to construct it back so that it uhhh ummm doesnt crash
// while going to the child destructors
@ -58,7 +59,7 @@ types::ret{index} {class_name}::{function_name}({parameters}){const} {{
// no crashes :pray:
CCDestructor::lock(this) = true;
{class_name}::~{unqualified_class_name}();
auto func = Function<types::meta{index}, {convention}>({{addresses::address{index}()}});
auto func = Function<types::meta{meta_index}, {convention}>({{addresses::address{addr_index}()}});
func(this{parameter_comma}{arguments});
}}
)GEN";
@ -75,6 +76,9 @@ types::ret{index} {class_name}::{function_name}({parameters}){const} {{
std::string generateBindingSource(Root& root) {
std::string output(format_strings::source_start);
TypeBank bank;
bank.loadFrom(root);
for (auto& c : root.classes) {
for (auto& f : c.fields) {
@ -100,7 +104,6 @@ std::string generateBindingSource(Root& root) {
fmt::arg("const", str_if(" const ", fn->beginning.is_const)),
fmt::arg("class_name", c.name),
fmt::arg("parameters", codegen::getParameters(fn->beginning)),
fmt::arg("index", f.field_id),
fmt::arg("definition", fn->inner)
);
break;
@ -110,7 +113,6 @@ std::string generateBindingSource(Root& root) {
fmt::arg("const", str_if(" const ", fn->beginning.is_const)),
fmt::arg("class_name", c.name),
fmt::arg("parameters", codegen::getParameters(fn->beginning)),
fmt::arg("index", f.field_id),
fmt::arg("definition", fn->inner),
fmt::arg("return", fn->beginning.ret.name)
);
@ -140,13 +142,18 @@ std::string generateBindingSource(Root& root) {
if (fn->beginning.is_virtual && fn->beginning.type != FunctionType::Dtor)
used_declare_format = format_strings::declare_virtual;
auto ids = bank.getIDs(fn->beginning, c.name);
output += fmt::format(used_declare_format,
fmt::arg("class_name", c.name),
fmt::arg("unqualified_class_name", codegen::getUnqualifiedClassName(c.name)),
fmt::arg("const", str_if(" const ", fn->beginning.is_const)),
fmt::arg("convention", codegen::getConvention(f)),
fmt::arg("function_name", fn->beginning.name),
fmt::arg("index", f.field_id),
fmt::arg("meta_index", ids.meta),
fmt::arg("member_index", ids.member),
fmt::arg("ret_index", ids.ret),
fmt::arg("addr_index", f.field_id),
fmt::arg("parameters", codegen::getParameters(fn->beginning)),
fmt::arg("parameter_types", codegen::getParameterTypes(fn->beginning)),
fmt::arg("arguments", codegen::getParameterNames(fn->beginning)),

View file

@ -1,84 +1,95 @@
#include "Shared.hpp"
#include "TypeOpt.hpp"
#include <set>
namespace { namespace format_strings {
char const* declare_member_type = R"GEN(
using ret{index} = {return};
using func{index} = ret{index}(*)({const}{class_name}*{parameter_type_comma}{parameter_types});
using pure{index} = ret{index}({parameter_types});
using meta{index} = ret{index}({const}{class_name}*{parameter_type_comma}{parameter_types});
using member{index} = ret{index}({class_name}::*)({parameter_types}){const};
)GEN";
char const* declare_static_type = R"GEN(
using ret{index} = {return};
using func{index} = ret{index}(*)({parameter_types});
using pure{index} = ret{index}({parameter_types});
using meta{index} = ret{index}({parameter_types});
using member{index} = func{index};
)GEN";
char const* declare_structor_type = R"GEN(
using ret{index} = void;
using func{index} = ret{index}(*)({class_name}*{parameter_type_comma}{parameter_types});
using pure{index} = ret{index}({parameter_types});
using meta{index} = ret{index}({const}{class_name}*{parameter_type_comma}{parameter_types});
using member{index} = func{index};
)GEN";
}}
static std::string getReturn(FunctionBegin const& fn, std::string const& parent) {
if (fn.type != FunctionType::Normal)
return "void";
if (fn.ret.name == "auto") {
std::vector<std::string> declvals;
for (auto& [t, n] : fn.args) {
declvals.push_back(fmt::format("std::declval<{}>()", t.name));
}
return fmt::format(
fn.is_static ? "decltype({}::{}({}))" : "decltype(std::declval<{}>().{}({}))",
parent,
fn.name,
fmt::join(declvals, ", ")
);
}
return fn.ret.name;
}
std::string generateTypeHeader(Root& root) {
std::string output;
for (auto& c : root.classes) {
for (auto& f : c.fields) {
if (codegen::getStatus(f) == BindStatus::Unbindable)
continue;
auto fn = f.get_fn();
TypeBank bank;
bank.loadFrom(root);
char const* used_format = format_strings::declare_member_type;
std::map<std::string, int> used_returns;
std::map<std::string, int> used_funcs;
std::map<std::string, int> used_pures;
if (fn->type != FunctionType::Normal) {
used_format = format_strings::declare_structor_type;
}
int i = 0;
for (auto& f : bank.typeList()) {
if (fn->is_static) {
used_format = format_strings::declare_static_type;
}
char const* return_fmt = "using ret{index} = {return};";
char const* func_fmt;
char const* pure_fmt = "ret{ret_index}({parameter_types});";
char const* meta_fmt;
char const* member_fmt;
output += fmt::format(used_format,
fmt::arg("parameter_types", codegen::getParameterTypes(*fn)),
fmt::arg("parameter_type_comma", str_if(", ", !fn->args.empty())),
fmt::arg("class_name", c.name),
fmt::arg("const", str_if(" const ", fn->is_const)),
fmt::arg("index", f.field_id),
fmt::arg("return", getReturn(*fn, c.name))
);
switch (f.type) {
case FuncType::Member:
func_fmt = "ret{ret_index}(*)( {const}{class_name}*{parameter_type_comma}{parameter_types});";
meta_fmt = "ret{ret_index}({const}{class_name}*{parameter_type_comma}{parameter_types});";
member_fmt = "ret{ret_index}({class_name}::*)({parameter_types}){const};";
break;
case FuncType::Static:
func_fmt = "ret{ret_index}(*)({parameter_types});";
meta_fmt = "ret{ret_index}({parameter_types});";
member_fmt = "func{index};";
break;
case FuncType::Structor:
func_fmt = "ret{ret_index}(*)({class_name}*{parameter_type_comma}{parameter_types});";
meta_fmt = "ret{ret_index}({const}{class_name}*{parameter_type_comma}{parameter_types});";
member_fmt = "func{index};";
break;
}
if (used_returns.count(f.return_type) == 0) {
output += fmt::format(return_fmt,
fmt::arg("index", i),
fmt::arg("return", f.return_type)
) + "\n";
used_returns[f.return_type] = i;
}
int ret_index = used_returns[f.return_type];
std::string pure_val = fmt::format(pure_fmt,
fmt::arg("ret_index", ret_index),
fmt::arg("parameter_types", fmt::join(f.parameter_types, ", "))
);
if (used_pures.count(pure_val) == 0) {
output += fmt::format("using pure{} = {}\n", i, pure_val);
used_pures[pure_val] = i;
}
std::string func_val = fmt::format(func_fmt,
fmt::arg("ret_index", ret_index),
fmt::arg("parameter_types", fmt::join(f.parameter_types, ", ")),
fmt::arg("parameter_type_comma", str_if(", ", !f.parameter_types.empty())),
fmt::arg("class_name", f.class_name),
fmt::arg("const", str_if(" const ", f.is_const))
);
std::string meta_val = fmt::format(meta_fmt,
fmt::arg("ret_index", ret_index),
fmt::arg("parameter_types", fmt::join(f.parameter_types, ", ")),
fmt::arg("parameter_type_comma", str_if(", ", !f.parameter_types.empty())),
fmt::arg("class_name", f.class_name),
fmt::arg("const", str_if(" const ", f.is_const))
);
std::string member_val = fmt::format(member_fmt,
fmt::arg("ret_index", ret_index),
fmt::arg("parameter_types", fmt::join(f.parameter_types, ", ")),
fmt::arg("class_name", f.class_name),
fmt::arg("const", str_if(" const ", f.is_const)),
fmt::arg("index", i)
);
if (used_funcs.count(func_val) == 0) {
output += fmt::format("using func{index} = {func}\nusing meta{index} = {meta}\nusing member{index} = {member}\n",
fmt::arg("index", i),
fmt::arg("func", func_val),
fmt::arg("meta", meta_val),
fmt::arg("member", member_val)
);
used_funcs[func_val] = i;
}
++i;
}
return output;

180
codegen/src/TypeOpt.hpp Normal file
View file

@ -0,0 +1,180 @@
#include "Shared.hpp"
#include <set>
#include <algorithm>
namespace {
enum class FuncType : int {
Structor,
Static,
Member
};
struct Func {
std::string return_type;
bool is_const;
std::string class_name;
std::vector<std::string> parameter_types;
FuncType type;
std::string toStr() const {
return fmt::format("{}{}{}{}{}",
return_type,
class_name,
fmt::join(parameter_types, ","),
is_const ? " const " : "",
static_cast<int>(type)
);
}
bool operator<(Func const& f) const {
return toStr() < f.toStr();
}
bool operator==(Func const& f) const {
return (
return_type == f.return_type &&
is_const == f.is_const &&
class_name == f.class_name &&
parameter_types == f.parameter_types &&
type == f.type
);
}
};
struct Ids {
int ret = -1;
int func = -1;
int pure = -1;
int meta = -1;
int member = -1;
};
class TypeBank {
std::vector<Func> m_stuff;
public:
static std::string getReturn(FunctionBegin const& fn, std::string const& parent) {
if (fn.type != FunctionType::Normal)
return "void";
if (fn.ret.name == "auto") {
std::vector<std::string> declvals;
for (auto& [t, n] : fn.args) {
declvals.push_back(fmt::format("std::declval<{}>()", t.name));
}
return fmt::format(
fn.is_static ? "decltype({}::{}({}))" : "decltype(std::declval<{}>().{}({}))",
parent,
fn.name,
fmt::join(declvals, ", ")
);
}
return fn.ret.name;
}
static Func makeFunc(FunctionBegin const& fn, std::string const& parent) {
Func f;
f.return_type = TypeBank::getReturn(fn, parent);
f.is_const = fn.is_const;
f.class_name = parent;
for (auto& arg : fn.args) {
f.parameter_types.push_back(arg.first.name);
}
if (fn.is_static)
f.type = FuncType::Static;
else if (fn.type != FunctionType::Normal)
f.type = FuncType::Structor;
else
f.type = FuncType::Member;
return f;
}
void loadFrom(Root& root) {
for (auto& c : root.classes) {
for (auto& f : c.fields) {
if (codegen::getStatus(f) == BindStatus::Unbindable)
continue;
m_stuff.push_back(TypeBank::makeFunc(*f.get_fn(), c.name));
}
}
std::sort(m_stuff.begin(), m_stuff.end());
m_stuff.erase(std::unique(m_stuff.begin(), m_stuff.end()), m_stuff.end());
}
std::vector<Func> const& typeList() { return m_stuff; }
Ids getIDs(FunctionBegin const& fn, std::string const& parent) {
Ids out;
Func in_f = TypeBank::makeFunc(fn, parent);
int i = 0;
for (auto f : m_stuff) {
if (out.ret == -1 && f.return_type == in_f.return_type) {
out.ret = i;
}
if (out.func == -1) {
if (in_f.type == FuncType::Member) {
if (f == in_f) out.func = i;
} else if (in_f.type == FuncType::Structor) {
if (
f.return_type == in_f.return_type &&
f.class_name == in_f.class_name &&
f.is_const == in_f.is_const &&
f.parameter_types == in_f.parameter_types
) out.func = i;
} else {
if (
f.return_type == in_f.return_type &&
f.parameter_types == in_f.parameter_types &&
f.type == in_f.type
) out.func = i;
}
}
if (out.meta == -1 || out.member == -1) {
Func assume_member = f;
assume_member.type = FuncType::Member;
if (in_f == assume_member) {
out.meta = i;
} else if (out.func != -1) {
out.meta = out.func;
}
}
if (out.pure == -1 && f.return_type == in_f.return_type && f.parameter_types == in_f.parameter_types) {
out.pure = i;
}
if (out.ret != -1 && out.func != -1 && out.pure != -1)
break;
++i;
}
out.member = out.func;
return out;
}
int getPure(FunctionBegin const& fn, std::string const& parent) {
Func in_f = TypeBank::makeFunc(fn, parent);
int i = 0;
for (auto f : m_stuff) {
if (f.return_type == in_f.return_type && f.parameter_types == in_f.parameter_types) {
return i;
}
++i;
}
return -1;
}
};
}

View file

@ -173,14 +173,14 @@ typedef void (CCObject::*SEL_MenuHandler)(CCObject*);
typedef void (CCObject::*SEL_EventHandler)(CCEvent*);
typedef int (CCObject::*SEL_Compare)(CCObject*);
#define schedule_selector(_SELECTOR) (SEL_SCHEDULE)(&_SELECTOR)
#define callfunc_selector(_SELECTOR) (SEL_CallFunc)(&_SELECTOR)
#define callfuncN_selector(_SELECTOR) (SEL_CallFuncN)(&_SELECTOR)
#define callfuncND_selector(_SELECTOR) (SEL_CallFuncND)(&_SELECTOR)
#define callfuncO_selector(_SELECTOR) (SEL_CallFuncO)(&_SELECTOR)
#define menu_selector(_SELECTOR) (SEL_MenuHandler)(&_SELECTOR)
#define event_selector(_SELECTOR) (SEL_EventHandler)(&_SELECTOR)
#define compare_selector(_SELECTOR) (SEL_Compare)(&_SELECTOR)
#define schedule_selector(_SELECTOR) (cocos2d::SEL_SCHEDULE)(&_SELECTOR)
#define callfunc_selector(_SELECTOR) (cocos2d::SEL_CallFunc)(&_SELECTOR)
#define callfuncN_selector(_SELECTOR) (cocos2d::SEL_CallFuncN)(&_SELECTOR)
#define callfuncND_selector(_SELECTOR) (cocos2d::SEL_CallFuncND)(&_SELECTOR)
#define callfuncO_selector(_SELECTOR) (cocos2d::SEL_CallFuncO)(&_SELECTOR)
#define menu_selector(_SELECTOR) (cocos2d::SEL_MenuHandler)(&_SELECTOR)
#define event_selector(_SELECTOR) (cocos2d::SEL_EventHandler)(&_SELECTOR)
#define compare_selector(_SELECTOR) (cocos2d::SEL_Compare)(&_SELECTOR)
// end of base_nodes group
/// @}

View file

@ -175,13 +175,13 @@ namespace geode {
void setValue(ValueType const& value) {
m_value = value;
if constexpr (std::is_base_of_v<IMinMax<ValueType>, Class>) {
static_cast<Class*>(this)->constrainMinMax(m_value);
(void)static_cast<Class*>(this)->constrainMinMax(m_value);
}
if constexpr (std::is_base_of_v<IOneOf<Class, ValueType>, Class>) {
static_cast<Class*>(this)->constrainOneOf(m_value);
(void)static_cast<Class*>(this)->constrainOneOf(m_value);
}
if constexpr (std::is_base_of_v<IMatch<Class, ValueType>, Class>) {
static_cast<Class*>(this)->constrainMatch(m_value);
(void)static_cast<Class*>(this)->constrainMatch(m_value);
}
this->valueChanged();
}

View file

@ -7,15 +7,16 @@
#include <Geode/loader/Mod.hpp>
#include <iostream>
#define GEODE_APPLY_MODIFY_FOR_FUNCTION(index, convention, className, functionName) \
using base##index = wrap::functionName<Base, types::pure##index>; \
using derived##index = wrap::functionName<Derived, types::pure##index>; \
if constexpr (derived##index::uuid != nullptr && (void*)base##index::uuid != (void*)derived##index::uuid) { \
Mod::get()->addHook<derived##index::value, convention>( \
#className "::" #functionName, \
(void*)addresses::address##index() \
); \
} \
#define GEODE_APPLY_MODIFY_FOR_FUNCTION(addr_index, pure_index, convention, className, functionName) \
if constexpr ( \
wrap::functionName<Derived, types::pure##pure_index>::uuid != nullptr && \
(void*)wrap::functionName<Base, types::pure##pure_index>::uuid != (void*)wrap::functionName<Derived, types::pure##pure_index>::uuid \
) { \
(void)Mod::get()->addHook<wrap::functionName<Derived, types::pure##pure_index>::value, convention>( \
#className "::" #functionName, \
(void*)addresses::address##addr_index() \
); \
} \
namespace geode::modifier {

View file

@ -0,0 +1,315 @@
#pragma once
#include "../DefaultInclude.hpp"
#include <string_view>
#include <string>
#include <variant>
namespace geode {
namespace {
struct AnyType {
explicit AnyType() = delete;
};
template<class V, class RV>
concept ConvertibleToResult =
std::is_convertible_v<
std::remove_reference_t<V>,
std::remove_reference_t<RV>
> || std::is_same_v<
std::remove_reference_t<V>,
std::remove_reference_t<RV>
>;
using DefaultValue = std::monostate;
using DefaultError = std::string;
}
template<class T = DefaultValue, class E = DefaultError>
class [[nodiscard]] NewResult {
public:
using value_type = std::remove_reference_t<T>;
using error_type = std::remove_reference_t<E>;
// for some reason doing requires causes errors with pch...
static_assert(
std::is_copy_constructible_v<value_type> ||
std::is_move_constructible_v<value_type>,
"T must be copiable or movable!"
);
static_assert(
std::is_copy_constructible_v<error_type> ||
std::is_move_constructible_v<error_type>,
"E must be copiable or movable!"
);
protected:
std::variant<value_type, error_type> m_value;
public:
bool isOk() const {
return std::holds_alternative<value_type>(m_value);
}
bool isErr() const {
return std::holds_alternative<error_type>(m_value);
}
explicit NewResult(value_type const& value)
requires std::is_copy_constructible_v<value_type>
: m_value(value) {}
explicit NewResult(value_type&& value)
requires std::is_move_constructible_v<value_type>
: m_value(std::forward<value_type>(value)) {}
explicit NewResult(error_type const& value)
requires std::is_copy_constructible_v<error_type>
: m_value(value) {}
explicit NewResult(error_type&& value)
requires std::is_move_constructible_v<error_type>
: m_value(std::forward<error_type>(value)) {}
NewResult(NewResult<T, E> const& other)
requires
std::is_copy_constructible_v<value_type> &&
std::is_copy_constructible_v<error_type>
= default;
NewResult(NewResult<T, E>&& other)
requires (
!std::is_copy_constructible_v<value_type> ||
!std::is_copy_constructible_v<error_type>
) = default;
template<class T2, class E2>
requires ConvertibleToResult<T2, T> && ConvertibleToResult<E2, E>
NewResult(NewResult<T2, E2> const& other)
requires
std::is_copy_constructible_v<value_type> &&
std::is_copy_constructible_v<error_type>
: m_value(other.isOk() ? other.unwrap() : other.unwrapErr()) {}
template<class T2, class E2>
requires ConvertibleToResult<T2, T> && ConvertibleToResult<E2, E>
NewResult(NewResult<T2, E2>&& other)
requires (
!std::is_copy_constructible_v<value_type> ||
!std::is_copy_constructible_v<error_type>
) : m_value(other.isOk() ? other.unwrap() : other.unwrapErr()) {}
template<class T2>
requires ConvertibleToResult<T2, T>
NewResult(NewResult<T2, AnyType> const& other)
requires
std::is_copy_constructible_v<value_type> &&
std::is_copy_constructible_v<error_type>
: NewResult(value_type(other.unwrap())) {}
template<class E2>
requires ConvertibleToResult<E2, E>
NewResult(NewResult<AnyType, E2> const& other)
requires
std::is_copy_constructible_v<value_type> &&
std::is_copy_constructible_v<error_type>
: m_value(std::forward<E2>(other.unwrapErr())) {}
template<class T2>
requires ConvertibleToResult<T2, T>
NewResult(NewResult<T2, AnyType>&& other)
requires (
!std::is_copy_constructible_v<value_type> ||
!std::is_copy_constructible_v<error_type>
) : m_value(other.unwrap()) {}
template<class E2>
requires ConvertibleToResult<E2, E>
NewResult(NewResult<AnyType, E2>&& other)
requires (
!std::is_copy_constructible_v<value_type> ||
!std::is_copy_constructible_v<error_type>
) : NewResult(std::forward<error_type>(other.unwrapErr())) {}
value_type unwrap() const
requires std::is_copy_constructible_v<value_type>
{
return std::get<value_type>(m_value);
}
value_type&& unwrap()
requires (!std::is_copy_constructible_v<value_type>)
{
return std::move(std::get<value_type>(m_value));
}
error_type unwrapErr() const
requires std::is_copy_constructible_v<error_type>
{
return std::get<error_type>(m_value);
}
error_type&& unwrapErr()
requires (!std::is_copy_constructible_v<error_type>)
{
return std::move(std::get<error_type>(m_value));
}
explicit operator bool() const requires (
!std::is_same_v<T, bool> &&
!std::is_same_v<E, bool>
) {
return this->isOk();
}
};
template<class T>
class [[nodiscard]] NewResult<T, T> {
public:
using value_type = std::remove_reference_t<T>;
using error_type = std::remove_reference_t<T>;
// for some reason doing requires causes errors with pch...
static_assert(
std::is_copy_constructible_v<value_type> ||
std::is_move_constructible_v<value_type>,
"T must be copiable or movable!"
);
protected:
bool m_success;
value_type m_value;
public:
bool isOk() const {
return m_success;
}
bool isErr() const {
return !m_success;
}
explicit NewResult(value_type const& value, bool success)
requires std::is_copy_constructible_v<value_type>
: m_value(value), m_success(success) {}
explicit NewResult(value_type&& value, bool success)
requires std::is_move_constructible_v<value_type>
: m_value(std::forward<value_type>(value)), m_success(success) {}
NewResult(NewResult<T, T> const& other)
requires std::is_copy_constructible_v<value_type> = default;
NewResult(NewResult<T, T>&& other)
requires (!std::is_copy_constructible_v<value_type>) = default;
template<class T2, class E2>
requires ConvertibleToResult<T2, T> && ConvertibleToResult<E2, T>
NewResult(NewResult<T2, E2> const& other)
requires
std::is_copy_constructible_v<value_type>
: m_value(other.isOk() ? other.unwrap() : other.unwrapErr()),
m_success(other.isOk()) {}
template<class T2, class E2>
requires ConvertibleToResult<T2, T> && ConvertibleToResult<E2, T>
NewResult(NewResult<T2, E2>&& other)
requires (
!std::is_copy_constructible_v<value_type>
) : m_value(other.isOk() ? other.unwrap() : other.unwrapErr()),
m_success(other.isOk()) {}
template<class T2>
requires ConvertibleToResult<T2, T>
NewResult(NewResult<T2, AnyType> const& other)
requires
std::is_copy_constructible_v<value_type>
: NewResult(value_type(other.unwrap()), true) {}
template<class T2>
requires ConvertibleToResult<T2, T>
NewResult(NewResult<T2, AnyType>&& other)
requires (
!std::is_copy_constructible_v<value_type>
) : NewResult(std::forward<value_type>(other.unwrap()), true) {}
template<class E2>
requires ConvertibleToResult<E2, T>
NewResult(NewResult<AnyType, E2> const& other)
requires
std::is_copy_constructible_v<value_type>
: NewResult(error_type(other.unwrapErr()), false) {}
template<class E2>
requires ConvertibleToResult<E2, T>
NewResult(NewResult<AnyType, E2>&& other)
requires (
!std::is_copy_constructible_v<value_type>
) : NewResult(std::forward<error_type>(other.unwrapErr()), false) {}
value_type unwrap() const
requires std::is_copy_constructible_v<value_type>
{
return m_value;
}
value_type&& unwrap()
requires (!std::is_copy_constructible_v<value_type>)
{
return std::move(m_value);
}
error_type unwrapErr() const
requires std::is_copy_constructible_v<error_type>
{
return m_value;
}
error_type&& unwrapErr()
requires (!std::is_copy_constructible_v<error_type>)
{
return std::move(m_value);
}
explicit operator bool() const requires (
!std::is_same_v<T, bool>
) {
return this->isOk();
}
};
template<class T = DefaultValue, class E = AnyType>
requires std::is_copy_constructible_v<T>
NewResult<T, E> NewOk(T value = T()) {
return NewResult<T, E>(value);
}
template<class T = DefaultValue, class E = AnyType>
requires (!std::is_copy_constructible_v<T>)
NewResult<T, E> NewOk(T&& value) {
return NewResult<T, E>(std::forward<T>(value));
}
template<class E = DefaultError, class T = AnyType>
requires std::is_copy_constructible_v<E>
NewResult<T, E> NewErr(E error = E()) {
return NewResult<T, E>(error);
}
template<class E = DefaultError, class T = AnyType>
requires (!std::is_copy_constructible_v<E>)
NewResult<T, E> NewErr(E&& error) {
return NewResult<T, E>(std::forward<E>(error));
}
#define GEODE_UNWRAP_INTO(into, ...) \
auto GEODE_CONCAT(res_, __LINE__) = (__VA_ARGS__);\
if (GEODE_CONCAT(res_, __LINE__).isErr()) {\
return Err(std::move(GEODE_CONCAT(res_, __LINE__).unwrapErr()));\
}\
into = std::move(GEODE_CONCAT(res_, __LINE__).unwrap())
#define GEODE_UNWRAP(...) \
{ auto GEODE_CONCAT(res_, __LINE__) = (__VA_ARGS__);\
if (GEODE_CONCAT(res_, __LINE__).isErr()) {\
return Err(std::move(GEODE_CONCAT(res_, __LINE__).unwrapErr()));\
} }
}

View file

@ -3,6 +3,7 @@
#include <Geode/DefaultInclude.hpp>
#include <string_view>
#include "types.hpp"
#include "NewResult.hpp"
namespace geode {
/**
@ -26,7 +27,7 @@ namespace geode {
* @authors Matcool, HJfod
*/
template <class T = no_result, class E = std::string>
class Result {
class [[nodiscard, deprecated("Result's implementation will be replaced with geode::NewResult later")]] Result {
protected:
bool success;
union {
@ -109,7 +110,7 @@ namespace geode {
* Convert to bool
* @example if (result) { <handle success> } else { <handle failure> }
*/
operator bool() const { return this->success; }
explicit operator bool() const { return this->success; }
/**
* Create a success result
@ -130,6 +131,7 @@ namespace geode {
* @returns Successful Result
*/
template <class T = no_result>
[[nodiscard, deprecated("Result's implementation will be replaced with geode::NewResult later")]]
Result<T> Ok(T value = T()) {
return Result<T>::ok(value);
}
@ -141,7 +143,7 @@ namespace geode {
* @returns Errorful Result
*/
template <class E = std::string>
struct Err {
struct [[nodiscard, deprecated("Result's implementation will be replaced with geode::NewResult later")]] Err {
const E _value;
Err(const TypeIdentityType<E> value) : _value(value) {}
template <class T>

View file

@ -0,0 +1,98 @@
#include <Geode/utils/cocos.hpp>
#include <Index.hpp>
#include "../ui/internal/list/ModListLayer.hpp"
#include <InternalMod.hpp>
#include "../ui/internal/info/ModInfoLayer.hpp"
#include <InternalLoader.hpp>
USE_GEODE_NAMESPACE();
#pragma warning(disable: 4217)
template<class T = CCNode>
requires std::is_base_of_v<CCNode, T>
T* setIDSafe(CCNode* node, int index, const char* id) {
if constexpr (std::is_same_v<CCNode, T>) {
if (auto child = getChild(node, index)) {
child->setID(id);
return child;
}
} else {
if (auto child = getChildOfType<T>(node, index)) {
child->setID(id);
return child;
}
}
return nullptr;
}
#include <Geode/modify/LevelSearchLayer.hpp>
class $modify(LevelSearchLayer) {
bool init() {
if (!LevelSearchLayer::init())
return false;
// set the funny ids
this->setID("creator-layer");
setIDSafe(this, 0, "creator-layer-bg");
getChildOfType<CCTextInputNode>(this, 0)->setID("search-bar");
getChildOfType<CCScale9Sprite>(this, 0)->setID("level-search-bg");
getChildOfType<CCScale9Sprite>(this, 1)->setID("level-search-bar-bg");
getChildOfType<CCScale9Sprite>(this, 2)->setID("quick-search-bg");
getChildOfType<CCScale9Sprite>(this, 3)->setID("difficulty-filters-bg");
getChildOfType<CCScale9Sprite>(this, 4)->setID("length-filters-bg");
getChildOfType<CCLabelBMFont>(this, 0)->setID("quick-search-title");
getChildOfType<CCLabelBMFont>(this, 1)->setID("filters-title");
getChildOfType<CCSprite>(this, 1)->setID("left-corner");
getChildOfType<CCSprite>(this, 2)->setID("right-corner");
if (auto filtermenu = getChildOfType<CCMenu>(this, 0)) {
filtermenu->setID("other-filter-menu");
setIDSafe(filtermenu, 0, "clear-filters-button");
setIDSafe(filtermenu, 1, "advanced-filters-button");
}
if (auto searchmenu = getChildOfType<CCMenu>(this, 1)) {
searchmenu->setID("search-button-menu");
setIDSafe(searchmenu, 0, "search-level-button");
setIDSafe(searchmenu, 1, "search-user-button");
}
if (auto quickmenu = getChildOfType<CCMenu>(this, 2)) {
quickmenu->setID("quick-search-menu");
setIDSafe(quickmenu, 0, "most-downloaded-button");
setIDSafe(quickmenu, 1, "most-liked-button");
setIDSafe(quickmenu, 2, "trending-button");
setIDSafe(quickmenu, 3, "recent-button");
setIDSafe(quickmenu, 4, "magic-button");
setIDSafe(quickmenu, 5, "awarded-button");
setIDSafe(quickmenu, 6, "followed-button");
setIDSafe(quickmenu, 7, "friends-button");
}
if (auto filtersmenu = getChildOfType<CCMenu>(this, 3)) {
filtersmenu->setID("difficulty-filter-menu");
setIDSafe(filtersmenu, 0, "na-filter-button");
setIDSafe(filtersmenu, 1, "easy-filter-button");
setIDSafe(filtersmenu, 2, "normal-filter-button");
setIDSafe(filtersmenu, 3, "hard-filter-button");
setIDSafe(filtersmenu, 4, "harder-filter-button");
setIDSafe(filtersmenu, 5, "insane-filter-button");
setIDSafe(filtersmenu, 6, "demon-filter-button");
setIDSafe(filtersmenu, 7, "auto-filter-button");
setIDSafe(filtersmenu, 8, "demon-type-filter-button");
}
if (auto filtersmenu = getChildOfType<CCMenu>(this, 4)) {
filtersmenu->setID("length-filter-menu");
setIDSafe(filtersmenu, 0, "clock-icon");
setIDSafe(filtersmenu, 1, "tiny-filter-button");
setIDSafe(filtersmenu, 2, "short-filter-button");
setIDSafe(filtersmenu, 3, "medium-filter-button");
setIDSafe(filtersmenu, 4, "long-filter-button");
setIDSafe(filtersmenu, 5, "xl-filter-button");
setIDSafe(filtersmenu, 6, "star-filter-button");
}
if (auto backmenu = getChildOfType<CCMenu>(this, 5)) {
backmenu->setID("exit-menu");
setIDSafe(backmenu, 0, "exit-button");
}
return true;
}
};

View file

@ -37,7 +37,7 @@ static CCEGLView* CCEGLView_CCEGLView(CCEGLView* self) {
}
$execute {
Mod::get()->addHook<&CCEGLView_CCEGLView, Thiscall>("CCEGLView::CCEGLView", CCEGLVIEW_CON_ADDR);
(void)Mod::get()->addHook<&CCEGLView_CCEGLView, Thiscall>("CCEGLView::CCEGLView", CCEGLVIEW_CON_ADDR);
}
#endif

View file

@ -150,7 +150,7 @@ void Index::updateIndex(IndexUpdateCallback callback, bool force) {
// different sha
if (force || currentCommitSHA != upcomingCommitSHA) {
// save new sha in file
utils::file::writeString(indexDir / "current", upcomingCommitSHA);
(void)utils::file::writeString(indexDir / "current", upcomingCommitSHA);
web::AsyncWebRequest()
.join("index-download")
@ -643,7 +643,7 @@ InstallItems::CallbackID InstallItems::start(
auto item = Index::get()->getKnownItem(inst);
auto indexDir = Loader::get()->getGeodeDirectory() / "index";
file::createDirectoryAll(indexDir / "temp");
(void)file::createDirectoryAll(indexDir / "temp");
auto tempFile = indexDir / "temp" / item.m_download.m_filename;
m_downloaded.push_back(tempFile);

View file

@ -257,11 +257,11 @@ static void printMods(std::ostream& stream) {
static LONG WINAPI exceptionHandler(LPEXCEPTION_POINTERS info) {
// make sure crashlog directory exists
utils::file::createDirectoryAll(crashlog::getCrashLogDirectory());
(void)utils::file::createDirectoryAll(crashlog::getCrashLogDirectory());
// add a file to let Geode know on next launch that it crashed previously
// this could also be done by saving a loader setting or smth but eh.
utils::file::writeBinary(
(void)utils::file::writeBinary(
crashlog::getCrashLogDirectory() + "/last-crashed", {}
);

View file

@ -362,7 +362,10 @@ bool Loader::setup() {
log::debug("Setting up Loader...");
this->createDirectories();
this->loadSettings();
auto sett = this->loadSettings();
if (!sett) {
log::warn("Unable to load loader settings: {}", sett.error());
}
this->refreshMods();
m_isSetup = true;

View file

@ -48,7 +48,7 @@ Mod::Mod(ModInfo const& info) {
}
Mod::~Mod() {
this->unload();
(void)this->unload();
}
Result<> Mod::loadSettings() {
@ -217,14 +217,14 @@ Result<> Mod::load() {
if (m_implicitLoadFunc) {
auto r = m_implicitLoadFunc(this);
if (!r) {
this->unloadPlatformBinary();
(void)this->unloadPlatformBinary();
RETURN_LOAD_ERR("Implicit mod entry point returned an error");
}
}
if (m_loadFunc) {
auto r = m_loadFunc(this);
if (!r) {
this->unloadPlatformBinary();
(void)this->unloadPlatformBinary();
RETURN_LOAD_ERR("Mod entry point returned an error");
}
}
@ -406,7 +406,7 @@ bool Mod::updateDependencyStates() {
}
if (dep.isUnresolved()) {
m_resolved = false;
this->unload();
(void)this->unload();
hasUnresolved = true;
}
}

View file

@ -195,7 +195,10 @@ Result<ModInfo> ModInfo::createFromFile(ghc::filesystem::path const& path) {
auto info = res.value();
info.m_path = path;
if (path.has_parent_path()) {
info.addSpecialFiles(path.parent_path());
auto err = info.addSpecialFiles(path.parent_path());
if (!err) {
return Err(err.error());
}
}
return Ok(info);
} catch(std::exception& e) {
@ -245,7 +248,10 @@ Result<ModInfo> ModInfo::createFromGeodeFile(ghc::filesystem::path const& path)
auto info = res.value();
info.m_path = path;
info.addSpecialFiles(unzip);
auto err = info.addSpecialFiles(unzip);
if (!err) {
return Err(err.error());
}
return Ok(info);
}

View file

@ -18,7 +18,7 @@ static bool tryOrAlert(Result<T> const& res, const char* title) {
if (!res) {
FLAlertLayer::create(title, res.error(), "OK")->show();
}
return res;
return res.is_value();
}
ModCell::ModCell(const char* name, CCSize size) :