ByNameModding / BNM-Android

Modding il2cpp games by classes, methods, fields names on Android.
MIT License
200 stars 38 forks source link

how i can get enum? #50

Closed Lsc0x80 closed 1 day ago

Lsc0x80 commented 1 month ago

i need pass enum to function i think. Because all other types for TouchScreenKeyboard::Open is matching

Creator1A commented 1 month ago

Passing type as int should work. You can also just rewrite the original enum and pass it

Lsc0x80 commented 1 month ago

this crashes

// ...
        if (ImGui::Button("Change")){
            // BNM::CreateMonoString
            // Mono::String::Create
            Mono::String *text_placeholder = BNM::CreateMonoString("b");
            LOGI("text_placeholder %p hash: %d", text_placeholder, text_placeholder->GetHash());
            Mono::String *text_ = BNM::CreateMonoString("a");
            LOGI("text_ %p hash: %d", text_, text_->GetHash());
            void *kb = offsets::TSKeyboard::Open(text_, (int32_t)0,
                    (bool)1, (bool)0, (bool)0, (bool)0,
                    text_placeholder, (int32_t)0);

            LOGI("CALLED OPEN()");
            /*
            text___->Destroy();
            text_placeholder->Destroy();
            LOGI("STRINGS DESTROYED");*/
        }
// ...
// ...
namespace offsets {
    bool inited = 0;
    // TouchScreenKeyboard
    BNM::Class TSKeyboard_c{};

    namespace TSKeyboard {

        // Static
        /* static TouchScreenKeyboard Open(string text, TouchScreenKeyboardType keyboardType,
            bool autocorrection, bool multiline, bool secure, bool alert,
            string textPlaceholder, int characterLimit)
        */
        //BNM::Method<BNM::IL2CPP::Il2CppObject *> Open{};
        BNM::Method<void *> Open{};

        BNM::Method<bool> isSupported{};
    }

    void Init() {
        TSKeyboard_c = BNM::Class("UnityEngine", "TouchScreenKeyboard");
        TSKeyboard::Open = TSKeyboard_c.GetMethod("Open", 8);
        TSKeyboard::isSupported = TSKeyboard_c.GetMethod("get_isSupported", 0);

        inited = 1;
    }
}
// ...
EduModsS commented 1 month ago

Hey, Lsc0x80, Did you manage to work with TouchScreenInput->Open() ?

I'm trying to make this work too, to use the keyboard on Imgui, but without success

BNM-Dev commented 1 month ago

this crashes

// ...
        if (ImGui::Button("Change")){
            // BNM::CreateMonoString
            // Mono::String::Create
            Mono::String *text_placeholder = BNM::CreateMonoString("b");
            LOGI("text_placeholder %p hash: %d", text_placeholder, text_placeholder->GetHash());
            Mono::String *text_ = BNM::CreateMonoString("a");
            LOGI("text_ %p hash: %d", text_, text_->GetHash());
            void *kb = offsets::TSKeyboard::Open(text_, (int32_t)0,
                    (bool)1, (bool)0, (bool)0, (bool)0,
                    text_placeholder, (int32_t)0);

            LOGI("CALLED OPEN()");
            /*
            text___->Destroy();
            text_placeholder->Destroy();
            LOGI("STRINGS DESTROYED");*/
        }
// ...
// ...
namespace offsets {
    bool inited = 0;
    // TouchScreenKeyboard
    BNM::Class TSKeyboard_c{};

    namespace TSKeyboard {

        // Static
        /* static TouchScreenKeyboard Open(string text, TouchScreenKeyboardType keyboardType,
            bool autocorrection, bool multiline, bool secure, bool alert,
            string textPlaceholder, int characterLimit)
        */
        //BNM::Method<BNM::IL2CPP::Il2CppObject *> Open{};
        BNM::Method<void *> Open{};

        BNM::Method<bool> isSupported{};
    }

    void Init() {
        TSKeyboard_c = BNM::Class("UnityEngine", "TouchScreenKeyboard");
        TSKeyboard::Open = TSKeyboard_c.GetMethod("Open", 8);
        TSKeyboard::isSupported = TSKeyboard_c.GetMethod("get_isSupported", 0);

        inited = 1;
    }
}
// ...

What imgui render type do you use? eglSwapBuffers hook (unity thread), GlSurfaceView/SurfaceView (non unity thread)? Unity calls should be done in unity thread. And for keyboard I'd like to recommend use pure android code, not unity's.

EduModsS commented 1 month ago

