Conversation
I'm not sure about the methods being throw by default. If you really reduced the find/run calls, then having
Delegates being removed too is a bit risky as custom-types isn't supported by default and, as far as I know, does not require xrefs in bs-hooks to support unlike custom-types.
The result type was confusing to use in its original design but if we moved to something more like
I would also like to add
You would likely need to rebase this PR onto my draft flamingo work since the hooking API is going to be different moving forward, especially given we will be allowing uninstalls too. The Other things we could explore: (if not already explored/discussed)
|
sc2ad
left a comment
There was a problem hiding this comment.
Haven't made it to the meat yet, I'll leave these comments for now as I go slep
| /// This is VERY DANGEROUS (and NOT THREAD SAFE!) and may cause all sorts of race conditions. Use at your own risk. | ||
| /// @param size The size to allocate the unsafe object with. | ||
| /// @return The returned GC-allocated instance. | ||
| [[deprecated("DO NOT USE")]] void* __allocate_unsafe(std::size_t size); |
There was a problem hiding this comment.
I think this was only done for delegates, so maybe can be killed? It might be used in some form for pinned allocations tho
| struct func_wrapper<R (*)(TArgs...)> : abstract_func<R(void*, TArgs...)> { | ||
| func_wrapper(auto&& f) : held(f) {} | ||
|
|
||
| R operator()(TArgs... args) const noexcept override { |
| void* data; | ||
| }; | ||
| dat d{.wrapper = held}; | ||
| return d.data; |
There was a problem hiding this comment.
nit: can use bitcast here instead of union trick
|
|
||
| template <typename R, typename T, typename... TArgs> | ||
| bool operator<(abstract_func<R(T*, TArgs...)> const& a, abstract_func<R(T*, TArgs...)> const& b) { | ||
| return a.ptr() < b.ptr(); |
There was a problem hiding this comment.
Is this for ordered sets? This operation is inherently wack without conversion to an int anyways (since tag byte of pointer could ruin the pointer comparison)
| #define CONFIG_PATH_FORMAT "/sdcard/ModData/{}/Configs/" | ||
| #endif | ||
|
|
||
| #define BS_HOOK_HIDDEN __attribute__((visibility("hidden"))) |
There was a problem hiding this comment.
I don't really know why these need to be bs-hook scoped (could be an old scad mistake) unless they are also conditioned on ifndef
| @@ -0,0 +1,37 @@ | |||
| #pragma once | |||
There was a problem hiding this comment.
This file feels more like "type debug" rather than strictly "debug"
| struct BS_HOOK_HIDDEN no_arg_type { | ||
| static inline Il2CppType const* get() { return &no_arg_class<T>::get()->byval_arg; } | ||
| static inline Il2CppType const* get() { | ||
| auto klass = no_arg_class<T>::get(); |
There was a problem hiding this comment.
is there any reason we don't store this in static storage? It would increase binary size but it'd be a useful optimization
There was a problem hiding this comment.
It's because the return value of no_arg_class::get() should always already be in static storage, and I don't think caching the pointer access would be a useful optimization but correct me if I'm wrong
| #include "types.hpp" | ||
|
|
||
| template <size_t S, i2c::str_lit Namespace, i2c::str_lit Name> | ||
| struct ValueW { |
There was a problem hiding this comment.
it'd be nice to add docs explaining what this type is or is used for
There was a problem hiding this comment.
ditto, is this meant to wrap an arbitrary object? If so, using an array of bytes is fundamentally flawed. Is it meant to wrap an arbitrary value type? In which case an array of bytes of proper storage size CAN be legal, but is still strange. If it's meant to "look" like the provided object, like for my hacky manual override, it's a scary concept but could work (though the size needs to be very carefully selected)
There was a problem hiding this comment.
Added some docs, but basically the second/third option (not sure what the distinction is). Still open for discussion, but the main case I imagine for this is for a value type as a parameter to a hook or function where the data is entirely or almost entirely unused.
| // Creates all directories for a provided file path | ||
| // Ex: /sdcard/Android/data/something/files/libs/ | ||
| int mkpath(std::string_view file_path); | ||
| // Reads all of the text of a file at the given filename. If the file does not exist, returns an empty string. |
There was a problem hiding this comment.
do we need to provide these filesystem utilities? Unless the std library does not offer a cross-platform solution I'm not sure this is necesary
There was a problem hiding this comment.
I just kept them around because they can be more convenient than std in some cases, but they could definitely be removed
sc2ad
left a comment
There was a problem hiding this comment.
Made it to the tests, but I'm going to bed as well. Rest will be reviewed in due time (I hope)
| }; | ||
|
|
||
| // template <typename T> | ||
| // struct BS_HOOK_HIDDEN arg_type<T&> { |
There was a problem hiding this comment.
w/o this, what is the intended way of resolving an object passed as a parameter to its actual type?
Ex, w/o cordl, if I use Il2CppObject* to hold some object B which inherits A, I want to ensure that I overload resolve using B instead of A or object.
There was a problem hiding this comment.
This case is handled in extract_type (now in the same file) and find_class_info and is explicitly only done for Il2CppObject*, with the reasoning that if a more specific type is used, it should be accurate.
It can lead to a slightly strange case though where you cast from A to Il2CppObject* in order to get B because you don't know exactly what B is - not sure how to improve this.
| #include "types.hpp" | ||
|
|
||
| template <size_t S, i2c::str_lit Namespace, i2c::str_lit Name> | ||
| struct ValueW { |
There was a problem hiding this comment.
ditto, is this meant to wrap an arbitrary object? If so, using an array of bytes is fundamentally flawed. Is it meant to wrap an arbitrary value type? In which case an array of bytes of proper storage size CAN be legal, but is still strange. If it's meant to "look" like the provided object, like for my hacky manual override, it's a scary concept but could work (though the size needs to be very carefully selected)
Changes:
utilsanywayil2cpp_utilstoi2cfor faster typingArrayWMAKE_HOOKandMAKE_HOOK_MATCHMAKE_HOOK_MATCHis unchanged, except it now resolves overloaded functions automaticallyMAKE_HOOKuses a parenthesized list in its second parameter to allow the functionality of all previous macros, except those with compile time type checking (just useMAKE_HOOK_MATCH)find_x_infostructs with overloaded constructorsfindandget/set/runcalls in the same expressionfindandruncallsmethodPointerinvokes, instead ofruntime_invokeresultoption if desired, by detecting it as the return type instead of doubling the number of methods)SafePtrUnitywith a switch insafe_ptr(automatically set when using cordl)ListWfor useArrayWandStringWConfigurationstruct (config file path is still available)arg_classandarg_typeResulttype (see note at end of methods API changes)Still TODO:
Sorry about the un-reviewability btw, but between the formatting changes, code moving, file renaming, snake casing, and simply the only way I could efficiently manage to actually get this code written, it kinda had to be this way. Hopefully the list above helps.