Merge branch 'main' of https://github.com/geode-sdk/geode into main

This commit is contained in:
HJfod 2022-12-04 18:41:14 +02:00
commit 08934132d8
22 changed files with 7010 additions and 843 deletions

View file

@ -84,7 +84,7 @@ IndentCaseLabels: true
IndentExternBlock: Indent
IndentGotoLabels: true
IndentPPDirectives: BeforeHash
IndentRequiresClause: false
IndentRequiresClause: true
IndentWrappedFunctionNames: false
# InsertBraces: true
InsertTrailingCommas: None
@ -101,6 +101,7 @@ PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyBreakOpenParenthesis: 0
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 1000000

View file

@ -210,6 +210,7 @@ class cocos2d::CCFileUtils : cocos2d::TypeInfo {
virtual void addSearchPath(const char* path);
virtual void removeSearchPath(const char *path);
virtual std::string fullPathForFilename(const char* filename, bool unk);
void removeAllPaths() = mac 0x241600;
}
class cocos2d::CCGLProgram {

View file

@ -0,0 +1,153 @@
#pragma once
#include <cstdint>
#include <array>
namespace geode::stl {
template <class Type>
class AllocatorTemplate {
protected:
inline Type* allocate(size_t count);
inline void deallocate(Type* pointer);
};
#if defined(GEODE_IS_WINDOWS)
template <class Type>
class AllocatorBase {
protected:
inline Type* allocate(size_t count) {
}
inline void deallocate(Type* pointer) {
}
};
#elif defined(GEODE_IS_MACOS) || defined(GEODE_IS_ANDROID)
#elif defined(GEODE_IS_IOS)
#endif
class StringTemplate : public AllocatorBase {
protected:
// depends on already set size and capacity
inline void setStorage(char* pointer) = delete;
inline char* getStorage() = delete;
inline void setSize(size_t size) = delete;
inline size_t getSize() = delete;
inline void setCapacity(size_t capacity) = delete;
inline size_t getCapacity() = delete;
};
#if defined(GEODE_IS_WINDOWS)
class StringBase : public StringTemplate {
protected:
union {
std::array<char, 16> m_smallStorage;
char* m_bigStorage;
};
size_t m_size;
size_t m_capacity;
inline void setStorage(char* storage) {
if (m_capacity > 15) {
return m_bigStorage = storage;
}
}
inline char* getStorage() {
if (m_capacity > 15) {
return m_bigStorage;
}
return m_smallStorage.data();
}
inline void setSize(size_t size) {
m_size = size;
}
inline size_t getSize() {
return m_size;
}
inline void setCapacity(size_t capacity) {
m_capacity = capacity;
}
inline size_t getCapacity() {
return m_capacity;
}
};
#elif defined(GEODE_IS_MACOS) || defined(GEODE_IS_ANDROID)
class StringBase : public StringTemplate {
protected:
char* m_storage;
size_t m_size;
size_t m_capacity;
inline void setStorage(char* storage) {
m_storage = storage;
}
inline char* getStorage() {
return m_storage;
}
inline void setSize(size_t size) {
m_size = size;
}
inline size_t getSize() {
return m_size;
}
inline void setCapacity(size_t capacity) {
m_capacity = capacity;
}
inline size_t getCapacity() {
return m_capacity;
}
};
#elif defined(GEODE_IS_IOS)
class StringBase : public StringTemplate {
protected:
struct Short {
uint8_t sizex2;
std::array<char, 23> shortStorage;
};
struct Long {
size_t capacitym1;
size_t size;
char* longStorage;
};
union {
Short m_short;
Long m_long;
};
inline void setStorage(char* storage) {
m_storage = storage;
}
inline char* getStorage() {
return m_storage;
}
inline void setSize(size_t size) {
m_size = size;
}
inline size_t getSize() {
return m_size;
}
inline void setCapacity(size_t capacity) {
m_capacity = capacity;
}
inline size_t getCapacity() {
return m_capacity;
}
};
#endif
}

View file

@ -296,84 +296,166 @@ namespace gd {
}
};
// template <class Type>
// using vector = std::vector<Type>;
template <typename T>
class GEODE_DLL vector {
public:
using value_type = T;
auto allocator() const {
return std::allocator<T>();
}
operator std::vector<T>() const {
std::vector<T> out;
for (auto i = m_start; i != m_finish; ++i) {
out.push_back(*i);
}
return out;
return std::vector<T>(m_start, m_finish);
}
vector(std::vector<T> input) {
std::allocator<T> alloc;
auto tmp = alloc.allocate(input.size());
vector() {
m_start = nullptr;
m_finish = nullptr;
m_reserveEnd = nullptr;
}
m_start = tmp;
m_finish = m_start + input.size();
m_capacity_end = m_start + input.size();
for (auto i : input) {
*tmp = i;
tmp++;
vector(std::vector<T> const& input) : vector() {
if (input.size()) {
m_start = this->allocator().allocate(input.size());
m_finish = m_start + input.size();
m_reserveEnd = m_start + input.size();
std::copy(input.begin(), input.end(), m_start);
}
}
vector(std::initializer_list<T> const& input) {
std::allocator<T> alloc;
auto tmp = alloc.allocate(input.size());
m_start = tmp;
m_finish = m_start + input.size();
m_capacity_end = m_start + input.size();
std::copy(input.begin(), input.end(), tmp);
vector(gd::vector<T> const& input) : vector() {
if (input.size()) {
m_start = this->allocator().allocate(input.size());
m_finish = m_start + input.size();
m_reserveEnd = m_start + input.size();
std::copy(input.begin(), input.end(), m_start);
}
}
vector(gd::vector<T>&& input) : vector() {
m_start = input.m_start;
m_finish = input.m_finish;
m_reserveEnd = input.m_reserveEnd;
input.m_start = nullptr;
input.m_finish = nullptr;
input.m_reserveEnd = nullptr;
}
vector& operator=(gd::vector<T> const& input) {
this->clear();
if (input.size()) {
m_start = this->allocator().allocate(input.size());
m_finish = m_start + input.size();
m_reserveEnd = m_start + input.size();
std::copy(input.begin(), input.end(), m_start);
}
return *this;
}
vector& operator=(gd::vector<T>&& input) {
m_start = input.m_start;
m_finish = input.m_finish;
m_reserveEnd = input.m_reserveEnd;
input.m_start = nullptr;
input.m_finish = nullptr;
input.m_reserveEnd = nullptr;
return *this;
}
vector(std::initializer_list<T> const& input) : vector() {
if (input.size()) {
m_start = this->allocator().allocate(input.size());
m_finish = m_start + input.size();
m_reserveEnd = m_start + input.size();
std::copy(input.begin(), input.end(), m_start);
}
}
void clear() {
std::allocator<T> alloc;
alloc.deallocate(m_start, (m_finish - m_start) / 8);
m_start = alloc.allocate(1);
m_finish = m_start;
m_capacity_end = m_start + 8;
if (m_start) {
std::destroy(m_start, m_finish);
this->allocator().deallocate(m_start, this->size());
}
m_start = nullptr;
m_finish = nullptr;
m_reserveEnd = nullptr;
}
T& operator[](size_t index) {
return m_start[index];
}
T const& operator[](size_t index) const {
return m_start[index];
}
T& at(size_t index) {
if (index >= this->size()) {
throw std::out_of_range("gd::vector::at");
}
return m_start[index];
}
T const& at(size_t index) const {
if (index >= this->size()) {
throw std::out_of_range("gd::vector::at");
}
return m_start[index];
}
T& front() {
return *m_start;
}
auto begin() {
T* begin() {
return m_start;
}
auto end() {
T* end() {
return m_finish;
}
auto begin() const {
return static_cast<const T*>(m_start);
T const* begin() const {
return m_start;
}
auto end() const {
return static_cast<const T*>(m_finish);
T const* end() const {
return m_finish;
}
vector(vector const& lol) : vector(std::vector<T>(lol)) {}
vector() : vector(std::vector<T>()) {}
~vector() {
for (auto i = m_start; i != m_finish; ++i) {
delete i;
}
}
size_t size() const {
return m_finish - m_start;
}
size_t capacity() const {
return m_reserveEnd - m_start;
}
protected:
T* m_start;
T* m_finish;
T* m_capacity_end;
T* m_reserveEnd;
};
struct _bit_reference {
@ -444,24 +526,32 @@ namespace gd {
uintptr_t* m_capacity_end;
public:
vector(std::vector<bool> input) : m_start(0), m_end(0) {
auto realsize = input.size() / int(sizeof(uintptr_t));
auto tmp = new uintptr_t[realsize];
m_start = _bit_iterator(tmp);
m_end = _bit_iterator(tmp + realsize, input.size() % sizeof(uintptr_t));
m_capacity_end = tmp + realsize;
auto itmp = m_start;
for (auto i : input) {
*itmp = i;
++itmp;
}
auto allocator() const {
return std::allocator<uintptr_t>();
}
vector(vector<bool> const& lol) : vector(std::vector<bool>(lol)) {}
vector() : m_start(nullptr), m_end(nullptr), m_capacity_end(nullptr) {}
vector() : vector(std::vector<bool>()) {}
// vector(std::vector<bool> input) : vector() {
// auto realsize = input.size() / int(sizeof(uintptr_t));
// auto start = this->allocator().allocate(realsize);
// m_start = _bit_iterator(start);
// m_end = _bit_iterator(start + realsize, input.size() % sizeof(uintptr_t));
// m_capacity_end = start + realsize;
// auto itmp = m_start;
// for (auto i : input) {
// *itmp = i;
// ++itmp;
// }
// }
// vector(vector<bool> const& input) : vector() {
// }
// vector() : vector(std::vector<bool>()) {}
~vector() {
delete[] m_start.m_bitptr;
@ -476,8 +566,8 @@ namespace gd {
}
_bit_reference operator[](size_t index) {
const auto real_index = index / sizeof(uintptr_t);
const auto offset = index % sizeof(uintptr_t);
auto const real_index = index / sizeof(uintptr_t);
auto const offset = index % sizeof(uintptr_t);
return _bit_reference(&m_start.m_bitptr[real_index], 1UL << offset);
}
@ -535,7 +625,7 @@ namespace gd {
operator std::vector<T>() {
return m_internal;
}
void clear() {
m_internal.clear();
}

File diff suppressed because it is too large Load diff

View file

@ -43,9 +43,16 @@ namespace geode {
Hook(Hook const&) = delete;
Hook operator=(Hook const&) = delete;
// Used by Mod
Result<> enable();
Result<> disable();
friend class Mod;
friend class Loader;
static std::vector<std::pair<Hook*, Mod*>> internalHooks;
static bool readyToHook;
public:
/**
* Get the address of the function hooked.

View file

@ -33,6 +33,31 @@ namespace geode {
IndexUpdateFilter();
};
class GEODE_DLL ModInstallEvent : public Event {
protected:
std::string m_id;
UpdateStatus m_status;
public:
ModInstallEvent(
std::string const& id,
UpdateStatus status
);
std::string getModID() const;
UpdateStatus getStatus() const;
};
class GEODE_DLL ModInstallFilter : public EventFilter<IndexUpdateEvent> {
protected:
std::string m_id;
public:
using Callback = void(ModInstallEvent*);
ListenerResult handle(std::function<Callback> fn, ModInstallEvent* event);
ModInstallFilter(std::string const& id);
};
struct GEODE_DLL IndexItem {
std::string sourceRepository;
ghc::filesystem::path path;

View file

@ -41,17 +41,14 @@ namespace geode::modifier {
return node->getFieldContainer();
}
};
GEODE_DLL size_t getFieldIndexForClass(size_t hash);
template <class Parent, class Base>
class FieldIntermediate {
// Padding used for guaranteeing any member of parents
// will be in between sizeof(Intermediate) and sizeof(Parent)
uintptr_t m_padding;
static inline std::unordered_map<size_t, size_t> nextIndex;
static size_t getFieldIndexForClass(size_t hash) {
return nextIndex[hash]++;
}
public:
static void fieldConstructor(void* offsetField) {

View file

@ -1,322 +1,230 @@
#pragma once
#include "../DefaultInclude.hpp"
#include "../external/result/result.hpp"
#include <fmt/format.h>
#include <string>
#include <string_view>
#include <type_traits>
#include <variant>
#include <fmt/format.h>
// clang-format off
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>>;
namespace impl {
using DefaultValue = std::monostate;
using DefaultError = std::string;
template <class T>
using WrappedResult = std::conditional_t<
std::is_lvalue_reference_v<T>, std::reference_wrapper<std::remove_reference_t<T>>,
std::remove_const_t<T>>;
template<class T, class E>
struct Storage {
std::variant<T, E> m_value;
template <class E = impl::DefaultError>
class [[nodiscard]] Failure {
public:
WrappedResult<E> m_error;
bool holdsOk() const {
return std::holds_alternative<T>(m_value);
Failure() = default;
template <class E2>
requires(std::is_constructible_v<E, E2 const&>)
explicit constexpr Failure(E2 const& e) : m_error(e) {}
template <class E2>
requires(std::is_constructible_v<E, E2 &&>)
explicit constexpr Failure(E2&& e) : m_error(std::move(e)) {}
E& error() & noexcept {
return m_error;
}
T getOk() const requires std::is_copy_constructible_v<T> {
return std::get<T>(m_value);
E const& error() const& noexcept {
return m_error;
}
T&& getOk() requires(!std::is_copy_constructible_v<T>) {
return std::move(std::get<T>(m_value));
E&& error() && noexcept {
return static_cast<E&&>(m_error);
}
E getErr() const requires std::is_copy_constructible_v<E> {
return std::get<E>(m_value);
E const&& error() const&& noexcept {
return static_cast<E&&>(m_error);
}
E&& getErr() requires(!std::is_copy_constructible_v<E>) {
return std::move(std::get<E>(m_value));
}
template<class T2, class E2>
requires
std::is_convertible_v<T2, T> &&
std::is_convertible_v<E2, E>
Storage(Storage<T2, E2> const& other)
requires
std::is_copy_constructible_v<T> &&
std::is_copy_constructible_v<E>
{
if (other.holdsOk()) {
m_value = other.getOk();
} else {
m_value = other.getErr();
}
}
Storage(T const& value)
requires std::is_copy_constructible_v<T>
: m_value(value) {}
Storage(T&& value)
requires std::is_move_constructible_v<T>
: m_value(std::forward<T>(value)) {}
Storage(E const& value, std::monostate)
requires std::is_copy_constructible_v<E>
: m_value(value) {}
Storage(E&& value, std::monostate)
requires std::is_move_constructible_v<E>
: m_value(std::forward<E>(value)) {}
};
template<class T>
struct Storage<T, T> {
bool m_holdsOk;
T m_value;
template <class T = impl::DefaultValue>
class [[nodiscard]] Success {
public:
WrappedResult<T> m_value;
bool holdsOk() const {
return m_holdsOk;
}
Success() = default;
T getOk() const requires std::is_copy_constructible_v<T> {
template <class T2>
requires(std::is_constructible_v<T, T2 const&>)
explicit constexpr Success(T2 const& v) : m_value(v) {}
template <class T2>
requires(std::is_constructible_v<T, T2 &&>)
explicit constexpr Success(T2&& v) : m_value(std::forward<T2>(v)) {}
T& value() & noexcept {
return m_value;
}
T&& getOk() requires(!std::is_copy_constructible_v<T>) {
return std::move(m_value);
}
T getErr() const requires std::is_copy_constructible_v<T> {
T const& value() const& noexcept {
return m_value;
}
T&& getErr() requires(!std::is_copy_constructible_v<T>) {
return std::move(m_value);
T&& value() && noexcept {
return static_cast<T&&>(m_value);
}
template<class T2, class E2>
requires
std::is_convertible_v<T2, T> &&
std::is_convertible_v<E2, T>
Storage(Storage<T2, E2> const& other)
requires
std::is_copy_constructible_v<T> &&
std::is_copy_constructible_v<T>
: m_value(other.holdsOk() ? other.getOk() : other.getErr()),
m_holdsOk(other.holdsOk()) {}
Storage(T const& value)
requires std::is_copy_constructible_v<T>
: m_value(value),
m_holdsOk(true) {}
Storage(T&& value)
requires std::is_move_constructible_v<T>
: m_value(std::forward<T>(value)),
m_holdsOk(true) {}
Storage(T const& value, std::monostate)
requires std::is_copy_constructible_v<T>
: m_value(value),
m_holdsOk(false) {}
Storage(T&& value, std::monostate)
requires std::is_move_constructible_v<T>
: m_value(std::forward<T>(value)),
m_holdsOk(false) {}
T const&& value() const&& noexcept {
return static_cast<T&&>(m_value);
}
};
}
template <class T = DefaultValue, class E = DefaultError>
class [[nodiscard]] Result final {
template <class T = impl::DefaultValue, class E = impl::DefaultError>
class [[nodiscard]] Result : public cpp::result<T, E> {
public:
using value_type = std::remove_reference_t<T>;
using error_type = std::remove_reference_t<E>;
using Base = cpp::result<T, E>;
using ValueType = typename Base::value_type;
using ErrorType = typename Base::error_type;
// 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!"
);
using Base::result;
protected:
Storage<value_type, error_type> m_value;
template <class U>
requires(cpp::detail::result_is_implicit_value_convertible<T, U>::value)
constexpr Result(U&& value) = delete;
public:
Storage<value_type, error_type> const& _Raw_Storage() const {
return m_value;
template <class E2>
requires(std::is_constructible_v<E, E2 const&>)
constexpr Result(impl::Failure<E2> const& e) : Base(cpp::failure<E>(e.error())) {}
template <class E2>
requires(std::is_constructible_v<E, E2 &&>)
constexpr Result(impl::Failure<E2>&& e) : Base(cpp::failure<E>(std::move(e.error()))) {}
template <class T2>
requires(std::is_constructible_v<T, T2 const&>)
constexpr Result(impl::Success<T2> const& s) : Base(s.value()) {}
template <class T2>
requires(std::is_constructible_v<T, T2 &&>)
constexpr Result(impl::Success<T2>&& s) : Base(std::move(s.value())) {}
[[nodiscard]] constexpr explicit operator bool() const noexcept {
return this->Base::operator bool();
}
bool isOk() const {
return m_value.holdsOk();
[[nodiscard]] constexpr bool isOk() const noexcept {
return this->Base::has_value();
}
bool isErr() const {
return !m_value.holdsOk();
[[nodiscard]] constexpr bool isErr() const noexcept {
return this->Base::has_error();
}
template<class ... Args>
Result<T, std::string> expect(const char* str, Args&&... args) {
if (isErr()) {
return Result<T, std::string>(fmt::format(
str,
std::forward<Args>(args)...,
fmt::arg("error", unwrapErr())
), std::monostate());
} else {
[[nodiscard]] constexpr decltype(auto) unwrap() & {
return this->Base::value();
}
[[nodiscard]] constexpr decltype(auto) unwrap() const& {
return this->Base::value();
}
[[nodiscard]] constexpr decltype(auto) unwrap() && {
return this->Base::value();
}
[[nodiscard]] constexpr decltype(auto) unwrap() const&& {
return this->Base::value();
}
[[nodiscard]] constexpr decltype(auto) unwrapErr() & {
return this->Base::error();
}
[[nodiscard]] constexpr decltype(auto) unwrapErr() const& {
return this->Base::error();
}
[[nodiscard]] constexpr decltype(auto) unwrapErr() && {
return this->Base::error();
}
[[nodiscard]] constexpr decltype(auto) unwrapErr() const&& {
return this->Base::error();
}
template <class... Args>
[[nodiscard]] Result<T, std::string> expect(std::string const& format, Args&&... args)
const {
if (this->isErr()) {
return impl::Failure<std::string>(fmt::format(
fmt::runtime(format), std::forward<Args>(args)...,
fmt::arg("error", this->unwrapErr())
));
}
else {
return *this;
}
}
explicit Result(value_type const& value)
requires std::is_copy_constructible_v<value_type>
: m_value(value) {}
explicit Result(value_type&& value)
requires std::is_move_constructible_v<value_type>
: m_value(std::forward<value_type>(value)) {}
explicit Result(error_type const& value, std::monostate)
requires std::is_copy_constructible_v<error_type>
: m_value(value, std::monostate()) {}
explicit Result(error_type&& value, std::monostate)
requires std::is_move_constructible_v<error_type>
: m_value(std::forward<error_type>(value), std::monostate()) {}
Result(Result<T, E> const& other)
requires
std::is_copy_constructible_v<value_type> &&
std::is_copy_constructible_v<error_type>
= default;
Result(Result<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
std::is_convertible_v<T2, T> &&
std::is_convertible_v<E2, E>
Result(Result<T2, E2> const& other)
requires
std::is_copy_constructible_v<value_type> &&
std::is_copy_constructible_v<error_type>
: m_value(other._Raw_Storage()) {}
template <class T2>
requires ConvertibleToResult<T2, T>
Result(Result<T2, AnyType> const& other)
requires
std::is_copy_constructible_v<value_type> &&
std::is_copy_constructible_v<error_type>
: Result(value_type(other.unwrap())) {}
template <class E2>
requires ConvertibleToResult<E2, E>
Result(Result<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()), std::monostate()) {}
template <class T2>
requires ConvertibleToResult<T2, T>
Result(Result<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>
Result(Result<AnyType, E2>&& other)
requires(
!std::is_copy_constructible_v<value_type> ||
!std::is_copy_constructible_v<error_type>
)
: Result(std::forward<error_type>(other.unwrapErr()), std::monostate()) {}
value_type unwrap() const requires std::is_copy_constructible_v<value_type> {
return m_value.getOk();
template <class U>
[[nodiscard]] constexpr decltype(auto) unwrapOr(U&& val) && {
return this->Base::value_or(std::forward<U>(val));
}
value_type&& unwrap() requires(!std::is_copy_constructible_v<value_type>) {
return std::forward<value_type>(m_value.getOk());
template <class U>
[[nodiscard]] constexpr decltype(auto) unwrapOr(U&& val) const& {
return this->Base::value_or(std::forward<U>(val));
}
error_type unwrapErr() const requires std::is_copy_constructible_v<error_type> {
return m_value.getErr();
template <class U>
[[nodiscard]] constexpr decltype(auto) errorOr(U&& val) && {
return this->Base::error_or(std::forward<U>(val));
}
error_type&& unwrapErr() requires(!std::is_copy_constructible_v<error_type>) {
return std::forward<error_type>(m_value.getErr());
}
explicit operator bool() const requires(
!std::is_same_v<T, bool> &&
!std::is_same_v<E, bool>
) {
return this->isOk();
template <class U>
[[nodiscard]] constexpr decltype(auto) errorOr(U&& val) const& {
return this->Base::error_or(std::forward<U>(val));
}
};
template <class T = DefaultValue, class E = AnyType>
requires std::is_copy_constructible_v<T>
Result<T, E> Ok(T value = T()) {
return Result<T, E>(value);
template <class T = impl::DefaultValue>
constexpr impl::Success<T> Ok() {
return impl::Success<T>();
}
template <class T = DefaultValue, class E = AnyType>
requires(!std::is_copy_constructible_v<T>)
Result<T, E> Ok(T&& value) {
return Result<T, E>(std::forward<T>(value));
template <class T>
constexpr impl::Success<T> Ok(T&& value) {
return impl::Success<T>(std::forward<T>(value));
}
template <class E = DefaultError, class T = AnyType>
requires std::is_copy_constructible_v<E>
Result<T, E> Err(E error) {
return Result<T, E>(error, std::monostate());
template <class E>
constexpr impl::Failure<E> Err(E&& error) {
return impl::Failure<E>(std::forward<E>(error));
}
template <class E = DefaultError, class T = AnyType>
requires(!std::is_copy_constructible_v<E>)
Result<T, E> Err(E&& error) {
return Result<T, E>(std::forward<E>(error), std::monostate());
template <class... Args>
inline impl::Failure<std::string> Err(std::string const& format, Args&&... args) {
return impl::Failure<std::string>(
fmt::format(fmt::runtime(format), std::forward<Args>(args)...)
);
}
#define GEODE_UNWRAP_INTO(into, ...) \
#define GEODE_UNWRAP_INTO(into, ...) \
auto GEODE_CONCAT(unwrap_res_, __LINE__) = (__VA_ARGS__); \
if (GEODE_CONCAT(unwrap_res_, __LINE__).isErr()) { \
return Err(std::move(GEODE_CONCAT(unwrap_res_, __LINE__).unwrapErr())); \
} \
into = std::move(GEODE_CONCAT(unwrap_res_, __LINE__).unwrap())
#define GEODE_UNWRAP(...) \
{ \
auto GEODE_CONCAT(unwrap_res_, __LINE__) = (__VA_ARGS__); \
if (GEODE_CONCAT(unwrap_res_, __LINE__).isErr()) { \
return Err(std::move(GEODE_CONCAT(unwrap_res_, __LINE__).unwrapErr())); \
} \
into = std::move(GEODE_CONCAT(unwrap_res_, __LINE__).unwrap())
#define GEODE_UNWRAP(...) \
{ \
auto GEODE_CONCAT(unwrap_res_, __LINE__) = (__VA_ARGS__); \
if (GEODE_CONCAT(unwrap_res_, __LINE__).isErr()) { \
return Err(std::move(GEODE_CONCAT(unwrap_res_, __LINE__).unwrapErr())); \
} \
}
}
}
// clang-format on

View file

@ -64,7 +64,6 @@ public:
};
// proxy forwards
// clang-format off
#include <Geode/modify/CCNode.hpp>
struct ProxyCCNode : Modify<ProxyCCNode, CCNode> {
virtual CCObject* getUserObject() {
@ -80,7 +79,10 @@ struct ProxyCCNode : Modify<ProxyCCNode, CCNode> {
}
};
// clang-format on
static inline std::unordered_map<size_t, size_t> s_nextIndex;
size_t modifier::getFieldIndexForClass(size_t hash) {
return s_nextIndex[hash]++;
}
// not const because might modify contents
FieldContainer* CCNode::getFieldContainer() {

View file

@ -58,6 +58,29 @@ bool InternalLoader::setup() {
return true;
}
bool InternalLoader::isReadyToHook() const {
return m_readyToHook;
}
void InternalLoader::addInternalHook(Hook* hook, Mod* mod) {
m_internalHooks.push_back({hook, mod});
}
bool InternalLoader::loadHooks() {
m_readyToHook = true;
auto thereWereErrors = false;
for (auto const& hook : m_internalHooks) {
auto res = hook.second->addHook(hook.first);
if (!res) {
log::log(Severity::Error, hook.second, "{}", res.unwrapErr());
thereWereErrors = true;
}
}
// free up memory
m_internalHooks.clear();
return !thereWereErrors;
}
void InternalLoader::queueInGDThread(ScheduledFunction func) {
std::lock_guard<std::mutex> lock(m_gdThreadMutex);
m_gdThreadQueue.push_back(func);

View file

@ -45,6 +45,9 @@ protected:
bool m_platformConsoleOpen = false;
std::unordered_set<std::string> m_shownInfoAlerts;
std::vector<std::pair<Hook*, Mod*>> m_internalHooks;
bool m_readyToHook;
void saveInfoAlerts(nlohmann::json& json);
void loadInfoAlerts(nlohmann::json& json);
@ -83,5 +86,8 @@ public:
bool verifyLoaderResources();
bool isReadyToHook() const;
void addInternalHook(Hook* hook, Mod* mod);
friend int geodeEntry(void* platformData);
};

View file

@ -12,33 +12,18 @@
USE_GEODE_NAMESPACE();
struct hook_info {
Hook* hook;
Mod* mod;
};
// for some reason this doesn't work as
// a normal static global. the vector just
// gets cleared for no reason somewhere
// between `addHook` and `loadHooks`
GEODE_STATIC_VAR(std::vector<hook_info>, internalHooks);
GEODE_STATIC_VAR(bool, readyToHook);
Result<> Mod::enableHook(Hook* hook) {
if (!hook->isEnabled()) {
auto res = std::invoke(hook->m_addFunction, hook->m_address);
Result<> Hook::enable() {
if (!m_enabled) {
auto res = std::invoke(m_addFunction, m_address);
if (res) {
log::debug("Enabling hook at function {}", hook->m_displayName);
this->m_hooks.push_back(hook);
hook->m_enabled = true;
hook->m_handle = res.unwrap();
log::debug("Enabling hook at function {}", m_displayName);
m_enabled = true;
m_handle = res.unwrap();
return Ok();
}
else {
return Err(
"Unable to create hook at " +
std::to_string(reinterpret_cast<uintptr_t>(hook->m_address))
"Unable to create hook at " + std::to_string(reinterpret_cast<uintptr_t>(m_address))
);
}
return Err("Hook already has a handle");
@ -46,56 +31,16 @@ Result<> Mod::enableHook(Hook* hook) {
return Ok();
}
Result<> Mod::disableHook(Hook* hook) {
if (hook->isEnabled()) {
if (geode::core::hook::remove(hook->m_handle)) {
log::debug("Disabling hook at function {}", hook->m_displayName);
hook->m_enabled = false;
return Ok();
}
return Err("Unable to remove hook");
Result<> Hook::disable() {
if (m_enabled) {
if (!geode::core::hook::remove(m_handle)) return Err("Unable to remove hook");
log::debug("Disabling hook at function {}", m_displayName);
m_enabled = false;
}
return Ok();
}
Result<> Mod::removeHook(Hook* hook) {
auto res = this->disableHook(hook);
if (res) {
ranges::remove(m_hooks, hook);
delete hook;
}
return res;
}
Result<Hook*> Mod::addHook(Hook* hook) {
if (readyToHook()) {
auto res = this->enableHook(hook);
if (!res) {
delete hook;
return Err("Can't create hook");
}
}
else {
internalHooks().push_back({ hook, this });
}
return Ok<Hook*>(hook);
}
bool InternalLoader::loadHooks() {
readyToHook() = true;
auto thereWereErrors = false;
for (auto const& hook : internalHooks()) {
auto res = hook.mod->addHook(hook.hook);
if (!res) {
log::log(Severity::Error, hook.mod, "{}", res.unwrapErr());
thereWereErrors = true;
}
}
// free up memory
internalHooks().clear();
return !thereWereErrors;
}
nlohmann::json Hook::getRuntimeInfo() const {
auto json = nlohmann::json::object();
json["address"] = reinterpret_cast<uintptr_t>(m_address);

View file

@ -22,6 +22,8 @@ std::string IndexSource::dirname() const {
return string::replace(this->repository, "/", "_");
}
// IndexUpdateEvent
IndexUpdateEvent::IndexUpdateEvent(
std::string const& src,
UpdateStatus status
@ -45,6 +47,34 @@ ListenerResult IndexUpdateFilter::handle(
IndexUpdateFilter::IndexUpdateFilter() {}
// ModInstallEvent
ModInstallEvent::ModInstallEvent(
std::string const& id,
UpdateStatus status
) : m_id(id), m_status(status) {}
std::string ModInstallEvent::getModID() const {
return m_id;
}
UpdateStatus ModInstallEvent::getStatus() const {
return m_status;
}
ListenerResult ModInstallFilter::handle(std::function<Callback> fn, ModInstallEvent* event) {
if (m_id == event->getModID()) {
fn(event);
}
return ListenerResult::Propagate;
}
ModInstallFilter::ModInstallFilter(
std::string const& id
) : m_id(id) {}
// Index
Index::Index() {
this->addSource("https://github.com/geode-sdk/index-test");
}
@ -81,8 +111,7 @@ bool Index::isUpToDate() const {
void Index::checkSourceUpdates(IndexSource& src) {
if (src.isUpToDate) {
IndexUpdateEvent(src.repository, UpdateFinished()).post();
return;
return this->updateSourceFromLocal(src);
}
IndexUpdateEvent(src.repository, UpdateProgress(0, "Checking status")).post();
auto data = Mod::get()->getSavedMutable<IndexSaveData>("index");
@ -176,10 +205,10 @@ void Index::updateSourceFromLocal(IndexSource& src) {
}
}
}
this->cleanupItems();
// todo: add shit
this->cleanupItems();
src.isUpToDate = true;
IndexUpdateEvent(src.repository, UpdateFinished()).post();
}

View file

@ -1,18 +1,14 @@
#include <Geode/cocos/support/zip_support/ZipUtils.h>
#include <Geode/loader/Hook.hpp>
#include <Geode/loader/Loader.hpp>
#include <Geode/loader/Dirs.hpp>
#include <Geode/loader/Log.hpp>
#include <Geode/loader/Mod.hpp>
#include <Geode/loader/Setting.hpp>
#include <Geode/utils/JsonValidation.hpp>
#include <Geode/utils/file.hpp>
#include <Geode/utils/map.hpp>
#include <Geode/utils/ranges.hpp>
#include <Geode/utils/string.hpp>
#include <InternalLoader.hpp>
#include <InternalMod.hpp>
#include <about.hpp>
#include <optional>
#include <string>
#include <vector>
USE_GEODE_NAMESPACE();
@ -26,338 +22,7 @@ Mod::~Mod() {
(void)this->unloadBinary();
}
Result<> Mod::loadData() {
ModStateEvent(this, ModEventType::DataLoaded).post();
// settings
// Check if settings exist
auto settPath = m_saveDirPath / "settings.json";
if (ghc::filesystem::exists(settPath)) {
GEODE_UNWRAP_INTO(auto settData, utils::file::readString(settPath));
try {
// parse settings.json
auto data = nlohmann::json::parse(settData);
JsonChecker checker(data);
auto root = checker.root("[settings.json]");
for (auto& [key, value] : root.items()) {
// check if this is a known setting
if (auto sett = this->getSetting(key)) {
// load its value
if (!sett->load(value.json())) {
return Err("Unable to load value for setting \"" + key + "\"");
}
}
else {
log::log(
Severity::Warning, this,
"Encountered unknown setting \"{}\" while loading "
"settings",
key
);
}
}
}
catch (std::exception& e) {
return Err(std::string("Unable to parse settings: ") + e.what());
}
}
// Saved values
auto savedPath = m_saveDirPath / "saved.json";
if (ghc::filesystem::exists(savedPath)) {
GEODE_UNWRAP_INTO(auto data, utils::file::readString(savedPath));
try {
m_saved = nlohmann::json::parse(data);
} catch(std::exception& e) {
return Err(std::string("Unable to parse saved values: ") + e.what());
}
}
return Ok();
}
Result<> Mod::saveData() {
ModStateEvent(this, ModEventType::DataSaved).post();
// settings
auto settPath = m_saveDirPath / "settings.json";
auto json = nlohmann::json::object();
for (auto& [key, sett] : m_info.m_settings) {
if (!sett->save(json[key])) {
return Err("Unable to save setting \"" + key + "\"");
}
}
auto sw = utils::file::writeString(settPath, json.dump(4));
if (!sw) {
return sw;
}
// Saved values
auto sdw = utils::file::writeString(
m_saveDirPath / "saved.json",
m_saved.dump(4)
);
if (!sdw) {
return sdw;
}
return Ok();
}
Result<> Mod::createTempDir() {
// Check if temp dir already exists
if (m_tempDirName.string().size()) {
return Ok();
}
// Create geode/temp
auto tempDir = dirs::getModRuntimeDir();
if (!file::createDirectoryAll(tempDir)) {
return Err("Unable to create mods' runtime directory");
}
// Create geode/temp/mod.id
auto tempPath = tempDir / m_info.m_id;
if (!file::createDirectoryAll(tempPath)) {
return Err("Unable to create mod runtime directory");
}
// Unzip .geode file into temp dir
GEODE_UNWRAP_INTO(auto unzip, file::Unzip::create(m_info.m_path));
if (!unzip.hasEntry(m_info.m_binaryName)) {
return Err(fmt::format(
"Unable to find platform binary under the name \"{}\"",
m_info.m_binaryName
));
}
GEODE_UNWRAP(unzip.extractAllTo(tempPath));
// Mark temp dir creation as succesful
m_tempDirName = tempPath;
return Ok();
}
Result<> Mod::loadBinary() {
if (m_binaryLoaded) {
return Ok();
}
GEODE_UNWRAP(this->createTempDir().expect("Unable to create temp directory"));
if (this->hasUnresolvedDependencies()) {
return Err("Mod has unresolved dependencies");
}
GEODE_UNWRAP(this->loadPlatformBinary());
// Call implicit entry point to place hooks etc.
m_implicitLoadFunc(this);
m_binaryLoaded = true;
ModStateEvent(this, ModEventType::Loaded).post();
auto loadRes = this->loadData();
if (!loadRes) {
log::warn("Unable to load data for \"{}\": {}", m_info.m_id, loadRes.unwrapErr());
}
Loader::get()->updateAllDependencies();
GEODE_UNWRAP(this->enable());
return Ok();
}
Result<> Mod::unloadBinary() {
if (!m_binaryLoaded) {
return Ok();
}
if (!m_info.m_supportsUnloading) {
return Err("Mod does not support unloading");
}
auto saveRes = this->saveData();
if (!saveRes) {
return saveRes;
}
GEODE_UNWRAP(this->disable());
ModStateEvent(this, ModEventType::Unloaded).post();
// Disabling unhooks and unpatches already
for (auto const& hook : m_hooks) {
delete hook;
}
m_hooks.clear();
for (auto const& patch : m_patches) {
delete patch;
}
m_patches.clear();
auto res = this->unloadPlatformBinary();
if (!res) {
return res;
}
m_binaryLoaded = false;
Loader::get()->updateAllDependencies();
return Ok();
}
Result<> Mod::enable() {
if (!m_binaryLoaded) {
return this->loadBinary();
}
for (auto const& hook : m_hooks) {
auto d = this->enableHook(hook);
if (!d) return d;
}
for (auto const& patch : m_patches) {
if (!patch->apply()) {
return Err("Unable to apply patch at " + std::to_string(patch->getAddress()));
}
}
ModStateEvent(this, ModEventType::Enabled).post();
m_enabled = true;
return Ok();
}
Result<> Mod::disable() {
if (!m_enabled) {
return Ok();
}
if (!m_info.m_supportsDisabling) {
return Err("Mod does not support disabling");
}
ModStateEvent(this, ModEventType::Disabled).post();
for (auto const& hook : m_hooks) {
auto d = this->disableHook(hook);
if (!d) return d;
}
for (auto const& patch : m_patches) {
if (!patch->restore()) {
return Err("Unable to restore patch at " + std::to_string(patch->getAddress()));
}
}
m_enabled = false;
return Ok();
}
Result<> Mod::uninstall() {
if (m_info.m_supportsDisabling) {
GEODE_UNWRAP(this->disable());
if (m_info.m_supportsUnloading) {
GEODE_UNWRAP(this->unloadBinary());
}
}
if (!ghc::filesystem::remove(m_info.m_path)) {
return Err(
"Unable to delete mod's .geode file! "
"This might be due to insufficient permissions - "
"try running GD as administrator."
);
}
return Ok();
}
bool Mod::isUninstalled() const {
return this != InternalMod::get() && !ghc::filesystem::exists(m_info.m_path);
}
bool Dependency::isUnresolved() const {
return m_required &&
(m_state == ModResolveState::Unloaded || m_state == ModResolveState::Unresolved ||
m_state == ModResolveState::Disabled);
}
bool Mod::updateDependencyStates() {
bool hasUnresolved = false;
for (auto& dep : m_info.m_dependencies) {
if (!dep.m_mod) {
dep.m_mod = Loader::get()->getLoadedMod(dep.m_id);
}
if (dep.m_mod) {
dep.m_mod->updateDependencyStates();
if (dep.m_mod->hasUnresolvedDependencies()) {
dep.m_state = ModResolveState::Unresolved;
}
else {
if (!dep.m_mod->m_resolved) {
dep.m_mod->m_resolved = true;
dep.m_state = ModResolveState::Resolved;
auto r = dep.m_mod->loadBinary();
if (!r) {
dep.m_state = ModResolveState::Unloaded;
log::log(Severity::Error, dep.m_mod, "{}", r.unwrapErr());
}
}
else {
if (dep.m_mod->isEnabled()) {
dep.m_state = ModResolveState::Loaded;
}
else {
dep.m_state = ModResolveState::Disabled;
}
}
}
}
else {
dep.m_state = ModResolveState::Unloaded;
}
if (dep.isUnresolved()) {
m_resolved = false;
(void)this->unloadBinary();
hasUnresolved = true;
}
}
if (!hasUnresolved && !m_resolved) {
log::debug("All dependencies for {} found", m_info.m_id);
m_resolved = true;
if (m_enabled) {
log::debug("Resolved & loading {}", m_info.m_id);
auto r = this->loadBinary();
if (!r) {
log::error("{} Error loading: {}", this, r.unwrapErr());
}
}
else {
log::debug("Resolved {}, however not loading it as it is disabled", m_info.m_id);
}
}
return hasUnresolved;
}
bool Mod::hasUnresolvedDependencies() const {
for (auto const& dep : m_info.m_dependencies) {
if (dep.isUnresolved()) {
return true;
}
}
return false;
}
std::vector<Dependency> Mod::getUnresolvedDependencies() {
std::vector<Dependency> res;
for (auto const& dep : m_info.m_dependencies) {
if (dep.isUnresolved()) {
res.push_back(dep);
}
}
return res;
}
// Getters
ghc::filesystem::path Mod::getSaveDir() const {
return m_saveDirPath;
@ -399,14 +64,6 @@ ghc::filesystem::path Mod::getPackagePath() const {
return m_info.m_path;
}
ghc::filesystem::path Mod::getConfigDir(bool create) const {
auto dir = dirs::getModConfigDir() / m_info.m_id;
if (create && !ghc::filesystem::exists(dir)) {
ghc::filesystem::create_directories(dir);
}
return dir;
}
VersionInfo Mod::getVersion() const {
return m_info.m_version;
}
@ -435,24 +92,6 @@ std::vector<Hook*> Mod::getHooks() const {
return m_hooks;
}
bool Mod::depends(std::string const& id) const {
return utils::ranges::contains(m_info.m_dependencies, [id](Dependency const& t) {
return t.m_id == id;
});
}
const char* Mod::expandSpriteName(const char* name) {
static std::unordered_map<std::string, const char*> expanded = {};
if (expanded.count(name)) {
return expanded[name];
}
auto exp = new char[strlen(name) + 2 + m_info.m_id.size()];
auto exps = m_info.m_id + "/" + name;
memcpy(exp, exps.c_str(), exps.size() + 1);
expanded[name] = exp;
return exp;
}
bool Mod::hasSettings() const {
return m_info.m_settings.size();
}
@ -461,24 +100,425 @@ decltype(ModInfo::m_settings) Mod::getSettings() const {
return m_info.m_settings;
}
// Settings and saved values
Result<> Mod::loadData() {
ModStateEvent(this, ModEventType::DataLoaded).post();
// Settings
// Check if settings exist
auto settingPath = m_saveDirPath / "settings.json";
if (ghc::filesystem::exists(settingPath)) {
GEODE_UNWRAP_INTO(auto settingData, utils::file::readString(settingPath));
try {
// parse settings.json
auto json = nlohmann::json::parse(settingData);
JsonChecker checker(json);
auto root = checker.root("[settings.json]");
for (auto& [key, value] : root.items()) {
// check if this is a known setting
if (auto setting = this->getSetting(key)) {
// load its value
if (!setting->load(value.json()))
return Err("Unable to load value for setting \"" + key + "\"");
}
else {
log::log(
Severity::Warning, this,
"Encountered unknown setting \"{}\" while loading "
"settings",
key
);
}
}
}
catch (std::exception& e) {
return Err(std::string("Unable to parse settings: ") + e.what());
}
}
// Saved values
auto savedPath = m_saveDirPath / "saved.json";
if (ghc::filesystem::exists(savedPath)) {
GEODE_UNWRAP_INTO(auto data, utils::file::readString(savedPath));
try {
m_saved = nlohmann::json::parse(data);
}
catch (std::exception& e) {
return Err(std::string("Unable to parse saved values: ") + e.what());
}
}
return Ok();
}
Result<> Mod::saveData() {
ModStateEvent(this, ModEventType::DataSaved).post();
// Settings
auto json = nlohmann::json::object();
for (auto& [key, value] : m_info.m_settings) {
if (!value->save(json[key])) return Err("Unable to save setting \"" + key + "\"");
}
GEODE_UNWRAP(utils::file::writeString(m_saveDirPath / "settings.json", json.dump(4)));
// Saved values
GEODE_UNWRAP(utils::file::writeString(m_saveDirPath / "saved.json", m_saved.dump(4)));
return Ok();
}
std::shared_ptr<Setting> Mod::getSetting(std::string const& key) const {
for (auto& sett : m_info.m_settings) {
if (sett.first == key) {
return sett.second;
for (auto& setting : m_info.m_settings) {
if (setting.first == key) {
return setting.second;
}
}
return nullptr;
}
bool Mod::hasSetting(std::string const& key) const {
for (auto& sett : m_info.m_settings) {
if (sett.first == key) {
for (auto& setting : m_info.m_settings) {
if (setting.first == key) {
return true;
}
}
return false;
}
// Loading, Toggling, Installing
Result<> Mod::loadBinary() {
if (!m_binaryLoaded) {
GEODE_UNWRAP(this->createTempDir().expect("Unable to create temp directory"));
if (this->hasUnresolvedDependencies()) return Err("Mod has unresolved dependencies");
GEODE_UNWRAP(this->loadPlatformBinary());
m_binaryLoaded = true;
// Call implicit entry point to place hooks etc.
m_implicitLoadFunc(this);
ModStateEvent(this, ModEventType::Loaded).post();
auto loadRes = this->loadData();
if (!loadRes) {
log::warn("Unable to load data for \"{}\": {}", m_info.m_id, loadRes.unwrapErr());
}
Loader::get()->updateAllDependencies();
GEODE_UNWRAP(this->enable());
}
return Ok();
}
Result<> Mod::unloadBinary() {
if (m_binaryLoaded) {
if (!m_info.m_supportsUnloading) return Err("Mod does not support unloading");
GEODE_UNWRAP(this->saveData());
GEODE_UNWRAP(this->disable());
ModStateEvent(this, ModEventType::Unloaded).post();
// Disabling unhooks and unpatches already
for (auto const& hook : m_hooks) {
delete hook;
}
m_hooks.clear();
for (auto const& patch : m_patches) {
delete patch;
}
m_patches.clear();
GEODE_UNWRAP(this->unloadPlatformBinary());
m_binaryLoaded = false;
Loader::get()->updateAllDependencies();
}
return Ok();
}
Result<> Mod::enable() {
if (!m_binaryLoaded) return this->loadBinary();
for (auto const& hook : m_hooks) {
GEODE_UNWRAP(this->enableHook(hook));
}
for (auto const& patch : m_patches) {
if (!patch->apply())
return Err("Unable to apply patch at " + std::to_string(patch->getAddress()));
}
ModStateEvent(this, ModEventType::Enabled).post();
m_enabled = true;
return Ok();
}
Result<> Mod::disable() {
if (m_enabled) {
if (!m_info.m_supportsDisabling) return Err("Mod does not support disabling");
ModStateEvent(this, ModEventType::Disabled).post();
for (auto const& hook : m_hooks) {
GEODE_UNWRAP(this->disableHook(hook));
}
for (auto const& patch : m_patches) {
if (!patch->restore())
return Err("Unable to restore patch at " + std::to_string(patch->getAddress()));
}
m_enabled = false;
}
return Ok();
}
Result<> Mod::uninstall() {
if (m_info.m_supportsDisabling) {
GEODE_UNWRAP(this->disable());
if (m_info.m_supportsUnloading) GEODE_UNWRAP(this->unloadBinary());
}
if (!ghc::filesystem::remove(m_info.m_path)) {
return Err(
"Unable to delete mod's .geode file! "
"This might be due to insufficient permissions - "
"try running GD as administrator."
);
}
return Ok();
}
bool Mod::isUninstalled() const {
return this != InternalMod::get() && !ghc::filesystem::exists(m_info.m_path);
}
// Dependencies
bool Dependency::isUnresolved() const {
return m_required &&
(m_state == ModResolveState::Unloaded || m_state == ModResolveState::Unresolved ||
m_state == ModResolveState::Disabled);
}
bool Mod::updateDependencyStates() {
bool hasUnresolved = false;
for (auto& dep : m_info.m_dependencies) {
if (!dep.m_mod) dep.m_mod = Loader::get()->getLoadedMod(dep.m_id);
if (dep.m_mod) {
dep.m_mod->updateDependencyStates();
if (dep.m_mod->hasUnresolvedDependencies()) {
dep.m_state = ModResolveState::Unresolved;
}
else {
if (!dep.m_mod->m_resolved) {
dep.m_mod->m_resolved = true;
dep.m_state = ModResolveState::Resolved;
auto r = dep.m_mod->loadBinary();
if (!r) {
dep.m_state = ModResolveState::Unloaded;
log::log(Severity::Error, dep.m_mod, "{}", r.unwrapErr());
}
}
else {
if (dep.m_mod->isEnabled()) {
dep.m_state = ModResolveState::Loaded;
}
else {
dep.m_state = ModResolveState::Disabled;
}
}
}
}
else {
dep.m_state = ModResolveState::Unloaded;
}
if (dep.isUnresolved()) {
m_resolved = false;
(void)this->unloadBinary();
hasUnresolved = true;
}
}
if (!hasUnresolved && !m_resolved) {
log::debug("All dependencies for {} found", m_info.m_id);
m_resolved = true;
if (m_enabled) {
log::debug("Resolved & loading {}", m_info.m_id);
auto r = this->loadBinary();
if (!r) {
log::error("{} Error loading: {}", this, r.unwrapErr());
}
}
else {
log::debug("Resolved {}, however not loading it as it is disabled", m_info.m_id);
}
}
return hasUnresolved;
}
bool Mod::hasUnresolvedDependencies() const {
for (auto const& dep : m_info.m_dependencies) {
if (dep.isUnresolved()) return true;
}
return false;
}
std::vector<Dependency> Mod::getUnresolvedDependencies() {
std::vector<Dependency> unresolved;
for (auto const& dep : m_info.m_dependencies) {
if (dep.isUnresolved()) unresolved.push_back(dep);
}
return unresolved;
}
bool Mod::depends(std::string const& id) const {
return utils::ranges::contains(m_info.m_dependencies, [id](Dependency const& t) {
return t.m_id == id;
});
}
// Hooks
Result<> Mod::enableHook(Hook* hook) {
auto res = hook->enable();
if (res) m_hooks.push_back(hook);
return res;
}
Result<> Mod::disableHook(Hook* hook) {
return hook->disable();
}
Result<Hook*> Mod::addHook(Hook* hook) {
if (InternalLoader::get()->isReadyToHook()) {
auto res = this->enableHook(hook);
if (!res) {
delete hook;
return Err("Can't create hook");
}
}
else {
InternalLoader::get()->addInternalHook(hook, this);
}
return Ok(hook);
}
Result<> Mod::removeHook(Hook* hook) {
auto res = this->disableHook(hook);
if (res) {
ranges::remove(m_hooks, hook);
delete hook;
}
return res;
}
// Patches
byte_array readMemory(void* address, size_t amount) {
byte_array ret;
for (size_t i = 0; i < amount; i++) {
ret.push_back(*reinterpret_cast<uint8_t*>(reinterpret_cast<uintptr_t>(address) + i));
}
return ret;
}
Result<Patch*> Mod::patch(void* address, byte_array data) {
auto p = new Patch;
p->m_address = address;
p->m_original = readMemory(address, data.size());
p->m_owner = this;
p->m_patch = data;
if (!p->apply()) {
delete p;
return Err("Unable to enable patch at " + std::to_string(p->getAddress()));
}
m_patches.push_back(p);
return Ok(p);
}
Result<> Mod::unpatch(Patch* patch) {
if (patch->restore()) {
ranges::remove(m_patches, patch);
delete patch;
return Ok();
}
return Err("Unable to restore patch!");
}
// Misc.
Result<> Mod::createTempDir() {
// Check if temp dir already exists
if (!m_tempDirName.string().empty()) {
return Ok();
}
// Create geode/temp
auto tempDir = dirs::getModRuntimeDir();
if (!file::createDirectoryAll(tempDir)) {
return Err("Unable to create mods' runtime directory");
}
// Create geode/temp/mod.id
auto tempPath = tempDir / m_info.m_id;
if (!file::createDirectoryAll(tempPath)) {
return Err("Unable to create mod runtime directory");
}
// Unzip .geode file into temp dir
GEODE_UNWRAP_INTO(auto unzip, file::Unzip::create(m_info.m_path));
if (!unzip.hasEntry(m_info.m_binaryName)) {
return Err(fmt::format(
"Unable to find platform binary under the name \"{}\"", m_info.m_binaryName
));
}
GEODE_UNWRAP(unzip.extractAllTo(tempPath));
// Mark temp dir creation as succesful
m_tempDirName = tempPath;
return Ok();
}
ghc::filesystem::path Mod::getConfigDir(bool create) const {
auto dir = dirs::getModConfigDir() / m_info.m_id;
if (create) {
(void)file::createDirectoryAll(dir);
}
return dir;
}
char const* Mod::expandSpriteName(char const* name) {
static std::unordered_map<std::string, char const*> expanded = {};
if (expanded.count(name)) return expanded[name];
auto exp = new char[strlen(name) + 2 + m_info.m_id.size()];
auto exps = m_info.m_id + "/" + name;
memcpy(exp, exps.c_str(), exps.size() + 1);
expanded[name] = exp;
return exp;
}
ModJson Mod::getRuntimeInfo() const {
auto json = m_info.toJSON();

View file

@ -123,7 +123,7 @@ Result<ModInfo> ModInfo::create(ModJson const& json) {
if (schema < Loader::minModVersion()) {
return Err(
"[mod.json] is built for an older version (" + schema.toString() +
") of Geode (current: " + Loader::minModVersion().toString() +
") of Geode (current: " + Loader::getVersion().toString() +
"). Please update the mod to the latest version, "
"and if the problem persists, contact the developer "
"to update it."
@ -132,7 +132,7 @@ Result<ModInfo> ModInfo::create(ModJson const& json) {
if (schema > Loader::maxModVersion()) {
return Err(
"[mod.json] is built for a newer version (" + schema.toString() +
") of Geode (current: " + Loader::maxModVersion().toString() +
") of Geode (current: " + Loader::getVersion().toString() +
"). You need to update Geode in order to use "
"this mod."
);

View file

@ -1,45 +1,8 @@
#include <Geode/loader/Hook.hpp>
#include <Geode/loader/Loader.hpp>
#include <Geode/loader/Mod.hpp>
#include <Geode/utils/casts.hpp>
#include <Geode/utils/ranges.hpp>
#include <InternalLoader.hpp>
#include <lilac/include/geode/core/hook/hook.hpp>
#include <vector>
USE_GEODE_NAMESPACE();
byte_array readMemory(void* address, size_t amount) {
byte_array ret;
for (size_t i = 0; i < amount; i++) {
ret.push_back(*reinterpret_cast<uint8_t*>(reinterpret_cast<uintptr_t>(address) + i));
}
return ret;
}
Result<Patch*> Mod::patch(void* address, byte_array data) {
auto p = new Patch;
p->m_address = address;
p->m_original = readMemory(address, data.size());
p->m_owner = this;
p->m_patch = data;
if (!p->apply()) {
delete p;
return Err("Unable to enable patch at " + std::to_string(p->getAddress()));
}
m_patches.push_back(p);
return Ok<Patch*>(p);
}
Result<> Mod::unpatch(Patch* patch) {
if (patch->restore()) {
ranges::remove(m_patches, patch);
delete patch;
return Ok();
}
return Err("Unable to restore patch!");
}
bool Patch::apply() {
return lilac::hook::write_memory(m_address, m_patch.data(), m_patch.size());
}

View file

@ -5,10 +5,6 @@
#include <cocos2d.h>
using namespace cocos2d;
void CCFileUtils::removeAllPaths() {
m_searchPathArray.clear();
}
CCObject* CCObject::copy()
{
return copyWithZone(0);

View file

@ -4,6 +4,7 @@
#include <Geode/binding/FLAlertLayerProtocol.hpp>
#include <Geode/ui/MDTextArea.hpp>
#include <Geode/ui/Scrollbar.hpp>
#include <Geode/ui/IconButtonSprite.hpp>
#include <Geode/loader/Index.hpp>
USE_GEODE_NAMESPACE();

View file

@ -1,20 +1,19 @@
#include <Geode/binding/LoadingCircle.hpp>
#include <Geode/ui/Notification.hpp>
#include <Geode/loader/Mod.hpp>
#include <Geode/ui/Notification.hpp>
USE_GEODE_NAMESPACE();
constexpr auto NOTIFICATION_FADEIN = .3f;
constexpr auto NOTIFICATION_FADEIN = .3f;
constexpr auto NOTIFICATION_FADEOUT = 1.f;
Ref<CCArray> Notification::s_queue = CCArray::create();
bool Notification::init(std::string const& text, CCSprite* icon, float time) {
if (!CCNodeRGBA::init())
return false;
if (!CCNodeRGBA::init()) return false;
m_time = time;
m_bg = CCScale9Sprite::create("square02b_small.png", { 0, 0, 40, 40 });
m_bg->setColor({ 0, 0, 0 });
this->addChild(m_bg);
@ -48,7 +47,8 @@ void Notification::updateLayout() {
if (m_icon) {
m_icon->setPosition({ size.height / 2, size.height / 2 });
m_label->setPosition(size / 2 + CCSize { spaceForIcon / 2, .0f });
} else {
}
else {
m_label->setPosition(size / 2);
}
}
@ -73,9 +73,7 @@ CCSprite* Notification::createIcon(NotificationIcon icon) {
case NotificationIcon::Loading: {
auto icon = CCSprite::create("loadingCircle.png");
icon->runAction(CCRepeatForever::create(
CCRotateBy::create(1.f, 360.f)
));
icon->runAction(CCRepeatForever::create(CCRotateBy::create(1.f, 360.f)));
icon->setBlendFunc({ GL_ONE, GL_ONE });
return icon;
} break;
@ -94,11 +92,7 @@ CCSprite* Notification::createIcon(NotificationIcon icon) {
}
}
Notification* Notification::create(
std::string const& text,
NotificationIcon icon,
float time
) {
Notification* Notification::create(std::string const& text, NotificationIcon icon, float time) {
return Notification::create(text, createIcon(icon), time);
}
@ -171,8 +165,7 @@ void Notification::show() {
CCCallFunc::create(this, callfunc_selector(Notification::animateIn)),
// wait for fade-in to finish
CCDelayTime::create(NOTIFICATION_FADEIN),
CCCallFunc::create(this, callfunc_selector(Notification::wait)),
nullptr
CCCallFunc::create(this, callfunc_selector(Notification::wait)), nullptr
));
}
@ -181,8 +174,7 @@ void Notification::wait() {
if (m_time) {
this->runAction(CCSequence::create(
CCDelayTime::create(m_time),
CCCallFunc::create(this, callfunc_selector(Notification::hide)),
nullptr
CCCallFunc::create(this, callfunc_selector(Notification::hide)), nullptr
));
}
}
@ -193,8 +185,6 @@ void Notification::hide() {
CCCallFunc::create(this, callfunc_selector(Notification::animateOut)),
// wait for fade-out to finish
CCDelayTime::create(NOTIFICATION_FADEOUT),
CCCallFunc::create(this, callfunc_selector(Notification::showNextNotification)),
nullptr
CCCallFunc::create(this, callfunc_selector(Notification::showNextNotification)), nullptr
));
}

View file

@ -1,5 +1,5 @@
#include <Geode/utils/cocos.hpp>
#include <Geode/modify/LoadingLayer.hpp>
#include <Geode/utils/cocos.hpp>
USE_GEODE_NAMESPACE();
@ -346,7 +346,7 @@ void geode::cocos::reloadTextures(CreateLayerFunc returnTo) {
GameManager::get()->reloadAll(false, false, true);
}
class $modify(LoadingLayer) {
struct LoadingFinished : Modify<LoadingFinished, LoadingLayer> {
void loadAssets() {
// loadFinished is inlined on Macchew OS :sob:

View file

@ -1,11 +1,11 @@
#include <Geode/utils/file.hpp>
#include <Geode/utils/string.hpp>
#include <Geode/utils/map.hpp>
#include <Geode/loader/Log.hpp>
#include <fstream>
#include <../support/zip_support/ZipUtils.h>
#include <../support/zip_support/ioapi.h>
#include <../support/zip_support/unzip.h>
#include <Geode/loader/Log.hpp>
#include <Geode/utils/file.hpp>
#include <Geode/utils/map.hpp>
#include <Geode/utils/string.hpp>
#include <fstream>
USE_GEODE_NAMESPACE();
using namespace geode::utils::file;
@ -140,9 +140,7 @@ public:
unz_file_info64 fileInfo;
// Read first file
if (unzGoToFirstFile64(
m_zip, &fileInfo, fileName, sizeof(fileName) - 1
)) {
if (unzGoToFirstFile64(m_zip, &fileInfo, fileName, sizeof(fileName) - 1)) {
return false;
}
// Loop over all files
@ -150,18 +148,15 @@ public:
// Read file and add to entries
unz_file_pos pos;
if (unzGetFilePos(m_zip, &pos) == UNZ_OK) {
m_entries.insert({
fileName, ZipEntry {
.m_pos = pos,
.m_compressedSize = fileInfo.compressed_size,
.m_uncompressedSize = fileInfo.uncompressed_size,
}
});
m_entries.insert({ fileName,
ZipEntry {
.m_pos = pos,
.m_compressedSize = fileInfo.compressed_size,
.m_uncompressedSize = fileInfo.uncompressed_size,
} });
}
// Read next file, or break on error
if (unzGoToNextFile64(
m_zip, &fileInfo, fileName, sizeof(fileName) - 1
) != UNZ_OK) {
if (unzGoToNextFile64(m_zip, &fileInfo, fileName, sizeof(fileName) - 1) != UNZ_OK) {
break;
}
}
@ -201,6 +196,7 @@ public:
}
UnzipImpl(unzFile zip, Path const& path) : m_zip(zip), m_zipPath(path) {}
~UnzipImpl() {
unzClose(m_zip);
}