How could I launch the keyboard, and read the content and send it to ImGui::inputText() ? using only c++

Lsc0x80 commented 1 month ago

this crashes

// ...
        if (ImGui::Button("Change")){
            // BNM::CreateMonoString
            // Mono::String::Create
            Mono::String *text_placeholder = BNM::CreateMonoString("b");
            LOGI("text_placeholder %p hash: %d", text_placeholder, text_placeholder->GetHash());
            Mono::String *text_ = BNM::CreateMonoString("a");
            LOGI("text_ %p hash: %d", text_, text_->GetHash());
            void *kb = offsets::TSKeyboard::Open(text_, (int32_t)0,
                    (bool)1, (bool)0, (bool)0, (bool)0,
                    text_placeholder, (int32_t)0);

            LOGI("CALLED OPEN()");
            /*
            text___->Destroy();
            text_placeholder->Destroy();
            LOGI("STRINGS DESTROYED");*/
        }
// ...
// ...
namespace offsets {
    bool inited = 0;
    // TouchScreenKeyboard
    BNM::Class TSKeyboard_c{};

    namespace TSKeyboard {

        // Static
        /* static TouchScreenKeyboard Open(string text, TouchScreenKeyboardType keyboardType,
            bool autocorrection, bool multiline, bool secure, bool alert,
            string textPlaceholder, int characterLimit)
        */
        //BNM::Method<BNM::IL2CPP::Il2CppObject *> Open{};
        BNM::Method<void *> Open{};

        BNM::Method<bool> isSupported{};
    }

    void Init() {
        TSKeyboard_c = BNM::Class("UnityEngine", "TouchScreenKeyboard");
        TSKeyboard::Open = TSKeyboard_c.GetMethod("Open", 8);
        TSKeyboard::isSupported = TSKeyboard_c.GetMethod("get_isSupported", 0);

        inited = 1;
    }
}
// ...

What imgui render type do you use? eglSwapBuffers hook (unity thread), GlSurfaceView/SurfaceView (non unity thread)? Unity calls should be done in unity thread. And for keyboard I'd like to recommend use pure android code, not unity's.

i use eglSwapBuffers.

BNM-Dev commented 1 month ago

So need get crash log.

Lsc0x80 commented 1 month ago

So need get crash log.

they are not there, that is, they do not appear. No anr, thombstones and logcat is clear for any crash or error messages

BNM-Dev commented 1 month ago

So need get crash log.

they are not there, that is, they do not appear. No anr, thombstones and logcat is clear for any crash or error messages

Try use custom crash handlers. At least basic sigaction with any backtrace code.

EduModsS commented 1 month ago

lsc 0x80.

I managed to invoke the keyboard with this:

struct TouchScreenKeyboard_InternalConstructorHelperArguments
{
    // Fields
    uint32_t keyboardType; // 0x10
    uint32_t autocorrection; // 0x14
    uint32_t multiline; // 0x18
    uint32_t secure; // 0x1c
    uint32_t alert; // 0x20
    int characterLimit; // 0x24
};

TouchScreenKeyboard_InternalConstructorHelperArguments keyArgs;
        keyArgs.keyboardType = 0; // Default
        keyArgs.autocorrection = 0; // Default
        keyArgs.multiline = 0; // Default
        keyArgs.secure = 0; // Default
        keyArgs.alert = 0; // Default
        keyArgs.characterLimit = 0; // Default

        LOGD("Keyboard Is Open");
        MethodInfo* Open8Param = TouchScreenKeyboard->getMethod("Open", 8);
        MethodInfo* InternalH = TouchScreenKeyboard->getMethod("TouchScreenKeyboard_InternalConstructorHelper");

        InternalH->invoke_static<Il2CppObject*>(
            &keyArgs,
            Il2cpp::NewString("??"),
            Il2cpp::NewString("")
        );

I know I'm not in the BNM lib, because I haven't been able to install it yet, but this code should work perfectly in the BNM mod, just change the get by name methods.

BNM-Dev commented 1 month ago

I still don't recommend to use unity's methods for keyboard. Using it you became unity's hostages. I know that this looks easier, but later, with other engines this will became a big problem.

EduModsS commented 1 month ago

I'm using a project, virtual keyboard imgui, for my imgui keyboard now

ArifRios1st commented 2 weeks ago

How could I launch the keyboard, and read the content and send it to ImGui::inputText() ? using only c++

you can use JNI

Lsc0x80 commented 1 week ago

How could I launch the keyboard, and read the content and send it to ImGui::inputText() ? using only c++

you can use JNI

but how to get events from virtual keyboard?

ArifRios1st commented 1 week ago

but how to get events from virtual keyboard?

you can hook nativeInjectEvent for that

ArifRios1st commented 1 week ago

but how to get events from virtual keyboard?

you can hook nativeInjectEvent for that

Example:

IMGUI_IMPL_API int32_t  ImGui_ImplAndroid_HandleInputEvent(JNIEnv *env, jobject input_event, bool isUseMotionEvent = false);

HOOK_DEF(jboolean, nativeInjectEvent,JNIEnv *env, jobject unityPlayer, jobject inputEvent) {

    if(ImGui::GetCurrentContext()){
        ImGui_ImplAndroid_HandleInputEvent(env,inputEvent, false);
    }

    return old_nativeInjectEvent(env, unityPlayer, inputEvent);
}

and for parse event it something like this

template<typename... Args>
static int32_t JNIInvokeIntMethod(JNIEnv *env, jobject m_object, const char *methodName, const char *methodSignature, Args... args) {
    jclass eventClass = env->GetObjectClass(m_object);
    jmethodID methodID = env->GetMethodID(eventClass, methodName, methodSignature);
    int32_t result = env->CallIntMethod(m_object, methodID, args...);

    env->DeleteLocalRef(eventClass);
    return result;
}

template<typename... Args>
static float JNIInvokeFloatMethod(JNIEnv *env, jobject m_object, const char *methodName, const char *methodSignature, Args... args) {
    jclass eventClass = env->GetObjectClass(m_object);
    jmethodID methodID = env->GetMethodID(eventClass, methodName, methodSignature);
    float result = env->CallFloatMethod(m_object, methodID, args...);

    env->DeleteLocalRef(eventClass);
    return result;
}

/**
 * Input event accessors.
 *
 * Note that most functions can only be used on input events that are of a given type.
 * Calling these functions on input events of other types will yield undefined behavior.
 */

/*** Accessors for all input events. ***/

/** Get the input event type. */
static int32_t AInputEvent_getType(JNIEnv *env, const jobject input_event){
    jclass KeyEventClass = env->FindClass(OBF("android/view/KeyEvent"));
    if(env->IsInstanceOf(input_event,KeyEventClass) == JNI_TRUE){
        env->DeleteLocalRef(KeyEventClass);
        return AINPUT_EVENT_TYPE_KEY;
    }

    jclass MotionEventClass = env->FindClass(OBF("android/view/MotionEvent"));
    if(env->IsInstanceOf(input_event,MotionEventClass) == JNI_TRUE){
        env->DeleteLocalRef(MotionEventClass);
        return AINPUT_EVENT_TYPE_MOTION;
    }

    env->DeleteLocalRef(KeyEventClass);
    env->DeleteLocalRef(MotionEventClass);
    return 0;
}

/**
 * Get the key code of the key event.
 * This is the physical key that was pressed, not the Unicode character.
 */
static int32_t AKeyEvent_getKeyCode(JNIEnv *env, const jobject input_event){
    return JNIInvokeIntMethod(env,input_event, OBF("getKeyCode"),OBF("()I"));
}

/**
 * Get the hardware key id of this key event.
 * These values are not reliable and vary from device to device.
 */
static int32_t AKeyEvent_getScanCode(JNIEnv *env, const jobject input_event){
    return JNIInvokeIntMethod(env,input_event, OBF("getScanCode"),OBF("()I"));
}

/*** Accessors for key events only. ***/

/** Get the key event action. */
static int32_t AKeyEvent_getAction(JNIEnv *env, const jobject input_event){
    return JNIInvokeIntMethod(env,input_event, OBF("getAction"),OBF("()I"));
}

/** Get the meta key state. */
static int32_t AKeyEvent_getMetaState(JNIEnv *env, const jobject input_event){
    return JNIInvokeIntMethod(env,input_event, OBF("getMetaState"),OBF("()I"));
}

/*** Accessors for key events only. ***/

/** Get the unicode of the key event. */
static int32_t AKeyEvent_getUnicodeChar(JNIEnv *env, const jobject input_event, int32_t meta_state){
    return JNIInvokeIntMethod(env,input_event, OBF("getUnicodeChar"),OBF("(I)I"),meta_state);
}

/*** Accessors for motion events only. ***/

/** Get the combined motion event action code and pointer index. */
static int32_t AMotionEvent_getAction(JNIEnv *env, const jobject input_event){
    return JNIInvokeIntMethod(env,input_event, OBF("getAction"),OBF("()I"));
}

/*** Accessors for motion events only. ***/

/** Get the motion event action code only. */
static int32_t AMotionEvent_getActionMasked(JNIEnv *env, const jobject input_event){
    return JNIInvokeIntMethod(env,input_event, OBF("getActionMasked"),OBF("()I"));
}
/*** Accessors for motion events only. ***/

/** Get the motion event pointer index only. */
static int32_t AMotionEvent_getActionIndex(JNIEnv *env, const jobject input_event){
    return JNIInvokeIntMethod(env,input_event, OBF("getActionIndex"),OBF("()I"));
}

/**
 * Get the tool type of a pointer for the given pointer index.
 * The tool type indicates the type of tool used to make contact such as a
 * finger or stylus, if known.
 */
static int32_t AMotionEvent_getToolType(JNIEnv *env, const jobject input_event, size_t pointer_index){
    return JNIInvokeIntMethod(env,input_event, OBF("getToolType"),OBF("(I)I"),pointer_index);
}

/** Get the button state of all buttons that are pressed. */
static int32_t AMotionEvent_getButtonState(JNIEnv *env, const jobject input_event){
    return JNIInvokeIntMethod(env,input_event, OBF("getButtonState"),OBF("()I"));
}

/**
 * Get the current X coordinate of this event for pointer index of 0.
 * Whole numbers are pixels; the value may have a fraction for input devices
 * that are sub-pixel precise.
 */
static float AMotionEvent_getX(JNIEnv *env, const jobject input_event){
    return JNIInvokeFloatMethod(env,input_event,OBF("getX"), OBF("()F"));
}

/**
 * Get the current Y coordinate of this event for pointer index of 0.
 * Whole numbers are pixels; the value may have a fraction for input devices
 * that are sub-pixel precise.
 */
static float AMotionEvent_getY(JNIEnv *env, const jobject input_event){
    return JNIInvokeFloatMethod(env,input_event,OBF("getY"), OBF("()F"));
}

/**
 * Get the current X coordinate of this event for the given pointer index.
 * Whole numbers are pixels; the value may have a fraction for input devices
 * that are sub-pixel precise.
 */
static float AMotionEvent_getX(JNIEnv *env, const jobject input_event, size_t pointer_index){
    return JNIInvokeFloatMethod(env,input_event,OBF("getX"), OBF("(I)F"),pointer_index);
}

/**
 * Get the current Y coordinate of this event for the given pointer index.
 * Whole numbers are pixels; the value may have a fraction for input devices
 * that are sub-pixel precise.
 */
static float AMotionEvent_getY(JNIEnv *env, const jobject input_event, size_t pointer_index){
    return JNIInvokeFloatMethod(env,input_event,OBF("getY"), OBF("(I)F"),pointer_index);
}

/** Get the value of the request axis for the given pointer index. */
static float AMotionEvent_getAxisValue(JNIEnv *env, const jobject input_event, int32_t axis, size_t pointer_index){
    return JNIInvokeFloatMethod(env,input_event,OBF("getAxisValue"), OBF("(II)F"),axis,pointer_index);
}

int32_t ImGui_ImplAndroid_HandleInputEvent(JNIEnv *env, jobject input_event, bool isUseMotionEvent)
{
    ImGuiIO& io = ImGui::GetIO();
    int32_t event_type = AInputEvent_getType(env,input_event);
    if(event_type == AINPUT_EVENT_TYPE_KEY){
        int32_t event_key_code = AKeyEvent_getKeyCode(env,input_event);
        int32_t event_scan_code = AKeyEvent_getScanCode(env,input_event);
        int32_t event_action = AKeyEvent_getAction(env,input_event);
        int32_t event_meta_state = AKeyEvent_getMetaState(env,input_event);

        io.AddKeyEvent(ImGuiMod_Ctrl,  (event_meta_state & AMETA_CTRL_ON)  != 0);
        io.AddKeyEvent(ImGuiMod_Shift, (event_meta_state & AMETA_SHIFT_ON) != 0);
        io.AddKeyEvent(ImGuiMod_Alt,   (event_meta_state & AMETA_ALT_ON)   != 0);
        io.AddKeyEvent(ImGuiMod_Super, (event_meta_state & AMETA_META_ON)  != 0);

        switch (event_action)
        {
            // FIXME: AKEY_EVENT_ACTION_DOWN and AKEY_EVENT_ACTION_UP occur at once as soon as a touch pointer
            // goes up from a key. We use a simple key event queue/ and process one event per key per frame in
            // ImGui_ImplAndroid_NewFrame()...or consider using IO queue, if suitable: https://github.com/ocornut/imgui/issues/2787
            case AKEY_EVENT_ACTION_DOWN:
            case AKEY_EVENT_ACTION_UP:
            {
                ImGuiKey key = ImGui_ImplAndroid_KeyCodeToImGuiKey(event_key_code);
                if (key != ImGuiKey_None)
                {
                    io.AddKeyEvent(key, event_action == AKEY_EVENT_ACTION_DOWN);
                    io.SetKeyEventNativeData(key, event_key_code, event_scan_code);
                }

                if (event_action == AKEY_EVENT_ACTION_DOWN){
                    int32_t unicodeChar = AKeyEvent_getUnicodeChar(env,input_event,event_meta_state);
                    io.AddInputCharacter(unicodeChar);
                    MenuManager::i().HandleKeyEvent(event_key_code);
                }

                break;
            }
            default:
                break;
        }
        return 0;
    }

    if(event_type == AINPUT_EVENT_TYPE_MOTION && isUseMotionEvent){
        int32_t event_action = AMotionEvent_getActionMasked(env,input_event);
        int32_t event_pointer_index = AMotionEvent_getActionIndex(env,input_event);

        switch (AMotionEvent_getToolType(env,input_event, event_pointer_index))
        {
            case AMOTION_EVENT_TOOL_TYPE_MOUSE:
                io.AddMouseSourceEvent(ImGuiMouseSource_Mouse);
                break;
            case AMOTION_EVENT_TOOL_TYPE_STYLUS:
            case AMOTION_EVENT_TOOL_TYPE_ERASER:
                io.AddMouseSourceEvent(ImGuiMouseSource_Pen);
                break;
            case AMOTION_EVENT_TOOL_TYPE_FINGER:
            default:
                io.AddMouseSourceEvent(ImGuiMouseSource_TouchScreen);
                break;
        }

        switch (event_action)
        {
            case AMOTION_EVENT_ACTION_DOWN:
            case AMOTION_EVENT_ACTION_UP:
            {
                // Physical mouse buttons (and probably other physical devices) also invoke the actions AMOTION_EVENT_ACTION_DOWN/_UP,
                // but we have to process them separately to identify the actual button pressed. This is done below via
                // AMOTION_EVENT_ACTION_BUTTON_PRESS/_RELEASE. Here, we only process "FINGER" input (and "UNKNOWN", as a fallback).
                int tool_type = AMotionEvent_getToolType(env,input_event, event_pointer_index);
                if (tool_type == AMOTION_EVENT_TOOL_TYPE_FINGER || tool_type == AMOTION_EVENT_TOOL_TYPE_UNKNOWN)
                {
                    io.AddMousePosEvent(AMotionEvent_getX(env,input_event, event_pointer_index),
                                        AMotionEvent_getY(env,input_event, event_pointer_index));
                    io.AddMouseButtonEvent(0, event_action == AMOTION_EVENT_ACTION_DOWN);
                }
                break;
            }
            case AMOTION_EVENT_ACTION_BUTTON_PRESS:
            case AMOTION_EVENT_ACTION_BUTTON_RELEASE:
            {
                int32_t button_state = AMotionEvent_getButtonState(env,input_event);
                io.AddMouseButtonEvent(0, (button_state & AMOTION_EVENT_BUTTON_PRIMARY) != 0);
                io.AddMouseButtonEvent(1, (button_state & AMOTION_EVENT_BUTTON_SECONDARY) != 0);
                io.AddMouseButtonEvent(2, (button_state & AMOTION_EVENT_BUTTON_TERTIARY) != 0);
                break;
            }
            case AMOTION_EVENT_ACTION_HOVER_MOVE: // Hovering: Tool moves while NOT pressed (such as a physical mouse)
            case AMOTION_EVENT_ACTION_MOVE:       // Touch pointer moves while DOWN
                io.AddMousePosEvent(AMotionEvent_getX(env,input_event, event_pointer_index),
                                    AMotionEvent_getY(env,input_event, event_pointer_index));
                break;
            case AMOTION_EVENT_ACTION_SCROLL:
                io.AddMouseWheelEvent(AMotionEvent_getAxisValue(env,input_event, AMOTION_EVENT_AXIS_HSCROLL, event_pointer_index),
                                      AMotionEvent_getAxisValue(env,input_event, AMOTION_EVENT_AXIS_VSCROLL, event_pointer_index));
                break;
            default:
                break;
        }

        return 1;
    }

    return 0;
}