From 4bd637b9a7a58ebdf34a16692304e669fb377ebf Mon Sep 17 00:00:00 2001 From: Debajit Gayen Date: Thu, 9 Apr 2026 17:16:34 +0100 Subject: [PATCH 1/4] integrate Brandon-T's RemoteInput --- build.gradle.kts | 2 +- config/checkstyle/checkstyle-suppressions.xml | 5 +- .../chromascape/controller/Controller.java | 28 +- .../scripts/DemoFishingScript.java | 2 +- .../chromascape/scripts/DemoWineScript.java | 9 +- .../utils/actions/ItemDropper.java | 12 +- .../input/keyboard/VirtualKeyboardUtils.java | 131 +- .../utils/core/input/mouse/MouseOverlay.java | 80 - .../core/input/mouse/VirtualMouseUtils.java | 348 +- .../utils/core/input/mouse/WindMouse.java | 149 +- .../core/input/remoteinput/ControlKey.java | 24 + .../utils/core/input/remoteinput/Kinput.java | 321 - .../core/input/remoteinput/MouseButton.java | 13 + .../core/input/remoteinput/RemoteInput.java | 333 + .../remoteinput/RemoteInputInterface.java | 226 + .../core/screen/topology/ColourContours.java | 7 +- .../screen/topology/TemplateMatching.java | 8 +- .../screen/window/LinuxProcessManager.java | 19 + .../core/screen/window/MacProcessManager.java | 19 + .../core/screen/window/ProcessManager.java | 15 + .../screen/window/ProcessManagerFactory.java | 24 + .../core/screen/window/ScreenManager.java | 251 +- .../core/screen/window/WindowHandler.java | 157 - .../screen/window/WindowsProcessManager.java | 91 + .../utils/domain/walker/Walker.java | 2 +- .../utils/domain/zones/ZoneManager.java | 23 +- third-party/DEV_README.md | 37 + .../RemoteInput/.github/workflows/build.yml | 311 + .../RemoteInput/.github/workflows/build.zip | Bin 0 -> 9038 bytes third-party/RemoteInput/.gitignore | 37 + third-party/RemoteInput/CMakeLists.txt | 418 ++ third-party/RemoteInput/README.md | 14 + third-party/RemoteInput/RemoteInput.cbp | 170 + third-party/RemoteInput/RemoteInput.depend | 643 ++ third-party/RemoteInput/RemoteInput.layout | 190 + .../RemoteInput.xcodeproj/project.pbxproj | 1178 ++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../UserInterfaceState.xcuserstate | Bin 0 -> 314888 bytes .../xcschemes/RemoteInputBootstrap.xcscheme | 67 + .../xcschemes/libRemoteInput.xcscheme | 67 + .../xcschemes/xcschememanagement.plist | 47 + .../RemoteInput/DetachedThreadPool.cxx | 93 + .../RemoteInput/DetachedThreadPool.hxx | 66 + third-party/RemoteInput/RemoteInput/EIOS.cxx | 522 ++ third-party/RemoteInput/RemoteInput/EIOS.hxx | 102 + .../RemoteInput/RemoteInput/EIOSTypes.hxx | 100 + .../RemoteInput/RemoteInput/Echo/Atomics.cxx | 199 + .../RemoteInput/RemoteInput/Echo/Atomics.hxx | 150 + .../RemoteInput/RemoteInput/Echo/Event.cxx | 180 + .../RemoteInput/RemoteInput/Echo/Event.hxx | 72 + .../RemoteInput/Echo/MemoryMap.cxx | 321 + .../RemoteInput/Echo/MemoryMap.hxx | 108 + .../RemoteInput/Echo/MemoryMapStream.hxx | 315 + .../RemoteInput/RemoteInput/Echo/Module.cxx | 25 + .../RemoteInput/RemoteInput/Echo/Module.hxx | 194 + .../RemoteInput/RemoteInput/Echo/Mutex.cxx | 229 + .../RemoteInput/RemoteInput/Echo/Mutex.hxx | 90 + .../RemoteInput/Echo/Semaphore.cxx | 231 + .../RemoteInput/Echo/Semaphore.hxx | 91 + .../RemoteInput/RemoteInput/Echo/Stream.cxx | 49 + .../RemoteInput/RemoteInput/Echo/Stream.hxx | 348 ++ .../RemoteInput/Echo/Synchronization.hxx | 209 + .../RemoteInput/RemoteInput/Echo/Time.cxx | 135 + .../RemoteInput/RemoteInput/Echo/Time.hxx | 34 + .../RemoteInput/Echo/TypeTraits.hxx | 284 + .../Echo/TypeTraits_Functional.hxx | 316 + .../Echo/TypeTraits_Functional_Attributes.hxx | 276 + .../RemoteInput/Hooks/ModelRendering.cxx | 1300 ++++ .../RemoteInput/Hooks/ModelRendering.hxx | 237 + .../RemoteInput/RemoteInput/Info.plist | 24 + .../RemoteInput/Injection/Injector.hxx | 17 + .../RemoteInput/Injection/Injector_Arm.cpp | 431 ++ .../RemoteInput/Injection/Injector_Darwin.cxx | 1324 ++++ .../RemoteInput/Injection/Injector_Linux.cpp | 402 ++ .../Injection/Injector_Windows.cxx | 66 + third-party/RemoteInput/RemoteInput/JVM.cxx | 239 + third-party/RemoteInput/RemoteInput/JVM.hxx | 49 + .../RemoteInput/Java/AWTAccessor.cxx | 63 + .../RemoteInput/Java/AWTAccessor.hxx | 38 + .../RemoteInput/RemoteInput/Java/AWTEvent.cxx | 128 + .../RemoteInput/RemoteInput/Java/AWTEvent.hxx | 38 + .../RemoteInput/Java/AWTEventAccessor.cxx | 62 + .../RemoteInput/Java/AWTEventAccessor.hxx | 38 + .../RemoteInput/RemoteInput/Java/Applet.cxx | 19 + .../RemoteInput/RemoteInput/Java/Applet.hxx | 24 + .../RemoteInput/Java/Component.cxx | 231 + .../RemoteInput/Java/Component.hxx | 64 + .../RemoteInput/Java/Container.cxx | 43 + .../RemoteInput/Java/Container.hxx | 25 + .../RemoteInput/Java/EventQueue.cxx | 68 + .../RemoteInput/Java/EventQueue.hxx | 40 + .../RemoteInput/Java/FocusEvent.cxx | 93 + .../RemoteInput/Java/FocusEvent.hxx | 66 + .../RemoteInput/RemoteInput/Java/Frame.cxx | 31 + .../RemoteInput/RemoteInput/Java/Frame.hxx | 26 + .../RemoteInput/Java/InputEvent.cxx | 40 + .../RemoteInput/Java/InputEvent.hxx | 35 + .../RemoteInput/Java/JNI_Common.hxx | 22 + .../RemoteInput/RemoteInput/Java/KeyEvent.cxx | 63 + .../RemoteInput/RemoteInput/Java/KeyEvent.hxx | 49 + .../RemoteInput/Java/MouseEvent.cxx | 63 + .../RemoteInput/Java/MouseEvent.hxx | 59 + .../RemoteInput/Java/MouseWheelEvent.cxx | 63 + .../RemoteInput/Java/MouseWheelEvent.hxx | 40 + .../RemoteInput/Java/PointerInfo.cxx | 101 + .../RemoteInput/Java/PointerInfo.hxx | 42 + .../RemoteInput/Java/RIEventQueue.cxx | 344 + .../RemoteInput/Java/RIEventQueue.hxx | 49 + .../RemoteInput/Java/SunToolkit.cxx | 42 + .../RemoteInput/Java/SunToolkit.hxx | 20 + .../RemoteInput/RemoteInput/Java/Toolkit.cxx | 70 + .../RemoteInput/RemoteInput/Java/Toolkit.hxx | 41 + .../RemoteInput/RemoteInput/Java/Window.cxx | 25 + .../RemoteInput/RemoteInput/Java/Window.hxx | 23 + .../RemoteInput/Java/WindowEvent.cxx | 60 + .../RemoteInput/Java/WindowEvent.hxx | 40 + .../RemoteInput/Platform/DebugConsole.cxx | 58 + .../RemoteInput/Platform/DebugConsole.hxx | 28 + .../RemoteInput/Platform/JavaInternal.hxx | 376 ++ .../RemoteInput/Platform/NativeHooks.hxx | 6 + .../Platform/NativeHooks_Darwin.cxx | 630 ++ .../Platform/NativeHooks_Linux.cxx | 639 ++ .../Platform/NativeHooks_Windows.cxx | 1529 +++++ .../RemoteInput/Platform/Platform.hxx | 49 + .../RemoteInput/Platform/Platform_Darwin.mm | 428 ++ .../RemoteInput/Platform/Platform_Linux.cxx | 632 ++ .../RemoteInput/Platform/Platform_Windows.cxx | 568 ++ .../Platform/RemoteInputBootstrap_Darwin.cxx | 75 + .../RemoteInput/Plugin/ControlCenter.cxx | 2011 ++++++ .../RemoteInput/Plugin/ControlCenter.hxx | 225 + .../RemoteInput/Plugin/Graphics.cxx | 1908 ++++++ .../RemoteInput/Plugin/Graphics.hxx | 97 + .../RemoteInput/Plugin/ImageData.cxx | 160 + .../RemoteInput/Plugin/ImageData.hxx | 57 + .../RemoteInput/Plugin/InputOutput.cxx | 1491 +++++ .../RemoteInput/Plugin/InputOutput.hxx | 101 + .../RemoteInput/Plugin/JVM/JVMCache.cxx | 134 + .../RemoteInput/Plugin/JVM/JVMCache.hxx | 45 + .../RemoteInput/Plugin/JVM/RemoteVM.cxx | 2805 +++++++++ .../RemoteInput/Plugin/JVM/RemoteVM.hxx | 204 + .../RemoteInput/Plugin/NativePlugin.cxx | 94 + .../RemoteInput/Plugin/NativePlugin.hxx | 29 + .../RemoteInput/RemoteInput/Plugin/Plugin.cxx | 195 + .../RemoteInput/RemoteInput/Plugin/Plugin.hxx | 21 + .../RemoteInput/Plugin/Python/Python.cxx | 5384 ++++++++++++++++ .../RemoteInput/Plugin/Python/Python.hxx | 1631 +++++ .../Plugin/Python/PythonCommon.cxx | 309 + .../Plugin/Python/PythonCommon.hxx | 123 + .../Plugin/Python/PythonCommon_Templates.hxx | 515 ++ .../RemoteInput/Plugin/Python/PythonEIOS.cxx | 1377 ++++ .../RemoteInput/Plugin/Python/PythonEIOS.hxx | 53 + .../Plugin/Python/PythonJavaArray.cxx | 789 +++ .../Plugin/Python/PythonJavaArray.hxx | 21 + .../Plugin/Python/PythonJavaList.cxx | 173 + .../Plugin/Python/PythonJavaList.hxx | 12 + .../Plugin/Python/PythonJavaObject.cxx | 478 ++ .../Plugin/Python/PythonJavaObject.hxx | 29 + .../Plugin/Python/PythonMacros.hxx | 53 + .../Plugin/Python/PythonPlugin.cxx | 225 + .../Plugin/Python/PythonPlugin.hxx | 40 + .../RemoteInput/RemoteInput/Plugin/Signal.hxx | 120 + .../RemoteInput/Plugin/SimbaPlugin.cxx | 2565 ++++++++ .../RemoteInput/Plugin/SimbaPlugin.hxx | 527 ++ .../RemoteInput/Plugin/TMemoryManager.hxx | 126 + .../RemoteInput/RemoteInput/Random.cxx | 33 + .../RemoteInput/RemoteInput/Random.hxx | 38 + .../RemoteInput/RemoteInput/Reflection.cxx | 370 ++ .../RemoteInput/RemoteInput/Reflection.hxx | 285 + .../RemoteInput/ReflectionHook.cxx | 22 + .../RemoteInput/ReflectionHook.hxx | 45 + .../RemoteInput/RemoteInput/RemoteInput.def | 28 + .../RemoteInput/RemoteInput/RemoteInput.h | 21 + .../RemoteInput/Thirdparty/CMakeLists.txt | 175 + .../RemoteInput/Thirdparty/Hook.cxx | 207 + .../RemoteInput/Thirdparty/Hook.hxx | 54 + .../RemoteInput/Thirdparty/Injector.cxx | 57 + .../RemoteInput/Thirdparty/Injector.hxx | 31 + .../Thirdparty/kubo_injector/LICENSE_GPL.txt | 280 + .../Thirdparty/kubo_injector/LICENSE_LGPL.txt | 458 ++ .../Thirdparty/kubo_injector/README.md | 298 + .../kubo_injector/include/injector.h | 245 + .../Thirdparty/kubo_injector/src/linux/elf.c | 624 ++ .../kubo_injector/src/linux/injector.c | 389 ++ .../src/linux/injector_internal.h | 193 + .../kubo_injector/src/linux/ptrace.c | 149 + .../kubo_injector/src/linux/remote_call.c | 821 +++ .../kubo_injector/src/linux/shellcode.S | 70 + .../Thirdparty/kubo_injector/src/linux/util.c | 82 + .../kubo_injector/src/macos/exc_handler.c | 314 + .../kubo_injector/src/macos/injector.c | 295 + .../src/macos/injector_internal.h | 121 + .../Thirdparty/kubo_injector/src/macos/mach.c | 112 + .../kubo_injector/src/macos/mach_exc.h | 330 + .../kubo_injector/src/macos/mach_excServer.c | 828 +++ .../kubo_injector/src/macos/ptrace.c | 81 + .../kubo_injector/src/macos/remote_call.c | 228 + .../Thirdparty/kubo_injector/src/macos/util.c | 155 + .../kubo_injector/src/windows/injector.c | 859 +++ .../kubo_injector/src/windows/injector.def | 9 + .../linux_detours/include/detours.h | 818 +++ .../linux_detours/include/plthook.h | 66 + .../Thirdparty/linux_detours/include/types.h | 337 + .../Thirdparty/linux_detours/src/barrier.cpp | 714 +++ .../Thirdparty/linux_detours/src/detours.cpp | 3328 ++++++++++ .../Thirdparty/linux_detours/src/disasm.cpp | 5539 +++++++++++++++++ .../linux_detours/src/plthook_elf.cpp | 892 +++ .../linux_detours/src/trampoline_arm.cpp | 317 + .../linux_detours/src/trampoline_x86.cpp | 204 + .../Thirdparty/min_hook/include/MinHook.h | 186 + .../Thirdparty/min_hook/src/buffer.c | 312 + .../Thirdparty/min_hook/src/buffer.h | 42 + .../Thirdparty/min_hook/src/hde/hde32.c | 326 + .../Thirdparty/min_hook/src/hde/hde32.h | 105 + .../Thirdparty/min_hook/src/hde/hde64.c | 337 + .../Thirdparty/min_hook/src/hde/hde64.h | 112 + .../Thirdparty/min_hook/src/hde/pstdint.h | 39 + .../Thirdparty/min_hook/src/hde/table32.h | 73 + .../Thirdparty/min_hook/src/hde/table64.h | 74 + .../Thirdparty/min_hook/src/hook.c | 889 +++ .../Thirdparty/min_hook/src/trampoline.c | 316 + .../Thirdparty/min_hook/src/trampoline.h | 105 + .../Thirdparty/nanobind/CMakeLists.txt | 170 + .../RemoteInput/Thirdparty/nanobind/LICENSE | 26 + .../RemoteInput/Thirdparty/nanobind/README.md | 59 + .../nanobind/cmake/collect-symbols-pypy.py | 28 + .../nanobind/cmake/collect-symbols.py | 42 + .../nanobind/cmake/darwin-ld-cpython.sym | 925 +++ .../nanobind/cmake/darwin-ld-pypy.sym | 964 +++ .../nanobind/cmake/nanobind-config.cmake | 472 ++ .../nanobind/ext/robin_map/CMakeLists.txt | 86 + .../Thirdparty/nanobind/ext/robin_map/LICENSE | 21 + .../nanobind/ext/robin_map/README.md | 521 ++ .../cmake/tsl-robin-mapConfig.cmake.in | 9 + .../include/tsl/robin_growth_policy.h | 415 ++ .../ext/robin_map/include/tsl/robin_hash.h | 1586 +++++ .../ext/robin_map/include/tsl/robin_map.h | 815 +++ .../ext/robin_map/include/tsl/robin_set.h | 668 ++ .../nanobind/include/nanobind/eigen/dense.h | 486 ++ .../nanobind/include/nanobind/eigen/sparse.h | 178 + .../nanobind/include/nanobind/eval.h | 61 + .../include/nanobind/intrusive/counter.h | 261 + .../include/nanobind/intrusive/counter.inl | 148 + .../nanobind/include/nanobind/intrusive/ref.h | 153 + .../nanobind/include/nanobind/make_iterator.h | 155 + .../nanobind/include/nanobind/nanobind.h | 58 + .../nanobind/include/nanobind/nb_accessor.h | 225 + .../nanobind/include/nanobind/nb_attr.h | 433 ++ .../nanobind/include/nanobind/nb_call.h | 150 + .../nanobind/include/nanobind/nb_cast.h | 699 +++ .../nanobind/include/nanobind/nb_class.h | 808 +++ .../nanobind/include/nanobind/nb_defs.h | 203 + .../nanobind/include/nanobind/nb_descr.h | 155 + .../nanobind/include/nanobind/nb_enums.h | 26 + .../nanobind/include/nanobind/nb_error.h | 152 + .../nanobind/include/nanobind/nb_func.h | 406 ++ .../nanobind/include/nanobind/nb_lib.h | 566 ++ .../nanobind/include/nanobind/nb_misc.h | 118 + .../nanobind/include/nanobind/nb_python.h | 61 + .../nanobind/include/nanobind/nb_traits.h | 220 + .../nanobind/include/nanobind/nb_tuple.h | 78 + .../nanobind/include/nanobind/nb_types.h | 932 +++ .../nanobind/include/nanobind/ndarray.h | 589 ++ .../nanobind/include/nanobind/operators.h | 145 + .../nanobind/include/nanobind/stl/array.h | 22 + .../nanobind/include/nanobind/stl/bind_map.h | 180 + .../include/nanobind/stl/bind_vector.h | 225 + .../nanobind/include/nanobind/stl/chrono.h | 227 + .../nanobind/include/nanobind/stl/complex.h | 69 + .../include/nanobind/stl/detail/chrono.h | 289 + .../include/nanobind/stl/detail/nb_array.h | 67 + .../include/nanobind/stl/detail/nb_dict.h | 95 + .../include/nanobind/stl/detail/nb_list.h | 83 + .../include/nanobind/stl/detail/nb_optional.h | 48 + .../include/nanobind/stl/detail/nb_set.h | 81 + .../include/nanobind/stl/detail/traits.h | 92 + .../include/nanobind/stl/filesystem.h | 93 + .../nanobind/include/nanobind/stl/function.h | 95 + .../nanobind/include/nanobind/stl/list.h | 22 + .../nanobind/include/nanobind/stl/map.h | 23 + .../nanobind/include/nanobind/stl/optional.h | 27 + .../nanobind/include/nanobind/stl/pair.h | 92 + .../nanobind/include/nanobind/stl/set.h | 24 + .../include/nanobind/stl/shared_ptr.h | 137 + .../nanobind/include/nanobind/stl/string.h | 39 + .../include/nanobind/stl/string_view.h | 39 + .../nanobind/include/nanobind/stl/tuple.h | 109 + .../include/nanobind/stl/unique_ptr.h | 177 + .../include/nanobind/stl/unordered_map.h | 23 + .../include/nanobind/stl/unordered_set.h | 24 + .../nanobind/include/nanobind/stl/variant.h | 63 + .../nanobind/include/nanobind/stl/vector.h | 22 + .../nanobind/include/nanobind/stl/wstring.h | 39 + .../nanobind/include/nanobind/trampoline.h | 76 + .../nanobind/include/nanobind/typing.h | 29 + .../Thirdparty/nanobind/src/__init__.py | 21 + .../Thirdparty/nanobind/src/__main__.py | 36 + .../Thirdparty/nanobind/src/buffer.h | 166 + .../Thirdparty/nanobind/src/common.cpp | 1206 ++++ .../Thirdparty/nanobind/src/error.cpp | 325 + .../Thirdparty/nanobind/src/hash.h | 37 + .../Thirdparty/nanobind/src/implicit.cpp | 75 + .../Thirdparty/nanobind/src/nb_combined.cpp | 84 + .../Thirdparty/nanobind/src/nb_enum.cpp | 288 + .../Thirdparty/nanobind/src/nb_func.cpp | 1418 +++++ .../Thirdparty/nanobind/src/nb_internals.cpp | 549 ++ .../Thirdparty/nanobind/src/nb_internals.h | 492 ++ .../Thirdparty/nanobind/src/nb_ndarray.cpp | 813 +++ .../nanobind/src/nb_static_property.cpp | 76 + .../Thirdparty/nanobind/src/nb_type.cpp | 2238 +++++++ .../Thirdparty/nanobind/src/stubgen.py | 1422 +++++ .../Thirdparty/nanobind/src/trampoline.cpp | 188 + .../Thirdparty/rd_route/include/rd_route.h | 57 + .../Thirdparty/rd_route/src/rd_route.c | 515 ++ .../RemoteInput/RemoteInput/ThreadPool.cxx | 86 + .../RemoteInput/RemoteInput/ThreadPool.hxx | 60 + .../precompiled/libRemoteInput.dll | Bin 0 -> 1797632 bytes third-party/RemoteInput/setup.py | 61 + 318 files changed, 95299 insertions(+), 1110 deletions(-) delete mode 100644 src/main/java/com/chromascape/utils/core/input/mouse/MouseOverlay.java create mode 100644 src/main/java/com/chromascape/utils/core/input/remoteinput/ControlKey.java delete mode 100644 src/main/java/com/chromascape/utils/core/input/remoteinput/Kinput.java create mode 100644 src/main/java/com/chromascape/utils/core/input/remoteinput/MouseButton.java create mode 100644 src/main/java/com/chromascape/utils/core/input/remoteinput/RemoteInput.java create mode 100644 src/main/java/com/chromascape/utils/core/input/remoteinput/RemoteInputInterface.java create mode 100644 src/main/java/com/chromascape/utils/core/screen/window/LinuxProcessManager.java create mode 100644 src/main/java/com/chromascape/utils/core/screen/window/MacProcessManager.java create mode 100644 src/main/java/com/chromascape/utils/core/screen/window/ProcessManager.java create mode 100644 src/main/java/com/chromascape/utils/core/screen/window/ProcessManagerFactory.java delete mode 100644 src/main/java/com/chromascape/utils/core/screen/window/WindowHandler.java create mode 100644 src/main/java/com/chromascape/utils/core/screen/window/WindowsProcessManager.java create mode 100644 third-party/DEV_README.md create mode 100644 third-party/RemoteInput/.github/workflows/build.yml create mode 100644 third-party/RemoteInput/.github/workflows/build.zip create mode 100644 third-party/RemoteInput/.gitignore create mode 100644 third-party/RemoteInput/CMakeLists.txt create mode 100644 third-party/RemoteInput/README.md create mode 100644 third-party/RemoteInput/RemoteInput.cbp create mode 100644 third-party/RemoteInput/RemoteInput.depend create mode 100644 third-party/RemoteInput/RemoteInput.layout create mode 100644 third-party/RemoteInput/RemoteInput.xcodeproj/project.pbxproj create mode 100644 third-party/RemoteInput/RemoteInput.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 third-party/RemoteInput/RemoteInput.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 third-party/RemoteInput/RemoteInput.xcodeproj/project.xcworkspace/xcuserdata/brandonanthony.xcuserdatad/UserInterfaceState.xcuserstate create mode 100644 third-party/RemoteInput/RemoteInput.xcodeproj/xcshareddata/xcschemes/RemoteInputBootstrap.xcscheme create mode 100644 third-party/RemoteInput/RemoteInput.xcodeproj/xcshareddata/xcschemes/libRemoteInput.xcscheme create mode 100644 third-party/RemoteInput/RemoteInput.xcodeproj/xcuserdata/brandonanthony.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 third-party/RemoteInput/RemoteInput/DetachedThreadPool.cxx create mode 100644 third-party/RemoteInput/RemoteInput/DetachedThreadPool.hxx create mode 100644 third-party/RemoteInput/RemoteInput/EIOS.cxx create mode 100644 third-party/RemoteInput/RemoteInput/EIOS.hxx create mode 100644 third-party/RemoteInput/RemoteInput/EIOSTypes.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Echo/Atomics.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Echo/Atomics.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Echo/Event.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Echo/Event.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Echo/MemoryMap.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Echo/MemoryMap.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Echo/MemoryMapStream.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Echo/Module.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Echo/Module.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Echo/Mutex.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Echo/Mutex.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Echo/Semaphore.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Echo/Semaphore.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Echo/Stream.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Echo/Stream.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Echo/Synchronization.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Echo/Time.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Echo/Time.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Echo/TypeTraits.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Echo/TypeTraits_Functional.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Echo/TypeTraits_Functional_Attributes.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Hooks/ModelRendering.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Hooks/ModelRendering.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Info.plist create mode 100644 third-party/RemoteInput/RemoteInput/Injection/Injector.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Injection/Injector_Arm.cpp create mode 100644 third-party/RemoteInput/RemoteInput/Injection/Injector_Darwin.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Injection/Injector_Linux.cpp create mode 100644 third-party/RemoteInput/RemoteInput/Injection/Injector_Windows.cxx create mode 100644 third-party/RemoteInput/RemoteInput/JVM.cxx create mode 100644 third-party/RemoteInput/RemoteInput/JVM.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Java/AWTAccessor.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Java/AWTAccessor.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Java/AWTEvent.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Java/AWTEvent.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Java/AWTEventAccessor.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Java/AWTEventAccessor.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Java/Applet.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Java/Applet.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Java/Component.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Java/Component.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Java/Container.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Java/Container.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Java/EventQueue.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Java/EventQueue.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Java/FocusEvent.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Java/FocusEvent.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Java/Frame.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Java/Frame.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Java/InputEvent.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Java/InputEvent.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Java/JNI_Common.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Java/KeyEvent.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Java/KeyEvent.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Java/MouseEvent.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Java/MouseEvent.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Java/MouseWheelEvent.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Java/MouseWheelEvent.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Java/PointerInfo.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Java/PointerInfo.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Java/RIEventQueue.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Java/RIEventQueue.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Java/SunToolkit.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Java/SunToolkit.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Java/Toolkit.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Java/Toolkit.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Java/Window.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Java/Window.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Java/WindowEvent.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Java/WindowEvent.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Platform/DebugConsole.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Platform/DebugConsole.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Platform/JavaInternal.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Platform/NativeHooks.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Platform/NativeHooks_Darwin.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Platform/NativeHooks_Linux.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Platform/NativeHooks_Windows.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Platform/Platform.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Platform/Platform_Darwin.mm create mode 100644 third-party/RemoteInput/RemoteInput/Platform/Platform_Linux.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Platform/Platform_Windows.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Platform/RemoteInputBootstrap_Darwin.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Plugin/ControlCenter.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Plugin/ControlCenter.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Plugin/Graphics.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Plugin/Graphics.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Plugin/ImageData.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Plugin/ImageData.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Plugin/InputOutput.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Plugin/InputOutput.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Plugin/JVM/JVMCache.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Plugin/JVM/JVMCache.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Plugin/JVM/RemoteVM.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Plugin/JVM/RemoteVM.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Plugin/NativePlugin.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Plugin/NativePlugin.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Plugin/Plugin.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Plugin/Plugin.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Plugin/Python/Python.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Plugin/Python/Python.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Plugin/Python/PythonCommon.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Plugin/Python/PythonCommon.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Plugin/Python/PythonCommon_Templates.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Plugin/Python/PythonEIOS.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Plugin/Python/PythonEIOS.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Plugin/Python/PythonJavaArray.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Plugin/Python/PythonJavaArray.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Plugin/Python/PythonJavaList.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Plugin/Python/PythonJavaList.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Plugin/Python/PythonJavaObject.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Plugin/Python/PythonJavaObject.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Plugin/Python/PythonMacros.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Plugin/Python/PythonPlugin.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Plugin/Python/PythonPlugin.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Plugin/Signal.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Plugin/SimbaPlugin.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Plugin/SimbaPlugin.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Plugin/TMemoryManager.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Random.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Random.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Reflection.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Reflection.hxx create mode 100644 third-party/RemoteInput/RemoteInput/ReflectionHook.cxx create mode 100644 third-party/RemoteInput/RemoteInput/ReflectionHook.hxx create mode 100644 third-party/RemoteInput/RemoteInput/RemoteInput.def create mode 100644 third-party/RemoteInput/RemoteInput/RemoteInput.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/CMakeLists.txt create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/Hook.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/Hook.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/Injector.cxx create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/Injector.hxx create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/LICENSE_GPL.txt create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/LICENSE_LGPL.txt create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/README.md create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/include/injector.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/linux/elf.c create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/linux/injector.c create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/linux/injector_internal.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/linux/ptrace.c create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/linux/remote_call.c create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/linux/shellcode.S create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/linux/util.c create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/macos/exc_handler.c create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/macos/injector.c create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/macos/injector_internal.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/macos/mach.c create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/macos/mach_exc.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/macos/mach_excServer.c create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/macos/ptrace.c create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/macos/remote_call.c create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/macos/util.c create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/windows/injector.c create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/windows/injector.def create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/linux_detours/include/detours.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/linux_detours/include/plthook.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/linux_detours/include/types.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/linux_detours/src/barrier.cpp create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/linux_detours/src/detours.cpp create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/linux_detours/src/disasm.cpp create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/linux_detours/src/plthook_elf.cpp create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/linux_detours/src/trampoline_arm.cpp create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/linux_detours/src/trampoline_x86.cpp create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/min_hook/include/MinHook.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/min_hook/src/buffer.c create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/min_hook/src/buffer.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/min_hook/src/hde/hde32.c create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/min_hook/src/hde/hde32.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/min_hook/src/hde/hde64.c create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/min_hook/src/hde/hde64.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/min_hook/src/hde/pstdint.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/min_hook/src/hde/table32.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/min_hook/src/hde/table64.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/min_hook/src/hook.c create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/min_hook/src/trampoline.c create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/min_hook/src/trampoline.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/CMakeLists.txt create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/LICENSE create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/README.md create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/cmake/collect-symbols-pypy.py create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/cmake/collect-symbols.py create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/cmake/darwin-ld-cpython.sym create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/cmake/darwin-ld-pypy.sym create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/cmake/nanobind-config.cmake create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/ext/robin_map/CMakeLists.txt create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/ext/robin_map/LICENSE create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/ext/robin_map/README.md create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/ext/robin_map/cmake/tsl-robin-mapConfig.cmake.in create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/ext/robin_map/include/tsl/robin_growth_policy.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/ext/robin_map/include/tsl/robin_hash.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/ext/robin_map/include/tsl/robin_map.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/ext/robin_map/include/tsl/robin_set.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/eigen/dense.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/eigen/sparse.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/eval.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/intrusive/counter.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/intrusive/counter.inl create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/intrusive/ref.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/make_iterator.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nanobind.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_accessor.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_attr.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_call.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_cast.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_class.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_defs.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_descr.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_enums.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_error.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_func.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_lib.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_misc.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_python.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_traits.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_tuple.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_types.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/ndarray.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/operators.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/stl/array.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/stl/bind_map.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/stl/bind_vector.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/stl/chrono.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/stl/complex.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/stl/detail/chrono.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/stl/detail/nb_array.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/stl/detail/nb_dict.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/stl/detail/nb_list.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/stl/detail/nb_optional.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/stl/detail/nb_set.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/stl/detail/traits.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/stl/filesystem.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/stl/function.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/stl/list.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/stl/map.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/stl/optional.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/stl/pair.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/stl/set.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/stl/shared_ptr.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/stl/string.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/stl/string_view.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/stl/tuple.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/stl/unique_ptr.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/stl/unordered_map.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/stl/unordered_set.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/stl/variant.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/stl/vector.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/stl/wstring.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/trampoline.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/typing.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/src/__init__.py create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/src/__main__.py create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/src/buffer.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/src/common.cpp create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/src/error.cpp create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/src/hash.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/src/implicit.cpp create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/src/nb_combined.cpp create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/src/nb_enum.cpp create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/src/nb_func.cpp create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/src/nb_internals.cpp create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/src/nb_internals.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/src/nb_ndarray.cpp create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/src/nb_static_property.cpp create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/src/nb_type.cpp create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/src/stubgen.py create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/src/trampoline.cpp create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/rd_route/include/rd_route.h create mode 100644 third-party/RemoteInput/RemoteInput/Thirdparty/rd_route/src/rd_route.c create mode 100644 third-party/RemoteInput/RemoteInput/ThreadPool.cxx create mode 100644 third-party/RemoteInput/RemoteInput/ThreadPool.hxx create mode 100644 third-party/RemoteInput/precompiled/libRemoteInput.dll create mode 100644 third-party/RemoteInput/setup.py diff --git a/build.gradle.kts b/build.gradle.kts index 8c7c7b5..854c530 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,7 +7,7 @@ plugins { } group = "com.chromascape" -version = "0.3.0-SNAPSHOT" +version = "0.3.0" // Customize build directories - put DLLs in build/dist layout.buildDirectory.set(file("build")) diff --git a/config/checkstyle/checkstyle-suppressions.xml b/config/checkstyle/checkstyle-suppressions.xml index 781e836..75f930f 100644 --- a/config/checkstyle/checkstyle-suppressions.xml +++ b/config/checkstyle/checkstyle-suppressions.xml @@ -6,11 +6,10 @@ - - + + - diff --git a/src/main/java/com/chromascape/controller/Controller.java b/src/main/java/com/chromascape/controller/Controller.java index f9cdd2b..cc0deb2 100644 --- a/src/main/java/com/chromascape/controller/Controller.java +++ b/src/main/java/com/chromascape/controller/Controller.java @@ -2,9 +2,9 @@ import com.chromascape.utils.core.input.keyboard.VirtualKeyboardUtils; import com.chromascape.utils.core.input.mouse.VirtualMouseUtils; -import com.chromascape.utils.core.input.remoteinput.Kinput; +import com.chromascape.utils.core.input.remoteinput.RemoteInput; +import com.chromascape.utils.core.screen.window.ProcessManagerFactory; import com.chromascape.utils.core.screen.window.ScreenManager; -import com.chromascape.utils.core.screen.window.WindowHandler; import com.chromascape.utils.domain.ocr.Ocr; import com.chromascape.utils.domain.walker.Walker; import com.chromascape.utils.domain.zones.ZoneManager; @@ -30,7 +30,7 @@ private enum ControllerState { private ControllerState state; - private Kinput kinput; + private RemoteInput remoteInput; private VirtualMouseUtils virtualMouseUtils; private VirtualKeyboardUtils virtualKeyboardUtils; private ZoneManager zoneManager; @@ -51,23 +51,20 @@ public Controller() { */ public void init() { logger.info("Setting up Font masks..."); - // Warmup: Pre-load common fonts - try { - Ocr.loadFont("Plain 11"); - Ocr.loadFont("Plain 12"); - Ocr.loadFont("Bold 12"); - } catch (Exception e) { - logger.error("Failed to pre-load fonts during init: {}", e.getMessage()); - } + Ocr.loadFont("Plain 11"); + Ocr.loadFont("Plain 12"); + Ocr.loadFont("Bold 12"); logger.info("Setting up Remote Input Library..."); // Obtain process ID of the target window to initialize input injection - kinput = new Kinput(WindowHandler.getPid(WindowHandler.getTargetWindow())); + remoteInput = new RemoteInput(ProcessManagerFactory.getProcessManager().getPid()); + // Give screen manager access to Remote Input to grab screen buffer + ScreenManager.setRemoteInput(remoteInput); // Initialize virtual input utilities with current window bounds and fullscreen status logger.info("Initialising mouse and keyboard utils..."); - virtualMouseUtils = new VirtualMouseUtils(kinput, ScreenManager.getWindowBounds()); - virtualKeyboardUtils = new VirtualKeyboardUtils(kinput); + virtualMouseUtils = new VirtualMouseUtils(remoteInput); + virtualKeyboardUtils = new VirtualKeyboardUtils(remoteInput); logger.info("Pre-loading and instantiating zones..."); // Initialize zone management with fixed mode option @@ -89,8 +86,7 @@ public void init() { * utilities until re-initialized. */ public void shutdown() { - mouse().getMouseOverlay().eraseOverlay(); - kinput.destroy(); + remoteInput.close(); state = ControllerState.STOPPED; logger.info("Shutting down"); } diff --git a/src/main/java/com/chromascape/scripts/DemoFishingScript.java b/src/main/java/com/chromascape/scripts/DemoFishingScript.java index 86d9094..d8cfd34 100644 --- a/src/main/java/com/chromascape/scripts/DemoFishingScript.java +++ b/src/main/java/com/chromascape/scripts/DemoFishingScript.java @@ -51,7 +51,7 @@ protected void cycle() { if (!checkIfCorrectInventoryLayout()) { logger.warn("Fly-fishing rod must be in inventory slot 27 / idx 26"); logger.warn("Feathers must be in inventory slot 28 / idx 27"); - logger.info("The top of your bait or feather images should be cropped by 10 px"); + logger.info("The top of feather image should be cropped by 10 px"); stop(); } diff --git a/src/main/java/com/chromascape/scripts/DemoWineScript.java b/src/main/java/com/chromascape/scripts/DemoWineScript.java index 71c3722..f04eb66 100644 --- a/src/main/java/com/chromascape/scripts/DemoWineScript.java +++ b/src/main/java/com/chromascape/scripts/DemoWineScript.java @@ -8,6 +8,7 @@ import com.chromascape.utils.core.screen.window.ScreenManager; import java.awt.Point; import java.awt.Rectangle; +import java.awt.event.KeyEvent; import java.awt.image.BufferedImage; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -86,9 +87,9 @@ protected void cycle() { * keyboard controller. */ private void pressEscape() { - controller().keyboard().sendModifierKey(401, "esc"); + controller().keyboard().sendKeyDown(KeyEvent.VK_ESCAPE); waitRandomMillis(80, 100); - controller().keyboard().sendModifierKey(402, "esc"); + controller().keyboard().sendKeyRelease(KeyEvent.VK_ESCAPE); } /** @@ -96,9 +97,9 @@ private void pressEscape() { * keyboard controller. */ private void pressSpace() { - controller().keyboard().sendModifierKey(401, "space"); + controller().keyboard().sendKeyDown(KeyEvent.VK_SPACE); waitRandomMillis(300, 500); - controller().keyboard().sendModifierKey(402, "space"); + controller().keyboard().sendKeyDown(KeyEvent.VK_SPACE); } /** diff --git a/src/main/java/com/chromascape/utils/actions/ItemDropper.java b/src/main/java/com/chromascape/utils/actions/ItemDropper.java index 0536ba3..b416e3b 100644 --- a/src/main/java/com/chromascape/utils/actions/ItemDropper.java +++ b/src/main/java/com/chromascape/utils/actions/ItemDropper.java @@ -4,6 +4,7 @@ import com.chromascape.utils.core.input.distribution.ClickDistribution; import java.awt.Point; import java.awt.Rectangle; +import java.awt.event.KeyEvent; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -19,11 +20,6 @@ public class ItemDropper { private static final Logger logger = LogManager.getLogger(ItemDropper.class); - - // AWT Event IDs for Key Press/Release - private static final int KEY_PRESS = 401; - private static final int KEY_RELEASE = 402; - private static final int INVENTORY_SIZE = 28; /** Defines the order in which items should be dropped. */ @@ -68,8 +64,8 @@ public static void dropAll(BaseScript baseScript, DropPattern pattern, int[] exc List slotsToDrop = generateSlotIndices(pattern); // Start Shift-Drop - baseScript.controller().keyboard().sendModifierKey(KEY_PRESS, "shift"); - BaseScript.waitRandomMillis(100, 250); + baseScript.controller().keyboard().sendKeyDown(KeyEvent.VK_SHIFT); + BaseScript.waitRandomMillis(400, 850); try { for (int slotIndex : slotsToDrop) { @@ -90,7 +86,7 @@ public static void dropAll(BaseScript baseScript, DropPattern pattern, int[] exc } } finally { BaseScript.waitRandomMillis(100, 200); - baseScript.controller().keyboard().sendModifierKey(KEY_RELEASE, "shift"); + baseScript.controller().keyboard().sendKeyRelease(KeyEvent.VK_SHIFT); } } diff --git a/src/main/java/com/chromascape/utils/core/input/keyboard/VirtualKeyboardUtils.java b/src/main/java/com/chromascape/utils/core/input/keyboard/VirtualKeyboardUtils.java index ce07630..a9fffc0 100644 --- a/src/main/java/com/chromascape/utils/core/input/keyboard/VirtualKeyboardUtils.java +++ b/src/main/java/com/chromascape/utils/core/input/keyboard/VirtualKeyboardUtils.java @@ -1,104 +1,119 @@ package com.chromascape.utils.core.input.keyboard; import com.chromascape.base.BaseScript; -import com.chromascape.utils.core.input.remoteinput.Kinput; +import com.chromascape.utils.core.input.remoteinput.RemoteInput; import com.chromascape.utils.core.state.BotState; import com.chromascape.utils.core.state.StateManager; import com.chromascape.utils.core.statistics.StatisticsManager; +import java.awt.event.KeyEvent; +import java.util.Random; /** - * Provides high-level methods for simulating keyboard input using the Kinput API. This utility - * supports sending typed characters, modifier keys (e.g., Shift, Ctrl), and directional keys (arrow - * keys), with synchronized access for safe use in multithreaded environments. + * Provides high-level methods for simulating keyboard input using the RemoteInput API. The user can + * use {@link KeyEvent} objects to dictate exactly what to press and for how long. There is + * functionality to type out a string, compensating for modifier keys where necessary. */ public class VirtualKeyboardUtils { - private final Kinput kinput; + private final RemoteInput input; + + private static final Random RANDOM = new Random(); /** - * Constructs a VirtualKeyboardUtils instance that wraps a Kinput instance. + * Constructs a VirtualKeyboardUtils instance that wraps a RemoteInput instance. * - * @param kinput The Kinput backend used to emit keyboard events to the target window. + * @param input The RemoteInput object that can operate IO */ - public VirtualKeyboardUtils(Kinput kinput) { - this.kinput = kinput; + public VirtualKeyboardUtils(RemoteInput input) { + this.input = input; } /** - * Sends a keyboard event for a character key (e.g., letters, numbers, symbols). Intended for use - * with regular text input simulation. - * - * @param keyChar The character key to send. + * Updates the state of the bot for the {@link BaseScript}'s stop() function and updates the + * BotState for the UI. */ - public synchronized void sendKeyChar(char keyChar) { + private void prepareInput() { BaseScript.checkInterrupted(); StateManager.setState(BotState.ACTING); StatisticsManager.incrementInputs(); - kinput.sendCharEvent(keyChar); } /** - * Updates the necessary states of the bot for the {@link BaseScript}'s stop() function and - * updates the BotState for the UI. + * Sends a key down, given that it isn't already held. As this function requires an int keycode, + * please use {@link KeyEvent}. Typing {@code KeyEvent.VK_} should show contextual actions in your + * IDE of choice, showing you available keys which are mapped to integer keycodes. You may opt to + * look for Java VK keycodes online, however this is the best approach. + * + *

Example usage: {@code controller().keyboard().sendKeyRelease(KeyEvent.VK_SHIFT);} + * + * @param javaKeyCode the {@link KeyEvent} key to hold */ - private void prepareInput() { - BaseScript.checkInterrupted(); - StateManager.setState(BotState.ACTING); - StatisticsManager.incrementInputs(); + public void sendKeyDown(int javaKeyCode) { + prepareInput(); + if (!input.isKeyHeld(javaKeyCode)) { + input.holdKey(javaKeyCode); + } } /** - * Sends a keyboard event for a modifier key (e.g., Shift, Ctrl, Alt, Enter). + * Releases a key, given that it is held. As this function requires an int keycode, please use + * {@link KeyEvent}. Typing {@code KeyEvent.VK_} should show contextual actions in your IDE of + * choice, showing you available keys which are mapped to integer keycodes. You may opt to look + * for Java VK keycodes online, however this is the best approach. * - * @param eventId 401 to simulate a key press, or 402 to simulate a key release. - * @param key The name of the modifier key. Acceptable values: "shift", "enter", "alt", "ctrl", - * "esc", "space". - * @throws IllegalArgumentException if the key name is invalid. + *

Example usage: {@code controller().keyboard().sendKeyRelease(KeyEvent.VK_SHIFT);} + * + * @param javaKeyCode the {@link KeyEvent} key to release */ - public synchronized void sendModifierKey(int eventId, String key) { + public void sendKeyRelease(int javaKeyCode) { prepareInput(); - int keyId = - switch (key.toLowerCase()) { - case "shift" -> 16; - case "enter" -> 10; - case "alt" -> 18; - case "ctrl" -> 17; - case "esc" -> 27; - case "space" -> 32; - default -> throw new IllegalArgumentException("Invalid modifier key: " + key); - }; - kinput.sendVirtualKeyEvent(eventId, keyId, key); + if (input.isKeyHeld(javaKeyCode)) { + input.releaseKey(javaKeyCode); + } + } + + /** + * Checks whether a key is currently being held. + * + * @param javaKeyCode the {@link KeyEvent} key to check + * @return Whether the key is currently being held or not + */ + public boolean isKeyHeld(int javaKeyCode) { + return input.isKeyHeld(javaKeyCode); } /** - * Sends a function key (F1-F12) using the KeyCode slot. + * Types out a given string to the client window using heuristics to mimic a human. Uses default + * heuristic settings for convenience. * - * @param eventId 401 to simulate a key press, or 402 to simulate a key release. - * @param functionKeyNumber The number of the F key (e.g., 6 for F6 or 1 for F1). + * @param string The String of characters to type out in a human like fashion */ - public synchronized void sendFunctionKey(int eventId, int functionKeyNumber) { + public synchronized void sendString(String string) { prepareInput(); - int keyId = 111 + functionKeyNumber; // F1 starts at 112 - kinput.sendVirtualKeyEvent(eventId, keyId, "F" + functionKeyNumber); + for (char c : string.toCharArray()) { + int keyWait = RANDOM.nextInt(30, 60); + int keyModWait = RANDOM.nextInt(30, 60); + int keyPressWait = RANDOM.nextInt(40, 85); + input.sendString(String.valueOf(c), keyWait, keyModWait); + BaseScript.waitMillis(keyPressWait); + } } /** - * Sends a keyboard event for an arrow (directional) key. + * Types out a given string to the client window using heuristics to mimic a human. Internally + * randomises between 1x - 1.1x the given modifier value. * - * @param eventId 401 to simulate a key press, or 402 to simulate a key release. - * @param key The arrow direction. Acceptable values: "up", "down", "left", "right". - * @throws IllegalArgumentException if the arrow direction is invalid. + * @param string The String of characters to type out in a human like fashion + * @param keyWait The amount of time to hold a key down + * @param keyModWait The amount of time to hold a modifier key down (e.g., shift) + * @param keyPressWait The amount of time to wait between pressing keys */ - public synchronized void sendArrowKey(int eventId, String key) { + public synchronized void sendString( + String string, int keyWait, int keyModWait, int keyPressWait) { prepareInput(); - int keyId = - switch (key.toLowerCase()) { - case "left" -> 37; - case "up" -> 38; - case "right" -> 39; - case "down" -> 40; - default -> throw new IllegalArgumentException("Invalid arrow key: " + key); - }; - kinput.sendVirtualKeyEvent(eventId, keyId, key); + for (char c : string.toCharArray()) { + input.sendString(String.valueOf(c), keyWait, keyModWait); + BaseScript.waitMillis(keyPressWait); + } } } diff --git a/src/main/java/com/chromascape/utils/core/input/mouse/MouseOverlay.java b/src/main/java/com/chromascape/utils/core/input/mouse/MouseOverlay.java deleted file mode 100644 index 1fc371e..0000000 --- a/src/main/java/com/chromascape/utils/core/input/mouse/MouseOverlay.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.chromascape.utils.core.input.mouse; - -import java.awt.BasicStroke; -import java.awt.Color; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.Point; -import javax.swing.JFrame; - -/** - * A lightweight transparent overlay used to visually render the virtual mouse position. This - * overlay draws a red "X" at the current mouse coordinates and remains always on top, without - * interfering with user input or system focus. - */ -public class MouseOverlay extends JFrame { - - private Point mousePoint = new Point(0, 0); - - /** - * Constructs the mouse overlay window. The overlay is frameless, transparent, always on top, and - * spans the full screen. It is also hidden from the taskbar and does not steal focus. - */ - public MouseOverlay() { - setUndecorated(true); - setBackground(new Color(0, 0, 0, 0)); - setAlwaysOnTop(true); - setFocusableWindowState(false); - setType(Type.UTILITY); - - setLayout(null); - setVisible(true); - } - - /** - * Updates the overlay to draw the red "X" at the specified mouse location. - * - * @param p The new position to draw the virtual mouse indicator. - */ - public void setMousePoint(Point p) { - this.mousePoint = p; - repaint(); - } - - /** - * Paints the virtual mouse indicator at the last provided point. Draws a red "X" using two - * diagonal lines. - * - * @param g The Graphics context for the overlay window. - */ - @Override - public void paint(Graphics g) { - super.paint(g); - Graphics2D g2d = (Graphics2D) g.create(); - g2d.setColor(Color.RED); - g2d.setStroke(new BasicStroke(2f)); - int size = 6; - - // Convert from global screen space to window-local coordinates - int x = mousePoint.x - getX(); - int y = mousePoint.y - getY(); - - g2d.drawLine(x - size, y - size, x + size, y + size); // Top-left to bottom-right - g2d.drawLine(x - size, y + size, x + size, y - size); // Bottom-left to top-right - - g2d.dispose(); - } - - /** - * Erases the overlay completely, removing it from the screen and freeing resources. After calling - * this method, the overlay will no longer be visible or repaintable. - */ - public void eraseOverlay() { - // Hide the window - setVisible(false); - // Dispose of the JFrame resources - dispose(); - // Reset the mouse point just in case - mousePoint = new Point(0, 0); - } -} diff --git a/src/main/java/com/chromascape/utils/core/input/mouse/VirtualMouseUtils.java b/src/main/java/com/chromascape/utils/core/input/mouse/VirtualMouseUtils.java index 8df7f42..3889cbf 100644 --- a/src/main/java/com/chromascape/utils/core/input/mouse/VirtualMouseUtils.java +++ b/src/main/java/com/chromascape/utils/core/input/mouse/VirtualMouseUtils.java @@ -1,268 +1,280 @@ package com.chromascape.utils.core.input.mouse; import com.chromascape.base.BaseScript; -import com.chromascape.utils.core.input.remoteinput.Kinput; -import com.chromascape.utils.core.screen.window.ScreenManager; +import com.chromascape.utils.core.input.remoteinput.MouseButton; +import com.chromascape.utils.core.input.remoteinput.RemoteInput; import com.chromascape.utils.core.state.BotState; import com.chromascape.utils.core.state.StateManager; import com.chromascape.utils.core.statistics.StatisticsManager; import java.awt.Point; import java.awt.Rectangle; +import java.util.Objects; import java.util.Random; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Consumer; -import javax.swing.SwingUtilities; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; /** - * The orchestrator for human-like virtual mouse behavior. - * - *

Provides movement, clicks, overshoots, pause-corrections, and overlay visuals without - * hijacking the physical system cursor. Designed for use with remote input libraries like {@link - * Kinput}, enabling low-level mouse event simulation. - * - *

Performance Note: This class uses a Producer-Consumer threading model. The physics - * engine (Producer) runs on the calling thread at ~60Hz, while the Input Dispatcher (Consumer) runs - * on a background thread. This ensures that slow hardware input calls do not slow down the physics - * calculation. + * High level abstraction for a user to handle mouse IO through the {@link + * com.chromascape.controller.Controller}. Orchestrator of both the mouse movement physics + * calculation and dispatching of IO via the JNA layer. Uses a producer -> consumer threading model + * to prevent a mouse movement from lagging if the IO fails or stutters. Provides IO capabilities + * such as mouse movement, clicking, and holding of {@link MouseButton}s. */ public class VirtualMouseUtils { /** The current virtual mouse position. */ private Point currentPosition; - /** Timestamp of the last overlay repaint to control frame rate. */ - private long lastOverlayUpdate = 0; - - /** Semi-transparent visual overlay to show virtual mouse position. */ - private final MouseOverlay overlay; - /** Interface for injecting low-level mouse events independently of system mouse. */ - private final Kinput kinput; - - /** Random number generator. */ - private final Random random; + private final RemoteInput input; /** Humanised mouse movement service. */ private final WindMouse windMouse; - /** Logger with appender to publish logs to the web UI. */ - private static final Logger logger = LogManager.getLogger(VirtualMouseUtils.class); + private final Random random = new Random(); - /** The latest point generated by the physics engine, waiting to be consumed by Kinput. */ + /** The latest point generated by the physics engine, waiting to be consumed by input. */ private final AtomicReference pendingInputPoint = new AtomicReference<>(); /** Flag to indicate if the virtual mouse is currently performing a movement path. */ private final AtomicBoolean isMoving = new AtomicBoolean(false); /** - * Lock object to ensure Kinput is accessed by only one thread at a time. + * Lock object to ensure input is accessed by only one thread at a time. * - *

Required because the underlying WebSocket/Connection in Kinput is not thread-safe and cannot + *

Required because the underlying WebSocket/Connection in input is not thread-safe and cannot * handle simultaneous write operations. */ - private final Object kinputLock = new Object(); + private final Object inputLock = new Object(); /** - * The orchestrator for all inputs mouse related. provides human like mouse movement, clicking and - * a little overlay so you can see where it is. + * Constructs the VMU class. Initialises the mouse overlay. Gives the mouse cursor a random start + * position. Starts the input consumer thread. * - * @param kinput The operating system dependant utility to send low level mouse inputs. This is - * how we can still use the system cursor separately while this mouse is active. - * @param bounds Rectangle, containing the screen's bounds. + * @param input A hardware RemoteInput capable object */ - public VirtualMouseUtils(final Kinput kinput, final Rectangle bounds) { - this.kinput = kinput; - overlay = new MouseOverlay(); - overlay.setSize(bounds.width, bounds.height); - - random = new Random(); + public VirtualMouseUtils(RemoteInput input) { + this.input = input; windMouse = new WindMouse(); - // Randomize starting position within the client window - int startX = bounds.x + random.nextInt(bounds.width); - int startY = bounds.y + random.nextInt(bounds.height); - currentPosition = new Point(startX, startY); + randomiseStartPos(); // Initialize atomic reference to prevent null pointer in consumer pendingInputPoint.set(currentPosition); - - // Place overlay at the same randomized position - SwingUtilities.invokeLater(() -> overlay.setMousePoint(currentPosition)); - overlay.setLocation(bounds.x, bounds.y); // keep overlay frame aligned with window - // Start the background Input Consumer thread + startInputConsumerThread(); + } + + /** Starts the input consumer thread to prepare for IO. */ + private void startInputConsumerThread() { Thread inputConsumerThread = new Thread(this::consumeInputLoop, "VirtualMouse-Input-Consumer"); inputConsumerThread.setDaemon(true); // Ensure thread dies when JVM shuts down inputConsumerThread.start(); } /** - * The loop for the background thread. Continuously checks for new points and sends them to - * Kinput. + * Randomises the start position of the cursor within the client's bounds. Only used at startup. + * If the bot ran before and has a persisting cursor, it will default to that instead. + */ + private void randomiseStartPos() { + if (Objects.equals(input.getMousePosition(), new Point(0, 0)) + || input.getMousePosition() == null) { + + Rectangle bounds = input.getTargetDimensions(); + int startX = bounds.x + random.nextInt(bounds.width); + int startY = bounds.y + random.nextInt(bounds.height); + currentPosition = new Point(startX, startY); + } else { + currentPosition = input.getMousePosition(); + } + currentPosition = input.getMousePosition(); + // Initialize atomic reference to prevent null pointer in consumer + pendingInputPoint.set(currentPosition); + } + + /** + * Used alongside the producer thread to reliably move the mouse. While the flag is set to true, + * consumes the latest snapshot of the mouse movement simulation and sends a synchronised call to + * the IO layer to move the mouse. Discards duplicates. */ private void consumeInputLoop() { Point lastSentPoint = null; while (true) { - try { - if (isMoving.get()) { - Point target = pendingInputPoint.get(); - - // Only send input if the point is new (and not null) - if (target != null && !target.equals(lastSentPoint)) { - Point clientPoint = ScreenManager.toClientCoords(target); - - // Prevent collision with clicks or final snaps - synchronized (kinputLock) { - kinput.moveMouse(clientPoint.x, clientPoint.y); - } - - lastSentPoint = target; + if (isMoving.get()) { + // Practically final value, will not change during execution + Point target = pendingInputPoint.get(); + + // Only send input if the point is new + if (target != null && !target.equals(lastSentPoint)) { + synchronized (inputLock) { + input.moveMouse(target); } - } else { - // If not moving, sleep longer to save resources - BaseScript.waitMillis(5); + lastSentPoint = target; } - } catch (Exception e) { - // Log error but keep thread alive - logger.error("Error while consuming inputL {}", e.getMessage()); - logger.error(e.getStackTrace()); + } else { + // If not moving, sleep longer to save resources + BaseScript.waitMillis(5); } } } /** - * Moves the virtual mouse to the given location using the WindMouse algorithm. - * - * @param target The destination point. - * @param speed Speed profile: "slow", "medium", "fast". + * Intended to be called before IO execution. Checks whether the script has been stopped to + * reliably prevent execution. Sets the bot state to acting and increments inputs for the web UI's + * statistics. */ - public void moveTo(final Point target, final String speed) { + private void prepareInput() { BaseScript.checkInterrupted(); StateManager.setState(BotState.ACTING); StatisticsManager.incrementInputs(); + } + + /** + * Moves the mouse to a target destination point onscreen at a given speed using a humanised mouse + * movement algorithm. + * + * @param target The {@link Point} location to travel to + * @param speed How fast the mouse should travel, "slow", "medium" or "fast" + */ + public void moveTo(final Point target, final String speed) { + prepareInput(); // Flag start of movement for the Consumer thread isMoving.set(true); try { - // Run Physics Loop (Producer) on the current thread. - // The callback purely updates the AtomicReference and Overlay. - // It does NOT wait for Kinput. - windMouse.move( - currentPosition, - target, - speed, - p -> { - // Update the shared reference for the Consumer to pick up - pendingInputPoint.set(p); - currentPosition = p; - - // Throttle overlay updates to ~60 FPS (16ms) - long now = System.currentTimeMillis(); - if (now - lastOverlayUpdate >= 16) { - if (!SwingUtilities.isEventDispatchThread()) { - SwingUtilities.invokeLater(() -> overlay.setMousePoint(p)); - lastOverlayUpdate = now; - } - } - }); + // WindMouse's physics loop will run on the current thread, producer + // The hardware IO execution (plus overlay/current state) is the consumer thread + windMouse.move(currentPosition, target, speed, this::moveMouseImpl); } finally { - // Movement finished. - isMoving.set(false); - - // Force the final position update to ensure exact accuracy. - currentPosition = target; - pendingInputPoint.set(target); // Sync consumer for safety + finaliseMovement(target); + } + } - Point finalClientPoint = ScreenManager.toClientCoords(target); + /** + * Callback for the WindMouse algorithm, where it used to execute the mouse movement action. This + * function only updates the state for the consumer thread, however serves as a nod to the source. + * + * @param p The point generated by WindMouse. + */ + private void moveMouseImpl(Point p) { + pendingInputPoint.set(p); + currentPosition = p; + } - // SYNCHRONIZED: Ensure final snap doesn't crash connection - synchronized (kinputLock) { - kinput.moveMouse(finalClientPoint.x, finalClientPoint.y); - } + /** + * Disables the flag for the consumer thread, signaling there is no more work to do. Forces the + * destination point for the internal state, remote input, and overlay; to ensure scripts do not + * fail. + * + * @param target The final mouse destination. + */ + private void finaliseMovement(Point target) { + // Movement finished. + isMoving.set(false); + // Force the final position update to ensure exact accuracy. + currentPosition = target; + pendingInputPoint.set(target); + // Snap to final position to force script safety + // synchronized to prevent race conditions with other IO + synchronized (inputLock) { + input.moveMouse(target); + } + } - SwingUtilities.invokeLater(() -> overlay.setMousePoint(target)); + /** + * Executes a mouse button press and release in a human-like fashion, given which button to press. + * Synchronised as not to collide with other IO. + * + * @param button the mouse button to press + */ + private void click(MouseButton button) { + prepareInput(); + synchronized (inputLock) { + input.holdMouse(button); + BaseScript.waitRandomMillis(50, 80); + input.releaseMouse(button); } } - /** Simulates a left-click at the current virtual mouse location. */ + /** Left clicks at the current mouse position. */ public void leftClick() { - performClick( - (p) -> { - synchronized (kinputLock) { - kinput.clickMouse(p.x, p.y, Kinput.MouseButton.LEFT.id); - // Redundant move ensures OS registers click at correct coords if jitter occurred - kinput.moveMouse(p.x, p.y); - } - }); + click(MouseButton.left); } - /** Simulates a right-click at the current virtual mouse location. */ + /** Right clicks at the current mouse position. */ public void rightClick() { - performClick( - (p) -> { - synchronized (kinputLock) { - kinput.clickMouse(p.x, p.y, Kinput.MouseButton.RIGHT.id); - kinput.moveMouse(p.x, p.y); - } - }); + click(MouseButton.right); + } + + /** Middle clicks at the current mouse position. */ + public void middleClick() { + click(MouseButton.middle); } /** - * Simulates a middle mouse button input. + * Holds down a Mouse button given which button to hold. * - * @param eventType The event code (501 for press, 502 for release). + * @param button the {@link MouseButton} to hold. */ - public void middleClick(int eventType) { - performClick( - (p) -> { - synchronized (kinputLock) { - kinput.middleInput(p.x, p.y, eventType); - } - }); + public void holdMouseButton(MouseButton button) { + prepareInput(); + synchronized (inputLock) { + if (!input.isMouseHeld(button)) { + input.holdMouse(button); + } + } } /** - * Applies a micro-jitter (1–3 pixels) to the virtual cursor, intended to simulate small human - * hand movements when clicking. + * Queries whether a {@link MouseButton} is currently being held. + * + * @param button The {@link MouseButton} to check + * @return Whether it's currently being held */ - public void microJitter() { - if (random.nextBoolean()) { - currentPosition.translate(random.nextInt(-1, 2), random.nextInt(-1, 4)); - - // Update consumer reference so hardware reflects jitter - pendingInputPoint.set(currentPosition); - - SwingUtilities.invokeLater(() -> overlay.setMousePoint(currentPosition)); - - // We can force this update directly as jitter is a single discrete action, - // not a high-frequency loop. - Point clientPoint = ScreenManager.toClientCoords(currentPosition); + public boolean isMouseButtonHeld(MouseButton button) { + return input.isMouseHeld(button); + } - synchronized (kinputLock) { - kinput.moveMouse(clientPoint.x, clientPoint.y); + /** + * Releases a Mouse button given which button to release. + * + * @param button the {@link MouseButton} to release. + */ + public void releaseMouseButton(MouseButton button) { + prepareInput(); + synchronized (inputLock) { + if (input.isMouseHeld(button)) { + input.releaseMouse(button); } } } - /** Helper to perform common action logic (Stats, State, Coordinate Conversion). */ - private void performClick(Consumer action) { - BaseScript.checkInterrupted(); - StateManager.setState(BotState.ACTING); - StatisticsManager.incrementInputs(); - Point clientPoint = ScreenManager.toClientCoords(currentPosition); - action.accept(clientPoint); - } - /** - * Returns the Mouse overlay object. Intended to be used by the Controller to destroy the overlay. + * Scrolls the mouse given the direction and amount. The mouse must have moved in the particular + * session for scrolling to take effect. * - * @return The MouseOverlay object. + * @param totalNotches Amount of notches to scroll + * @param down true if scrolling down, false if scrolling up */ - public MouseOverlay getMouseOverlay() { - return overlay; + public void scrollMouse(int totalNotches, boolean down) { + int notchesSent = 0; + int k = 1; + + int step = (int) Math.round(random.nextGaussian() * 0.6 + 6.0); + + while (notchesSent < totalNotches) { + input.scrollMouse(down ? 1 : -1); + notchesSent++; + + if (k % step == 0) { + step = (int) Math.round(random.nextGaussian() * 0.6 + 6.0); + BaseScript.waitRandomMillis(215, 410); + k = 0; + } else { + BaseScript.waitRandomMillis(25, 46); + } + k++; + } } } diff --git a/src/main/java/com/chromascape/utils/core/input/mouse/WindMouse.java b/src/main/java/com/chromascape/utils/core/input/mouse/WindMouse.java index fe4ae84..9d0d28f 100644 --- a/src/main/java/com/chromascape/utils/core/input/mouse/WindMouse.java +++ b/src/main/java/com/chromascape/utils/core/input/mouse/WindMouse.java @@ -1,3 +1,19 @@ +/** + * Copyright 2006-2013 by Benjamin J. Land (a.k.a. BenLand100) + * + *

This file is part of the SMART Minimizing Autoing Resource Thing (SMART) + * + *

SMART is free software: you can redistribute it and/or modify it under the terms of the GNU + * General Public License as published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + *

SMART is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + *

You should have received a copy of the GNU General Public License along with SMART. If not, + * see . + */ package com.chromascape.utils.core.input.mouse; import static java.util.concurrent.locks.LockSupport.parkNanos; @@ -7,25 +23,30 @@ import java.util.function.Consumer; /** - * A robust implementation of the WindMouse algorithm for human-like mouse movement. * - *

This class simulates mouse movement using a physics-based model that accounts for: + * + *

+ * + * "The WindMouse algorithm is inspired by highschool physics that me-of-fifteen-years-ago was just + * getting interested in. The cursor is modeled as an object with some inertia (mass) that is acted + * on by two forces: * *
    - *
  • Gravity: A directional force pulling the cursor towards the target. - *
  • Wind: A chaotic force adding randomness to the path to mimic human imperfection. - *
  • Momentum: Velocity preservation with friction/braking near the target. + *
  • Gravity, which is constant in magnitude (a configurable parameter) and always points + * towards the final destination. + *
  • Wind, which exerts a random force in a random direction, and smoothly changes in both + * magnitude and direction over time." - BenLand100 *
* - *

The implementation has been tuned for standard simulation (approx. 60Hz internal logic) to - * ensure compatibility with standard client ticking rates while maintaining the statistical - * properties of the original algorithm. + *

+ * + *

This modified impl has been tuned to 60hz as opposed to 30 * *

Original Algorithm by BenLand100. Tweaked * "WindMouse2" implementation by holic. - * Adapted for ChromaScape by StaticSweep. + * Adapted for ChromaScape. */ public class WindMouse { @@ -40,18 +61,14 @@ public class WindMouse { * @param target The destination coordinates. * @param speedProfile A string constant determining movement characteristics ("slow", "medium", * "fast"). Defaults to "medium" if the profile is unrecognized. - * @param onMove A {@link Consumer} that accepts a {@link Point} for every step of the path. This - * is typically used to trigger the actual hardware or robot input. + * @param moveMouseImpl A {@link Consumer} that accepts a {@link Point} for every step of the + * path. This is typically used to trigger the actual hardware or robot input. */ - public void move(Point start, Point target, String speedProfile, Consumer onMove) { - // defaults (Tuned for ~60Hz update rate) - // Speed: Controls loop frequency (higher = lower sleep times). - // Gravity/Wind: Forces applied per loop iteration. + public void move(Point start, Point target, String speedProfile, Consumer moveMouseImpl) { double mouseSpeed = 30; double mouseGravity = 4.5; double mouseWind = 1.5; - // Adjust parameters based on profile switch (speedProfile.toLowerCase()) { case "slow" -> { mouseSpeed = 20; @@ -68,22 +85,21 @@ public void move(Point start, Point target, String speedProfile, Consumer } } - windMouse2(start, target, mouseGravity, mouseWind, mouseSpeed, onMove); + windMouse2(start, target, mouseGravity, mouseWind, mouseSpeed, moveMouseImpl); } /** - * Orchestrates the movement logic, optionally generating an intermediate waypoint for - * long-distance moves. + * Moves the mouse from the current position to the specified position. Approximates human + * movement in a way where smoothness and accuracy are relative to speed, as it should be. * - *

If the distance is greater than 250 pixels, there is a 50% chance an intermediate point will - * be generated to simulate a human "correction arc" or two-stage movement. + *

Algorithm by BenLand100, modified by holic and later ChromaScape. * * @param start The starting point. * @param target The final destination. * @param gravity The gravitational pull towards the target. * @param wind The magnitude of random perturbations. * @param speed The timing speed factor. - * @param onMove The callback for cursor updates. + * @param moveMouseImpl The callback for cursor updates. */ private void windMouse2( Point start, @@ -91,10 +107,8 @@ private void windMouse2( double gravity, double wind, double speed, - Consumer onMove) { + Consumer moveMouseImpl) { - // Random intermediate point for long distances (simulates human "arc" or - // "correction") Point intermediate = (distance(target, start) > 250 && random.nextInt(2) == 1) ? randomPoint(target, start) @@ -110,33 +124,39 @@ private void windMouse2( wind, speed, random.nextInt(10, 25), - onMove); - // Small pause between "stages" of movement to look natural + moveMouseImpl); + + // Small pause between each movement sleepPrecise(random.nextInt(1, 150)); start = intermediate; // Continue from intermediate } // Move to final target windMouseImpl( - start.x, start.y, target.x, target.y, gravity, wind, speed, random.nextInt(10, 25), onMove); + start.x, + start.y, + target.x, + target.y, + gravity, + wind, + speed, + random.nextInt(10, 25), + moveMouseImpl); } /** - * The core WindMouse physics simulation loop. + * Internal mouse movement algorithm. Do not use this without credit to either Benjamin J. Land or + * BenLand100. This is synchronized to prevent multiple motions and bannage. * - *

This method runs a loop that continuously calculates velocity vectors based on gravity - * (distance to target) and wind (random noise). It applies these vectors to the current position - * and triggers the {@code onMove} callback. - * - * @param xs Current X position. - * @param ys Current Y position. - * @param xe Target X position. - * @param ye Target Y position. - * @param gravity The strength of the pull towards the target. - * @param wind The strength of the random wind forces. - * @param speed Controls the sleep duration between steps. - * @param targetArea The radius (in pixels) around the target where "braking" logic begins. - * @param onMove The callback to execute when the integer coordinates change. + * @param xs The x start + * @param ys The y start + * @param xe The x destination + * @param ye The y destination + * @param gravity Strength pulling the position towards the destination + * @param wind Strength pulling the position in random directions + * @param speed Influences the rate of sleeps, speeding up or slowing down the routine + * @param targetArea Radius of area around the destination that should trigger slowing, prevents + * spiraling */ private void windMouseImpl( double xs, @@ -151,7 +171,6 @@ private void windMouseImpl( double dist, veloX = 0, veloY = 0, windX = 0, windY = 0; - // Pre-calculated square roots for vector normalization double sqrt2 = Math.sqrt(2); double sqrt3 = Math.sqrt(3); double sqrt5 = Math.sqrt(5); @@ -159,29 +178,19 @@ private void windMouseImpl( int tDist = (int) distance(new Point((int) xs, (int) ys), new Point((int) xe, (int) ye)); long t = System.currentTimeMillis() + 10000; // 10-second timeout safety - // Loop until we are within 3 pixels. - // Stopping at 3px prevents the "micro-orbiting" physics glitch where gravity - // causes the cursor to overshoot and circle the target pixel indefinitely. while ((dist = Math.hypot((xs - xe), (ys - ye))) >= 3) { if (System.currentTimeMillis() > t) break; - // Cap wind force so it doesn't exceed the remaining distance wind = Math.min(wind, dist); - // Adaptive step size based on total distance long d = (Math.round((Math.round(((double) (tDist))) * 0.3)) / 7); if (d > 20) d = 20; if (d < 5) d = 5; - // Occasional random slowdown if (random.nextInt(6) == 0) { d = 2; } - // Max step calculation. - // Multiplier set to 1.5 (Standard WindMouse). - // Since we run at 60Hz (half the 120Hz rate), we need to cover twice the - // distance per frame to preserve the visual speed. double maxStep = (Math.min(d, Math.round(dist))) * 1.5; if (dist >= targetArea) { @@ -190,23 +199,17 @@ private void windMouseImpl( windX = (windX / sqrt3) + ((random.nextInt(windRange) - wind) / sqrt5); windY = (windY / sqrt3) + ((random.nextInt(windRange) - wind) / sqrt5); } else { - // Short range (Entering Target Area): - // Dampen wind (divide by sqrt2) + windX = (windX / sqrt2); windY = (windY / sqrt2); - // (Friction) - // Multiplier 0.64 allows velocity to decay naturally over the standard frame count. - // (Previously 0.80 at 120Hz; 0.80^2 approx 0.64 for 60Hz). veloX *= 0.64; veloY *= 0.64; } - // Apply forces to velocity veloX += windX + gravity * (xe - xs) / dist; veloY += windY + gravity * (ye - ys) / dist; - // Cap velocity at maxStep if (Math.hypot(veloX, veloY) > maxStep) { maxStep = ((maxStep / 2) < 1) ? 2 : maxStep; double randomDist = (maxStep / 2) + random.nextInt((int) (Math.round(maxStep) / 2)); @@ -215,21 +218,16 @@ private void windMouseImpl( veloY = (veloY / veloMag) * randomDist; } - // Update position int lastX = ((int) (Math.round(xs))); int lastY = ((int) (Math.round(ys))); xs += veloX; ys += veloY; - // Only fire callback if the integer pixel coordinate actually changed if ((lastX != Math.round(xs)) || (lastY != Math.round(ys))) { Point newP = new Point((int) Math.round(xs), (int) Math.round(ys)); if (onMove != null) onMove.accept(newP); } - // Sleep calculation - // Logic yields ~10-20ms sleeps, targeting standard 60Hz. - // (Multiplier increased from 6 to 12 to double sleep times). int w = random.nextInt((int) (Math.round(100.0 / speed))) * 12; if (w < 10) { w = 10; @@ -239,7 +237,6 @@ private void windMouseImpl( sleepPrecise(w); } - // Instantly bridge the last <3 pixels to ensure pixel-perfect accuracy. if ((Math.round(xe) != Math.round(xs)) || (Math.round(ye) != Math.round(ys))) { Point finalP = new Point((int) Math.round(xe), (int) Math.round(ye)); if (onMove != null) onMove.accept(finalP); @@ -247,31 +244,20 @@ private void windMouseImpl( } /** - * Executes a high-precision sleep using a hybrid approach. - * - *

Standard {@link Thread#sleep(long)} is too coarse (~15ms resolution on Windows) for smooth - * mouse movement. Pure busy-waiting burns 100% CPU. - * - *

This method parks the thread (yields CPU) for the majority of the duration, then switches to - * a busy-wait spin loop for the final millisecond to ensure sub-millisecond precision. + * Precisely sleeps for a given length of time, as other approaches aren't as accurate. * * @param millis The duration to sleep in milliseconds. */ private void sleepPrecise(long millis) { long end = System.nanoTime() + millis * 1_000_000L; long timeLeft = end - System.nanoTime(); - - // If we have more than 2ms, we can afford to park the thread to save CPU. - // We wake up 1ms early to spin for the final precision. while (timeLeft > 2_000_000L) { parkNanos(timeLeft - 1_000_000L); timeLeft = end - System.nanoTime(); } - // Busy-wait for the final <1ms while (System.nanoTime() < end) { try { - // Java 9+ hint to CPU to optimize power consumption during spin-waits java.lang.Thread.onSpinWait(); } catch (NoSuchMethodError e) { // Fallback for older JDKs (implicitly just busy-waits) @@ -284,7 +270,7 @@ private void sleepPrecise(long millis) { * * @param p1 The first point. * @param p2 The second point. - * @return pixel perfect distance. + * @return The distance. */ private double distance(Point p1, Point p2) { return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2)); @@ -304,10 +290,7 @@ private Point randomPoint(Point p1, Point p2) { } /** - * Generates a pseudo-random floating-point value between two bounds. - * - *

This utility is used to calculate randomized intermediate coordinates (waypoints) when - * generating the mouse path. It handles both positive and negative directions automatically. + * Generates a random floating-point value between two bounds. * * @param corner1 The first boundary (e.g., the starting coordinate). * @param corner2 The second boundary (e.g., the target coordinate). diff --git a/src/main/java/com/chromascape/utils/core/input/remoteinput/ControlKey.java b/src/main/java/com/chromascape/utils/core/input/remoteinput/ControlKey.java new file mode 100644 index 0000000..1bc349f --- /dev/null +++ b/src/main/java/com/chromascape/utils/core/input/remoteinput/ControlKey.java @@ -0,0 +1,24 @@ +package com.chromascape.utils.core.input.remoteinput; + +/** + * Key value pair enum containing keys and their Java key code. Used to define the preferred Java + * keycode for RemoteInput. e.g. enter is typically 10, we want 13. + */ +public enum ControlKey { + VK_LEFT_CONTROL(162), + VK_LEFT_ALT(164), + VK_RIGHT_ALT(165), + VK_LEFT_WINDOWS(91), + VK_RETURN(13); + + public final int nativeCode; + + /** + * Constructs the enum with an extra value, which contains the java keycode. + * + * @param nativeCode the Java keycode + */ + ControlKey(int nativeCode) { + this.nativeCode = nativeCode; + } +} diff --git a/src/main/java/com/chromascape/utils/core/input/remoteinput/Kinput.java b/src/main/java/com/chromascape/utils/core/input/remoteinput/Kinput.java deleted file mode 100644 index b0dfe8f..0000000 --- a/src/main/java/com/chromascape/utils/core/input/remoteinput/Kinput.java +++ /dev/null @@ -1,321 +0,0 @@ -package com.chromascape.utils.core.input.remoteinput; - -import com.sun.jna.Library; -import com.sun.jna.Native; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Random; - -/** - * Provides a wrapper for the native Kinput interface, allowing injection of mouse and keyboard - * events into a target Java application's process via JNA and Kinput DLLs. - * - *

This class is responsible for: - * - *

- * - *

Intended for use with 64-bit Java applications that expose a {@code java.awt.Canvas}. - */ -public class Kinput { - - /** JNA interface mapping for KInputCtrl.dll native methods. */ - public interface KinputInterface extends Library { - - /** - * Creates a Kinput instance for the specified process ID. - * - * @param pid the target process ID - * @return true if creation succeeded; false otherwise - */ - boolean KInput_Create(int pid); - - /** - * Deletes the Kinput instance for the specified process ID. - * - * @param pid the target process ID - * @return true if deletion succeeded; false otherwise - */ - boolean KInput_Delete(int pid); - - /** - * Sends a focus event to the target process. - * - * @param pid the target process ID - * @param eventID the focus event type (e.g., gain or loss) - * @return true if event succeeded; false otherwise - */ - boolean KInput_FocusEvent(int pid, int eventID); - - /** - * Sends a keyboard event to the target process. - * - * @param pid the target process ID - * @param eventID the keyboard event ID - * @param when event timestamp for queuing - * @param modifiers key modifiers - * @param keyCode virtual key code - * @param keyChar the character associated - * @param keyLocation key location - * @return true if the event was successfully sent; false otherwise - */ - boolean KInput_KeyEvent( - int pid, - int eventID, - long when, - int modifiers, - int keyCode, - short keyChar, - int keyLocation); - - /** - * Sends a mouse event to the target process. - * - * @param pid the target process ID - * @param eventID the mouse event type ID - * @param when event timestamp for queuing - * @param modifiers mouse modifiers - * @param x the x-coordinate of the mouse event - * @param y the y-coordinate of the mouse event - * @param clickCount number of clicks - * @param popupTrigger whether this event should pop up - * @param button mouse button ID - * @return true if the event was successfully sent; false otherwise - */ - boolean KInput_MouseEvent( - int pid, - int eventID, - long when, - int modifiers, - int x, - int y, - int clickCount, - boolean popupTrigger, - int button); - } - - private static final int FOCUS_GAINED = 1004; - private static final int FOCUS_LOST = 1005; - - private final int pid; - private final KinputInterface kinput; - - /** Mouse event type IDs mapped to standard AWT semantics. */ - public enum MouseEventType { - MOUSE_CLICK(500), - MOUSE_PRESS(501), - MOUSE_RELEASE(502), - MOUSE_MOVE(503), - MOUSE_ENTER(504), - MOUSE_EXIT(505), - MOUSE_DRAG(506), - MOUSE_WHEEL(507); - - public final int id; - - MouseEventType(int id) { - this.id = id; - } - } - - /** Mouse button IDs. */ - public enum MouseButton { - NONE(0), - LEFT(1), - MIDDLE(2), - RIGHT(3); - - public final int id; - - MouseButton(int id) { - this.id = id; - } - } - - /** - * Constructs a new Kinput instance for the given process ID. - * - * @param pid the process ID of the target Java application - */ - public Kinput(int pid) { - this.pid = pid; - this.kinput = loadLibrary(); - if (!kinput.KInput_Create(pid)) { - throw new RuntimeException("Failed to create Kinput instance for PID: " + pid); - } - } - - /** - * Loads the Kinput native libraries from the build directory and prepares them for JNA. - * - * @return the loaded KinputInterface - */ - private static KinputInterface loadLibrary() { - try { - // Try to load from build/dist directory first (preferred) - Path buildDistPath = Path.of("build/dist"); - Path dllFileCtrl = buildDistPath.resolve("KInputCtrl.dll"); - Path dllFile64 = buildDistPath.resolve("KInput.dll"); - - if (Files.exists(dllFileCtrl) && Files.exists(dllFile64)) { - // Use build directory directly - System.setProperty("jna.library.path", buildDistPath.toAbsolutePath().toString()); - return Native.load("KInputCtrl", KinputInterface.class); - } - - // If DLLs are not found in build directory, throw an error - throw new IOException( - "Missing native libraries in build/dist directory. Please run the build process first."); - } catch (Throwable e) { - throw new RuntimeException("Failed to load Kinput DLLs", e); - } - } - - /** Sleeps briefly to simulate human-like click timing. */ - private void sleepHumanClick() { - try { - Thread.sleep(new Random().nextInt(50, 80)); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - - /** Ensures the target window is focused before sending input. */ - private synchronized void focus() { - if (!kinput.KInput_FocusEvent(pid, FOCUS_GAINED)) { - throw new RuntimeException("Focus event failed for PID: " + pid); - } - } - - /** - * Sends a mouse press and release to a specified screen co-ordinate. This operates in client - * relative co-ordinates, not screen relative co-ordinates. - * - * @param x the x co-ordinate of the action. - * @param y the y co-ordinate of the action. - * @param button which {@link MouseButton} to use. - */ - public synchronized void clickMouse(int x, int y, int button) { - focus(); - if (!kinput.KInput_MouseEvent( - pid, - MouseEventType.MOUSE_PRESS.id, - System.currentTimeMillis(), - 1, - x, - y, - 1, - false, - button)) { - throw new RuntimeException(button + " press failed"); - } - sleepHumanClick(); - if (!kinput.KInput_MouseEvent( - pid, - MouseEventType.MOUSE_RELEASE.id, - System.currentTimeMillis(), - 1, - x, - y, - 1, - false, - button)) { - throw new RuntimeException(button + " release failed"); - } - } - - /** Sends a middle mouse button input. */ - public synchronized void middleInput(int x, int y, int eventID) { - focus(); - if (!kinput.KInput_MouseEvent( - pid, eventID, System.currentTimeMillis(), 1, x, y, 1, false, MouseButton.MIDDLE.id)) { - throw new RuntimeException("Middle mouse event failed"); - } - } - - /** Moves the mouse cursor to a given screen coordinate. */ - public synchronized void moveMouse(int x, int y) { - focus(); - if (!kinput.KInput_MouseEvent( - pid, - MouseEventType.MOUSE_ENTER.id, - System.currentTimeMillis(), - 0, - x, - y, - 0, - false, - MouseButton.NONE.id)) { - throw new RuntimeException("Mouse enter failed"); - } - if (!kinput.KInput_MouseEvent( - pid, - MouseEventType.MOUSE_MOVE.id, - System.currentTimeMillis(), - 0, - x, - y, - 0, - false, - MouseButton.NONE.id)) { - throw new RuntimeException("Mouse move failed"); - } - } - - /** - * Sends a key character to the client window. - * - * @param eventID - 401 to press/ 402 to release. - * @param keyChar - The key character to send. - */ - public synchronized void sendKeyEvent(int eventID, char keyChar) { - focus(); - if (!kinput.KInput_KeyEvent( - pid, eventID, System.currentTimeMillis(), 0, 0, (short) keyChar, 0)) { - throw new RuntimeException("Key event failed for char: " + keyChar); - } - } - - /** - * Types a character to the client window. - * - * @param keyChar The character to send. - */ - public synchronized void sendCharEvent(char keyChar) { - focus(); - // Passes keyChar into the 'short' parameter (arg5) - if (!kinput.KInput_KeyEvent(pid, 400, System.currentTimeMillis(), 0, 0, (short) keyChar, 0)) { - throw new RuntimeException("Character event failed for char: '" + keyChar + "'"); - } - } - - /** - * Sends a virtual key event (Arrow, Modifier, F-Key) to the client window's SunAwtCanvas. - * - * @param eventID 401 for press, 402 for release. - * @param keyCode The virtual key code. - * @param keyName A descriptive name used purely for error logging. - */ - public synchronized void sendVirtualKeyEvent(int eventID, int keyCode, String keyName) { - focus(); - // Passes keyCode into the integer parameter (arg4) - if (!kinput.KInput_KeyEvent( - pid, eventID, System.currentTimeMillis(), 0, keyCode, (short) 0, 0)) { - throw new RuntimeException("Virtual key event failed for: " + keyName); - } - } - - /** - * Destroys the active KInput instance. Should be called on shutdown to free memory and close - * resources. - */ - public synchronized void destroy() { - if (!kinput.KInput_Delete(pid)) { - throw new RuntimeException("Failed to delete KInput instance"); - } - } -} diff --git a/src/main/java/com/chromascape/utils/core/input/remoteinput/MouseButton.java b/src/main/java/com/chromascape/utils/core/input/remoteinput/MouseButton.java new file mode 100644 index 0000000..e62b0fa --- /dev/null +++ b/src/main/java/com/chromascape/utils/core/input/remoteinput/MouseButton.java @@ -0,0 +1,13 @@ +package com.chromascape.utils.core.input.remoteinput; + +/** + * Enum containing the available mouse buttons for use with RemoteInput. Namely, the functions + * {@link RemoteInput#holdMouse(MouseButton)}, {@link RemoteInput#releaseMouse(MouseButton)}, and + * {@link RemoteInput#isMouseHeld(MouseButton)}. The ordinal of each mouse button refers to the + * integer value required by RI. + */ +public enum MouseButton { + right, + left, + middle +} diff --git a/src/main/java/com/chromascape/utils/core/input/remoteinput/RemoteInput.java b/src/main/java/com/chromascape/utils/core/input/remoteinput/RemoteInput.java new file mode 100644 index 0000000..b682edd --- /dev/null +++ b/src/main/java/com/chromascape/utils/core/input/remoteinput/RemoteInput.java @@ -0,0 +1,333 @@ +package com.chromascape.utils.core.input.remoteinput; + +import com.sun.jna.Native; +import com.sun.jna.Pointer; +import com.sun.jna.ptr.IntByReference; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.event.KeyEvent; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +/** + * RemoteInput allows ChromaScape to send Java AWT signals to a target Java app, to simulate IO. + * This approach as opposed to sending OS signals, allows the user to fully minimise and or cover + * the target app. It also allows the user to keep using their computer as they wish, as if + * ChromaScape was never running. This class provides functionality to load a RemoteInput binary + * regardless of operating system, provide IO to the target application, and receive the most + * up-to-date snapshot of the application's Java canvas (updated whenever they draw a new frame). + */ +public class RemoteInput implements AutoCloseable { + + private static final String COMPILED_BINARY_FILENAME = "libRemoteInput" + getExtension(); + + private final int pid; + + /** JNA needs to load the binary as an interface, the interface acts as the exported headers. */ + private final RemoteInputInterface remoteInput; + + /** + * RemoteInput returns a Pointer which acts as a reference to a specific client/target. A single + * instance of RI can support several targets. RI requests this pointer when performing IO, to + * specify which target to use. + */ + private Pointer target; + + /** + * Constructs the RemoteInput class. + * + * @param pid The process ID of the target Java application + */ + public RemoteInput(int pid) { + this.pid = pid; + this.remoteInput = loadRemoteInput(); + initialise(); + } + + /** + * The RI binary can be compiled on Linux, Mac and Windows. This function detects OS and applies + * the corresponding filetype. + * + * @return OS specific filetype + */ + private static String getExtension() { + String os = System.getProperty("os.name").toLowerCase(); + if (os.contains("win")) { + return ".dll"; + } + if (os.contains("mac")) { + return ".dylib"; + } + return ".so"; + } + + /** + * RemoteInput expects specific Java keycodes for several keys, compared to generic keycodes. + * e.g., enter is typically 10, we want 13. + * + * @param javaKeyCode The {@link KeyEvent} Java keycode + * @return RemoteInput's preferred keycode + */ + private int toNativeCode(int javaKeyCode) { + return switch (javaKeyCode) { + case KeyEvent.VK_ENTER -> ControlKey.VK_RETURN.nativeCode; + case KeyEvent.VK_CONTROL -> ControlKey.VK_LEFT_CONTROL.nativeCode; + case KeyEvent.VK_ALT -> ControlKey.VK_LEFT_ALT.nativeCode; + case KeyEvent.VK_ALT_GRAPH -> ControlKey.VK_RIGHT_ALT.nativeCode; + case KeyEvent.VK_WINDOWS -> ControlKey.VK_LEFT_WINDOWS.nativeCode; + default -> javaKeyCode; + }; + } + + /** + * Checks if the target application is already a registered client. If not, will inject RI into + * the application. Finally, it pairs with the target + */ + private void initialise() { + if (!isInjectedClient()) { + remoteInput.EIOS_Inject_PID(pid); + } + pairClient(); + } + + /** + * Checks if the target application is already registered within RI's internal list of connected + * clients. A registered client is one that already has RI injected into it. + * + * @return Whether the target application is registered + */ + private boolean isInjectedClient() { + long totalClients = remoteInput.EIOS_GetClients(false); + + for (long i = 0; i < totalClients; i++) { + if (remoteInput.EIOS_GetClientPID(i) == pid) { + return true; + } + } + return false; + } + + /** + * Grants IO operation over a registered client by returning a pointer to reference a specific + * client. + * + * @see #target <- The returned pointer + */ + private void pairClient() { + target = remoteInput.EIOS_RequestTarget(String.valueOf(pid)); + if (target == null) { + throw new RuntimeException("Target Not Found with pid: " + pid); + } + } + + /** + * Loads the RemoteInput binary as a {@link RemoteInputInterface} object to allow Java to + * communicate directly to the native binary. Will first check if a user compiled binary exists, + * if not, uses a provided pre-compiled binary. + * + * @return An interface that acts as a bridge to talk to the binary in Java, used within this + * class to provide IO operations + */ + private static RemoteInputInterface loadRemoteInput() { + Path binaryFile = + Paths.get("third-party", "RemoteInput", "cmake-build-release", COMPILED_BINARY_FILENAME); + if (!Files.exists(binaryFile)) { + binaryFile = Paths.get("third-party", "RemoteInput", "precompiled", COMPILED_BINARY_FILENAME); + } + + try { + return Native.load(binaryFile.toString(), RemoteInputInterface.class); + } catch (UnsatisfiedLinkError e) { + throw new RuntimeException("Unable to load RemoteInput binary from path", e); + } + } + + /** + * Retrieves the memory pointer to the target's current image buffer. The buffer contains pixel + * data in BGRA (Blue, Green, Red, Alpha) format. Each pixel occupies 4 bytes. The total size of + * the buffer is {@code width * height * 4} bytes. The data is updated automatically by the native + * hooks in the target application whenever a frame is rendered. + * + * @return A pointer to the start of the BGRA pixel array + */ + public synchronized Pointer getImageBuffer() { + Pointer p = remoteInput.EIOS_GetImageBuffer(target); + if (p == null) { + throw new RuntimeException("Image Buffer Not Found with pid: " + pid); + } + return p; + } + + /** + * Retrieves a memory pointer similarly to {@link #getImageBuffer()}. This method however will + * contain the mouse pointer and any objects drawn onto the canvas. + * + * @return A pointer to the start of the BGRA pixel array + */ + public synchronized Pointer getDebugImageBuffer() { + Pointer p = remoteInput.EIOS_GetDebugImageBuffer(target); + if (p == null) { + throw new RuntimeException("Image Buffer Not Found with pid: " + pid); + } + return p; + } + + /** Will get focus of the client if in an unfocused state, necessary for mouse input. */ + private void getFocusIfNotFocused() { + if (!remoteInput.EIOS_HasFocus(target)) { + remoteInput.EIOS_GainFocus(target); + } + } + + /** Will enable keyboard input if it's currently disabled, necessary for keyboard input. */ + private void setKeyboardInputIfDisabled() { + if (!remoteInput.EIOS_IsKeyboardInputEnabled(target)) { + remoteInput.EIOS_SetKeyboardInputEnabled(target, true); + } + } + + /** + * RemoteInput stores an internal mouse position that stays persistent after ChromaScape restarts. + * This will return that mouse position. VirtualMouseUtils automatically syncs to this unless + * RemoteInput is pairing for the first time, where it'll randomise mouse position. + * + * @return A {@link Point} referring to the client relative mouse position + */ + public synchronized Point getMousePosition() { + IntByReference x = new IntByReference(); + IntByReference y = new IntByReference(); + remoteInput.EIOS_GetMousePosition(target, x, y); + return new Point(x.getValue(), y.getValue()); + } + + /** + * Gets the target app's window dimensions. Due to all IO being performed in client relative + * space, the user can assume the origin as 0,0. + * + * @return A rectangle defining the origin and bounds of the target application + */ + public synchronized Rectangle getTargetDimensions() { + IntByReference x = new IntByReference(); + IntByReference y = new IntByReference(); + remoteInput.EIOS_GetTargetDimensions(target, x, y); + return new Rectangle(0, 0, x.getValue(), y.getValue()); + } + + /** + * Sends a key down in the target java app. To get the keycode, use a {@link + * java.awt.event.KeyEvent} object. Example: {@code KeyEvent.VK_ENTER}. + * + * @param javaKeyCode The Java keycode corresponding to the key being pressed + */ + public synchronized void holdKey(int javaKeyCode) { + setKeyboardInputIfDisabled(); + getFocusIfNotFocused(); + remoteInput.EIOS_HoldKey(target, toNativeCode(javaKeyCode)); + } + + /** + * Queries whether a key is currently being held. + * + * @param javaKeyCode The Java keycode of the key in question + * @return Whether the key is currently being held + */ + public synchronized boolean isKeyHeld(int javaKeyCode) { + return remoteInput.EIOS_IsKeyHeld(target, toNativeCode(javaKeyCode)); + } + + /** + * Sends a key release event to the target Java app. This should be used in conjunction with + * {@link #holdKey(int)} to simulate a full key press. + * + * @param javaKeyCode The Java keycode corresponding to the key being released + */ + public synchronized void releaseKey(int javaKeyCode) { + setKeyboardInputIfDisabled(); + getFocusIfNotFocused(); + remoteInput.EIOS_ReleaseKey(target, toNativeCode(javaKeyCode)); + } + + /** + * Sends a mouse button down in the target Java app. + * + * @param button The {@link MouseButton} button to hold + */ + public synchronized void holdMouse(MouseButton button) { + getFocusIfNotFocused(); + Point mousePosition = getMousePosition(); + remoteInput.EIOS_HoldMouse(target, mousePosition.x, mousePosition.y, button.ordinal()); + } + + /** + * Queries whether a {@link MouseButton} is currently held. + * + * @param button The {@link MouseButton} to check + * @return If the mouse button is currently being held + */ + public synchronized boolean isMouseHeld(MouseButton button) { + return remoteInput.EIOS_IsMouseHeld(target, button.ordinal()); + } + + /** + * Releases a mouse button at the designated client local co-ordinates. + * + * @param button The {@link MouseButton} to release + */ + public synchronized void releaseMouse(MouseButton button) { + getFocusIfNotFocused(); + Point mousePosition = getMousePosition(); + remoteInput.EIOS_ReleaseMouse(target, mousePosition.x, mousePosition.y, button.ordinal()); + } + + /** + * Moves a mouse to a designated client local co-ordinate. + * + * @param location The {@link Point} location to snap the mouse to + */ + public synchronized void moveMouse(Point location) { + getFocusIfNotFocused(); + remoteInput.EIOS_MoveMouse(target, location.x, location.y); + } + + /** + * Performs a mouse wheel scroll at the current virtual mouse position. RemoteInput natively adds + * a small float value to this, to simulate human imperfection + * + * @param notches The number of mouse notches to scroll, down is positive, up is negative + */ + public synchronized void scrollMouse(int notches) { + getFocusIfNotFocused(); + Point mousePosition = getMousePosition(); + remoteInput.EIOS_ScrollMouse(target, mousePosition.x, mousePosition.y, notches); + } + + /** + * Types out a string of characters whilst compensating for the need of modifier keys. Useful when + * typing something to a dialogue box, will compensate for special characters, however lacks delay + * between keypresses. + * + * @param string The text to be typed + * @param keyWait The time in milliseconds to hold down a key + * @param keyModWait The time in milliseconds to hold down modifier keys + */ + public synchronized void sendString(String string, int keyWait, int keyModWait) { + setKeyboardInputIfDisabled(); + getFocusIfNotFocused(); + remoteInput.EIOS_SendString(target, string, keyWait, keyModWait); + } + + /** + * Since this class implements the {@link AutoCloseable} interface, it must be closed to relieve + * native memory. This method will release the target, effectively shutting down RemoteInput for + * the particular ChromaScape instance. However, this does not delete the injected part of RI in + * the target, simply shuts down control over it. + */ + @Override + public void close() { + if (target != null) { + remoteInput.EIOS_ReleaseTarget(target); + target = null; + } + } +} diff --git a/src/main/java/com/chromascape/utils/core/input/remoteinput/RemoteInputInterface.java b/src/main/java/com/chromascape/utils/core/input/remoteinput/RemoteInputInterface.java new file mode 100644 index 0000000..7cff1bc --- /dev/null +++ b/src/main/java/com/chromascape/utils/core/input/remoteinput/RemoteInputInterface.java @@ -0,0 +1,226 @@ +package com.chromascape.utils.core.input.remoteinput; + +import com.sun.jna.Library; +import com.sun.jna.Pointer; +import com.sun.jna.ptr.IntByReference; + +/** + * JNA interface to load the RemoteInput DLL as a {@link RemoteInput} object. Contains the available + * exported headers available for ChromaScape to use. + */ +public interface RemoteInputInterface extends Library { + + /** + * Injects part of the RemoteInput Process into the target Java app. RI will only work after this + * pairing process has been performed. + * + * @param pid The OS process ID of which to inject into. + */ + void EIOS_Inject_PID(int pid); + + /** + * Requests to control a specific target app after it's been registered using {@link + * #EIOS_Inject_PID(int)}. Many targets can be controlled using a single instance of RemoteInput. + * + * @param initargs The String value of the target Java app's process ID (PID) + * @return A pointer reference to the target's EIOS object. This object is used in IO operations + * as a reference to which target should receive IO + */ + Pointer EIOS_RequestTarget(String initargs); + + /** + * Retrieves the memory pointer to the target's current image buffer. The buffer contains pixel + * data in BGRA (Blue, Green, Red, Alpha) format. Each pixel occupies 4 bytes. The total size of + * the buffer is {@code width * height * 4} bytes. The data is updated automatically by the native + * hooks in the target application whenever a frame is rendered. + * + * @param eios The pointer to the paired EIOS target instance + * @return A pointer to the start of the BGRA pixel array + */ + Pointer EIOS_GetImageBuffer(Pointer eios); + + /** + * Retrieves a memory pointer similarly to {@link #EIOS_GetImageBuffer(Pointer)}. This method + * however will contain the mouse pointer and any objects drawn onto the canvas. + * + * @param eios The pointer to the paired EIOS target instance + * @return A pointer to the start of the BGRA pixel array + */ + Pointer EIOS_GetDebugImageBuffer(Pointer eios); + + /** + * Checks if the target application has keyboard input enabled. This is useful when using {@link + * #EIOS_HoldKey(Pointer, int)}, {@link #EIOS_ReleaseKey(Pointer, int)} and or {@link + * #EIOS_SendString(Pointer, String, int, int)}. + * + * @param eios The pointer to the paired EIOS target instance + * @return If the target has keyboard input enabled + */ + boolean EIOS_IsKeyboardInputEnabled(Pointer eios); + + /** + * Sets the keyboard input to either enabled or disabled, useful when conducting keyboard input. + * + * @param eios The pointer to the paired EIOS target instance + * @param enabled Whether you want keyboard input to be enabled + */ + void EIOS_SetKeyboardInputEnabled(Pointer eios, boolean enabled); + + /** + * Gets the total number of clients that currently have RemoteInput injected into them. + * + * @param unpaired_only Whether to count only unpaired targets. + * @return The total number of connected targets. + */ + long EIOS_GetClients(boolean unpaired_only); + + /** + * Fetches the process ID of a client given the index of the client in RemoteInput's internal + * client array. + * + * @param index The index position of the client + * @return The process ID + */ + int EIOS_GetClientPID(long index); + + /** + * Whether the host machine has focus over the target Java app. Focus is required when sending + * inputs to the client, it's recommended to use {@link #EIOS_GainFocus(Pointer)} to do so. + * + * @param eios The pointer to the paired EIOS target instance + * @return Whether the host has focus over the client + */ + boolean EIOS_HasFocus(Pointer eios); + + /** + * Used to gain focus over the target app. + * + * @param eios The pointer to the paired EIOS target instance + */ + void EIOS_GainFocus(Pointer eios); + + /** + * Used to lose focus over the target app, might be useful for antiban possibly. + * + * @param eios The pointer to the paired EIOS target instance + */ + void EIOS_LoseFocus(Pointer eios); + + /** + * RemoteInput stores an internal mouse position that stays persistent after ChromaScape restarts. + * This will return that mouse position. VirtualMouseUtils automatically syncs to this unless + * RemoteInput is pairing for the first time, where it'll randomise mouse position. + * + * @param eios The pointer to the paired EIOS target instance + * @param x An {@link IntByReference} which gets mutated with the x value + * @param y An {@link IntByReference} which gets mutated with the y value + */ + void EIOS_GetMousePosition(Pointer eios, IntByReference x, IntByReference y); + + /** + * Gets the target app's window dimensions. Due to all IO being performed in client relative + * space, the user can assume the origin as 0,0. + * + * @param eios The pointer to the paired EIOS target instance + * @param width An {@link IntByReference} which gets mutated with the width's value in pixels + * @param height An {@link IntByReference} which gets mutated with the height's value in pixels + */ + void EIOS_GetTargetDimensions(Pointer eios, IntByReference width, IntByReference height); + + /** + * Sends a key down in the target java app. To get the keycode, use a {@link + * java.awt.event.KeyEvent} object. Example: {@code KeyEvent.VK_ENTER}. + * + * @param eios The pointer to the paired EIOS target instance + * @param key The Java keycode corresponding to the key being pressed + */ + void EIOS_HoldKey(Pointer eios, int key); + + /** + * Sends a mouse button down in the target java app. + * + * @param eios The pointer to the paired EIOS target instance + * @param x The x co-ordinate of the input + * @param y The y co-ordinate of the input + * @param button The {@link MouseButton} to use + */ + void EIOS_HoldMouse(Pointer eios, int x, int y, int button); + + /** + * Queries whether a key is currently held. + * + * @param eios The pointer to the paired EIOS target instance + * @param key The Java keycode of the key + * @return If the key is currently being held + */ + boolean EIOS_IsKeyHeld(Pointer eios, int key); + + /** + * Queries whether a {@link MouseButton} is currently held. + * + * @param eios The pointer to the paired EIOS target instance + * @param button The {@link MouseButton} to check + * @return If the mouse button is currently being held + */ + boolean EIOS_IsMouseHeld(Pointer eios, int button); + + /** + * Moves a mouse to a designated client local co-ordinate. + * + * @param eios The pointer to the paired EIOS target instance + * @param x The x co-ordinate of the input + * @param y The y co-ordinate of the input + */ + void EIOS_MoveMouse(Pointer eios, int x, int y); + + /** + * Sends a key release event to the target Java app. This should be used in conjunction with + * {@link #EIOS_HoldKey(Pointer, int)} to simulate a full key press. + * + * @param eios The pointer to the paired EIOS target instance + * @param key The Java keycode corresponding to the key being released + */ + void EIOS_ReleaseKey(Pointer eios, int key); + + /** + * Releases a mouse button at the designated client local co-ordinates. + * + * @param eios The pointer to the paired EIOS target instance + * @param x The x co-ordinate of the input + * @param y The y co-ordinate of the input + * @param button The {@link MouseButton} to release + */ + void EIOS_ReleaseMouse(Pointer eios, int x, int y, int button); + + /** + * Severs the connection to the target EIOS object and releases native resources. This should be + * called when the script stops or the controller shuts down to avoid memory leaks in the target + * app. + * + * @param eios The pointer to the paired EIOS target instance + */ + void EIOS_ReleaseTarget(Pointer eios); + + /** + * Performs a mouse wheel scroll at the current virtual mouse position. RemoteInput natively adds + * a small float value to this, to simulate human imperfection + * + * @param eios The pointer to the paired EIOS target instance + * @param x The x co-ordinate of the input + * @param y The y co-ordinate of the input + * @param lines The number of notches to scroll; positive for down, negative for up + */ + void EIOS_ScrollMouse(Pointer eios, int x, int y, int lines); + + /** + * Types out a string of characters whilst compensating for the need of modifier keys. Useful when + * typing something to a dialogue box, will compensate for special characters, however lacks delay + * between keypresses. + * + * @param eios The pointer to the paired EIOS target instance + * @param string The text to be typed + * @param keywait The time in milliseconds to hold down a key + * @param keymodwait The time in milliseconds to hold down modifier keys + */ + void EIOS_SendString(Pointer eios, String string, int keywait, int keymodwait); +} diff --git a/src/main/java/com/chromascape/utils/core/screen/topology/ColourContours.java b/src/main/java/com/chromascape/utils/core/screen/topology/ColourContours.java index 1f2281d..2c679ff 100644 --- a/src/main/java/com/chromascape/utils/core/screen/topology/ColourContours.java +++ b/src/main/java/com/chromascape/utils/core/screen/topology/ColourContours.java @@ -179,9 +179,7 @@ public static List createChromaObjects(MatVector contours) { for (int i = 0; i < contours.size(); i++) { Mat contour = contours.get(i); Rect rect = boundingRect(contour); - Rectangle offset = ScreenManager.getWindowBounds(); - Rectangle contourBounds = - new Rectangle(rect.x() + offset.x, rect.y() + offset.y, rect.width(), rect.height()); + Rectangle contourBounds = new Rectangle(rect.x(), rect.y(), rect.width(), rect.height()); chromaObjects.add(new ChromaObj(i, contour, contourBounds)); StatisticsManager.incrementObjectsDetected(); } @@ -196,8 +194,7 @@ public static List createChromaObjects(MatVector contours) { * @return true if the point lies inside the contour; false otherwise */ public static boolean isPointInContour(Point point, Mat contour) { - Point clientPoint = ScreenManager.toClientCoords(point); - try (Point2f point2f = new Point2f(clientPoint.x, clientPoint.y)) { + try (Point2f point2f = new Point2f(point.x, point.y)) { return pointPolygonTest(contour, point2f, false) > 0; } } diff --git a/src/main/java/com/chromascape/utils/core/screen/topology/TemplateMatching.java b/src/main/java/com/chromascape/utils/core/screen/topology/TemplateMatching.java index 7dd0384..0c9f3d7 100644 --- a/src/main/java/com/chromascape/utils/core/screen/topology/TemplateMatching.java +++ b/src/main/java/com/chromascape/utils/core/screen/topology/TemplateMatching.java @@ -6,7 +6,6 @@ import static org.opencv.imgproc.Imgproc.TM_SQDIFF_NORMED; import com.chromascape.utils.core.screen.viewport.ViewportManager; -import com.chromascape.utils.core.screen.window.ScreenManager; import com.chromascape.utils.core.state.BotState; import com.chromascape.utils.core.state.StateManager; import com.chromascape.utils.core.statistics.StatisticsManager; @@ -136,12 +135,7 @@ public static MatchResult match(String templateImg, BufferedImage baseImg, doubl return new MatchResult(null, minVal.get(), false, "MinVal greater than threshold"); } - // offset for screen cords (Note this means that everything template matched is zone relative) - Rectangle offset = ScreenManager.getWindowBounds(); - - Rectangle match = - new Rectangle( - offset.x + minLoc.x(), offset.y + minLoc.y(), template.cols(), template.rows()); + Rectangle match = new Rectangle(minLoc.x(), minLoc.y(), template.cols(), template.rows()); // Update singleton state manager to update stats in UI StatisticsManager.incrementObjectsDetected(); diff --git a/src/main/java/com/chromascape/utils/core/screen/window/LinuxProcessManager.java b/src/main/java/com/chromascape/utils/core/screen/window/LinuxProcessManager.java new file mode 100644 index 0000000..1aade8d --- /dev/null +++ b/src/main/java/com/chromascape/utils/core/screen/window/LinuxProcessManager.java @@ -0,0 +1,19 @@ +package com.chromascape.utils.core.screen.window; + +/** + * A class whose sole responsibility is to provide a native Linux implementation to return the + * process ID of RuneLite. + */ +public class LinuxProcessManager implements ProcessManager { + + /** + * To provide a linux native way of grabbing and returning the Process ID of RuneLite. This is to + * be used by RemoteInput. + * + * @return An integer Process ID + */ + @Override + public int getPid() { + return -1; + } +} diff --git a/src/main/java/com/chromascape/utils/core/screen/window/MacProcessManager.java b/src/main/java/com/chromascape/utils/core/screen/window/MacProcessManager.java new file mode 100644 index 0000000..e36367e --- /dev/null +++ b/src/main/java/com/chromascape/utils/core/screen/window/MacProcessManager.java @@ -0,0 +1,19 @@ +package com.chromascape.utils.core.screen.window; + +/** + * A class whose sole responsibility is to provide a native macOS implementation to return the + * process ID of RuneLite. + */ +public class MacProcessManager implements ProcessManager { + + /** + * To provide a macOS native way of grabbing and returning the Process ID of RuneLite. This is to + * be used by RemoteInput. + * + * @return An integer Process ID + */ + @Override + public int getPid() { + return -1; + } +} diff --git a/src/main/java/com/chromascape/utils/core/screen/window/ProcessManager.java b/src/main/java/com/chromascape/utils/core/screen/window/ProcessManager.java new file mode 100644 index 0000000..4a1f02b --- /dev/null +++ b/src/main/java/com/chromascape/utils/core/screen/window/ProcessManager.java @@ -0,0 +1,15 @@ +package com.chromascape.utils.core.screen.window; + +/** + * An interface to be implemented by Windows, Mac and Linux implementors. The sole responsibility of + * each implementor is to provide an OS native method to return the Process ID of RuneLite. + */ +public interface ProcessManager { + /** + * To provide an OS native way of grabbing and returning the Process ID of RuneLite. This is to be + * used by RemoteInput. + * + * @return An integer Process ID + */ + int getPid(); +} diff --git a/src/main/java/com/chromascape/utils/core/screen/window/ProcessManagerFactory.java b/src/main/java/com/chromascape/utils/core/screen/window/ProcessManagerFactory.java new file mode 100644 index 0000000..c36d53a --- /dev/null +++ b/src/main/java/com/chromascape/utils/core/screen/window/ProcessManagerFactory.java @@ -0,0 +1,24 @@ +package com.chromascape.utils.core.screen.window; + +/** + * Factory class to return the OS specific implementation of a {@link ProcessManager}. Detects OS by + * OS-name and returns the native implementor of the PM interface. + */ +public class ProcessManagerFactory { + + /** + * A factory that creates an OS specific {@link ProcessManager}. + * + * @return The OS specific ProcessManager implementor, used to extract Process ID. + */ + public static ProcessManager getProcessManager() { + String os = System.getProperty("os.name").toLowerCase(); + if (os.contains("win")) { + return new WindowsProcessManager(); + } + if (os.contains("mac")) { + return new MacProcessManager(); + } + return new LinuxProcessManager(); + } +} diff --git a/src/main/java/com/chromascape/utils/core/screen/window/ScreenManager.java b/src/main/java/com/chromascape/utils/core/screen/window/ScreenManager.java index 0b4c3d9..ada48b9 100644 --- a/src/main/java/com/chromascape/utils/core/screen/window/ScreenManager.java +++ b/src/main/java/com/chromascape/utils/core/screen/window/ScreenManager.java @@ -1,222 +1,113 @@ package com.chromascape.utils.core.screen.window; -import com.sun.jna.Memory; -import com.sun.jna.Native; -import com.sun.jna.platform.win32.GDI32; -import com.sun.jna.platform.win32.User32; -import com.sun.jna.platform.win32.WinDef; -import com.sun.jna.platform.win32.WinDef.HBITMAP; -import com.sun.jna.platform.win32.WinDef.HDC; -import com.sun.jna.platform.win32.WinDef.HWND; -import com.sun.jna.platform.win32.WinDef.RECT; -import com.sun.jna.platform.win32.WinGDI; -import com.sun.jna.platform.win32.WinGDI.BITMAPINFO; -import com.sun.jna.platform.win32.WinNT.HANDLE; -import java.awt.Graphics; -import java.awt.Point; +import com.chromascape.utils.core.input.remoteinput.RemoteInput; +import com.sun.jna.Pointer; import java.awt.Rectangle; -import java.awt.Robot; +import java.awt.Transparency; +import java.awt.color.ColorSpace; import java.awt.image.BufferedImage; -import java.awt.image.DataBufferInt; +import java.awt.image.ColorModel; +import java.awt.image.ComponentColorModel; +import java.awt.image.DataBuffer; +import java.awt.image.DataBufferByte; +import java.awt.image.Raster; +import java.awt.image.WritableRaster; /** - * Utility class for capturing screen regions, retrieving window bounds, and interacting with native - * window functionality using JNA. - * - *

Provides methods for: - * - *

+ * Utility class for capturing screen regions and retrieving window bounds. Screen capture utilities + * are intended to be used with colour contour extraction and template matching. */ public class ScreenManager { - /** - * Grabs the HWND of the second child of the RuneLite window - The game view portion. This is - * prone to breaking if RuneLite add more canvas elements, but this is not likely. - */ - private static final HWND canvasHwnd = - WindowHandler.findNthChildWindow(WindowHandler.getTargetWindow(), "SunAwtCanvas", 2); - - /** - * JNA extension interface to allow calling {@code ClientToScreen} which converts window-relative - * coordinates to screen coordinates. - */ - public interface User32Extended extends User32 { + private static RemoteInput remoteInput; - /** - * Converts the client-relative point to screen coordinates. - * - * @param hwnd the window handle of the client. - * @param point the point to convert. - */ - void ClientToScreen(HWND hwnd, POINT point); - - /** - * Retrieves the coordinates of a window's client area. The client coordinates specify the - * upper-left and lower-right corners of the client area. Because client coordinates are - * relative to the upper-left corner of a window's client area, the coordinates of the - * upper-left corner are (0,0). Link - * to documentation - * - * @param hwnd Handle to the window. - * @param rect Long pointer to a RECT structure that receives the client coordinates. The left - * and top members are zero. The right and bottom members contain the width and height of - * the window. - * @return True if succeeded, false otherwise. - */ - boolean GetClientRect(HWND hwnd, RECT rect); - - User32Extended INSTANCE = Native.load("user32", User32Extended.class); - } + private static Pointer screenBuffer = null; /** - * Captures the entire content of the game window using the Windows GDI BitBlt function. - * - *

Unlike the standard {@link Robot} class which captures the composite screen (including - * overlays), this method directly reads the Device Context (DC) of the target window. This allows - * for capturing the game view cleanly even when external overlays are drawn on top of it. + * Captures a {@link Rectangle} region on the client screen, intended to be used when + * screenshotting zones for template matching and or colour extraction. * - *

This method performs a manual memory copy from native GDI resources to a Java {@link - * BufferedImage} to ensure compatibility and performance. - * - * @return A {@link BufferedImage} containing the window contents in BGR format, suitable for - * OpenCV processing. + * @param zone The rectangle area in client relative screen co-ordinates + * @return A {@link BufferedImage} of the captured area */ - public static BufferedImage captureWindow() { - RECT bounds = new RECT(); - User32.INSTANCE.GetClientRect(canvasHwnd, bounds); - int width = bounds.right - bounds.left; - int height = bounds.bottom - bounds.top; - - return performCapture(0, 0, width, height); + public static BufferedImage captureZone(Rectangle zone) { + BufferedImage screen = captureWindow(); + if (screen == null) { + throw new RuntimeException("Screen could not be captured"); + } + return screen.getSubimage(zone.x, zone.y, zone.width, zone.height); } /** - * Captures a specific rectangular region of the screen directly from the window's Device Context. - * - *

This is critical for high frequency checks like OCR or HP bars, where we only need a tiny - * screen region. + * Grabs the latest rendered frame of the target application, regardless of if the client is + * maximised, minimised, partially or fully covered. This is to be used with template matching and + * {@link com.chromascape.utils.core.screen.topology.ChromaObj} detection. * - * @param zone The screen-space {@link Rectangle} to capture. - * @return A {@link BufferedImage} containing only the requested region. + * @return A {@link BufferedImage} of the client's screen */ - public static BufferedImage captureZone(Rectangle zone) { - Rectangle clientRect = toClientBounds(new Rectangle(zone)); - - return performCapture(clientRect.x, clientRect.y, clientRect.width, clientRect.height); - } + public static synchronized BufferedImage captureWindow() { + Rectangle dims = remoteInput.getTargetDimensions(); + int width = dims.width; + int height = dims.height; - /** Internal helper to perform GDI BitBlt and pixel conversion. */ - private static BufferedImage performCapture(int x, int y, int w, int h) { - if (w <= 0 || h <= 0) { - return new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR); + if (width <= 0 || height <= 0) { + return null; } - HDC hdcSrc = User32.INSTANCE.GetDC(canvasHwnd); - HDC hdcMem = GDI32.INSTANCE.CreateCompatibleDC(hdcSrc); - HBITMAP hBitmap = GDI32.INSTANCE.CreateCompatibleBitmap(hdcSrc, w, h); - HANDLE hOld = GDI32.INSTANCE.SelectObject(hdcMem, hBitmap); - - boolean success = GDI32.INSTANCE.BitBlt(hdcMem, 0, 0, w, h, hdcSrc, x, y, 0x00CC0020); - - BufferedImage bgr = null; - - if (success) { - BITMAPINFO bmi = new BITMAPINFO(); - bmi.bmiHeader.biWidth = w; - bmi.bmiHeader.biHeight = -h; - bmi.bmiHeader.biPlanes = 1; - bmi.bmiHeader.biBitCount = 32; - bmi.bmiHeader.biCompression = WinGDI.BI_RGB; - - Memory buffer = new Memory((long) w * h * 4); - GDI32.INSTANCE.GetDIBits(hdcMem, hBitmap, 0, h, buffer, bmi, WinGDI.DIB_RGB_COLORS); - - BufferedImage argb = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); - int[] pixels = buffer.getIntArray(0, w * h); - int[] targetPixels = ((DataBufferInt) argb.getRaster().getDataBuffer()).getData(); - System.arraycopy(pixels, 0, targetPixels, 0, pixels.length); - - bgr = new BufferedImage(w, h, BufferedImage.TYPE_3BYTE_BGR); - Graphics g = bgr.getGraphics(); - g.drawImage(argb, 0, 0, null); - g.dispose(); + if (screenBuffer == null) { + screenBuffer = remoteInput.getImageBuffer(); } - GDI32.INSTANCE.SelectObject(hdcMem, hOld); - GDI32.INSTANCE.DeleteObject(hBitmap); - GDI32.INSTANCE.DeleteDC(hdcMem); - User32.INSTANCE.ReleaseDC(canvasHwnd, hdcSrc); + int bufferSize = width * height * 4; + byte[] data = screenBuffer.getByteArray(0, bufferSize); - if (bgr == null) { - throw new RuntimeException("GDI Capture failed"); - } - return bgr; + return createBufferedImage(data, width, height); } /** - * Gets the bounds of the (game view) RuneLite AWT Canvas object. + * Internal helper to create a buffered image from a C++ style byte array of pixels in BGRA + * format. * - *

Converts the client-relative origin to screen coordinates using {@code ClientToScreen}. - * - * @return A {@link Rectangle} representing the on-screen position and size of RuneLite's client - * area excluding possible window borders, title or scrollbars. + * @param pixels The byte array of pixel data in BGRA format + * @param width The width of the client in pixels + * @param height The height of the client in pixels + * @return A {@link BufferedImage} representing the image */ - public static Rectangle getWindowBounds() { - WinDef.RECT dimensions = new WinDef.RECT(); - User32.INSTANCE.GetClientRect(canvasHwnd, dimensions); - - WinDef.POINT clientTopLeft = new WinDef.POINT(); - clientTopLeft.x = 0; - clientTopLeft.y = 0; - - User32Extended uex = User32Extended.INSTANCE; - uex.ClientToScreen(canvasHwnd, clientTopLeft); - - return new Rectangle( - clientTopLeft.x, - clientTopLeft.y, - dimensions.right - dimensions.left, - dimensions.bottom - dimensions.top); + private static BufferedImage createBufferedImage(byte[] pixels, int width, int height) { + DataBufferByte buffer = new DataBufferByte(pixels, pixels.length); + WritableRaster raster = + Raster.createInterleavedRaster( + buffer, width, height, width * 4, 4, new int[] {2, 1, 0, 3}, null); + + ColorModel cm = + new ComponentColorModel( + ColorSpace.getInstance(ColorSpace.CS_sRGB), + new int[] {8, 8, 8, 8}, + true, + false, + Transparency.TRANSLUCENT, + DataBuffer.TYPE_BYTE); + + return new BufferedImage(cm, raster, false, null); } /** - * Converts a screen-space {@link Rectangle} to RuneLite game-view canvas local coordinates. - * - *

This method adjusts the rectangle's position by subtracting the top-left corner of the - * canvas (as determined by {@link ScreenManager#getWindowBounds()}) from its {@code x} and {@code - * y} coordinates. This is necessary when working with screen-detected regions (e.g., from - * template matching) and applying them to canvas-local images. - * - *

Note: This method mutates and returns the original {@code Rectangle} instance. + * Gets the bounds of the (game view) RuneLite AWT Canvas object. * - * @param screenBounds the rectangle in absolute screen coordinates - * @return the same rectangle, now adjusted to canvas-local coordinates + * @return A {@link Rectangle} representing the size of RuneLite's client area, excluding possible + * window borders, title or scrollbars. */ - public static Rectangle toClientBounds(Rectangle screenBounds) { - Rectangle offset = ScreenManager.getWindowBounds(); - screenBounds.x -= offset.x; - screenBounds.y -= offset.y; - return screenBounds; + public static Rectangle getWindowBounds() { + return remoteInput.getTargetDimensions(); } /** - * Converts a screen-space {@link Point} to RuneLite game-view canvas local coordinates. - * - *

This method adjusts the point's position by subtracting the top-left corner of the canvas - * (as returned by {@link ScreenManager#getWindowBounds()}). This is typically used when - * translating points detected in full-screen captures into the coordinate space of the - * canvas-local window image. + * Sets the RemoteInput object in the ScreenManager, allowing it to access to the client's screen + * buffer. * - * @param screenPoint the point in absolute screen coordinates - * @return a new {@code Point} adjusted to canvas-local coordinates + * @param remoteInput The {@link RemoteInput} object */ - public static Point toClientCoords(Point screenPoint) { - Rectangle offset = ScreenManager.getWindowBounds(); - return new Point(screenPoint.x - offset.x, screenPoint.y - offset.y); + public static void setRemoteInput(RemoteInput remoteInput) { + ScreenManager.remoteInput = remoteInput; } } diff --git a/src/main/java/com/chromascape/utils/core/screen/window/WindowHandler.java b/src/main/java/com/chromascape/utils/core/screen/window/WindowHandler.java deleted file mode 100644 index 8c4f3f8..0000000 --- a/src/main/java/com/chromascape/utils/core/screen/window/WindowHandler.java +++ /dev/null @@ -1,157 +0,0 @@ -package com.chromascape.utils.core.screen.window; - -import com.sun.jna.Native; -import com.sun.jna.Pointer; -import com.sun.jna.platform.win32.WinDef.HWND; -import com.sun.jna.platform.win32.WinUser.WNDENUMPROC; -import com.sun.jna.ptr.IntByReference; -import com.sun.jna.win32.StdCallLibrary; -import java.util.concurrent.atomic.AtomicReference; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Utility class for locating and identifying a specific native window (e.g., "RuneLite") on the - * Windows operating system using JNA and Win32 APIs. - */ -public class WindowHandler { - private static final Logger logger = LoggerFactory.getLogger(WindowHandler.class); - private static final String WINDOW_NAME = "RuneLite"; - - /** - * JNA interface for accessing low-level Win32 User32 functions that are not provided by the - * default JNA platform mappings. - */ - public interface User32 extends StdCallLibrary { - User32 INSTANCE = Native.load("user32", User32.class); - - /** - * Enumerates all top-level windows on the screen by invoking the provided callback. - * - * @param lpEnumFunc The callback to be called for each window. - * @param arg A user-defined value passed to the callback (usually null). - */ - void EnumWindows(WNDENUMPROC lpEnumFunc, Pointer arg); - - /** - * Retrieves the title text of the specified window. - * - * @param hwnd Handle to the window. - * @param lpString Buffer that receives the window title. - * @param maxCount Maximum number of characters to copy. - */ - void GetWindowTextA(HWND hwnd, byte[] lpString, int maxCount); - - /** - * Retrieves the process identifier (PID) for the specified window. - * - * @param hwnd Handle to the window. - * @param lpDword Receives the process ID. - */ - void GetWindowThreadProcessId(HWND hwnd, IntByReference lpDword); - - /** - * Enumerates all child windows of a specified parent window. - * - *

This function calls the provided callback {@code lpEnumFunc} for each child window of the - * specified parent {@code hWndParent}. It is commonly used to locate specific child controls or - * canvases within a larger window. - * - * @param hwndParent the handle of the parent window whose children are to be enumerated - * @param lpEnumFunc the callback function to be called for each child window - * @param data a pointer to user-defined data that will be passed to the callback - */ - void EnumChildWindows(HWND hwndParent, WNDENUMPROC lpEnumFunc, Pointer data); - - /** - * Retrieves the class name of a specified window. - * - *

This function writes the window class name of {@code hwnd} into the provided byte array - * {@code lpString}, up to a maximum of {@code maxCount} characters. It is useful for - * identifying specific child windows or controls by class. - * - * @param hwnd the handle of the window whose class name is to be retrieved - * @param lpString the byte array to receive the class name - * @param maxCount the maximum number of characters to copy into {@code lpString} - */ - void GetClassNameA(HWND hwnd, byte[] lpString, int maxCount); - } - - /** - * Attempts to locate the window whose title matches the {@code WINDOW_NAME}. - * - * @return The {@link HWND} handle of the target window, or {@code null} if not found. - */ - public static HWND getTargetWindow() { - AtomicReference targetHwnd = new AtomicReference<>(); - User32 user32 = User32.INSTANCE; - - user32.EnumWindows( - (hwnd, arg) -> { - byte[] buffer = new byte[512]; - user32.GetWindowTextA(hwnd, buffer, 512); - String title = Native.toString(buffer); - - if (title.trim().equals(WINDOW_NAME)) { - targetHwnd.set(hwnd); - return false; // stop enumeration - } - return true; - }, - null); - - return targetHwnd.get(); // May be null if not found - } - - /** - * Searches for the N-th child window of a given parent window that matches the specified class - * name. - * - *

This is useful when multiple child windows share the same class (e.g., RuneLite has two - * SunAwtCanvas children: the outer canvas with black bars and the inner canvas with the game - * viewport). By specifying an index, you can reliably retrieve the intended child. - * - * @param parent the {@link HWND} of the parent window whose children are to be enumerated - * @param className the class name of the child windows to match (e.g., "SunAwtCanvas") - * @param n the 1-based index of the matching child window to retrieve (1 = first match) - * @return the {@link HWND} of the N-th matching child window, or {@code null} if no such child - * exists - */ - public static HWND findNthChildWindow(HWND parent, String className, int n) { - AtomicReference result = new AtomicReference<>(); - User32 user32 = User32.INSTANCE; - int[] count = {0}; - - user32.EnumChildWindows( - parent, - (hwnd, data) -> { - byte[] buffer = new byte[512]; - user32.GetClassNameA(hwnd, buffer, buffer.length); - String clsName = Native.toString(buffer).trim(); - - if (clsName.equals(className)) { - count[0]++; - if (count[0] == n) { - result.set(hwnd); - return false; // stop enumeration - } - } - return true; // continue enumeration - }, - null); - - return result.get(); // may be null if no match found - } - - /** - * Retrieves the process ID associated with a given window handle. - * - * @param hwnd The handle of the target window. - * @return The process ID (PID) of the window's owning process. - */ - public static int getPid(HWND hwnd) { - IntByReference pid = new IntByReference(); - User32.INSTANCE.GetWindowThreadProcessId(hwnd, pid); - return pid.getValue(); - } -} diff --git a/src/main/java/com/chromascape/utils/core/screen/window/WindowsProcessManager.java b/src/main/java/com/chromascape/utils/core/screen/window/WindowsProcessManager.java new file mode 100644 index 0000000..fb3f3e1 --- /dev/null +++ b/src/main/java/com/chromascape/utils/core/screen/window/WindowsProcessManager.java @@ -0,0 +1,91 @@ +package com.chromascape.utils.core.screen.window; + +import com.sun.jna.Native; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.WinDef.HWND; +import com.sun.jna.platform.win32.WinUser.WNDENUMPROC; +import com.sun.jna.ptr.IntByReference; +import com.sun.jna.win32.StdCallLibrary; +import java.util.concurrent.atomic.AtomicReference; + +/** + * Utility class for locating and identifying a specific native window (e.g., "RuneLite") on the + * Windows operating system using JNA and Win32 APIs. + */ +public class WindowsProcessManager implements ProcessManager { + + private static final String WINDOW_NAME = "RuneLite"; + + /** + * JNA interface for accessing low-level Win32 User32 functions that are not provided by the + * default JNA platform mappings. + */ + public interface User32 extends StdCallLibrary { + User32 INSTANCE = Native.load("user32", User32.class); + + /** + * Enumerates all top-level windows on the screen by invoking the provided callback. + * + * @param lpEnumFunc The callback to be called for each window. + * @param arg A user-defined value passed to the callback (usually null). + */ + void EnumWindows(WNDENUMPROC lpEnumFunc, Pointer arg); + + /** + * Retrieves the title text of the specified window. + * + * @param hwnd Handle to the window. + * @param lpString Buffer that receives the window title. + * @param maxCount Maximum number of characters to copy. + */ + void GetWindowTextA(HWND hwnd, byte[] lpString, int maxCount); + + /** + * Retrieves the process identifier (PID) for the specified window. + * + * @param hwnd Handle to the window. + * @param lpDword Receives the process ID. + */ + void GetWindowThreadProcessId(HWND hwnd, IntByReference lpDword); + } + + /** + * Attempts to locate the window whose title matches the {@code WINDOW_NAME}. + * + * @return The {@link HWND} handle of the target window, or {@code null} if not found. + */ + public static HWND getTargetWindow() { + AtomicReference targetHwnd = new AtomicReference<>(); + User32 user32 = User32.INSTANCE; + + user32.EnumWindows( + (hwnd, arg) -> { + byte[] buffer = new byte[512]; + user32.GetWindowTextA(hwnd, buffer, 512); + String title = Native.toString(buffer); + + if (title.trim().equals(WINDOW_NAME)) { + targetHwnd.set(hwnd); + return false; // stop enumeration + } + return true; + }, + null); + + return targetHwnd.get(); // May be null if not found + } + + /** + * To return the Process ID of RuneLite. Avoids non-ChromaScape related instances by searching for + * "RuneLite" only as opposed to "RuneLite - {UserName}" + * + * @return The integer process ID of RuneLite loaded with the ChromaScape profile + */ + @Override + public int getPid() { + HWND windowHandle = getTargetWindow(); + IntByReference pid = new IntByReference(); + User32.INSTANCE.GetWindowThreadProcessId(windowHandle, pid); + return pid.getValue(); + } +} diff --git a/src/main/java/com/chromascape/utils/domain/walker/Walker.java b/src/main/java/com/chromascape/utils/domain/walker/Walker.java index e28bd66..f464099 100644 --- a/src/main/java/com/chromascape/utils/domain/walker/Walker.java +++ b/src/main/java/com/chromascape/utils/domain/walker/Walker.java @@ -188,7 +188,7 @@ public void pathTo(Point destination, boolean isMembers) // Async precomputing the next click point while waiting for the bot to stop pointFuture = CompletableFuture.supplyAsync(() -> getClickLocation(newTarget, oldTarget)); // This blocks the main thread, but the next point is being computed already. - logger.info("Precomputing next click at " + newTarget.x() + ", " + newTarget.y()); + logger.info("Precomputing next click at {}, {}", newTarget.x(), newTarget.y()); waitToStop(); // Recalculate path and cancel async if not at expected location Tile position = getPlayerPosition(); diff --git a/src/main/java/com/chromascape/utils/domain/zones/ZoneManager.java b/src/main/java/com/chromascape/utils/domain/zones/ZoneManager.java index b4a85f5..9367afc 100644 --- a/src/main/java/com/chromascape/utils/domain/zones/ZoneManager.java +++ b/src/main/java/com/chromascape/utils/domain/zones/ZoneManager.java @@ -78,21 +78,16 @@ public void mapper() { ctrlPanel = SubZoneMapper.mapCtrlPanel(ctrlPanelBounds); inventorySlots = SubZoneMapper.mapInventory(ctrlPanelBounds); - Rectangle windowBounds = ScreenManager.getWindowBounds(); - mouseOver = new Rectangle(windowBounds.x, windowBounds.y, 407, 26); + mouseOver = new Rectangle(0, 0, 407, 26); if (isFixed) { minimapBounds = locateUiElement(zoneTemplates[3]); minimap = SubZoneMapper.mapFixedMinimap(minimapBounds); - gridInfo = - SubZoneMapper.mapGridInfo( - new Rectangle(windowBounds.x + 9, windowBounds.y + 24, 129, 56)); + gridInfo = SubZoneMapper.mapGridInfo(new Rectangle(9, 24, 129, 56)); } else { minimapBounds = locateUiElement(zoneTemplates[0]); minimap = SubZoneMapper.mapMinimap(minimapBounds); - gridInfo = - SubZoneMapper.mapGridInfo( - new Rectangle(windowBounds.x + 5, windowBounds.y + 20, 129, 56)); + gridInfo = SubZoneMapper.mapGridInfo(new Rectangle(5, 20, 129, 56)); } } @@ -127,19 +122,13 @@ private boolean checkIfFixed() { public BufferedImage getGameView() { BufferedImage gameViewMask = ScreenManager.captureWindow(); if (ctrlPanelBounds != null) { - gameViewMask = - MaskZones.maskZones( - gameViewMask, ScreenManager.toClientBounds(new Rectangle(ctrlPanelBounds))); + gameViewMask = MaskZones.maskZones(gameViewMask, ctrlPanelBounds); } if (chatBounds != null) { - gameViewMask = - MaskZones.maskZones( - gameViewMask, ScreenManager.toClientBounds(new Rectangle(chatBounds))); + gameViewMask = MaskZones.maskZones(gameViewMask, chatBounds); } if (minimapBounds != null) { - gameViewMask = - MaskZones.maskZones( - gameViewMask, ScreenManager.toClientBounds(new Rectangle(minimapBounds))); + gameViewMask = MaskZones.maskZones(gameViewMask, minimapBounds); } return gameViewMask; } diff --git a/third-party/DEV_README.md b/third-party/DEV_README.md new file mode 100644 index 0000000..2bfe645 --- /dev/null +++ b/third-party/DEV_README.md @@ -0,0 +1,37 @@ +# Preamble +[Brandon-T's RemoteInput](https://github.com/Brandon-T/RemoteInput) (RI) is a C++ based utility that provides IO to a Java application. +It serves as a bridge between the automation framework (this project) and the client. +RemoteInput functions by injecting a part of itself into the target and then using commands in AWT semantics to simulate IO. +This approach as opposed to OS level ones allows the user to keep using their mouse and keyboard and bypasses +the `LLMHF_INJECTED` flag that RuneLite checks for. +RemoteInput is currently being used in [SRL](https://github.com/Villavu/SRL-Development), +we have taken a great deal of inspiration from their integration of RI. + +The following guide explains how to build the binary for yourself, allowing you to audit the code beforehand. +If you want to use pre-compiled binaries, they are available from +[Brandon-T's reflection Auto-Build GitHub Actions pipeline](https://github.com/Brandon-T/Reflection/releases/tag/autobuild). +Alternatively, they are pre-packaged with ChromaScape (currently only Windows) in the `third-party/RemoteInput/precompiled` folder. + +# Windows Instructions + +1. Install [MSYS2](https://www.msys2.org/) +2. In an MSYS2 terminal, install the following dependencies: + - Windows x32: + > `pacman -S mingw-w64-i686-gcc mingw-w64-i686-clang mingw-w64-i686-python mingw-w64-i686-cmake make` + - Windows x64: + > `pacman -S mingw-w64-x86_64-gcc mingw-w64-x86_64-clang mingw-w64-x86_64-python mingw-w64-x86_64-cmake make` +3. In an MSYS2 MinGW terminal, navigate to the RemoteInput directory: + > e.g., `cd /c/Users/YourName/repos/ChromaScape/third-party/RemoteInput` +4. To build the binary, execute the following in the `RemoteInput` project's root folder, same level as CMakeLists.txt, + in an MSYS2 MinGW terminal: + ``` + # Set flags: "-m64" for 64-bit or "-m32" for 32-bit + cmake -S . -B cmake-build-release -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DOTHER_LINKER_FLAGS="-m64" + + # Build + cmake --build cmake-build-release --target all -j 4 + ``` +5. The binary will be located as: `third-party/RemoteInput/cmake-build-release/libRemoteInput.dll` +6. ChromaScape checks for a compiled binary before resorting to a provided pre-compiled one, + however you may safely delete the pre-compiled folder and any binaries within: + > `third-party/RemoteInput/precompiled` \ No newline at end of file diff --git a/third-party/RemoteInput/.github/workflows/build.yml b/third-party/RemoteInput/.github/workflows/build.yml new file mode 100644 index 0000000..ec42340 --- /dev/null +++ b/third-party/RemoteInput/.github/workflows/build.yml @@ -0,0 +1,311 @@ +name: Build + +on: + push: + branches: + - master + - 'feature/**' + pull_request: + branches: + - master + +jobs: + Build: + name: ${{ matrix.config.name }} + runs-on: ${{ matrix.config.os }} + strategy: + fail-fast: false + matrix: + config: + #Linux-aarch64 + - os: ubuntu-24.04-arm + name: Linux-aarch64 + other_linker_flags: '-L/usr/aarch64-linux-gnu/local/lib' + arch: aarch64 + output: libRemoteInput.so + release: libRemoteInput-aarch64.so + + #Linux-64 + - os: ubuntu-latest + name: Linux-64 + other_linker_flags: '-m64' + arch: x86_64 + output: libRemoteInput.so + release: libRemoteInput-x86_64.so + + #MacOS-64 + - os: macos-15-intel + name: MacOS-64 + other_linker_flags: '-m64' + arch: x86_64 + output: libRemoteInput.dylib + release: libRemoteInput-x86_64.dylib + + #MacOS-aarch64 + - os: macos-latest + name: MacOS-aarch64 + other_linker_flags: '-m64' + arch: aarch64 + output: libRemoteInput.dylib + release: libRemoteInput-aarch64.dylib + + #Windows-32 + - os: windows-latest + name: Windows-32 + other_linker_flags: '-m32' + arch: i686 + output: libRemoteInput.dll + release: libRemoteInput-i686.dll + + #Windows-64 + - os: windows-latest + name: Windows-64 + other_linker_flags: '-m64' + arch: x86_64 + output: libRemoteInput.dll + release: libRemoteInput-x86_64.dll + + steps: + - uses: actions/checkout@v1 + with: + submodules: true + + - name: Set up Python + if: matrix.config.name != 'Linux-aarch64' + uses: actions/setup-python@v4 + with: + python-version: '3.x' + + #Linux-aarch64 + - name: Update Sources - Linux-aarch64 + if: matrix.config.name == 'Linux-aarch64' + run: | + #source_version=$(cat /etc/apt/sources.list | grep "main restricted" | head -1 | sed -e 's/.*com\/ //' -e 's/ main.*//') + #echo "Found: ${ source_version }" + sudo apt-get update + + - name: Dependencies - Linux-aarch64 + if: matrix.config.name == 'Linux-aarch64' + run: | + sudo apt-get install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu + #sudo apt-get install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf + sudo apt-get install procps:arm64 mesa-common-dev:arm64 libglu1-mesa:arm64 libglu1-mesa-dev:arm64 libgl1:arm64 libgl1-mesa-dev:arm64 libglfw3-dev:arm64 cmake + sudo apt-get install openjdk-11-jdk + sudo apt-get install libpython3-dev:arm64 + + - name: CMake Generate Build Files - Linux-aarch64 + if: matrix.config.name == 'Linux-aarch64' + env: + JAVA_HOME: '/usr/lib/jvm/java-11-openjdk-amd64' + run: | + cmake -S '${{ github.workspace }}' -B '${{ github.workspace }}/cmake-build-release' -DCMAKE_SYSTEM_NAME=Linux -DCMAKE_SYSTEM_PROCESSOR=aarch64 -DCMAKE_C_COMPILER=/usr/bin/aarch64-linux-gnu-gcc -DCMAKE_CXX_COMPILER=/usr/bin/aarch64-linux-gnu-g++ -DCMAKE_BUILD_TYPE=Release -DOTHER_LINKER_FLAGS:STRING=${{ matrix.config.other_linker_flags }} -G "CodeBlocks - Unix Makefiles" + + - name: Build - Linux-aarch64 + if: matrix.config.name == 'Linux-aarch64' + env: + JAVA_HOME: '/usr/lib/jvm/java-11-openjdk-amd64' + run: | + cmake --build '${{ github.workspace }}/cmake-build-release' --target all -- -j 4 + + #Linux-64 + - name: Dependencies - Linux-64 + if: matrix.config.name == 'Linux-64' + run: | + sudo apt-get update + sudo apt-get install procps mesa-common-dev libglu1-mesa libglu1-mesa-dev libgl1 libgl1-mesa-dev libpython3-dev clang cmake g++ + + - name: CMake Generate Build Files - Linux-64 + if: matrix.config.name == 'Linux-64' + run: | + cmake -S '${{ github.workspace }}' -B '${{ github.workspace }}/cmake-build-release' -DCMAKE_BUILD_TYPE=Release -DOTHER_LINKER_FLAGS:STRING=${{ matrix.config.other_linker_flags }} -G "CodeBlocks - Unix Makefiles" + + - name: Build - Linux-64 + if: matrix.config.name == 'Linux-64' + run: | + cmake --build '${{ github.workspace }}/cmake-build-release' --target all -- -j 4 + + #MacOS-64 + - name: CMake Generate Build Files - MacOS-64 + if: matrix.config.name == 'MacOS-64' + run: | + cmake -S '${{ github.workspace }}' -B '${{ github.workspace }}/cmake-build-release' -DCMAKE_BUILD_TYPE=Release -DOTHER_LINKER_FLAGS:STRING=${{ matrix.config.other_linker_flags }} -G "CodeBlocks - Unix Makefiles" + + - name: Build - MacOS-64 + if: matrix.config.name == 'MacOS-64' + run: | + cmake --build '${{ github.workspace }}/cmake-build-release' --target all -- -j 4 + + #MacOS-aarch64 + - name: CMake Generate Build Files - MacOS-aarch64 + if: matrix.config.name == 'MacOS-aarch64' + run: | + cmake -S '${{ github.workspace }}' -B '${{ github.workspace }}/cmake-build-release' -DCMAKE_BUILD_TYPE=Release -DOTHER_LINKER_FLAGS:STRING=${{ matrix.config.other_linker_flags }} -G "CodeBlocks - Unix Makefiles" + + - name: Build - MacOS-aarch64 + if: matrix.config.name == 'MacOS-aarch64' + run: | + cmake --build '${{ github.workspace }}/cmake-build-release' --target all -- -j 4 + + #Windows-32 + - name: Dependencies - Windows-32 + if: matrix.config.name == 'Windows-32' + uses: msys2/setup-msys2@v2 + with: + msystem: MINGW32 + install: mingw-w64-i686-gcc mingw-w64-i686-clang mingw-w64-i686-python mingw-w64-i686-cmake make + update: false + + - name: CMake Generate Build Files - Windows-32 + if: matrix.config.name == 'Windows-32' + shell: msys2 {0} + run: | + set MSYSTEM=MINGW32 + MINGW_INSTALLS=MINGW32 + cmake -S '${{ github.workspace }}' -B '${{ github.workspace }}/cmake-build-release' -DCMAKE_BUILD_TYPE=Release -DOTHER_LINKER_FLAGS:STRING=${{ matrix.config.other_linker_flags }} -G "CodeBlocks - Unix Makefiles" + + - name: Build - Windows-32 + if: matrix.config.name == 'Windows-32' + shell: msys2 {0} + run: | + set MSYSTEM=MINGW32 + MINGW_INSTALLS=MINGW32 + cmake --build '${{ github.workspace }}/cmake-build-release' --target all -- -j 4 + + #Windows-64 + - name: Dependencies - Windows-64 + if: matrix.config.name == 'Windows-64' + uses: msys2/setup-msys2@v2 + with: + msystem: MINGW64 + install: mingw-w64-x86_64-gcc mingw-w64-x86_64-clang mingw-w64-x86_64-python mingw-w64-x86_64-cmake make + update: false + + - name: CMake Generate Build Files - Windows-64 + if: matrix.config.name == 'Windows-64' + shell: msys2 {0} + run: | + set MSYSTEM=MINGW64 + MINGW_INSTALLS=MINGW64 + cmake -S '${{ github.workspace }}' -B '${{ github.workspace }}/cmake-build-release' -DCMAKE_BUILD_TYPE=Release -DOTHER_LINKER_FLAGS:STRING=${{ matrix.config.other_linker_flags }} -G "CodeBlocks - Unix Makefiles" + + - name: Build - Windows-64 + if: matrix.config.name == 'Windows-64' + shell: msys2 {0} + run: | + set MSYSTEM=MINGW64 + MINGW_INSTALLS=MINGW64 + cmake --build '${{ github.workspace }}/cmake-build-release' --target all -- -j 4 + + #Shared + - name: Move Artifacts - ${{ matrix.config.name }} + if: github.event_name == 'push' + run: | + mv '${{ github.workspace }}/cmake-build-release/${{ matrix.config.output }}' '${{ github.workspace }}/cmake-build-release/${{ matrix.config.release }}' + + - name: 'Upload Artifacts - ${{ matrix.config.name }}' + if: github.event_name == 'push' + uses: actions/upload-artifact@v4 + with: + overwrite: true + name: 'artifact-${{ matrix.config.name }}' + path: '${{ github.workspace }}/cmake-build-release/${{ matrix.config.release }}' + + Release: + runs-on: ${{ matrix.config.os }} + strategy: + fail-fast: true + matrix: + config: + - os: windows-latest + target_release_repo: Reflection + + steps: + - name: Wait For Builds + if: github.event_name == 'push' + uses: Brandon-T/wait-for-check-action@v1 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + check_names: '["Linux-aarch64", "Linux-64", "MacOS-64", "MacOS-aarch64", "Windows-32", "Windows-64"]' + statuses: '["completed", "completed", "completed", "completed", "completed", "completed"]' + conclusions: '["success", "success", "success", "success", "success", "success"]' + timeout: 600 + poll_interval: 10 + + - name: Gather Release Notes + id: release_notes + if: github.event_name == 'push' + run: | + if [ "${{ github.event_name }}" = "push" ]; then + commit="${{ github.event.head_commit.message }}" + author="${{ github.event.head_commit.author.name }}" + notes="${commit}

- ${author}" + notes="${notes//\\n/
}" + notes="${notes//$'\n'/
}" + elif [ "${{ github.event_name }}" = "pull_request" ]; then + commit="${{ github.event.pull_request.title }}" + body="${{ github.event.pull_request.body }}" + author="${{ github.event.pull_request.author }}" + notes="${commit}
${body}

- ${author}" + notes="${notes//\\n/
}" + notes="${notes//$'\n'/
}" + fi + echo ::set-output name=description::"${notes}" + shell: bash + + - name: Download Artifacts + if: github.event_name == 'push' + uses: actions/download-artifact@v4 + with: + pattern: 'artifact-*' + path: 'artifact' + merge-multiple: true + + - name: List Artifacts + run: | + ls artifact + + - name: Upload Autobuild + if: github.event_name == 'push' && !startsWith(github.ref, 'refs/tags/v') + uses: Brandon-T/update-release-action@v1 + with: + github_token: ${{ secrets.CI_RELEASE_TOKEN }} + release_name: 'Auto-Build' + file: 'artifact/*' + is_file_glob: true + overwrite: true + release_notes: ${{ steps.release_notes.outputs.description }} + deletes_existing_release: true + pre_release: false + prefix_branch_name: false + suffix_branch_name: false + draft_release: false + retry_count: 2 + retry_delay: 5 + owner: '${{ github.repository_owner }}' + repo: '${{ matrix.config.target_release_repo }}' + tag: 'autobuild' + bump_tag: true + ref: 'master' + + - name: Upload Release Build + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') + uses: Brandon-T/update-release-action@v1 + with: + github_token: ${{ secrets.CI_RELEASE_TOKEN }} + release_name: ${{ steps.release_notes.outputs.version }} + file: 'artifact/*' + is_file_glob: true + overwrite: true + release_notes: ${{ steps.release_notes.outputs.description }} + deletes_existing_release: false + pre_release: false + prefix_branch_name: false + suffix_branch_name: false + draft_release: false + retry_count: 2 + retry_delay: 5 + owner: '${{ github.repository_owner }}' + repo: '${{ matrix.config.target_release_repo }}' + tag: ${{ steps.release_notes.outputs.version }} + bump_tag: true + ref: 'master' diff --git a/third-party/RemoteInput/.github/workflows/build.zip b/third-party/RemoteInput/.github/workflows/build.zip new file mode 100644 index 0000000000000000000000000000000000000000..35210e3ec9fc7c1645bd9a2b90d95c02b19ce8b7 GIT binary patch literal 9038 zcmb7~WmFv5qJ%zkhcL;JwehqLSG|gzCmYV`|j@=l#|bPm4r6TPmMi3#J4;4@Qbk1PfmuZy$iR z%NoW4z)2j^T(w}H&u@=6wG`HB%b97O*$(!usw1Edrh?*<#X6$A?PgSYXnh*< zS@`+}o1Jc%twn|%k8?9ToK-~+6x^-(qt{QI6%kW00aDvxoDC#J5i6TzrS-lruneUb zcU#|VQg&lsM;<$9bSHVD8yUl~@1eBE;d9&{mzlK$wx0fTmuBoj!#j{y=%PmGZL#?N1zmc=Q#ZLqvU#ff z;CuMDAM&@i-`wQsu+VH-iB6OEbRr^A9&sn7Fc?b@#ryddF&4cEpjMvd39(0wO(G;7 z(AKV~bd*%I`9%Rl6etkN5gS;vcsMrvZWtTJF+_o|oTeR%z)nTmLoqp1xDY;MpD9e; z<|;qSR%b;xh-0ahQZs!Qp{2r7pwcuw^l2ZEVUR;9mvbcX_A7lf+~EFRIWE!=Dnljp zfSbT>mv?q}*_e55Q7DYb>3~8B3w3ZT>F&ocSZ02>cH1`a*^}Lz;2E+jN}F~4*JKYc zax~xi_cQY;roc=nKE3^eP3!6n!Un|3%}WzCW>g&n#0b~Z6SMk8&gNzQlWTpCf*afR zV}Asq_GpRfcBtwNey>2`>ihPG53W3TSI9T3mY9p-9n(fpKR;9?gjk_mkEyCJ+V?WQ zFJYOXwd8bck9En(kw^#&EsnsgglZ##hqyp%${;;a5mYSY;S)RN(09wzrln^tYu*sV zxIG`FlX4kA-^DUgCvTYPGRzkcrCl!{uLgQ}uLp27G&mzF_`)Iam4Wzlr*#_DBaaaZ z^2|J}85E@lgR9eN$LA@qTvPFsD3_1+U|3gHNUC4XV0e7fZ39HX8dco1&vuy!3H_|F z&AnWe3H@3yb+>vQj8)7iTh%t^^i^BCcMl*p_mr%GwUq0SJdyQ?^Kp8?k?mRH^Wrf( z-C<1Dl$8Ab719L%&9pAA7Moq$6A=Xsg7!@nUJ&-4J$4C4kZzt^Pzo{vN`;UB zsJUm=3wn(!Ooqqg=-%})-g=)d{WLCSA$K&K>;bB(j1yrXdL`#Wd0uh$EYc0hjR=2+ zV}!n?U`(sbQ>gV|dKPgPPmE^(6=``)oH2(EC#4a5SD4~9SmiJ=2?aVHCLqt@_^Lys zd1IZ>rE_{Zy&hru{7>Jo> z(Cj4s*vmdCy4#TfpC;)_A}T`eJPSy*vJZnp2dn7ZnwGtze$T9X>)f-*`(^~Fcg-KV zLnrPwy}Ah&2>2pBmp&LDLb?E0nJ(|ZCiD>+?Z|hfHPmoVc4yVrFHgdD&-J*cYX>)f zSs1T)0v}wT6XA7u)TQ=Z->f|8wd>cpoa&j~(Cxkelw{%Hk+9msYheI@->$FpAM;=Z z8vsE0|2%N8HV6KfX>b`}@j<3Sx83N^G`Lh=LRiQjhL*`US*RprECY1-kj zwaoA-6P7|a{hbEj^U}zPR5*SC`U&`?dg+cPDmIh_z=f#1fEN^WJn(xEEy`;a(2Y|N zr1)`MQ$92s$ZP;~9%vvf6BCgHd- zRRO`G<_OAK%H#2lmINfl0+g(LoBqi#6%v&$Q4lr2k;VmKNXRVa0J2#a>FMH`x1eL! zVQh`2bmcs1xqI5l;@fbQ^jyY4kVq*KHJEN*bRVWI1YyV==(f5R1m^PrVx(I{7~O4 zfl8|!2SH8LvTy^cd@@eHw_Wsog+|9LF|kUx5uzb--_O+W+vxUM5r+2#_Ho579)X%ZHCz&pvp)AG)}RCCT>ae;jt7xi9k zk3w9!{2tr!L)8PGM@qCsUioV@1Mx zp?|2Wt+jXr8f&yT()Q}aea><#VrDT8M@Jf!Gl}{rMg=-g#61kpGd1B%FEpc*5C4>jX#Km~PAsy_N?)91@u#u7+2*ul(01R_fDDM2iUatFNh?G1=J z=gKL@dFf@j*dH1nf>WN>$GM?=AAP)7macEOT;T-UNs}pTB_E!8+&YBomRWIbo}Qmi zR=-ae%sC=buKSstgtwrt!69b7x_xpK|V zt8-;}jmUF5aN5qcy*dB1bq1K49A@>Qgqf@nSm&7My&HntuBW~4!#6_ z*h5IfQWvxw7b@fqxqvNyi~KlU9?%@6J@c+9qN5G&qJHnz`p8c*#sX& zW8E@&*w_llA2rXZ%FN6^6&O8z^7Z`GkI5|RWYmOxv6Fse9ibr3wn$e_UOZvu(}yH% z53(J`a%@%Zarx_b>;mh!6kllcHR0m~1A1#t^@>E_mXqm^ds@NxhDQ@0_jF+Dt`s^L znNV-Gs>_zkGO~NtlCG2U#n;2muMyct`(}wRT~s#R!$C&O?@R-($%;jX z9cMQ^^{j1eMC3KnJy4Z6K_$C}*>CWAisu9dj{E~2c8G|4mP{X``kz&6=`|m53B2gk zjR_qUGjmRp3y%?HK6(W2*IfT-^9R5K=88>)9%}8C-e583sU@rF{IyQ465CJKhY!9SOYf|r;huY*GkWyA z<+;1vzcYo|A;A<1L}m``$uFlA`ed)aNyyEax0H4yfq zs}0j_yK0Iq4##zDK52qZ3M^lh>Zejfd-eNz_Mav0$+wryQ zAA{fCFm;=q+>AZNr+GdIwX2Z5!2R}nIq2uC74>i4c78+NvTvTX~1?50P(dphog>orh zevwEOtg0KU?8G_xHy>i>`60W;kMuoWXh=FNIw^%JoHEl}sn=RgLs;vwXTE}w+vsvr zsrT^{TXH{b{F9jPRo&B&^d#4Uv7?OeXa*9{xs2nZ3yVI#r*o$9hDt()<>HYf7uM~V z+|k&X2oh_D#m85gUD(mnXX8w*W10oWhJwDRWo()xXabwPGl9I<3{Ip|MkX}XI}{Y` z1D0RsF6yMoiC;vd@isH{mObCvqSIYoqpzs;AdGya#M$rK1fh%e!yqM!UJ#VTtjrSO z2|8WeF{dh(5o4rpGG`-!@(cS4`&aDojw&dT8}9co&5E_6(HZb?p0is-10h1gkM0en zBCAiDRt3>Im>)^KfVkl&Oe*W7viB)Rmg_{abfY81$^tSr{*g@gk@Dk>1b6;=#02>C zgBp?Q1x5Ye^~jQl2kn-gS8Onc*za_ii)GgrbbTDZVs^aebGBI6oUf3aA}l0>MIZj< zKMXGiuj}fZAnVL@uP|iSVL&=iE89quLdIak zy&qNtj^cjln*$N`!m1+QM7vCW!cSWD3vsPmzT0x{%I4rya>Ol#RDcbL!bJ9_t7<0S z#*F=?_9lp8EtM(+L+p5VXP>^5L5fGtG0O@$#8L?Y9T0ZuPOY`KXBc{^@jRL8ou<}2Zk-K6Ao8`xx3FkOT za2P4-FYaCrlPbWsbL8FkgC@&d__b|97m~PZZ6E9$yAPQ->S|Vb2hRBjXF*9!)f5l% zKAbp z_Rv<90N#a^&_k{o4?PeY*Um(u*?p2a@+q`lXOsN+(&x_s9aANTV;mm-C`;Lge20eX z7j4@tS%iEgdRSt%He;l!vXlJ&uwFbS7Td{o-tV8L>I<$(lV(owbzwdVt3fegf7Vh0 z^V5gpe`;tD2R|-gY(Cm_BTxk3d%BI})H- zHI_1WvEuM!VH0KYd!cjdFgbPUAZbPI?Q3JOBk4ti;__oaxNHw5V<*V(qAL;2SWBqL zUwX1cDMUP56qAe=l2V1>JFua@)7zMpTFDV?l?Yu6t28lbX~{Pw z8%jPIIRT9{S_RbC8n2#xgs&Szy9)i{`T{KbR9z2tsJOe^oeRv^@AYO{D`8ms=Kd>} z@A;3E8=ATu3xfp!erMT#PPuy2zopzi?6}meZMP&)pC_tQXAZoQER+;Ms;xy9Lukm9 zDHZV3WflqxD|&rImdxwAaQOr;o%MS7Ki@-^f*y8n{Q~#Xmeb7iT;k({JkH{Z;!^2d z&u7DY9oEc0&jrGMWl+ID$pQFNfOzso0)cL|NNl?MZBKobF^8nWOP359_)T;NHMJitXnUT}U+f>wp z3U4J$c+PVT+KY1zHA93Pv5`Pz9E63p?O67Dfzl(iAFo)ehBjbzFnEhsm>bS`F^G^r zW1!MBDMzawvMgHJ7{oL-5CyMTBWu}r%=L%klh9tSh)&5v7DJH|sOam0NcV{=lkwJ( z05P$5+ymAY;g<}xW~al6qB+HL6m!WCne0Kt9t;anaQX|a3Xz64cmyhv##gk6T8^Y8 z780T=4#+L*)WOF(>*I(^jGx7Vo6SxYnLT`NfOdBa#82m*)$9#ynBKUZM{l1-d*)$; zCYTi;7Qxr%JS{(5n=mgV)>Db>9;O5^(2gC@6tI+?66#=Z1B^aM)qga$S!aDorNH0M zGihm>@Ux2+{`!P8=#28vAA~?DuCCEs@kK&UbKXbGyPF-=?YjIb4m_>;a0kr_VDHNQ zobZh&Cq3zmu6)!KgKvssMgI1RJ$DjRlqDKRA?M1KsEr{*wjra9pcWqKsEwr-e;5|a zugX}O8K`!hiStU|qxz5t$NI<35wp`4HtNGK=FPUYvrTGl*e*M3#S>Jq7oBd}8whac z>S|V?;=AozD75v{8TM3f&1zb$IlV|?!BmxJ&wJyh2{9hDSN^tG)O+V1>P%KHIt&CF zdFp{|O-1Bs_KBdyI(*tKabw6pZSoMXns|KK>P)HtT9!qoPw>ly&-a_@li;;rbwdeCepZIC{TIZM< zXS*X6L7#tyzLY|d7q7SUiTYDz&D)VcZq7H_oLI$P+fl-MWM_8jJKvF%sgCe;hdTiOVjIPiUtOoX|${_~r&*0s-Jgmi+GHP4Mqj>_Z zrIvLqMPe!0g;RuNaJjOLUl5kTWxHe!OM6$Bzjn3Uo zC$dc^Cw!|F2`{j};aixjLZ1&B0QhY_|JjzJ(EeJ!TY+qx-Tt{T{aL>Mu3IEq-_`vt z-`(wJNI-l%$RUP|f7C4vuXW27$><;DJFUH5Byct5KG5_qyWk{y+fiWOX#kRE*PWO8 zlGwF3>p1Fd8e9#8Qlba?CKib@M+~(b+!^&)DVJQ&;2RRL(j|S$**&z)0qS=k%NQ9Z zbhxpKMH}!M$rz2$jd5Cuz~!-HOJ}ekVr6Xk7-ucoI-G3O)SJSn{1DSc8+*S*2N7i? z5Ct+vsTiCCRD!VIQ@KQ)!kt1t~Vw>qJ4PWHfuyp-Y8|7!@Qs2N#mbgj7jN6gM zjJY&*B1e|Hp}&`R9_;224}(A?idvGxbIIK9tS;)_?LOadstHGs;A}^%qW>&J*pNqO zO!Xz}Olt@r!GOiUrL%|CR2+x3a{eKAt|_`9;6UgTXSUJjZatfMOMQEGO^32kbE*Vo zjhyfc4=l+g2zrg|8i75z?Y{8tm86oMtU9d_0~Wxf>FiV}KB-@~HnSYXR4=OB+Yz!J zt;(iz9elc=qH-lRCJyN(%CXd9isbsVu?o)yVE!7}FZX>QsPdGZ^>j_!lfgV&M4&(g zcKK*A;yoY5&+hs?@?`dQ&2{Y157CL!QU>xM_^(R_0q&qz z*WqJIf-}^oBs7j@-!Xc zT{G56)6VS!rAZ^N%Ym-l?h@hMQJH=W1d-e{(i5>Sm|Rk+(OAqs4Kbl#CjwYkK?rE? z#jXo0Mk7R}(!W-*34AM%>(kom3;giziJ$F3UcTDs&~@{B^9jC{>67f~>V8>+XL_=l zM$m$xpi^lZfd_qhxpA@X3?#o&%_sdM;kv5k^nsZn^GZFd;kzH-Kx=)h>^*^=Yp!s< z3263@LSiM_LL3ewKirk;HCPLtp0@2c1PD*EQTomw625D)tFKJvyQ<5%B||9_O6gfX zy!jQs#(Y)z5q3P{)N{_#S*Hcrn!}LF1ap&s;mUy_K1EKK)<(mOF687;6~mZXq}#X@ zYR*SC+&UW_nw-JJvFhz)y&Y?zO92h0XMf^_`@ysLoVzPpn_SL&itysSJRF1Cc}^lp z9x&Y5Q`TTApc8pBlu}=vr`-9-!pv+uO3cml*zOee2rF2N)z6jSBh-N;zBA+7kG%A~ zBOWCNGy;=PudnwU5LMIG;a=SX{6f1ho>8!Uz8WFu#PjHp)^m(==o5D$D|2(`{fmL} z1?sooC0ZM0#J&36Z&&-zez%J9R}8Z@G`4m8M`eS$wdM9JhGpxsI`@?Y%bXPrXH5F|x86S{|t=~qpzKH1)3=wlum53d~#12G8| zVwrsW`_}CJ117WYvBjdZA|W?m?NiUk0hHSoh?GQ?`~$u&x$oF8@;gC4a_9PvbZ%J+ z5lZ5KWb`$ZNlsM^)J!`gJMoyP^8;fN>`qsU?af8Up2a2f&#FYlWD~8^wC2o}8Y>FS z!;zvnz`30fAnpWizARKKh*+q)E&;EDA9B#dCxCi4&y{&cZd}?VydR>uU1f=BZasfz zjm<*U&DX=-0gTwj9T;)ba~reak(HN}ci{1u$Bo1UtG@5X+s<nz_-fQ$UP)3RtoXjf2(H*?MBdn?MA zQWy{3kD53Cb|fLQ#`tKlbhh4 zS&DEd`bzhbKF(m`{92Xr_PyzsG1A5#zwxe0YU0VjmeKI)N(HgG+1gs$cpsnxxMdjRbv#{yX0s`{EcAR9cGzYcMkWIt-e2O zgRPYaJIX$;Sd_A0;o1fu;N-=~;;UatQ=OD{q{z()a(e!by9L~{Pg7# zlN{F&LYJjTlM5jmySx~umLd#H*>5!j=cT{%j>$&&i{JppP$0_bCy4d!y;%ydm0>u@ z|4ej+WU$IyJ9VvYgcGw?>SK)RZZ~?6J0HrML%f2echS0w&CSY%IJ{9XR91?=x-wSSF5 zf7I6r>!0%fbdl}v;(wj@{<}B;>0jdixY7N)_+RPu-^KUP|04d+c=hk%e~pTNzw6$w vE0h0wTmB0i{=58NANaptyEVbT) + + IF(NOT USE_SYSTEM_PYBIND11) + target_include_directories(RemoteInput PRIVATE ${CMAKE_SOURCE_DIR}/RemoteInput/Thirdparty/nanobind/include) + ENDIF() +ELSE() + add_subdirectory(RemoteInput/Thirdparty) + add_library(${PROJECT_NAME} SHARED ${SRC_LIST} $) +ENDIF() + +#set_target_properties(${PROJECT_NAME} PROPERTIES VERSION ${PROJECT_VERSION}) +target_include_directories(${PROJECT_NAME} PRIVATE ${INCLUDE_DIRECTORIES}) + +IF(PYTHON_BINDINGS) + IF (USE_PYBIND11) + target_compile_definitions(${PROJECT_NAME} PRIVATE USE_DETOURS=1 HOOK_OPENGL_SWAP=1 HOOK_DIRECTX_BLIT=1 USE_PYBIND11=1 Py_BUILD_CORE=1 Py_NO_ENABLE_SHARED=1) + ELSEIF(Py_LIMITED_API) + target_compile_definitions(${PROJECT_NAME} PRIVATE USE_DETOURS=1 HOOK_OPENGL_SWAP=1 HOOK_DIRECTX_BLIT=1 Py_LIMITED_API=${Py_LIMITED_API} Py_BUILD_CORE=1 Py_NO_ENABLE_SHARED=1) + ELSE() + target_compile_definitions(${PROJECT_NAME} PRIVATE USE_DETOURS=1 HOOK_OPENGL_SWAP=1 HOOK_DIRECTX_BLIT=1 Py_BUILD_CORE=1 Py_NO_ENABLE_SHARED=1) + ENDIF() +ELSE() + target_compile_definitions(${PROJECT_NAME} PRIVATE USE_DETOURS=1) +ENDIF() + +IF(WIN32) + IF(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + target_compile_options(${PROJECT_NAME} PRIVATE + $<$:-DDEBUG> + $<$:-O3 -fvisibility=hidden>) + target_link_options(${PROJECT_NAME} PRIVATE + $<$:-static -stdlib=libc++ -fuse-ld=lld -Wl,--enable-stdcall-fixup -Wl,--kill-at -Wl"/DEF:RemoteInput/RemoteInput.def" ${PY_LINK_OPTIONS} ${PY_DYNAMIC_LINKER_FLAGS}> + $<$:-s -static -stdlib=libc++ -fuse-ld=lld -Wl,--enable-stdcall-fixup -Wl,--kill-at -Wl"/DEF:RemoteInput/RemoteInput.def" ${PY_LINK_OPTIONS} ${PY_DYNAMIC_LINKER_FLAGS}>) + ELSEIF(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_compile_options(${PROJECT_NAME} PRIVATE + $<$:-DDEBUG -Wl,--input-def=RemoteInput/RemoteInput.def> + $<$:-O3 -fvisibility=hidden -Wl,--input-def=RemoteInput/RemoteInput.def>) + target_link_options(${PROJECT_NAME} PRIVATE + $<$:-static -static-libgcc -static-libstdc++ -Wl,--enable-stdcall-fixup -Wl,--kill-at ${PY_LINK_OPTIONS} ${PY_DYNAMIC_LINKER_FLAGS}> + $<$:-s -static -static-libgcc -static-libstdc++ -Wl,--enable-stdcall-fixup -Wl,--kill-at ${PY_LINK_OPTIONS} ${PY_DYNAMIC_LINKER_FLAGS}>) + ELSEIF(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + set_property(TARGET ${PROJECT_NAME} PROPERTY + MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + + set(CMAKE_EXE_LINKER_FLAGS + "${CMAKE_EXE_LINKER_FLAGS} /DEF:RemoteInput/RemoteInput.def") + target_compile_options(${PROJECT_NAME} PRIVATE + $<$:/DDEBUG /DWIN32_LEAN_AND_MEAN /DNOMINMAX /MTd> + $<$:-O3 /DWIN32_LEAN_AND_MEAN /DNOMINMAX /MT>) + + IF(PYTHON_BINDINGS) + target_link_options(${PROJECT_NAME} PRIVATE + $<$:> + $<$:/LIBPATH:${Python_LIBRARY_DIRS} ${Py_LINK_OPTIONS} ${PY_DYNAMIC_LINKER_FLAGS}>) + ELSE() + target_link_options(${PROJECT_NAME} PRIVATE + $<$:> + $<$:>) + ENDIF() + ENDIF() +ELSEIF(APPLE) + target_compile_options(${PROJECT_NAME} PRIVATE + $<$:-DDEBUG -g -fvisibility=hidden> + $<$:-O3 -fvisibility=hidden>) + target_link_options(${PROJECT_NAME} PRIVATE + $<$:-static -stdlib=libc++ -Wl"/DEF:RemoteInput/RemoteInput.def" ${PY_LINK_OPTIONS} ${PY_DYNAMIC_LINKER_FLAGS}> + $<$:-s -static -stdlib=libc++ -Wl"/DEF:RemoteInput/RemoteInput.def" ${PY_LINK_OPTIONS} ${PY_DYNAMIC_LINKER_FLAGS}>) +ELSE() + target_compile_options(${PROJECT_NAME} PRIVATE + $<$:-DDEBUG -g -fvisibility=hidden> + $<$:-O3 -fvisibility=hidden>) + target_link_options(${PROJECT_NAME} PRIVATE + $<$:-g ${PY_LINK_OPTIONS} ${PY_DYNAMIC_LINKER_FLAGS}> #-static-libgcc -static-libstdc++ + $<$:-s ${PY_LINK_OPTIONS} ${PY_DYNAMIC_LINKER_FLAGS}>) #-static-libgcc -static-libstdc++ +ENDIF() + +IF(PYTHON_BINDINGS) + IF(USE_PYTHON3) + set(PY_MODULE Python3::Python) + ELSE() + set(PY_MODULE Python::Python) + ENDIF() + + IF(USE_PYBIND11) + target_link_libraries(${PROJECT_NAME} PRIVATE ${LIBRARIES_LIST} ${EXTRA_LIBRARIES} ${PY_LIBRARIES} ${PY_MODULE}) #nanobind::module + ELSE() + target_link_libraries(${PROJECT_NAME} PRIVATE ${LIBRARIES_LIST} ${EXTRA_LIBRARIES} ${PY_LIBRARIES}) + ENDIF() +ELSE() + target_link_libraries(${PROJECT_NAME} PRIVATE ${LIBRARIES_LIST} ${EXTRA_LIBRARIES}) +ENDIF() + + +# ---------------------------- RENAME ---------------------------- + +IF(WIN32) + add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E rename + $ + ${CMAKE_BINARY_DIR}/libRemoteInput.dll + COMMENT "Renaming module to RemoteInput.dll" + ) +ELSEIF(APPLE) + add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E rename + $ + ${CMAKE_BINARY_DIR}/libRemoteInput.dylib + COMMENT "Renaming module to RemoteInput.dylib" + ) +ELSE() + add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E rename + $ + ${CMAKE_BINARY_DIR}/libRemoteInput.so + COMMENT "Renaming module to RemoteInput.so" + ) +ENDIF() \ No newline at end of file diff --git a/third-party/RemoteInput/README.md b/third-party/RemoteInput/README.md new file mode 100644 index 0000000..79d3caa --- /dev/null +++ b/third-party/RemoteInput/README.md @@ -0,0 +1,14 @@ +# RemoteInput + +![example workflow](https://github.com/Brandon-T/RemoteInput/actions/workflows/build.yml/badge.svg) + +Remote input injects into a Java process to allow io operations such as keyboard and mouse input, reading canvas data and drawing on the canvas. + +Binaries are available here: +https://github.com/Brandon-T/Reflection/releases/tag/autobuild + +--- + +> [!NOTE] +> Linux has protection against dll injection, `setcap` can be used on the executable injecting remoteinput. +> `sudo setcap cap_sys_ptrace=eip /path/to/executable` diff --git a/third-party/RemoteInput/RemoteInput.cbp b/third-party/RemoteInput/RemoteInput.cbp new file mode 100644 index 0000000..aae3809 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput.cbp @@ -0,0 +1,170 @@ + + + + + + diff --git a/third-party/RemoteInput/RemoteInput.depend b/third-party/RemoteInput/RemoteInput.depend new file mode 100644 index 0000000..131923b --- /dev/null +++ b/third-party/RemoteInput/RemoteInput.depend @@ -0,0 +1,643 @@ +# depslib dependency file v1.0 +1585447356 source:c:\users\brandon\desktop\remoteinput\remoteinput\echo\module.cxx + "Module.hxx" + +1585447356 c:\users\brandon\desktop\remoteinput\remoteinput\echo\module.hxx + + + + + + + +1585447356 source:c:\users\brandon\desktop\remoteinput\remoteinput\echo\time.cxx + "Time.hxx" + +1585447356 c:\users\brandon\desktop\remoteinput\remoteinput\echo\time.hxx + + + + + +1585447356 source:c:\users\brandon\desktop\remoteinput\remoteinput\platform\debugconsole.cxx + "DebugConsole.hxx" + + +1585447356 c:\users\brandon\desktop\remoteinput\remoteinput\platform\debugconsole.hxx + + + +1570993320 source:c:\users\brandon\desktop\remoteinput\remoteinput\platform\detour.cxx + "Detour.hxx" + +1570993520 c:\users\brandon\desktop\remoteinput\remoteinput\platform\detour.hxx + + + +1586011076 source:c:\users\brandon\desktop\remoteinput\remoteinput\jvm.cxx + "JVM.hxx" + +1585447356 c:\users\brandon\desktop\remoteinput\remoteinput\jvm.hxx + + + + + + + +1566286641 c:\program files\java\jdk-12.0.2\include\jni.h + + + "jni_md.h" + +1566286641 c:\program files\java\jdk-12.0.2\include\win32\jni_md.h + +1579049548 source:c:\users\brandon\desktop\remoteinput\remoteinput\platform\platform_darwin.cxx + "Platform.hxx" + + + + + + +1585447356 c:\users\brandon\desktop\remoteinput\remoteinput\platform\platform.hxx + + + + + + "Reflection.hxx" + +1585755058 c:\users\brandon\desktop\remoteinput\remoteinput\reflection.hxx + + + + + "JVM.hxx" + "ReflectionHook.hxx" + +1586235951 source:c:\users\brandon\desktop\remoteinput\remoteinput\platform\platform_linux.cxx + "Platform.hxx" + + + + + + + + + + + + + + + + +1586236603 source:c:\users\brandon\desktop\remoteinput\remoteinput\platform\platform_windows.cxx + "Platform.hxx" + + + "Thirdparty/main.hxx" + + + +1586011072 source:c:\users\brandon\desktop\remoteinput\remoteinput\reflection.cxx + "Reflection.hxx" + +1585447356 source:c:\users\brandon\desktop\remoteinput\remoteinput\threadpool.cxx + "ThreadPool.hxx" + +1585447356 c:\users\brandon\desktop\remoteinput\remoteinput\threadpool.hxx + + + + + + + + + + +1586235951 source:c:\users\brandon\desktop\remoteinput\remoteinput\echo\sharedevent.cxx + "SharedEvent.hxx" + + + +1585447356 c:\users\brandon\desktop\remoteinput\remoteinput\echo\sharedevent.hxx + + + + + + "Time.hxx" + + + + + + + + + + + + + + + + + + + + + + + + + + + +1581385784 D + +1586010899 source:c:\users\brandon\desktop\remoteinput\remoteinput\plugin\simbaplugin.cxx + "SimbaPlugin.hxx" + + + + + "ControlCenter.hxx" + +1585077447 a7a9eb951068d6ebd1812307eac0a2f2f33f4 + "SimbaPlugin.hxx" + + + + + "ControlCenter.hxx" + + + "EIOS.hxx" + "TMemoryManager.hxx" + + + "ControlCenter.hxx" + "EIOS.hxx" + + + + + + "MemoryMap.hxx" + "SharedEvent.hxx" + "Reflection.hxx" + "Platform.hxx" + + "Signal.hxx" + "Reflection.hxx" + "Applet.hxx" + "MemoryMap.hxx" + "InputOutput.hxx" + "ControlCenter.hxx" + + + + "EIOS.hxx" + "Platform.hxx" + "NativeHooks.hxx" + "Graphics.hxx" + "ReflectionHook.hxx" + + + + + + "DetachedThreadPool.hxx" + "Reflection.hxx" + "Component.hxx" + + + + "AWTEvent.hxx" + "Component.hxx" + + "InputOutput.hxx" + "ControlCenter.hxx" + "Platform.hxx" + "Random.hxx" + "Applet.hxx" + "InputEvent.hxx" + "FocusEvent.hxx" + "KeyEvent.hxx" + "MouseEvent.hxx" + "MouseWheelEvent.hxx" + "PointerInfo.hxx" + "PointerInfo.hxx" + + +1585447356 c:\users\brandon\desktop\remoteinput\remoteinput\plugin\simbaplugin.hxx + + + "EIOS.hxx" + "TMemoryManager.hxx" + +1585447356 c:\users\brandon\desktop\remoteinput\remoteinput\eios.hxx + + + "ControlCenter.hxx" + +1570069164 c:\users\brandon\desktop\remoteinput\remoteinput\shmallocator.hxx + + + +1585719994 c:\users\brandon\desktop\remoteinput\remoteinput\plugin\tmemorymanager.hxx + +1585447356 c:\users\brandon\desktop\remoteinput\remoteinput\echo\memorymap.hxx + + + + + + + +1585447356 c:\users\brandon\desktop\remoteinput\remoteinput\platform\nativehooks.hxx + +1586235951 c:\users\brandon\desktop\remoteinput\remoteinput\platform\javainternal.hxx + + +1586010797 source:c:\users\brandon\desktop\remoteinput\remoteinput\eios.cxx + "EIOS.hxx" + + + + + + "MemoryMap.hxx" + "SharedEvent.hxx" + "Reflection.hxx" + "Platform.hxx" + +1572918827 source:c:\users\brandon\desktop\remoteinput\remoteinput\plugin\commandthread.cxx + "CommandThread.hxx" + +1572913992 c:\users\brandon\desktop\remoteinput\remoteinput\plugin\commandthread.hxx + + + + + + + + + "../Echo/SharedEvent.hxx" + +1585447356 source:c:\users\brandon\desktop\remoteinput\remoteinput\plugin\graphics.cxx + "Graphics.hxx" + + + + + + + + + + +1585447356 c:\users\brandon\desktop\remoteinput\remoteinput\plugin\graphics.hxx + + +1585447356 c:\users\brandon\desktop\remoteinput\remoteinput\plugin\controlcenter.hxx + + + "Signal.hxx" + "Reflection.hxx" + "Applet.hxx" + "MemoryMap.hxx" + "InputOutput.hxx" + +1573527112 source:c:\users\brandon\desktop\remoteinput\remoteinput\platform\nativehooks.cxx + "NativeHooks.hxx" + + "rd_route.h" + + + "JavaInternal.hxx" + "EIOS.hxx" + "Graphics.hxx" + +1586010866 source:c:\users\brandon\desktop\remoteinput\remoteinput\plugin\controlcenter.cxx + "ControlCenter.hxx" + + + + "EIOS.hxx" + "Platform.hxx" + "NativeHooks.hxx" + "Graphics.hxx" + "ReflectionHook.hxx" + +1586235951 source:c:\users\brandon\desktop\remoteinput\remoteinput\platform\nativehooks_darwin.cxx + "NativeHooks.hxx" + "main.hxx" + "Thirdparty/main.hxx" + + + + + + + "JavaInternal.hxx" + "EIOS.hxx" + "Graphics.hxx" + +1586235951 source:c:\users\brandon\desktop\remoteinput\remoteinput\platform\nativehooks_linux.cxx + "NativeHooks.hxx" + + + + "JavaInternal.hxx" + "EIOS.hxx" + "Graphics.hxx" + + + + +1586236794 source:c:\users\brandon\desktop\remoteinput\remoteinput\platform\nativehooks_windows.cxx + "NativeHooks.hxx" + + + + + + + + "JavaInternal.hxx" + "EIOS.hxx" + "Graphics.hxx" + "Thirdparty/main.hxx" + + +1455496858 c:\program files\java\jdk1.8.0_73\include\jni.h + + + "jni_md.h" + +1455496858 c:\program files\java\jdk1.8.0_73\include\win32\jni_md.h + +1586235951 c:\users\brandon\desktop\remoteinput\remoteinput\reflectionhook.hxx + + + + +1586010892 c:\users\brandon\desktop\remoteinput\remoteinput\plugin\signal.hxx + "SpinLock.hxx" + "SharedEvent.hxx" + +1585447356 c:\users\brandon\desktop\remoteinput\remoteinput\echo\spinlock.hxx + + + + + +1586011076 source:c:\users\brandon\desktop\remoteinput\remoteinput\plugin\plugin.cxx + "Plugin.hxx" + + + + + "MemoryMap.hxx" + "SharedEvent.hxx" + "Platform.hxx" + "NativeHooks.hxx" + "ControlCenter.hxx" + +1586011076 c:\users\brandon\desktop\remoteinput\remoteinput\plugin\plugin.hxx + + + + "EIOS.hxx" + + +1585447356 c:\users\brandon\desktop\remoteinput\remoteinput\plugin\inputoutput.hxx + + + + + + "DetachedThreadPool.hxx" + "Reflection.hxx" + "Component.hxx" + +1585447356 source:c:\users\brandon\desktop\remoteinput\remoteinput\java\applet.cxx + "Applet.hxx" + + +1585447356 c:\users\brandon\desktop\remoteinput\remoteinput\java\applet.hxx + + "Component.hxx" + +1585447356 c:\users\brandon\desktop\remoteinput\remoteinput\java\component.hxx + + + + "AWTEvent.hxx" + +1585447356 c:\users\brandon\desktop\remoteinput\remoteinput\java\awtevent.hxx + + +1585447356 source:c:\users\brandon\desktop\remoteinput\remoteinput\java\awtaccessor.cxx + "AWTAccessor.hxx" + + +1585447356 c:\users\brandon\desktop\remoteinput\remoteinput\java\awtaccessor.hxx + + "AWTEventAccessor.hxx" + +1585447356 c:\users\brandon\desktop\remoteinput\remoteinput\java\awteventaccessor.hxx + + "AWTEvent.hxx" + +1585447356 source:c:\users\brandon\desktop\remoteinput\remoteinput\java\awtevent.cxx + "AWTEvent.hxx" + +1585447356 source:c:\users\brandon\desktop\remoteinput\remoteinput\java\awteventaccessor.cxx + "AWTEventAccessor.hxx" + + +1585447356 source:c:\users\brandon\desktop\remoteinput\remoteinput\java\component.cxx + "Component.hxx" + + +1585447356 source:c:\users\brandon\desktop\remoteinput\remoteinput\java\eventqueue.cxx + "EventQueue.hxx" + + +1585447356 c:\users\brandon\desktop\remoteinput\remoteinput\java\eventqueue.hxx + + "AWTEvent.hxx" + +1585447356 source:c:\users\brandon\desktop\remoteinput\remoteinput\java\focusevent.cxx + "FocusEvent.hxx" + + +1585447356 c:\users\brandon\desktop\remoteinput\remoteinput\java\focusevent.hxx + + "AWTEvent.hxx" + "Component.hxx" + +1585447356 source:c:\users\brandon\desktop\remoteinput\remoteinput\java\frame.cxx + "Frame.hxx" + + +1585447356 c:\users\brandon\desktop\remoteinput\remoteinput\java\frame.hxx + + "Component.hxx" + +1585447356 source:c:\users\brandon\desktop\remoteinput\remoteinput\java\inputevent.cxx + "InputEvent.hxx" + +1585447356 c:\users\brandon\desktop\remoteinput\remoteinput\java\inputevent.hxx + + + +1585447356 source:c:\users\brandon\desktop\remoteinput\remoteinput\java\keyevent.cxx + "KeyEvent.hxx" + + +1585447356 c:\users\brandon\desktop\remoteinput\remoteinput\java\keyevent.hxx + + "AWTEvent.hxx" + "Component.hxx" + +1585447356 source:c:\users\brandon\desktop\remoteinput\remoteinput\java\mouseevent.cxx + "MouseEvent.hxx" + + +1585447356 c:\users\brandon\desktop\remoteinput\remoteinput\java\mouseevent.hxx + + "AWTEvent.hxx" + "Component.hxx" + +1585447356 source:c:\users\brandon\desktop\remoteinput\remoteinput\java\mousewheelevent.cxx + "MouseWheelEvent.hxx" + + +1585447356 c:\users\brandon\desktop\remoteinput\remoteinput\java\mousewheelevent.hxx + + "AWTEvent.hxx" + "Component.hxx" + +1585447356 source:c:\users\brandon\desktop\remoteinput\remoteinput\java\toolkit.cxx + "Toolkit.hxx" + + +1585447356 c:\users\brandon\desktop\remoteinput\remoteinput\java\toolkit.hxx + + "EventQueue.hxx" + +1585447356 source:c:\users\brandon\desktop\remoteinput\remoteinput\plugin\inputoutput.cxx + "InputOutput.hxx" + "ControlCenter.hxx" + "Platform.hxx" + "Random.hxx" + "Applet.hxx" + "InputEvent.hxx" + "FocusEvent.hxx" + "KeyEvent.hxx" + "MouseEvent.hxx" + "MouseWheelEvent.hxx" + "PointerInfo.hxx" + +1585447356 c:\users\brandon\desktop\remoteinput\remoteinput\random.hxx + + + + +1585447356 source:c:\users\brandon\desktop\remoteinput\remoteinput\random.cxx + "Random.hxx" + +1585447356 source:c:\users\brandon\desktop\remoteinput\remoteinput\thirdparty\min_hook\src\buffer.c + + "buffer.h" + +1585447356 c:\users\brandon\desktop\remoteinput\remoteinput\thirdparty\min_hook\src\buffer.h + +1585447356 source:c:\users\brandon\desktop\remoteinput\remoteinput\thirdparty\min_hook\src\hde\hde32.c + "hde32.h" + "table32.h" + +1585447356 c:\users\brandon\desktop\remoteinput\remoteinput\thirdparty\min_hook\src\hde\hde32.h + "pstdint.h" + +1585447356 c:\users\brandon\desktop\remoteinput\remoteinput\thirdparty\min_hook\src\hde\pstdint.h + + +1585447356 c:\users\brandon\desktop\remoteinput\remoteinput\thirdparty\min_hook\src\hde\table32.h + +1585447356 source:c:\users\brandon\desktop\remoteinput\remoteinput\thirdparty\min_hook\src\hde\hde64.c + "hde64.h" + "table64.h" + +1585447356 c:\users\brandon\desktop\remoteinput\remoteinput\thirdparty\min_hook\src\hde\hde64.h + "pstdint.h" + +1585447356 c:\users\brandon\desktop\remoteinput\remoteinput\thirdparty\min_hook\src\hde\table64.h + +1585447356 source:c:\users\brandon\desktop\remoteinput\remoteinput\thirdparty\min_hook\src\hook.c + + + + "../include/MinHook.h" + "buffer.h" + "trampoline.h" + +1585447356 c:\users\brandon\desktop\remoteinput\remoteinput\thirdparty\min_hook\include\minhook.h + + +1585447356 c:\users\brandon\desktop\remoteinput\remoteinput\thirdparty\min_hook\src\trampoline.h + +1585447356 source:c:\users\brandon\desktop\remoteinput\remoteinput\thirdparty\min_hook\src\trampoline.c + + "./hde/hde64.h" + "./hde/hde32.h" + "trampoline.h" + "buffer.h" + +1585447356 source:c:\users\brandon\desktop\remoteinput\remoteinput\detachedthreadpool.cxx + "DetachedThreadPool.hxx" + +1585447356 c:\users\brandon\desktop\remoteinput\remoteinput\detachedthreadpool.hxx + + + + + + + + + + +1585447356 c:\users\brandon\desktop\remoteinput\remoteinput\java\pointerinfo.hxx + + "Component.hxx" + +1585447356 source:c:\users\brandon\desktop\remoteinput\remoteinput\java\pointerinfo.cxx + "PointerInfo.hxx" + + +1586235951 c:\users\brandon\desktop\remoteinput\remoteinput\thirdparty\main.hxx + + + + +1586236616 source:c:\users\brandon\desktop\remoteinput\remoteinput\thirdparty\main.cxx + "main.hxx" + + + + + + + + + diff --git a/third-party/RemoteInput/RemoteInput.layout b/third-party/RemoteInput/RemoteInput.layout new file mode 100644 index 0000000..582a081 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput.layout @@ -0,0 +1,190 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/third-party/RemoteInput/RemoteInput.xcodeproj/project.pbxproj b/third-party/RemoteInput/RemoteInput.xcodeproj/project.pbxproj new file mode 100644 index 0000000..b964ba9 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput.xcodeproj/project.pbxproj @@ -0,0 +1,1178 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 5E07A6C02378B1B100EB4CDF /* Graphics.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5E07A6BE2378B1B100EB4CDF /* Graphics.cxx */; }; + 5E07A6C12378B1B100EB4CDF /* Graphics.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5E07A6BF2378B1B100EB4CDF /* Graphics.hxx */; }; + 5E0F52AC23AE8EAD002518AF /* Plugin.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5E0F52AA23AE8EAD002518AF /* Plugin.cxx */; }; + 5E0F52AD23AE8EAD002518AF /* Plugin.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5E0F52AA23AE8EAD002518AF /* Plugin.cxx */; }; + 5E0F52AE23AE8EAD002518AF /* Plugin.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5E0F52AB23AE8EAD002518AF /* Plugin.hxx */; }; + 5E0F52AF23AE8EAD002518AF /* Plugin.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5E0F52AB23AE8EAD002518AF /* Plugin.hxx */; }; + 5E1236A423946AA900D8AE33 /* ReflectionHook.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5E1236A223946AA900D8AE33 /* ReflectionHook.cxx */; }; + 5E1236A523946AA900D8AE33 /* ReflectionHook.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5E1236A323946AA900D8AE33 /* ReflectionHook.hxx */; }; + 5E27B24423B829C60091232F /* InputOutput.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5E27B24223B829C60091232F /* InputOutput.cxx */; }; + 5E27B24523B829C60091232F /* InputOutput.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5E27B24223B829C60091232F /* InputOutput.cxx */; }; + 5E27B24623B829C60091232F /* InputOutput.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5E27B24323B829C60091232F /* InputOutput.hxx */; }; + 5E27B24723B829C60091232F /* InputOutput.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5E27B24323B829C60091232F /* InputOutput.hxx */; }; + 5E27B24A23B9A0430091232F /* Random.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5E27B24823B9A0430091232F /* Random.cxx */; }; + 5E27B24B23B9A0430091232F /* Random.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5E27B24823B9A0430091232F /* Random.cxx */; }; + 5E27B24C23B9A0430091232F /* Random.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5E27B24923B9A0430091232F /* Random.hxx */; }; + 5E27B24D23B9A0430091232F /* Random.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5E27B24923B9A0430091232F /* Random.hxx */; }; + 5E31A8B323C83E3F00E751F6 /* MouseWheelEvent.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5E31A8B123C83E3F00E751F6 /* MouseWheelEvent.cxx */; }; + 5E31A8B423C83E3F00E751F6 /* MouseWheelEvent.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5E31A8B223C83E3F00E751F6 /* MouseWheelEvent.hxx */; }; + 5E31A8B523C840F500E751F6 /* MouseWheelEvent.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5E31A8B123C83E3F00E751F6 /* MouseWheelEvent.cxx */; }; + 5E31A8B623C840F500E751F6 /* MouseWheelEvent.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5E31A8B223C83E3F00E751F6 /* MouseWheelEvent.hxx */; }; + 5E4CDC1D2381F40700A05869 /* NativeHooks_Darwin.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5E4CDC1A2381F40700A05869 /* NativeHooks_Darwin.cxx */; }; + 5E4CDC1E2381F40700A05869 /* NativeHooks_Linux.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5E4CDC1B2381F40700A05869 /* NativeHooks_Linux.cxx */; }; + 5E4CDC1F2381F40700A05869 /* NativeHooks_Windows.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5E4CDC1C2381F40700A05869 /* NativeHooks_Windows.cxx */; }; + 5E4D644B241F424D0010EDBE /* PointerInfo.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5E4D6449241F424D0010EDBE /* PointerInfo.cxx */; }; + 5E4D644C241F424D0010EDBE /* PointerInfo.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5E4D644A241F424D0010EDBE /* PointerInfo.hxx */; }; + 5E4D644D241F47930010EDBE /* PointerInfo.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5E4D6449241F424D0010EDBE /* PointerInfo.cxx */; }; + 5E4D644E241F47990010EDBE /* PointerInfo.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5E4D644A241F424D0010EDBE /* PointerInfo.hxx */; }; + 5E5B3F8A239D36A400F07DD1 /* ModelRendering.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E5B3F88239D36A400F07DD1 /* ModelRendering.cpp */; }; + 5E5B3F8B239D36A400F07DD1 /* ModelRendering.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 5E5B3F89239D36A400F07DD1 /* ModelRendering.hpp */; }; + 5E5B3F8D239D39F700F07DD1 /* ModelRendering.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 5E5B3F89239D36A400F07DD1 /* ModelRendering.hpp */; }; + 5E5B3F8E239D39FA00F07DD1 /* ModelRendering.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E5B3F88239D36A400F07DD1 /* ModelRendering.cpp */; }; + 5E5FC02B237264840048997B /* ControlCenter.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5E5FC029237264840048997B /* ControlCenter.cxx */; }; + 5E5FC02C237264840048997B /* ControlCenter.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5E5FC02A237264840048997B /* ControlCenter.hxx */; }; + 5E6281F12421B79E006BA298 /* RemoteInputBootstrap_Darwin.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5E6281EF2421B79E006BA298 /* RemoteInputBootstrap_Darwin.cxx */; }; + 5E667F9A23C8D1BB00931DEC /* libawt_lwawt.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E667F9923C8D1BB00931DEC /* libawt_lwawt.dylib */; }; + 5E667F9E23C8D1C000931DEC /* libawt.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E667F9D23C8D1C000931DEC /* libawt.dylib */; }; + 5E6B9A002394C57600E6720C /* Time.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBD66B1235CF15400D183B1 /* Time.cxx */; }; + 5E6B9A012394C57600E6720C /* SharedEvent.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBD66B2235CF15400D183B1 /* SharedEvent.cxx */; }; + 5E6B9A022394C57600E6720C /* MemoryMap.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBD66B3235CF15400D183B1 /* MemoryMap.hxx */; }; + 5E6B9A032394C57600E6720C /* Module.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBD66B4235CF15400D183B1 /* Module.hxx */; }; + 5E6B9A042394C57600E6720C /* Module.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBD66B5235CF15400D183B1 /* Module.cxx */; }; + 5E6B9A052394C57600E6720C /* Time.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBD66B6235CF15400D183B1 /* Time.hxx */; }; + 5E6B9A062394C57600E6720C /* SharedEvent.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBD66B7235CF15400D183B1 /* SharedEvent.hxx */; }; + 5E6B9A072394C57600E6720C /* EIOS.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBD66AD235CF15400D183B1 /* EIOS.cxx */; }; + 5E6B9A082394C57600E6720C /* EIOS.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBD66A5235CF15400D183B1 /* EIOS.hxx */; }; + 5E6B9A092394C57600E6720C /* JVM.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBD66AF235CF15400D183B1 /* JVM.cxx */; }; + 5E6B9A0A2394C57600E6720C /* JVM.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBD66AB235CF15400D183B1 /* JVM.hxx */; }; + 5E6B9A0B2394C57A00E6720C /* NativeHooks_Darwin.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5E4CDC1A2381F40700A05869 /* NativeHooks_Darwin.cxx */; }; + 5E6B9A0C2394C57A00E6720C /* NativeHooks_Linux.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5E4CDC1B2381F40700A05869 /* NativeHooks_Linux.cxx */; }; + 5E6B9A0D2394C57A00E6720C /* NativeHooks_Windows.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5E4CDC1C2381F40700A05869 /* NativeHooks_Windows.cxx */; }; + 5E6B9A0E2394C57A00E6720C /* DebugConsole.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBD6697235CF15400D183B1 /* DebugConsole.hxx */; }; + 5E6B9A0F2394C57A00E6720C /* Platform_Linux.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBD6698235CF15400D183B1 /* Platform_Linux.cxx */; }; + 5E6B9A102394C57A00E6720C /* Platform.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBD669A235CF15400D183B1 /* Platform.hxx */; }; + 5E6B9A112394C57A00E6720C /* NativeHooks.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBD669B235CF15400D183B1 /* NativeHooks.hxx */; }; + 5E6B9A132394C57A00E6720C /* DebugConsole.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBD669F235CF15400D183B1 /* DebugConsole.cxx */; }; + 5E6B9A142394C57A00E6720C /* JavaInternal.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBD66A0235CF15400D183B1 /* JavaInternal.hxx */; }; + 5E6B9A152394C57A00E6720C /* Platform_Windows.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBD66A1235CF15400D183B1 /* Platform_Windows.cxx */; }; + 5E6B9A162394C58500E6720C /* SimbaPlugin.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBD66A8235CF15400D183B1 /* SimbaPlugin.cxx */; }; + 5E6B9A172394C58500E6720C /* SimbaPlugin.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBD66AA235CF15400D183B1 /* SimbaPlugin.hxx */; }; + 5E6B9A182394C58500E6720C /* TMemoryManager.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBD66A9235CF15400D183B1 /* TMemoryManager.hxx */; }; + 5E6B9A192394C58500E6720C /* ControlCenter.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5E5FC029237264840048997B /* ControlCenter.cxx */; }; + 5E6B9A1A2394C58500E6720C /* ControlCenter.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5E5FC02A237264840048997B /* ControlCenter.hxx */; }; + 5E6B9A1B2394C58500E6720C /* Graphics.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5E07A6BE2378B1B100EB4CDF /* Graphics.cxx */; }; + 5E6B9A1C2394C58500E6720C /* Graphics.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5E07A6BF2378B1B100EB4CDF /* Graphics.hxx */; }; + 5E6B9A1D2394C59400E6720C /* Reflection.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBD66A4235CF15400D183B1 /* Reflection.cxx */; }; + 5E6B9A1E2394C59400E6720C /* Reflection.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBD66A2235CF15400D183B1 /* Reflection.hxx */; }; + 5E6B9A1F2394C59700E6720C /* ThreadPool.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBD66AE235CF15400D183B1 /* ThreadPool.cxx */; }; + 5E6B9A202394C59700E6720C /* ThreadPool.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBD66B8235CF15400D183B1 /* ThreadPool.hxx */; }; + 5E6B9A212394C59700E6720C /* RemoteInput.h in Headers */ = {isa = PBXBuildFile; fileRef = 5EC5950A232D956B00922111 /* RemoteInput.h */; }; + 5E6B9A242394C59C00E6720C /* ReflectionHook.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5E1236A223946AA900D8AE33 /* ReflectionHook.cxx */; }; + 5E6B9A252394C59C00E6720C /* ReflectionHook.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5E1236A323946AA900D8AE33 /* ReflectionHook.hxx */; }; + 5E6B9A262394C60A00E6720C /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5EBD66DC235D041D00D183B1 /* Foundation.framework */; }; + 5E6B9A272394C61200E6720C /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5EBD66DA235D041600D183B1 /* Cocoa.framework */; }; + 5E6EA10123C91350003CB0B5 /* DetachedThreadPool.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5E6EA0FF23C91350003CB0B5 /* DetachedThreadPool.cxx */; }; + 5E6EA10223C91350003CB0B5 /* DetachedThreadPool.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5E6EA0FF23C91350003CB0B5 /* DetachedThreadPool.cxx */; }; + 5E6EA10323C91350003CB0B5 /* DetachedThreadPool.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5E6EA10023C91350003CB0B5 /* DetachedThreadPool.hxx */; }; + 5E6EA10423C91350003CB0B5 /* DetachedThreadPool.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5E6EA10023C91350003CB0B5 /* DetachedThreadPool.hxx */; }; + 5E6EA10A23C91371003CB0B5 /* rd_route.h in Headers */ = {isa = PBXBuildFile; fileRef = 5E6EA10723C91371003CB0B5 /* rd_route.h */; }; + 5E6EA10B23C91371003CB0B5 /* rd_route.h in Headers */ = {isa = PBXBuildFile; fileRef = 5E6EA10723C91371003CB0B5 /* rd_route.h */; }; + 5E6EA10C23C91371003CB0B5 /* rd_route.c in Sources */ = {isa = PBXBuildFile; fileRef = 5E6EA10923C91371003CB0B5 /* rd_route.c */; }; + 5E6EA10D23C91371003CB0B5 /* rd_route.c in Sources */ = {isa = PBXBuildFile; fileRef = 5E6EA10923C91371003CB0B5 /* rd_route.c */; }; + 5EB76181239B922500218452 /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5EB76180239B922500218452 /* OpenGL.framework */; }; + 5EBD66B9235CF15400D183B1 /* DebugConsole.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBD6697235CF15400D183B1 /* DebugConsole.hxx */; }; + 5EBD66BA235CF15400D183B1 /* Platform_Linux.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBD6698235CF15400D183B1 /* Platform_Linux.cxx */; }; + 5EBD66BC235CF15400D183B1 /* Platform.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBD669A235CF15400D183B1 /* Platform.hxx */; }; + 5EBD66BD235CF15400D183B1 /* NativeHooks.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBD669B235CF15400D183B1 /* NativeHooks.hxx */; }; + 5EBD66C1235CF15400D183B1 /* DebugConsole.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBD669F235CF15400D183B1 /* DebugConsole.cxx */; }; + 5EBD66C2235CF15400D183B1 /* JavaInternal.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBD66A0235CF15400D183B1 /* JavaInternal.hxx */; }; + 5EBD66C3235CF15400D183B1 /* Platform_Windows.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBD66A1235CF15400D183B1 /* Platform_Windows.cxx */; }; + 5EBD66C4235CF15400D183B1 /* Reflection.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBD66A2235CF15400D183B1 /* Reflection.hxx */; }; + 5EBD66C6235CF15400D183B1 /* Reflection.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBD66A4235CF15400D183B1 /* Reflection.cxx */; }; + 5EBD66C7235CF15400D183B1 /* EIOS.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBD66A5235CF15400D183B1 /* EIOS.hxx */; }; + 5EBD66C8235CF15400D183B1 /* RemoteInput.def in Resources */ = {isa = PBXBuildFile; fileRef = 5EBD66A6235CF15400D183B1 /* RemoteInput.def */; }; + 5EBD66C9235CF15400D183B1 /* SimbaPlugin.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBD66A8235CF15400D183B1 /* SimbaPlugin.cxx */; }; + 5EBD66CA235CF15400D183B1 /* TMemoryManager.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBD66A9235CF15400D183B1 /* TMemoryManager.hxx */; }; + 5EBD66CB235CF15400D183B1 /* SimbaPlugin.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBD66AA235CF15400D183B1 /* SimbaPlugin.hxx */; }; + 5EBD66CC235CF15400D183B1 /* JVM.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBD66AB235CF15400D183B1 /* JVM.hxx */; }; + 5EBD66CE235CF15400D183B1 /* EIOS.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBD66AD235CF15400D183B1 /* EIOS.cxx */; }; + 5EBD66CF235CF15400D183B1 /* ThreadPool.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBD66AE235CF15400D183B1 /* ThreadPool.cxx */; }; + 5EBD66D0235CF15400D183B1 /* JVM.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBD66AF235CF15400D183B1 /* JVM.cxx */; }; + 5EBD66D1235CF15400D183B1 /* Time.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBD66B1235CF15400D183B1 /* Time.cxx */; }; + 5EBD66D2235CF15400D183B1 /* SharedEvent.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBD66B2235CF15400D183B1 /* SharedEvent.cxx */; }; + 5EBD66D3235CF15400D183B1 /* MemoryMap.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBD66B3235CF15400D183B1 /* MemoryMap.hxx */; }; + 5EBD66D4235CF15400D183B1 /* Module.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBD66B4235CF15400D183B1 /* Module.hxx */; }; + 5EBD66D5235CF15400D183B1 /* Module.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBD66B5235CF15400D183B1 /* Module.cxx */; }; + 5EBD66D6235CF15400D183B1 /* Time.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBD66B6235CF15400D183B1 /* Time.hxx */; }; + 5EBD66D7235CF15400D183B1 /* SharedEvent.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBD66B7235CF15400D183B1 /* SharedEvent.hxx */; }; + 5EBD66D8235CF15400D183B1 /* ThreadPool.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBD66B8235CF15400D183B1 /* ThreadPool.hxx */; }; + 5EBD66DB235D041600D183B1 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5EBD66DA235D041600D183B1 /* Cocoa.framework */; }; + 5EBD66DD235D041D00D183B1 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5EBD66DC235D041D00D183B1 /* Foundation.framework */; }; + 5EBE1A9923C7C84800B99E32 /* KeyEvent.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBE1A8123C7C84700B99E32 /* KeyEvent.hxx */; }; + 5EBE1A9A23C7C84800B99E32 /* KeyEvent.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBE1A8123C7C84700B99E32 /* KeyEvent.hxx */; }; + 5EBE1A9B23C7C84800B99E32 /* Applet.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBE1A8223C7C84700B99E32 /* Applet.cxx */; }; + 5EBE1A9C23C7C84800B99E32 /* Applet.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBE1A8223C7C84700B99E32 /* Applet.cxx */; }; + 5EBE1A9D23C7C84800B99E32 /* AWTEvent.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBE1A8323C7C84700B99E32 /* AWTEvent.cxx */; }; + 5EBE1A9E23C7C84800B99E32 /* AWTEvent.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBE1A8323C7C84700B99E32 /* AWTEvent.cxx */; }; + 5EBE1A9F23C7C84800B99E32 /* AWTEventAccessor.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBE1A8423C7C84700B99E32 /* AWTEventAccessor.cxx */; }; + 5EBE1AA023C7C84800B99E32 /* AWTEventAccessor.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBE1A8423C7C84700B99E32 /* AWTEventAccessor.cxx */; }; + 5EBE1AA123C7C84800B99E32 /* Component.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBE1A8523C7C84700B99E32 /* Component.cxx */; }; + 5EBE1AA223C7C84800B99E32 /* Component.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBE1A8523C7C84700B99E32 /* Component.cxx */; }; + 5EBE1AA323C7C84800B99E32 /* AWTAccessor.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBE1A8623C7C84700B99E32 /* AWTAccessor.cxx */; }; + 5EBE1AA423C7C84800B99E32 /* AWTAccessor.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBE1A8623C7C84700B99E32 /* AWTAccessor.cxx */; }; + 5EBE1AA523C7C84800B99E32 /* InputEvent.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBE1A8723C7C84700B99E32 /* InputEvent.cxx */; }; + 5EBE1AA623C7C84800B99E32 /* InputEvent.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBE1A8723C7C84700B99E32 /* InputEvent.cxx */; }; + 5EBE1AA723C7C84800B99E32 /* MouseEvent.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBE1A8823C7C84700B99E32 /* MouseEvent.cxx */; }; + 5EBE1AA823C7C84800B99E32 /* MouseEvent.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBE1A8823C7C84700B99E32 /* MouseEvent.cxx */; }; + 5EBE1AA923C7C84800B99E32 /* EventQueue.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBE1A8923C7C84700B99E32 /* EventQueue.cxx */; }; + 5EBE1AAA23C7C84800B99E32 /* EventQueue.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBE1A8923C7C84700B99E32 /* EventQueue.cxx */; }; + 5EBE1AAB23C7C84800B99E32 /* Toolkit.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBE1A8A23C7C84700B99E32 /* Toolkit.hxx */; }; + 5EBE1AAC23C7C84800B99E32 /* Toolkit.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBE1A8A23C7C84700B99E32 /* Toolkit.hxx */; }; + 5EBE1AAD23C7C84800B99E32 /* AWTAccessor.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBE1A8B23C7C84700B99E32 /* AWTAccessor.hxx */; }; + 5EBE1AAE23C7C84800B99E32 /* AWTAccessor.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBE1A8B23C7C84700B99E32 /* AWTAccessor.hxx */; }; + 5EBE1AAF23C7C84800B99E32 /* Toolkit.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBE1A8C23C7C84700B99E32 /* Toolkit.cxx */; }; + 5EBE1AB023C7C84800B99E32 /* Toolkit.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBE1A8C23C7C84700B99E32 /* Toolkit.cxx */; }; + 5EBE1AB123C7C84800B99E32 /* FocusEvent.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBE1A8D23C7C84700B99E32 /* FocusEvent.hxx */; }; + 5EBE1AB223C7C84800B99E32 /* FocusEvent.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBE1A8D23C7C84700B99E32 /* FocusEvent.hxx */; }; + 5EBE1AB323C7C84800B99E32 /* Applet.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBE1A8E23C7C84700B99E32 /* Applet.hxx */; }; + 5EBE1AB423C7C84800B99E32 /* Applet.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBE1A8E23C7C84700B99E32 /* Applet.hxx */; }; + 5EBE1AB523C7C84800B99E32 /* AWTEventAccessor.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBE1A8F23C7C84700B99E32 /* AWTEventAccessor.hxx */; }; + 5EBE1AB623C7C84800B99E32 /* AWTEventAccessor.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBE1A8F23C7C84700B99E32 /* AWTEventAccessor.hxx */; }; + 5EBE1AB723C7C84800B99E32 /* AWTEvent.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBE1A9023C7C84700B99E32 /* AWTEvent.hxx */; }; + 5EBE1AB823C7C84800B99E32 /* AWTEvent.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBE1A9023C7C84700B99E32 /* AWTEvent.hxx */; }; + 5EBE1AB923C7C84800B99E32 /* Component.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBE1A9123C7C84700B99E32 /* Component.hxx */; }; + 5EBE1ABA23C7C84800B99E32 /* Component.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBE1A9123C7C84700B99E32 /* Component.hxx */; }; + 5EBE1ABB23C7C84800B99E32 /* Frame.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBE1A9223C7C84800B99E32 /* Frame.hxx */; }; + 5EBE1ABC23C7C84800B99E32 /* Frame.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBE1A9223C7C84800B99E32 /* Frame.hxx */; }; + 5EBE1ABD23C7C84800B99E32 /* InputEvent.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBE1A9323C7C84800B99E32 /* InputEvent.hxx */; }; + 5EBE1ABE23C7C84800B99E32 /* InputEvent.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBE1A9323C7C84800B99E32 /* InputEvent.hxx */; }; + 5EBE1ABF23C7C84800B99E32 /* FocusEvent.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBE1A9423C7C84800B99E32 /* FocusEvent.cxx */; }; + 5EBE1AC023C7C84800B99E32 /* FocusEvent.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBE1A9423C7C84800B99E32 /* FocusEvent.cxx */; }; + 5EBE1AC123C7C84800B99E32 /* KeyEvent.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBE1A9523C7C84800B99E32 /* KeyEvent.cxx */; }; + 5EBE1AC223C7C84800B99E32 /* KeyEvent.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBE1A9523C7C84800B99E32 /* KeyEvent.cxx */; }; + 5EBE1AC323C7C84800B99E32 /* MouseEvent.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBE1A9623C7C84800B99E32 /* MouseEvent.hxx */; }; + 5EBE1AC423C7C84800B99E32 /* MouseEvent.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBE1A9623C7C84800B99E32 /* MouseEvent.hxx */; }; + 5EBE1AC523C7C84800B99E32 /* EventQueue.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBE1A9723C7C84800B99E32 /* EventQueue.hxx */; }; + 5EBE1AC623C7C84800B99E32 /* EventQueue.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EBE1A9723C7C84800B99E32 /* EventQueue.hxx */; }; + 5EBE1AC723C7C84800B99E32 /* Frame.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBE1A9823C7C84800B99E32 /* Frame.cxx */; }; + 5EBE1AC823C7C84800B99E32 /* Frame.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EBE1A9823C7C84800B99E32 /* Frame.cxx */; }; + 5EC5950C232D956B00922111 /* RemoteInput.h in Headers */ = {isa = PBXBuildFile; fileRef = 5EC5950A232D956B00922111 /* RemoteInput.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 5EC942EB23D4A81B00E42B21 /* Platform_Darwin.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5EC942EA23D4A81B00E42B21 /* Platform_Darwin.mm */; }; + 5EC942EC23D4A8AA00E42B21 /* Platform_Darwin.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5EC942EA23D4A81B00E42B21 /* Platform_Darwin.mm */; }; + 5EE7DD94243BD9E5008FDC5D /* mach_inject.h in Headers */ = {isa = PBXBuildFile; fileRef = 5EE7DD91243BD9E5008FDC5D /* mach_inject.h */; }; + 5EE7DD95243BD9E5008FDC5D /* mach_inject.h in Headers */ = {isa = PBXBuildFile; fileRef = 5EE7DD91243BD9E5008FDC5D /* mach_inject.h */; }; + 5EE7DD96243BD9E5008FDC5D /* mach_inject.c in Sources */ = {isa = PBXBuildFile; fileRef = 5EE7DD93243BD9E5008FDC5D /* mach_inject.c */; }; + 5EE7DD97243BD9E5008FDC5D /* mach_inject.c in Sources */ = {isa = PBXBuildFile; fileRef = 5EE7DD93243BD9E5008FDC5D /* mach_inject.c */; }; + 5EE7DD9A243BD9F7008FDC5D /* main.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EE7DD98243BD9F7008FDC5D /* main.hxx */; }; + 5EE7DD9B243BD9F7008FDC5D /* main.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EE7DD98243BD9F7008FDC5D /* main.hxx */; }; + 5EE7DD9C243BD9F7008FDC5D /* main.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EE7DD99243BD9F7008FDC5D /* main.cxx */; }; + 5EE7DD9D243BD9F7008FDC5D /* main.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EE7DD99243BD9F7008FDC5D /* main.cxx */; }; + 5EFF49BA23A7037C00DC83D1 /* SpinLock.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EFF49B223A6EF4100DC83D1 /* SpinLock.hxx */; }; + 5EFF49BC23A7039C00DC83D1 /* SpinLock.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EFF49B223A6EF4100DC83D1 /* SpinLock.hxx */; }; + 5EFF49BE23A703C700DC83D1 /* SpinLock.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EFF49BD23A703C700DC83D1 /* SpinLock.cxx */; }; + 5EFF49BF23A703C700DC83D1 /* SpinLock.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 5EFF49BD23A703C700DC83D1 /* SpinLock.cxx */; }; + 5EFF49C423A704DF00DC83D1 /* Signal.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EFF49C123A704DF00DC83D1 /* Signal.hxx */; }; + 5EFF49C523A704DF00DC83D1 /* Signal.hxx in Headers */ = {isa = PBXBuildFile; fileRef = 5EFF49C123A704DF00DC83D1 /* Signal.hxx */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 5E07A6BE2378B1B100EB4CDF /* Graphics.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Graphics.cxx; sourceTree = ""; }; + 5E07A6BF2378B1B100EB4CDF /* Graphics.hxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Graphics.hxx; sourceTree = ""; }; + 5E0F52AA23AE8EAD002518AF /* Plugin.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Plugin.cxx; sourceTree = ""; }; + 5E0F52AB23AE8EAD002518AF /* Plugin.hxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Plugin.hxx; sourceTree = ""; }; + 5E1236A223946AA900D8AE33 /* ReflectionHook.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ReflectionHook.cxx; sourceTree = ""; }; + 5E1236A323946AA900D8AE33 /* ReflectionHook.hxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ReflectionHook.hxx; sourceTree = ""; }; + 5E27B24223B829C60091232F /* InputOutput.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = InputOutput.cxx; sourceTree = ""; }; + 5E27B24323B829C60091232F /* InputOutput.hxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = InputOutput.hxx; sourceTree = ""; }; + 5E27B24823B9A0430091232F /* Random.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Random.cxx; sourceTree = ""; }; + 5E27B24923B9A0430091232F /* Random.hxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Random.hxx; sourceTree = ""; }; + 5E31A8B123C83E3F00E751F6 /* MouseWheelEvent.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MouseWheelEvent.cxx; sourceTree = ""; }; + 5E31A8B223C83E3F00E751F6 /* MouseWheelEvent.hxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MouseWheelEvent.hxx; sourceTree = ""; }; + 5E4CDC1A2381F40700A05869 /* NativeHooks_Darwin.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NativeHooks_Darwin.cxx; sourceTree = ""; }; + 5E4CDC1B2381F40700A05869 /* NativeHooks_Linux.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NativeHooks_Linux.cxx; sourceTree = ""; }; + 5E4CDC1C2381F40700A05869 /* NativeHooks_Windows.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NativeHooks_Windows.cxx; sourceTree = ""; }; + 5E4D6449241F424D0010EDBE /* PointerInfo.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = PointerInfo.cxx; sourceTree = ""; }; + 5E4D644A241F424D0010EDBE /* PointerInfo.hxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = PointerInfo.hxx; sourceTree = ""; }; + 5E5B3F88239D36A400F07DD1 /* ModelRendering.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ModelRendering.cpp; sourceTree = ""; }; + 5E5B3F89239D36A400F07DD1 /* ModelRendering.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ModelRendering.hpp; sourceTree = ""; }; + 5E5FC029237264840048997B /* ControlCenter.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ControlCenter.cxx; sourceTree = ""; }; + 5E5FC02A237264840048997B /* ControlCenter.hxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ControlCenter.hxx; sourceTree = ""; }; + 5E6281EB2421B776006BA298 /* libRemoteInputBootstrap.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libRemoteInputBootstrap.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; + 5E6281EF2421B79E006BA298 /* RemoteInputBootstrap_Darwin.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = RemoteInputBootstrap_Darwin.cxx; sourceTree = ""; }; + 5E667F9923C8D1BB00931DEC /* libawt_lwawt.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libawt_lwawt.dylib; path = "../../../../Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/lib/libawt_lwawt.dylib"; sourceTree = ""; }; + 5E667F9D23C8D1C000931DEC /* libawt.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libawt.dylib; path = "../../../../Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/lib/libawt.dylib"; sourceTree = ""; }; + 5E667FA023C8D1D000931DEC /* libjawt.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libjawt.dylib; path = "../../../../Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/lib/libjawt.dylib"; sourceTree = ""; }; + 5E667FA323C8D1F500931DEC /* libjvm.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libjvm.dylib; path = "../../../../Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/lib/server/libjvm.dylib"; sourceTree = ""; }; + 5E6B99FC2394C4F200E6720C /* liblibRemoteInput.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = liblibRemoteInput.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; + 5E6EA0FF23C91350003CB0B5 /* DetachedThreadPool.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DetachedThreadPool.cxx; sourceTree = ""; }; + 5E6EA10023C91350003CB0B5 /* DetachedThreadPool.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = DetachedThreadPool.hxx; sourceTree = ""; }; + 5E6EA10723C91371003CB0B5 /* rd_route.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rd_route.h; sourceTree = ""; }; + 5E6EA10923C91371003CB0B5 /* rd_route.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rd_route.c; sourceTree = ""; }; + 5EB76180239B922500218452 /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = System/Library/Frameworks/OpenGL.framework; sourceTree = SDKROOT; }; + 5EBD6697235CF15400D183B1 /* DebugConsole.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = DebugConsole.hxx; sourceTree = ""; }; + 5EBD6698235CF15400D183B1 /* Platform_Linux.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Platform_Linux.cxx; sourceTree = ""; }; + 5EBD669A235CF15400D183B1 /* Platform.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Platform.hxx; sourceTree = ""; }; + 5EBD669B235CF15400D183B1 /* NativeHooks.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = NativeHooks.hxx; sourceTree = ""; }; + 5EBD669F235CF15400D183B1 /* DebugConsole.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DebugConsole.cxx; sourceTree = ""; }; + 5EBD66A0235CF15400D183B1 /* JavaInternal.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = JavaInternal.hxx; sourceTree = ""; }; + 5EBD66A1235CF15400D183B1 /* Platform_Windows.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Platform_Windows.cxx; sourceTree = ""; }; + 5EBD66A2235CF15400D183B1 /* Reflection.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Reflection.hxx; sourceTree = ""; }; + 5EBD66A4235CF15400D183B1 /* Reflection.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Reflection.cxx; sourceTree = ""; }; + 5EBD66A5235CF15400D183B1 /* EIOS.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = EIOS.hxx; sourceTree = ""; }; + 5EBD66A6235CF15400D183B1 /* RemoteInput.def */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = RemoteInput.def; sourceTree = ""; }; + 5EBD66A8235CF15400D183B1 /* SimbaPlugin.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SimbaPlugin.cxx; sourceTree = ""; }; + 5EBD66A9235CF15400D183B1 /* TMemoryManager.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TMemoryManager.hxx; sourceTree = ""; }; + 5EBD66AA235CF15400D183B1 /* SimbaPlugin.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SimbaPlugin.hxx; sourceTree = ""; }; + 5EBD66AB235CF15400D183B1 /* JVM.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = JVM.hxx; sourceTree = ""; }; + 5EBD66AD235CF15400D183B1 /* EIOS.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EIOS.cxx; sourceTree = ""; }; + 5EBD66AE235CF15400D183B1 /* ThreadPool.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ThreadPool.cxx; sourceTree = ""; }; + 5EBD66AF235CF15400D183B1 /* JVM.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JVM.cxx; sourceTree = ""; }; + 5EBD66B1235CF15400D183B1 /* Time.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Time.cxx; sourceTree = ""; }; + 5EBD66B2235CF15400D183B1 /* SharedEvent.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SharedEvent.cxx; sourceTree = ""; }; + 5EBD66B3235CF15400D183B1 /* MemoryMap.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MemoryMap.hxx; sourceTree = ""; }; + 5EBD66B4235CF15400D183B1 /* Module.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Module.hxx; sourceTree = ""; }; + 5EBD66B5235CF15400D183B1 /* Module.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Module.cxx; sourceTree = ""; }; + 5EBD66B6235CF15400D183B1 /* Time.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Time.hxx; sourceTree = ""; }; + 5EBD66B7235CF15400D183B1 /* SharedEvent.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SharedEvent.hxx; sourceTree = ""; }; + 5EBD66B8235CF15400D183B1 /* ThreadPool.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ThreadPool.hxx; sourceTree = ""; }; + 5EBD66DA235D041600D183B1 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; + 5EBD66DC235D041D00D183B1 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 5EBE1A8123C7C84700B99E32 /* KeyEvent.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = KeyEvent.hxx; sourceTree = ""; }; + 5EBE1A8223C7C84700B99E32 /* Applet.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Applet.cxx; sourceTree = ""; }; + 5EBE1A8323C7C84700B99E32 /* AWTEvent.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AWTEvent.cxx; sourceTree = ""; }; + 5EBE1A8423C7C84700B99E32 /* AWTEventAccessor.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AWTEventAccessor.cxx; sourceTree = ""; }; + 5EBE1A8523C7C84700B99E32 /* Component.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Component.cxx; sourceTree = ""; }; + 5EBE1A8623C7C84700B99E32 /* AWTAccessor.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AWTAccessor.cxx; sourceTree = ""; }; + 5EBE1A8723C7C84700B99E32 /* InputEvent.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InputEvent.cxx; sourceTree = ""; }; + 5EBE1A8823C7C84700B99E32 /* MouseEvent.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MouseEvent.cxx; sourceTree = ""; }; + 5EBE1A8923C7C84700B99E32 /* EventQueue.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EventQueue.cxx; sourceTree = ""; }; + 5EBE1A8A23C7C84700B99E32 /* Toolkit.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Toolkit.hxx; sourceTree = ""; }; + 5EBE1A8B23C7C84700B99E32 /* AWTAccessor.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = AWTAccessor.hxx; sourceTree = ""; }; + 5EBE1A8C23C7C84700B99E32 /* Toolkit.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Toolkit.cxx; sourceTree = ""; }; + 5EBE1A8D23C7C84700B99E32 /* FocusEvent.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FocusEvent.hxx; sourceTree = ""; }; + 5EBE1A8E23C7C84700B99E32 /* Applet.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Applet.hxx; sourceTree = ""; }; + 5EBE1A8F23C7C84700B99E32 /* AWTEventAccessor.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = AWTEventAccessor.hxx; sourceTree = ""; }; + 5EBE1A9023C7C84700B99E32 /* AWTEvent.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = AWTEvent.hxx; sourceTree = ""; }; + 5EBE1A9123C7C84700B99E32 /* Component.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Component.hxx; sourceTree = ""; }; + 5EBE1A9223C7C84800B99E32 /* Frame.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Frame.hxx; sourceTree = ""; }; + 5EBE1A9323C7C84800B99E32 /* InputEvent.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = InputEvent.hxx; sourceTree = ""; }; + 5EBE1A9423C7C84800B99E32 /* FocusEvent.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FocusEvent.cxx; sourceTree = ""; }; + 5EBE1A9523C7C84800B99E32 /* KeyEvent.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = KeyEvent.cxx; sourceTree = ""; }; + 5EBE1A9623C7C84800B99E32 /* MouseEvent.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MouseEvent.hxx; sourceTree = ""; }; + 5EBE1A9723C7C84800B99E32 /* EventQueue.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = EventQueue.hxx; sourceTree = ""; }; + 5EBE1A9823C7C84800B99E32 /* Frame.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Frame.cxx; sourceTree = ""; }; + 5EC59507232D956B00922111 /* RemoteInput.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RemoteInput.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 5EC5950A232D956B00922111 /* RemoteInput.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RemoteInput.h; sourceTree = ""; }; + 5EC5950B232D956B00922111 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 5EC942EA23D4A81B00E42B21 /* Platform_Darwin.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Platform_Darwin.mm; sourceTree = ""; }; + 5EE7DD91243BD9E5008FDC5D /* mach_inject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mach_inject.h; sourceTree = ""; }; + 5EE7DD93243BD9E5008FDC5D /* mach_inject.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mach_inject.c; sourceTree = ""; }; + 5EE7DD98243BD9F7008FDC5D /* main.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = main.hxx; sourceTree = ""; }; + 5EE7DD99243BD9F7008FDC5D /* main.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = main.cxx; sourceTree = ""; }; + 5EFF49B223A6EF4100DC83D1 /* SpinLock.hxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = SpinLock.hxx; sourceTree = ""; }; + 5EFF49BD23A703C700DC83D1 /* SpinLock.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SpinLock.cxx; sourceTree = ""; }; + 5EFF49C123A704DF00DC83D1 /* Signal.hxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Signal.hxx; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 5E6281E92421B776006BA298 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 5E6B99FA2394C4F200E6720C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 5E6B9A272394C61200E6720C /* Cocoa.framework in Frameworks */, + 5E667F9A23C8D1BB00931DEC /* libawt_lwawt.dylib in Frameworks */, + 5E667F9E23C8D1C000931DEC /* libawt.dylib in Frameworks */, + 5E6B9A262394C60A00E6720C /* Foundation.framework in Frameworks */, + 5EB76181239B922500218452 /* OpenGL.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 5EC59504232D956B00922111 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 5EBD66DB235D041600D183B1 /* Cocoa.framework in Frameworks */, + 5EBD66DD235D041D00D183B1 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 5E5B3F83239D367000F07DD1 /* Hooks */ = { + isa = PBXGroup; + children = ( + 5E5B3F88239D36A400F07DD1 /* ModelRendering.cpp */, + 5E5B3F89239D36A400F07DD1 /* ModelRendering.hpp */, + ); + path = Hooks; + sourceTree = ""; + }; + 5E667F9223C8C60000931DEC /* Thirdparty */ = { + isa = PBXGroup; + children = ( + 5EE7DD99243BD9F7008FDC5D /* main.cxx */, + 5EE7DD98243BD9F7008FDC5D /* main.hxx */, + 5EE7DD8F243BD9E5008FDC5D /* mach_inject */, + 5E6EA10523C91371003CB0B5 /* rd_route */, + ); + path = Thirdparty; + sourceTree = ""; + }; + 5E6EA10523C91371003CB0B5 /* rd_route */ = { + isa = PBXGroup; + children = ( + 5E6EA10623C91371003CB0B5 /* include */, + 5E6EA10823C91371003CB0B5 /* src */, + ); + path = rd_route; + sourceTree = ""; + }; + 5E6EA10623C91371003CB0B5 /* include */ = { + isa = PBXGroup; + children = ( + 5E6EA10723C91371003CB0B5 /* rd_route.h */, + ); + path = include; + sourceTree = ""; + }; + 5E6EA10823C91371003CB0B5 /* src */ = { + isa = PBXGroup; + children = ( + 5E6EA10923C91371003CB0B5 /* rd_route.c */, + ); + path = src; + sourceTree = ""; + }; + 5EBD6696235CF15400D183B1 /* Platform */ = { + isa = PBXGroup; + children = ( + 5E4CDC1A2381F40700A05869 /* NativeHooks_Darwin.cxx */, + 5E4CDC1B2381F40700A05869 /* NativeHooks_Linux.cxx */, + 5E4CDC1C2381F40700A05869 /* NativeHooks_Windows.cxx */, + 5EBD6697235CF15400D183B1 /* DebugConsole.hxx */, + 5EBD6698235CF15400D183B1 /* Platform_Linux.cxx */, + 5EC942EA23D4A81B00E42B21 /* Platform_Darwin.mm */, + 5EBD66A1235CF15400D183B1 /* Platform_Windows.cxx */, + 5EBD669A235CF15400D183B1 /* Platform.hxx */, + 5EBD669B235CF15400D183B1 /* NativeHooks.hxx */, + 5EBD669F235CF15400D183B1 /* DebugConsole.cxx */, + 5EBD66A0235CF15400D183B1 /* JavaInternal.hxx */, + 5E6281EF2421B79E006BA298 /* RemoteInputBootstrap_Darwin.cxx */, + ); + path = Platform; + sourceTree = ""; + }; + 5EBD66A7235CF15400D183B1 /* Plugin */ = { + isa = PBXGroup; + children = ( + 5E0F52AA23AE8EAD002518AF /* Plugin.cxx */, + 5E0F52AB23AE8EAD002518AF /* Plugin.hxx */, + 5EBD66A8235CF15400D183B1 /* SimbaPlugin.cxx */, + 5EBD66AA235CF15400D183B1 /* SimbaPlugin.hxx */, + 5EBD66A9235CF15400D183B1 /* TMemoryManager.hxx */, + 5E5FC029237264840048997B /* ControlCenter.cxx */, + 5E5FC02A237264840048997B /* ControlCenter.hxx */, + 5E07A6BE2378B1B100EB4CDF /* Graphics.cxx */, + 5E07A6BF2378B1B100EB4CDF /* Graphics.hxx */, + 5EFF49C123A704DF00DC83D1 /* Signal.hxx */, + 5E27B24223B829C60091232F /* InputOutput.cxx */, + 5E27B24323B829C60091232F /* InputOutput.hxx */, + ); + path = Plugin; + sourceTree = ""; + }; + 5EBD66B0235CF15400D183B1 /* Echo */ = { + isa = PBXGroup; + children = ( + 5EBD66B1235CF15400D183B1 /* Time.cxx */, + 5EBD66B2235CF15400D183B1 /* SharedEvent.cxx */, + 5EBD66B7235CF15400D183B1 /* SharedEvent.hxx */, + 5EBD66B3235CF15400D183B1 /* MemoryMap.hxx */, + 5EBD66B4235CF15400D183B1 /* Module.hxx */, + 5EBD66B5235CF15400D183B1 /* Module.cxx */, + 5EBD66B6235CF15400D183B1 /* Time.hxx */, + 5EFF49BD23A703C700DC83D1 /* SpinLock.cxx */, + 5EFF49B223A6EF4100DC83D1 /* SpinLock.hxx */, + ); + path = Echo; + sourceTree = ""; + }; + 5EBD66D9235D041600D183B1 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 5E667FA323C8D1F500931DEC /* libjvm.dylib */, + 5E667FA023C8D1D000931DEC /* libjawt.dylib */, + 5E667F9D23C8D1C000931DEC /* libawt.dylib */, + 5E667F9923C8D1BB00931DEC /* libawt_lwawt.dylib */, + 5EB76180239B922500218452 /* OpenGL.framework */, + 5EBD66DC235D041D00D183B1 /* Foundation.framework */, + 5EBD66DA235D041600D183B1 /* Cocoa.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 5EBE1A5323C7AE2000B99E32 /* Java */ = { + isa = PBXGroup; + children = ( + 5EBE1A8223C7C84700B99E32 /* Applet.cxx */, + 5EBE1A8E23C7C84700B99E32 /* Applet.hxx */, + 5EBE1A8623C7C84700B99E32 /* AWTAccessor.cxx */, + 5EBE1A8B23C7C84700B99E32 /* AWTAccessor.hxx */, + 5EBE1A8323C7C84700B99E32 /* AWTEvent.cxx */, + 5EBE1A9023C7C84700B99E32 /* AWTEvent.hxx */, + 5EBE1A8423C7C84700B99E32 /* AWTEventAccessor.cxx */, + 5EBE1A8F23C7C84700B99E32 /* AWTEventAccessor.hxx */, + 5EBE1A8523C7C84700B99E32 /* Component.cxx */, + 5EBE1A9123C7C84700B99E32 /* Component.hxx */, + 5EBE1A8923C7C84700B99E32 /* EventQueue.cxx */, + 5EBE1A9723C7C84800B99E32 /* EventQueue.hxx */, + 5EBE1A9423C7C84800B99E32 /* FocusEvent.cxx */, + 5EBE1A8D23C7C84700B99E32 /* FocusEvent.hxx */, + 5EBE1A9823C7C84800B99E32 /* Frame.cxx */, + 5EBE1A9223C7C84800B99E32 /* Frame.hxx */, + 5EBE1A8723C7C84700B99E32 /* InputEvent.cxx */, + 5EBE1A9323C7C84800B99E32 /* InputEvent.hxx */, + 5EBE1A9523C7C84800B99E32 /* KeyEvent.cxx */, + 5EBE1A8123C7C84700B99E32 /* KeyEvent.hxx */, + 5EBE1A8823C7C84700B99E32 /* MouseEvent.cxx */, + 5EBE1A9623C7C84800B99E32 /* MouseEvent.hxx */, + 5E31A8B123C83E3F00E751F6 /* MouseWheelEvent.cxx */, + 5E31A8B223C83E3F00E751F6 /* MouseWheelEvent.hxx */, + 5EBE1A8C23C7C84700B99E32 /* Toolkit.cxx */, + 5EBE1A8A23C7C84700B99E32 /* Toolkit.hxx */, + 5E4D6449241F424D0010EDBE /* PointerInfo.cxx */, + 5E4D644A241F424D0010EDBE /* PointerInfo.hxx */, + ); + path = Java; + sourceTree = ""; + }; + 5EC594FD232D956B00922111 = { + isa = PBXGroup; + children = ( + 5EC59509232D956B00922111 /* RemoteInput */, + 5EC59508232D956B00922111 /* Products */, + 5EBD66D9235D041600D183B1 /* Frameworks */, + ); + sourceTree = ""; + }; + 5EC59508232D956B00922111 /* Products */ = { + isa = PBXGroup; + children = ( + 5EC59507232D956B00922111 /* RemoteInput.framework */, + 5E6B99FC2394C4F200E6720C /* liblibRemoteInput.dylib */, + 5E6281EB2421B776006BA298 /* libRemoteInputBootstrap.dylib */, + ); + name = Products; + sourceTree = ""; + }; + 5EC59509232D956B00922111 /* RemoteInput */ = { + isa = PBXGroup; + children = ( + 5E667F9223C8C60000931DEC /* Thirdparty */, + 5EBE1A5323C7AE2000B99E32 /* Java */, + 5E5B3F83239D367000F07DD1 /* Hooks */, + 5EBD66B0235CF15400D183B1 /* Echo */, + 5EBD66AD235CF15400D183B1 /* EIOS.cxx */, + 5EBD66A5235CF15400D183B1 /* EIOS.hxx */, + 5EBD66AF235CF15400D183B1 /* JVM.cxx */, + 5EBD66AB235CF15400D183B1 /* JVM.hxx */, + 5EBD6696235CF15400D183B1 /* Platform */, + 5EBD66A7235CF15400D183B1 /* Plugin */, + 5EBD66A4235CF15400D183B1 /* Reflection.cxx */, + 5EBD66A2235CF15400D183B1 /* Reflection.hxx */, + 5EBD66A6235CF15400D183B1 /* RemoteInput.def */, + 5EBD66AE235CF15400D183B1 /* ThreadPool.cxx */, + 5EBD66B8235CF15400D183B1 /* ThreadPool.hxx */, + 5E6EA0FF23C91350003CB0B5 /* DetachedThreadPool.cxx */, + 5E6EA10023C91350003CB0B5 /* DetachedThreadPool.hxx */, + 5EC5950A232D956B00922111 /* RemoteInput.h */, + 5EC5950B232D956B00922111 /* Info.plist */, + 5E1236A223946AA900D8AE33 /* ReflectionHook.cxx */, + 5E1236A323946AA900D8AE33 /* ReflectionHook.hxx */, + 5E27B24823B9A0430091232F /* Random.cxx */, + 5E27B24923B9A0430091232F /* Random.hxx */, + ); + path = RemoteInput; + sourceTree = ""; + }; + 5EE7DD8F243BD9E5008FDC5D /* mach_inject */ = { + isa = PBXGroup; + children = ( + 5EE7DD90243BD9E5008FDC5D /* include */, + 5EE7DD92243BD9E5008FDC5D /* src */, + ); + path = mach_inject; + sourceTree = ""; + }; + 5EE7DD90243BD9E5008FDC5D /* include */ = { + isa = PBXGroup; + children = ( + 5EE7DD91243BD9E5008FDC5D /* mach_inject.h */, + ); + path = include; + sourceTree = ""; + }; + 5EE7DD92243BD9E5008FDC5D /* src */ = { + isa = PBXGroup; + children = ( + 5EE7DD93243BD9E5008FDC5D /* mach_inject.c */, + ); + path = src; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 5E6281E72421B776006BA298 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 5E6B99F82394C4F200E6720C /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 5E6EA10423C91350003CB0B5 /* DetachedThreadPool.hxx in Headers */, + 5EBE1AB823C7C84800B99E32 /* AWTEvent.hxx in Headers */, + 5EBE1AB223C7C84800B99E32 /* FocusEvent.hxx in Headers */, + 5EBE1AAE23C7C84800B99E32 /* AWTAccessor.hxx in Headers */, + 5EBE1AB423C7C84800B99E32 /* Applet.hxx in Headers */, + 5E4D644E241F47990010EDBE /* PointerInfo.hxx in Headers */, + 5EE7DD95243BD9E5008FDC5D /* mach_inject.h in Headers */, + 5EBE1ABA23C7C84800B99E32 /* Component.hxx in Headers */, + 5E6B9A0A2394C57600E6720C /* JVM.hxx in Headers */, + 5E6B9A1C2394C58500E6720C /* Graphics.hxx in Headers */, + 5E5B3F8D239D39F700F07DD1 /* ModelRendering.hpp in Headers */, + 5EBE1AC623C7C84800B99E32 /* EventQueue.hxx in Headers */, + 5EBE1AAC23C7C84800B99E32 /* Toolkit.hxx in Headers */, + 5E27B24723B829C60091232F /* InputOutput.hxx in Headers */, + 5EFF49BC23A7039C00DC83D1 /* SpinLock.hxx in Headers */, + 5E6B9A032394C57600E6720C /* Module.hxx in Headers */, + 5EBE1ABE23C7C84800B99E32 /* InputEvent.hxx in Headers */, + 5E6B9A052394C57600E6720C /* Time.hxx in Headers */, + 5E6B9A142394C57A00E6720C /* JavaInternal.hxx in Headers */, + 5EE7DD9B243BD9F7008FDC5D /* main.hxx in Headers */, + 5E0F52AF23AE8EAD002518AF /* Plugin.hxx in Headers */, + 5EBE1AB623C7C84800B99E32 /* AWTEventAccessor.hxx in Headers */, + 5EBE1A9A23C7C84800B99E32 /* KeyEvent.hxx in Headers */, + 5E6B9A182394C58500E6720C /* TMemoryManager.hxx in Headers */, + 5E6B9A202394C59700E6720C /* ThreadPool.hxx in Headers */, + 5E6B9A082394C57600E6720C /* EIOS.hxx in Headers */, + 5E27B24D23B9A0430091232F /* Random.hxx in Headers */, + 5EFF49C523A704DF00DC83D1 /* Signal.hxx in Headers */, + 5EBE1AC423C7C84800B99E32 /* MouseEvent.hxx in Headers */, + 5E6B9A062394C57600E6720C /* SharedEvent.hxx in Headers */, + 5E6B9A1A2394C58500E6720C /* ControlCenter.hxx in Headers */, + 5E6B9A172394C58500E6720C /* SimbaPlugin.hxx in Headers */, + 5E6B9A252394C59C00E6720C /* ReflectionHook.hxx in Headers */, + 5EBE1ABC23C7C84800B99E32 /* Frame.hxx in Headers */, + 5E6B9A022394C57600E6720C /* MemoryMap.hxx in Headers */, + 5E6B9A0E2394C57A00E6720C /* DebugConsole.hxx in Headers */, + 5E6B9A112394C57A00E6720C /* NativeHooks.hxx in Headers */, + 5E6B9A102394C57A00E6720C /* Platform.hxx in Headers */, + 5E6B9A212394C59700E6720C /* RemoteInput.h in Headers */, + 5E6EA10B23C91371003CB0B5 /* rd_route.h in Headers */, + 5E31A8B623C840F500E751F6 /* MouseWheelEvent.hxx in Headers */, + 5E6B9A1E2394C59400E6720C /* Reflection.hxx in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 5EC59502232D956B00922111 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 5E6EA10323C91350003CB0B5 /* DetachedThreadPool.hxx in Headers */, + 5EBE1AB723C7C84800B99E32 /* AWTEvent.hxx in Headers */, + 5EBE1AB123C7C84800B99E32 /* FocusEvent.hxx in Headers */, + 5EBE1AAD23C7C84800B99E32 /* AWTAccessor.hxx in Headers */, + 5EBE1AB323C7C84800B99E32 /* Applet.hxx in Headers */, + 5EBE1AB923C7C84800B99E32 /* Component.hxx in Headers */, + 5EE7DD94243BD9E5008FDC5D /* mach_inject.h in Headers */, + 5EBD66C4235CF15400D183B1 /* Reflection.hxx in Headers */, + 5EBD66C7235CF15400D183B1 /* EIOS.hxx in Headers */, + 5E5B3F8B239D36A400F07DD1 /* ModelRendering.hpp in Headers */, + 5EBE1AC523C7C84800B99E32 /* EventQueue.hxx in Headers */, + 5EBE1AAB23C7C84800B99E32 /* Toolkit.hxx in Headers */, + 5E27B24623B829C60091232F /* InputOutput.hxx in Headers */, + 5EFF49BA23A7037C00DC83D1 /* SpinLock.hxx in Headers */, + 5EBD66D8235CF15400D183B1 /* ThreadPool.hxx in Headers */, + 5EBE1ABD23C7C84800B99E32 /* InputEvent.hxx in Headers */, + 5E07A6C12378B1B100EB4CDF /* Graphics.hxx in Headers */, + 5EBD66BD235CF15400D183B1 /* NativeHooks.hxx in Headers */, + 5E0F52AE23AE8EAD002518AF /* Plugin.hxx in Headers */, + 5EE7DD9A243BD9F7008FDC5D /* main.hxx in Headers */, + 5EBE1AB523C7C84800B99E32 /* AWTEventAccessor.hxx in Headers */, + 5EBE1A9923C7C84800B99E32 /* KeyEvent.hxx in Headers */, + 5EBD66C2235CF15400D183B1 /* JavaInternal.hxx in Headers */, + 5E1236A523946AA900D8AE33 /* ReflectionHook.hxx in Headers */, + 5EBD66D3235CF15400D183B1 /* MemoryMap.hxx in Headers */, + 5E27B24C23B9A0430091232F /* Random.hxx in Headers */, + 5EFF49C423A704DF00DC83D1 /* Signal.hxx in Headers */, + 5EBE1AC323C7C84800B99E32 /* MouseEvent.hxx in Headers */, + 5EBD66CA235CF15400D183B1 /* TMemoryManager.hxx in Headers */, + 5EBD66CC235CF15400D183B1 /* JVM.hxx in Headers */, + 5EBD66CB235CF15400D183B1 /* SimbaPlugin.hxx in Headers */, + 5E4D644C241F424D0010EDBE /* PointerInfo.hxx in Headers */, + 5EBD66B9235CF15400D183B1 /* DebugConsole.hxx in Headers */, + 5EBE1ABB23C7C84800B99E32 /* Frame.hxx in Headers */, + 5EBD66D4235CF15400D183B1 /* Module.hxx in Headers */, + 5EC5950C232D956B00922111 /* RemoteInput.h in Headers */, + 5EBD66D6235CF15400D183B1 /* Time.hxx in Headers */, + 5E5FC02C237264840048997B /* ControlCenter.hxx in Headers */, + 5EBD66BC235CF15400D183B1 /* Platform.hxx in Headers */, + 5E6EA10A23C91371003CB0B5 /* rd_route.h in Headers */, + 5EBD66D7235CF15400D183B1 /* SharedEvent.hxx in Headers */, + 5E31A8B423C83E3F00E751F6 /* MouseWheelEvent.hxx in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 5E6281EA2421B776006BA298 /* RemoteInputBootstrap */ = { + isa = PBXNativeTarget; + buildConfigurationList = 5E6281EE2421B776006BA298 /* Build configuration list for PBXNativeTarget "RemoteInputBootstrap" */; + buildPhases = ( + 5E6281E72421B776006BA298 /* Headers */, + 5E6281E82421B776006BA298 /* Sources */, + 5E6281E92421B776006BA298 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = RemoteInputBootstrap; + productName = RemoteInputBootstrap; + productReference = 5E6281EB2421B776006BA298 /* libRemoteInputBootstrap.dylib */; + productType = "com.apple.product-type.library.dynamic"; + }; + 5E6B99FB2394C4F200E6720C /* libRemoteInput */ = { + isa = PBXNativeTarget; + buildConfigurationList = 5E6B99FD2394C4F200E6720C /* Build configuration list for PBXNativeTarget "libRemoteInput" */; + buildPhases = ( + 5E6B99F82394C4F200E6720C /* Headers */, + 5E6B99F92394C4F200E6720C /* Sources */, + 5E6B99FA2394C4F200E6720C /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = libRemoteInput; + productName = libRemoteInput; + productReference = 5E6B99FC2394C4F200E6720C /* liblibRemoteInput.dylib */; + productType = "com.apple.product-type.library.dynamic"; + }; + 5EC59506232D956B00922111 /* RemoteInput */ = { + isa = PBXNativeTarget; + buildConfigurationList = 5EC5950F232D956B00922111 /* Build configuration list for PBXNativeTarget "RemoteInput" */; + buildPhases = ( + 5EC59502232D956B00922111 /* Headers */, + 5EC59503232D956B00922111 /* Sources */, + 5EC59504232D956B00922111 /* Frameworks */, + 5EC59505232D956B00922111 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = RemoteInput; + productName = RemoteInput; + productReference = 5EC59507232D956B00922111 /* RemoteInput.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 5EC594FE232D956B00922111 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1100; + ORGANIZATIONNAME = XIO; + TargetAttributes = { + 5E6281EA2421B776006BA298 = { + CreatedOnToolsVersion = 11.3.1; + }; + 5E6B99FB2394C4F200E6720C = { + CreatedOnToolsVersion = 11.2.1; + }; + 5EC59506232D956B00922111 = { + CreatedOnToolsVersion = 11.0; + }; + }; + }; + buildConfigurationList = 5EC59501232D956B00922111 /* Build configuration list for PBXProject "RemoteInput" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 5EC594FD232D956B00922111; + productRefGroup = 5EC59508232D956B00922111 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 5EC59506232D956B00922111 /* RemoteInput */, + 5E6B99FB2394C4F200E6720C /* libRemoteInput */, + 5E6281EA2421B776006BA298 /* RemoteInputBootstrap */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 5EC59505232D956B00922111 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 5EBD66C8235CF15400D183B1 /* RemoteInput.def in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 5E6281E82421B776006BA298 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 5E6281F12421B79E006BA298 /* RemoteInputBootstrap_Darwin.cxx in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 5E6B99F92394C4F200E6720C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 5E6B9A192394C58500E6720C /* ControlCenter.cxx in Sources */, + 5EBE1AA023C7C84800B99E32 /* AWTEventAccessor.cxx in Sources */, + 5EBE1A9E23C7C84800B99E32 /* AWTEvent.cxx in Sources */, + 5E6B9A1B2394C58500E6720C /* Graphics.cxx in Sources */, + 5E5B3F8E239D39FA00F07DD1 /* ModelRendering.cpp in Sources */, + 5E0F52AD23AE8EAD002518AF /* Plugin.cxx in Sources */, + 5E6B9A0B2394C57A00E6720C /* NativeHooks_Darwin.cxx in Sources */, + 5E6B9A162394C58500E6720C /* SimbaPlugin.cxx in Sources */, + 5E6B9A1F2394C59700E6720C /* ThreadPool.cxx in Sources */, + 5EBE1AC223C7C84800B99E32 /* KeyEvent.cxx in Sources */, + 5E27B24523B829C60091232F /* InputOutput.cxx in Sources */, + 5E31A8B523C840F500E751F6 /* MouseWheelEvent.cxx in Sources */, + 5E27B24B23B9A0430091232F /* Random.cxx in Sources */, + 5E6B9A002394C57600E6720C /* Time.cxx in Sources */, + 5EBE1AA223C7C84800B99E32 /* Component.cxx in Sources */, + 5EC942EC23D4A8AA00E42B21 /* Platform_Darwin.mm in Sources */, + 5EBE1AA823C7C84800B99E32 /* MouseEvent.cxx in Sources */, + 5E6B9A092394C57600E6720C /* JVM.cxx in Sources */, + 5EE7DD97243BD9E5008FDC5D /* mach_inject.c in Sources */, + 5EE7DD9D243BD9F7008FDC5D /* main.cxx in Sources */, + 5EFF49BF23A703C700DC83D1 /* SpinLock.cxx in Sources */, + 5E6B9A152394C57A00E6720C /* Platform_Windows.cxx in Sources */, + 5E6B9A132394C57A00E6720C /* DebugConsole.cxx in Sources */, + 5EBE1AC823C7C84800B99E32 /* Frame.cxx in Sources */, + 5E6B9A0C2394C57A00E6720C /* NativeHooks_Linux.cxx in Sources */, + 5EBE1AC023C7C84800B99E32 /* FocusEvent.cxx in Sources */, + 5EBE1A9C23C7C84800B99E32 /* Applet.cxx in Sources */, + 5EBE1AAA23C7C84800B99E32 /* EventQueue.cxx in Sources */, + 5E6EA10223C91350003CB0B5 /* DetachedThreadPool.cxx in Sources */, + 5E6B9A1D2394C59400E6720C /* Reflection.cxx in Sources */, + 5E4D644D241F47930010EDBE /* PointerInfo.cxx in Sources */, + 5EBE1AB023C7C84800B99E32 /* Toolkit.cxx in Sources */, + 5E6B9A072394C57600E6720C /* EIOS.cxx in Sources */, + 5EBE1AA423C7C84800B99E32 /* AWTAccessor.cxx in Sources */, + 5E6B9A0F2394C57A00E6720C /* Platform_Linux.cxx in Sources */, + 5E6B9A0D2394C57A00E6720C /* NativeHooks_Windows.cxx in Sources */, + 5E6B9A042394C57600E6720C /* Module.cxx in Sources */, + 5EBE1AA623C7C84800B99E32 /* InputEvent.cxx in Sources */, + 5E6EA10D23C91371003CB0B5 /* rd_route.c in Sources */, + 5E6B9A012394C57600E6720C /* SharedEvent.cxx in Sources */, + 5E6B9A242394C59C00E6720C /* ReflectionHook.cxx in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 5EC59503232D956B00922111 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 5E4CDC1E2381F40700A05869 /* NativeHooks_Linux.cxx in Sources */, + 5EBE1A9F23C7C84800B99E32 /* AWTEventAccessor.cxx in Sources */, + 5EBE1A9D23C7C84800B99E32 /* AWTEvent.cxx in Sources */, + 5EBD66D5235CF15400D183B1 /* Module.cxx in Sources */, + 5E31A8B323C83E3F00E751F6 /* MouseWheelEvent.cxx in Sources */, + 5EBD66D1235CF15400D183B1 /* Time.cxx in Sources */, + 5E0F52AC23AE8EAD002518AF /* Plugin.cxx in Sources */, + 5EBD66D0235CF15400D183B1 /* JVM.cxx in Sources */, + 5E5B3F8A239D36A400F07DD1 /* ModelRendering.cpp in Sources */, + 5EBD66CE235CF15400D183B1 /* EIOS.cxx in Sources */, + 5EBE1AC123C7C84800B99E32 /* KeyEvent.cxx in Sources */, + 5E27B24423B829C60091232F /* InputOutput.cxx in Sources */, + 5E27B24A23B9A0430091232F /* Random.cxx in Sources */, + 5E4D644B241F424D0010EDBE /* PointerInfo.cxx in Sources */, + 5EBD66BA235CF15400D183B1 /* Platform_Linux.cxx in Sources */, + 5EBE1AA123C7C84800B99E32 /* Component.cxx in Sources */, + 5EBE1AA723C7C84800B99E32 /* MouseEvent.cxx in Sources */, + 5EC942EB23D4A81B00E42B21 /* Platform_Darwin.mm in Sources */, + 5EBD66C1235CF15400D183B1 /* DebugConsole.cxx in Sources */, + 5EE7DD96243BD9E5008FDC5D /* mach_inject.c in Sources */, + 5EE7DD9C243BD9F7008FDC5D /* main.cxx in Sources */, + 5EFF49BE23A703C700DC83D1 /* SpinLock.cxx in Sources */, + 5E4CDC1F2381F40700A05869 /* NativeHooks_Windows.cxx in Sources */, + 5EBD66C9235CF15400D183B1 /* SimbaPlugin.cxx in Sources */, + 5EBD66C6235CF15400D183B1 /* Reflection.cxx in Sources */, + 5EBE1AC723C7C84800B99E32 /* Frame.cxx in Sources */, + 5E4CDC1D2381F40700A05869 /* NativeHooks_Darwin.cxx in Sources */, + 5EBE1ABF23C7C84800B99E32 /* FocusEvent.cxx in Sources */, + 5EBE1A9B23C7C84800B99E32 /* Applet.cxx in Sources */, + 5EBE1AA923C7C84800B99E32 /* EventQueue.cxx in Sources */, + 5E6EA10123C91350003CB0B5 /* DetachedThreadPool.cxx in Sources */, + 5E1236A423946AA900D8AE33 /* ReflectionHook.cxx in Sources */, + 5EBE1AAF23C7C84800B99E32 /* Toolkit.cxx in Sources */, + 5EBE1AA323C7C84800B99E32 /* AWTAccessor.cxx in Sources */, + 5EBD66D2235CF15400D183B1 /* SharedEvent.cxx in Sources */, + 5EBD66CF235CF15400D183B1 /* ThreadPool.cxx in Sources */, + 5E5FC02B237264840048997B /* ControlCenter.cxx in Sources */, + 5EBE1AA523C7C84800B99E32 /* InputEvent.cxx in Sources */, + 5E6EA10C23C91371003CB0B5 /* rd_route.c in Sources */, + 5E07A6C02378B1B100EB4CDF /* Graphics.cxx in Sources */, + 5EBD66C3235CF15400D183B1 /* Platform_Windows.cxx in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 5E6281EC2421B776006BA298 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_WARN_ATOMIC_IMPLICIT_SEQ_CST = YES; + CODE_SIGN_STYLE = Automatic; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + EXECUTABLE_PREFIX = lib; + MACOSX_DEPLOYMENT_TARGET = 10.15; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + 5E6281ED2421B776006BA298 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_WARN_ATOMIC_IMPLICIT_SEQ_CST = YES; + CODE_SIGN_STYLE = Automatic; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + EXECUTABLE_PREFIX = lib; + MACOSX_DEPLOYMENT_TARGET = 10.15; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Release; + }; + 5E6B99FE2394C4F200E6720C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++17"; + CLANG_WARN_ATOMIC_IMPLICIT_SEQ_CST = YES; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = YTT3U95S25; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + EXECUTABLE_PREFIX = lib; + GCC_C_LANGUAGE_STANDARD = c11; + GCC_OPTIMIZATION_LEVEL = 3; + HEADER_SEARCH_PATHS = ( + "/Library/Java/JavaVirtualMachines/jdk-12.0.2.jdk/Contents/Home/include", + "/Library/Java/JavaVirtualMachines/jdk-12.0.2.jdk/Contents/Home/include/darwin", + ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(LOCAL_LIBRARY_DIR)/Internet\\ Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/lib", + "$(LOCAL_LIBRARY_DIR)/Internet\\ Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/lib", + "$(LOCAL_LIBRARY_DIR)/Internet\\ Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/lib", + "$(LOCAL_LIBRARY_DIR)/Internet\\ Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/lib/server", + ); + MACOSX_DEPLOYMENT_TARGET = 10.12; + OTHER_CFLAGS = "-fvisibility=hidden"; + OTHER_LDFLAGS = ( + "-rpath", + "/Library/Internet\\ Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/lib", + "-rpath", + "/Library/Internet\\ Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/lib/server/", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + 5E6B99FF2394C4F200E6720C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++17"; + CLANG_WARN_ATOMIC_IMPLICIT_SEQ_CST = YES; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = YTT3U95S25; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + EXECUTABLE_PREFIX = lib; + GCC_C_LANGUAGE_STANDARD = c11; + HEADER_SEARCH_PATHS = ( + "/Library/Java/JavaVirtualMachines/jdk-12.0.2.jdk/Contents/Home/include", + "/Library/Java/JavaVirtualMachines/jdk-12.0.2.jdk/Contents/Home/include/darwin", + ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(LOCAL_LIBRARY_DIR)/Internet\\ Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/lib", + "$(LOCAL_LIBRARY_DIR)/Internet\\ Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/lib", + "$(LOCAL_LIBRARY_DIR)/Internet\\ Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/lib", + "$(LOCAL_LIBRARY_DIR)/Internet\\ Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/lib/server", + ); + MACOSX_DEPLOYMENT_TARGET = 10.12; + OTHER_CFLAGS = "-fvisibility=hidden"; + OTHER_LDFLAGS = ( + "-rpath", + "/Library/Internet\\ Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/lib", + "-rpath", + "/Library/Internet\\ Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/lib/server/", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Release; + }; + 5EC5950D232D956B00922111 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.12; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 5EC5950E232D956B00922111 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.12; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = macosx; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 5EC59510232D956B00922111 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + "USE_DETOURS=1", + ); + HEADER_SEARCH_PATHS = ( + "/Library/Java/JavaVirtualMachines/jdk-12.0.2.jdk/Contents/Home/include", + "/Library/Java/JavaVirtualMachines/jdk-12.0.2.jdk/Contents/Home/include/darwin", + ); + INFOPLIST_FILE = RemoteInput/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + OTHER_CFLAGS = "-fvisibility=hidden"; + PRODUCT_BUNDLE_IDENTIFIER = XIO.RemoteInput; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + 5EC59511232D956B00922111 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREPROCESSOR_DEFINITIONS = "USE_DETOURS=1"; + HEADER_SEARCH_PATHS = ( + "/Library/Java/JavaVirtualMachines/jdk-12.0.2.jdk/Contents/Home/include", + "/Library/Java/JavaVirtualMachines/jdk-12.0.2.jdk/Contents/Home/include/darwin", + ); + INFOPLIST_FILE = RemoteInput/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + OTHER_CFLAGS = "-fvisibility=hidden"; + PRODUCT_BUNDLE_IDENTIFIER = XIO.RemoteInput; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 5E6281EE2421B776006BA298 /* Build configuration list for PBXNativeTarget "RemoteInputBootstrap" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5E6281EC2421B776006BA298 /* Debug */, + 5E6281ED2421B776006BA298 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 5E6B99FD2394C4F200E6720C /* Build configuration list for PBXNativeTarget "libRemoteInput" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5E6B99FE2394C4F200E6720C /* Debug */, + 5E6B99FF2394C4F200E6720C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 5EC59501232D956B00922111 /* Build configuration list for PBXProject "RemoteInput" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5EC5950D232D956B00922111 /* Debug */, + 5EC5950E232D956B00922111 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 5EC5950F232D956B00922111 /* Build configuration list for PBXNativeTarget "RemoteInput" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5EC59510232D956B00922111 /* Debug */, + 5EC59511232D956B00922111 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 5EC594FE232D956B00922111 /* Project object */; +} diff --git a/third-party/RemoteInput/RemoteInput.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/third-party/RemoteInput/RemoteInput.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..f291d4c --- /dev/null +++ b/third-party/RemoteInput/RemoteInput.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/third-party/RemoteInput/RemoteInput.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/third-party/RemoteInput/RemoteInput.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/third-party/RemoteInput/RemoteInput.xcodeproj/project.xcworkspace/xcuserdata/brandonanthony.xcuserdatad/UserInterfaceState.xcuserstate b/third-party/RemoteInput/RemoteInput.xcodeproj/project.xcworkspace/xcuserdata/brandonanthony.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000000000000000000000000000000000000..2952946de33f1d3468d6f20526a8e9fd904be592 GIT binary patch literal 314888 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%0DRo5VXTU>j&E5 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

racyhl=GtDc&4$X8vS>c!Dj^|IK#>fTvh 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 + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/third-party/RemoteInput/RemoteInput.xcodeproj/xcshareddata/xcschemes/libRemoteInput.xcscheme b/third-party/RemoteInput/RemoteInput.xcodeproj/xcshareddata/xcschemes/libRemoteInput.xcscheme new file mode 100644 index 0000000..47e29c3 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput.xcodeproj/xcshareddata/xcschemes/libRemoteInput.xcscheme @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/third-party/RemoteInput/RemoteInput.xcodeproj/xcuserdata/brandonanthony.xcuserdatad/xcschemes/xcschememanagement.plist b/third-party/RemoteInput/RemoteInput.xcodeproj/xcuserdata/brandonanthony.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..50ca13b --- /dev/null +++ b/third-party/RemoteInput/RemoteInput.xcodeproj/xcuserdata/brandonanthony.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,47 @@ + + + + + SchemeUserState + + RemoteInput.xcscheme_^#shared#^_ + + orderHint + 0 + + RemoteInputBootstrap.xcscheme_^#shared#^_ + + orderHint + 1 + + libRemoteInput.xcscheme_^#shared#^_ + + orderHint + 0 + + remoteInput64.xcscheme_^#shared#^_ + + orderHint + 1 + + + SuppressBuildableAutocreation + + 5E6281EA2421B776006BA298 + + primary + + + 5E6B99FB2394C4F200E6720C + + primary + + + 5EC59506232D956B00922111 + + primary + + + + + diff --git a/third-party/RemoteInput/RemoteInput/DetachedThreadPool.cxx b/third-party/RemoteInput/RemoteInput/DetachedThreadPool.cxx new file mode 100644 index 0000000..9e0f015 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/DetachedThreadPool.cxx @@ -0,0 +1,93 @@ +#include "DetachedThreadPool.hxx" + +DetachedThreadPool::DetachedThreadPool() noexcept : DetachedThreadPool(std::thread::hardware_concurrency()) +{ +} + +DetachedThreadPool::DetachedThreadPool(std::size_t max_threads) noexcept : mutex(std::make_shared()), condition(std::make_shared()), tasks(std::make_shared>>()), threads(), stop(std::make_shared(false)), max_threads(max_threads) +{ + this->create_threads(); +} + +DetachedThreadPool::DetachedThreadPool(DetachedThreadPool&& other) noexcept : mutex(std::move(other.mutex)), condition(std::move(other.condition)), tasks(std::move(other.tasks)), threads(std::move(other.threads)), stop(std::move(other.stop)), max_threads(other.max_threads) +{ + other.max_threads = 0; +} + +DetachedThreadPool::~DetachedThreadPool() noexcept +{ + if (max_threads == 0) + { + return; + } + + this->terminate(); +} + +void DetachedThreadPool::create_threads() noexcept +{ + if (max_threads == 0) + { + return; + } + + if (threads.size() != max_threads) + { + threads.reserve(max_threads); + + for (std::size_t i = 0; i < max_threads; ++i) + { + threads.emplace_back([mutex = this->mutex, condition = this->condition, tasks = this->tasks, stop = this->stop] { + while(true) + { + std::unique_lock lock(*mutex); + condition->wait(lock, [tasks, stop] { + return !tasks->empty() || !stop || *stop; + }); + + if (!stop || *stop) //&& tasks->empty() + { + lock.unlock(); + break; + } + + auto task = std::move(tasks->front()); + tasks->pop(); + + lock.unlock(); + + task(*stop.get()); + } + }); + + this->threads[i].detach(); + } + } +} + +void DetachedThreadPool::terminate() noexcept +{ + if (stop && !*stop) + { + std::unique_lock lock(*mutex); + *stop = true; + lock.unlock(); + condition->notify_all(); + std::vector().swap(this->threads); + } +} + +void DetachedThreadPool::add_task(std::function &&task) +{ + if (stop && !*stop) + { + std::unique_lock lock(*mutex); + tasks->emplace(task); + lock.unlock(); + condition->notify_one(); + } + else + { + throw std::runtime_error("DetachedThreadPool is currently shutting down. Cannot enqueue more tasks."); + } +} diff --git a/third-party/RemoteInput/RemoteInput/DetachedThreadPool.hxx b/third-party/RemoteInput/RemoteInput/DetachedThreadPool.hxx new file mode 100644 index 0000000..53eded3 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/DetachedThreadPool.hxx @@ -0,0 +1,66 @@ +#ifndef DETACHEDTHREADPOOL_HXX_INCLUDED +#define DETACHEDTHREADPOOL_HXX_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class DetachedThreadPool final +{ +private: + std::shared_ptr mutex; + std::shared_ptr condition; + std::shared_ptr>> tasks; + std::vector threads; + std::shared_ptr stop; + std::size_t max_threads; + void create_threads() noexcept; + +public: + DetachedThreadPool() noexcept; + DetachedThreadPool(std::size_t max_threads) noexcept; + DetachedThreadPool(const DetachedThreadPool&) = delete; + DetachedThreadPool(DetachedThreadPool&& other) noexcept; + ~DetachedThreadPool() noexcept; + + + DetachedThreadPool& operator = (const DetachedThreadPool&) = delete; + DetachedThreadPool& operator = (DetachedThreadPool&&) = delete; + + void terminate() noexcept; + + void add_task(std::function &&task); + + template + auto enqueue(Task &&task, Args&&... args) noexcept -> std::future>; +}; + +template +auto DetachedThreadPool::enqueue(Task &&task, Args&&... args) noexcept -> std::future> +{ + auto packaged_task = std::make_shared(Args&&...)>>( + std::bind(std::forward(task), std::forward(args)...) + ); + + std::future> result = packaged_task->get_future(); + std::unique_lock lock(*mutex); + if (*stop) + { + throw std::runtime_error("DetachedThreadPool is currently shutting down. Cannot enqueue more tasks."); + } + + tasks->emplace([packaged_task](std::atomic_bool &stopped) { + (*packaged_task)(stopped); + }, std::ref(stop)); + + condition->notify_one(); + return result; +} + +#endif // DETACHEDTHREADPOOL_HXX_INCLUDED diff --git a/third-party/RemoteInput/RemoteInput/EIOS.cxx b/third-party/RemoteInput/RemoteInput/EIOS.cxx new file mode 100644 index 0000000..d2ce55b --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/EIOS.cxx @@ -0,0 +1,522 @@ +// +// EIOS.cpp +// RemoteInput +// +// Created by Brandon on 2019-09-14. +// Copyright © 2019 XIO. All rights reserved. +// + +#include "EIOS.hxx" +#include +#include +#include +#include "SimbaPlugin.hxx" +#include "MemoryMap.hxx" +#include "Platform.hxx" + +std::unordered_map clients; +std::vector all_clients; +extern TSimbaInfomation PLUGIN_SIMBA_INFO; +extern TSimbaMethodsExtended PLUGIN_SIMBA_METHODS; + +EIOS* STD_CALL EIOS_RequestTarget(const char* initargs) noexcept +{ + std::size_t pos = 0; + std::int32_t pid = std::stoi(initargs, &pos); + if (pos == std::string(initargs).length()) + { + return EIOS_PairClient(pid); + } + return nullptr; +} + +void STD_CALL EIOS_ReleaseTarget(EIOS* eios) noexcept +{ + if (eios) + { + clients.erase(eios->pid); + eios->control_center->set_parent_process_id(-1); + eios->control_center->set_parent_thread_id(-1); + eios->control_center->terminate(); + eios->control_center.reset(); + eios->pid = -1; + delete eios; + } +} + +void STD_CALL EIOS_GetTargetDimensions(EIOS* eios, std::int32_t* width, std::int32_t* height) noexcept +{ + if (eios) + { + eios->control_center->get_target_dimensions(width, height); + } +} + +ImageFormat STD_CALL EIOS_GetImageFormat(EIOS* eios) noexcept +{ + if (eios) + { + return eios->control_center->get_image_format(); + } + return ImageFormat::BGRA; +} + +void STD_CALL EIOS_SetImageFormat(EIOS* eios, ImageFormat format) noexcept +{ + if (eios) + { + eios->control_center->set_image_format(format); + } +} + +std::uint8_t* STD_CALL EIOS_GetImageBuffer(EIOS* eios) noexcept +{ + if (eios) + { + return eios->control_center->get_image(); + } + return nullptr; +} + +std::uint8_t* STD_CALL EIOS_GetDebugImageBuffer(EIOS* eios) noexcept +{ + if (eios) + { + return eios->control_center->get_debug_image(); + } + return nullptr; +} + +void STD_CALL EIOS_SetGraphicsDebugging(EIOS* eios, bool enabled) noexcept +{ + if (eios) + { + eios->control_center->set_debug_graphics(enabled); + } +} + +void STD_CALL EIOS_UpdateImageBuffer(EIOS* eios) noexcept +{ + //printf("%s\n", __FUNCTION__); +} + +bool STD_CALL EIOS_HasFocus(EIOS* eios) noexcept +{ + if (eios) + { + return eios->control_center->has_focus(); + } + return false; +} + +void STD_CALL EIOS_GainFocus(EIOS* eios) noexcept +{ + if (eios) + { + eios->control_center->gain_focus(); + } +} + +void STD_CALL EIOS_LoseFocus(EIOS* eios) noexcept +{ + if (eios) + { + eios->control_center->lose_focus(); + } +} + +bool STD_CALL EIOS_IsKeyboardInputEnabled(EIOS* eios) noexcept +{ + if (eios) + { + return eios->control_center->is_keyboard_input_enabled(); + } + return false; +} + +void STD_CALL EIOS_SetKeyboardInputEnabled(EIOS* eios, bool enabled) noexcept +{ + if (eios) + { + eios->control_center->set_keyboard_input_enabled(enabled); + } +} + +bool STD_CALL EIOS_IsMouseInputEnabled(EIOS* eios) noexcept +{ + if (eios) + { + return eios->control_center->is_mouse_input_enabled(); + } + return false; +} + +void STD_CALL EIOS_SetMouseInputEnabled(EIOS* eios, bool enabled) noexcept +{ + if (eios) + { + eios->control_center->set_mouse_input_enabled(enabled); + } +} + +void STD_CALL EIOS_GetMousePosition(EIOS* eios, std::int32_t* x, std::int32_t* y) noexcept +{ + + if (eios) + { + eios->control_center->get_mouse_position(x, y); + } +} + +void STD_CALL EIOS_GetRealMousePosition(EIOS* eios, std::int32_t* x, std::int32_t* y) noexcept +{ + + if (eios) + { + eios->control_center->get_real_mouse_position(x, y); + } +} + +void STD_CALL EIOS_MoveMouse(EIOS* eios, std::int32_t x, std::int32_t y) noexcept +{ + if (eios) + { + eios->control_center->move_mouse(x, y); + } +} + +void STD_CALL EIOS_HoldMouse(EIOS* eios, std::int32_t x, std::int32_t y, std::int32_t button) noexcept +{ + if (eios) + { + eios->control_center->hold_mouse(button); + } +} + +void STD_CALL EIOS_ReleaseMouse(EIOS* eios, std::int32_t x, std::int32_t y, std::int32_t button) noexcept +{ + if (eios) + { + eios->control_center->release_mouse(button); + } +} + +void STD_CALL EIOS_ScrollMouse(EIOS* eios, std::int32_t x, std::int32_t y, std::int32_t lines) noexcept +{ + if (eios) + { + eios->control_center->scroll_mouse(lines); + } +} + +bool STD_CALL EIOS_IsMouseButtonHeld(EIOS* eios, std::int32_t button) noexcept +{ + if (eios) + { + return eios->control_center->is_mouse_held(button); + } + return false; +} + +void STD_CALL EIOS_SendString(EIOS* eios, const char* string, std::int32_t keywait, std::int32_t keymodwait) noexcept +{ + if (eios) + { + eios->control_center->send_string(string, keywait, keymodwait); + } +} + +void STD_CALL EIOS_HoldKey(EIOS* eios, std::int32_t key) noexcept +{ + if (eios) + { + eios->control_center->hold_key(key); + } +} + +void STD_CALL EIOS_ReleaseKey(EIOS* eios, std::int32_t key) noexcept +{ + if (eios) + { + eios->control_center->release_key(key); + } +} + +bool STD_CALL EIOS_IsKeyHeld(EIOS* eios, std::int32_t key) noexcept +{ + if (eios) + { + return eios->control_center->is_key_held(key); + } + return false; +} + +std::int32_t STD_CALL EIOS_GetKeyboardSpeed(EIOS* eios) noexcept +{ + if (eios) + { + return eios->control_center->get_keyboard_speed(); + } + return -1; +} + +void STD_CALL EIOS_SetKeyboardSpeed(EIOS* eios, std::int32_t speed) noexcept +{ + if (eios) + { + eios->control_center->set_keyboard_speed(speed); + } +} + +std::int32_t STD_CALL EIOS_GetKeyboardRepeatDelay(EIOS* eios) noexcept +{ + if (eios) + { + return eios->control_center->get_keyboard_repeat_delay(); + } + return -1; +} + +void STD_CALL EIOS_SetKeyboardRepeatDelay(EIOS* eios, std::int32_t delay) noexcept +{ + if (eios) + { + eios->control_center->set_keyboard_repeat_delay(delay); + } +} + +bool STD_CALL EIOS_GetGraphicsScaling(EIOS* eios) noexcept +{ + if (eios) + { + return eios->control_center->get_graphics_scaling(); + } + return false; +} + +void STD_CALL EIOS_SetGraphicsScaling(EIOS* eios, bool enabled) noexcept +{ + if (eios) + { + eios->control_center->set_graphics_scaling(enabled); + } +} + +EIOS* STD_CALL EIOS_PairClient(std::int32_t pid) noexcept +{ + std::int32_t tid = GetCurrentThreadID(); + + if (clients.count(pid)) + { + EIOS* eios = clients[pid]; + return eios->control_center->parent_thread_id() == tid ? eios : nullptr; + } + + if (!IsProcessAlive(pid)) + { + clients.erase(pid); + ControlCenter::kill_zombie_process(pid); + return nullptr; + } + + try { + std::unique_ptr control_center = std::make_unique(pid, true, nullptr); + if (control_center) + { + EIOS* eios = new EIOS(); + eios->pid = pid; + eios->control_center = std::move(control_center); + eios->control_center->set_parent_process_id(getpid()); + eios->control_center->set_parent_thread_id(tid); + + clients[pid] = eios; + return eios; + } + } + catch(std::exception& e) + { + fprintf(stderr, "%s\n", e.what()); + } + return nullptr; +} + +void STD_CALL EIOS_KillClientPID(std::int32_t pid) noexcept +{ + if (pid != -1 && clients.count(pid) && IsProcessAlive(pid)) + { + if (EIOS* eios = clients[pid]; eios->control_center->parent_thread_id() == GetCurrentThreadID()) + { + clients.erase(pid); + eios->control_center->kill_process(pid); + } + } +} + +void STD_CALL EIOS_KillClient(EIOS* eios) noexcept +{ + if (eios && eios->pid != -1 && clients.count(eios->pid) && IsProcessAlive(eios->pid)) + { + if (eios->control_center->parent_thread_id() == GetCurrentThreadID()) + { + clients.erase(eios->pid); + eios->control_center->kill_process(eios->pid); + } + } +} + +void STD_CALL EIOS_ReleaseClient(EIOS* eios) noexcept +{ + EIOS_ReleaseTarget(eios); +} + +void STD_CALL EIOS_KillZombieClients() noexcept +{ + std::vector dead_clients; + for(auto it = clients.begin(); it != clients.end(); ++it) + { + if (!IsProcessAlive(it->first)) + { + ControlCenter::kill_zombie_process(it->first); + dead_clients.push_back(it->first); + } + } + + for (auto pid : dead_clients) + { + clients.erase(pid); + } +} + +std::size_t STD_CALL EIOS_GetClients(bool unpaired_only) noexcept +{ + all_clients.clear(); + + auto pids = get_pids(); + for (auto pid : pids) + { + if (unpaired_only) + { + bool exists = false; + if (!ControlCenter::controller_is_paired(pid, &exists)) + { + if (exists) + { + all_clients.push_back(pid); + } + } + } + else + { + if (ControlCenter::controller_exists(pid)) + { + all_clients.push_back(pid); + } + } + } + + return all_clients.size(); +} + +std::int32_t STD_CALL EIOS_GetClientPID(std::size_t index) noexcept +{ + return index < all_clients.size() && all_clients.size() > 0 ? all_clients[index] : -1; +} + +EIOS* SimbaPluginTarget_Request(const char* initargs) noexcept +{ + return EIOS_RequestTarget(initargs); +} + +EIOS* SimbaPluginTarget_RequestWithDebugImage(const char* initargs, void** image) noexcept +{ + EIOS* eios = EIOS_RequestTarget(initargs); + + if (image) + { + std::int32_t width = 0; + std::int32_t height = 0; + eios->control_center->get_target_dimensions(&width, &height); + eios->control_center->set_image_format(ImageFormat::BGRA); + eios->control_center->set_debug_graphics(true); + *image = PLUGIN_SIMBA_METHODS.ExternalImage_Create(true); + PLUGIN_SIMBA_METHODS.ExternalImage_SetMemory(*image, eios->control_center->get_debug_image(), width, height); + } + + return eios; +} + +void SimbaPluginTarget_Release(EIOS* eios) noexcept +{ + EIOS_ReleaseTarget(eios); +} + +void SimbaPluginTarget_GetDimensions(EIOS* eios, std::int32_t* width, std::int32_t* height) noexcept +{ + EIOS_GetTargetDimensions(eios, width, height); +} + +bool SimbaPluginTarget_GetImageData(EIOS* eios, std::int32_t x, std::int32_t y, std::int32_t width, std::int32_t height, void** bgra, std::int32_t* data_width) noexcept +{ + if (eios) + { + *data_width = eios->control_center->get_target_width(); + *bgra = &eios->control_center->get_image()[(y * (*data_width) + x) * 4]; + + return true; + } else { + return false; + } +} + +bool SimbaPluginTarget_MousePressed(EIOS* eios, int mouse_button) noexcept +{ + return EIOS_IsMouseButtonHeld(eios, mouse_button); +} + +void SimbaPluginTarget_MousePosition(EIOS* eios, std::int32_t* x, std::int32_t* y) noexcept +{ + EIOS_GetMousePosition(eios, x, y); +} + +void SimbaPluginTarget_MouseTeleport(EIOS* eios, std::int32_t x, std::int32_t y) noexcept +{ + EIOS_MoveMouse(eios, x, y); +} + +void SimbaPluginTarget_MouseUp(EIOS* eios, int mouse_button) noexcept +{ + EIOS_ReleaseMouse(eios, 0, 0, mouse_button); +} + +void SimbaPluginTarget_MouseDown(EIOS* eios, int mouse_button) noexcept +{ + EIOS_HoldMouse(eios, 0, 0, mouse_button); +} + +void SimbaPluginTarget_MouseScroll(EIOS* eios, int scrolls) noexcept +{ + EIOS_ScrollMouse(eios, 0, 0, scrolls); +} + +void SimbaPluginTarget_KeyDown(EIOS* eios, std::int32_t key) noexcept +{ + EIOS_HoldKey(eios, key); +} + +void SimbaPluginTarget_KeyUp(EIOS* eios, std::int32_t key) noexcept +{ + EIOS_ReleaseKey(eios, key); +} + +void SimbaPluginTarget_KeySend(EIOS* eios, char* text, std::int32_t len, std::int32_t* sleeptimes) noexcept +{ + if (eios) + { + eios->control_center->key_send(text, len, sleeptimes); + } +} + +bool SimbaPluginTarget_KeyPressed(EIOS* eios, std::int32_t key) noexcept +{ + return EIOS_IsKeyHeld(eios, key); +} \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/EIOS.hxx b/third-party/RemoteInput/RemoteInput/EIOS.hxx new file mode 100644 index 0000000..c972a36 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/EIOS.hxx @@ -0,0 +1,102 @@ +// +// EIOS.hxx +// RemoteInput +// +// Created by Brandon on 2019-09-14. +// Copyright © 2019 XIO. All rights reserved. +// + +#ifndef EIOS_HXX_INCLUDED +#define EIOS_HXX_INCLUDED + +#ifdef __cplusplus +#include +#else +#include +#endif + +#if defined(_WIN32) || defined (_WIN64) +#define EXPORT __declspec(dllexport) +#define STD_CALL __stdcall +#else +#define EXPORT [[gnu::visibility("default")]] +#define STD_CALL +#endif + +#include "EIOSTypes.hxx" +#include "ControlCenter.hxx" + +typedef struct EIOS +{ + std::int32_t pid; + std::intptr_t local_storage; + std::unique_ptr control_center; +} EIOS; + +#ifdef __cplusplus +extern "C" { +#endif + +EXPORT EIOS* STD_CALL EIOS_RequestTarget(const char* initargs) noexcept; +EXPORT void STD_CALL EIOS_ReleaseTarget(EIOS* eios) noexcept; +EXPORT void STD_CALL EIOS_GetTargetDimensions(EIOS* eios, std::int32_t* width, std::int32_t* height) noexcept; +EXPORT ImageFormat STD_CALL EIOS_GetImageFormat(EIOS* eios) noexcept; +EXPORT void STD_CALL EIOS_SetImageFormat(EIOS* eios, ImageFormat format) noexcept; +EXPORT std::uint8_t* STD_CALL EIOS_GetImageBuffer(EIOS* eios) noexcept; +EXPORT std::uint8_t* STD_CALL EIOS_GetDebugImageBuffer(EIOS* eios) noexcept; +EXPORT void STD_CALL EIOS_SetGraphicsDebugging(EIOS* eios, bool enabled) noexcept; +EXPORT void STD_CALL EIOS_UpdateImageBuffer(EIOS* eios) noexcept; +EXPORT bool STD_CALL EIOS_HasFocus(EIOS* eios) noexcept; +EXPORT void STD_CALL EIOS_GainFocus(EIOS* eios) noexcept; +EXPORT void STD_CALL EIOS_LoseFocus(EIOS* eios) noexcept; +EXPORT bool STD_CALL EIOS_IsKeyboardInputEnabled(EIOS* eios) noexcept; +EXPORT void STD_CALL EIOS_SetKeyboardInputEnabled(EIOS* eios, bool enabled) noexcept; +EXPORT bool STD_CALL EIOS_IsMouseInputEnabled(EIOS* eios) noexcept; +EXPORT void STD_CALL EIOS_SetMouseInputEnabled(EIOS* eios, bool enabled) noexcept; +EXPORT void STD_CALL EIOS_GetMousePosition(EIOS* eios, std::int32_t* x, std::int32_t* y) noexcept; +EXPORT void STD_CALL EIOS_GetRealMousePosition(EIOS* eios, std::int32_t* x, std::int32_t* y) noexcept; +EXPORT void STD_CALL EIOS_MoveMouse(EIOS* eios, std::int32_t x, std::int32_t y) noexcept; +EXPORT void STD_CALL EIOS_HoldMouse(EIOS* eios, std::int32_t x, std::int32_t y, std::int32_t button) noexcept; +EXPORT void STD_CALL EIOS_ReleaseMouse(EIOS* eios, std::int32_t x, std::int32_t y, std::int32_t button) noexcept; +EXPORT void STD_CALL EIOS_ScrollMouse(EIOS* eios, std::int32_t x, std::int32_t y, std::int32_t lines) noexcept; +EXPORT bool STD_CALL EIOS_IsMouseButtonHeld(EIOS* eios, std::int32_t button) noexcept; +EXPORT void STD_CALL EIOS_SendString(EIOS* eios, const char* string, std::int32_t keywait, std::int32_t keymodwait) noexcept; +EXPORT void STD_CALL EIOS_HoldKey(EIOS* eios, std::int32_t key) noexcept; +EXPORT void STD_CALL EIOS_ReleaseKey(EIOS* eios, std::int32_t key) noexcept; +EXPORT bool STD_CALL EIOS_IsKeyHeld(EIOS* eios, std::int32_t key) noexcept; +EXPORT std::int32_t STD_CALL EIOS_GetKeyboardSpeed(EIOS* eios) noexcept; +EXPORT void STD_CALL EIOS_SetKeyboardSpeed(EIOS* eios, std::int32_t speed) noexcept; +EXPORT std::int32_t STD_CALL EIOS_GetKeyboardRepeatDelay(EIOS* eios) noexcept; +EXPORT void STD_CALL EIOS_SetKeyboardRepeatDelay(EIOS* eios, std::int32_t delay) noexcept; +EXPORT bool STD_CALL EIOS_GetGraphicsScaling(EIOS* eios) noexcept; +EXPORT void STD_CALL EIOS_SetGraphicsScaling(EIOS* eios, bool enabled) noexcept; +EXPORT EIOS* STD_CALL EIOS_PairClient(std::int32_t pid) noexcept; +EXPORT void STD_CALL EIOS_KillClientPID(std::int32_t pid) noexcept; +EXPORT void STD_CALL EIOS_KillClient(EIOS* eios) noexcept; +EXPORT void STD_CALL EIOS_ReleaseClient(EIOS* eios) noexcept; +EXPORT void STD_CALL EIOS_KillZombieClients() noexcept; +EXPORT std::size_t STD_CALL EIOS_GetClients(bool unpaired_only) noexcept; +EXPORT std::int32_t STD_CALL EIOS_GetClientPID(std::size_t index) noexcept; + +// New in Simba 2.0 https://villavu.github.io/Simba/tutorials/plugins/plugin-target.html +EXPORT EIOS* SimbaPluginTarget_Request(const char* initargs) noexcept; +EXPORT EIOS* SimbaPluginTarget_RequestWithDebugImage(const char* initargs, void** image) noexcept; +EXPORT void SimbaPluginTarget_Release(EIOS* eios) noexcept; +EXPORT void SimbaPluginTarget_GetDimensions(EIOS* eios, std::int32_t* width, std::int32_t* height) noexcept; +EXPORT bool SimbaPluginTarget_GetImageData(EIOS* eios, std::int32_t x, std::int32_t y, std::int32_t width, std::int32_t height, void** bgra, std::int32_t* data_width) noexcept; +EXPORT bool SimbaPluginTarget_MousePressed(EIOS* eios, int mouse_button) noexcept; +EXPORT void SimbaPluginTarget_MousePosition(EIOS* eios, std::int32_t* x, std::int32_t* y) noexcept; +EXPORT void SimbaPluginTarget_MouseTeleport(EIOS* eios, std::int32_t x, std::int32_t y) noexcept; +EXPORT void SimbaPluginTarget_MouseUp(EIOS* eios, int mouse_button) noexcept; +EXPORT void SimbaPluginTarget_MouseDown(EIOS* eios, int mouse_button) noexcept; +EXPORT void SimbaPluginTarget_MouseScroll(EIOS* eios, int scrolls) noexcept; +EXPORT void SimbaPluginTarget_KeyDown(EIOS* eios, std::int32_t key) noexcept; +EXPORT void SimbaPluginTarget_KeyUp(EIOS* eios, std::int32_t key) noexcept; +EXPORT void SimbaPluginTarget_KeySend(EIOS* eios, char* text, std::int32_t len, std::int32_t* sleeptimes) noexcept; +EXPORT bool SimbaPluginTarget_KeyPressed(EIOS* eios, std::int32_t key) noexcept; + +#ifdef __cplusplus +} +#endif + +#endif /* EIOS_HXX_INCLUDED */ diff --git a/third-party/RemoteInput/RemoteInput/EIOSTypes.hxx b/third-party/RemoteInput/RemoteInput/EIOSTypes.hxx new file mode 100644 index 0000000..fa90d62 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/EIOSTypes.hxx @@ -0,0 +1,100 @@ +// +// Created by Brandon on 2023-03-28. +// + +#ifndef EIOSTYPES_HXX_INCLUDED +#define EIOSTYPES_HXX_INCLUDED + +#include + +enum class ImageFormat: std::uint32_t +{ + BGR_BGRA, // Compatibility for Simba's Black pixels representing Transparency. Simba should really fix this. + BGRA, + RGBA, + ARGB, + ABGR +}; + +enum class EIOSCommand: std::uint32_t +{ + COMMAND_NONE, + KILL_APPLICATION, + + GET_IMAGE_DIMENSIONS, + GET_TARGET_DIMENSIONS, + HAS_FOCUS, + GAIN_FOCUS, + LOSE_FOCUS, + IS_KEYBOARD_INPUT_ENABLED, + SET_KEYBOARD_INPUT_ENABLED, + IS_MOUSE_INPUT_ENABLED, + SET_MOUSE_INPUT_ENABLED, + GET_MOUSE, + GET_REAL_MOUSE, + MOVE_MOUSE, + HOLD_MOUSE, + RELEASE_MOUSE, + SCROLL_MOUSE, + IS_MOUSE_HELD, + SEND_STRING, + SEND_KEY, + KEY_SEND, + HOLD_KEY, + RELEASE_KEY, + IS_KEY_HELD, + + GET_KEYBOARD_SPEED, + SET_KEYBOARD_SPEED, + GET_KEYBOARD_REPEAT_DELAY, + SET_KEYBOARD_REPEAT_DELAY, + STOP_ALL_PROCESSING, + + GET_UI_SCALING, + SET_UI_SCALING, + + REFLECT_OBJECT, + REFLECT_COMPARE_OBJECTS, + REFLECT_INSTANCE_OF, + REFLECT_RELEASE_OBJECT, + REFLECT_RELEASE_OBJECTS, + REFLECT_CHAR, + REFLECT_BYTE, + REFLECT_BOOLEAN, + REFLECT_SHORT, + REFLECT_INT, + REFLECT_LONG, + REFLECT_FLOAT, + REFLECT_DOUBLE, + REFLECT_STRING, + REFLECT_ARRAY, + REFLECT_ARRAY_SIZE, + REFLECT_ARRAY_WITH_SIZE, + + REFLECT_ARRAY_ALL, + REFLECT_ARRAY_WITH_INDEX_SIZE, + REFLECT_ARRAY_INDICES, + + REFLECT_CLASS_NAME, + REFLECT_CLASS_TYPE, + REFLECT_CLASS_LOADER, + + REMOTE_VM_INSTRUCTION +}; + +typedef struct EIOSData +{ + std::int32_t parent_process_id; + std::int32_t parent_thread_id; + std::int32_t image_width; + std::int32_t image_height; + std::int32_t target_width; + std::int32_t target_height; + bool debug_graphics; + bool ui_scaling; + ImageFormat image_format; + EIOSCommand command; + std::uint8_t data[100000 * 8]; +} EIOSData; + +#endif /* EIOSTYPES_HXX_INCLUDED */ diff --git a/third-party/RemoteInput/RemoteInput/Echo/Atomics.cxx b/third-party/RemoteInput/RemoteInput/Echo/Atomics.cxx new file mode 100644 index 0000000..5063cc5 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Echo/Atomics.cxx @@ -0,0 +1,199 @@ +// +// Created by Brandon on 2023-12-30. +// + +#include "Atomics.hxx" + +bool back_off(std::chrono::nanoseconds elapsed) +{ + if (elapsed > std::chrono::milliseconds(128)) + { + std::this_thread::sleep_for(std::chrono::milliseconds(8)); + } + else if (elapsed > std::chrono::microseconds(64)) + { + std::this_thread::sleep_for(elapsed / 2); + } + else if (elapsed > std::chrono::microseconds(4)) + { + std::this_thread::yield(); + } + return false; +} + +template +bool wait_polling(T* self, TestFn&& test, BackoffFn &&back_off, std::chrono::nanoseconds max_elapsed = std::chrono::nanoseconds::zero()) +{ + int count = 0; + const auto start = std::chrono::high_resolution_clock::now(); + while (true) + { + if (test(self)) + { + return true; + } + + if (count < 64) + { + ++count; + continue; + } + + const std::chrono::nanoseconds elapsed = std::chrono::high_resolution_clock::now() - start; + if (max_elapsed != std::chrono::nanoseconds::zero() && max_elapsed < elapsed) + { + return false; + } + + if (back_off(elapsed)) + { + return false; + } + } +} + +bool counting_semaphore::wait_polling(counting_semaphore* self, bool (*test)(counting_semaphore* self), std::chrono::nanoseconds max_elapsed) +{ + return ::wait_polling(self, test, back_off, max_elapsed); +} + +void counting_semaphore::release(std::ptrdiff_t amount) +{ + if (counter.fetch_add(amount, std::memory_order_release) >= 0) + { + + } + else if(amount > 1) + { + counter.notify_all(); + } + else + { + counter.notify_one(); + } +} + +void counting_semaphore::acquire() +{ + #if !defined(USE_ATOMIC_POLLING) + const auto test = [](counting_semaphore* self) -> bool { + auto old = self->counter.load(std::memory_order_relaxed); + if (old != 0) + { + return self->counter.compare_exchange_strong(old, old - 1, std::memory_order_acquire, std::memory_order_relaxed); + } + return false; + }; + + wait_polling(this, test); + #else + auto old = counter.load(std::memory_order_relaxed); + while (old == 0 || !counter.compare_exchange_strong(old, old - 1, std::memory_order_acquire, std::memory_order_relaxed)) + { + old = counter.load(std::memory_order_relaxed); + + #if defined(USE_ATOMIC_WAIT) + counter.wait(old - 1); + #else + std::this_thread::yield(); + #endif + } + #endif +} + +bool counting_semaphore::try_acquire() +{ + auto old = counter.load(std::memory_order_acquire); + while (true) + { + if (old == 0) + { + return false; + } + + if (counter.compare_exchange_strong(old, old - 1, std::memory_order_acquire, std::memory_order_relaxed)) + { + return true; + } + } +} + +bool binary_semaphore::wait_polling(binary_semaphore* self, bool (*test)(binary_semaphore*), std::chrono::nanoseconds max_elapsed) +{ + return ::wait_polling(self, test, back_off, max_elapsed); +} + +void binary_semaphore::release() +{ + #if defined(USE_ATOMIC_BOOL) + flag = false; + flag.notify_one(); + #else + flag.clear(); + flag.notify_one(); + #endif +} + +void binary_semaphore::acquire() +{ + #if defined(USE_ATOMIC_BOOL) + bool old; + while (!flag.compare_exchange_weak(old = false, true)) + { + #if defined(USE_ATOMIC_WAIT) + flag.wait(old); + #else + std::this_thread::yield(); + #endif + } + #else + bool old; + while ((old = flag.test_and_set(std::memory_order_acquire))) + { + #if defined(USE_ATOMIC_WAIT) + flag.wait(old, std::memory_order_relaxed); + #else + std::this_thread::yield(); + #endif + } + #endif +} + +bool binary_semaphore::try_acquire() +{ + #if defined(USE_ATOMIC_BOOL) + bool old = false; + return flag.compare_exchange_weak(old, true); + #else + return !flag.test_and_set(std::memory_order_acquire); + #endif +} + +bool atomic_lock::wait_polling(atomic_lock* self, bool (*test)(atomic_lock*), std::chrono::nanoseconds max_elapsed) +{ + return ::wait_polling(self, test, back_off, max_elapsed); +} + +atomic_lock::atomic_lock() : flag(false) {} + +void atomic_lock::lock() +{ + while (flag.test_and_set(std::memory_order_acquire)) + { + #if defined(USE_ATOMIC_WAIT) + flag.wait(false, std::memory_order_relaxed); + #else + std::this_thread::yield(); + #endif + } +} + +void atomic_lock::unlock() +{ + flag.clear(std::memory_order_release); +} + +bool atomic_lock::try_lock() +{ + return flag.test_and_set(std::memory_order_acquire); +} \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Echo/Atomics.hxx b/third-party/RemoteInput/RemoteInput/Echo/Atomics.hxx new file mode 100644 index 0000000..1b3b562 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Echo/Atomics.hxx @@ -0,0 +1,150 @@ +// +// Created by Brandon on 2023-12-30. +// + +#ifndef REMOTEINPUT_ATOMICS_HXX +#define REMOTEINPUT_ATOMICS_HXX + +#include +#include +#include + +class counting_semaphore +{ +private: + std::atomic_ptrdiff_t counter; + + static bool wait_polling(counting_semaphore* self, bool (*test)(counting_semaphore*), std::chrono::nanoseconds max_elapsed = std::chrono::nanoseconds::zero()); + +public: + constexpr explicit counting_semaphore(std::ptrdiff_t count) : counter(count) {} + ~counting_semaphore() = default; + + counting_semaphore(const counting_semaphore&) = delete; + counting_semaphore& operator=(const counting_semaphore&) = delete; + + void release(std::ptrdiff_t amount = 1); + void acquire(); + bool try_acquire(); + + template + bool try_acquire_for(const std::chrono::duration &relative_time) + { + auto nano_seconds = std::chrono::duration_cast(relative_time); + if (nano_seconds == std::chrono::duration::zero()) + { + return try_acquire(); + } + + const auto test = [](counting_semaphore* self) { return self->try_acquire(); }; + return wait_polling(this, test, nano_seconds); + } + + template + bool try_acquire_until(const std::chrono::time_point &absolute_time) + { + auto const current = Clock::now(); + if (current >= absolute_time) + { + return try_acquire(); + } + else + { + return try_acquire_for(absolute_time - current); + } + } +}; + +class binary_semaphore +{ +private: + #if defined(USE_ATOMIC_BOOL) + std::atomic_bool flag; + #else + std::atomic_flag flag; + #endif + + static bool wait_polling(binary_semaphore* self, bool (*test)(binary_semaphore*), std::chrono::nanoseconds max_elapsed = std::chrono::nanoseconds::zero()); + +public: + constexpr explicit binary_semaphore(bool is_set = false) : flag(!is_set) {} + ~binary_semaphore() = default; + + binary_semaphore(const binary_semaphore&) = delete; + binary_semaphore& operator=(const binary_semaphore&) = delete; + + void release(); + void acquire(); + bool try_acquire(); + + template + bool try_acquire_for(const std::chrono::duration &relative_time) + { + if (relative_time == std::chrono::duration::zero()) + { + return try_acquire(); + } + + const auto test = [](binary_semaphore* self) { return self->try_acquire(); }; + return wait_polling(this, test, std::chrono::duration_cast(relative_time)); + } + + template + bool try_acquire_until(const std::chrono::time_point &absolute_time) + { + const auto current = Clock::now(); + if (current >= absolute_time) + { + return try_acquire(); + } + else + { + return try_acquire_for(absolute_time - current); + } + } +}; + +struct atomic_lock +{ +private: + std::atomic_flag flag; + + static bool wait_polling(atomic_lock* self, bool (*test)(atomic_lock*), std::chrono::nanoseconds max_elapsed = std::chrono::nanoseconds::zero()); + +public: + atomic_lock(); + ~atomic_lock() = default; + + void lock(); + void unlock(); + + bool try_lock(); + + template + bool try_lock_for(const std::chrono::duration &relative_time) + { + if (relative_time == std::chrono::duration::zero()) + { + return try_lock(); + } + + const auto test = [](atomic_lock* self) { return self->try_lock(); }; + return wait_polling(this, test, std::chrono::duration_cast(relative_time)); + } + + template + bool try_lock_until(const std::chrono::time_point &absolute_time) + { + const auto current = Clock::now(); + if (current >= absolute_time) + { + return try_lock(); + } + else + { + return try_lock_for(absolute_time - current); + } + } +}; + +#endif //REMOTEINPUT_ATOMICS_HXX diff --git a/third-party/RemoteInput/RemoteInput/Echo/Event.cxx b/third-party/RemoteInput/RemoteInput/Echo/Event.cxx new file mode 100644 index 0000000..a5d0bd6 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Echo/Event.cxx @@ -0,0 +1,180 @@ +// +// Created by Brandon on 2023-12-30. +// + +#include "Event.hxx" + +bool atomic_signal_timedlock(std::atomic_flag* flag, const struct timespec* timeout) +{ + if (!timeout) + { + return flag->test_and_set(std::memory_order_acquire); + } + + std::chrono::nanoseconds time_out = std::chrono::seconds(timeout->tv_sec) + + std::chrono::nanoseconds(timeout->tv_nsec); + + auto start = std::chrono::high_resolution_clock::now(); + while (!flag->test_and_set(std::memory_order_acquire)) + { + std::chrono::nanoseconds elapsed_time = std::chrono::high_resolution_clock::now() - start; + + if (elapsed_time >= time_out) + { + errno = EAGAIN; + return false; + } + + std::this_thread::sleep_for(std::chrono::milliseconds(5)); + } + + return true; +} + +#if defined(_WIN32) || defined(_WIN64) +Event::Event() : hEvent(nullptr) +{ + hEvent = CreateEvent(nullptr, true, false, nullptr); +} + +Event::Event(std::string name) : hEvent(nullptr) +{ + hEvent = OpenEvent(0, false, name.c_str()); + if (!hEvent) + { + hEvent = CreateEvent(nullptr, true, false, name.c_str()); + } + + if (!hEvent) + { + throw std::runtime_error("Cannot Create or Open Event"); + } +} +#else +Event::Event() : flag(new std::atomic_flag(false)), ref(nullptr), mem_map() +{ +} + +Event::Event(std::string name) : flag(nullptr), ref(nullptr), mem_map(std::make_unique(name, sizeof(std::atomic_flag) + sizeof(std::int32_t), MemoryMap::destroy_mode::unmap)) +{ + bool created = false; + if (!mem_map->open(MemoryMap::open_mode::read | MemoryMap::open_mode::write)) + { + created = true; + if (!mem_map->open(MemoryMap::open_mode::read | MemoryMap::open_mode::write | MemoryMap::open_mode::create)) + { + perror(nullptr); + throw std::runtime_error("Cannot create or open memory map"); + } + } + + if (!mem_map->map()) + { + throw std::runtime_error("Cannot map memory"); + } + + std::atomic_flag* flag = static_cast(mem_map->data()); + std::int32_t* ref = reinterpret_cast(flag + 1); + + if (created) + { + std::construct_at(flag, false); + std::construct_at(ref, 0); + } + + *ref += 1; + this->flag = flag; + this->ref = ref; + msync(ref, sizeof(*ref), MS_SYNC); +} +#endif + +Event::~Event() +{ + #if defined(_WIN32) || defined(_WIN64) + CloseHandle(hEvent); + #else + if (mem_map && flag && mem_map->is_open() && mem_map->is_mapped()) + { + *ref -= 1; + msync(ref, sizeof(*ref), MS_SYNC); + + if (*ref == 0) + { + std::destroy_at(ref); + std::destroy_at(flag); + mem_map->close(); + } + mem_map->unmap(); + + ref = nullptr; + flag = nullptr; + } + + flag = nullptr; + ref = nullptr; + #endif +} + +bool Event::is_signalled() const noexcept +{ + #if defined(_WIN32) || defined(_WIN64) + return WaitForSingleObjectEx(hEvent, 0, true) == WAIT_OBJECT_0; + #else + return flag->test(std::memory_order_relaxed); + #endif +} + +void Event::wait() const noexcept +{ + #if defined(_WIN32) || defined(_WIN64) + WaitForSingleObject(hEvent, INFINITE); // == WAIT_OBJECT_0; + #else + while (flag->test_and_set(std::memory_order_acquire)) + { + #if defined(USE_ATOMIC_WAIT) + flag->wait(false, std::memory_order_relaxed); + #else + std::this_thread::yield(); + #endif + } + #endif +} + +void Event::signal() const noexcept +{ + #if defined(_WIN32) || defined(_WIN64) + SetEvent(hEvent); + #else + flag->clear(std::memory_order_release); + #endif +} + +bool Event::try_wait() const noexcept +{ + #if defined(_WIN32) || defined(_WIN64) + return WaitForSingleObject(hEvent, 0) == WAIT_OBJECT_0; + #else + return flag->test_and_set(std::memory_order_acquire); + #endif +} + +bool Event::timed_wait(std::uint64_t nanoseconds) const noexcept +{ + if (!nanoseconds) + { + return try_wait(); + } + + #if defined(_WIN32) || defined(_WIN64) + auto milli = std::chrono::duration_cast(std::chrono::nanoseconds(nanoseconds)); + return WaitForSingleObject(hEvent, milli.count()) == WAIT_OBJECT_0; + #else + auto duration = std::chrono::nanoseconds(nanoseconds); + auto seconds = std::chrono::duration_cast(duration); + std::chrono::nanoseconds nano_seconds = (duration - seconds); + + struct timespec ts = { seconds.count(), nano_seconds.count() }; + return atomic_signal_timedlock(flag, &ts); + #endif +} \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Echo/Event.hxx b/third-party/RemoteInput/RemoteInput/Echo/Event.hxx new file mode 100644 index 0000000..fbf276f --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Echo/Event.hxx @@ -0,0 +1,72 @@ +// +// Created by Brandon on 2023-12-30. +// + +#ifndef EVENT_HXX_INCLUDED +#define EVENT_HXX_INCLUDED + +#include +#include +#include +#include +#include + +#if defined(_WIN32) || defined(_WIN64) +#include +#endif + +#include "MemoryMap.hxx" + +class Event +{ +private: + #if defined(_WIN32) || defined(_WIN64) + HANDLE hEvent; + #else + std::atomic_flag* flag; + std::int32_t* ref; + std::unique_ptr mem_map; + #endif + + bool timed_wait(std::uint64_t nanoseconds) const noexcept; + +public: + Event(); + Event(std::string name); + ~Event(); + + bool is_signalled() const noexcept; + + void wait() const noexcept; + void signal() const noexcept; + + bool try_wait() const noexcept; + + template + bool try_wait_for(const std::chrono::duration& relative_time) const noexcept + { + auto nano_seconds = std::chrono::duration_cast(relative_time); + if (nano_seconds == std::chrono::duration::zero()) + { + return try_wait(); + } + + return timed_wait(nano_seconds.count()); + } + + template + bool try_wait_until(const std::chrono::time_point& absolute_time) const noexcept + { + const auto current = Clock::now(); + if (current >= absolute_time) + { + return try_wait(); + } + else + { + return try_wait_for(absolute_time - current); + } + } +}; + +#endif //EVENT_HXX_INCLUDED diff --git a/third-party/RemoteInput/RemoteInput/Echo/MemoryMap.cxx b/third-party/RemoteInput/RemoteInput/Echo/MemoryMap.cxx new file mode 100644 index 0000000..d02c903 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Echo/MemoryMap.cxx @@ -0,0 +1,321 @@ +// +// Created by Brandon on 2023-12-27. +// + +#include "MemoryMap.hxx" + +inline MemoryMap::open_mode operator & (MemoryMap::open_mode a, MemoryMap::open_mode b) +{ + return static_cast( + static_cast::type>(a) & + static_cast::type>(b)); +} + +inline bool operator ! (MemoryMap::open_mode mode) +{ + return !static_cast::type>(mode); +} + +#if defined(_WIN32) || defined(_WIN64) +MemoryMap::MemoryMap(std::string_view path, std::size_t size, destroy_mode destroy_option) noexcept : hFile(nullptr), hMap(nullptr), physical(false), path(path), pData(nullptr), pSize(size), mode(open_mode::read | open_mode::write | open_mode::create), destruct_mode(destroy_option) {} +#else +MemoryMap::MemoryMap(std::string_view path, std::size_t size, destroy_mode destroy_option) noexcept : hFile(-1), physical(false), path(path), pData(MAP_FAILED), pSize(size), mode(open_mode::read | open_mode::write | open_mode::create), destruct_mode(destroy_option) {} +#endif + +MemoryMap::~MemoryMap() noexcept +{ + switch (destruct_mode) + { + case destroy_mode::none: break; + case destroy_mode::unmap: unmap(); break; + case destroy_mode::close: close(); break; + case destroy_mode::unmap_and_close: unmap(); close(); break; + default: unmap(); close(); break; + } +} + +bool MemoryMap::open(open_mode mode) noexcept +{ + if (is_open() || is_mapped()) + { + return true; + } + + this->mode = mode; + this->physical = false; + bool read_only = !(mode & open_mode::write); + bool open_only = !(mode & open_mode::create); + + #if defined(_WIN32) || defined(_WIN64) + DWORD dwAccess = read_only ? PAGE_READONLY : PAGE_READWRITE; + + if (open_only) + { + hMap = OpenFileMappingA(FILE_MAP_ALL_ACCESS, false, path.c_str()); + return hMap != nullptr; + } + + hMap = CreateFileMappingA(INVALID_HANDLE_VALUE, nullptr, dwAccess, 0, pSize, path.c_str()); + return hMap != nullptr; + #else + bool created = false; + int dwFlags = read_only ? O_RDONLY : O_RDWR; + + if (open_only) + { + hFile = shm_open(path.c_str(), dwFlags, S_IRWXU | S_IRWXO); + } + else + { + created = true; + hFile = shm_open(path.c_str(), dwFlags | O_CREAT | O_EXCL, S_IRWXU | S_IRWXO); + if (hFile == -1 && errno == EEXIST) + { + created = false; + hFile = shm_open(path.c_str(), dwFlags, S_IRWXU | S_IRWXO); + } + } + + if (hFile != -1) + { + if (pSize == 0) + { + struct stat info = {0}; + if (fstat(hFile, &info) != -1) + { + pSize = info.st_size; + } + } + + if (!read_only && created) + { + if (ftruncate(hFile, pSize) != -1) + { + struct stat info = {0}; + return fstat(hFile, &info) != -1 && info.st_size >= static_cast(pSize); + } + return false; + } + return true; + } + return false; + #endif +} + +bool MemoryMap::open_file(open_mode mode) noexcept +{ + this->mode = mode; + this->physical = true; + bool read_only = !(mode & open_mode::write); + bool open_only = !(mode & open_mode::create); + + #if defined(_WIN32) || defined(_WIN64) + DWORD dwAccess = read_only ? GENERIC_READ : (GENERIC_READ | GENERIC_WRITE); + DWORD dwCreation = open_only ? OPEN_EXISTING : OPEN_ALWAYS; + DWORD dwAttributes = FILE_ATTRIBUTE_TEMPORARY & (read_only ? FILE_ATTRIBUTE_READONLY : 0); + + hFile = CreateFileA(path.c_str(), dwAccess, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, dwCreation, dwAttributes, nullptr); + + if (hFile != INVALID_HANDLE_VALUE) + { + std::uint32_t high = (static_cast(pSize) >> 32); + std::uint32_t low = (static_cast(pSize) & 0xFFFFFFFF); + if ((SetFilePointer(hFile, static_cast(low), reinterpret_cast(&high), FILE_BEGIN) != INVALID_SET_FILE_POINTER)) + { + if (!read_only && SetEndOfFile(hFile)) + { + low = GetFileSize(hFile, reinterpret_cast(&high)); + if (low != INVALID_FILE_SIZE && ((static_cast(high) << 32) | low) == pSize) + { + DWORD dwAccess = read_only ? PAGE_READONLY : PAGE_READWRITE; + + if (open_only) + { + hMap = OpenFileMappingA(FILE_MAP_ALL_ACCESS, false, path.c_str()); + return hMap != nullptr; + } + + hMap = CreateFileMappingA(hFile, nullptr, dwAccess, 0, pSize, path.c_str()); + return hMap != nullptr; + } + return false; + } + + low = GetFileSize(hFile, reinterpret_cast(&high)); + pSize = low != INVALID_FILE_SIZE ? (static_cast(high) << 32) | low : 0; + if (low != INVALID_FILE_SIZE) + { + DWORD dwAccess = read_only ? PAGE_READONLY : PAGE_READWRITE; + + if (open_only) + { + hMap = OpenFileMappingA(FILE_MAP_ALL_ACCESS, false, path.c_str()); + return hMap != nullptr; + } + + hMap = CreateFileMappingA(hFile, nullptr, dwAccess, 0, pSize, path.c_str()); + return hMap != nullptr; + } + } + } + return false; + #else + int dwFlags = read_only ? O_RDONLY : O_RDWR; + + if (open_only) + { + hFile = ::open(path.c_str(), dwFlags, S_IRWXU | S_IRWXO); + } + else + { + hFile = ::open(path.c_str(), dwFlags | O_CREAT | O_EXCL | O_TRUNC, S_IRWXU | S_IRWXO); + if (hFile == -1 && errno == EEXIST) + { + hFile = ::open(path.c_str(), dwFlags, S_IRWXU | S_IRWXO); + } + } + + if (hFile != -1) + { + if (!read_only && ftruncate(hFile, pSize) != -1) + { + struct stat info = {0}; + return fstat(hFile, &info) != -1 && info.st_size >= static_cast(pSize); + } + + pSize = 0; + struct stat info = {0}; + if (fstat(hFile, &info) != -1) + { + pSize = info.st_size; + return true; + } + } + return false; + #endif +} + +bool MemoryMap::map() noexcept +{ + if (!is_open()) + { + return false; + } + + if (is_mapped()) + { + return true; + } + + bool read_only = !(mode & open_mode::write); + #if defined(_WIN32) || defined(_WIN64) + if (pData) + { + return true; + } + + DWORD dwAccess = read_only ? FILE_MAP_READ : FILE_MAP_READ | FILE_MAP_WRITE; + pData = MapViewOfFile(hMap, dwAccess, 0, 0, pSize); + return pData != nullptr; + #else + if (pData != MAP_FAILED) + { + return true; + } + + int dwAccess = read_only ? PROT_READ : (PROT_READ | PROT_WRITE); + pData = mmap(nullptr, pSize, dwAccess, MAP_SHARED, hFile, 0); + return pData != MAP_FAILED && pData != nullptr; + #endif +} + +bool MemoryMap::unmap() noexcept +{ + bool result = true; + if (is_mapped()) + { + #if defined(_WIN32) || defined(_WIN64) + result = UnmapViewOfFile(pData); + pData = nullptr; + #else + result = !munmap(pData, pSize); + pData = nullptr; + #endif + } + return result; +} + +bool MemoryMap::close() noexcept +{ + #if defined(_WIN32) || defined(_WIN64) + bool result = CloseHandle(hMap); + hMap = nullptr; + + if (hFile != INVALID_HANDLE_VALUE) + { + result = CloseHandle(hFile) && result; + hFile = INVALID_HANDLE_VALUE; + } + return result; + #else + bool result = false; + if (physical) + { + result = ::close(hFile) != -1 && result; + hFile = -1; + } + else + { + result = !shm_unlink(path.c_str()) && result; + } + return result; + #endif +} + +bool MemoryMap::is_open() const noexcept +{ + #if defined(_WIN32) || defined(_WIN64) + return ((hMap != nullptr) && (hMap != INVALID_HANDLE_VALUE)) || ((hFile != nullptr) && (hFile != INVALID_HANDLE_VALUE)); + #else + return hFile != -1; + #endif +} + +bool MemoryMap::is_mapped() const noexcept +{ + #if defined(_WIN32) || defined(_WIN64) + return pData != nullptr; + #else + return pData != MAP_FAILED && pData != nullptr; + #endif +} + +std::size_t MemoryMap::size() const noexcept +{ + return pSize; +} + +void* MemoryMap::data() const noexcept +{ + return pData; +} + +void MemoryMap::flush() const noexcept +{ + #if defined(_WIN32) || defined(_WIN64) + FlushViewOfFile(pData, pSize); + #else + msync(pData, pSize, MS_SYNC); + #endif +} + +std::size_t MemoryMap::granularity() noexcept +{ + #if defined(_WIN32) || defined(_WIN64) + SYSTEM_INFO info = {0}; + GetSystemInfo(&info); + return info.dwAllocationGranularity; + #else + return sysconf(_SC_PAGESIZE); + #endif +} \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Echo/MemoryMap.hxx b/third-party/RemoteInput/RemoteInput/Echo/MemoryMap.hxx new file mode 100644 index 0000000..a05d5b4 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Echo/MemoryMap.hxx @@ -0,0 +1,108 @@ +// +// MemoryMap.hxx +// Echo +// +// Created by Brandon on 2019-09-14. +// Copyright © 2019 XIO. All rights reserved. +// + +#ifndef MEMORYMAP_HXX_INCLUDED +#define MEMORYMAP_HXX_INCLUDED + +#if defined(_WIN32) || defined(_WIN64) +#if !defined(WIN32_LEAN_AND_MEAN) +#define WIN32_LEAN_AND_MEAN +#endif + +#include +#else +#include +#include +#include +#include +#include +#endif + +#include +#include +#include + +class MemoryMap +{ +public: + /********************************************//** + * @brief A set of flags that determine how the file is memory + * + * @param read open_mode - Determine if the memory map should be opened for reading + * @param write open_mode - Determine if the memory map should be opened for writing + * @param create open_mode - Determine if the memory map should be created if it doesn't already exist + * + ***********************************************/ + enum class open_mode: std::uint32_t + { + read = 0x01, + write = 0x02, + create = 0x04 + }; + + /********************************************//** + * @brief A set of flags that determine how the file is closed or unmapped on destruction of this class + * + * @param none destroy_mode - Leave the memory map open and mapped + * @param unmap destroy_mode - Unmaps the memory, but leaves the file open + * @param close destroy_mode - Closes the file, but leaves the memory mapped + * @param unmap_and_close destroy_mode - Unmaps the memory, and closes the file + * + ***********************************************/ + enum class destroy_mode: std::uint32_t + { + none = 0x00, + unmap = 0x01, + close = 0x02, + unmap_and_close = 0x04 + }; + + explicit MemoryMap(std::string_view path, std::size_t size, destroy_mode destroy_option = destroy_mode::unmap_and_close) noexcept; + ~MemoryMap() noexcept; + + bool open(open_mode mode = open_mode::read | open_mode::write | open_mode::create) noexcept; + bool open_file(open_mode mode = open_mode::read | open_mode::write | open_mode::create) noexcept; + + bool map() noexcept; + bool unmap() noexcept; + + bool close() noexcept; + + bool is_open() const noexcept; + bool is_mapped() const noexcept; + + std::size_t size() const noexcept; + void* data() const noexcept; + void flush() const noexcept; + + static std::size_t granularity() noexcept; + + friend inline open_mode operator | (open_mode a, open_mode b) + { + return static_cast( + static_cast::type>(a) | + static_cast::type>(b)); + } + +private: + #if defined(_WIN32) || defined(_WIN64) + HANDLE hFile; + HANDLE hMap; + #else + int hFile; + #endif + + bool physical; + std::string path; + void* pData; + std::size_t pSize; + open_mode mode; + destroy_mode destruct_mode; +}; + +#endif /* MEMORYMAP_HXX_INCLUDED */ diff --git a/third-party/RemoteInput/RemoteInput/Echo/MemoryMapStream.hxx b/third-party/RemoteInput/RemoteInput/Echo/MemoryMapStream.hxx new file mode 100644 index 0000000..0a0c5d5 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Echo/MemoryMapStream.hxx @@ -0,0 +1,315 @@ +// +// Created by Brandon on 2023-03-19. +// + +#ifndef MEMORYMAPSTREAM_HXX_INCLUDED +#define MEMORYMAPSTREAM_HXX_INCLUDED + +#if defined(_WIN32) || defined(_WIN64) +#if !defined(WIN32_LEAN_AND_MEAN) +#define WIN32_LEAN_AND_MEAN +#endif + +#include +#else +#include +#include +#include +#include +#include +#endif + +#include + +template +class MemoryMapStream +{ +public: + /********************************************//** + * @brief A set of flags that determine how the file is memory + * + * @param read open_mode - Determine if the memory map should be opened for reading + * @param write open_mode - Determine if the memory map should be opened for writing + * @param create open_mode - Determine if the memory map should be created if it doesn't already exist + * + ***********************************************/ + enum class open_mode: std::uint32_t + { + read = 0x01, + write = 0x02, + create = 0x04 + }; + + MemoryMapStream(const std::string &path, std::size_t size, MemoryMapStream::open_mode mode); + MemoryMapStream(MemoryMapStream&& other); + MemoryMapStream(const MemoryMapStream& other) = delete; + ~MemoryMapStream(); + + T& data(); + + bool is_mapped() const noexcept; + + MemoryMapStream& operator = (MemoryMapStream&& other); + MemoryMapStream& operator = (const MemoryMapStream& other) = delete; + + friend inline open_mode operator | (open_mode a, open_mode b) + { + return static_cast( + static_cast::type>(a) | + static_cast::type>(b)); + } + +private: + bool owner; + std::string path; + void* pData; + std::size_t pSize; + MemoryMapStream::open_mode mode; + std::unique_ptr data_stream; + + #if defined(_WIN32) || defined(_WIN64) + HANDLE hFile; + #else + int hFile; + #endif + + bool open(); + bool close(); + bool map(); + bool unmap(); + + friend inline open_mode operator & (open_mode a, open_mode b) + { + return static_cast( + static_cast::type>(a) & + static_cast::type>(b)); + } + + friend inline bool operator ! (MemoryMapStream::open_mode mode) + { + return !static_cast::type>(mode); + } +}; + +#if defined(_WIN32) || defined(_WIN64) +template +MemoryMapStream::MemoryMapStream(const std::string &path, std::size_t size, MemoryMapStream::open_mode mode) : owner(false), path(path), pData(nullptr), pSize(size), mode(mode), data_stream(nullptr), hFile(nullptr) +#else +template +MemoryMapStream::MemoryMapStream(const std::string &path, std::size_t size, MemoryMapStream::open_mode mode) : owner(false), path(path), pData(MAP_FAILED), pSize(size), mode(mode), data_stream(nullptr), hFile(-1) +#endif +{ + open(); + map(); +} + +template +MemoryMapStream::MemoryMapStream(MemoryMapStream&& other) : owner(other.owner), path(other.path), pData(other.pData), pSize(other.pSize), mode(other.mode), data_stream(std::move(other.data_stream)), hFile(other.hFile) +{ + other.owner = false; + other.path = std::string(); + other.pSize = 0; + other.mode = static_cast(0); + other.data_stream = nullptr; + + #if defined(_WIN32) || defined(_WIN64) + other.pData = nullptr; + other.hFile = nullptr; + #else + other.pData = nullptr; + other.hFile = -1; + #endif +} + +template +MemoryMapStream::~MemoryMapStream() +{ + data_stream.reset(); + close(); +} + +template +MemoryMapStream& MemoryMapStream::operator = (MemoryMapStream&& other) +{ + data_stream.reset(); + close(); + std::swap(owner, other.owner); + std::swap(path, other.path); + std::swap(pData, other.pData); + std::swap(pSize, other.pSize); + std::swap(mode, other.mode); + std::swap(data_stream, other.data_stream); + std::swap(hFile, other.hFile); + return *this; +} + +template +bool MemoryMapStream::open() +{ + #if defined(_WIN32) || defined(_WIN64) + if (hFile != nullptr) + { + return true; + } + #else + if (hFile != -1) + { + return true; + } + #endif + + bool read_only = !(mode & open_mode::write); + bool open_only = !(mode & open_mode::create); + + #if defined(_WIN32) || defined(_WIN64) + DWORD dwAccess = read_only ? PAGE_READONLY : PAGE_READWRITE; + if (open_only) + { + owner = false; + hFile = OpenFileMappingA(FILE_MAP_ALL_ACCESS, false, path.c_str()); + return hFile != nullptr; + } + + owner = true; + hFile = CreateFileMappingA(INVALID_HANDLE_VALUE, nullptr, dwAccess, 0, pSize, path.c_str()); // GetLastError() == ERROR_ALREADY_EXISTS + return hFile != nullptr; + #else + int dwFlags = read_only ? O_RDONLY : O_RDWR; + + if (open_only) + { + owner = false; + hFile = shm_open(path.c_str(), dwFlags, S_IRWXU); + } + else + { + owner = true; + hFile = shm_open(path.c_str(), dwFlags | O_CREAT | O_EXCL | O_TRUNC, S_IRWXU); + if (errno == EEXIST) + { + owner = false; + hFile = shm_open(path.c_str(), dwFlags, S_IRWXU); + } + } + + if (hFile != -1) + { + if (pSize == 0) + { + struct stat info = {0}; + if (fstat(hFile, &info) != -1) + { + pSize = info.st_size; + } + } + + if (!read_only && owner) + { + if (ftruncate(hFile, pSize) != -1) + { + struct stat info = {0}; + return fstat(hFile, &info) != -1 && info.st_size >= static_cast(pSize); + } + return false; + } + return true; + } + return false; + #endif +} + +template +bool MemoryMapStream::close() +{ + bool result = unmap(); + #if defined(_WIN32) || defined(_WIN64) + result = CloseHandle(hFile) && result; + hFile = nullptr; + owner = false; + #else + if (owner) + { + result = !shm_unlink(path.c_str()) && result; + } + else + { + result = ::close(hFile) != -1 && result; + } + + hFile = -1; + owner = false; + #endif + return result; +} + +template +bool MemoryMapStream::map() +{ + #if defined(_WIN32) || defined(_WIN64) + if (pData != nullptr) + { + return true; + } + #else + if (pData != MAP_FAILED) + { + return true; + } + #endif + + bool read_only = !(mode & open_mode::write); + #if defined(_WIN32) || defined(_WIN64) + DWORD dwAccess = read_only ? FILE_MAP_READ : FILE_MAP_READ | FILE_MAP_WRITE; + pData = MapViewOfFile(hFile, dwAccess, 0, 0, pSize); + return pData != nullptr; + #else + int dwAccess = read_only ? PROT_READ : (PROT_READ | PROT_WRITE); + pData = mmap(nullptr, pSize, dwAccess, MAP_SHARED, hFile, 0); + return pData != MAP_FAILED; + #endif +} + +template +bool MemoryMapStream::unmap() +{ + bool result = true; + if (pData) + { + #if defined(_WIN32) || defined(_WIN64) + result = UnmapViewOfFile(pData); + pData = nullptr; + #else + result = !munmap(pData, pSize); + pData = nullptr; + #endif + } + return result; +} + +template +T& MemoryMapStream::data() +{ + if (!open() && !map()) + { + throw std::runtime_error("MemoryStreamMap: Cannot create data from closed map"); + } + + if (!data_stream) + { + data_stream = std::make_unique(pData, pSize); + } + + return *data_stream; +} + +template +bool MemoryMapStream::is_mapped() const noexcept +{ + #if defined(_WIN32) || defined(_WIN64) + return pData != nullptr; + #else + return pData != MAP_FAILED; + #endif +} + +#endif /* MEMORYMAPSTREAM_HXX_INCLUDED */ diff --git a/third-party/RemoteInput/RemoteInput/Echo/Module.cxx b/third-party/RemoteInput/RemoteInput/Echo/Module.cxx new file mode 100644 index 0000000..cd5cb51 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Echo/Module.cxx @@ -0,0 +1,25 @@ +// +// Module.cxx +// RemoteInput +// +// Created by Brandon on 2019-09-14. +// Copyright © 2019 XIO. All rights reserved. +// + +#include "Module.hxx" + +#if defined _WIN32 || defined _WIN64 +Module::Module(const char* path) noexcept : module(static_cast(LoadLibraryA(path))) {} +Module::Module(const wchar_t* path) noexcept : module(static_cast(LoadLibraryW(path))) {} + +Module::~Module() noexcept {FreeLibrary(static_cast(module));} +#else +Module::Module(const char* path) noexcept : module(dlopen(path, RTLD_LAZY | RTLD_GLOBAL)) {} +Module::Module(const wchar_t* path) noexcept : module(nullptr) +{ + std::string utf8 = std::wstring_convert>().to_bytes(path); + module = dlopen(utf8.c_str(), RTLD_LAZY | RTLD_GLOBAL); +} + +Module::~Module() noexcept {dlclose(module);} +#endif diff --git a/third-party/RemoteInput/RemoteInput/Echo/Module.hxx b/third-party/RemoteInput/RemoteInput/Echo/Module.hxx new file mode 100644 index 0000000..120d850 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Echo/Module.hxx @@ -0,0 +1,194 @@ +// +// Module.hxx +// RemoteInput +// +// Created by Brandon on 2019-09-14. +// Copyright © 2019 XIO. All rights reserved. +// + +#ifndef MODULE_HXX_INCLUDED +#define MODULE_HXX_INCLUDED + +#if defined _WIN32 || defined _WIN64 +#if !defined(WIN32_LEAN_AND_MEAN) +#define WIN32_LEAN_AND_MEAN +#endif + +#include +#else +#include +#endif + +#include +#include +#include +#include + +/********************************************//** + * @brief Wraps the loading of modules/shared-libraries + ***********************************************/ +class Module +{ +private: + void* module; + +public: + /********************************************//** + * @brief Loads a module/shared-library. + * + * @param path const char* Path to the module to be loaded. + * + ***********************************************/ + Module(const char* path) noexcept; + + + /********************************************//** + * @brief Loads a module/shared-library. + * + * @param path const wchar_t* Path to the module to be loaded. + * + ***********************************************/ + Module(const wchar_t* path) noexcept; + + + /********************************************//** + * @brief Releases the module. + ***********************************************/ + ~Module() noexcept; + + + + /********************************************//** + * @brief Copy constructor explicitly deleted. Modules cannot be "copied". + * + * @param other const Module& - Module to copy. + * + ***********************************************/ + Module(const Module& other) = delete; + + + /********************************************//** + * @brief Move constructs a module from another module. Leaves the other module in a destructible state. + * + * @param other.module - Module to be moved into this container. + * + ***********************************************/ + Module(Module&& other) noexcept : module(other.module) { other.module = nullptr; } + + + /********************************************//** + * @brief When this class is casted to a void*. + * + * @return void* - Handle to the underlying module. + * + ***********************************************/ + explicit operator void*() const noexcept { return module; } + + + /********************************************//** + * @brief When this class is casted to a bool. + * + * @return bool - Boolean indicating whether the module was loaded successfully. + * + ***********************************************/ + explicit operator bool() const noexcept { return module != nullptr; } + + + /********************************************//** + * @brief Returns the loaded state of the underlying module. + * + * @return bool - Boolean indicating whether the module was loaded successfully. + * + ***********************************************/ + bool IsLoaded() const noexcept { return module != nullptr; } + + + /********************************************//** + * @brief Retrieves the address of a function within the underlying module. + * + * @param FunctionName const std::string& - Name of the function whose address is to be retrieved. + * @return T - Casts the address to type T and returns the result. + * + ***********************************************/ + template + T AddressOf(const std::string &FunctionName) const; + + + /********************************************//** + * @brief Retrieves the address of a function within the underlying module. + * + * @param FunctionDefinition T& - Reference to a pointer that the address will be stored in. + * @param FunctionName const std::string& - Name of the function whose address is to be retrieved. + * @return bool - True if the function exists in the module, false otherwise. + * + ***********************************************/ + template + bool AddressOf(T &FunctionDefinition, const std::string &FunctionName) const noexcept; + + + /********************************************//** + * @brief Calls a function via pointer and passes the specified arguments to it. + * + * @param func void* - Address of the function to be called. + * @param args Args... - Arguments to pass to the function pointer. + * + * Uses the __stdcall convention! + * + ***********************************************/ + template + void Call(void* func, Args... args) const noexcept; + + + /********************************************//** + * @brief Calls a function via pointer, passes the specified arguments to it and returns the result of the function call. + * + * @param func void* - Address of the function to be called. + * @param args Args... - Arguments to pass to the function pointer. + * @return R - Result of the function call. + * + * Uses the __stdcall convention! + * + ***********************************************/ + template + R Call(void* func, Args... args) const noexcept; +}; + + +template +T Module::AddressOf(const std::string &Name) const +{ + #if defined _WIN32 || defined _WIN64 + T Result = reinterpret_cast(GetProcAddress(static_cast(module), Name.c_str())); + #else + T Result = reinterpret_cast(dlsym(module, Name.c_str())); + #endif + return Result ? Result : throw std::runtime_error(Name); +} + +template +bool Module::AddressOf(T &Definition, const std::string &Name) const noexcept +{ + return (Definition = AddressOf(Name)); +} + +template +void Module::Call(void* func, Args... args) const noexcept +{ + #if defined(_WIN32) + return reinterpret_cast(func)(std::forward(args)...); + #else + return reinterpret_cast(func)(std::forward(args)...); + #endif +} + +template +R Module::Call(void* func, Args... args) const noexcept +{ + #if defined(_WIN32) + return reinterpret_cast(func)(std::forward(args)...); + #else + return reinterpret_cast(func)(std::forward(args)...); + #endif +} + +#endif /* MODULE_HXX_INCLUDED */ diff --git a/third-party/RemoteInput/RemoteInput/Echo/Mutex.cxx b/third-party/RemoteInput/RemoteInput/Echo/Mutex.cxx new file mode 100644 index 0000000..c052423 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Echo/Mutex.cxx @@ -0,0 +1,229 @@ +// +// Mutex.cxx +// RemoteInput +// +// Created by Brandon on 2019-09-14. +// Copyright © 2019 XIO. All rights reserved. +// + +#include "Mutex.hxx" + +#if !defined(_WIN32) && !defined(_WIN64) && !(_POSIX_TIMEOUTS >= 200112L) +int pthread_mutex_timedlock(pthread_mutex_t* mutex, const struct timespec* timeout) +{ + if (!timeout) + { + return pthread_mutex_trylock(mutex); + } + + int retcode = 0; + std::chrono::nanoseconds time_out = std::chrono::seconds(timeout->tv_sec) + + std::chrono::nanoseconds(timeout->tv_nsec); + + auto start = std::chrono::high_resolution_clock::now(); + while ((retcode = pthread_mutex_trylock(mutex)) == EBUSY) + { + std::chrono::nanoseconds elapsed_time = std::chrono::high_resolution_clock::now() - start; + + if (elapsed_time >= time_out) + { + errno = EAGAIN; + return -1; + } + + std::this_thread::sleep_for(std::chrono::milliseconds(5)); + } + + return retcode; +} +#endif + + +#if defined (_WIN32) || defined(_WIN64) +Mutex::Mutex() : hMutex(INVALID_HANDLE_VALUE) +{ + hMutex = CreateMutex(0, false, nullptr); + + if (!hMutex) + { + throw std::runtime_error("Cannot create mutex"); + } +} + +Mutex::Mutex(const std::string &name) : hMutex(INVALID_HANDLE_VALUE) +{ + hMutex = OpenMutex(0, false, name.c_str()); + if (!hMutex) + { + hMutex = CreateMutex(0, false, name.c_str()); + } + + if (!hMutex) + { + throw std::runtime_error("Cannot create mutex"); + } +} + +Mutex::~Mutex() +{ + CloseHandle(hMutex); +} +#else +Mutex::Mutex() : mutex(nullptr), ref(nullptr), mem_map() +{ + pthread_mutexattr_t attributes; + if (pthread_mutexattr_init(&attributes)) + { + throw std::runtime_error("Cannot allocated mutex"); + } + + if (pthread_mutexattr_setpshared(&attributes, PTHREAD_PROCESS_PRIVATE)) + { + pthread_mutexattr_destroy(&attributes); + throw std::runtime_error("Cannot set mutex private attributes"); + } + + mutex = new pthread_mutex_t(); + if (pthread_mutex_init(mutex, &attributes)) + { + pthread_mutexattr_destroy(&attributes); + delete mutex; + throw std::runtime_error("Cannot create mutex"); + } + + pthread_mutexattr_destroy(&attributes); +} + +Mutex::Mutex(const std::string &name) : mutex(nullptr), ref(nullptr), mem_map(std::make_unique(name, sizeof(pthread_mutex_t) + sizeof(std::int32_t))) +{ + bool created = false; + if (!mem_map->open(MemoryMap::open_mode::read | MemoryMap::open_mode::write)) + { + created = true; + if (!mem_map->open(MemoryMap::open_mode::read | MemoryMap::open_mode::write | MemoryMap::open_mode::create)) + { + perror(nullptr); + throw std::runtime_error("Cannot create or open memory map"); + } + } + + if (!mem_map->map()) + { + throw std::runtime_error("Cannot map memory"); + } + + pthread_mutex_t* mutex = static_cast(mem_map->data()); + std::int32_t* ref = reinterpret_cast(mutex + 1); + + if (created) + { + pthread_mutexattr_t attributes; + if (pthread_mutexattr_init(&attributes)) + { + throw std::runtime_error("Cannot create mutex attributes"); + } + + if (pthread_mutexattr_setpshared(&attributes, PTHREAD_PROCESS_SHARED)) + { + pthread_mutexattr_destroy(&attributes); + throw std::runtime_error("Cannot set mutex shared attributes"); + } + + if (pthread_mutex_init(mutex, &attributes)) + { + pthread_mutexattr_destroy(&attributes); + throw std::runtime_error("Cannot create mutex"); + } + + pthread_mutexattr_destroy(&attributes); + } + + *ref += 1; + this->mutex = mutex; + this->ref = ref; +} + +Mutex::~Mutex() +{ + if (mutex) + { + if (mem_map->is_open() && mem_map->is_mapped()) + { + *ref -= 1; + if (*ref == 0) + { + pthread_mutex_destroy(mutex); + mem_map->close(); + } + mem_map->unmap(); + } + else + { + pthread_mutex_destroy(mutex); + delete mutex; + } + } + + mutex = nullptr; + ref = nullptr; +} +#endif + +void Mutex::lock() const +{ + #if defined(_WIN32) || defined(_WIN64) + if (WaitForSingleObject(hMutex, INFINITE) != WAIT_OBJECT_0) + { + throw std::system_error(GetLastError(), std::system_category()); + } + #else + if (pthread_mutex_lock(mutex)) + { + throw std::system_error(errno, std::system_category()); + } + #endif +} + +void Mutex::unlock() const +{ + #if defined(_WIN32) || defined(_WIN64) + if (!ReleaseMutex(hMutex)) + { + throw std::system_error(GetLastError(), std::system_category()); + } + #else + if (pthread_mutex_unlock(mutex)) + { + throw std::system_error(errno, std::system_category()); + } + #endif +} + +bool Mutex::try_lock() const noexcept +{ + #if defined(_WIN32) || defined(_WIN64) + return WaitForSingleObject(hMutex, 0) == WAIT_OBJECT_0; + #else + return mutex && !pthread_mutex_trylock(mutex); + #endif +} + +bool Mutex::timed_lock(std::uint64_t nanoseconds) const noexcept +{ + if (!nanoseconds) + { + return try_lock(); + } + + #if defined(_WIN32) || defined(_WIN64) + auto milli = std::chrono::duration_cast(std::chrono::nanoseconds(nanoseconds)); + return WaitForSingleObject(hMutex, milli.count()) == WAIT_OBJECT_0; + #else + auto duration = std::chrono::nanoseconds(nanoseconds); + auto seconds = std::chrono::duration_cast(duration); + std::chrono::nanoseconds nano_seconds = (duration - seconds); + + struct timespec ts = { seconds.count(), nano_seconds.count() }; + return !pthread_mutex_timedlock(mutex, &ts); + #endif +} diff --git a/third-party/RemoteInput/RemoteInput/Echo/Mutex.hxx b/third-party/RemoteInput/RemoteInput/Echo/Mutex.hxx new file mode 100644 index 0000000..1c90bea --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Echo/Mutex.hxx @@ -0,0 +1,90 @@ +// +// Mutex.hxx +// RemoteInput +// +// Created by Brandon on 2019-09-14. +// Copyright © 2019 XIO. All rights reserved. +// + +#ifndef MUTEX_HXX_INCLUDED +#define MUTEX_HXX_INCLUDED + +#include +#include +#include +#include +#include +#include + +#if defined(_WIN32) || defined(_WIN64) +#include +#endif + +#if defined(__APPLE__) +#include +#endif + +#if !defined(_WIN32) && !defined(_WIN64) +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#include "MemoryMap.hxx" + +class Mutex +{ +private: + #if defined(_WIN32) || defined(_WIN64) + HANDLE hMutex; + #else + pthread_mutex_t* mutex; + std::int32_t* ref; + std::unique_ptr mem_map; + #endif + + bool timed_lock(std::uint64_t nanoseconds) const noexcept; + +public: + Mutex(); + Mutex(const std::string &name); + ~Mutex(); + + Mutex(const Mutex &other) = delete; + Mutex& operator = (const Mutex &other) = delete; + + void lock() const; + void unlock() const; + + bool try_lock() const noexcept; + + template + bool try_lock_for(const std::chrono::duration& duration) const noexcept + { + if (duration == std::chrono::duration::zero()) + { + return try_lock(); + } + + return timed_lock(std::chrono::duration_cast(duration)); + } + + template + bool try_lock_until(const std::chrono::time_point& absolute_time) const noexcept + { + auto const current = Clock::now(); + if (current >= absolute_time) + { + return try_lock(); + } + + return try_lock_for(absolute_time - current); + } +}; + +#endif /* MUTEX_HXX_INCLUDED */ diff --git a/third-party/RemoteInput/RemoteInput/Echo/Semaphore.cxx b/third-party/RemoteInput/RemoteInput/Echo/Semaphore.cxx new file mode 100644 index 0000000..aa7fe44 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Echo/Semaphore.cxx @@ -0,0 +1,231 @@ +// +// Semaphore.cxx +// RemoteInput +// +// Created by Brandon on 2019-09-14. +// Copyright © 2019 XIO. All rights reserved. +// + +#include "Semaphore.hxx" + +#if !defined(_WIN32) && !defined(_WIN64) && !(_POSIX_TIMEOUTS >= 200112L) +int sem_timedwait(sem_t* sem, const struct timespec* timeout) +{ + if (!timeout) + { + return sem_trywait(sem); + } + + int retcode = 0; + std::chrono::nanoseconds time_out = std::chrono::seconds(timeout->tv_sec) + + std::chrono::nanoseconds(timeout->tv_nsec); + + auto start = std::chrono::high_resolution_clock::now(); + while ((retcode = sem_trywait(sem)) != 0) + { + std::chrono::nanoseconds elapsed_time = std::chrono::high_resolution_clock::now() - start; + + if (elapsed_time >= time_out) + { + errno = EAGAIN; + return -1; + } + + std::this_thread::sleep_for(std::chrono::milliseconds(5)); + } + + return retcode; +} +#endif + +#if defined(_WIN32) || defined(_WIN64) +Semaphore::Semaphore(std::int32_t count) +{ + hSemaphore = OpenSemaphore(SEMAPHORE_ALL_ACCESS, false, nullptr); + if (!hSemaphore) + { + hSemaphore = CreateSemaphore(nullptr, count, LONG_MAX, nullptr); + } + + if (!hSemaphore) + { + throw std::runtime_error("Cannot create semaphore"); + } +} + +Semaphore::Semaphore(const std::string &name, std::int32_t count) +{ + hSemaphore = OpenSemaphore(SEMAPHORE_ALL_ACCESS, false, name.c_str()); + if (!hSemaphore) + { + hSemaphore = CreateSemaphore(nullptr, count, LONG_MAX, name.c_str()); + } + + if (!hSemaphore) + { + throw std::runtime_error("Cannot create semaphore"); + } +} + +Semaphore::~Semaphore() +{ + CloseHandle(hSemaphore); +} +#else +Semaphore::Semaphore(std::int32_t count) : shared(false), owned(true), hSem(SEM_FAILED), name() +{ + #if defined(__APPLE__) + hSem = dispatch_semaphore_create(count); + if (hSem == nullptr) + { + throw std::runtime_error("Cannot create or open semaphore"); + } + #else + hSem = new sem_t(); + if (sem_init(static_cast(hSem), false, count) == -1) + { + throw std::runtime_error("Cannot initialize semaphore"); + } + + if (hSem == SEM_FAILED) + { + throw std::runtime_error("Cannot create or open semaphore"); + } + #endif +} + +Semaphore::Semaphore(const std::string &name, std::int32_t count) : shared(true), owned(true), hSem(SEM_FAILED), name(name) +{ + hSem = sem_open(name.c_str(), O_CREAT | O_EXCL | O_RDWR, S_IRWXU, count); + if (hSem == SEM_FAILED && errno == EEXIST) + { + owned = false; + hSem = sem_open(name.c_str(), O_RDWR, S_IRWXU, count); + } + + if (hSem == SEM_FAILED) + { + throw std::runtime_error("Cannot create or open semaphore"); + } +} + +Semaphore::~Semaphore() +{ + #if defined(__APPLE__) + if (owned && !shared && hSem != nullptr) + { + dispatch_release(static_cast(hSem)); + return; + } + #endif + + if (hSem != SEM_FAILED) + { + if (!shared && name.empty()) + { + #if !defined(__APPLE__) + sem_destroy(static_cast(hSem)); + #endif + delete static_cast(hSem); + hSem = SEM_FAILED; + } + else + { + sem_close(static_cast(hSem)); + hSem = SEM_FAILED; + } + + if (owned && shared && !name.empty()) + { + sem_unlink(name.c_str()); + } + } +} +#endif + +void Semaphore::acquire() const +{ + #if defined(_WIN32) || defined(_WIN64) + if (WaitForSingleObject(hSemaphore, INFINITE) != WAIT_OBJECT_0) + { + throw std::system_error(GetLastError(), std::system_category()); + } + #elif defined(__APPLE__) + if (owned && !shared) + { + if (!dispatch_semaphore_wait(static_cast(hSem), DISPATCH_TIME_FOREVER)) + { + throw std::system_error(errno, std::system_category()); + } + } + #else + if (sem_wait(static_cast(hSem))) + { + throw std::system_error(errno, std::system_category()); + } + #endif +} + +void Semaphore::release() const +{ + #if defined(_WIN32) || defined(_WIN64) + if (!ReleaseSemaphore(hSemaphore, 1, nullptr)) + { + throw std::system_error(GetLastError(), std::system_category()); + } + #elif defined(__APPLE__) + if (owned && !shared) + { + if (dispatch_semaphore_signal(static_cast(hSem))) + { + throw std::system_error(errno, std::system_category()); + } + } + #else + if (sem_post(static_cast(hSem))) + { + throw std::system_error(errno, std::system_category()); + } + #endif +} + +bool Semaphore::try_acquire() const noexcept +{ + #if defined(_WIN32) || defined(_WIN64) + return WaitForSingleObject(hSemaphore, 0) == WAIT_OBJECT_0; + #elif defined(__APPLE__) + if (owned && !shared) + { + return !dispatch_semaphore_wait(static_cast(hSem), DISPATCH_TIME_NOW); + } + #else + return !sem_trywait(static_cast(hSem)); + #endif +} + +bool Semaphore::timed_acquire(std::uint64_t nanoseconds) const noexcept +{ + if (!nanoseconds) + { + return try_acquire(); + } + + #if defined(_WIN32) || defined(_WIN64) + auto milli = std::chrono::duration_cast(std::chrono::nanoseconds(nanoseconds)); + return WaitForSingleObject(hSemaphore, milli.count()) == WAIT_OBJECT_0; + #elif defined(__APPLE__) + if (owned && !shared) + { + auto milli = std::chrono::duration_cast(std::chrono::nanoseconds(nanoseconds)); + dispatch_time_t time_out = dispatch_time(DISPATCH_TIME_NOW, milli.count()); + return !dispatch_semaphore_wait(static_cast(hSem), time_out); + } + #else + auto duration = std::chrono::nanoseconds(nanoseconds); + auto seconds = std::chrono::duration_cast(duration); + std::chrono::nanoseconds nano_seconds = (duration - seconds); + + struct timespec ts = { seconds.count(), nano_seconds.count() }; + return !sem_timedwait(static_cast(hSem), &ts); + #endif +} diff --git a/third-party/RemoteInput/RemoteInput/Echo/Semaphore.hxx b/third-party/RemoteInput/RemoteInput/Echo/Semaphore.hxx new file mode 100644 index 0000000..fa25d44 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Echo/Semaphore.hxx @@ -0,0 +1,91 @@ +// +// Semaphore.cxx +// RemoteInput +// +// Created by Brandon on 2019-09-14. +// Copyright © 2019 XIO. All rights reserved. +// + +#ifndef SEMAPHORE_HXX_INCLUDED +#define SEMAPHORE_HXX_INCLUDED + +#include +#include +#include +#include +#include +#include + +#if defined(_WIN32) || defined(_WIN64) +#include +#endif + +#if defined(__APPLE__) +#include +#endif + +#if !defined(_WIN32) && !defined(_WIN64) +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#include "MemoryMap.hxx" + +class Semaphore +{ +private: + #if defined(_WIN32) || defined(_WIN64) + HANDLE hSemaphore; + #else + bool shared; + bool owned; + void* hSem; + std::string name; + #endif + + bool timed_acquire(std::uint64_t nanoseconds) const noexcept; + +public: + Semaphore(std::int32_t count = 0); + Semaphore(const std::string &name, std::int32_t count = 0); + ~Semaphore(); + + Semaphore(const Semaphore &other) = delete; + Semaphore& operator = (const Semaphore &other) = delete; + + void acquire() const; + void release() const; + + bool try_acquire() const noexcept; + + template + bool try_acquire_for(const std::chrono::duration& duration) const noexcept + { + if (duration == std::chrono::duration::zero()) + { + return try_acquire(); + } + + return timed_acquire(std::chrono::duration_cast(duration)); + } + + template + bool try_acquire_until(const std::chrono::time_point& absolute_time) const noexcept + { + auto const current = Clock::now(); + if (current >= absolute_time) + { + return try_acquire(); + } + + return try_acquire_for(absolute_time - current); + } +}; + +#endif //SEMAPHORE_HXX_INCLUDED diff --git a/third-party/RemoteInput/RemoteInput/Echo/Stream.cxx b/third-party/RemoteInput/RemoteInput/Echo/Stream.cxx new file mode 100644 index 0000000..a28787e --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Echo/Stream.cxx @@ -0,0 +1,49 @@ +// +// Created by Brandon on 2020-12-12. +// + +#include "Stream.hxx" + +void Stream::read(void* value, std::size_t size) +{ + DirectStream::read(reinterpret_cast(value), size); +} + +void Stream::write(const void* value, std::size_t size) +{ + DirectStream::write(reinterpret_cast(value), size); +} + +std::streampos Stream::tellg() +{ + return DirectStream::tellg(); +} + +std::streampos Stream::tellp() +{ + return DirectStream::tellp(); +} + +Stream& Stream::seekg(std::streamoff off, std::ios_base::seekdir dir) +{ + DirectStream::seekg(off, dir); + return *this; +} + +Stream& Stream::seekp(std::streamoff off, std::ios_base::seekdir dir) +{ + DirectStream::seekp(off, dir); + return *this; +} + +Stream& Stream::operator << (const char* value) +{ + write(value); + return *this; +} + +Stream& Stream::operator >> (char* value) +{ + read(value); + return *this; +} \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Echo/Stream.hxx b/third-party/RemoteInput/RemoteInput/Echo/Stream.hxx new file mode 100644 index 0000000..1c60503 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Echo/Stream.hxx @@ -0,0 +1,348 @@ +// +// Created by Brandon on 2020-12-12. +// + +#ifndef REMOTEINPUT_STREAM_HXX +#define REMOTEINPUT_STREAM_HXX + +#include +#include +#include +#include +#include +#include +#include +#include + +#if __cplusplus >= 202002L +#include +#endif + +template> +class DirectStream : private std::basic_streambuf, public std::basic_iostream +{ +public: + typedef typename traits_type::pos_type pos_type; + typedef typename traits_type::off_type off_type; + + DirectStream(char_type* buffer, std::size_t buffer_size) + : std::basic_streambuf(), + std::basic_iostream(this), std::ios(0) + { + std::basic_streambuf::pubsetbuf(nullptr, 0); + std::basic_streambuf::setp(buffer, buffer + buffer_size); + std::basic_streambuf::setg(buffer, buffer, buffer + buffer_size); + std::basic_iostream::init(this); + } + +protected: + pos_type seekpos(pos_type pos, std::ios_base::openmode which) + { + return seekoff(pos - pos_type(off_type(0)), std::ios_base::beg, which); + } + + pos_type seekoff(off_type off, std::ios_base::seekdir dir, std::ios_base::openmode which) + { + typedef std::basic_streambuf this_type; + + if (which & std::ios_base::in) + { + if (dir == std::ios_base::beg) + { + this_type::setg(this_type::eback(), this_type::eback() + off, this_type::egptr()); + } + else if (dir == std::ios_base::cur) + { + // this_type::gbump(off); + this_type::setg(this_type::eback(), this_type::gptr() + off, this_type::egptr()); + } + else if (dir == std::ios_base::end) + { + this_type::setg(this_type::eback(), this_type::egptr() + off, this_type::egptr()); + } + + return this_type::gptr() - this_type::eback(); + } + + if (which & std::ios_base::out) + { + if (dir == std::ios_base::beg) + { + this_type::setp(this_type::pbase() + off, this_type::epptr()); + } + else if (dir == std::ios_base::cur) + { + //this_type::pbump(off); + this_type::setp(this_type::pptr() + off, this_type::epptr()); + } + else if (dir == std::ios_base::end) + { + this_type::setp(this_type::epptr() + off, this_type::epptr()); + } + return this_type::pptr() - this_type::pbase(); + } + + return pos_type(off_type(-1)); + } +}; + + +class Stream : private DirectStream +{ +public: + Stream(void* buffer, std::size_t buffer_size) : DirectStream(static_cast(buffer), buffer_size) {} + + // MARK: - Read + + template + void read(T (&value)[size]); + + template + typename std::enable_if::value, void>::type read(T &value); + + template + typename std::enable_if::value, void>::type read(T* value); + + void read(void* value, std::size_t size); + + template class U, typename... Args> + U read(); + + template + T read(); + + // MARK: - Write + + template + void write(const T (&value)[size]); + + template + typename std::enable_if::value, void>::type write(const T &value); + + template + typename std::enable_if::value, void>::type write(const T* value); + + void write(const void* value, std::size_t size); + + template class U, typename... Args> + void write(const U &value); + + // MARK: - Stream Positions + + std::streampos tellg(); + std::streampos tellp(); + Stream& seekg(std::streamoff off, std::ios_base::seekdir dir = std::ios_base::beg); + Stream& seekp(std::streamoff off, std::ios_base::seekdir dir = std::ios_base::beg); + + // MARK: - Fundamental Types + + template + typename std::enable_if::value + || std::is_enum::value + || std::is_pointer::value + || std::is_member_pointer::value + || std::is_null_pointer::value, Stream&>::type operator << (const T &value); + + template + typename std::enable_if::value + || std::is_enum::value + || std::is_pointer::value + || std::is_member_pointer::value + || std::is_null_pointer::value, Stream&>::type operator >> (T &value); + + // MARK: - String types + + Stream& operator << (const char* value); + Stream& operator >> (char* value); + + + // MARK: - Container types + + template class U, typename... Args> + Stream& operator << (const U &value); + + template class U, typename... Args> + Stream& operator >> (U &value); + + template + Stream& operator << (const std::array &value); + + template + Stream& operator >> (std::array &value); + + #if __cplusplus >= 202002L + template + Stream& operator << (const std::span &value); + + template + Stream& operator >> (std::span &value) = delete; + #endif +}; + +template +void Stream::read(T (&value)[size]) +{ + std::size_t read_size = read(); + DirectStream::read(reinterpret_cast(&value), std::min(read_size, size) * sizeof(T)); +} + +template +typename std::enable_if::value, void>::type Stream::read(T &value) +{ + DirectStream::read(reinterpret_cast(&value), sizeof(T)); +} + +template +typename std::enable_if::value, void>::type Stream::read(T* value) +{ + std::size_t size = 0; + read(size); + read(value, size + 1); +} + +template class U, typename... Args> +U Stream::read() +{ + U result; + *this >> result; + return result; +} + +template +T Stream::read() +{ + T result; + *this >> result; + return result; +} + +template +void Stream::write(const T (&value)[size]) +{ + *this << size; + DirectStream::write(reinterpret_cast(&value), size * sizeof(T)); +} + +template +typename std::enable_if::value, void>::type Stream::write(const T &value) +{ + DirectStream::write(reinterpret_cast(&value), sizeof(T)); +} + +template +typename std::enable_if::value, void>::type Stream::write(const T* value) +{ + std::size_t size = strlen(value); + write(size); + write(value, size); +} + +template class U, typename... Args> +void Stream::write(const U &value) +{ + *this << value; +} + +template +typename std::enable_if::value + || std::is_enum::value + || std::is_pointer::value + || std::is_member_pointer::value + || std::is_null_pointer::value, Stream&>::type Stream::operator << (const T &value) +{ + write(value); + return *this; +} + +template +typename std::enable_if::value + || std::is_enum::value + || std::is_pointer::value + || std::is_member_pointer::value + || std::is_null_pointer::value, Stream&>::type Stream::operator >> (T &value) +{ + read(value); + return *this; +} + +template class U, typename... Args> +Stream& Stream::operator << (const U &value) +{ + *this << std::size(value); + for (const auto &it : value) + { + *this << it; + } + return *this; +} + +template class U, typename... Args> +Stream& Stream::operator >> (U &value) +{ + typename U::size_type size = 0; + *this >> size; + value.reserve(size); + + for (typename U::size_type i = 0; i < size; ++i) + { + T temp; + *this >> temp; + + if constexpr(std::is_same, U>::value) + { + value += std::move(temp); + } + else + { + value.emplace_back(std::move(temp)); + } + } + return *this; +} + +template +Stream& Stream::operator << (const std::array &value) +{ + *this << Size; + for (const auto &it : value) + { + *this << it; + } + return *this; +} + +template +Stream& Stream::operator >> (std::array &value) +{ + std::size_t real_size = 0; + *this >> real_size; + + if (real_size != Size) + { + throw std::runtime_error("Stream >> Reading Array of wrong size!"); + return *this; + } + + for (std::size_t i = 0; i < real_size; ++i) + { + T temp; + *this >> temp; + value[i] = std::move(temp); + } + return *this; +} + +#if __cplusplus >= 202002L +template +Stream& Stream::operator << (const std::span &value) +{ + *this << Extent; + for (const auto &it : value) + { + *this << it; + } + return *this; +} +#endif + +#endif // REMOTEINPUT_STREAM_HXX diff --git a/third-party/RemoteInput/RemoteInput/Echo/Synchronization.hxx b/third-party/RemoteInput/RemoteInput/Echo/Synchronization.hxx new file mode 100644 index 0000000..3c034d3 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Echo/Synchronization.hxx @@ -0,0 +1,209 @@ +// +// Created by Brandon on 2023-12-30. +// + +#ifndef REMOTEINPUT_SYNCHRONIZATION_HXX +#define REMOTEINPUT_SYNCHRONIZATION_HXX + +#include +#include +#include + +#include "MemoryMap.hxx" + +namespace synchronization +{ + // MARK: - Locks + template + concept lockable = requires(T t) + { + t.lock(); + t.unlock(); + }; + + template + concept try_lockable = lockable && requires(T t) + { + t.try_lock(); + }; + + template + concept timed_lockable = try_lockable && requires(T t) + { + t.try_lock_for(std::chrono::milliseconds(0)); + t.try_lock_until(std::chrono::high_resolution_clock::now()); + }; + + // MARK: - Semaphores + template + concept releasable = requires(T t) + { + t.release(1); + }; + + template + concept binary_releasable = requires(T t) + { + t.release(); + }; + + template + concept acquirable = (releasable || binary_releasable) && requires(T t) + { + t.acquire(); + }; + + template + concept try_acquirable = acquirable && requires(T t) + { + t.try_acquire(); + }; + + template + concept timed_acquirable = try_acquirable && requires(T t) + { + t.try_acquire_for(std::chrono::milliseconds(0)); + t.try_acquire_until(std::chrono::high_resolution_clock::now()); + }; +}; + +namespace synchronization +{ + template + class SyncPrimitive + { + private: + T* primitive; + std::int32_t* ref; + std::unique_ptr mem_map; + + public: + template + SyncPrimitive(std::string_view name, Args&&... args) requires lockable || acquirable + : primitive(nullptr), ref(nullptr), mem_map(std::make_unique(name, sizeof(T) + sizeof(std::int32_t), MemoryMap::destroy_mode::unmap)) + { + bool created = false; + if (!mem_map->open(MemoryMap::open_mode::read | MemoryMap::open_mode::write)) + { + if (!mem_map->open(MemoryMap::open_mode::read | MemoryMap::open_mode::write | MemoryMap::open_mode::create)) + { + perror(nullptr); + throw std::runtime_error("Cannot create or open memory map"); + } + + created = true; + } + + if (!mem_map->map()) + { + throw std::runtime_error("Cannot map memory"); + } + + T* primitive = static_cast(mem_map->data()); + std::int32_t* ref = reinterpret_cast(primitive + 1); + + if (created) + { + std::construct_at(primitive, std::forward(args)...); + std::construct_at(ref, 0); + } + + *ref += 1; + this->primitive = primitive; + this->ref = ref; + } + + ~SyncPrimitive() + { + if (primitive && mem_map->is_open() && mem_map->is_mapped()) + { + *ref -= 1; + if (*ref == 0) + { + std::destroy_at(ref); + std::destroy_at(primitive); + mem_map->close(); + } + mem_map->unmap(); + } + + primitive = nullptr; + ref = nullptr; + } + + SyncPrimitive(const SyncPrimitive &other) = delete; + SyncPrimitive& operator = (const SyncPrimitive &other) = delete; + + // MARK: - Lock + + void lock() const noexcept requires lockable + { + primitive->lock(); + } + + void unlock() const noexcept requires lockable + { + primitive->unlock(); + } + + bool try_lock() const noexcept requires try_lockable + { + return primitive->try_lock(); + } + + template + bool try_lock_for(const std::chrono::duration& relative_time) const noexcept requires timed_lockable + { + return primitive->try_lock_for(relative_time); + } + + template + bool try_lock_until(const std::chrono::time_point& absolute_time) const noexcept requires timed_lockable + { + return primitive->try_lock_until(absolute_time); + } + + template + bool try_lock_until(const std::chrono::time_point& absolute_time) const noexcept requires timed_lockable + { + return primitive->try_lock_until(absolute_time); + } + + // MARK: - Semaphore + + void acquire() const noexcept requires acquirable + { + primitive->acquire(); + } + + void release() const noexcept requires acquirable + { + primitive->release(); + } + + bool try_acquire() const noexcept requires try_acquirable + { + return primitive->try_acquire(); + } + + template + bool try_acquire_for(const std::chrono::duration& relative_time) const noexcept requires timed_acquirable + { + return primitive->try_acquire_for(relative_time); + } + + template + bool try_acquire_until(const std::chrono::time_point& absolute_time) const noexcept requires timed_acquirable + { + return primitive->try_acquire_until(absolute_time); + } + + template + bool try_acquire_until(const std::chrono::time_point& absolute_time) const noexcept requires timed_acquirable + { + return primitive->try_acquire_until(absolute_time); + } + }; +} + +#endif //REMOTEINPUT_SYNCHRONIZATION_HXX diff --git a/third-party/RemoteInput/RemoteInput/Echo/Time.cxx b/third-party/RemoteInput/RemoteInput/Echo/Time.cxx new file mode 100644 index 0000000..bc84f67 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Echo/Time.cxx @@ -0,0 +1,135 @@ +// +// Time.cxx +// RemoteInput +// +// Created by Brandon on 2019-09-14. +// Copyright © 2019 XIO. All rights reserved. +// + +#include "Time.hxx" +#if defined(_MSC_VER) +#include +#include +#endif + +void timeval_to_timespec(struct timeval* tv, struct timespec* ts) noexcept +{ + ts->tv_sec = tv->tv_sec; + ts->tv_nsec = tv->tv_usec * 1000; +} + +void timespec_to_timeval(struct timespec* ts, struct timeval* tv) noexcept +{ + tv->tv_sec = ts->tv_sec; + tv->tv_usec = static_cast(ts->tv_nsec / 1000); +} + +struct timespec add_timespec(struct timespec* a, struct timespec* b) noexcept +{ + struct timespec result = {a->tv_sec + b->tv_sec, b->tv_nsec + b->tv_nsec}; + if(result.tv_nsec >= 1000000000) + { + result.tv_nsec -= 1000000000; + ++result.tv_sec; + } + return result; +} + +struct timespec sub_timespec(struct timespec* a, struct timespec* b) noexcept +{ + struct timespec result = {a->tv_sec - b->tv_sec, 0}; + if (b->tv_nsec > a->tv_nsec) + { + result.tv_nsec = a->tv_nsec + 1000000000 - b->tv_nsec; + --result.tv_sec; + return result; + } + result.tv_nsec = a->tv_nsec - b->tv_nsec; + return result; +} + +struct timeval filetime_to_timeval(uint64_t filetime) noexcept +{ + uint64_t time = filetime - 116444736000000000ULL; + return {static_cast(time / 10000000ULL), static_cast(static_cast(time % 10000000ULL) / 10)}; +} + +uint64_t timeval_to_filetime(struct timeval* tp) noexcept +{ + uint64_t time = tp->tv_sec * 10000000ULL + tp->tv_usec * 10; + return time + 116444736000000000ULL; +} + + +uint64_t get_file_time() noexcept +{ + #if defined(_WIN32) || defined(_WIN64) + static void (__stdcall *PreciseSystemFileTime)(FILETIME* file_time) = [] { + void (__stdcall *PreciseSystemFileTime)(FILETIME* file_time) = nullptr; + HMODULE kernel = GetModuleHandleW(L"Kernel32.dll"); + PreciseSystemFileTime = reinterpret_cast(GetProcAddress(kernel, "GetSystemTimePreciseAsFileTime")); + if (!PreciseSystemFileTime) + { + PreciseSystemFileTime = reinterpret_cast(GetProcAddress(kernel, "GetSystemTimeAsFileTime")); + } + return PreciseSystemFileTime; + }(); + + FILETIME file_time; + PreciseSystemFileTime(&file_time); + return (static_cast(file_time.dwHighDateTime) << 32) | static_cast(file_time.dwLowDateTime); + #elif __linux__ + struct timespec ts; + #ifdef __FreeBSD__ + clock_gettime(CLOCK_REALTIME_PRECISE, &ts); + #else + clock_gettime(CLOCK_REALTIME, &ts); + #endif + return static_cast(ts.tv_sec) * 1000000000 + static_cast(ts.tv_nsec); + #else + struct timeval tv; + gettimeofday(&tv, nullptr); + return static_cast(tv.tv_sec) * 1000000 + static_cast(tv.tv_usec); + #endif // defined +} + + +uint64_t get_adjusted_file_time() noexcept +{ + uint64_t time = get_file_time(); + static uint64_t timestamp = []{ + #if defined(_WIN32) || defined(_WIN64) + LARGE_INTEGER li; + QueryPerformanceCounter(&li); + return static_cast(li.QuadPart); + #else + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return static_cast(ts.tv_sec) * 1000000000 + static_cast(ts.tv_nsec); + #endif + }(); + + static uint64_t frequency = []{ + #if defined(_WIN32) || defined(_WIN64) + LARGE_INTEGER li; + QueryPerformanceFrequency(&li); + return static_cast(li.QuadPart); + #else + struct timespec ts; + clock_getres(CLOCK_MONOTONIC, &ts); + return 1000000000 / static_cast(ts.tv_nsec); + #endif + }(); + + #if defined(_WIN32) || defined(_WIN64) + LARGE_INTEGER li; + QueryPerformanceCounter(&li); + uint64_t delta = li.QuadPart - timestamp; + #else + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + uint64_t delta = (static_cast(ts.tv_sec) * 1000000000 + static_cast(ts.tv_nsec)) - timestamp; + #endif + uint64_t delta_us = delta * 10000000 / frequency; + return delta_us + time; +} diff --git a/third-party/RemoteInput/RemoteInput/Echo/Time.hxx b/third-party/RemoteInput/RemoteInput/Echo/Time.hxx new file mode 100644 index 0000000..5235eae --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Echo/Time.hxx @@ -0,0 +1,34 @@ +// +// Time.hxx +// RemoteInput +// +// Created by Brandon on 2019-09-14. +// Copyright © 2019 XIO. All rights reserved. +// + +#ifndef TIME_HXX_INCLUDED +#define TIME_HXX_INCLUDED + +#if defined(_WIN32) || defined(_WIN64) +#include +#else +#include +#endif + +#include +#include + + +void timeval_to_timespec(struct timeval* tv, struct timespec* ts) noexcept; +void timespec_to_timeval(struct timespec* ts, struct timeval* tv) noexcept; +struct timespec add_timespec(struct timespec* a, struct timespec* b) noexcept; +struct timespec sub_timespec(struct timespec* a, struct timespec* b) noexcept; + +struct timeval filetime_to_timeval(uint64_t filetime) noexcept; +uint64_t timeval_to_filetime(struct timeval* tp) noexcept; + +uint64_t get_file_time() noexcept; +uint64_t get_adjusted_file_time() noexcept; +inline uint64_t time_since_epoch(uint64_t time) noexcept {return time - 116444736000000000ULL;} + +#endif // TIME_HXX_INCLUDED diff --git a/third-party/RemoteInput/RemoteInput/Echo/TypeTraits.hxx b/third-party/RemoteInput/RemoteInput/Echo/TypeTraits.hxx new file mode 100644 index 0000000..80a8caf --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Echo/TypeTraits.hxx @@ -0,0 +1,284 @@ +// +// PythonTraits.hxx +// RemoteInput +// +// Created by Brandon on 2020-11-10. +// Copyright © 2020 XIO. All rights reserved. +// + +#ifndef REMOTEINPUT_TYPETRAITS_HXX +#define REMOTEINPUT_TYPETRAITS_HXX + +#if defined(_WIN32) || defined(_WIN64) +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +template +struct is_same_of : std::bool_constant<(std::is_same::type, typename std::remove_cv::type>::value || ...)> { }; + +template +struct is_vector +{ + static bool const value = false; +}; + +template +struct is_vector> +{ + static bool const value = true; +}; + +template +struct all_of : std::bool_constant<(Ts::value && ...)> { }; //std::conjunction + +template +struct any_of : std::bool_constant<(Ts::value || ...)> { }; //std::disjunction + +template +struct none_of : std::bool_constant::value> { }; + +struct string_hash +{ + using is_transparent = void; //c++20 heterogeneous lookup + + size_t operator()(const char* string) const + { + return std::hash{}(string); + } + + size_t operator()(std::string_view string) const + { + return std::hash{}(string); + } + + size_t operator()(const std::string &string) const + { + return std::hash{}(string); + } +}; + +template +inline void hash_combine(std::size_t& seed, const T &value) +{ + // boost::hash + seed ^= std::hash{}(value) + 0x9E3779B9 + (seed << 6) + (seed >> 2); +} + +template +auto select_tuple(Tuple&& tuple, std::index_sequence) +{ + return std::tuple...>(std::get(std::forward(tuple))...); +} + +template +struct offset_sequence; + +template +struct offset_sequence> +{ + using type = std::index_sequence; +}; + +template +using offset_sequence_t = typename offset_sequence::type; + +template +using offset_index_sequence_t = offset_sequence_t>; + +template +constexpr auto tuple_remove_first_n_impl(std::tuple&& tp, std::index_sequence) +{ + return std::tuple{std::get(tp)...}; +} + +template +constexpr auto tuple_remove_first_n(std::tuple tp) +{ + return tuple_remove_first_n_impl(std::forward>(tp), std::make_index_sequence{}); +} + +template +constexpr auto tuple_remove_last_n_impl(std::tuple tp, std::index_sequence) +{ + return std::tuple{std::get(tp)...}; +} + +template +constexpr auto tuple_remove_last_n(std::tuple tp) +{ + return tuple_remove_last_n_impl(tp, std::make_index_sequence{}); +} + +template +auto tuple_index_impl(Fn&& fn, Tp&& t, std::index_sequence) +{ + if constexpr(sizeof...(Ns) == 0) + { + return std::tuple<>(); // no values in tuple + } + else if constexpr(std::is_same_v(t), 0)), void>) + { + (fn(std::get(t), Ns), ...); // no return value expected + return; + } + else if constexpr(std::is_lvalue_reference_v(t), 0))>) + { + return std::tie(fn(std::get(t), Ns)...); + } + else if constexpr(std::is_rvalue_reference_v(t), 0))>) + { + return std::forward_as_tuple(fn(std::get(t), Ns)...); + } + else + { + return std::tuple(fn(std::get(t), Ns)...); + } +} + +template +auto tuple_transform(Fn&& fn, const std::tuple& tuple) +{ + auto transform = [fn = std::forward(fn)](auto arg, [[maybe_unused]] std::size_t index) { + return fn(arg); + }; + return tuple_index_impl(transform, tuple, std::make_index_sequence()); +} + +template +auto tuple_map(Fn&& fn, const std::tuple& tp) +{ + return tuple_index_impl(std::forward(fn), tp, std::make_index_sequence()); +} + +template +void tuple_for_each(Tp&& tuple, Fn&& fn) +{ + return std::apply(std::forward(tuple), [&fn](auto&&... args) { + return (fn(std::forward(args)) && ...); + }); +} + +template +std::string join(const std::vector &range, const std::string &separator, Predicate converter) +{ + if (range.empty()) + { + return std::string(); + } + + if (range.size() == 1) + { + return converter(range[0]); + } + + std::string result = ""; + for (std::size_t i = 0; i < range.size() - 1; ++i) + { + result += converter(range[i]) + separator; + } + return result + converter(range[range.size() - 1]); + +// return std::accumulate( +// next(begin(range)), +// end(range), +// converter(range[0]), // the initial value +// [&separator, &converter](auto result, const auto &value) { +// return result + separator + converter(value); +// } +// ); +} + +template +std::string to_string(const std::tuple &ts, const char* separator = "") +{ + auto convert_to_utf8 = [](const std::wstring &value) -> std::string { + #if defined(_WIN32) || defined(_WIN64) + int len = WideCharToMultiByte(CP_UTF8, 0, value.c_str(), -1, NULL, 0, 0, 0); + if (len) + { + std::string utf8 = std::string(len, '\0'); + WideCharToMultiByte(CP_UTF8, 0, value.c_str(), -1, &utf8[0], len, 0, 0); + return utf8; + } + return std::string(); + #elif __cplusplus > 199711L && __cplusplus < 201703L + return std::wstring_convert>().to_bytes(value); + #else + int utf16len = wcslen(value.c_str()); + int utf8len = std::wcstombs(nullptr, value.c_str(), utf16len); + if (utf8len) + { + std::string utf8 = std::string(utf8len, '\0'); + std::wcstombs(&utf8[0], value.c_str(), utf16len); + return utf8; + } + return std::string(); + #endif + }; + + auto convert_pointer_to_string = [](const auto &value) -> std::string { + char buffer[(sizeof(void*) * 2) + 2] = {0}; + snprintf(buffer, sizeof(buffer) - 1, "%p", value); + return std::string(buffer); + }; + + auto convert_to_string = [convert_to_utf8, convert_pointer_to_string](auto value) -> std::string { + if constexpr(is_same_of, char, char*, const char*, std::string>::value) + { + return value; + } + else if constexpr(is_same_of, wchar_t, std::wstring>::value) + { + return convert_to_utf8(value); + } + else if constexpr (is_vector>::value) + { + using value_type = typename std::decay_t::value_type; + if constexpr(is_same_of::value) + { + return "[" + join(value, " ", [](const value_type &value) { return value; }) + "]"; + } + else if constexpr(is_same_of::value) + { + return "[" + join(value, " ", [convert_to_utf8](const value_type &value) { return convert_to_utf8(value); }) + "]"; + } + else if constexpr(std::is_pointer::value) + { + return "[" + join(value, " ", convert_pointer_to_string) + "]"; + } + else if constexpr(std::is_union::value) + { + return "[cannot_convert_type_to_string]"; + } + else + { + return "[" + join(value, " ", [](const value_type &value) { return std::to_string(value); }) + "]"; + } + } + else if constexpr(std::is_pointer::value) + { + return convert_pointer_to_string(value); + } + else + { + return std::to_string(value); + } + }; + + return std::apply([separator, convert_to_string](Args... ts) { + std::string result; + const char* sep = ""; + ((static_cast(result += sep + convert_to_string(ts)), sep = separator), ...); + return result; + }, ts); +} + +#endif //REMOTEINPUT_TYPETRAITS_HXX diff --git a/third-party/RemoteInput/RemoteInput/Echo/TypeTraits_Functional.hxx b/third-party/RemoteInput/RemoteInput/Echo/TypeTraits_Functional.hxx new file mode 100644 index 0000000..abac024 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Echo/TypeTraits_Functional.hxx @@ -0,0 +1,316 @@ +// +// Created by Brandon on 2022-09-16. +// + +#ifndef REMOTEINPUT_TYPETRAITS_FUNCTIONAL_HXX +#define REMOTEINPUT_TYPETRAITS_FUNCTIONAL_HXX + +#include +#include + +template +struct is_function_pointer +{ + static bool const value = std::bool_constant::value && + std::is_function::type>::value>::value; +}; + +template +struct is_lambda +{ + static bool const value = !std::remove_reference_t::value && + !std::is_function::value && + !std::is_pointer::value && + !std::is_member_pointer::value; +}; + +template +struct remove_class {}; + +template +struct remove_class +{ + using type = R(A...); +}; + +template +struct remove_class +{ + using type = R(A...); +}; + +template +struct remove_class +{ + using type = R(A...); +}; + +template +struct remove_class +{ + using type = R(A...); +}; + +template +struct remove_class +{ + using type = R(A...); +}; + +template +struct remove_class +{ + using type = R(A...); +}; + +template +struct remove_class +{ + using type = R(A...); +}; + +template +struct remove_class +{ + using type = R(A...); +}; + +template +struct function_signature +{ + using type = typename std::conditional::value || std::is_member_function_pointer::value, + typename remove_class::type::operator())>::type, + typename remove_class::type::operator())>::type>::type; +}; + +template +struct function_signature +{ + using type = R(A...); +}; + +template +struct function_signature +{ + using type = R(A...); +}; + +template +struct function_signature +{ + using type = R(A...); +}; + +template +struct function_signature +{ + using type = R(A...); +}; + +template +struct function_signature +{ + using type = R(A...); +}; + +template +struct function_signature +{ + using type = R(A...); +}; + +template +struct function_signature +{ + using type = R(A...); +}; + +template +struct function_signature +{ + using type = R(A...); +}; + +template +struct function_signature +{ + using type = R(A...); +}; + +template +struct function_signature +{ + using type = R(A...); +}; + +template +struct function_signature +{ + using type = R(A...); +}; + +template +struct function_signature +{ + using type = R(A...); +}; + +template +struct function_arguments_type +{ + using type = typename function_arguments_type::type>::type; +}; + +template +struct function_arguments_type +{ + using type = typename std::tuple; +}; + +template +struct function_arguments_type +{ + using type = typename std::tuple; +}; + +template +struct function_arguments_type +{ + using type = typename std::tuple; +}; + +template +struct function_arguments_type +{ + using type = typename std::tuple; +}; + +template +struct function_arguments_type +{ + using type = typename std::tuple; +}; + +template +struct function_arguments_type +{ + using type = typename std::tuple; +}; + +template +struct function_arguments_type +{ + using type = typename std::tuple; +}; + +template +struct function_arguments_type +{ + using type = typename std::tuple; +}; + +template +struct function_arguments_type +{ + using type = typename std::tuple; +}; + +template +struct function_arguments_type +{ + using type = typename std::tuple; +}; + +template +struct function_arguments_type +{ + using type = typename std::tuple; +}; + +template +struct function_arguments_type +{ + using type = typename std::tuple; +}; + +template +struct function_return_type +{ + using type = typename function_return_type::type>::type; +}; + +template +struct function_return_type +{ + using type = R; +}; + +template +struct function_return_type +{ + using type = R; +}; + +template +struct function_return_type +{ + using type = R; +}; + +template +struct function_return_type +{ + using type = R; +}; + +template +struct function_return_type +{ + using type = R; +}; + +template +struct function_return_type +{ + using type = R; +}; + +template +struct function_return_type +{ + using type = R; +}; + +template +struct function_return_type +{ + using type = R; +}; + +template +struct function_return_type +{ + using type = R; +}; + +template +struct function_return_type +{ + using type = R; +}; + +template +struct function_return_type +{ + using type = R; +}; + +template +struct function_return_type +{ + using type = R; +}; + +#include "TypeTraits_Functional_Attributes.hxx" + +#endif //REMOTEINPUT_TYPETRAITS_FUNCTIONAL_HXX diff --git a/third-party/RemoteInput/RemoteInput/Echo/TypeTraits_Functional_Attributes.hxx b/third-party/RemoteInput/RemoteInput/Echo/TypeTraits_Functional_Attributes.hxx new file mode 100644 index 0000000..1ad7b25 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Echo/TypeTraits_Functional_Attributes.hxx @@ -0,0 +1,276 @@ +// +// Created by Brandon on 2022-09-23. +// + +#ifndef REMOTEINPUT_TYPETRAITS_FUNCTIONAL_ATTRIBUTES_HXX +#define REMOTEINPUT_TYPETRAITS_FUNCTIONAL_ATTRIBUTES_HXX + +#if defined(_WIN32) && !defined(_WIN64) + +template +struct remove_class +{ + using type = R(A...); +}; + +template +struct remove_class +{ + using type = R(A...); +}; + +template +struct remove_class +{ + using type = R(A...); +}; + +template +struct remove_class +{ + using type = R(A...); +}; + +template +struct remove_class +{ + using type = R(A...); +}; + +template +struct remove_class +{ + using type = R(A...); +}; + +template +struct remove_class +{ + using type = R(A...); +}; + +template +struct remove_class +{ + using type = R(A...); +}; + +template +struct function_signature +{ + using type = R(A...); +}; + +template +struct function_signature +{ + using type = R(A...); +}; + +template +struct function_signature +{ + using type = R(A...); +}; + +template +struct function_signature +{ + using type = R(A...); +}; + +template +struct function_signature +{ + using type = R(A...); +}; + +template +struct function_signature +{ + using type = R(A...); +}; + +template +struct function_signature +{ + using type = R(A...); +}; + +template +struct function_signature +{ + using type = R(A...); +}; + +template +struct function_signature +{ + using type = R(A...); +}; + +template +struct function_signature +{ + using type = R(A...); +}; + +template +struct function_signature +{ + using type = R(A...); +}; + +template +struct function_signature +{ + using type = R(A...); +}; + +template +struct function_arguments_type +{ + using type = typename std::tuple; +}; + +template +struct function_arguments_type +{ + using type = typename std::tuple; +}; + +template +struct function_arguments_type +{ + using type = typename std::tuple; +}; + +template +struct function_arguments_type +{ + using type = typename std::tuple; +}; + +template +struct function_arguments_type +{ + using type = typename std::tuple; +}; + +template +struct function_arguments_type +{ + using type = typename std::tuple; +}; + +template +struct function_arguments_type +{ + using type = typename std::tuple; +}; + +template +struct function_arguments_type +{ + using type = typename std::tuple; +}; + +template +struct function_arguments_type +{ + using type = typename std::tuple; +}; + +template +struct function_arguments_type +{ + using type = typename std::tuple; +}; + +template +struct function_arguments_type +{ + using type = typename std::tuple; +}; + +template +struct function_arguments_type +{ + using type = typename std::tuple; +}; + +template +struct function_return_type +{ + using type = R; +}; + +template +struct function_return_type +{ + using type = R; +}; + +template +struct function_return_type +{ + using type = R; +}; + +template +struct function_return_type +{ + using type = R; +}; + +template +struct function_return_type +{ + using type = R; +}; + +template +struct function_return_type +{ + using type = R; +}; + +template +struct function_return_type +{ + using type = R; +}; + +template +struct function_return_type +{ + using type = R; +}; + +template +struct function_return_type +{ + using type = R; +}; + +template +struct function_return_type +{ + using type = R; +}; + +template +struct function_return_type +{ + using type = R; +}; + +template +struct function_return_type +{ + using type = R; +}; + +#endif //_Win32 + +#endif //REMOTEINPUT_TYPETRAITS_FUNCTIONAL_ATTRIBUTES_HXX diff --git a/third-party/RemoteInput/RemoteInput/Hooks/ModelRendering.cxx b/third-party/RemoteInput/RemoteInput/Hooks/ModelRendering.cxx new file mode 100644 index 0000000..a0c2d63 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Hooks/ModelRendering.cxx @@ -0,0 +1,1300 @@ +// +// ModelRendering.cpp +// RemoteInput +// +// Created by Brandon on 2019-12-08. +// Copyright © 2019 XIO. All rights reserved. +// + +#include "ModelRendering.hxx" + +#if defined(DEBUG) +#include "Hooks.hpp" +#include "ReflectionHook.hxx" +#include "ControlCenter.hxx" +#include + + +template +T ReadPointer(void* &ptr) +{ + T result = *static_cast(ptr); + ptr = static_cast(ptr) + 1; + return result; +} + +std::string ReadPointer(void* &ptr) +{ + std::size_t length = *static_cast(ptr); + ptr = static_cast(ptr) + 1; + + std::string result = std::string(reinterpret_cast(ptr), length); + ptr = static_cast(ptr) + (result.length() * sizeof(char)); + ptr = static_cast(ptr) + 1; + return result; +} + +template +void WritePointer(void* &ptr, T result) +{ + *static_cast(ptr) = result; + ptr = static_cast(ptr) + 1; +} + +void WritePointer(void* &ptr, const std::string &result) +{ + *static_cast(ptr) = result.length(); + ptr = static_cast(ptr) + 1; + + memcpy(ptr, &result[0], result.length() * sizeof(char)); + ptr = static_cast(ptr) + (result.length() * sizeof(char)); + + *static_cast(ptr) = '\0'; + ptr = static_cast(ptr) + 1; +} + +class Reflector +{ +private: + typedef std::unique_ptr> object; + + Reflection* env; + void process_reflect_array_index(Reflection* eios, jarray array, void* &arguments, void* &response, int dimensions); + +public: + Reflector::object make_safe(void* object); + + Reflector(Reflection* env); + ~Reflector(); + + Reflector(const Reflector &other) = delete; + Reflector(Reflector &&other) = delete; + Reflector& operator = (const Reflector &other) = delete; + Reflector& operator = (Reflector &&other) = delete; + + Reflector::object GetObject(void* object, const Hook hook); + void ReleaseObject(void* object); + void ReleaseObjects(void** objects, std::size_t amount); + bool GetBoolean(void* object, const Hook hook); + char GetChar(void* object, const Hook hook); + std::uint8_t GetByte(void* object, const Hook hook); + std::int16_t GetShort(void* object, const Hook hook); + std::int32_t GetInt(void* object, const Hook hook); + std::int64_t GetLong(void* object, const Hook hook); + float GetFloat(void* object, const Hook hook); + double GetDouble(void* object, const Hook hook); + std::string GetString(void* object, const Hook hook); + Reflector::object GetArray(void* object, const Hook hook); + std::int32_t GetArraySize(void* array); + + Reflector::object ArrayIndex(void* array, std::int32_t index); + + template + std::vector GetArrayIndex(void* array, std::int32_t index, std::size_t length); + + template + std::vector GetArrayIndex2D(void* array, std::size_t length, std::int32_t x, std::int32_t y); + + template + std::vector GetArrayIndex3D(void* array, std::size_t length, std::int32_t x, std::int32_t y, std::int32_t z); + + template + std::vector GetArrayIndex4D(void* array, std::size_t length, std::int32_t x, std::int32_t y, std::int32_t z, std::int32_t w); +}; + +Reflector* get_reflector(Reflection* reflection) +{ + for (std::size_t i = 0; i < 2048; ++i) + { + SINE[i] = static_cast(65536.0 * sin(static_cast(i) * UNIT)); + COSINE[i] = static_cast(65536.0 * cos(static_cast(i) * UNIT)); + } + return new Reflector(reflection); +} + +Reflector::Reflector(Reflection* env) : env(env) +{ +} + +Reflector::~Reflector() +{ +} + +Reflector::object Reflector::make_safe(void* object) +{ + auto deleter = [&](void* const ptr) { + this->ReleaseObject(ptr); + }; + + return std::unique_ptr{static_cast(object), deleter}; +} + +Reflector::object Reflector::GetObject(void* object, const Hook hook) +{ + return make_safe(env->getField(static_cast(object), hook.hook())); +} + +void Reflector::ReleaseObject(void* object) +{ + if (object) + { + env->getEnv()->DeleteGlobalRef(static_cast(object)); + } +} + +void Reflector::ReleaseObjects(void** objects, std::size_t amount) +{ + for (std::size_t i = 0; i < amount; ++i) + { + ReleaseObject(objects[i]); + } +} + +bool Reflector::GetBoolean(void* object, const Hook hook) +{ + return env->getPrimitive(static_cast(object), hook.hook()); +} + +char Reflector::GetChar(void* object, const Hook hook) +{ + return env->getPrimitive(static_cast(object), hook.hook()); +} + +std::uint8_t Reflector::GetByte(void* object, const Hook hook) +{ + return env->getPrimitive(static_cast(object), hook.hook()); +} + +std::int16_t Reflector::GetShort(void* object, const Hook hook) +{ + return env->getPrimitive(static_cast(object), hook.hook()); +} + +std::int32_t Reflector::GetInt(void* object, const Hook hook) +{ + return env->getPrimitive(static_cast(object), hook.hook()); +} + +std::int64_t Reflector::GetLong(void* object, const Hook hook) +{ + return env->getPrimitive(static_cast(object), hook.hook()); +} + +float Reflector::GetFloat(void* object, const Hook hook) +{ + return env->getPrimitive(static_cast(object), hook.hook()); +} + +double Reflector::GetDouble(void* object, const Hook hook) +{ + return env->getPrimitive(static_cast(object), hook.hook()); +} + +std::string Reflector::GetString(void* object, const Hook hook) +{ + return env->getField(static_cast(object), hook.hook()); +} + +Reflector::object Reflector::GetArray(void* object, const Hook hook) +{ + return make_safe(env->getField(static_cast(object), hook.hook())); +} + +std::int32_t Reflector::GetArraySize(void* array) +{ + return env->getEnv()->GetArrayLength(static_cast(array)); +} + +Reflector::object Reflector::ArrayIndex(void* array, std::int32_t index) +{ + return make_safe(GetArrayIndex(array, index, 1)[0]); +} + +void Reflector::process_reflect_array_index(Reflection* eios, jarray array, void* &arguments, void* &response, int dimensions) +{ + auto write_result = [eios](jarray array, ReflectionArrayType type, jsize index, jsize length, void* &response) -> void { + if (!array) + { + return WritePointer(response, nullptr); + } + + //Maybe better to use GetPrimitiveArrayCritical + memcpy + switch (type) { + case ReflectionArrayType::CHAR: + { + eios->getEnv()->GetCharArrayRegion(static_cast(array), index, length, static_cast(response)); + } + break; + + case ReflectionArrayType::BYTE: + { + eios->getEnv()->GetByteArrayRegion(static_cast(array), index, length, static_cast(response)); + } + break; + + case ReflectionArrayType::BOOL: + { + eios->getEnv()->GetBooleanArrayRegion(static_cast(array), index, length, static_cast(response)); + } + break; + + case ReflectionArrayType::SHORT: + { + eios->getEnv()->GetShortArrayRegion(static_cast(array), index, length, static_cast(response)); + } + break; + + case ReflectionArrayType::INT: + { + eios->getEnv()->GetIntArrayRegion(static_cast(array), index, length, static_cast(response)); + } + break; + + case ReflectionArrayType::LONG: + { + eios->getEnv()->GetLongArrayRegion(static_cast(array), index, length, static_cast(response)); + } + break; + + case ReflectionArrayType::FLOAT: + { + eios->getEnv()->GetFloatArrayRegion(static_cast(array), index, length, static_cast(response)); + } + break; + + case ReflectionArrayType::DOUBLE: + { + eios->getEnv()->GetDoubleArrayRegion(static_cast(array), index, length, static_cast(response)); + } + break; + + case ReflectionArrayType::STRING: + { + auto get_string = [eios](jstring string){ + if (string) + { + jsize length = eios->getEnv()->GetStringUTFLength(string); + const char* chars = eios->getEnv()->GetStringUTFChars(string, nullptr); + std::string result = std::string(chars, length); + eios->getEnv()->ReleaseStringUTFChars(string, chars); + return result; + } + return std::string(); + }; + + if (length > 1) + { + for (jsize i = 0; i < length; ++i) + { + auto element = eios->getEnv()->GetObjectArrayElement(static_cast(array), index + i); + std::string result = get_string(static_cast(element)); + WritePointer(response, result); + eios->getEnv()->DeleteLocalRef(element); + } + } + else + { + auto element = eios->getEnv()->GetObjectArrayElement(static_cast(array), index); + std::string result = get_string(static_cast(element)); + WritePointer(response, result); + eios->getEnv()->DeleteLocalRef(element); + } + } + break; + + case ReflectionArrayType::OBJECT: + { + if (length > 1) + { + for (jsize i = 0; i < length; ++i) + { + auto result = eios->getEnv()->GetObjectArrayElement(static_cast(array), index + i); + WritePointer(response, result ? eios->getEnv()->NewGlobalRef(result) : nullptr); + } + } + else + { + auto result = eios->getEnv()->GetObjectArrayElement(static_cast(array), index); + WritePointer(response, result ? eios->getEnv()->NewGlobalRef(result) : nullptr); + } + } + break; + } + }; + + if (dimensions == 1) + { + ReflectionArrayType type = ReadPointer(arguments); + jsize index = ReadPointer(arguments); + jsize length = ReadPointer(arguments); + write_result(array, type, index, length, response); + return; + } + + ReflectionArrayType type = ReadPointer(arguments); + for (int i = 0; i < dimensions - 1; ++i) + { + jsize index = ReadPointer(arguments); + + if (array) + { + array = static_cast(eios->getEnv()->GetObjectArrayElement(static_cast(array), index)); + } + } + + jsize index = ReadPointer(arguments); + jsize length = ReadPointer(arguments); + write_result(array, type, index, length, response); +} + +template +ReflectionArrayType GetArrayType() +{ + ReflectionArrayType type = ReflectionArrayType::OBJECT; + if (std::is_same::value || std::is_same::value) + { + type = ReflectionArrayType::CHAR; + } + + if (std::is_same::value || std::is_same::value) + { + type = ReflectionArrayType::BYTE; + } + + if (std::is_same::value || std::is_same::value) + { + type = ReflectionArrayType::BOOL; + } + + if (std::is_same::value || std::is_same::value) + { + type = ReflectionArrayType::SHORT; + } + + if (std::is_same::value || std::is_same::value) + { + type = ReflectionArrayType::INT; + } + + if (std::is_same::value || std::is_same::value) + { + type = ReflectionArrayType::LONG; + } + + if (std::is_same::value || std::is_same::value) + { + type = ReflectionArrayType::FLOAT; + } + + if (std::is_same::value || std::is_same::value) + { + type = ReflectionArrayType::DOUBLE; + } + + if (std::is_same::value || std::is_same::value) + { + type = ReflectionArrayType::OBJECT; + } + return type; +} + +template +std::vector Reflector::GetArrayIndex(void* array, std::int32_t index, std::size_t length) +{ + std::vector args(1024); + void* arguments = &args[0]; + + WritePointer(arguments, GetArrayType()); + WritePointer(arguments, static_cast(index)); + WritePointer(arguments, static_cast(length)); + + std::vector buffer(length); + arguments = &args[0]; + void* response = &buffer[0]; + process_reflect_array_index(env, static_cast(array), arguments, response, 1); + return buffer; +} + +template +std::vector Reflector::GetArrayIndex2D(void* array, std::size_t length, std::int32_t x, std::int32_t y) +{ + std::vector args(1024); + void* arguments = &args[0]; + + WritePointer(arguments, GetArrayType()); + WritePointer(arguments, static_cast(x)); + WritePointer(arguments, static_cast(y)); + WritePointer(arguments, static_cast(length)); + + std::vector buffer(length); + arguments = &args[0]; + void* response = &buffer[0]; + process_reflect_array_index(env, static_cast(array), arguments, response, 2); + return buffer; +} + +template +std::vector Reflector::GetArrayIndex3D(void* array, std::size_t length, std::int32_t x, std::int32_t y, std::int32_t z) +{ + std::vector args(1024); + void* arguments = &args[0]; + + WritePointer(arguments, GetArrayType()); + WritePointer(arguments, static_cast(x)); + WritePointer(arguments, static_cast(y)); + WritePointer(arguments, static_cast(z)); + WritePointer(arguments, static_cast(length)); + + std::vector buffer(length); + arguments = &args[0]; + void* response = &buffer[0]; + process_reflect_array_index(env, static_cast(array), arguments, response, 3); + return buffer; +} + +template +std::vector Reflector::GetArrayIndex4D(void* array, std::size_t length, std::int32_t x, std::int32_t y, std::int32_t z, std::int32_t w) +{ + std::vector args(1024); + void* arguments = &args[0]; + + WritePointer(arguments, GetArrayType()); + WritePointer(arguments, static_cast(x)); + WritePointer(arguments, static_cast(y)); + WritePointer(arguments, static_cast(z)); + WritePointer(arguments, static_cast(w)); + WritePointer(arguments, static_cast(length)); + + std::vector buffer(length); + arguments = &args[0]; + void* response = &buffer[0]; + process_reflect_array_index(env, static_cast(array), arguments, response, 4); + return buffer; +} + + + + +// MARK: - MODELS + +Point Point::rotate(std::int32_t orientation) +{ + return Point(((x * COSINE[orientation]) + (z * SINE[orientation])) >> 16, + y, + ((z * COSINE[orientation]) - (x * SINE[orientation])) >> 16 + ); +} + +Polygon::Polygon(std::vector xpoints, std::vector ypoints, std::int32_t npoints) +{ + if (npoints > xpoints.size() || npoints > ypoints.size()) + { + throw "npoints > xpoints.length || npoints > ypoints.length"; + } + + if (npoints < 0) + { + throw "npoints < 0"; + } + + this->npoints = npoints; + this->xpoints = xpoints; + this->ypoints = ypoints; +} + +// MARK: - Client + +std::uint8_t Client::getTileSettings(Reflector* eios, std::int32_t x, std::int32_t y, std::int32_t z) +{ + static std::once_flag flag; + static std::vector>> result; + + std::call_once(flag, [&]{ + auto settings = eios->GetArray(nullptr, HOOK_CLIENT_TILESETTINGS); + std::size_t size = eios->GetArraySize(settings.get()); + result.reserve(size); + + auto jx = eios->GetArrayIndex(settings.get(), 0, size); + for (auto& x : jx) + { + std::vector> inner; + auto jy = eios->GetArrayIndex(x, 0, eios->GetArraySize(x)); + for (auto& y : jy) + { + inner.push_back(eios->GetArrayIndex(y, 0, eios->GetArraySize(y))); + } + result.push_back(inner); + eios->ReleaseObjects((void**)&jy[0], jy.size()); + } + eios->ReleaseObjects((void**)&jx[0], jx.size()); + }); + return result[x][y][z]; +} + +std::int32_t Client::getTileHeights(Reflector* eios, std::int32_t x, std::int32_t y, std::int32_t z) +{ + static std::once_flag flag; + static std::vector>> result; + + std::call_once(flag, [&]{ + auto settings = eios->GetArray(nullptr, HOOK_CLIENT_TILEHEIGHTS); + std::size_t size = eios->GetArraySize(settings.get()); + result.reserve(size); + + auto jx = eios->GetArrayIndex(settings.get(), 0, size); + for (auto& x : jx) + { + std::vector> inner; + auto jy = eios->GetArrayIndex(x, 0, eios->GetArraySize(x)); + for (auto& y : jy) + { + inner.push_back(eios->GetArrayIndex(y, 0, eios->GetArraySize(y))); + } + result.push_back(inner); + eios->ReleaseObjects((void**)&jy[0], jy.size()); + } + eios->ReleaseObjects((void**)&jx[0], jx.size()); + }); + return result[x][y][z]; +} + +std::int32_t Client::plane(Reflector* eios) +{ + return eios->GetInt(nullptr, HOOK_CLIENT_PLANE) * static_cast(HOOK_CLIENT_PLANE.multiplier); +} + +std::int32_t Client::viewPortWidth(Reflector* eios) +{ + return eios->GetInt(nullptr, HOOK_CLIENT_VIEWPORTWIDTH) * static_cast(HOOK_CLIENT_VIEWPORTWIDTH.multiplier); +} + +std::int32_t Client::viewPortHeight(Reflector* eios) +{ + return eios->GetInt(nullptr, HOOK_CLIENT_VIEWPORTHEIGHT) * static_cast(HOOK_CLIENT_VIEWPORTHEIGHT.multiplier); +} + +std::int32_t Client::viewPortScale(Reflector* eios) +{ + return eios->GetInt(nullptr, HOOK_CLIENT_VIEWPORTSCALE) * static_cast(HOOK_CLIENT_VIEWPORTSCALE.multiplier); +} + +// MARK: - Camera + +std::int32_t Camera::getX(Reflector* eios) +{ + return eios->GetInt(nullptr, HOOK_CLIENT_CAMERAX) * static_cast(HOOK_CLIENT_CAMERAX.multiplier); +} + +std::int32_t Camera::getY(Reflector* eios) +{ + return eios->GetInt(nullptr, HOOK_CLIENT_CAMERAY) * static_cast(HOOK_CLIENT_CAMERAY.multiplier); +} + +std::int32_t Camera::getZ(Reflector* eios) +{ + return eios->GetInt(nullptr, HOOK_CLIENT_CAMERAZ) * static_cast(HOOK_CLIENT_CAMERAZ.multiplier); +} + +std::int32_t Camera::pitch(Reflector* eios) +{ + return eios->GetInt(nullptr, HOOK_CLIENT_CAMERAPITCH) * static_cast(HOOK_CLIENT_CAMERAPITCH.multiplier); +} + +std::int32_t Camera::yaw(Reflector* eios) +{ + return eios->GetInt(nullptr, HOOK_CLIENT_CAMERAYAW) * static_cast(HOOK_CLIENT_CAMERAYAW.multiplier); +} + +// MARK: - Projector + +std::int32_t Projector::getTileHeight(Reflector* eios, std::int32_t x, std::int32_t y, std::int32_t plane) +{ + std::int32_t xSize = x >> 7; + std::int32_t ySize = y >> 7; + + if (xSize >= 0 && ySize >= 0 && xSize < 104 && ySize < 104) + { + if (plane < 3 && (Client::getTileSettings(eios, 1, xSize, ySize) & 2) == 2) + { + ++plane; + } + + std::int32_t var1 = x & 127; + std::int32_t var2 = y & 127; + + std::int32_t var3 = ((var1 * Client::getTileHeights(eios, plane, xSize + 1, ySize)) + ((128 - var1) * Client::getTileHeights(eios, plane, xSize, ySize))) >> 7; + + std::int32_t var4 = ((Client::getTileHeights(eios, plane, xSize, ySize + 1) * (128 - var1)) + (var1 * Client::getTileHeights(eios, plane, xSize + 1, ySize + 1))) >> 7; + + return (((128 - var2) * var3) + (var2 * var4)) >> 7; + } + + return 0; +} + +Point Projector::toScreen(Reflector* eios, Point p, std::int32_t localX, std::int32_t localY, std::int32_t plane) +{ + std::int32_t cameraX = Camera::getX(eios); + std::int32_t cameraY = Camera::getY(eios); + std::int32_t cameraZ = Camera::getZ(eios); + std::int32_t cameraPitch = Camera::pitch(eios); + std::int32_t cameraYaw = Camera::yaw(eios); + std::int32_t clientPlane = Client::plane(eios); + + std::int32_t viewPortWidth = Client::viewPortWidth(eios); + std::int32_t viewPortHeight = Client::viewPortHeight(eios); + std::int32_t viewPortScale = Client::viewPortScale(eios); + + return Projector::toScreen(eios, p, localX, localY, plane, cameraX, cameraY, cameraZ, cameraPitch, cameraYaw, clientPlane, viewPortWidth, viewPortHeight, viewPortScale); +} + +Point Projector::toScreen(Reflector* eios, Point p, std::int32_t localX, std::int32_t localY, std::int32_t plane, + std::int32_t cameraX, std::int32_t cameraY, std::int32_t cameraZ, + std::int32_t cameraPitch, std::int32_t cameraYaw, + std::int32_t clientPlane, + std::int32_t viewPortWidth, std::int32_t viewPortHeight, + std::int32_t viewPortScale) +{ + std::int32_t x = localX - p.x; + std::int32_t y = localY - p.z; + + if (x >= 128 && y >= 128 && x <= 13056 && y <= 13056) + { + std::int32_t z = getTileHeight(eios, x, y, clientPlane) - plane; + x -= cameraX; + y -= cameraY; + z -= cameraZ; + + int sinCurveY = SINE[cameraPitch]; + int cosCurveY = COSINE[cameraPitch]; + int sinCurveX = SINE[cameraYaw]; + int cosCurveX = COSINE[cameraYaw]; + int calculation = ((sinCurveX * y) + (cosCurveX * x)) >> 16; + y = ((y * cosCurveX) - (x * sinCurveX)) >> 16; + x = calculation; + calculation = ((cosCurveY * z) - (sinCurveY * y)) >> 16; + y = ((sinCurveY * z) + (cosCurveY * y)) >> 16; + z = calculation; + + if (y >= 50) + { + int screenX = (x * viewPortScale / y) + (viewPortWidth / 2); + int screenY = (z * viewPortScale / y) + (viewPortHeight / 2); + + if (screenX < 0 || screenX > viewPortWidth || screenY < 0 || screenY > viewPortHeight) + { + return Point(-1, -1, 0); + } + else + { + return Point(screenX, screenY, 0); + } + } + } + + return Point(-1, -1, 0); +} + +// MARK: - Model +Model::Model(Reflector* eios, object&& ref) : eios(eios), ref(std::move(ref)) +{ + if (this->ref) + { + this->vertices = this->getVertices(); + this->triangles = this->getTriangles(); + } +} + +Model::Model(Model&& other) : ref(std::move(other.ref)), eios(other.eios), vertices(std::move(other.vertices)), triangles(std::move(other.triangles)) +{ +} + +Model& Model::operator = (Model&& other) +{ + ref = std::move(other.ref); + eios = other.eios; + vertices = std::move(other.vertices); + triangles = std::move(other.triangles); + return *this; +} + +Model::operator bool() const +{ + return ref.get() != nullptr; +} + +void Model::scale(std::int32_t x, std::int32_t y, std::int32_t z) +{ + for (std::size_t i = 0; i < this->vertices.size(); ++i) + { + Point p = this->vertices[i]; + int px = p.x * x / 128; + int py = p.y * x / 128; + int pz = p.z * x / 128; + this->vertices[i] = Point(px, py, pz); + } +} + +std::vector Model::getVertices() +{ + std::vector result; + auto verticesX = eios->GetArray(ref.get(), HOOK_MODEL_VERTICESX); + auto verticesY = eios->GetArray(ref.get(), HOOK_MODEL_VERTICESY); + auto verticesZ = eios->GetArray(ref.get(), HOOK_MODEL_VERTICESZ); + + std::size_t size = eios->GetArraySize(verticesX.get()); + result.resize(size); + + auto x = eios->GetArrayIndex(verticesX.get(), 0, size); + auto y = eios->GetArrayIndex(verticesY.get(), 0, size); + auto z = eios->GetArrayIndex(verticesZ.get(), 0, size); + + for (std::size_t i = 0; i < size; ++i) + { + result[i] = Point(x[i], y[i], z[i]); + } + return result; +} + +std::vector Model::getTriangles() +{ + std::vector result; + auto indicesX = eios->GetArray(ref.get(), HOOK_MODEL_INDICESX); + auto indicesY = eios->GetArray(ref.get(), HOOK_MODEL_INDICESY); + auto indicesZ = eios->GetArray(ref.get(), HOOK_MODEL_INDICESZ); + + std::int32_t size = eios->GetArraySize(indicesX.get()); + result.resize(size); + + auto x = eios->GetArrayIndex(indicesX.get(), 0, size); + auto y = eios->GetArrayIndex(indicesY.get(), 0, size); + auto z = eios->GetArrayIndex(indicesZ.get(), 0, size); + + for (std::int32_t i = 0; i < size; ++i) + { + Point a = this->vertices[x[i]]; + Point b = this->vertices[y[i]]; + Point c = this->vertices[z[i]]; + result[i] = Triangle(a, b, c); + } + return result; +} + +std::vector Model::getRotatedTriangles(std::int32_t orientation) +{ + for (auto triangle : this->triangles) + { + triangle.a = triangle.a.rotate(orientation); + triangle.b = triangle.b.rotate(orientation); + triangle.c = triangle.c.rotate(orientation); + } + + return triangles; +} + +// MARK: - Node +Node::Node(Reflector* eios, object&& ref) : eios(eios), ref(std::move(ref)) {} +Node::Node(Node&& other) : ref(std::move(other.ref)), eios(other.eios) {} +Node& Node::operator = (Node&& other) +{ + ref = std::move(other.ref); + eios = other.eios; + return *this; +} + +Node::operator bool() const +{ + return (bool)ref; +} + +std::int64_t Node::uid() +{ + return eios->GetLong(ref.get(), HOOK_NODE_UID) * HOOK_NODE_UID.multiplier; +} + +Node Node::next() +{ + auto node = eios->GetObject(ref.get(), HOOK_NODE_NEXT); + return Node(eios, std::move(node)); +} + +// MARK: - HashTable + +HashTable::HashTable(Reflector* eios, object&& ref) : eios(eios), ref(std::move(ref)) {} + +HashTable::operator bool() const +{ + return (bool)ref; +} + +std::int32_t HashTable::size() +{ + return eios->GetInt(ref.get(), HOOK_HASHTABLE_CAPACITY) * static_cast(HOOK_HASHTABLE_CAPACITY.multiplier); +} + +std::vector HashTable::buckets() +{ + std::vector result; + auto buckets = eios->GetArray(ref.get(), HOOK_HASHTABLE_CACHE_BUCKETS); + std::int32_t size = eios->GetArraySize(buckets.get()); + result.resize(size); + + auto buckets_array = eios->GetArrayIndex(buckets.get(), 0, size); + + for (std::int32_t i = 0; i < size; ++i) + { + result[i] = Node(eios, eios->make_safe(buckets_array[i])); + } + + return result; +} + +// MARK: - Cache + +Cache::Cache(Reflector* eios, object&& ref) : eios(eios), ref(std::move(ref)) {} +Cache::Cache(Cache&& other) : ref(std::move(other.ref)), eios(other.eios) {} + +Cache& Cache::operator = (Cache&& other) +{ + ref = std::move(other.ref); + eios = other.eios; + return *this; +} + +Cache::operator bool() const +{ + return (bool)ref; +} + +HashTable Cache::hash_table() +{ + auto hashtable = eios->GetObject(ref.get(), HOOK_CACHE_HASHTABLE); + return HashTable(eios, std::move(hashtable)); +} + +// MARK: PlayerDefinition + +PlayerDefinition::PlayerDefinition(Reflector* eios, object&& ref) : eios(eios), ref(std::move(ref)) {} +PlayerDefinition::PlayerDefinition(PlayerDefinition&& other) : ref(std::move(other.ref)), eios(other.eios) {} + +PlayerDefinition& PlayerDefinition::operator = (PlayerDefinition&& other) +{ + ref = std::move(other.ref); + eios = other.eios; + return *this; +} + +PlayerDefinition::operator bool() const +{ + return (bool)ref; +} + +std::int32_t PlayerDefinition::id() +{ + return eios->GetInt(ref.get(), HOOK_PLAYERDEFINITION_ID) * static_cast(HOOK_PLAYERDEFINITION_ID.multiplier); +} + +std::int64_t PlayerDefinition::model_id() +{ + return eios->GetLong(ref.get(), HOOK_PLAYERDEFINITION_MODELID) * HOOK_PLAYERDEFINITION_MODELID.multiplier; +} + +std::int64_t PlayerDefinition::animated_model_id() +{ + return eios->GetLong(ref.get(), HOOK_PLAYERDEFINITION_ANIMATEDMODELID) * HOOK_PLAYERDEFINITION_ANIMATEDMODELID.multiplier; +} + +bool PlayerDefinition::is_female() +{ + return eios->GetBoolean(ref.get(), HOOK_PLAYERDEFINITION_ISFEMALE); +} + +Cache PlayerDefinition::model_cache() +{ + auto cache = eios->GetObject(nullptr, HOOK_PLAYERDEFINITION_MODELCACHE); + return Cache(eios, std::move(cache)); +} + +// MARK: - Player + +Player::Player(Reflector* eios, object&& ref) : eios(eios), ref(std::move(ref)) {} +Player::Player(Player&& other) : ref(std::move(other.ref)), eios(other.eios) {} + +Player& Player::operator = (Player&& other) +{ + ref = std::move(other.ref); + eios = other.eios; + return *this; +} + +Player::operator bool() const +{ + return (bool)ref; +} + +Player Player::localPlayer(Reflector* eios) +{ + auto player = eios->GetObject(nullptr, HOOK_CLIENT_LOCALPLAYER); + return Player(eios, std::move(player)); +} + +std::vector Player::getAllPlayers(Reflector* eios) +{ + std::vector result; + auto players = eios->GetArray(nullptr, HOOK_CLIENT_LOCALPLAYERS); + std::int32_t player_count = eios->GetArraySize(players.get()); + + auto array = eios->GetArrayIndex(players.get(), 0, player_count); + + for (std::int32_t i = 0; i < player_count; ++i) + { + if (array[i]) + { + result.push_back(Player(eios, eios->make_safe(array[i]))); + } + } + return result; +} + +std::string Player::name() +{ + auto name_info = eios->GetObject(ref.get(), HOOK_PLAYER_NAME); + if (name_info) + { + auto name = eios->GetString(name_info.get(), HOOK_NAMEINFO_NAME); + return name; + } + return std::string(); +} + +std::int32_t Player::orientation() +{ + return eios->GetInt(ref.get(), HOOK_ENTITY_ORIENTATION) * static_cast(HOOK_ENTITY_ORIENTATION.multiplier); +} + +std::int32_t Player::getX() +{ + return eios->GetInt(ref.get(), HOOK_ENTITY_LOCALX) * static_cast(HOOK_ENTITY_LOCALX.multiplier); +} + +std::int32_t Player::getY() +{ + return eios->GetInt(ref.get(), HOOK_ENTITY_LOCALY) * static_cast(HOOK_ENTITY_LOCALY.multiplier); +} + +PlayerDefinition Player::definition() +{ + auto definition = eios->GetObject(ref.get(), HOOK_PLAYER_DEFINITION); + return PlayerDefinition(eios, std::move(definition)); +} + +Model Player::model() +{ + PlayerDefinition def = this->definition(); + if (!def) + { + return Model(eios, nullptr); + } + + std::int64_t modelId = def.animated_model_id(); + + Cache cache = def.model_cache(); + if (!cache) + { + return Model(eios, nullptr); + } + + HashTable hashTable = cache.hash_table(); + if (!hashTable) + { + return Model(eios, nullptr); + } + + std::vector buckets = hashTable.buckets(); + std::vector objectsToRelease; + objectsToRelease.reserve(buckets.size()); + + if (!buckets.empty()) + { + std::int64_t index = (modelId & (hashTable.size() - 1)); + Node& head = buckets[index]; + Node current = head.next(); + + while (head.uid() != current.uid()) + { + std::int64_t uid = current.uid(); + if (uid == modelId) + { + eios->ReleaseObjects(&objectsToRelease[0], objectsToRelease.size()); + return Model(eios, current.getRef()); + } + + if (uid == -1) + { + break; + } + + Node next = current.next(); + objectsToRelease.push_back(current.release()); + current = std::move(next); + } + } + + eios->ReleaseObjects(&objectsToRelease[0], objectsToRelease.size()); + return Model(eios, nullptr); +} + +void draw_player(std::function renderer) +{ + extern std::unique_ptr control_center; + Reflector* eios = nullptr; //get_reflector(control_center->get_reflector()); + + static bool isAttached = false; + if (!isAttached) + { + //control_center->get_reflector()->AttachAsDaemon(); + } + + static int amount = 1; + static double time = 0.0f; + auto start = std::chrono::high_resolution_clock::now(); + + std::vector players; + players.push_back(Player::localPlayer(eios)); + + std::int32_t cameraX = Camera::getX(eios); + std::int32_t cameraY = Camera::getY(eios); + std::int32_t cameraZ = Camera::getZ(eios); + std::int32_t cameraPitch = Camera::pitch(eios); + std::int32_t cameraYaw = Camera::yaw(eios); + std::int32_t clientPlane = Client::plane(eios); + + std::int32_t viewPortWidth = Client::viewPortWidth(eios); + std::int32_t viewPortHeight = Client::viewPortHeight(eios); + std::int32_t viewPortScale = Client::viewPortScale(eios); + + for (auto& player : players) + { + if (player && !player.name().empty()) + { + std::int32_t width = control_center->get_width(); + std::int32_t height = control_center->get_height(); + + std::int32_t playerOrientation = player.orientation(); + std::int32_t orientation = ((playerOrientation & 0x3FFF) + 1024) % 2048; + + + Model model = player.model(); + auto triangles = model.getRotatedTriangles(orientation); + + int localX = player.getX(); + int localY = player.getY(); + + std::vector polygons; + for (std::size_t i = 0; i < triangles.size(); ++i) + { + Point vx = triangles[i].a; + Point vy = triangles[i].b; + Point vz = triangles[i].c; + + Point a = Projector::toScreen(eios, vx, localX, localY, -vx.y, + cameraX, cameraY, cameraZ, cameraPitch, + cameraYaw, clientPlane, + viewPortWidth, viewPortHeight, + viewPortScale); + + Point b = Projector::toScreen(eios, vy, localX, localY, -vy.y, + cameraX, cameraY, cameraZ, cameraPitch, + cameraYaw, clientPlane, + viewPortWidth, viewPortHeight, + viewPortScale); + + Point c = Projector::toScreen(eios, vz, localX, localY, -vz.y, + cameraX, cameraY, cameraZ, cameraPitch, + cameraYaw, clientPlane, + viewPortWidth, viewPortHeight, + viewPortScale); + polygons.push_back(Polygon({a.x, b.x, c.x}, {a.y, b.y, c.y}, 3)); + } + + for (Polygon &p : polygons) + { + renderer(&p, width, height); + } + } + + auto end = std::chrono::high_resolution_clock::now(); + time += std::chrono::duration_cast(end - start).count(); + + printf("TIME: %.2f\n", time / (double)amount); + ++amount; + } + + players.clear(); +} + + + +//void DrawLine(CGLContextObj ctx, jint x1, jint y1, jint x2, jint y2) +//{ +// CGLContextObj CGL_MACRO_CONTEXT = ctx; +// +// glBegin(GL_LINES); +// if (y1 == y2) { +// GLfloat fx1 = (GLfloat)x1; +// GLfloat fx2 = (GLfloat)x2; +// GLfloat fy = ((GLfloat)y1) + 0.2f; +// +// if (x1 > x2) { +// GLfloat t = fx1; fx1 = fx2; fx2 = t; +// } +// +// glVertex2f(fx1+0.2f, fy); +// glVertex2f(fx2+1.2f, fy); +// } else if (x1 == x2) { +// GLfloat fx = ((GLfloat)x1) + 0.2f; +// GLfloat fy1 = (GLfloat)y1; +// GLfloat fy2 = (GLfloat)y2; +// +// if (y1 > y2) { +// GLfloat t = fy1; fy1 = fy2; fy2 = t; +// } +// +// glVertex2f(fx, fy1+0.2f); +// glVertex2f(fx, fy2+1.2f); +// } else { +// GLfloat fx1 = (GLfloat)x1; +// GLfloat fy1 = (GLfloat)y1; +// GLfloat fx2 = (GLfloat)x2; +// GLfloat fy2 = (GLfloat)y2; +// +// if (x1 < x2) { +// fx1 += 0.2f; +// fx2 += 1.0f; +// } else { +// fx1 += 0.8f; +// fx2 -= 0.2f; +// } +// +// if (y1 < y2) { +// fy1 += 0.2f; +// fy2 += 1.0f; +// } else { +// fy1 += 0.8f; +// fy2 -= 0.2f; +// } +// +// glVertex2f(fx1, fy1); +// glVertex2f(fx2, fy2); +// } +// glEnd(); +//} +// +//void DrawPoly(CGLContextObj ctx, +// jint nPoints, jint isClosed, +// jint transX, jint transY, +// jint *xPoints, jint *yPoints) +//{ +// CGLContextObj CGL_MACRO_CONTEXT = ctx; +// jboolean isEmpty = JNI_TRUE; +// jint mx, my; +// jint i; +// +// if (xPoints == NULL || yPoints == NULL) +// { +// return; +// } +// +// mx = xPoints[0]; +// my = yPoints[0]; +// +// glBegin(GL_LINE_STRIP); +// for (i = 0; i < nPoints; i++) { +// jint x = xPoints[i]; +// jint y = yPoints[i]; +// +// isEmpty = isEmpty && (x == mx && y == my); +// glVertex2f((GLfloat)(x + transX) + 0.5f, +// (GLfloat)(y + transY) + 0.5f); +// } +// +// if (isClosed && !isEmpty && +// (xPoints[nPoints-1] != mx || +// yPoints[nPoints-1] != my)) +// { +// glVertex2f((GLfloat)(mx + transX) + 0.5f, +// (GLfloat)(my + transY) + 0.5f); +// glEnd(); +// } else if (!isClosed || isEmpty) { +// glBegin(GL_LINES); +// mx = xPoints[nPoints-1] + transX; +// my = yPoints[nPoints-1] + transY; +// glVertex2i(mx, my); +// glVertex2i(mx+1, my+1); +// } else { +// glEnd(); +// } +//} +// +//void DrawPolyLines(CGLContextObj ctx, std::int32_t npoints, std::int32_t* xpoints, std::int32_t* ypoints) +//{ +// std::int32_t size = npoints - 2; +// for (int i = 0; i <= size; ++i) +// { +// DrawLine(ctx, xpoints[i], ypoints[i], xpoints[i + 1], ypoints[i + 1]); +// } +// +// DrawLine(ctx, xpoints[size + 1], ypoints[size + 1], xpoints[0], ypoints[0]); +//} +// +// +// +// +//void DrawLine2(int x, int y, int x2, int y2, std::function plot) +//{ +// bool yLonger=false; +// int incrementVal; +// int shortLen=y2-y; +// int longLen=x2-x; +// +// if (abs(shortLen)>abs(longLen)) +// { +// int swap=shortLen; +// shortLen=longLen; +// longLen=swap; +// yLonger=true; +// } +// +// if (longLen<0) incrementVal=-1; +// else incrementVal=1; +// +// double multDiff; +// if (longLen==0.0) multDiff=(double)shortLen; +// else multDiff=(double)shortLen/(double)longLen; +// if (yLonger) +// { +// for (int i=0;i!=longLen;i+=incrementVal) +// { +// plot(x+(int)((double)i*multDiff),y+i); +// } +// } +// else +// { +// for (int i=0;i!=longLen;i+=incrementVal) +// { +// plot(x+i,y+(int)((double)i*multDiff)); +// } +// } +//} +// +//void DrawPolyLines2(std::int32_t npoints, std::int32_t* xpoints, std::int32_t* ypoints, std::function plot) +//{ +// std::int32_t size = npoints - 2; +// for (int i = 0; i <= size; ++i) +// { +// DrawLine2(xpoints[i], ypoints[i], xpoints[i + 1], ypoints[i + 1], plot); +// } +// +// DrawLine2(xpoints[size + 1], ypoints[size + 1], xpoints[0], ypoints[0], plot); +//} +#endif \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Hooks/ModelRendering.hxx b/third-party/RemoteInput/RemoteInput/Hooks/ModelRendering.hxx new file mode 100644 index 0000000..c2986b5 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Hooks/ModelRendering.hxx @@ -0,0 +1,237 @@ +// +// ModelRendering.hpp +// RemoteInput +// +// Created by Brandon on 2019-12-08. +// Copyright © 2019 XIO. All rights reserved. +// + +#ifndef ModelRendering_hpp +#define ModelRendering_hpp + +#if defined(DEBUG) +#include +#include +#include +#include "JNI_Common.hxx" +#include "Reflection.hxx" + +class Reflector; + +Reflector* get_reflector(Reflection* reflection); + +static const double UNIT = M_PI / 1024.0; +static int SINE[2048] = {0}; +static int COSINE[2048] = {0}; + +struct Point +{ + std::int32_t x; + std::int32_t y; + std::int32_t z; + + Point() : x(0), y(0), z(0) {} + Point(std::int32_t x, std::int32_t y, std::int32_t z = 0) : x(x), y(y), z(z) {} + + Point rotate(std::int32_t orientation); +}; + +struct Triangle +{ + Point a; + Point b; + Point c; + + Triangle() {} + Triangle(Point a, Point b, Point c) : a(a), b(b), c(c) {} +}; + +struct Polygon +{ + std::int32_t npoints; + std::vector xpoints; + std::vector ypoints; + + Polygon() {} + Polygon(std::vector xpoints, std::vector ypoints, std::int32_t npoints); +}; + +class Client +{ +private: + typedef std::unique_ptr> object; + +public: + static std::uint8_t getTileSettings(Reflector* eios, std::int32_t x, std::int32_t y, std::int32_t z); + static std::int32_t getTileHeights(Reflector* eios, std::int32_t x, std::int32_t y, std::int32_t z); + static std::int32_t plane(Reflector* eios); + static std::int32_t viewPortWidth(Reflector* eios); + static std::int32_t viewPortHeight(Reflector* eios); + static std::int32_t viewPortScale(Reflector* eios); +}; + +class Camera +{ +private: + typedef std::unique_ptr> object; + +public: + static std::int32_t getX(Reflector* eios); + static std::int32_t getY(Reflector* eios); + static std::int32_t getZ(Reflector* eios); + static std::int32_t pitch(Reflector* eios); + static std::int32_t yaw(Reflector* eios); +}; + +class Projector +{ +private: + static std::int32_t getTileHeight(Reflector* eios, std::int32_t x, std::int32_t y, std::int32_t plane); + +public: + static Point toScreen(Reflector* eios, Point p, std::int32_t localX, std::int32_t localY, std::int32_t plane); + + static Point toScreen(Reflector* eios, Point p, std::int32_t localX, std::int32_t localY, std::int32_t plane, + std::int32_t cameraX, std::int32_t cameraY, std::int32_t cameraZ, + std::int32_t cameraPitch, std::int32_t cameraYaw, + std::int32_t clientPlane, + std::int32_t viewPortWidth, std::int32_t viewPortHeight, + std::int32_t viewPortScale); +}; + +class Model +{ +private: + typedef std::unique_ptr> object; + object ref; + Reflector* eios; + + std::vector vertices; + std::vector triangles; + +public: + Model(Reflector* eios, object&& ref); + Model(Model&& other); + Model& operator = (Model&& other); + + object getRef() { return std::move(ref); } + void scale(std::int32_t x, std::int32_t y, std::int32_t z); + std::vector getVertices(); + std::vector getTriangles(); + std::vector getRotatedTriangles(std::int32_t orientation); + operator bool() const; +}; + +class Node +{ +private: + typedef std::unique_ptr> object; + object ref; + Reflector* eios; + +public: + Node() {} + Node(Reflector* eios, object&& ref); + Node(Node&& other); + + Node& operator = (Node&& other); + object getRef() { return std::move(ref); } + void* release() { return ref.release(); } + std::int64_t uid(); + Node next(); + + operator bool() const; +}; + + +class HashTable +{ +private: + typedef std::unique_ptr> object; + object ref; + Reflector* eios; + +public: + HashTable(Reflector* eios, object&& ref); + + object getRef() { return std::move(ref); } + std::int32_t size(); + std::vector buckets(); + + operator bool() const; +}; + +class Cache +{ +private: + typedef std::unique_ptr> object; + object ref; + Reflector* eios; + +public: + Cache(Reflector* eios, object&& ref); + Cache(Cache&& other); + + Cache& operator = (Cache&& other); + + object getRef() { return std::move(ref); } + HashTable hash_table(); + + operator bool() const; +}; + +class PlayerDefinition +{ +private: + typedef std::unique_ptr> object; + object ref; + Reflector* eios; + +public: + PlayerDefinition(Reflector* eios, object&& ref); + PlayerDefinition(PlayerDefinition&& other); + + PlayerDefinition& operator = (PlayerDefinition&& other); + + object getRef() { return std::move(ref); } + std::int32_t id(); + std::int64_t model_id(); + std::int64_t animated_model_id(); + bool is_female(); + Cache model_cache(); + + operator bool() const; +}; + +class Player +{ +private: + typedef std::unique_ptr> object; + object ref; + Reflector* eios; + +public: + Player(Reflector* eios, object&& ref); + Player(Player&& other); + Player& operator = (Player&& other); + + object getRef() { return std::move(ref); } + static Player localPlayer(Reflector* eios); + static std::vector getAllPlayers(Reflector* eios); + + std::string name(); + std::int32_t orientation(); + std::int32_t getX(); + std::int32_t getY(); + PlayerDefinition definition(); + Model model(); + + operator bool() const; +}; + + +void draw_polygon(std::uint8_t* dest, std::int32_t npoints, std::int32_t* xpoints, std::int32_t* ypoints, std::int32_t width, std::int32_t height); +void draw_player(std::function renderer); +#endif + +#endif /* ModelRendering_hpp */ diff --git a/third-party/RemoteInput/RemoteInput/Info.plist b/third-party/RemoteInput/RemoteInput/Info.plist new file mode 100644 index 0000000..1accca5 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSHumanReadableCopyright + Copyright © 2019 XIO. All rights reserved. + + diff --git a/third-party/RemoteInput/RemoteInput/Injection/Injector.hxx b/third-party/RemoteInput/RemoteInput/Injection/Injector.hxx new file mode 100644 index 0000000..0f15f0f --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Injection/Injector.hxx @@ -0,0 +1,17 @@ +// +// Created by Brandon on 2020-05-02. +// + +#ifndef REMOTEINPUT_INJECTOR_HXX +#define REMOTEINPUT_INJECTOR_HXX + +#include +#include + +class Injector +{ +public: + static bool Inject(std::string module_path, std::int32_t pid, void* bootstrap) noexcept; +}; + +#endif //REMOTEINPUT_INJECTOR_HXX diff --git a/third-party/RemoteInput/RemoteInput/Injection/Injector_Arm.cpp b/third-party/RemoteInput/RemoteInput/Injection/Injector_Arm.cpp new file mode 100644 index 0000000..dc99245 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Injection/Injector_Arm.cpp @@ -0,0 +1,431 @@ +// +// Created by Brandon on 2020-05-02. +// + +#include "Injector.hxx" + +#if !defined(__APPLE__) +#if defined(__aarch64__) || defined(__arm__) +#include +#include +#include +#include +//#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#endif + +//SYSCall calling conventions: man7.org/linux/man-pages/man2/syscall.2.html + +#if defined(__arm__) +#define CPSR_THUMB_MODE_BIT (1u << 5) +#endif + +#if defined(__aarch64__) || defined(__arm__) +auto find_library = [](std::int32_t pid, const char* library_name) -> std::uintptr_t { + char file_map_name[256] = {0}; + snprintf(file_map_name, sizeof(file_map_name) - 1, "/proc/%d/maps", pid); + + std::fstream file = std::fstream(file_map_name, std::ios::in | std::ios::binary); + if (file) + { + std::string line; + while(std::getline(file, line)) + { + if (line.find(library_name) != std::string::npos) + { + void* start_address = nullptr; + void* end_address = nullptr; + sscanf(line.c_str(), "%p-%p", &start_address, &end_address); + return reinterpret_cast(start_address); + } + } + } + return reinterpret_cast(nullptr); +}; + +auto find_symbol = [](std::int32_t pid, const char* library_name, void* local_symbol) -> std::uintptr_t { + std::intptr_t remote_library_address = find_library(pid, library_name); + std::intptr_t local_library_address = [local_symbol]() -> std::intptr_t { + Dl_info info = {0}; + dladdr(local_symbol, &info); + return reinterpret_cast(info.dli_fbase); + }(); + + if (remote_library_address && local_library_address) + { + return reinterpret_cast(local_symbol) + (remote_library_address - local_library_address); + } + return reinterpret_cast(nullptr); +}; + +auto read_vm_memory = [](std::int32_t pid, void* dest, void* src, std::size_t size) { + iovec local_vec = {0}; + local_vec.iov_base = dest; + local_vec.iov_len = size; + + iovec remote_vec = {0}; + remote_vec.iov_base = src; + remote_vec.iov_len = size; + + process_vm_readv(pid, &local_vec, 1, &remote_vec, 1, 0); +}; + +auto write_vm_memory = [](std::int32_t pid, void* dest, void* src, std::size_t size) { + iovec local_vec = {0}; + local_vec.iov_base = src; + local_vec.iov_len = size; + + iovec remote_vec = {0}; + remote_vec.iov_base = dest; + remote_vec.iov_len = size; + + process_vm_writev(pid, &local_vec, 1, &remote_vec, 1, 0); +}; + +auto get_signal = [](int status, int expected, const char* message) -> int { + if (WIFSTOPPED(status)) + { + if (WSTOPSIG(status) == expected) + { + return 0; + } + + if (WSTOPSIG(status) == 11) + { + printf("PROCESS DIED: %s\n", message); + return -1; + } + + printf("STOPPED FOR SOME OTHER SIGNAL: %s\n", message); + return -1; + } + return 0; +}; +#endif + +#if defined(__aarch64__) +auto remote_alloc = [](std::int32_t pid, std::size_t size) -> void* { + struct user_regs_struct registers = {0}; + struct user_regs_struct registers_backup = {0}; + iovec regs_vec = { .iov_base = ®isters, .iov_len = sizeof(registers) }; + ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, ®s_vec); + memcpy(®isters_backup, ®isters, sizeof(registers)); + + long pc = ptrace(PTRACE_PEEKDATA, pid, registers.pc, nullptr); //pc -- program counter + + //x0, x1, x2, x3, x4, x5 + registers.regs[0] = reinterpret_cast(nullptr); //addr + registers.regs[1] = size; //length + registers.regs[2] = PROT_READ | PROT_WRITE | PROT_EXEC; //prot + registers.regs[3] = MAP_PRIVATE | MAP_ANONYMOUS; //flags + registers.regs[4] = -1; //fd + registers.regs[5] = 0; //offset + + //syscall x8 + registers.regs[8] = SYS_mmap; + + regs_vec = { .iov_base = ®isters, .iov_len = sizeof(registers) }; + ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, ®s_vec); + ptrace(PTRACE_POKEDATA, pid, registers.pc, 0xD4000001); //syscall instruction -- svc #0 + ptrace(PTRACE_SINGLESTEP, pid, nullptr, nullptr); + + int status = 0; + if (waitpid(pid, &status, 0) < 1 || get_signal(status, 5, "Alloc Mem") < 0) + { + ptrace(PTRACE_POKEDATA, pid, registers_backup.pc, pc); + ptrace(PTRACE_SETREGSET, pid, nullptr, ®isters_backup); + ptrace(PTRACE_CONT, pid, nullptr, nullptr); + return nullptr; + } + + regs_vec = { .iov_base = ®isters, .iov_len = sizeof(registers) }; + ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, ®s_vec); + + regs_vec = { .iov_base = ®isters_backup, .iov_len = sizeof(registers_backup) }; + ptrace(PTRACE_POKEDATA, pid, registers_backup.pc, pc); + ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, ®s_vec); + + return reinterpret_cast(registers.regs[0]); +}; + +auto remote_free = [](std::int32_t pid, void* memory, std::size_t size) -> int { + struct user_regs_struct registers = {0}; + struct user_regs_struct registers_backup = {0}; + iovec regs_vec = { .iov_base = ®isters, .iov_len = sizeof(registers) }; + ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, ®s_vec); + memcpy(®isters_backup, ®isters, sizeof(registers)); + + long pc = ptrace(PTRACE_PEEKDATA, pid, registers.pc, nullptr); //pc -- program counter + + //x0, x1, x2, x3, x4, x5 + registers.regs[0] = reinterpret_cast(memory); //address + registers.regs[1] = size; //length + registers.regs[2] = 0; + registers.regs[3] = 0; + registers.regs[4] = 0; + registers.regs[5] = 0; + + //syscall x8 + registers.regs[8] = SYS_munmap; + + regs_vec = { .iov_base = ®isters, .iov_len = sizeof(registers) }; + ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, ®s_vec); + ptrace(PTRACE_POKEDATA, pid, registers.pc, 0xD4000001); //syscall instruction -- svc #0 + ptrace(PTRACE_SINGLESTEP, pid, nullptr, nullptr); + + int status = 0; + if (waitpid(pid, &status, 0) < 1 || get_signal(status, 5, "Free Mem") < 0) + { + ptrace(PTRACE_POKEDATA, pid, registers_backup.pc, pc); + ptrace(PTRACE_SETREGSET, pid, nullptr, ®isters_backup); + ptrace(PTRACE_CONT, pid, nullptr, nullptr); + return -1; + } + + regs_vec = { .iov_base = ®isters, .iov_len = sizeof(registers) }; + ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, ®s_vec); + + regs_vec = { .iov_base = ®isters_backup, .iov_len = sizeof(registers_backup) }; + ptrace(PTRACE_POKEDATA, pid, registers_backup.pc, pc); + ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, ®s_vec); + + return static_cast(registers.regs[0]); +}; + +auto remote_dlopen = [](std::int32_t pid, std::uintptr_t dlopen_address, std::uintptr_t remote_path, int flags) -> void* { + struct user_regs_struct registers = {0}; + struct user_regs_struct registers_backup = {0}; + iovec regs_vec = { .iov_base = ®isters, .iov_len = sizeof(registers) }; + ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, ®s_vec); + memcpy(®isters_backup, ®isters, sizeof(registers)); + + long pc = ptrace(PTRACE_PEEKDATA, pid, registers.pc, nullptr); //pc -- program counter + + //x0, x1, x2, x3 + registers.regs[0] = remote_path; + registers.regs[1] = flags; + registers.regs[2] = 0; + registers.regs[3] = 0; + registers.regs[4] = 0; + registers.regs[5] = 0; + registers.regs[6] = dlopen_address; + + regs_vec = { .iov_base = ®isters, .iov_len = sizeof(registers) }; + ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, ®s_vec); + ptrace(PTRACE_POKEDATA, pid, registers.pc, 0xD4200000D63F00C0); //blr x6 (0xD63F00C0), 0xD420000 (brk #0) + ptrace(PTRACE_CONT, pid, nullptr, nullptr); + + int status = 0; + if (waitpid(pid, &status, 0) < 1 || get_signal(status, 5, "remote dlopen") < 0) + { + ptrace(PTRACE_POKEDATA, pid, registers_backup.pc, pc); + ptrace(PTRACE_SETREGSET, pid, nullptr, ®isters_backup); + ptrace(PTRACE_CONT, pid, nullptr, nullptr); + return nullptr; + } + + regs_vec = { .iov_base = ®isters, .iov_len = sizeof(registers) }; + ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, ®s_vec); + + regs_vec = { .iov_base = ®isters_backup, .iov_len = sizeof(registers_backup) }; + ptrace(PTRACE_POKEDATA, pid, registers_backup.pc, pc); + ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, ®s_vec); + + return reinterpret_cast(registers.regs[0]); +}; +#endif + +#if defined(__arm__) +auto remote_alloc = [](std::int32_t pid, std::size_t size) -> void* { + struct user_regs registers = {0}; + struct user_regs registers_backup = {0}; + ptrace(PTRACE_GETREGS, pid, nullptr, ®isters); + memcpy(®isters_backup, ®isters, sizeof(registers)); + + long pc = ptrace(PTRACE_PEEKDATA, pid, registers.uregs[15], nullptr); //pc -- program counter + + //r0, r1, r2, r3, r4, r5 + registers.uregs[0] = reinterpret_cast(nullptr); //addr + registers.uregs[1] = size; //length + registers.uregs[2] = PROT_READ | PROT_WRITE | PROT_EXEC; //prot + registers.uregs[3] = MAP_PRIVATE | MAP_ANONYMOUS; //flags + registers.uregs[4] = -1; //fd + registers.uregs[5] = 0; //offset + + //syscall r7 + registers.uregs[7] = SYS_mmap2; + registers.uregs[16] &= ~CPSR_THUMB_MODE_BIT; + + ptrace(PTRACE_SETREGS, pid, nullptr, ®isters); + ptrace(PTRACE_POKEDATA, pid, registers.uregs[15], 0xEF000000); //syscall instruction -- swi 0x0 -- software interrupt + ptrace(PTRACE_SINGLESTEP, pid, nullptr, nullptr); + + int status = 0; + if (waitpid(pid, &status, 0) < 1 || get_signal(status, 5, "Alloc Mem") < 0) + { + ptrace(PTRACE_POKEDATA, pid, registers_backup.uregs[15], pc); + ptrace(PTRACE_SETREGSET, pid, nullptr, ®isters_backup); + ptrace(PTRACE_CONT, pid, nullptr, nullptr); + return nullptr; + } + + ptrace(PTRACE_GETREGS, pid, nullptr, ®isters); + ptrace(PTRACE_POKEDATA, pid, registers_backup.uregs[15], pc); + ptrace(PTRACE_SETREGS, pid, nullptr, ®isters_backup); + + return reinterpret_cast(registers.uregs[0]); +}; + +auto remote_free = [](std::int32_t pid, void* memory, std::size_t size) -> int { + struct user_regs registers = {0}; + struct user_regs registers_backup = {0}; + ptrace(PTRACE_GETREGS, pid, nullptr, ®isters); + memcpy(®isters_backup, ®isters, sizeof(registers)); + + long pc = ptrace(PTRACE_PEEKDATA, pid, registers.uregs[15], nullptr); //pc -- program counter + + //r0, r1, r2, r3, r4, r5 + registers.uregs[0] = reinterpret_cast(memory); //address + registers.uregs[1] = size; //length + registers.uregs[2] = 0; + registers.uregs[3] = 0; + registers.uregs[4] = 0; + registers.uregs[5] = 0; + + //syscall r7 + registers.uregs[7] = SYS_munmap; + registers.uregs[16] &= ~CPSR_THUMB_MODE_BIT; + + ptrace(PTRACE_SETREGS, pid, nullptr, ®isters); + ptrace(PTRACE_POKEDATA, pid, registers.uregs[15], 0xEF000000); //syscall instruction -- swi 0x0 -- software interrupt + ptrace(PTRACE_SINGLESTEP, pid, nullptr, nullptr); + + int status = 0; + if (waitpid(pid, &status, 0) < 1 || get_signal(status, 5, "Free Mem") < 0) + { + ptrace(PTRACE_POKEDATA, pid, registers_backup.uregs[15], pc); + ptrace(PTRACE_SETREGSET, pid, nullptr, ®isters_backup); + ptrace(PTRACE_CONT, pid, nullptr, nullptr); + return -1; + } + + ptrace(PTRACE_GETREGS, pid, nullptr, ®isters); + ptrace(PTRACE_POKEDATA, pid, registers_backup.uregs[15], pc); + ptrace(PTRACE_SETREGS, pid, nullptr, ®isters_backup); + + return static_cast(registers.uregs[0]); +}; + +auto remote_dlopen = [](std::int32_t pid, std::uintptr_t dlopen_address, std::uintptr_t remote_path, int flags) -> void* { + struct user_regs registers = {0}; + struct user_regs registers_backup = {0}; + ptrace(PTRACE_GETREGS, pid, nullptr, ®isters); + memcpy(®isters_backup, ®isters, sizeof(registers)); + + long pc = ptrace(PTRACE_PEEKDATA, pid, registers.uregs[15], nullptr); //pc -- program counter + + //r0, r1, r2, r3, r4, r5 + registers.uregs[0] = remote_path; + registers.uregs[1] = flags; + registers.uregs[2] = 0; + registers.uregs[3] = 0; + registers.uregs[4] = 0; + registers.uregs[5] = 0; + registers.uregs[6] = dlopen_address; + + registers.uregs[15] = dlopen_address; + registers.uregs[16] &= ~CPSR_THUMB_MODE_BIT; + + ptrace(PTRACE_SETREGS, pid, nullptr, ®isters); + ptrace(PTRACE_POKEDATA, pid, registers.uregs[15], 0xE7F001F0D63F00C0); //blx r6 (0xD63F00C0), 0xE1200070 (bkpt #0), 0xE7F001F0 (undefined instruction breakpoint) + ptrace(PTRACE_CONT, pid, nullptr, nullptr); + + int status = 0; + if (waitpid(pid, &status, 0) < 1 || get_signal(status, 5, "remote dlopen") < 0) + { + ptrace(PTRACE_POKEDATA, pid, registers_backup.uregs[15], pc); + ptrace(PTRACE_SETREGS, pid, nullptr, ®isters_backup); + ptrace(PTRACE_CONT, pid, nullptr, nullptr); + return nullptr; + } + + ptrace(PTRACE_GETREGS, pid, nullptr, ®isters); + ptrace(PTRACE_POKEDATA, pid, registers_backup.uregs[15], pc); + ptrace(PTRACE_SETREGS, pid, nullptr, ®isters_backup); + + return reinterpret_cast(registers.uregs[0]); +}; +#endif + + +#if defined(__aarch64__) || defined(__arm__) +bool Injector::Inject(std::string module_path, std::int32_t pid, void* bootstrap) noexcept +{ + //Determine if the module is already injected + if (find_library(pid, basename(module_path.c_str()))) + { + return false; + } + + //Get ASLR address of dlopen + std::uintptr_t dlopen_address = find_symbol(pid, "libc.so", reinterpret_cast(dlopen)); + if (!dlopen_address) + { + dlopen_address = find_symbol(pid, "libdl.so", reinterpret_cast(dlopen)); + } + + if (!dlopen_address) + { + return false; + } + + if (ptrace(PTRACE_ATTACH, pid, nullptr, nullptr) < 0) + { + return false; + } + + int status = 0; + if (waitpid(pid, &status, 0) < 1 || get_signal(status, 19, "Injector Attach") < 0) + { + return false; + } + + //Allocate path + void* remote_path = remote_alloc(pid, module_path.length() + 1); + if (remote_path) + { + //Write the remote path.. + write_vm_memory(pid, remote_path, &module_path[0], module_path.length() + 1); + + //Load the module + void* result = remote_dlopen(pid, dlopen_address, reinterpret_cast(remote_path), RTLD_LAZY); + + //Free path + remote_free(pid, remote_path, module_path.length() + 1); + ptrace(PTRACE_CONT, pid, nullptr, nullptr); + + if (!result || result == reinterpret_cast(dlopen_address)) + { + ptrace(PTRACE_DETACH, pid, nullptr, nullptr); + return false; + } + + ptrace(PTRACE_DETACH, pid, nullptr, nullptr); + return true; + } + + ptrace(PTRACE_DETACH, pid, nullptr, nullptr); + return false; +} +#endif +#endif \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Injection/Injector_Darwin.cxx b/third-party/RemoteInput/RemoteInput/Injection/Injector_Darwin.cxx new file mode 100644 index 0000000..389c492 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Injection/Injector_Darwin.cxx @@ -0,0 +1,1324 @@ +// +// Created by Brandon on 2020-05-02. +// + +#include "Injector.hxx" + +#if defined(__APPLE__) +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#endif + +//#define OLD_INJECTION_CODE + +#if defined(__APPLE__) +//Strip the module name from its path +auto strip_path = [](const std::string &path_to_strip) -> std::string { + std::string::size_type pos = path_to_strip.find_last_of("\\/"); + if (pos != std::string::npos) + { + //Strip path components + std::string path = path_to_strip.substr(pos + 1); + pos = path.find_last_of('.'); + + //Strip extension + return pos > 0 && pos != std::string::npos ? path.substr(0, pos) : path; + } + return path_to_strip; +}; + +// Find shared cache slide +auto find_shared_cache_slide = [](mach_port_t task) -> std::intptr_t { + task_dyld_info_data_t info; + mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; + task_info(task, TASK_DYLD_INFO, reinterpret_cast(&info), &count); + + //Get the loaded dylibs/images + dyld_all_image_infos infos = {0}; + mach_vm_size_t size = info.all_image_info_size; + kern_return_t err = mach_vm_read_overwrite(task, info.all_image_info_addr, info.all_image_info_size, reinterpret_cast(&infos), &size); + return err == KERN_SUCCESS ? infos.sharedCacheSlide : static_cast(NULL); +}; + +#if defined(__aarch64__) +//Calls dlopen inside of a pthread in the remote target +auto remote_load_library = [](std::size_t* instructions_size) -> std::uint8_t* { + #if defined(__clang__) + static std::uint8_t assembly[] = { + 0xFD, 0x7B, 0xBD, 0xA9, //stp x29, x30, [sp, #-48]! + 0xF5, 0x0B, 0x00, 0xF9, //str x21, [sp, #16] + 0xF4, 0x4F, 0x02, 0xA9, //stp x20, x19, [sp, #32] + 0xFD, 0x03, 0x00, 0x91, //mov x29, sp + 0x02, 0x4C, 0x40, 0xA9, //ldp x2, x19, [x0] //_dlopen_pointer/_pthread_callback = data[0] + 0x08, 0x50, 0x41, 0xA9, //ldp x8, x20, [x0, #16] //_pthread_create_from_mach_thread = data[2] + 0x15, 0x10, 0x40, 0xF9, //ldr x21, [x0, #32] //_mach_thread_self = data[4] + 0xBF, 0x0F, 0x00, 0xF9, //str xzr, [x29, #24] //pthread_t thread_id = 0; + 0xE3, 0x03, 0x01, 0xAA, //mov x3, x1 //param + 0xE1, 0x03, 0x1F, 0xAA, //mov x1, xzr //nullptr + 0xA0, 0x63, 0x00, 0x91, //add x0, x29, #24 //&thread_id + 0x00, 0x01, 0x3F, 0xD6, //blr x8 //_pthread_create_from_mach_thread(&thread_id, nullptr, _dlopen_pointer, param) + 0xEA, 0x03, 0x00, 0xAA, //mov x10, x0 //store return value in x10 + 0xA0, 0x0F, 0x40, 0xF9, //ldr x0, [x29, #24] //thread_id + 0xEB, 0x03, 0x00, 0xAA, //mov x11, x0 //store thread_id in x11 + 0x60, 0x02, 0x3F, 0xD6, //blr x19 //_pthread_set_self(thread_id) + 0xA0, 0x02, 0x3F, 0xD6, //blr x21 //_mach_thread_self() + 0x80, 0x02, 0x3F, 0xD6, //blr x20 //_thread_suspend(_mach_thread_self()) + 0xA0, 0x0F, 0x40, 0xF9, //ldr x0, [x29, #24] //return thread_id + 0xF4, 0x4F, 0x42, 0xA9, //ldp x20, x19, [sp, #32] + 0xF5, 0x0B, 0x40, 0xF9, //ldr x21, [sp, #16] + 0xFD, 0x7B, 0xC3, 0xA8, //ldp x29, x30, [sp], #48 + 0xC0, 0x03, 0x5F, 0xD6, //ret + + 0x1F, 0x20, 0x03, 0xD5, + 0x1F, 0x20, 0x03, 0xD5, + 0x1F, 0x20, 0x03, 0xD5, //nop + 0x1F, 0x20, 0x03, 0xD5, + 0x1F, 0x20, 0x03, 0xD5, + + 0x41, 0x00, 0x80, 0x52, //mov w1, #2 //RTLD_LAZY = 0x1 -- RTLD_NOW = 0x02 + 0xE2, 0xDD, 0x97, 0xD2, //mov x2, #0xBEEF //address of dlopen + 0xA2, 0xD5, 0xBB, 0xF2, //movk x2, #0xDEAD, lsl #16 //address of dlopen + 0x02, 0x00, 0xD6, 0xF2, //movk x2, #0xB000, lsl #32 //address of dlopen + 0x02, 0x00, 0xF4, 0xF2, //movk x2, #0xA000, lsl #48 //address of dlopen + 0x40, 0x00, 0x1F, 0xD6, //br x2 //call dlopen(pthread_parameters, RTLD_LAZY) + + /*pthread_t LoadLibrary(void** data, void* param) + { + void* (*_dlopen_pointer)(void* param) = (decltype(_dlopen_pointer))(data[0]); + void (*_pthread_set_self)(pthread_t thread) = (decltype(_pthread_set_self))(data[1]); + int (*_pthread_create_from_mach_thread)(pthread_t *, const pthread_attr_t *, void *(*)(void *), void *) = (decltype(_pthread_create_from_mach_thread))(data[2]); + kern_return_t (*_thread_suspend)(thread_read_t target_act) = (decltype(_thread_suspend))(data[3]); + mach_port_t (*_mach_thread_self)(void) = (decltype(_mach_thread_self))(data[4]); + + pthread_t thread_id = 0; + _pthread_create_from_mach_thread(&thread_id, nullptr, _dlopen_pointer, param); + //while (1) + //{ + // swtch_pri(0); // mach_yield + //} + + + _pthread_set_self(thread_id); + _thread_suspend(_mach_thread_self()); + return thread_id; + } + + void* _dlopen_pointer(void* param) + { + decltype(dlopen)* _dlopen = (decltype(dlopen)*)0xA000B000DEADBEEF; + return _dlopen((const char*)param, RTLD_NOW); + }*/ + }; + #else + static std::uint8_t assembly[] = { + 0xFD, 0x7B, 0xBC, 0xA9, //stp x29, x30, [sp, -64]! + 0xE4, 0x03, 0x00, 0xAA, //mov x4, x0 //_pthread_set_self = data[0] + 0xF5, 0x13, 0x00, 0xF9, //str x21, [sp, 32] + 0x95, 0x04, 0x40, 0xF9, //ldr x21, [x4, 8] //pthread_set_self = data[1] + 0xF3, 0x53, 0x01, 0xA9, //stp x19, x20, [sp, 16] + 0x85, 0x4C, 0x41, 0xA9, //ldp x5, x19, [x4, 16] //_thread_suspend = data[3] + 0x94, 0x10, 0x40, 0xF9, //ldr x20, [x4, 32] //_mach_thread_self = data[4] + 0xFD, 0x03, 0x00, 0x91, //mov x29, sp + 0x82, 0x00, 0x40, 0xF9, //ldr x2, [x4] + 0xFF, 0x1F, 0x00, 0xF9, //str xzr, [sp, 56] //pthread_t thread_id = 0; + 0xE3, 0x03, 0x01, 0xAA, //mov x3, x1 //param + 0x01, 0x00, 0x80, 0xD2, //mov x1, 0 //nullptr + 0xE0, 0xE3, 0x00, 0x91, //add x0, sp, 56 //&thread_id + 0xA0, 0x00, 0x3F, 0xD6, //blr x5 //_pthread_create_from_mach_thread(&thread_id, nullptr, _dlopen_pointer, param) + 0xEA, 0x03, 0x00, 0xAA, //mov x10, x0 //store return value in x10 + 0xE0, 0x1F, 0x40, 0xF9, //ldr x0, [sp, 56] //thread_id + 0xEB, 0x03, 0x00, 0xAA, //mov x11, x0 //store thread_id in x11 + 0xA0, 0x02, 0x3F, 0xD6, //blr x21 //_pthread_set_self(thread_id); + 0x80, 0x02, 0x3F, 0xD6, //blr x20 //_mach_thread_self(); + 0x60, 0x02, 0x3F, 0xD6, //blr x19 //_thread_suspend(_mach_thread_self()); + 0xF3, 0x53, 0x41, 0xA9, //ldp x19, x20, [sp, 16] + 0xF5, 0x13, 0x40, 0xF9, //ldr x21, [sp, 32] + 0xE0, 0x1F, 0x40, 0xF9, //ldr x0, [sp, 56] + 0xFD, 0x7B, 0xC4, 0xA8, //ldp x29, x30, [sp], 64 + 0xC0, 0x03, 0x5F, 0xD6, //ret + + 0x1F, 0x20, 0x03, 0xD5, + 0x1F, 0x20, 0x03, 0xD5, + 0x1F, 0x20, 0x03, 0xD5, //nop + 0x1F, 0x20, 0x03, 0xD5, + 0x1F, 0x20, 0x03, 0xD5, + + 0x41, 0x00, 0x80, 0x52, //mov w1, 2 //RTLD_LAZY = 0x1 -- RTLD_NOW = 0x02 + 0xE2, 0xDD, 0x97, 0xD2, //mov x2, 0xBEEF //address of dlopen + 0xA2, 0xD5, 0xBB, 0xF2, //movk x2, 0xDEAD, lsl 16 //address of dlopen + 0x02, 0x00, 0xD6, 0xF2, //movk x2, 0xB000, lsl 32 //address of dlopen + 0x02, 0x00, 0xF4, 0xF2, //movk x2, 0xA000, lsl 48 //address of dlopen + 0x40, 0x00, 0x1F, 0xD6, //br x2 //call dlopen(pthread_parameters, RTLD_LAZY) + + /*pthread_t LoadLibrary(void** data, void* param) + { + void* (*_dlopen_pointer)(void* param) = (decltype(_dlopen_pointer))(data[0]); + void (*_pthread_set_self)(pthread_t thread) = (decltype(_pthread_set_self))(data[1]); + int (*_pthread_create_from_mach_thread)(pthread_t *, const pthread_attr_t *, void *(*)(void *), void *) = (decltype(_pthread_create_from_mach_thread))(data[2]); + kern_return_t (*_thread_suspend)(thread_read_t target_act) = (decltype(_thread_suspend))(data[3]); + mach_port_t (*_mach_thread_self)(void) = (decltype(_mach_thread_self))(data[4]); + + pthread_t thread_id = 0; + _pthread_create_from_mach_thread(&thread_id, nullptr, _dlopen_pointer, param); + //while (1) + //{ + // swtch_pri(0); // mach_yield + //} + + + _pthread_set_self(thread_id); + _thread_suspend(_mach_thread_self()); + return thread_id; + } + + void* _dlopen_pointer(void* param) + { + decltype(dlopen)* _dlopen = (decltype(dlopen)*)0xA000B000DEADBEEF; + return _dlopen((const char*)param, RTLD_NOW); + }*/ + }; + #endif + *instructions_size = sizeof(assembly); + return &assembly[0]; +}; +#else +//Calls dlopen inside of a pthread in the remote target +auto remote_load_library = [](std::size_t* instructions_size) -> std::uint8_t* { + #ifndef OLD_INJECTION_CODE + static std::uint8_t assembly[] = { + 0x41, 0x57, //push r15 + 0x41, 0x56, //push r14 + 0x41, 0x54, //push r12 + 0x53, //push rbx + 0x50, //push rax + 0x48, 0x89, 0xF1, //mov rcx, rsi + 0x48, 0x89, 0xFB, //mov rbx, rdi + 0x48, 0x8B, 0x17, //mov rdx, QWORD PTR[rdi + 0x00] //_dlopen_pointer = data[0] + 0x4C, 0x8B, 0x77, 0x08, //mov r14, QWORD PTR[rdi + 0x08] //_pthread_set_self = data[1] + 0x48, 0x8B, 0x47, 0x10, //mov rax, QWORD PTR[rdi + 0x10] //_pthread_create_from_mach_thread = data[2] + 0x4C, 0x8B, 0x7F, 0x18, //mov r15, QWORD PTR[rdi + 0x18] //_thread_suspend = data[3] + 0x4C, 0x8B, 0x67, 0x20, //mov r12, QWORD PTR[rdi + 0x20] //_mach_thread_self = data[4] + 0x48, 0xC7, 0x04, 0x24, 0x00, 0x00, 0x00, 0x00, //mov QWORD PTR[rsp + 0x0], 0x00 //pthread_t thread_id = 0; + 0x48, 0x89, 0xE7, //mov rdi, rsp //&thread_id + 0x31, 0xF6, //xor esi, esi //nullptr + 0xFF, 0xD0, //call rax //_pthread_create_from_mach_thread(&thread_id, nullptr, _dlopen_pointer, param) + 0x48, 0x8B, 0x0C, 0x24, //mov rcx, QWORD PTR[rsp + 0x00] //thread_id + 0x48, 0x8B, 0x53, 0x28, //mov rdx, QWORD PTR[rbx + 0x28] //&data[5] + 0x48, 0x89, 0x0A, //mov QWORD PTR[rdx + 0x00], rcx //*data[5] = thread_id + 0x48, 0x8B, 0x4B, 0x30, //mov rcx, QWORD PTR[rbx + 0x30] //&data[6] + 0x89, 0x01, //mov DWORD PTR[rcx + 0x00], eax //*data[6] = result + 0x48, 0x8B, 0x3C, 0x24, //mov rdi, QWORD PTR[rsp + 0x00] //thread_id + 0x41, 0xFF, 0xD6, //call r14 //_pthread_set_self(thread_id) + 0x41, 0xFF, 0xD4, //call r12 //_mach_thread_self() + 0x89, 0xC7, //mov edi, eax //self_thread + 0x41, 0xFF, 0xD7, //call r15 //_thread_suspend(self_thread); + 0x48, 0x8B, 0x04, 0x24, //mov rax, QWORD PTR[rsp + 0x00] //return thread_id + 0x48, 0x83, 0xC4, 0x08, //add rsp, 0x08 + 0x5B, //pop rbx + 0x41, 0x5C, //pop r12 + 0x41, 0x5E, //pop r14 + 0x41, 0x5F, //pop r15 + 0xC3, //ret + 0x90, + 0x90, + 0x90, //nop + 0x90, + 0x90, + 0x48, 0xB8, 0xEF, 0xBE, 0xAD, 0xDE, 0x00, 0xB0, 0x00, 0xA0, //movabs rax, 0xA000B000DEADBEEF //address of dlopen + 0xBE, 0x02, 0x00, 0x00, 0x00, //mov esi, RTLD_LAZY //RTLD_LAZY = 0x1 -- RTLD_NOW = 0x02 + 0xFF, 0xE0 //jmp rax //call dlopen(pthread_parameters, RTLD_LAZY) + + /*pthread_t LoadLibrary(void** data, void* param) + { + void* (*_dlopen_pointer)(void* param) = (decltype(_dlopen_pointer))(data[0]); + void (*_pthread_set_self)(pthread_t thread) = (decltype(_pthread_set_self))(data[1]); + int (*_pthread_create_from_mach_thread)(pthread_t *, const pthread_attr_t *, void *(*)(void *), void *) = (decltype(_pthread_create_from_mach_thread))(data[2]); + kern_return_t (*_thread_suspend)(thread_read_t target_act) = (decltype(_thread_suspend))(data[3]); + mach_port_t (*_mach_thread_self)(void) = (decltype(_mach_thread_self))(data[4]); + + pthread_t thread_id = 0; + _pthread_create_from_mach_thread(&thread_id, nullptr, _dlopen_pointer, param); + //while (1) + //{ + // swtch_pri(0); // mach_yield + //} + + + _pthread_set_self(thread_id); + _thread_suspend(_mach_thread_self()); + return thread_id; + } + + void* _dlopen_pointer(void* param) + { + decltype(dlopen)* _dlopen = (decltype(dlopen)*)0xA000B000DEADBEEF; + return _dlopen((const char*)param, RTLD_NOW); + }*/ + }; + #else + static std::uint8_t assembly[] = { + 0x50, //push rax + 0x48, 0x89, 0xF1, //mov rcx, rsi + 0x48, 0x8B, 0x17, //mov rdx, qword ptr[rdi] //_pthread_callback = data[0] + 0x48, 0x8B, 0x47, 0x08, //mov rax, qword ptr[rdi + 8] //_pthread_create_from_mach_thread = data[1] + 0x48, 0xC7, 0x04, 0x24, 0x00, 0x00, 0x00, 0x00, //mov qword ptr[rsp], 0 //pthread thread_id = 0 + 0x48, 0x89, 0xE7, //mov rdi, rsp + 0x31, 0xF6, //xor esi, esi + 0xFF, 0xD0, //call rax //call _pthread_create_from_mach_thread(&thread_id, nullptr, _pthread_callback, param) + 0x4C, 0x8B, 0x24, 0x24, //mov r12, qword ptr[rsp] //return pthread_id + 0x59, //pop rcx + 0xEB, 0xFE, //jmp 0x00 //Do not return to callee + 0xC3, //ret + 0x90, + 0x90, + 0x90, //nop + 0x90, + 0x90, + 0x48, 0xB8, 0xEF, 0xBE, 0xAD, 0xDE, 0x00, 0x00, 0x00, 0x00, //movabs rax, 0x0000DEADBEEF //address of dlopen + 0xBE, 0x01, 0x00, 0x00, 0x00, //mov esi, RTLD_LAZY //RTLD_LAZY = 0x1 -- RTLD_NOW = 0x02 + 0xFF, 0xE0 //jmp rax //call dlopen(pthread_parameters, RTLD_LAZY) + + /*pthread_t LoadLibrary(void** data, void* param) + { + void* (*_dlopen_pointer)(void* param) = (decltype(_dlopen_pointer))(data[0]); + int (*_pthread_create_from_mach_thread)(pthread_t *, const pthread_attr_t *, void *(*)(void *), void *) = (decltype(_pthread_create_from_mach_thread))(data[1]); + + pthread_t thread_id = 0; + _pthread_create_from_mach_thread(&thread_id, nullptr, _dlopen_pointer, param); + return thread_id; + } + + void* _dlopen_pointer(void* param) + { + decltype(dlopen)* _dlopen = (decltype(dlopen)*)0xa000deadbeef; + return _dlopen((const char*)param, RTLD_NOW); + }*/ + }; + #endif + *instructions_size = sizeof(assembly); + return &assembly[0]; +}; +#endif + +//Find address of a dynamic library +auto find_library = [](mach_port_t task, const char* library) -> std::intptr_t { + task_dyld_info_data_t info; + mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; + + kern_return_t err = task_info(task, TASK_DYLD_INFO, reinterpret_cast(&info), &count); + if (err != KERN_SUCCESS) + { + //print mach_error_string(err); + return NULL; + } + + //Get the loaded dylibs/images + dyld_all_image_infos infos = {0}; + mach_vm_size_t size = info.all_image_info_size; // or sizeof(infos) + err = mach_vm_read_overwrite(task, info.all_image_info_addr, info.all_image_info_size, reinterpret_cast(&infos), &size); + if (err != KERN_SUCCESS || size != info.all_image_info_size) + { + return NULL; + } + + //Get the info for each dylib/image + size = sizeof(dyld_image_info) * infos.infoArrayCount; + std::unique_ptr image_infos = std::make_unique(infos.infoArrayCount); + err = mach_vm_read_overwrite(task, reinterpret_cast(infos.infoArray), size, reinterpret_cast(image_infos.get()), &size); + if (err != KERN_SUCCESS || size != sizeof(dyld_image_info) * infos.infoArrayCount) + { + return NULL; + } + + //Get each image's file paths + for (std::uint32_t i = 0; i < infos.infoArrayCount; ++i) + { + char buffer[512] = {0}; + mach_vm_size_t size = sizeof(buffer); + + mach_vm_size_t err = mach_vm_read_overwrite(task, reinterpret_cast(image_infos[i].imageFilePath), size, reinterpret_cast(&buffer[0]), &size); + if (err == KERN_SUCCESS && size > 0) + { + std::string path = strip_path(buffer); + if (!strcasecmp(path.c_str(), library)) + { + return reinterpret_cast(image_infos[i].imageLoadAddress); + } + } + } + + return NULL; +}; + +//Find address of a symbol within a dynamic library +auto find_symbol = [](mach_port_t task, mach_vm_address_t library_header_address, const char* symbol) -> std::intptr_t { + mach_vm_size_t size = sizeof(mach_header_64); + struct mach_header_64 header = {0}; + mach_vm_read_overwrite(task, library_header_address, size, reinterpret_cast(&header), &size); + + //64-bit mach-o module.. + if (header.magic == MH_MAGIC_64) + { + std::intptr_t shared_image_cache_slide = 0; + if ((header.flags & 0x80000000) == 0x80000000) + { + shared_image_cache_slide = find_shared_cache_slide(task); + } + + load_command command = {0}; + mach_vm_size_t size = sizeof(command); + + //stackoverflow.com/a/40793165/1462718 + //stackoverflow.com/questions/20481058/find-pathname-from-dlopen-handle-on-osx + mach_vm_address_t seg_linkedit_addr = reinterpret_cast(nullptr); + mach_vm_address_t seg_text_addr = reinterpret_cast(nullptr); + mach_vm_address_t symtab_addr = reinterpret_cast(nullptr); + mach_vm_address_t load_command_address = library_header_address + sizeof(struct mach_header_64); + + //Iterate through all the load commands in the header.. + for (uint32_t i = 0; i < header.ncmds; ++i) + { + mach_vm_read_overwrite(task, load_command_address, size, reinterpret_cast(&command), &size); + switch(command.cmd) + { + case LC_SEGMENT: + case LC_SEGMENT_64: + { + char name[512] = {0}; + mach_vm_size_t size = sizeof(name); + + //Read the name of each segment in the header + std::intptr_t segname_offset = offsetof(segment_command_64, segname); + mach_vm_read_overwrite(task, load_command_address + segname_offset, size, reinterpret_cast(&name[0]), &size); + + if (std::string(name) == SEG_TEXT) + { + seg_text_addr = load_command_address; + } + else if (std::string(name) == SEG_LINKEDIT) + { + seg_linkedit_addr = load_command_address; + } + } + break; + + case LC_SYMTAB: + { + symtab_addr = load_command_address; + } + break; + } + + load_command_address += command.cmdsize; + } + + if (!seg_text_addr || !seg_linkedit_addr || !symtab_addr) + { + return NULL; + } + + //Read each segment + segment_command_64 seg_linkedit = {0}; + segment_command_64 seg_text = {0}; + symtab_command symtab = {0}; + + size = sizeof(seg_linkedit); + mach_vm_read_overwrite(task, seg_linkedit_addr, size, reinterpret_cast(&seg_linkedit), &size); + + size = sizeof(seg_text); + mach_vm_read_overwrite(task, seg_text_addr, size, reinterpret_cast(&seg_text), &size); + + size = sizeof(symtab); + mach_vm_read_overwrite(task, symtab_addr, size, reinterpret_cast(&symtab), &size); + + //ASLR slide calculations + std::intptr_t file_slide = seg_linkedit.vmaddr - seg_text.vmaddr - seg_linkedit.fileoff; + std::intptr_t strings = library_header_address + symtab.stroff + file_slide; + std::intptr_t sym_addr = library_header_address + symtab.symoff + file_slide; + + //Iterate through all the symbols in that segment + for (uint32_t i = 0; i < symtab.nsyms; ++i) + { + nlist_64 sym = {0}; + size = sizeof(sym); + mach_vm_read_overwrite(task, sym_addr, size, reinterpret_cast(&sym), &size); + + if (/*(sym.n_type & N_EXT) == N_EXT &&*/ sym.n_value) + { + char name[512] = {0}; + mach_vm_size_t size = sizeof(name); + + std::intptr_t symname_offset = strings + sym.n_un.n_strx; + mach_vm_read_overwrite(task, symname_offset, size, reinterpret_cast(&name[0]), &size); + + if (std::string(name).substr(1) == symbol) + { + if (sym.n_value < 0x1000) + { + return sym.n_value + library_header_address; + } + + if (shared_image_cache_slide) + { + return sym.n_value + shared_image_cache_slide; + } + + return sym.n_value + library_header_address; //kx86_64DefaultBaseAddress + } + } + + sym_addr += size; + } + } + + return NULL; +}; + +bool IsProcessRunningInRosetta(pid_t pid) +{ + int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid}; + struct kinfo_proc proc; + std::size_t buffer_size = sizeof(proc); + if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), &proc, &buffer_size, nullptr, 0) < 0) + { + //Error assume native + return false; + } + + return ((proc.kp_proc.p_flag & P_TRANSLATED) == P_TRANSLATED); +} + +bool Injector::Inject(std::string module_path, std::int32_t pid, void* bootstrap) noexcept +{ + std::size_t assembly_size = 0; + std::uint64_t stack_size = 16 * 1024; + std::uint8_t* assembly = remote_load_library(&assembly_size); + + //Retrieve a task port for the remote process.. + mach_port_t remote_task = 0; + mach_error_t err = task_for_pid(mach_task_self(), pid, &remote_task); + if (err == KERN_FAILURE) + { + fprintf(stderr, "Could not access task for pid %d. You probably need to add user to procmod group\n", pid); + return false; + } + + // If the library is already loaded + if (find_library(remote_task, strip_path(module_path).c_str())) + { + return true; + } + + std::intptr_t libdyld = find_library(remote_task, "libdyld"); + if (!libdyld) + { + fprintf(stderr, "Cannot find libdyld in remote process\n"); + return false; + } + + std::intptr_t libsystem_pthread = find_library(remote_task, "libsystem_pthread"); + if (!libsystem_pthread) + { + fprintf(stderr, "Cannot find libsystem_pthread in remote process\n"); + return false; + } + + std::intptr_t libsystem_kernel = find_library(remote_task, "libsystem_kernel"); + if (!libsystem_kernel) + { + fprintf(stderr, "Cannot find libsystem_kernel in remote process\n"); + return false; + } + + std::intptr_t dlopen = find_symbol(remote_task, libdyld, "dlopen"); + if (!dlopen) + { + fprintf(stderr, "Cannot find dlopen in remote process\n"); + return false; + } + + std::intptr_t pthread_create_from_mach_thread = find_symbol(remote_task, libsystem_pthread, "pthread_create_from_mach_thread"); + if (!pthread_create_from_mach_thread) + { + fprintf(stderr, "Cannot find pthread_create_from_mach_thread in remote process\n"); + return false; + } + + std::intptr_t pthread_set_self = find_symbol(remote_task, libsystem_pthread, "_pthread_set_self"); + if (!pthread_set_self) + { + fprintf(stderr, "Cannot find pthread_set_self in remote process\n"); + return false; + } + + std::intptr_t thread_suspend = find_symbol(remote_task, libsystem_kernel, "thread_suspend"); + if (!thread_suspend) + { + fprintf(stderr, "Cannot find thread_suspend in remote process\n"); + return false; + } + + std::intptr_t mach_thread_self = find_symbol(remote_task, libsystem_kernel, "mach_thread_self"); + if (!mach_thread_self) + { + fprintf(stderr, "Cannot find mach_thread_self in remote process\n"); + return false; + } + + // For arm64e, we need to sign pointers otherwise we'd get access violations + #if defined(__aarch64__) + dlopen = ptrauth_sign_unauthenticated(dlopen, ptrauth_key_function_pointer, 0); + pthread_create_from_mach_thread = ptrauth_sign_unauthenticated(pthread_create_from_mach_thread, ptrauth_key_function_pointer, 0); + pthread_set_self = ptrauth_sign_unauthenticated(pthread_set_self, ptrauth_key_function_pointer, 0); + thread_suspend = ptrauth_sign_unauthenticated(thread_suspend, ptrauth_key_function_pointer, 0); + mach_thread_self = ptrauth_sign_unauthenticated(mach_thread_self, ptrauth_key_function_pointer, 0); + #endif + + + // Allocate and write the path size.. + mach_vm_address_t remote_path = reinterpret_cast(nullptr); + kern_return_t ret = mach_vm_allocate(remote_task, &remote_path, module_path.size() + 1, VM_FLAGS_ANYWHERE); + if (ret != KERN_SUCCESS) + { + fprintf(stderr, "Failed to Allocate Remote Module Path\n"); + return false; + } + + ret = mach_vm_write(remote_task, remote_path, reinterpret_cast(module_path.c_str()), static_cast(module_path.size())); + if (ret != KERN_SUCCESS) + { + mach_vm_deallocate(remote_task, remote_path, module_path.size() + 1); + fprintf(stderr, "Failed to Write Remote Module Path\n"); + return false; + } + + ret = mach_vm_protect(remote_task, remote_path, module_path.size() + 1, 0, VM_PROT_READ | VM_PROT_WRITE); + if (ret != KERN_SUCCESS) + { + mach_vm_deallocate(remote_task, remote_path, module_path.size() + 1); + fprintf(stderr, "Failed to Protect Remote Module Path\n"); + return false; + } + + #if defined(__aarch64__) + // Bits 21 to 5 = imm of the MOV wide immediate instruction + // https://developer.arm.com/documentation/ddi0602/2023-03/Base-Instructions/MOV--wide-immediate---Move--wide-immediate---an-alias-of-MOVZ- + auto copy_bits = [](std::uint32_t ®, std::uint16_t value) { + for (int bit = 20, valueBit = 15; bit >= 5; --bit, --valueBit) + { + std::uint32_t bit_to_set = ((value >> valueBit) & 1); + reg ^= (-bit_to_set ^ reg) & (static_cast(1) << bit); + } + }; + + // Convert the instruction bytes to an instruction + auto decode_instruction = [](std::uint8_t instructions[]) -> std::uint32_t { + return (static_cast(instructions[3]) << 24) | + (static_cast(instructions[2]) << 16) | + (static_cast(instructions[1]) << 8) | + (static_cast(instructions[0]) << 0); + }; + + // Convert the instruction back into instruction bytes + auto encode_instruction = [](std::uint32_t instruction, std::uint8_t (&instructions)[4]) { + instructions[3] = (instruction & 0xFF000000) >> 24; + instructions[2] = (instruction & 0x00FF0000) >> 16; + instructions[1] = (instruction & 0x0000FF00) >> 8; + instructions[0] = (instruction & 0x000000FF) >> 0; + }; + + // Get the instructions offset, and write the address of dlopen to each part, 16-bits at a time + auto write_instruction_address = [&](std::uint32_t address_intermediate, std::uint8_t assembly[], std::size_t offset) { + std::uint8_t instructions[] = {0x00, 0x00, 0x00, 0x00}; + memcpy(&instructions, &assembly[assembly_size + offset], sizeof(instructions)); + + std::uint32_t instruction = decode_instruction(instructions); + copy_bits(instruction, address_intermediate); + encode_instruction(instruction, instructions); + memcpy(&assembly[assembly_size + offset], &instructions, sizeof(instructions)); + }; + + // Convert the dlopen address to its 16-bit parts + std::uintptr_t dlopen_address = static_cast(dlopen); + std::uint32_t beef = ((dlopen_address & 0x000000000000FFFF) >> 0); + std::uint32_t dead = ((dlopen_address & 0x00000000FFFF0000) >> 16); + std::uint32_t b000 = ((dlopen_address & 0x0000FFFF00000000) >> 32); + std::uint32_t a000 = ((dlopen_address & 0xFFFF000000000000) >> 48); + + // Write the encoded instructions back into the assembly + write_instruction_address(a000, assembly, -8); + write_instruction_address(b000, assembly, -12); + write_instruction_address(dead, assembly, -16); + write_instruction_address(beef, assembly, -20); + + //Allocate and write our remote code + mach_vm_address_t remote_code = reinterpret_cast(nullptr); + ret = mach_vm_allocate(remote_task, &remote_code, assembly_size, VM_FLAGS_ANYWHERE); + if (ret != KERN_SUCCESS) + { + mach_vm_deallocate(remote_task, remote_path, module_path.size() + 1); + fprintf(stderr, "Failed to Allocate Remote Code\n"); + return false; + } + + ret = mach_vm_write(remote_task, remote_code, reinterpret_cast(&assembly[0]), static_cast(assembly_size)); + if (ret != KERN_SUCCESS) + { + mach_vm_deallocate(remote_task, remote_code, assembly_size); + mach_vm_deallocate(remote_task, remote_path, module_path.size() + 1); + fprintf(stderr, "Failed to Write Remote Code\n"); + return false; + } + + ret = mach_vm_protect(remote_task, remote_code, assembly_size, false, VM_PROT_READ | VM_PROT_EXECUTE); + if (ret != KERN_SUCCESS) + { + mach_vm_deallocate(remote_task, remote_code, assembly_size); + mach_vm_deallocate(remote_task, remote_path, module_path.size() + 1); + fprintf(stderr, "Failed to Protect Remote Code\n"); + return false; + } + + //Allocate remote stack.. + mach_vm_address_t remote_stack = reinterpret_cast(nullptr); + ret = mach_vm_allocate(remote_task, &remote_stack, stack_size, VM_FLAGS_ANYWHERE); + if (ret != KERN_SUCCESS) + { + mach_vm_deallocate(remote_task, remote_code, assembly_size); + mach_vm_deallocate(remote_task, remote_path, module_path.size() + 1); + fprintf(stderr, "Failed to Allocate Remote Stack\n"); + return false; + } + + ret = mach_vm_protect(remote_task, remote_stack, stack_size, true, VM_PROT_READ | VM_PROT_WRITE); + if (ret != KERN_SUCCESS) + { + mach_vm_deallocate(remote_task, remote_code, assembly_size); + mach_vm_deallocate(remote_task, remote_stack, stack_size); + mach_vm_deallocate(remote_task, remote_path, module_path.size() + 1); + fprintf(stderr, "Failed to Protect Remote Stack\n"); + return false; + } + + //Offset stack pointer.. + mach_vm_address_t local_stack = remote_stack; + remote_stack += (stack_size / 2); //stack location + + //Allocate & write parameters.. + void* parameters[] = { + (void*)(remote_code + (assembly_size - 24)), + (void*)pthread_set_self, + (void*)pthread_create_from_mach_thread, + (void*)thread_suspend, + (void*)mach_thread_self + }; + + mach_vm_address_t remote_parameters = reinterpret_cast(nullptr); + ret = mach_vm_allocate(remote_task, &remote_parameters, sizeof(parameters), VM_FLAGS_ANYWHERE); + if (ret != KERN_SUCCESS) + { + mach_vm_deallocate(remote_task, remote_stack, stack_size); + mach_vm_deallocate(remote_task, remote_code, assembly_size); + mach_vm_deallocate(remote_task, remote_path, module_path.size() + 1); + fprintf(stderr, "Failed to Allocate Remote Parameters\n"); + return false; + } + + ret = mach_vm_write(remote_task, remote_parameters, reinterpret_cast(¶meters[0]), static_cast(sizeof(parameters))); + if (ret != KERN_SUCCESS) + { + mach_vm_deallocate(remote_task, remote_parameters, sizeof(parameters)); + mach_vm_deallocate(remote_task, remote_stack, stack_size); + mach_vm_deallocate(remote_task, remote_code, assembly_size); + mach_vm_deallocate(remote_task, remote_path, module_path.size() + 1); + fprintf(stderr, "Failed to Write Remote Parameters\n"); + return false; + } + + // To support ARMv7 and ARMv8, we use arm_unified_thread_state_t instead of arm_thread_state64_t + arm_unified_thread_state_t state = {0}; + memset(&state, 0, sizeof(state)); + + //Parameter order for aarch64: x0, x1, x2, x3, x4, x5 + state.ash.flavor = ARM_THREAD_STATE64; + state.ash.count = ARM_THREAD_STATE64_COUNT; + state.ts_64.__x[0] = remote_parameters; //pointers to functions + state.ts_64.__x[1] = remote_path; //path of module + state.ts_64.__pc = ptrauth_sign_unauthenticated(remote_code, ptrauth_key_function_pointer, 0); //code to execute + state.ts_64.__sp = remote_stack; + state.ts_64.__lr = local_stack; + + //Create our remote thread + thread_act_t thread; + err = thread_create_running(remote_task, ARM_THREAD_STATE64, (thread_state_t) &state.ts_64, ARM_THREAD_STATE64_COUNT, &thread); + if (err != KERN_SUCCESS) + { + mach_vm_deallocate(remote_task, remote_parameters, sizeof(parameters)); + mach_vm_deallocate(remote_task, remote_stack, stack_size); + mach_vm_deallocate(remote_task, remote_code, assembly_size); + mach_vm_deallocate(remote_task, remote_path, module_path.size() + 1); + fprintf(stderr, "ERROR Creating Running Thread!\n"); + return 0; + } + + //Give the remote thread some time to run (Maybe a better way than this???) + std::this_thread::sleep_for(std::chrono::seconds(2)); + + //Check to make sure our remote thread is suspended, and finished its job + thread_basic_info_data_t basic_info; + mach_msg_type_number_t info_count = THREAD_BASIC_INFO_COUNT; + err = thread_info(thread, THREAD_BASIC_INFO, (thread_info_t)&basic_info, &info_count); + if (err == KERN_SUCCESS) + { + bool is_suspended = (basic_info.suspend_count > 0); + if (is_suspended) + { + //Retrieve our remote thread state. + arm_thread_state64_t state = {0}; + mach_msg_type_number_t count = ARM_THREAD_STATE64_COUNT; + thread_get_state(thread, ARM_THREAD_STATE64, reinterpret_cast(&state), &count); + + //x10 contains the result of pthread_create_from_mach_thread + //x11 contains the pthread_t thread_id + int result = static_cast(state.__x[10]); + pthread_t thread_id = reinterpret_cast(state.__x[11]); + + if (result == 0 && thread_id != 0) + { + //Give some time for pthread_create_from_mach_thread to run, and call dlopen + std::this_thread::sleep_for(std::chrono::seconds(2)); + + //Thread should be finished its job. + thread_terminate(thread); + return true; + } + } + } + + mach_vm_deallocate(remote_task, remote_parameters, sizeof(parameters)); + mach_vm_deallocate(remote_task, remote_stack, stack_size); + mach_vm_deallocate(remote_task, remote_code, assembly_size); + mach_vm_deallocate(remote_task, remote_path, module_path.size() + 1); + return false; + #else + //Update our dlopen address.. + std::uintptr_t dlopen_address = static_cast(dlopen); + std::memcpy(&assembly[assembly_size - 15], reinterpret_cast(&dlopen_address), sizeof(dlopen_address)); + + //Allocate and write our remote code + mach_vm_address_t remote_code = reinterpret_cast(nullptr); + ret = mach_vm_allocate(remote_task, &remote_code, assembly_size, VM_FLAGS_ANYWHERE); + if (ret != KERN_SUCCESS) + { + mach_vm_deallocate(remote_task, remote_path, module_path.size() + 1); + fprintf(stderr, "Failed to Allocate Remote Code\n"); + return false; + } + + ret = mach_vm_write(remote_task, remote_code, reinterpret_cast(&assembly[0]), static_cast(assembly_size)); + if (ret != KERN_SUCCESS) + { + mach_vm_deallocate(remote_task, remote_code, assembly_size); + mach_vm_deallocate(remote_task, remote_path, module_path.size() + 1); + fprintf(stderr, "Failed to Write Remote Code\n"); + return false; + } + + ret = mach_vm_protect(remote_task, remote_code, assembly_size, false, VM_PROT_READ | VM_PROT_EXECUTE); + if (ret != KERN_SUCCESS) + { + mach_vm_deallocate(remote_task, remote_code, assembly_size); + mach_vm_deallocate(remote_task, remote_path, module_path.size() + 1); + fprintf(stderr, "Failed to Protect Remote Code\n"); + return false; + } + + //Allocate remote stack.. + mach_vm_address_t remote_stack = reinterpret_cast(nullptr); + ret = mach_vm_allocate(remote_task, &remote_stack, stack_size, VM_FLAGS_ANYWHERE); + if (ret != KERN_SUCCESS) + { + mach_vm_deallocate(remote_task, remote_code, assembly_size); + mach_vm_deallocate(remote_task, remote_path, module_path.size() + 1); + fprintf(stderr, "Failed to Allocate Remote Stack\n"); + return false; + } + + ret = mach_vm_protect(remote_task, remote_stack, stack_size, true, VM_PROT_READ | VM_PROT_WRITE); + if (ret != KERN_SUCCESS) + { + mach_vm_deallocate(remote_task, remote_code, assembly_size); + mach_vm_deallocate(remote_task, remote_stack, stack_size); + mach_vm_deallocate(remote_task, remote_path, module_path.size() + 1); + fprintf(stderr, "Failed to Protect Remote Stack\n"); + return false; + } + + //Offset stack pointer.. + mach_vm_address_t local_stack = remote_stack; + remote_stack += (stack_size / 2); //stack location + remote_stack -= 8; // Must be 16-byte-off-by-8 aligned + + //Allocate & write parameters.. + #ifndef OLD_INJECTION_CODE + void* parameters[] = { + (void*)(remote_code + (assembly_size - 18)), + (void*)pthread_set_self, + (void*)pthread_create_from_mach_thread, + (void*)thread_suspend, + (void*)mach_thread_self, + (void*)local_stack, //thread_id, + (void*)(local_stack + 8) //result + }; + #else + void* parameters[] = { + (void*)(remote_code + (assembly_size - 18)), + (void*)pthread_create_from_mach_thread + }; + #endif + + mach_vm_address_t remote_parameters = reinterpret_cast(nullptr); + ret = mach_vm_allocate(remote_task, &remote_parameters, sizeof(parameters), VM_FLAGS_ANYWHERE); + if (ret != KERN_SUCCESS) + { + mach_vm_deallocate(remote_task, remote_code, assembly_size); + mach_vm_deallocate(remote_task, remote_stack, stack_size); + mach_vm_deallocate(remote_task, remote_path, module_path.size() + 1); + fprintf(stderr, "Failed to Allocate Remote Parameters\n"); + return false; + } + + ret = mach_vm_write(remote_task, remote_parameters, reinterpret_cast(¶meters[0]), static_cast(sizeof(parameters))); + if (ret != KERN_SUCCESS) + { + mach_vm_deallocate(remote_task, remote_parameters, sizeof(parameters)); + mach_vm_deallocate(remote_task, remote_code, assembly_size); + mach_vm_deallocate(remote_task, remote_stack, stack_size); + mach_vm_deallocate(remote_task, remote_path, module_path.size() + 1); + fprintf(stderr, "Failed to Write Remote Parameters\n"); + return false; + } + + ret = mach_vm_protect(remote_task, remote_parameters, sizeof(remote_parameters), true, VM_PROT_READ | VM_PROT_WRITE); + if (ret != KERN_SUCCESS) + { + mach_vm_deallocate(remote_task, remote_parameters, sizeof(parameters)); + mach_vm_deallocate(remote_task, remote_code, assembly_size); + mach_vm_deallocate(remote_task, remote_stack, stack_size); + mach_vm_deallocate(remote_task, remote_path, module_path.size() + 1); + fprintf(stderr, "Failed to Protect Remote Parameters\n"); + return false; + } + + // To support x86_64 only, we use x86_thread_state_64_t + x86_thread_state64_t state = {0}; + memset(&state, 0, sizeof(state)); + + //Fake return address on the stack + std::intptr_t return_address_stack[] = { 0x00000DEADBEA7DAD }; + err = mach_vm_write(remote_task, remote_stack, reinterpret_cast(&return_address_stack[0]), sizeof(return_address_stack)); + if (err != KERN_SUCCESS) + { + mach_vm_deallocate(remote_task, remote_parameters, sizeof(parameters)); + mach_vm_deallocate(remote_task, remote_stack, stack_size); + mach_vm_deallocate(remote_task, remote_code, assembly_size); + mach_vm_deallocate(remote_task, remote_path, module_path.size() + 1); + fprintf(stderr, "Failed to Write Return Address\n"); + return false; + } + + //Parameter order for x86_64: RDI, RSI, RDX, RCX, R8, R9 + state.__rdi = remote_parameters; //pointers to functions + state.__rsi = remote_path; //path of module + state.__rdx = local_stack; //userp + state.__rip = remote_code; //code to execute + state.__rax = local_stack; //return address is the top of our stack + state.__rsp = remote_stack; //Set remote Stack Pointer + state.__rbp = remote_stack; //Set remote Stack Pointer + + //Create our remote thread + thread_act_t thread; + err = thread_create_running(remote_task, x86_THREAD_STATE64, (thread_state_t) &state, x86_THREAD_STATE64_COUNT, &thread); + if (err != KERN_SUCCESS) + { + mach_vm_deallocate(remote_task, remote_parameters, sizeof(parameters)); + mach_vm_deallocate(remote_task, remote_stack, stack_size); + mach_vm_deallocate(remote_task, remote_code, assembly_size); + mach_vm_deallocate(remote_task, remote_path, module_path.size() + 1); + fprintf(stderr, "ERROR Creating Running Thread!\n"); + return false; + } + + //Give the remote thread some time to run (Maybe a better way than this???) + //std::this_thread::sleep_for(std::chrono::seconds(2)); + + #ifndef OLD_INJECTION_CODE + for (std::size_t i = 0; i < 5; ++i) + { + thread_basic_info_data_t basic_info; + mach_msg_type_number_t info_count = THREAD_BASIC_INFO_COUNT; + err = thread_info(thread, THREAD_BASIC_INFO, (thread_info_t)&basic_info, &info_count); + + if (err != KERN_SUCCESS) + { + return false; + } + + if (basic_info.suspend_count > 0) + { + break; + } + + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + #endif + + //Check our thread's to see if it's suspended, and finished its job + thread_basic_info_data_t basic_info; + mach_msg_type_number_t info_count = THREAD_BASIC_INFO_COUNT; + err = thread_info(thread, THREAD_BASIC_INFO, (thread_info_t)&basic_info, &info_count); + + #ifndef OLD_INJECTION_CODE + if (err == KERN_SUCCESS) + { + bool is_suspended = (basic_info.suspend_count > 0); + if (is_suspended) + { + //Retrieve our remote thread returned values via parameters + std::intptr_t return_values[] = { + 0, //thread_id + 0 //result + }; + + std::size_t size_read = 0; + err = mach_vm_read(remote_task, local_stack, sizeof(return_values), reinterpret_cast(&return_values[0]), reinterpret_cast(&size_read)); + if (err != KERN_SUCCESS || size_read != sizeof(return_values)) + { + mach_vm_deallocate(remote_task, remote_parameters, sizeof(parameters)); + mach_vm_deallocate(remote_task, remote_stack, stack_size); + mach_vm_deallocate(remote_task, remote_code, assembly_size); + mach_vm_deallocate(remote_task, remote_path, module_path.size() + 1); + mach_port_deallocate(mach_task_self(), remote_task); + return false; + } + + //r12 contains the result of pthread_create_from_mach_thread + //r13 contains the pthread_t thread_id + int result = static_cast(return_values[1]); + pthread_t thread_id = reinterpret_cast(return_values[0]); + + if (result == 0 && thread_id != 0) + { + //Thread should be finished its job. + thread_terminate(thread); + + //Cleanup + mach_vm_deallocate(remote_task, remote_parameters, sizeof(parameters)); + mach_vm_deallocate(remote_task, remote_stack, stack_size); + mach_vm_deallocate(remote_task, remote_code, assembly_size); + mach_vm_deallocate(remote_task, remote_path, module_path.size() + 1); + mach_port_deallocate(mach_task_self(), remote_task); + return true; + } + } + } + + //Thread should be finished its job. + thread_terminate(thread); + + //Cleanup + mach_vm_deallocate(remote_task, remote_parameters, sizeof(parameters)); + mach_vm_deallocate(remote_task, remote_stack, stack_size); + mach_vm_deallocate(remote_task, remote_code, assembly_size); + mach_vm_deallocate(remote_task, remote_path, module_path.size() + 1); + mach_port_deallocate(mach_task_self(), remote_task); + #else + if (err == KERN_SUCCESS) + { + bool is_suspended = (basic_info.suspend_count > 0); + if (!is_suspended) + { + //Retrieve our remote thread state. + x86_thread_state64_t state = {0}; + mach_msg_type_number_t count = x86_THREAD_STATE64_COUNT; + thread_get_state(thread, x86_THREAD_STATE64, reinterpret_cast(&state), &count); + + //r12 contains the pthread_t thread_id + pthread_t thread_id = reinterpret_cast(state.__r12); + + if (thread_id != 0) + { + //Thread should be finished its job. + ::thread_suspend(thread); + thread_terminate(thread); + + //Cleanup + mach_vm_deallocate(remote_task, remote_parameters, sizeof(parameters)); + mach_vm_deallocate(remote_task, remote_stack, stack_size); + mach_vm_deallocate(remote_task, remote_code, sizeof(assembly)); + mach_vm_deallocate(remote_task, remote_path, module_path.size() + 1); + mach_port_deallocate(mach_task_self(), remote_task); + return true; + } + } + } + + //Thread should be finished its job. + thread_terminate(thread); + + //Cleanup + mach_vm_deallocate(remote_task, remote_parameters, sizeof(parameters)); + mach_vm_deallocate(remote_task, remote_stack, stack_size); + mach_vm_deallocate(remote_task, remote_code, assembly_size); + mach_vm_deallocate(remote_task, remote_path, module_path.size() + 1); + mach_port_deallocate(mach_task_self(), remote_task); + #endif + #endif + + return false; +} +#endif + + +#if DEBUG_FUNCTIONS +void list_symbols(const mach_header_64* header) +{ + Dl_info info = {0}; + if (dladdr(header, &info) == 0) + { + return; + } + + segment_command_64* seg_linkedit = nullptr; + segment_command_64* seg_text = nullptr; + symtab_command* symtab = nullptr; + + load_command* cmd = reinterpret_cast(reinterpret_cast(header) + sizeof(mach_header_64)); + + for (std::uint32_t i = 0; i < header->ncmds; ++i) + { + switch(cmd->cmd) + { + case LC_SEGMENT_64: + { + if (!strcmp(reinterpret_cast(cmd)->segname, SEG_TEXT)) + { + seg_text = reinterpret_cast(cmd); + } + else if (!strcmp(reinterpret_cast(cmd)->segname, SEG_LINKEDIT)) + { + seg_linkedit = reinterpret_cast(cmd); + } + } + break; + + case LC_SYMTAB: + { + symtab = reinterpret_cast(cmd); + } + break; + } + + cmd = reinterpret_cast(reinterpret_cast(cmd) + cmd->cmdsize); + } + + if (!seg_text || !seg_linkedit || !symtab) + { + return; + } + + //ASLR slide calculations + std::intptr_t file_slide = seg_linkedit->vmaddr - seg_text->vmaddr - seg_linkedit->fileoff; + std::intptr_t strings = reinterpret_cast(header) + symtab->stroff + file_slide; + nlist_64* sym_list = reinterpret_cast(reinterpret_cast(header) + symtab->symoff + file_slide); + + for (std::uint32_t i = 0; i < symtab->nsyms; ++i, ++sym_list) + { + if ((sym_list->n_type & N_EXT) != N_EXT || !sym_list->n_value) + { + continue; + } + + printf("%s\n", reinterpret_cast(strings) + sym_list->n_un.n_strx); + } +} + +void list_all_symbols() +{ + for (std::int32_t i = _dyld_image_count(); i >= 0 ; --i) + { + list_symbols((const mach_header_64 *)_dyld_get_image_header(i)); + } +} + +void* get_symbol(const mach_header_64* header, const char* symbol_name) +{ + segment_command_64* seg_linkedit = nullptr; + segment_command_64* seg_text = nullptr; + symtab_command* symtab = nullptr; + + load_command* cmd = reinterpret_cast(reinterpret_cast(header) + sizeof(mach_header_64)); + + std::intptr_t shared_image_cache_slide = 0; + if ((header->flags & 0x80000000) == 0x80000000) + { + shared_image_cache_slide = find_shared_cache_slide(mach_task_self()); + } + + for (std::uint32_t i = 0; i < header->ncmds; ++i) + { + switch(cmd->cmd) + { + case LC_SEGMENT: + case LC_SEGMENT_64: + { + if (!strcmp(reinterpret_cast(cmd)->segname, SEG_TEXT)) + { + seg_text = reinterpret_cast(cmd); + } + else if (!strcmp(reinterpret_cast(cmd)->segname, SEG_LINKEDIT)) + { + seg_linkedit = reinterpret_cast(cmd); + } + } + break; + + case LC_SYMTAB: + { + symtab = reinterpret_cast(cmd); + } + break; + } + + cmd = reinterpret_cast(reinterpret_cast(cmd) + cmd->cmdsize); + } + + if (!seg_text || !seg_linkedit || !symtab) + { + return nullptr; + } + + //ASLR slide calculations + std::intptr_t file_slide = seg_linkedit->vmaddr - seg_text->vmaddr - seg_linkedit->fileoff; + std::intptr_t strings = reinterpret_cast(header) + symtab->stroff + file_slide; + nlist_64* sym_list = reinterpret_cast(reinterpret_cast(header) + symtab->symoff + file_slide); + + for (std::uint32_t i = 0; i < symtab->nsyms; ++i, ++sym_list) + { + if (!sym_list->n_value) + { + continue; + } + + const char* found_symbol = (reinterpret_cast(strings) + sym_list->n_un.n_strx); + if (!strcmp(found_symbol + 1, symbol_name)) + { + if (shared_image_cache_slide) + { + return reinterpret_cast(sym_list->n_value + shared_image_cache_slide); + } + return reinterpret_cast(reinterpret_cast(header) + sym_list->n_value); + } + } + return nullptr; +} + +void* get_symbol(void* module_handle, const char* symbol_name) +{ + mach_header_64* header = nullptr; + + for (std::int32_t i = _dyld_image_count(); i >= 0 ; --i) + { + const char* image_name = _dyld_get_image_name(i); + if (image_name) + { + void* library = dlopen(image_name, RTLD_NOLOAD); + dlclose(library); + + // If the handle is the same as what was passed in (modulo mode bits), return this image name + if ((reinterpret_cast(module_handle) & -4) == (reinterpret_cast(library) & -4)) + { + header = const_cast(reinterpret_cast(_dyld_get_image_header(i))); + break; + } + } + } + + return get_symbol(header, symbol_name); +} + +void* get_module_handle(const char* module_name) +{ + for (std::int32_t i = _dyld_image_count(); i >= 0 ; --i) + { + const char* image_name = _dyld_get_image_name(i); + if (image_name && strip_path(image_name) == module_name) + { + return dlopen(image_name, RTLD_NOLOAD /*RTLD_DEFAULT*/); + } + } + return nullptr; +} + +const char* pathname_for_handle(void* handle) +{ + for (std::int32_t i = _dyld_image_count(); i >= 0 ; --i) + { + const char* image_name = _dyld_get_image_name(i); + void* library = dlopen(image_name, RTLD_NOLOAD); + dlclose(library); + + // If the handle is the same as what was passed in (modulo mode bits), return this image name + if ((reinterpret_cast(handle) & -4) == (reinterpret_cast(library) & -4)) + { + return image_name; + } + } + return nullptr; +} + +void* deauth_ptr(void* ptr) +{ + return ptrauth_sign_unauthenticated(ptr, ptrauth_key_function_pointer, 0); +} + +kern_return_t thread_port_for_task(mach_port_t task, mach_port_t *thread) +{ + thread_array_t thread_list = NULL; + mach_msg_type_number_t thread_list_count = 0; + kern_return_t ret = task_threads(task, &thread_list, &thread_list_count); + if (ret) return ret; + + //main-thread = thread_list[0] + *thread = thread_list[0]; + vm_deallocate(mach_task_self(), (vm_address_t) thread_list, thread_list_count * sizeof(thread_list[0])); + return KERN_SUCCESS; +} + +int find_mach_o(unsigned long addr, unsigned long *base) { + *base = 0; + + while(true) { + chmod((char *)addr, 0777); + if(errno == 2 && /*ENOENT*/ + ((int *)addr)[0] == 0xfeedfacf /*MH_MAGIC_64*/) { + *base = addr; + return 0; + } + + addr += 0x1000; + } + return 1; +} +#endif diff --git a/third-party/RemoteInput/RemoteInput/Injection/Injector_Linux.cpp b/third-party/RemoteInput/RemoteInput/Injection/Injector_Linux.cpp new file mode 100644 index 0000000..8b52cef --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Injection/Injector_Linux.cpp @@ -0,0 +1,402 @@ +// +// Created by Brandon on 2020-05-02. +// + +#include "Injector.hxx" + +#if defined(__linux__) && (defined(__x86_64__) || defined(__i386__)) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#endif + +//SYSCall calling conventions: man7.org/linux/man-pages/man2/syscall.2.html + +#if defined(__linux__) && (defined(__x86_64__) || defined(__i386__)) +auto find_library = [](std::int32_t pid, const char* library_name) -> std::uintptr_t { + char file_map_name[256] = {0}; + snprintf(file_map_name, sizeof(file_map_name) - 1, "/proc/%d/maps", pid); + + std::fstream file = std::fstream(file_map_name, std::ios::in | std::ios::binary); + if (file) + { + std::string line; + while(std::getline(file, line)) + { + if (line.find(library_name) != std::string::npos) + { + void* start_address = nullptr; + void* end_address = nullptr; + sscanf(line.c_str(), "%p-%p", &start_address, &end_address); + return reinterpret_cast(start_address); + } + } + } + return reinterpret_cast(nullptr); +}; + +auto find_symbol = [](std::int32_t pid, const char* library_name, void* local_symbol) -> std::uintptr_t { + std::intptr_t remote_library_address = find_library(pid, library_name); + std::intptr_t local_library_address = [local_symbol]() -> std::intptr_t { + Dl_info info = {0}; + dladdr(local_symbol, &info); + return reinterpret_cast(info.dli_fbase); + }(); + + if (remote_library_address && local_library_address) + { + return reinterpret_cast(local_symbol) + (remote_library_address - local_library_address); + } + return reinterpret_cast(nullptr); +}; + +auto read_vm_memory = [](std::int32_t pid, void* dest, void* src, std::size_t size) { + iovec local_vec = {0}; + local_vec.iov_base = dest; + local_vec.iov_len = size; + + iovec remote_vec = {0}; + remote_vec.iov_base = src; + remote_vec.iov_len = size; + + process_vm_readv(pid, &local_vec, 1, &remote_vec, 1, 0); +}; + +auto write_vm_memory = [](std::int32_t pid, void* dest, void* src, std::size_t size) { + iovec local_vec = {0}; + local_vec.iov_base = src; + local_vec.iov_len = size; + + iovec remote_vec = {0}; + remote_vec.iov_base = dest; + remote_vec.iov_len = size; + + process_vm_writev(pid, &local_vec, 1, &remote_vec, 1, 0); +}; + +auto get_signal = [](int status, int expected, const char* message) -> int { + if (WIFSTOPPED(status)) + { + if (WSTOPSIG(status) == expected) + { + return 0; + } + + if (WSTOPSIG(status) == 11) + { + printf("PROCESS DIED: %s\n", message); + return -1; + } + + printf("STOPPED FOR SOME OTHER SIGNAL: %s\n", message); + return -1; + } + return 0; +}; +#endif + +#if defined(__linux__) && defined(__x86_64__) +auto remote_alloc = [](std::int32_t pid, std::size_t size) -> void* { + struct user_regs_struct registers = {0}; + struct user_regs_struct registers_backup = {0}; + ptrace(PTRACE_GETREGS, pid, nullptr, ®isters); + memcpy(®isters_backup, ®isters, sizeof(registers)); + + long rip = ptrace(PTRACE_PEEKDATA, pid, registers.rip, nullptr); + + registers.rdi = reinterpret_cast(nullptr); //address + registers.rsi = size; //length + registers.rdx = PROT_READ | PROT_WRITE | PROT_EXEC; //prot + registers.r10 = MAP_PRIVATE | MAP_ANONYMOUS; //flags + registers.r8 = -1; //fd + registers.r9 = 0; //offset + registers.rax = SYS_mmap; //__NR_mmap or filippo.io/linux-syscall-table/ + //registers.rip = sys_call_address; + + ptrace(PTRACE_SETREGS, pid, nullptr, ®isters); + ptrace(PTRACE_POKEDATA, pid, registers.rip, 0x050F); //syscall instruction + ptrace(PTRACE_SINGLESTEP, pid, nullptr, nullptr); + + int status = 0; + if (waitpid(pid, &status, 0) < 1 || get_signal(status, 5, "Alloc Mem") < 0) + { + ptrace(PTRACE_POKEDATA, pid, registers_backup.rip, rip); + ptrace(PTRACE_SETREGS, pid, nullptr, ®isters_backup); + ptrace(PTRACE_CONT, pid, nullptr, nullptr); + return nullptr; + } + + ptrace(PTRACE_GETREGS, pid, nullptr, ®isters); + ptrace(PTRACE_POKEDATA, pid, registers_backup.rip, rip); + ptrace(PTRACE_SETREGS, pid, nullptr, ®isters_backup); + return reinterpret_cast(registers.rax); +}; + +auto remote_free = [](std::int32_t pid, void* memory, std::size_t size) -> int { + struct user_regs_struct registers = {0}; + struct user_regs_struct registers_backup = {0}; + ptrace(PTRACE_GETREGS, pid, nullptr, ®isters); + memcpy(®isters_backup, ®isters, sizeof(registers)); + + long rip = ptrace(PTRACE_PEEKDATA, pid, registers.rip, nullptr); + + registers.rdi = reinterpret_cast(memory); //address + registers.rsi = size; //length + registers.rdx = 0; + registers.r10 = 0; + registers.r8 = 0; + registers.r9 = 0; + registers.rax = SYS_munmap; //__NR_munmap or filippo.io/linux-syscall-table/ + //registers.rip = sys_call_address; + + ptrace(PTRACE_SETREGS, pid, nullptr, ®isters); + ptrace(PTRACE_POKEDATA, pid, registers.rip, 0x050F); //syscall instruction + ptrace(PTRACE_SINGLESTEP, pid, nullptr, nullptr); + + int status = 0; + if (waitpid(pid, &status, 0) < 1 || get_signal(status, 5, "Free Mem") < 0) + { + ptrace(PTRACE_POKEDATA, pid, registers_backup.rip, rip); + ptrace(PTRACE_SETREGS, pid, nullptr, ®isters_backup); + ptrace(PTRACE_CONT, pid, nullptr, nullptr); + return -1; + } + + ptrace(PTRACE_GETREGS, pid, nullptr, ®isters); + ptrace(PTRACE_POKEDATA, pid, registers_backup.rip, rip); + ptrace(PTRACE_SETREGS, pid, nullptr, ®isters_backup); + return static_cast(registers.rax); +}; + +auto remote_dlopen = [](std::int32_t pid, std::uintptr_t dlopen_address, std::uintptr_t remote_path, int flags) -> void* { + struct user_regs_struct registers = {0}; + struct user_regs_struct registers_backup = {0}; + ptrace(PTRACE_GETREGS, pid, nullptr, ®isters); + memcpy(®isters_backup, ®isters, sizeof(registers)); + + long rip = ptrace(PTRACE_PEEKDATA, pid, registers.rip, nullptr); + + registers.rdi = remote_path; + registers.rsi = flags; + registers.rax = dlopen_address; + + ptrace(PTRACE_SETREGS, pid, nullptr, ®isters); + ptrace(PTRACE_POKEDATA, pid, registers.rip, 0xCCD0FF); //call rax, int3 -- 0xFF, 0xD0, 0xCC + ptrace(PTRACE_CONT, pid, nullptr, nullptr); + + int status = 0; + if (waitpid(pid, &status, 0) < 1) + { + ptrace(PTRACE_POKEDATA, pid, registers_backup.rip, rip); + ptrace(PTRACE_SETREGS, pid, nullptr, ®isters_backup); + //ptrace(PTRACE_CONT, pid, nullptr, nullptr); + return nullptr; + } + + ptrace(PTRACE_GETREGS, pid, nullptr, ®isters); + ptrace(PTRACE_POKEDATA, pid, registers_backup.rip, rip); + ptrace(PTRACE_SETREGS, pid, nullptr, ®isters_backup); + //ptrace(PTRACE_CONT, pid, nullptr, nullptr); + + return reinterpret_cast(registers.rax); +}; +#endif + +#if defined(__linux__) && defined(__i386__) +auto remote_alloc = [](std::int32_t pid, std::size_t size) -> void* { + struct user_regs_struct registers = {0}; + struct user_regs_struct registers_backup = {0}; + ptrace(PTRACE_GETREGS, pid, nullptr, ®isters); + memcpy(®isters_backup, ®isters, sizeof(registers)); + + long eip = ptrace(PTRACE_PEEKDATA, pid, registers.eip, nullptr); + + registers.ebx = reinterpret_cast(nullptr); //address + registers.ecx = size; //length + registers.edx = PROT_READ | PROT_WRITE | PROT_EXEC; //prot + registers.esi = MAP_PRIVATE | MAP_ANONYMOUS; //flags + registers.edi = -1; //fd + registers.ebp = 0; //offset + registers.eax = SYS_mmap; //__NR_mmap or filippo.io/linux-syscall-table/ + //registers.eip = sys_call_address; + + ptrace(PTRACE_SETREGS, pid, nullptr, ®isters); + ptrace(PTRACE_POKEDATA, pid, registers.eip, 0x80CD); //syscall instruction -- int $0x80 -- interrupt 80 + ptrace(PTRACE_SINGLESTEP, pid, nullptr, nullptr); + + int status = 0; + if (waitpid(pid, &status, 0) < 1 || get_signal(status, 5, "Alloc Mem") < 0) + { + ptrace(PTRACE_POKEDATA, pid, registers_backup.eip, eip); + ptrace(PTRACE_SETREGS, pid, nullptr, ®isters_backup); + ptrace(PTRACE_CONT, pid, nullptr, nullptr); + return nullptr; + } + + ptrace(PTRACE_GETREGS, pid, nullptr, ®isters); + ptrace(PTRACE_POKEDATA, pid, registers_backup.eip, eip); + ptrace(PTRACE_SETREGS, pid, nullptr, ®isters_backup); + return reinterpret_cast(registers.eax); +}; + +auto remote_free = [](std::int32_t pid, void* memory, std::size_t size) -> int { + struct user_regs_struct registers = {0}; + struct user_regs_struct registers_backup = {0}; + ptrace(PTRACE_GETREGS, pid, nullptr, ®isters); + memcpy(®isters_backup, ®isters, sizeof(registers)); + + long eip = ptrace(PTRACE_PEEKDATA, pid, registers.eip, nullptr); + + registers.ebx = reinterpret_cast(memory); //address + registers.ebc = size; //length + registers.edx = 0; + registers.esi = 0; + registers.edi = 0; + registers.ebp = 0; + registers.eax = SYS_munmap; //__NR_munmap or filippo.io/linux-syscall-table/ + //registers.eip = sys_call_address; + + ptrace(PTRACE_SETREGS, pid, nullptr, ®isters); + ptrace(PTRACE_POKEDATA, pid, registers.eip, 0x80CD); //syscall instruction -- int $0x80 -- interrupt 80 + ptrace(PTRACE_SINGLESTEP, pid, nullptr, nullptr); + + int status = 0; + if (waitpid(pid, &status, 0) < 1 || get_signal(status, 5, "Free Mem") < 0) + { + ptrace(PTRACE_POKEDATA, pid, registers_backup.eip, eip); + ptrace(PTRACE_SETREGS, pid, nullptr, ®isters_backup); + ptrace(PTRACE_CONT, pid, nullptr, nullptr); + return -1; + } + + ptrace(PTRACE_GETREGS, pid, nullptr, ®isters); + ptrace(PTRACE_POKEDATA, pid, registers_backup.eip, eip); + ptrace(PTRACE_SETREGS, pid, nullptr, ®isters_backup); + return static_cast(registers.eip); +}; + +auto remote_dlopen = [](std::int32_t pid, std::uintptr_t dlopen_address, std::uintptr_t remote_path, int flags) void* { + struct user_regs_struct registers = {0}; + struct user_regs_struct registers_backup = {0}; + ptrace(PTRACE_GETREGS, pid, nullptr, ®isters); + memcpy(®isters_backup, ®isters, sizeof(registers)); + + + std::uint64_t stack_size = 16 * 512; + void* remote_stack = remote_alloc(pid, stack_size); + remote_stack += (stack_size / 2); + remote_stack -= 8; + + static std::uint8_t assembly[] = { + 0x6A, 0x00, 0x00, 0x00, 0x00, //push 0x00000000 //flags + 0x68, 0xA7, 0xBE, 0xAD, 0xDE, //push 0xDEADBEE7 //path + 0xFF, 0xD0, //call eax //call dlopen + 0x83, 0xC4, 0x08 //add esp, 0x08 //cleanup cdecl stack + }; + + memcpy(&assembly[1], &flags, sizeof(flags)); + memcpy(&assembly[2], &remote_path, sizeof(remote_path)); + write_vm_memory(pid, remote_stack, &assembly[0], sizeof(assembly)); + + registers.eax = dlopen_address; + registers.eip = remote_stack; + + ptrace(PTRACE_SETREGS, pid, nullptr, ®isters); + ptrace(PTRACE_POKEDATA, pid, registers.eip, 0xCCD0FF); //call eax, int3 + ptrace(PTRACE_CONT, pid, nullptr, nullptr); + + int status = 0; + if (waitpid(pid, &status, 0) < 1) + { + ptrace(PTRACE_POKEDATA, pid, registers_backup.eax, eax); + ptrace(PTRACE_SETREGS, pid, nullptr, ®isters_backup); + //ptrace(PTRACE_CONT, pid, nullptr, nullptr); + return nullptr; + } + + ptrace(PTRACE_GETREGS, pid, nullptr, ®isters); + ptrace(PTRACE_POKEDATA, pid, registers_backup.eax, eax); + ptrace(PTRACE_SETREGS, pid, nullptr, ®isters_backup); + //ptrace(PTRACE_CONT, pid, nullptr, nullptr); + + remote_stack += 8; + remote_stack -= (stack_size / 2); + remote_free(pid, remote_stack, stack_size); + + return reinterpret_cast(registers.eax); +}; +#endif + +#if defined(__linux__) && (defined(__x86_64__) || defined(__i386__)) +bool Injector::Inject(std::string module_path, std::int32_t pid, void* bootstrap) noexcept +{ + //Determine if the module is already injected + if (find_library(pid, basename(module_path.c_str()))) + { + return false; + } + + //Get ASLR address of dlopen + std::uintptr_t dlopen_address = find_symbol(pid, "libc.so", reinterpret_cast(dlopen)); + if (!dlopen_address) + { + dlopen_address = find_symbol(pid, "libdl.so", reinterpret_cast(dlopen)); + } + + if (!dlopen_address) + { + return false; + } + + if (ptrace(PTRACE_ATTACH, pid, nullptr, nullptr) < 0) + { + return false; + } + + int status = 0; + if (waitpid(pid, &status, 0) < 1 || get_signal(status, 19, "Injector Attach") < 0) + { + return false; + } + + //Allocate path + void* remote_path = remote_alloc(pid, module_path.length() + 1); + if (remote_path) + { + //Write the remote path.. + write_vm_memory(pid, remote_path, &module_path[0], module_path.length() + 1); + + //Load the module + void* result = remote_dlopen(pid, dlopen_address, reinterpret_cast(remote_path), RTLD_LAZY); + + //Free path + remote_free(pid, remote_path, module_path.length() + 1); + ptrace(PTRACE_CONT, pid, nullptr, nullptr); + + if (!result || result == reinterpret_cast(dlopen_address)) + { + ptrace(PTRACE_DETACH, pid, nullptr, nullptr); + return false; + } + + ptrace(PTRACE_DETACH, pid, nullptr, nullptr); + return true; + } + + ptrace(PTRACE_DETACH, pid, nullptr, nullptr); + return false; +} +#endif diff --git a/third-party/RemoteInput/RemoteInput/Injection/Injector_Windows.cxx b/third-party/RemoteInput/RemoteInput/Injection/Injector_Windows.cxx new file mode 100644 index 0000000..11af6ef --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Injection/Injector_Windows.cxx @@ -0,0 +1,66 @@ +// +// Created by Brandon on 2020-05-02. +// + +#include "Injector.hxx" + +#if defined(_WIN32) || defined(_WIN64) +#include +#endif + +#if defined(_WIN32) || defined(_WIN64) +auto IsProcessAlive = [](std::int32_t pid) -> bool { + HANDLE process = OpenProcess(SYNCHRONIZE, FALSE, pid); + if (process) + { + DWORD ret = WaitForSingleObject(process, 0); + CloseHandle(process); + return ret == WAIT_TIMEOUT; + } + return false; +}; + +bool Injector::Inject(std::string module_path, std::int32_t pid, void* bootstrap) noexcept +{ + if (IsProcessAlive(pid)) + { + HMODULE hKernel32 = nullptr; + void* RemoteAddress = nullptr; + HANDLE ProcessHandle, hThread = nullptr; + LPTHREAD_START_ROUTINE LoadLibraryHandle = nullptr; + + if ((ProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, false, pid))) + { + if ((hKernel32 = GetModuleHandle("Kernel32.dll"))) + { + LoadLibraryHandle = reinterpret_cast(GetProcAddress(hKernel32, "LoadLibraryA")); + if (LoadLibraryHandle) + { + RemoteAddress = VirtualAllocEx(ProcessHandle, nullptr, module_path.size() * sizeof(char), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + if (RemoteAddress) + { + if (WriteProcessMemory(ProcessHandle, RemoteAddress, &module_path[0], module_path.size() * sizeof(char), nullptr)) + { + hThread = CreateRemoteThread(ProcessHandle, nullptr, 0, LoadLibraryHandle, RemoteAddress, 0, nullptr); + if (hThread) + { + //More than enough time to wait for a proccess to create a thread.. + WaitForSingleObject(hThread, 5000); + CloseHandle(hThread); + + VirtualFreeEx(ProcessHandle, RemoteAddress, module_path.size() * sizeof(char), MEM_RELEASE); + CloseHandle(ProcessHandle); + return true; + } + } + + VirtualFreeEx(ProcessHandle, RemoteAddress, module_path.size() * sizeof(char), MEM_RELEASE); + } + } + } + CloseHandle(ProcessHandle); + } + } + return false; +} +#endif \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/JVM.cxx b/third-party/RemoteInput/RemoteInput/JVM.cxx new file mode 100644 index 0000000..5e9e9cb --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/JVM.cxx @@ -0,0 +1,239 @@ +#include "JVM.hxx" + +JVM::JVM() noexcept : vm(nullptr), createdVM(false), loadedJNI(false), module(nullptr) +{ + #if defined(_WIN32) || defined(_WIN64) + this->module = GetModuleHandleW(L"jvm.dll"); + if (!this->module) + { + loadedJNI = true; + this->module = LoadLibraryW(L"jvm.dll"); + } + #elif defined(__APPLE__) + extern void* GetModuleHandle(const char*); + this->loadedJNI = true; + this->module = GetModuleHandle("libjvm.dylib"); + + ///Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/lib/server/libjvm.dylib + ///Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/server/libjvm.dylib + #else + extern void* GetModuleHandle(const char*); + this->loadedJNI = true; + this->module = GetModuleHandle("libjvm.so"); + #endif // defined + + #if defined(_WIN32) || defined(_WIN64) + if (!this->module) + { + return; + } + #else + if (!module) + { + this->loadedJNI = false; + module = RTLD_DEFAULT; + } + + if (!dlsym(module, "JNI_GetCreatedJavaVMs")) + { + return; + } + #endif + + jint num_vms = 0; + const jint max_vms = 1; + JavaVM* vms[max_vms] = {0}; + if (this->JNI_GetCreatedJavaVMs(vms, max_vms, &num_vms) == JNI_OK) + { + for (int i = 0; i < num_vms; ++i) + { + if (vms[i] != nullptr) + { + this->vm = vms[i]; + break; + } + } + } +} + +JVM::JVM(JNIEnv* env) noexcept : vm(nullptr), createdVM(false), module(nullptr) +{ + env->GetJavaVM(&this->vm); +} + +JVM::JVM(int argc, const char* argv[]) noexcept : vm(nullptr), createdVM(false), module(nullptr) +{ + if (argc) + { + Init(argc, argv); + } + else + { + const char* args[] = {"-Djava.compiler=NONE", "-Djava.class.path=."}; + Init(2, args); + } +} + +JVM::~JVM() noexcept +{ + if (this->vm) + { + if (this->createdVM) + { + jint (JNICALL *pDestroyJavaVM)() = nullptr; + #if defined(_WIN32) || defined(_WIN64) + pDestroyJavaVM = reinterpret_cast(GetProcAddress(this->module, "DestroyJavaVM")); + #else + pDestroyJavaVM = reinterpret_cast(dlsym(this->module, "DestroyJavaVM")); + #endif // defined + + pDestroyJavaVM(); + } + + this->vm = nullptr; + + if (loadedJNI) + { + #if defined(_WIN32) || defined(_WIN64) + CloseHandle(this->module); + #else + dlclose(this->module); + #endif // defined + } + this->module = nullptr; + } +} + +bool JVM::Init(int argc, const char* argv[]) noexcept +{ + #if defined(_WIN32) || defined(_WIN64) + this->module = LoadLibraryW(L"jvm.dll"); //C:/Program Files/Java/jre1.8.0_73/bin/server/jvm.dll + #elif defined(__APPLE__) + this->module = dlopen("libjvm.dylib", RTLD_LAZY); ///Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/server/libjvm.dylib + #else + this->module = dlopen("libjvm.so", RTLD_LAZY); + #endif // defined + + if (!this->module) + { + return false; + } + + bool result = false; + this->createdVM = false; + + if (argc > 0) + { + JavaVMInitArgs jvm_args; + std::vector options(argc); + + for (int i = 0; i < argc; ++i) + { + options[i].optionString = const_cast(argv[i]); + } + + JNI_GetDefaultJavaVMInitArgs(&jvm_args); + + jvm_args.version = JNI_VERSION_1_6; + jvm_args.nOptions = argc; + jvm_args.options = &options[0]; + jvm_args.ignoreUnrecognized = false; + + result = true; + + jint num_vms = 0; + const jint max_vms = 5; + JavaVM* vms[max_vms] = {0}; + if (this->JNI_GetCreatedJavaVMs(vms, max_vms, &num_vms) == JNI_OK) + { + for (int i = 0; i < num_vms; ++i) + { + if (vms[i] != NULL) + { + this->vm = vms[i]; + return result; + } + } + } + + this->createdVM = true; + JNIEnv* env = nullptr; + if (this->JNI_CreateJavaVM(&this->vm, reinterpret_cast(&env), &jvm_args) != JNI_OK) + { + result = false; + this->vm = nullptr; + this->createdVM = false; + } + } + return result; +} + +JVM::operator bool() const noexcept +{ + return this->vm != nullptr; +} + +JavaVM* JVM::getVM() const noexcept +{ + return vm; +} + +jint JVM::AttachCurrentThread(JNIEnv** env) noexcept +{ + if (!this->vm) + { + return JNI_ERR; + } + return this->vm->AttachCurrentThread(reinterpret_cast(env), nullptr); +} + +jint JVM::AttachCurrentThreadAsDaemon(JNIEnv** env) noexcept +{ + if (!this->vm) + { + return JNI_ERR; + } + return this->vm->AttachCurrentThreadAsDaemon(reinterpret_cast(env), nullptr); +} + +jint JVM::DetachCurrentThread() noexcept +{ + if (!this->vm) + { + return JNI_ERR; + } + return this->vm->DetachCurrentThread(); +} + +jint JVM::JNI_GetDefaultJavaVMInitArgs(void *args) const noexcept +{ + jint (JNICALL *pJNI_GetDefaultJavaVMInitArgs)(void *args) = nullptr; + #if defined(_WIN32) || defined(_WIN64) + pJNI_GetDefaultJavaVMInitArgs = reinterpret_cast(GetProcAddress(this->module, "JNI_GetDefaultJavaVMInitArgs")); + #else + pJNI_GetDefaultJavaVMInitArgs = reinterpret_cast(dlsym(this->module, "JNI_GetDefaultJavaVMInitArgs")); + #endif // defined + return pJNI_GetDefaultJavaVMInitArgs(args); +} + +jint JVM::JNI_CreateJavaVM(JavaVM **pvm, void **penv, void *args) const noexcept +{ + jint (JNICALL *pJNI_CreateJavaVM)(JavaVM **pvm, void **penv, void *args) = nullptr; + #if defined(_WIN32) || defined(_WIN64) + pJNI_CreateJavaVM = reinterpret_cast(GetProcAddress(this->module, "JNI_CreateJavaVM")); + #else + pJNI_CreateJavaVM = reinterpret_cast(dlsym(this->module, "JNI_CreateJavaVM")); + #endif // defined + return pJNI_CreateJavaVM(pvm, penv, args); +} + +jint JVM::JNI_GetCreatedJavaVMs(JavaVM **vm, jsize size, jsize *size2) const noexcept +{ + jint (JNICALL *pJNI_GetCreatedJavaVMs)(JavaVM **, jsize, jsize *) = nullptr; + #if defined(_WIN32) || defined(_WIN64) + pJNI_GetCreatedJavaVMs = reinterpret_cast(GetProcAddress(this->module, "JNI_GetCreatedJavaVMs")); + #else + pJNI_GetCreatedJavaVMs = reinterpret_cast(dlsym(this->module, "JNI_GetCreatedJavaVMs")); + #endif // defined + return pJNI_GetCreatedJavaVMs(vm, size, size2); +} \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/JVM.hxx b/third-party/RemoteInput/RemoteInput/JVM.hxx new file mode 100644 index 0000000..291891a --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/JVM.hxx @@ -0,0 +1,49 @@ +#ifndef JVM_HXX_INCLUDED +#define JVM_HXX_INCLUDED + +#if defined(_WIN32) || defined(_WIN64) +#include +#else +#include +#endif // defined + +#include +#include +#include + +#include "JNI_Common.hxx" + +class JVM +{ +private: + JavaVM* vm; + bool createdVM; + bool loadedJNI; + #if defined(_WIN32) || defined(_WIN64) + HMODULE module; + #else + void* module; + #endif // defined + + bool Init(int argc, const char* argv[]) noexcept; + jint JNI_GetDefaultJavaVMInitArgs(void *args) const noexcept; + jint JNI_CreateJavaVM(JavaVM **pvm, void **penv, void *args) const noexcept; + jint JNI_GetCreatedJavaVMs(JavaVM **, jsize, jsize *) const noexcept; + + +public: + JVM() noexcept; + JVM(JNIEnv* env) noexcept; + JVM(int argc, const char* argv[]) noexcept; + ~JVM() noexcept; + + operator bool() const noexcept; + JavaVM* getVM() const noexcept; + + + jint AttachCurrentThread(JNIEnv** env) noexcept; + jint AttachCurrentThreadAsDaemon(JNIEnv** env) noexcept; + jint DetachCurrentThread() noexcept; +}; + +#endif // JVM_HXX_INCLUDED diff --git a/third-party/RemoteInput/RemoteInput/Java/AWTAccessor.cxx b/third-party/RemoteInput/RemoteInput/Java/AWTAccessor.cxx new file mode 100644 index 0000000..2e6514c --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Java/AWTAccessor.cxx @@ -0,0 +1,63 @@ +// +// AWTAccessor.cxx +// RemoteInput +// +// Created by Brandon on 2020-01-09. +// Copyright © 2020 XIO. All rights reserved. +// + +#include "AWTAccessor.hxx" +#include + +namespace java +{ + AWTAccessor::AWTAccessor(JNIEnv* env, jclass cls, jobject accessor) noexcept : env(env), cls(cls), accessor(accessor) + { + if (!this->cls) + { + this->cls = env->GetObjectClass(accessor); + } + + env->DeleteLocalRef(std::exchange(this->cls, static_cast(env->NewGlobalRef(this->cls)))); + env->DeleteLocalRef(std::exchange(this->accessor, env->NewGlobalRef(this->accessor))); + } + + AWTAccessor::~AWTAccessor() noexcept + { + if (cls) + { + env->DeleteGlobalRef(cls); + } + + if (accessor) + { + env->DeleteGlobalRef(accessor); + } + } + + AWTAccessor::AWTAccessor(AWTAccessor&& other) noexcept : env(other.env), cls(other.cls), accessor(other.accessor) + { + other.env = nullptr; + other.cls = nullptr; + other.accessor = nullptr; + } + + AWTAccessor& AWTAccessor::operator = (AWTAccessor&& other) noexcept + { + this->env = other.env; + this->cls = other.cls; + this->accessor = other.accessor; + other.env = nullptr; + other.cls = nullptr; + other.accessor = nullptr; + return *this; + } + + AWTEventAccessor AWTAccessor::getAWTEventAccessor(JNIEnv* env) noexcept + { + jclass cls = env->FindClass("sun/awt/AWTEventAccessor"); + static jmethodID methodId = env->GetStaticMethodID(cls, "getAWTEventAccessor", "()Lsun/awt/AWTEventAccessor;"); + jobject accessor = env->CallStaticObjectMethod(cls, methodId); + return AWTEventAccessor{env, cls, accessor}; + } +} \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Java/AWTAccessor.hxx b/third-party/RemoteInput/RemoteInput/Java/AWTAccessor.hxx new file mode 100644 index 0000000..0f001aa --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Java/AWTAccessor.hxx @@ -0,0 +1,38 @@ +// +// AWTAccessor.hxx +// RemoteInput +// +// Created by Brandon on 2020-01-09. +// Copyright © 2020 XIO. All rights reserved. +// + +#ifndef AWTAccessor_HXX_INCLUDED +#define AWTAccessor_HXX_INCLUDED + +#include "JNI_Common.hxx" +#include "AWTEventAccessor.hxx" + +namespace java +{ + class AWTAccessor + { + private: + JNIEnv* env; + jclass cls; + jobject accessor; + + AWTAccessor(JNIEnv* env, jclass cls, jobject accessor) noexcept; + + public: + AWTAccessor(AWTAccessor&& other) noexcept; + AWTAccessor(const AWTAccessor& other) = delete; + ~AWTAccessor() noexcept; + + AWTAccessor& operator = (AWTAccessor&& other) noexcept; + AWTAccessor& operator = (const AWTAccessor& other) = delete; + + static AWTEventAccessor getAWTEventAccessor(JNIEnv* env) noexcept; + }; +} + +#endif /* AWTAccessor_HXX_INCLUDED */ diff --git a/third-party/RemoteInput/RemoteInput/Java/AWTEvent.cxx b/third-party/RemoteInput/RemoteInput/Java/AWTEvent.cxx new file mode 100644 index 0000000..890b054 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Java/AWTEvent.cxx @@ -0,0 +1,128 @@ +// +// AWTEvent.cxx +// RemoteInput +// +// Created by Brandon on 2020-01-09. +// Copyright © 2020 XIO. All rights reserved. +// + +#include "AWTEvent.hxx" +#include "RIEventQueue.hxx" +#include + +namespace java +{ + AWTEvent::AWTEvent() noexcept : self(nullptr) + { + } + + AWTEvent::~AWTEvent() noexcept + { + } + + AWTEvent::AWTEvent(jobject self) noexcept : self(self) + { + } + + AWTEvent::AWTEvent(AWTEvent&& other) noexcept : self(other.self) + { + other.self = nullptr; + } + + AWTEvent& AWTEvent::operator = (AWTEvent&& other) noexcept + { + this->self = other.self; + other.self = nullptr; + return *this; + } + + jobject AWTEvent::get() const noexcept + { + return self; + } + + jobject AWTEvent::getSource(JNIEnv* env) const noexcept + { + jclass cls = env->FindClass("java/util/EventObject"); + if (cls) + { + jmethodID methodID = env->GetMethodID(cls, "getSource", "()Ljava/lang/Object;"); + jobject result = env->CallObjectMethod(this->self, methodID); + env->DeleteLocalRef(cls); + + env->DeleteLocalRef(std::exchange(result, env->NewGlobalRef(result))); + return result; + } + return nullptr; + } + + void AWTEvent::SetSystemGenerated(JNIEnv* env, jobject event, bool is_system_generated) + { + if (is_system_generated) + { + //Get AWTEventAccessor + jclass cls = env->FindClass("sun/awt/AWTEventAccessor"); + if (cls) + { + static jmethodID accessor_methodId = env->GetStaticMethodID(cls, "getAWTEventAccessor", "()Lsun/awt/AWTEventAccessor;"); + jobject accessor = env->CallStaticObjectMethod(cls, accessor_methodId); + + if (accessor) + { + //Set System Generated + static jmethodID system_generated_methodId = env->GetMethodID(cls, "setSystemGenerated", "(Ljava/awt/AWTEvent;)V"); + env->CallVoidMethod(accessor, system_generated_methodId, event); + + //Cleanup + env->DeleteLocalRef(accessor); + } + + //Cleanup + env->DeleteLocalRef(cls); + } + } + } + + void AWTEvent::Post(JNIEnv* env, jobject event, bool is_system_generated) + { + if (is_system_generated) + { + SetSystemGenerated(env, event, is_system_generated); + } + + //Get Default Toolkit + jclass toolkit_class = env->FindClass("java/awt/Toolkit"); + if (toolkit_class) + { + static jmethodID toolkit_methodId = env->GetStaticMethodID(toolkit_class, "getDefaultToolkit", + "()Ljava/awt/Toolkit;"); + jobject toolkit = env->CallStaticObjectMethod(toolkit_class, toolkit_methodId); + if (toolkit) + { + //Get System Event Queue + static jmethodID queue_methodId = env->GetMethodID(toolkit_class, "getSystemEventQueue", + "()Ljava/awt/EventQueue;"); + jobject queue = env->CallObjectMethod(toolkit, queue_methodId); + if (queue) + { + //Post Event + jclass queue_class = env->FindClass("java/awt/EventQueue"); + if (queue_class) + { + static jmethodID post_methodId = env->GetMethodID(queue_class, "postEvent", "(Ljava/awt/AWTEvent;)V"); + env->CallVoidMethod(queue, post_methodId, RIEvent(env, event).get()); + + //Cleanup + env->DeleteLocalRef(queue_class); + } + //Cleanup + env->DeleteLocalRef(queue); + } + //Cleanup + env->DeleteLocalRef(toolkit); + } + //Cleanup + env->DeleteLocalRef(toolkit_class); + } + } +} \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Java/AWTEvent.hxx b/third-party/RemoteInput/RemoteInput/Java/AWTEvent.hxx new file mode 100644 index 0000000..ac5f554 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Java/AWTEvent.hxx @@ -0,0 +1,38 @@ +// +// AWTEvent.hxx +// RemoteInput +// +// Created by Brandon on 2020-01-09. +// Copyright © 2020 XIO. All rights reserved. +// + +#ifndef AWTEvent_HXX_INCLUDED +#define AWTEvent_HXX_INCLUDED + +#include "JNI_Common.hxx" + +namespace java +{ + class AWTEvent + { + protected: + jobject self; + static void SetSystemGenerated(JNIEnv* env, jobject event, bool is_system_generated); + static void Post(JNIEnv* env, jobject event, bool is_system_generated); + + public: + AWTEvent() noexcept; + AWTEvent(jobject self) noexcept; + AWTEvent(AWTEvent&& other) noexcept; + AWTEvent(const AWTEvent& other) = delete; + virtual ~AWTEvent() noexcept; + + AWTEvent& operator = (AWTEvent&& other) noexcept; + AWTEvent& operator = (const AWTEvent& other) = delete; + + virtual jobject get() const noexcept; + virtual jobject getSource(JNIEnv* env) const noexcept; + }; +} + +#endif /* AWTEvent_HXX_INCLUDED */ diff --git a/third-party/RemoteInput/RemoteInput/Java/AWTEventAccessor.cxx b/third-party/RemoteInput/RemoteInput/Java/AWTEventAccessor.cxx new file mode 100644 index 0000000..446252c --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Java/AWTEventAccessor.cxx @@ -0,0 +1,62 @@ +// +// AWTEventAccessor.cxx +// RemoteInput +// +// Created by Brandon on 2020-01-09. +// Copyright © 2020 XIO. All rights reserved. +// + +#include "AWTEventAccessor.hxx" +#include + +namespace java +{ + AWTEventAccessor::AWTEventAccessor(JNIEnv* env, jclass cls, jobject accessor) noexcept : env(env), cls(cls), accessor(accessor) + { + env->DeleteLocalRef(std::exchange(this->cls, static_cast(env->NewGlobalRef(this->cls)))); + env->DeleteLocalRef(std::exchange(this->accessor, env->NewGlobalRef(this->accessor))); + } + + AWTEventAccessor::~AWTEventAccessor() noexcept + { + if (cls) + { + env->DeleteGlobalRef(cls); + } + + if (accessor) + { + env->DeleteGlobalRef(accessor); + } + } + + AWTEventAccessor::AWTEventAccessor(AWTEventAccessor&& other) noexcept : env(other.env), cls(other.cls), accessor(other.accessor) + { + other.env = nullptr; + other.cls = nullptr; + other.accessor = nullptr; + } + + AWTEventAccessor& AWTEventAccessor::operator = (AWTEventAccessor&& other) noexcept + { + this->env = other.env; + this->cls = other.cls; + this->accessor = other.accessor; + other.env = nullptr; + other.cls = nullptr; + other.accessor = nullptr; + return *this; + } + + void AWTEventAccessor::setSystemGenerated(AWTEvent* event) const noexcept + { + static jmethodID methodId = env->GetMethodID(cls, "setSystemGenerated", "(Ljava/awt/AWTEvent;)V"); + env->CallVoidMethod(accessor, methodId, event->get()); + } + + void AWTEventAccessor::setPosted(AWTEvent* event) const noexcept + { + static jmethodID methodId = env->GetMethodID(cls, "setPosted", "(Ljava/awt/AWTEvent;)V"); + env->CallVoidMethod(accessor, methodId, event->get()); + } +} \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Java/AWTEventAccessor.hxx b/third-party/RemoteInput/RemoteInput/Java/AWTEventAccessor.hxx new file mode 100644 index 0000000..abfcb5c --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Java/AWTEventAccessor.hxx @@ -0,0 +1,38 @@ +// +// AWTEventAccessor.hxx +// RemoteInput +// +// Created by Brandon on 2020-01-09. +// Copyright © 2020 XIO. All rights reserved. +// + +#ifndef AWTEventAccessor_HXX_INCLUDED +#define AWTEventAccessor_HXX_INCLUDED + +#include "JNI_Common.hxx" +#include "AWTEvent.hxx" + +namespace java +{ + class AWTEventAccessor + { + private: + JNIEnv* env; + jclass cls; + jobject accessor; + + public: + AWTEventAccessor(JNIEnv* env, jclass cls, jobject accessor) noexcept; + AWTEventAccessor(AWTEventAccessor&& other) noexcept; + AWTEventAccessor(const AWTEventAccessor& other) = delete; + ~AWTEventAccessor() noexcept; + + AWTEventAccessor& operator = (AWTEventAccessor&& other) noexcept; + AWTEventAccessor& operator = (const AWTEventAccessor& other) = delete; + + void setSystemGenerated(AWTEvent* event) const noexcept; + void setPosted(AWTEvent* event) const noexcept; + }; +} + +#endif /* AWTEventAccessor_HXX_INCLUDED */ diff --git a/third-party/RemoteInput/RemoteInput/Java/Applet.cxx b/third-party/RemoteInput/RemoteInput/Java/Applet.cxx new file mode 100644 index 0000000..4fd3c16 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Java/Applet.cxx @@ -0,0 +1,19 @@ +// +// Applet.cxx +// RemoteInput +// +// Created by Brandon on 2020-01-09. +// Copyright © 2020 XIO. All rights reserved. +// + +#include "Applet.hxx" +#include + +namespace java +{ + Applet::Applet(JNIEnv* env, jobject applet, bool canDelete) noexcept : Window(env, nullptr, applet, canDelete) + { + this->cls = applet ? env->GetObjectClass(applet) : env->FindClass("java/awt/Applet"); + env->DeleteLocalRef(std::exchange(this->cls, static_cast(env->NewGlobalRef(this->cls)))); + } +} diff --git a/third-party/RemoteInput/RemoteInput/Java/Applet.hxx b/third-party/RemoteInput/RemoteInput/Java/Applet.hxx new file mode 100644 index 0000000..ed962d0 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Java/Applet.hxx @@ -0,0 +1,24 @@ +// +// Applet.hxx +// RemoteInput +// +// Created by Brandon on 2020-01-09. +// Copyright © 2020 XIO. All rights reserved. +// + +#ifndef APPLET_HXX_INCLUDED +#define APPLET_HXX_INCLUDED + +#include "JNI_Common.hxx" +#include "Window.hxx" + +namespace java +{ + class Applet final : public Window + { + public: + Applet(JNIEnv* env, jobject applet, bool canDelete = true) noexcept; + }; +} + +#endif /* APPLET_HXX_INCLUDED */ diff --git a/third-party/RemoteInput/RemoteInput/Java/Component.cxx b/third-party/RemoteInput/RemoteInput/Java/Component.cxx new file mode 100644 index 0000000..477bcdd --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Java/Component.cxx @@ -0,0 +1,231 @@ +// +// Component.cxx +// RemoteInput +// +// Created by Brandon on 2020-01-09. +// Copyright © 2020 XIO. All rights reserved. +// + +#include "Component.hxx" +#include "Container.hxx" +#include + +namespace java +{ + Component::Component(JNIEnv* env, jobject component, bool canDelete) noexcept : env(env), cls(nullptr), component(component), canDelete(canDelete) + { + if (!component) + { + this->cls = env->FindClass("java/awt/Component"); + env->DeleteLocalRef(std::exchange(this->cls, static_cast(env->NewGlobalRef(this->cls)))); + } + else + { + this->cls = env->GetObjectClass(component); + env->DeleteLocalRef(std::exchange(this->cls, static_cast(env->NewGlobalRef(this->cls)))); + } + } + + Component::Component(JNIEnv* env, jclass cls, jobject component, bool canDelete) noexcept : env(env), cls(cls), component(component), canDelete(canDelete) + { + } + + Component::Component(Component&& other) noexcept : env(other.env), cls(other.cls), component(other.component), canDelete(other.canDelete) + { + other.env = nullptr; + other.cls = nullptr; + other.component = nullptr; + other.canDelete = false; + } + + Component::~Component() noexcept + { + if (cls) + { + env->DeleteGlobalRef(cls); + } + + if (canDelete && component) + { + env->DeleteGlobalRef(component); + } + } + + Component& Component::operator = (Component&& other) noexcept + { + this->env = other.env; + this->cls = other.cls; + this->component = other.component; + this->canDelete = other.canDelete; + other.env = nullptr; + other.cls = nullptr; + other.component = nullptr; + other.canDelete = false; + return *this; + } + + JNIEnv* Component::getEnv() const noexcept + { + return env; + } + + + jobject Component::get() const noexcept + { + return component; + } + + void Component::getLocationOnScreen(std::int32_t &x, std::int32_t &y) const noexcept + { + jclass pointClass = env->FindClass("java/awt/Point"); + static jmethodID methodId = env->GetMethodID(cls, "getLocationOnScreen", "()Ljava/awt/Point;"); + static jfieldID xFieldId = env->GetFieldID(pointClass, "x", "I"); + static jfieldID yFieldId = env->GetFieldID(pointClass, "y", "I"); + + jobject object = env->CallObjectMethod(component, methodId); + if (object) + { + x = env->GetIntField(object, xFieldId); + y = env->GetIntField(object, yFieldId); + + env->DeleteLocalRef(object); + env->DeleteLocalRef(pointClass); + } + else + { + x = -1; + y = -1; + } + } + + void Component::getMousePosition(std::int32_t &x, std::int32_t &y) const noexcept + { + jclass pointClass = env->FindClass("java/awt/Point"); + static jmethodID methodId = env->GetMethodID(cls, "getMousePosition", "()Ljava/awt/Point;"); + static jfieldID xFieldId = env->GetFieldID(pointClass, "x", "I"); + static jfieldID yFieldId = env->GetFieldID(pointClass, "y", "I"); + + jobject object = env->CallObjectMethod(component, methodId); + if (object) + { + x = env->GetIntField(object, xFieldId); + y = env->GetIntField(object, yFieldId); + + env->DeleteLocalRef(object); + env->DeleteLocalRef(pointClass); + } + else + { + x = -1; + y = -1; + } + } + + void Component::getLocation(std::int32_t &x, std::int32_t &y) const noexcept + { + jclass pointClass = env->FindClass("java/awt/Point"); + static jmethodID methodId = env->GetMethodID(cls, "getLocation", "()Ljava/awt/Point;"); + static jfieldID xFieldId = env->GetFieldID(pointClass, "x", "I"); + static jfieldID yFieldId = env->GetFieldID(pointClass, "y", "I"); + + jobject object = env->CallObjectMethod(component, methodId); + if (object) + { + x = env->GetIntField(object, xFieldId); + y = env->GetIntField(object, yFieldId); + + env->DeleteLocalRef(object); + env->DeleteLocalRef(pointClass); + } + else + { + x = -1; + y = -1; + } + } + + void Component::getSize(std::size_t &width, std::size_t &height) const noexcept + { + static jmethodID widthId = env->GetMethodID(cls, "getWidth", "()I"); + static jmethodID heightId = env->GetMethodID(cls, "getHeight", "()I"); + width = env->CallIntMethod(component, widthId); + height = env->CallIntMethod(component, heightId); + } + + Component Component::getComponentAt(std::int32_t x, std::int32_t y) const noexcept + { + static jmethodID methodId = env->GetMethodID(cls, "getComponentAt", "(II)Ljava/awt/Component;"); + jobject object = env->CallObjectMethod(component, methodId, x, y); + if (object) + { + env->DeleteLocalRef(std::exchange(object, env->NewGlobalRef(object))); + return {env, object}; + } + return {env, nullptr}; + } + + Container Component::getParent() const noexcept + { + static jmethodID methodId = env->GetMethodID(cls, "getParent", "()Ljava/awt/Container;"); + jobject object = env->CallObjectMethod(component, methodId); + if (object) + { + env->DeleteLocalRef(std::exchange(object, env->NewGlobalRef(object))); + return {env, object}; + } + return {env, nullptr}; + } + + void Component::dispatchEvent(AWTEvent* event) const noexcept + { + Component::dispatchEvent(event->get()); + } + + void Component::dispatchEvent(jobject event) const noexcept + { + static jmethodID methodId = env->GetMethodID(cls, "dispatchEvent", "(Ljava/awt/AWTEvent;)V"); + env->CallVoidMethod(component, methodId, event); + } + + bool Component::isVisible() const noexcept + { + static jmethodID methodId = env->GetMethodID(cls, "isVisible", "()Z"); + return env->CallBooleanMethod(component, methodId); + } + + bool Component::isValid() const noexcept + { + static jmethodID methodId = env->GetMethodID(cls, "isValid", "()Z"); + return env->CallBooleanMethod(component, methodId); + } + + bool Component::isEnabled() const noexcept + { + static jmethodID methodId = env->GetMethodID(cls, "isEnabled", "()Z"); + return env->CallBooleanMethod(component, methodId); + } + + void Component::setEnabled(bool enabled) const noexcept + { + static jmethodID methodId = env->GetMethodID(cls, "setEnabled", "(Z)V"); + env->CallVoidMethod(component, methodId, enabled); + } + + bool Component::hasFocus() const noexcept + { + static jmethodID methodId = env->GetMethodID(cls, "hasFocus", "()Z"); + return env->CallBooleanMethod(component, methodId); + } + + bool Component::requestFocusInWindow() const noexcept + { + static jmethodID methodId = env->GetMethodID(cls, "requestFocusInWindow", "()Z"); + return env->CallBooleanMethod(component, methodId); + } + + void Component::requestFocus() const noexcept + { + static jmethodID methodId = env->GetMethodID(cls, "requestFocus", "()V"); + env->CallVoidMethod(component, methodId); + } +} \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Java/Component.hxx b/third-party/RemoteInput/RemoteInput/Java/Component.hxx new file mode 100644 index 0000000..e73a819 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Java/Component.hxx @@ -0,0 +1,64 @@ +// +// Component.hxx +// RemoteInput +// +// Created by Brandon on 2020-01-09. +// Copyright © 2020 XIO. All rights reserved. +// + +#ifndef COMPONENT_HXX_INCLUDED +#define COMPONENT_HXX_INCLUDED + +#include +#include +#include "JNI_Common.hxx" +#include "AWTEvent.hxx" + +namespace java +{ + class Container; + + class Component + { + protected: + JNIEnv* env; + jclass cls; + jobject component; + bool canDelete; + + public: + Component(JNIEnv* env, jobject component, bool canDelete = true) noexcept; + Component(JNIEnv* env, jclass cls, jobject component, bool canDelete = true) noexcept; + Component(Component&& other) noexcept; + Component(const Component& other) = delete; + virtual ~Component() noexcept; + + + Component& operator = (Component&& other) noexcept; + Component& operator = (const Component& other) = delete; + + JNIEnv* getEnv() const noexcept; + jobject get() const noexcept; + + void getLocationOnScreen(std::int32_t &x, std::int32_t &y) const noexcept; + void getMousePosition(std::int32_t &x, std::int32_t &y) const noexcept; + void getLocation(std::int32_t &x, std::int32_t &y) const noexcept; + void getSize(std::size_t &width, std::size_t &height) const noexcept; + Component getComponentAt(std::int32_t x, std::int32_t y) const noexcept; + Container getParent() const noexcept; + void dispatchEvent(AWTEvent* event) const noexcept; + void dispatchEvent(jobject event) const noexcept; + + + bool isVisible() const noexcept; + bool isValid() const noexcept; + bool isEnabled() const noexcept; + void setEnabled(bool enabled) const noexcept; + + bool hasFocus() const noexcept; + bool requestFocusInWindow() const noexcept; + void requestFocus() const noexcept; + }; +} + +#endif /* COMPONENT_HXX_INCLUDED */ diff --git a/third-party/RemoteInput/RemoteInput/Java/Container.cxx b/third-party/RemoteInput/RemoteInput/Java/Container.cxx new file mode 100644 index 0000000..0238d8e --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Java/Container.cxx @@ -0,0 +1,43 @@ +// +// Created by Brandon on 2021-10-21. +// + +#include "Container.hxx" +#include + +namespace java +{ + Container::Container(JNIEnv* env, jobject container, bool canDelete) noexcept : Component(env, nullptr, container, canDelete) + { + this->cls = container ? env->GetObjectClass(container) : env->FindClass("java/awt/Container"); + env->DeleteLocalRef(std::exchange(this->cls, static_cast(env->NewGlobalRef(this->cls)))); + } + + Container::Container(JNIEnv* env, jclass cls, jobject container, bool canDelete) noexcept : Component(env, cls, container, canDelete) + { + } + + Component Container::getComponent(std::int32_t index) const noexcept + { + jclass containerClass = env->FindClass("java/awt/Container"); + static jmethodID methodId = env->GetMethodID(containerClass, "getComponent", "(I)Ljava/awt/Component;"); + env->DeleteLocalRef(containerClass); + + jobject object = env->CallObjectMethod(this->component, methodId, index); + if (object) + { + env->DeleteLocalRef(std::exchange(object, env->NewGlobalRef(object))); + return {env, object}; + } + return {env, nullptr}; + } + + int Container::getComponentCount() const noexcept + { + jclass containerClass = env->FindClass("java/awt/Container"); + static jmethodID methodId = env->GetMethodID(containerClass, "getComponentCount", "()I"); + env->DeleteLocalRef(containerClass); + + return env->CallIntMethod(component, methodId); + } +} \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Java/Container.hxx b/third-party/RemoteInput/RemoteInput/Java/Container.hxx new file mode 100644 index 0000000..7a60d64 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Java/Container.hxx @@ -0,0 +1,25 @@ +// +// Created by Brandon on 2021-10-21. +// + +#ifndef REMOTEINPUT_CONTAINER_HXX +#define REMOTEINPUT_CONTAINER_HXX + +#include "JNI_Common.hxx" +#include "Component.hxx" + +namespace java +{ + class Container : public Component + { + public: + Container(JNIEnv* env, jobject container, bool canDelete = true) noexcept; + Container(JNIEnv* env, jclass cls, jobject container, bool canDelete = true) noexcept; + + Component getComponent(std::int32_t index) const noexcept; + int getComponentCount() const noexcept; + }; +} + + +#endif //REMOTEINPUT_CONTAINER_HXX diff --git a/third-party/RemoteInput/RemoteInput/Java/EventQueue.cxx b/third-party/RemoteInput/RemoteInput/Java/EventQueue.cxx new file mode 100644 index 0000000..5ddbcdb --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Java/EventQueue.cxx @@ -0,0 +1,68 @@ +// +// EventQueue.cxx +// RemoteInput +// +// Created by Brandon on 2020-01-09. +// Copyright © 2020 XIO. All rights reserved. +// + +#include "EventQueue.hxx" +#include + +namespace java +{ + EventQueue::EventQueue(JNIEnv* env) noexcept : env(env), cls(nullptr), queue(nullptr) + { + } + + EventQueue::EventQueue(JNIEnv* env, jclass cls, jobject queue) noexcept : env(env), cls(cls), queue(queue) + { + env->DeleteLocalRef(std::exchange(this->cls, static_cast(env->NewGlobalRef(this->cls)))); + env->DeleteLocalRef(std::exchange(this->queue, env->NewGlobalRef(this->queue))); + } + + EventQueue::~EventQueue() noexcept + { + if (cls) + { + env->DeleteGlobalRef(cls); + } + + // In Java 6, the system deletes the queue. + // If we attempt to delete it, it will crash! + /*if (queue) + { + env->DeleteGlobalRef(queue); + }*/ + } + + EventQueue::EventQueue(EventQueue&& other) noexcept : env(other.env), cls(other.cls), queue(other.queue) + { + other.env = nullptr; + other.cls = nullptr; + other.queue = nullptr; + } + + EventQueue& EventQueue::operator = (EventQueue&& other) noexcept + { + this->env = other.env; + this->cls = other.cls; + this->queue = other.queue; + other.env = nullptr; + other.cls = nullptr; + other.queue = nullptr; + return *this; + } + + void EventQueue::push(EventQueue* queue) const noexcept + { + static jmethodID methodId = env->GetMethodID(cls, "push", "(Ljava/awt/EventQueue;)V"); + env->CallVoidMethod(this->queue, methodId, queue->queue); + } + + void EventQueue::postEvent(AWTEvent* event) const noexcept + { + static jmethodID methodId = env->GetMethodID(cls, "postEvent", "(Ljava/awt/AWTEvent;)V"); + env->CallVoidMethod(queue, methodId, event->get()); + } +} \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Java/EventQueue.hxx b/third-party/RemoteInput/RemoteInput/Java/EventQueue.hxx new file mode 100644 index 0000000..adbeb4d --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Java/EventQueue.hxx @@ -0,0 +1,40 @@ +// +// EventQueue.hxx +// RemoteInput +// +// Created by Brandon on 2020-01-09. +// Copyright © 2020 XIO. All rights reserved. +// + +#ifndef EventQueue_HXX_INCLUDED +#define EventQueue_HXX_INCLUDED + +#include "JNI_Common.hxx" +#include "AWTEvent.hxx" + +namespace java +{ + class EventQueue + { + protected: + JNIEnv* env; + jclass cls; + jobject queue; + + EventQueue(JNIEnv* env) noexcept; + + public: + EventQueue(JNIEnv* env, jclass cls, jobject queue) noexcept; + EventQueue(EventQueue&& other) noexcept; + EventQueue(const EventQueue& other) = delete; + virtual ~EventQueue() noexcept; + + EventQueue& operator = (EventQueue&& other) noexcept; + EventQueue& operator = (const EventQueue& other) = delete; + + virtual void push(EventQueue* queue) const noexcept; + virtual void postEvent(AWTEvent* event) const noexcept; + }; +} + +#endif /* EventQueue_HXX_INCLUDED */ diff --git a/third-party/RemoteInput/RemoteInput/Java/FocusEvent.cxx b/third-party/RemoteInput/RemoteInput/Java/FocusEvent.cxx new file mode 100644 index 0000000..a1d5025 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Java/FocusEvent.cxx @@ -0,0 +1,93 @@ +// +// FocusEvent.cxx +// RemoteInput +// +// Created by Brandon on 2020-01-09. +// Copyright © 2020 XIO. All rights reserved. +// + +#include "FocusEvent.hxx" +#include + +namespace java +{ + FocusEvent::FocusEvent(JNIEnv* env, Component* receiver, std::int32_t id, bool temporary, Cause cause) noexcept : AWTEvent(), env(env), cls(nullptr) + { + this->cls = env->FindClass("java/awt/event/FocusEvent"); + env->DeleteLocalRef(std::exchange(this->cls, static_cast(env->NewGlobalRef(this->cls)))); + + static jmethodID methodId = env->GetMethodID(cls, "", "(Ljava/awt/Component;IZ)V"); + self = env->NewObject(cls, methodId, receiver->get(), id, temporary); + env->DeleteLocalRef(std::exchange(self, static_cast(env->NewGlobalRef(self)))); + } + + FocusEvent::~FocusEvent() noexcept + { + env->DeleteGlobalRef(cls); + env->DeleteGlobalRef(self); + } + + void FocusEvent::Dispatch(JNIEnv* env, Component* receiver, std::int32_t id, bool temporary, Cause cause, bool is_system_generated) noexcept + { + jclass cls = env->FindClass("java/awt/event/FocusEvent"); + if (cls) + { + static jmethodID methodId = env->GetMethodID(cls, "", "(Ljava/awt/Component;IZ)V"); + jobject event = env->NewObject(cls, methodId, receiver->get(), id, temporary); + + if (event) + { + AWTEvent::SetSystemGenerated(env, event, is_system_generated); + receiver->dispatchEvent(event); + env->DeleteLocalRef(event); + } + + env->DeleteLocalRef(cls); + } + } + + void FocusEvent::Post(JNIEnv* env, Component* receiver, std::int32_t id, bool temporary, Cause cause, bool is_system_generated) noexcept + { + jclass cls = env->FindClass("java/awt/event/FocusEvent"); + if (cls) + { + static jmethodID methodId = env->GetMethodID(cls, "", "(Ljava/awt/Component;IZ)V"); + jobject event = env->NewObject(cls, methodId, receiver->get(), id, temporary); + if (event) + { + AWTEvent::Post(env, event, is_system_generated); + env->DeleteLocalRef(event); + } + env->DeleteLocalRef(cls); + } + } + + jobject FocusEvent::GetCauseDescription(JNIEnv* env, Cause cause) noexcept + { + //Java 9+ + static const char* const causes[] = { + "UNKNOWN", + "MOUSE_EVENT", + "TRAVERSAL", + "TRAVERSAL_UP", + "TRAVERSAL_DOWN", + "TRAVERSAL_FORWARD", + "TRAVERSAL_BACKWARD", + "ROLLBACK", + "UNEXPECTED", + "ACTIVATION", + "CLEAR_GLOBAL_FOCUS_OWNER" + }; + + jclass cause_cls = env->FindClass("java/awt/event/FocusEvent$Cause"); + if (cause_cls) + { + static jfieldID fieldId = env->GetStaticFieldID(cause_cls , causes[static_cast(cause)], "Ljava/awt/event/FocusEvent$Cause;"); + jobject cause = env->GetStaticObjectField(cause_cls, fieldId); + env->DeleteLocalRef(cause_cls); + env->DeleteLocalRef(std::exchange(cause_cls, static_cast(env->NewGlobalRef(cause_cls)))); + return cause; + } + return nullptr; + } +} \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Java/FocusEvent.hxx b/third-party/RemoteInput/RemoteInput/Java/FocusEvent.hxx new file mode 100644 index 0000000..f99d5f3 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Java/FocusEvent.hxx @@ -0,0 +1,66 @@ +// +// FocusEvent.hxx +// RemoteInput +// +// Created by Brandon on 2020-01-09. +// Copyright © 2020 XIO. All rights reserved. +// + +#ifndef FocusEvent_HXX_INCLUDED +#define FocusEvent_HXX_INCLUDED + +#include "JNI_Common.hxx" +#include "AWTEvent.hxx" +#include "Component.hxx" + +namespace java +{ + class FocusEvent final : public AWTEvent + { + public: + enum class Cause : std::uint32_t; + + private: + JNIEnv* env; + jclass cls; + static jobject GetCauseDescription(JNIEnv* env, Cause cause) noexcept; + + public: + FocusEvent(JNIEnv* env, Component* receiver, std::int32_t id, bool temporary, Cause cause) noexcept; + virtual ~FocusEvent() noexcept; + + static void Dispatch(JNIEnv* env, Component* receiver, std::int32_t id, bool temporary, Cause cause, bool is_system_generated = false) noexcept; + static void Post(JNIEnv* env, Component* receiver, std::int32_t id, bool temporary, Cause cause, bool is_system_generated = false) noexcept; + + #if !defined(_MSC_VER) + #warning "WINDOWS BS" + #endif + + #if defined(MOUSE_EVENT) && (defined(_WIN32) || defined(_WIN64)) + #undef MOUSE_EVENT + #endif + + enum class Cause : std::uint32_t + { + UNKNOWN, + MOUSE_EVENT, + TRAVERSAL, + TRAVERSAL_UP, + TRAVERSAL_DOWN, + TRAVERSAL_FORWARD, + TRAVERSAL_BACKWARD, + ROLLBACK, + UNEXPECTED, + ACTIVATION, + CLEAR_GLOBAL_FOCUS_OWNER + }; + + enum FocusCodes : std::int32_t + { + FOCUS_GAINED = 1004, + FOCUS_LOST = 1005 + }; + }; +} + +#endif /* FocusEvent_HXX_INCLUDED */ diff --git a/third-party/RemoteInput/RemoteInput/Java/Frame.cxx b/third-party/RemoteInput/RemoteInput/Java/Frame.cxx new file mode 100644 index 0000000..ba1c924 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Java/Frame.cxx @@ -0,0 +1,31 @@ +// +// Frame.cxx +// RemoteInput +// +// Created by Brandon on 2020-01-09. +// Copyright © 2020 XIO. All rights reserved. +// + +#include "Frame.hxx" +#include + +namespace java +{ + Frame::Frame(JNIEnv* env, jobject frame, bool canDelete) noexcept : Window(env, nullptr, frame, canDelete) + { + this->cls = frame ? env->GetObjectClass(frame) : env->FindClass("java/awt/JFrame"); + env->DeleteLocalRef(std::exchange(this->cls, static_cast(env->NewGlobalRef(this->cls)))); + } + + Component Frame::getContentPane() const noexcept + { + static jmethodID methodId = env->GetMethodID(cls, "getContentPane", "()Ljava/awt/Component;"); + jobject object = env->CallObjectMethod(component, methodId); + if (object) + { + env->DeleteLocalRef(std::exchange(object, env->NewGlobalRef(object))); + return {env, object}; + } + return {env, nullptr}; + } +} \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Java/Frame.hxx b/third-party/RemoteInput/RemoteInput/Java/Frame.hxx new file mode 100644 index 0000000..d4c00be --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Java/Frame.hxx @@ -0,0 +1,26 @@ +// +// Frame.hxx +// RemoteInput +// +// Created by Brandon on 2020-01-09. +// Copyright © 2020 XIO. All rights reserved. +// + +#ifndef FRAME_HXX_INCLUDED +#define FRAME_HXX_INCLUDED + +#include "JNI_Common.hxx" +#include "Window.hxx" + +namespace java +{ + class Frame final : public Window + { + public: + Frame(JNIEnv* env, jobject frame, bool canDelete = true) noexcept; + + Component getContentPane() const noexcept; + }; +} + +#endif /* FRAME_HXX_INCLUDED */ diff --git a/third-party/RemoteInput/RemoteInput/Java/InputEvent.cxx b/third-party/RemoteInput/RemoteInput/Java/InputEvent.cxx new file mode 100644 index 0000000..72ee226 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Java/InputEvent.cxx @@ -0,0 +1,40 @@ +// +// InputEvent.cxx +// RemoteInput +// +// Created by Brandon on 2019-12-27. +// Copyright © 2019 XIO. All rights reserved. +// + +#include "InputEvent.hxx" + +namespace java +{ + std::int32_t InputEvent::GetDownMaskForButton(std::int32_t button) noexcept + { + static const std::int32_t masks[] = { + InputEventMasks::BUTTON1_DOWN_MASK, + InputEventMasks::BUTTON2_DOWN_MASK, + InputEventMasks::BUTTON3_DOWN_MASK, + 1 << 14, //4th physical button (this is not a wheel!) + 1 << 15, //(this is not a wheel!) + 1 << 16, + 1 << 17, + 1 << 18, + 1 << 19, + 1 << 20, + 1 << 21, + 1 << 22, + 1 << 23, + 1 << 24, + 1 << 25, + 1 << 26, + 1 << 27, + 1 << 28, + 1 << 29, + 1 << 30 + }; + + return masks[button - 1]; + } +} \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Java/InputEvent.hxx b/third-party/RemoteInput/RemoteInput/Java/InputEvent.hxx new file mode 100644 index 0000000..a364659 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Java/InputEvent.hxx @@ -0,0 +1,35 @@ +// +// InputEvent.hxx +// RemoteInput +// +// Created by Brandon on 2019-12-27. +// Copyright © 2019 XIO. All rights reserved. +// + +#ifndef InputEvent_HXX_INCLUDED +#define InputEvent_HXX_INCLUDED + +#include "JNI_Common.hxx" +#include + +namespace java +{ + struct InputEvent + { + enum InputEventMasks : std::uint32_t + { + SHIFT_DOWN_MASK = 1 << 6, + CTRL_DOWN_MASK = 1 << 7, + META_DOWN_MASK = 1 << 8, + ALT_DOWN_MASK = 1 << 9, + BUTTON1_DOWN_MASK = 1 << 10, + BUTTON2_DOWN_MASK = 1 << 11, + BUTTON3_DOWN_MASK = 1 << 12, + ALT_GRAPH_DOWN_MASK = 1 << 13 + }; + + static std::int32_t GetDownMaskForButton(std::int32_t button) noexcept; + }; +} + +#endif /* InputEvent_HXX_INCLUDED */ diff --git a/third-party/RemoteInput/RemoteInput/Java/JNI_Common.hxx b/third-party/RemoteInput/RemoteInput/Java/JNI_Common.hxx new file mode 100644 index 0000000..7ad7690 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Java/JNI_Common.hxx @@ -0,0 +1,22 @@ +// JNI_Common.hxx +// RemoteInput +// +// Created by Brandon on 2022-12-02. +// Copyright © 2022 XIO. All rights reserved. +// + +#ifndef JNI_COMMON_HXX_INCLUDED +#define JNI_COMMON_HXX_INCLUDED + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wignored-attributes" +#endif + +#include + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + +#endif //JNI_COMMON_HXX_INCLUDED diff --git a/third-party/RemoteInput/RemoteInput/Java/KeyEvent.cxx b/third-party/RemoteInput/RemoteInput/Java/KeyEvent.cxx new file mode 100644 index 0000000..83b6bf9 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Java/KeyEvent.cxx @@ -0,0 +1,63 @@ +// +// KeyEvent.cxx +// RemoteInput +// +// Created by Brandon on 2020-01-09. +// Copyright © 2020 XIO. All rights reserved. +// + +#include "KeyEvent.hxx" +#include + +namespace java +{ + KeyEvent::KeyEvent(JNIEnv* env, Component* source, std::int32_t id, std::int64_t when, std::int32_t modifiers, std::int32_t keyCode, jchar keyChar, std::int32_t keyLocation) noexcept : AWTEvent(), env(env), cls(nullptr) + { + this->cls = env->FindClass("java/awt/event/KeyEvent"); + env->DeleteLocalRef(std::exchange(this->cls, static_cast(env->NewGlobalRef(this->cls)))); + + static jmethodID methodId = env->GetMethodID(cls, "", "(Ljava/awt/Component;IJIICI)V"); + self = env->NewObject(cls, methodId, source->get(), id, when, modifiers, keyCode, keyChar, keyLocation); + env->DeleteLocalRef(std::exchange(self, static_cast(env->NewGlobalRef(self)))); + } + + KeyEvent::~KeyEvent() noexcept + { + env->DeleteGlobalRef(cls); + env->DeleteGlobalRef(self); + } + + void KeyEvent::Dispatch(JNIEnv* env, Component* receiver, Component* source, std::int32_t id, std::int64_t when, std::int32_t modifiers, std::int32_t keyCode, jchar keyChar, std::int32_t keyLocation, bool is_system_generated) noexcept + { + jclass cls = env->FindClass("java/awt/event/KeyEvent"); + if (cls) + { + static jmethodID methodId = env->GetMethodID(cls, "", "(Ljava/awt/Component;IJIICI)V"); + jobject event = env->NewObject(cls, methodId, source->get(), id, when, modifiers, keyCode, keyChar, keyLocation); + if (event) + { + AWTEvent::SetSystemGenerated(env, event, is_system_generated); + receiver->dispatchEvent(event); + env->DeleteLocalRef(event); + } + + env->DeleteLocalRef(cls); + } + } + + void KeyEvent::Post(JNIEnv* env, Component* source, std::int32_t id, std::int64_t when, std::int32_t modifiers, std::int32_t keyCode, jchar keyChar, std::int32_t keyLocation, bool is_system_generated) noexcept + { + jclass cls = env->FindClass("java/awt/event/KeyEvent"); + if (cls) + { + static jmethodID methodId = env->GetMethodID(cls, "", "(Ljava/awt/Component;IJIICI)V"); + jobject event = env->NewObject(cls, methodId, source->get(), id, when, modifiers, keyCode, keyChar, keyLocation); + if (event) + { + AWTEvent::Post(env, event, is_system_generated); + env->DeleteLocalRef(event); + } + env->DeleteLocalRef(cls); + } + } +} \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Java/KeyEvent.hxx b/third-party/RemoteInput/RemoteInput/Java/KeyEvent.hxx new file mode 100644 index 0000000..58d0e24 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Java/KeyEvent.hxx @@ -0,0 +1,49 @@ +// +// KeyEvent.hxx +// RemoteInput +// +// Created by Brandon on 2020-01-09. +// Copyright © 2020 XIO. All rights reserved. +// + +#ifndef KeyEvent_HXX_INCLUDED +#define KeyEvent_HXX_INCLUDED + +#include "JNI_Common.hxx" +#include "AWTEvent.hxx" +#include "Component.hxx" + +namespace java +{ + class KeyEvent final : public AWTEvent + { + private: + JNIEnv* env; + jclass cls; + + public: + KeyEvent(JNIEnv* env, Component* source, std::int32_t id, std::int64_t when, std::int32_t modifiers, std::int32_t keyCode, jchar keyChar, std::int32_t keyLocation) noexcept; + virtual ~KeyEvent() noexcept; + + static void Dispatch(JNIEnv* env, Component* receiver, Component* source, std::int32_t id, std::int64_t when, std::int32_t modifiers, std::int32_t keyCode, jchar keyChar, std::int32_t keyLocation, bool is_system_generated = false) noexcept; + + static void Post(JNIEnv* env, Component* source, std::int32_t id, std::int64_t when, std::int32_t modifiers, std::int32_t keyCode, jchar keyChar, std::int32_t keyLocation, bool is_system_generated = false) noexcept; + + enum KeyCodes : std::int32_t + { + KEY_TYPED = 400, + KEY_PRESSED = 401, + KEY_RELEASED = 402, + + VK_UNDEFINED = 0x0, + CHAR_UNDEFINED = 0xFF, + KEY_LOCATION_UNKNOWN = 0x0, + KEY_LOCATION_STANDARD = 0x1, + KEY_LOCATION_LEFT = 0x2, + KEY_LOCATION_RIGHT = 0x3, + KEY_LOCATION_NUMPAD = 0x4 + }; + }; +} + +#endif /* KeyEvent_HXX_INCLUDED */ diff --git a/third-party/RemoteInput/RemoteInput/Java/MouseEvent.cxx b/third-party/RemoteInput/RemoteInput/Java/MouseEvent.cxx new file mode 100644 index 0000000..457c6af --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Java/MouseEvent.cxx @@ -0,0 +1,63 @@ +// +// MouseEvent.cxx +// RemoteInput +// +// Created by Brandon on 2020-01-09. +// Copyright © 2020 XIO. All rights reserved. +// + +#include "MouseEvent.hxx" +#include + +namespace java +{ + MouseEvent::MouseEvent(JNIEnv* env, Component* source, std::int32_t id, std::int64_t when, std::int32_t modifiers, std::int32_t x, std::int32_t y, std::int32_t clickCount, bool popupTrigger, std::int32_t button) noexcept : AWTEvent(), env(env), cls(nullptr) + { + this->cls = env->FindClass("java/awt/event/MouseEvent"); + env->DeleteLocalRef(std::exchange(this->cls, static_cast(env->NewGlobalRef(this->cls)))); + + static jmethodID methodId = env->GetMethodID(cls, "", "(Ljava/awt/Component;IJIIIIZI)V"); + self = env->NewObject(cls, methodId, source->get(), id, when, modifiers, x, y, clickCount, popupTrigger, button); + env->DeleteLocalRef(std::exchange(self, static_cast(env->NewGlobalRef(self)))); + } + + MouseEvent::~MouseEvent() noexcept + { + env->DeleteGlobalRef(cls); + env->DeleteGlobalRef(self); + } + + void MouseEvent::Dispatch(JNIEnv* env, Component* receiver, Component* source, std::int32_t id, std::int64_t when, std::int32_t modifiers, std::int32_t x, std::int32_t y, std::int32_t clickCount, bool popupTrigger, std::int32_t button, bool is_system_generated) noexcept + { + jclass cls = env->FindClass("java/awt/event/MouseEvent"); + if (cls) + { + static jmethodID methodId = env->GetMethodID(cls, "", "(Ljava/awt/Component;IJIIIIZI)V"); + jobject event = env->NewObject(cls, methodId, source->get(), id, when, modifiers, x, y, clickCount, popupTrigger, button); + if (event) + { + AWTEvent::SetSystemGenerated(env, event, is_system_generated); + receiver->dispatchEvent(event); + env->DeleteLocalRef(event); + } + + env->DeleteLocalRef(cls); + } + } + + void MouseEvent::Post(JNIEnv* env, Component* source, std::int32_t id, std::int64_t when, std::int32_t modifiers, std::int32_t x, std::int32_t y, std::int32_t clickCount, bool popupTrigger, std::int32_t button, bool is_system_generated) noexcept + { + jclass cls = env->FindClass("java/awt/event/MouseEvent"); + if (cls) + { + static jmethodID methodId = env->GetMethodID(cls, "", "(Ljava/awt/Component;IJIIIIZI)V"); + jobject event = env->NewObject(cls, methodId, source->get(), id, when, modifiers, x, y, clickCount, popupTrigger, button); + if (event) + { + AWTEvent::Post(env, event, is_system_generated); + env->DeleteLocalRef(event); + } + env->DeleteLocalRef(cls); + } + } +} \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Java/MouseEvent.hxx b/third-party/RemoteInput/RemoteInput/Java/MouseEvent.hxx new file mode 100644 index 0000000..fe7037d --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Java/MouseEvent.hxx @@ -0,0 +1,59 @@ +// +// MouseEvent.hxx +// RemoteInput +// +// Created by Brandon on 2020-01-09. +// Copyright © 2020 XIO. All rights reserved. +// + +#ifndef MouseEvent_HXX_INCLUDED +#define MouseEvent_HXX_INCLUDED + +#include "JNI_Common.hxx" +#include "AWTEvent.hxx" +#include "Component.hxx" + +namespace java +{ + class MouseEvent final : public AWTEvent + { + private: + JNIEnv* env; + jclass cls; + + public: + MouseEvent(JNIEnv* env, Component* source, std::int32_t id, std::int64_t when, std::int32_t modifiers, std::int32_t x, std::int32_t y, std::int32_t clickCount, bool popupTrigger, std::int32_t button) noexcept; + virtual ~MouseEvent() noexcept; + + static void Dispatch(JNIEnv* env, Component* receiver, Component* source, std::int32_t id, std::int64_t when, std::int32_t modifiers, std::int32_t x, std::int32_t y, std::int32_t clickCount, bool popupTrigger, std::int32_t button, bool is_system_generated = false) noexcept; + + static void Post(JNIEnv* env, Component* source, std::int32_t id, std::int64_t when, std::int32_t modifiers, std::int32_t x, std::int32_t y, std::int32_t clickCount, bool popupTrigger, std::int32_t button, bool is_system_generated = false) noexcept; + + #if !defined(_MSC_VER) + #warning "WINDOWS BS" + #endif + + #if defined(MOUSE_MOVED) && (defined(_WIN32) || defined(_WIN64)) + #undef MOUSE_MOVED + #endif // MOUSE_MOVED + + enum MouseEventCodes : std::uint32_t + { + MOUSE_CLICKED = 500, + MOUSE_PRESSED = 501, + MOUSE_RELEASED = 502, + MOUSE_MOVED = 503, + MOUSE_ENTERED = 504, + MOUSE_EXITED = 505, + MOUSE_DRAGGED = 506, + MOUSE_WHEEL = 507, + + NOBUTTON = 0, + BUTTON1 = 1, + BUTTON2 = 2, + BUTTON3 = 3, + }; + }; +} + +#endif /* MouseEvent_HXX_INCLUDED */ diff --git a/third-party/RemoteInput/RemoteInput/Java/MouseWheelEvent.cxx b/third-party/RemoteInput/RemoteInput/Java/MouseWheelEvent.cxx new file mode 100644 index 0000000..804d99d --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Java/MouseWheelEvent.cxx @@ -0,0 +1,63 @@ +// +// MouseWheelEvent.cxx +// RemoteInput +// +// Created by Brandon on 2020-01-10. +// Copyright © 2020 XIO. All rights reserved. +// + +#include "MouseWheelEvent.hxx" +#include + +namespace java +{ + MouseWheelEvent::MouseWheelEvent(JNIEnv* env, Component* source, std::int32_t id, std::int64_t when, std::int32_t modifiers, std::int32_t x, std::int32_t y, std::int32_t xAbs, std::int32_t yAbs, std::int32_t clickCount, bool popupTrigger, std::int32_t scrollType, std::int32_t scrollAmount, std::int32_t wheelRotation, double preciseWheelRotation) noexcept : AWTEvent(), env(env), cls(nullptr) + { + this->cls = env->FindClass("java/awt/event/MouseWheelEvent"); + env->DeleteLocalRef(std::exchange(this->cls, static_cast(env->NewGlobalRef(this->cls)))); + + static jmethodID methodId = env->GetMethodID(cls, "", "(Ljava/awt/Component;IJIIIIIIZIIID)V"); + self = env->NewObject(cls, methodId, source->get(), id, when, modifiers, x, y, xAbs, yAbs, clickCount, popupTrigger, scrollType, scrollAmount, wheelRotation, preciseWheelRotation); + env->DeleteLocalRef(std::exchange(self, static_cast(env->NewGlobalRef(self)))); + } + + MouseWheelEvent::~MouseWheelEvent() noexcept + { + env->DeleteGlobalRef(cls); + env->DeleteGlobalRef(self); + } + + void MouseWheelEvent::Dispatch(JNIEnv* env, Component* receiver, Component* source, std::int32_t id, std::int64_t when, std::int32_t modifiers, std::int32_t x, std::int32_t y, std::int32_t xAbs, std::int32_t yAbs, std::int32_t clickCount, bool popupTrigger, std::int32_t scrollType, std::int32_t scrollAmount, std::int32_t wheelRotation, double preciseWheelRotation, bool is_system_generated) noexcept + { + jclass cls = env->FindClass("java/awt/event/MouseWheelEvent"); + if (cls) + { + static jmethodID methodId = env->GetMethodID(cls, "", "(Ljava/awt/Component;IJIIIIIIZIIID)V"); + jobject event = env->NewObject(cls, methodId, source->get(), id, when, modifiers, x, y, xAbs, yAbs, clickCount, popupTrigger, scrollType, scrollAmount, wheelRotation, preciseWheelRotation); + if (event) + { + AWTEvent::SetSystemGenerated(env, event, is_system_generated); + receiver->dispatchEvent(event); + env->DeleteLocalRef(event); + } + + env->DeleteLocalRef(cls); + } + } + + void MouseWheelEvent::Post(JNIEnv* env, Component* source, std::int32_t id, std::int64_t when, std::int32_t modifiers, std::int32_t x, std::int32_t y, std::int32_t xAbs, std::int32_t yAbs, std::int32_t clickCount, bool popupTrigger, std::int32_t scrollType, std::int32_t scrollAmount, std::int32_t wheelRotation, double preciseWheelRotation, bool is_system_generated) noexcept + { + jclass cls = env->FindClass("java/awt/event/MouseWheelEvent"); + if (cls) + { + static jmethodID methodId = env->GetMethodID(cls, "", "(Ljava/awt/Component;IJIIIIIIZIIID)V"); + jobject event = env->NewObject(cls, methodId, source->get(), id, when, modifiers, x, y, xAbs, yAbs, clickCount, popupTrigger, scrollType, scrollAmount, wheelRotation, preciseWheelRotation); + if (event) + { + AWTEvent::Post(env, event, is_system_generated); + env->DeleteLocalRef(event); + } + env->DeleteLocalRef(cls); + } + } +} \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Java/MouseWheelEvent.hxx b/third-party/RemoteInput/RemoteInput/Java/MouseWheelEvent.hxx new file mode 100644 index 0000000..82525f2 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Java/MouseWheelEvent.hxx @@ -0,0 +1,40 @@ +// +// MouseWheelEvent.hxx +// RemoteInput +// +// Created by Brandon on 2020-01-10. +// Copyright © 2020 XIO. All rights reserved. +// + +#ifndef MouseWheelEvent_HXX_INCLUDED +#define MouseWheelEvent_HXX_INCLUDED + +#include "JNI_Common.hxx" +#include "AWTEvent.hxx" +#include "Component.hxx" + +namespace java +{ + class MouseWheelEvent final : public AWTEvent + { + private: + JNIEnv* env; + jclass cls; + + public: + MouseWheelEvent(JNIEnv* env, Component* source, std::int32_t id, std::int64_t when, std::int32_t modifiers, std::int32_t x, std::int32_t y, std::int32_t xAbs, std::int32_t yAbs, std::int32_t clickCount, bool popupTrigger, std::int32_t scrollType, std::int32_t scrollAmount, std::int32_t wheelRotation, double preciseWheelRotation) noexcept; + virtual ~MouseWheelEvent() noexcept; + + static void Dispatch(JNIEnv* env, Component* receiver, Component* source, std::int32_t id, std::int64_t when, std::int32_t modifiers, std::int32_t x, std::int32_t y, std::int32_t xAbs, std::int32_t yAbs, std::int32_t clickCount, bool popupTrigger, std::int32_t scrollType, std::int32_t scrollAmount, std::int32_t wheelRotation, double preciseWheelRotation, bool is_system_generated = false) noexcept; + + static void Post(JNIEnv* env, Component* source, std::int32_t id, std::int64_t when, std::int32_t modifiers, std::int32_t x, std::int32_t y, std::int32_t xAbs, std::int32_t yAbs, std::int32_t clickCount, bool popupTrigger, std::int32_t scrollType, std::int32_t scrollAmount, std::int32_t wheelRotation, double preciseWheelRotation, bool is_system_generated = false) noexcept; + + enum MouseWheelEventCodes : std::uint32_t + { + WHEEL_UNIT_SCROLL = 0, + WHEEL_BLOCK_SCROLL = 1 + }; + }; +} + +#endif /* MouseWheelEvent_HXX_INCLUDED */ diff --git a/third-party/RemoteInput/RemoteInput/Java/PointerInfo.cxx b/third-party/RemoteInput/RemoteInput/Java/PointerInfo.cxx new file mode 100644 index 0000000..fbd2f34 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Java/PointerInfo.cxx @@ -0,0 +1,101 @@ +// +// PointerInfo.cxx +// RemoteInput +// +// Created by Brandon on 2020-03-16. +// Copyright © 2020 XIO. All rights reserved. +// + +#include "PointerInfo.hxx" +#include + +namespace java +{ + PointerInfo::PointerInfo(JNIEnv* env, jclass cls, jobject pointerInfo) noexcept : env(env), cls(cls), pointerInfo(pointerInfo) + { + if (!this->cls) + { + this->cls = env->GetObjectClass(pointerInfo); + } + + env->DeleteLocalRef(std::exchange(this->cls, static_cast(env->NewGlobalRef(this->cls)))); + env->DeleteLocalRef(std::exchange(this->pointerInfo, env->NewGlobalRef(this->pointerInfo))); + } + + PointerInfo::~PointerInfo() noexcept + { + if (cls) + { + env->DeleteGlobalRef(cls); + } + + if (pointerInfo) + { + env->DeleteGlobalRef(pointerInfo); + } + } + + PointerInfo::PointerInfo(PointerInfo&& other) noexcept : env(other.env), cls(other.cls), pointerInfo(other.pointerInfo) + { + other.env = nullptr; + other.cls = nullptr; + other.pointerInfo = nullptr; + } + + PointerInfo& PointerInfo::operator = (PointerInfo&& other) noexcept + { + this->env = other.env; + this->cls = other.cls; + this->pointerInfo = other.pointerInfo; + other.env = nullptr; + other.cls = nullptr; + other.pointerInfo = nullptr; + return *this; + } + + PointerInfo PointerInfo::getPointerInfo(JNIEnv* env) noexcept + { + jclass cls = env->FindClass("java/awt/MouseInfo"); + static jmethodID methodId = env->GetStaticMethodID(cls, "getPointerInfo", "()Ljava/awt/PointerInfo;"); + jobject pointerInfo = env->CallStaticObjectMethod(cls, methodId); + return PointerInfo{env, cls, pointerInfo}; + } + + void PointerInfo::getLocation(std::int32_t &x, std::int32_t &y) const noexcept + { + jclass pointInfoClass = env->FindClass("java/awt/PointerInfo"); + static jmethodID methodId = env->GetMethodID(pointInfoClass, "getLocation", "()Ljava/awt/Point;"); + + jobject object = env->CallObjectMethod(pointerInfo, methodId); + if (object) + { + jclass pointClass = env->FindClass("java/awt/Point"); + static jfieldID xFieldId = env->GetFieldID(pointClass, "x", "I"); + static jfieldID yFieldId = env->GetFieldID(pointClass, "y", "I"); + x = env->GetIntField(object, xFieldId); + y = env->GetIntField(object, yFieldId); + env->DeleteLocalRef(pointClass); + + env->DeleteLocalRef(object); + env->DeleteLocalRef(pointInfoClass); + } + else + { + x = -1; + y = -1; + } + } + + void PointerInfo::PointToScreen(JNIEnv* env, std::int32_t &x, std::int32_t &y, Component* component) noexcept + { + // jclass swingUtilitiesClass = env->FindClass("javax/swing/SwingUtilities"); + // static jmethodID methodId = env->GetMethodID(cls, "convertPointFromScreen", "(Ljava/awt/Point;Ljava/awt/Component)"); + + std::int32_t cx = 0; + std::int32_t cy = 0; + component->getLocationOnScreen(cx, cy); + + x -= cx; + y -= cy; + } +} \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Java/PointerInfo.hxx b/third-party/RemoteInput/RemoteInput/Java/PointerInfo.hxx new file mode 100644 index 0000000..72de8b5 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Java/PointerInfo.hxx @@ -0,0 +1,42 @@ +// +// PointerInfo.hxx +// RemoteInput +// +// Created by Brandon on 2020-03-16. +// Copyright © 2020 XIO. All rights reserved. +// + +#ifndef PointerInfo_HXX_INCLUDED +#define PointerInfo_HXX_INCLUDED + +#include "JNI_Common.hxx" +#include "Component.hxx" + +namespace java +{ + class PointerInfo + { + private: + JNIEnv* env; + jclass cls; + jobject pointerInfo; + + PointerInfo(JNIEnv* env, jclass cls, jobject pointerInfo) noexcept; + + public: + PointerInfo(PointerInfo&& other) noexcept; + PointerInfo(const PointerInfo& other) = delete; + ~PointerInfo() noexcept; + + PointerInfo& operator = (PointerInfo&& other) noexcept; + PointerInfo& operator = (const PointerInfo& other) = delete; + + static PointerInfo getPointerInfo(JNIEnv* env) noexcept; + + void getLocation(std::int32_t &x, std::int32_t &y) const noexcept; + + static void PointToScreen(JNIEnv* env, std::int32_t &x, std::int32_t &y, Component* component) noexcept; + }; +} + +#endif /* PointerInfo_HXX_INCLUDED */ diff --git a/third-party/RemoteInput/RemoteInput/Java/RIEventQueue.cxx b/third-party/RemoteInput/RemoteInput/Java/RIEventQueue.cxx new file mode 100644 index 0000000..735efa1 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Java/RIEventQueue.cxx @@ -0,0 +1,344 @@ +// +// Created by Brandon on 2021-10-17. +// + +#include "RIEventQueue.hxx" +#include "WindowEvent.hxx" +#include + +namespace java +{ + jobject RIEvent::Wrap(JNIEnv* env, jobject original) noexcept + { + jclass cls = env->FindClass("eos/EventInput/AWTEvent"); + if (cls) + { + jmethodID methodID = env->GetMethodID(cls, "", "(Ljava/awt/AWTEvent;)V"); + jobject ref = env->NewObject(cls, methodID, original); + env->DeleteLocalRef(cls); + + env->DeleteLocalRef(std::exchange(ref, env->NewGlobalRef(ref))); + return ref; + } + return nullptr; + } + + RIEvent::RIEvent(JNIEnv* env, AWTEvent* original) noexcept : AWTEvent(RIEvent::Wrap(env, original->get())), env(env) + { + } + + RIEvent::RIEvent(JNIEnv* env, jobject original) noexcept : AWTEvent(RIEvent::Wrap(env, original)), env(env) + { + } + + RIEvent::RIEvent(RIEvent&& other) noexcept + { + this->env = other.env; + this->self = other.self; + other.self = nullptr; + } + + RIEvent::~RIEvent() noexcept + { + if (self) + { + env->DeleteGlobalRef(self); + self = nullptr; + env = nullptr; + } + } + + RIEvent& RIEvent::operator = (RIEvent&& other) noexcept + { + if (self) + { + env->DeleteGlobalRef(self); + } + + this->env = other.env; + this->self = other.self; + other.self = nullptr; + return *this; + } + + void RIEventQueue::DefineClass() + { + // EventQueue + std::vector bytes = { + 0xCA, 0xFE, 0xBA, 0xBE, 0x0, 0x0, 0x0, 0x32, 0x0, 0x16, 0x1, 0x0, 0x19, 0x65, 0x6F, + 0x73, 0x2F, 0x45, 0x76, 0x65, 0x6E, 0x74, 0x49, 0x6E, 0x70, 0x75, 0x74, 0x2F, 0x45, + 0x76, 0x65, 0x6E, 0x74, 0x51, 0x75, 0x65, 0x75, 0x65, 0x7, 0x0, 0x1, 0x1, 0x0, 0x13, + 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x61, 0x77, 0x74, 0x2F, 0x45, 0x76, 0x65, 0x6E, 0x74, + 0x51, 0x75, 0x65, 0x75, 0x65, 0x7, 0x0, 0x3, 0x1, 0x0, 0xF, 0x45, 0x76, 0x65, 0x6E, + 0x74, 0x51, 0x75, 0x65, 0x75, 0x65, 0x2E, 0x6A, 0x61, 0x76, 0x61, 0x1, 0x0, 0xB, 0x6E, + 0x61, 0x74, 0x69, 0x76, 0x65, 0x51, 0x75, 0x65, 0x75, 0x65, 0x1, 0x0, 0x1, 0x4A, 0x1, + 0x0, 0x6, 0x3C, 0x69, 0x6E, 0x69, 0x74, 0x3E, 0x1, 0x0, 0x3, 0x28, 0x29, 0x56, 0xC, 0x0, + 0x8, 0x0, 0x9, 0xA, 0x0, 0x4, 0x0, 0xA, 0xC, 0x0, 0x6, 0x0, 0x7, 0x9, 0x0, 0x2, 0x0, 0xC, + 0x1, 0x0, 0x4, 0x74, 0x68, 0x69, 0x73, 0x1, 0x0, 0x1B, 0x4C, 0x65, 0x6F, 0x73, 0x2F, 0x45, + 0x76, 0x65, 0x6E, 0x74, 0x49, 0x6E, 0x70, 0x75, 0x74, 0x2F, 0x45, 0x76, 0x65, 0x6E, 0x74, + 0x51, 0x75, 0x65, 0x75, 0x65, 0x3B, 0x1, 0x0, 0xD, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, + 0x68, 0x45, 0x76, 0x65, 0x6E, 0x74, 0x1, 0x0, 0x16, 0x28, 0x4C, 0x6A, 0x61, 0x76, 0x61, 0x2F, + 0x61, 0x77, 0x74, 0x2F, 0x41, 0x57, 0x54, 0x45, 0x76, 0x65, 0x6E, 0x74, 0x3B, 0x29, 0x56, + 0x1, 0x0, 0x4, 0x43, 0x6F, 0x64, 0x65, 0x1, 0x0, 0x12, 0x4C, 0x6F, 0x63, 0x61, 0x6C, 0x56, + 0x61, 0x72, 0x69, 0x61, 0x62, 0x6C, 0x65, 0x54, 0x61, 0x62, 0x6C, 0x65, 0x1, 0x0, 0xF, 0x4C, + 0x69, 0x6E, 0x65, 0x4E, 0x75, 0x6D, 0x62, 0x65, 0x72, 0x54, 0x61, 0x62, 0x6C, 0x65, 0x1, 0x0, + 0xA, 0x53, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6C, 0x65, 0x0, 0x21, 0x0, 0x2, 0x0, + 0x4, 0x0, 0x0, 0x0, 0x1, 0x0, 0x2, 0x0, 0x6, 0x0, 0x7, 0x0, 0x0, 0x0, 0x2, 0x0, 0x1, 0x0, 0x8, + 0x0, 0x9, 0x0, 0x1, 0x0, 0x12, 0x0, 0x0, 0x0, 0x38, 0x0, 0x3, 0x0, 0x1, 0x0, 0x0, 0x0, 0xA, + 0x2A, 0xB7, 0x0, 0xB, 0x2A, 0x9, 0xB5, 0x0, 0xD, 0xB1, 0x0, 0x0, 0x0, 0x2, 0x0, 0x13, 0x0, 0x0, + 0x0, 0xC, 0x0, 0x1, 0x0, 0x0, 0x0, 0xA, 0x0, 0xE, 0x0, 0xF, 0x0, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, + 0xA, 0x0, 0x2, 0x0, 0x0, 0x0, 0x3, 0x0, 0x4, 0x0, 0x4, 0x1, 0x4, 0x0, 0x10, 0x0, 0x11, 0x0, + 0x0, 0x0, 0x1, 0x0, 0x15, 0x0, 0x0, 0x0, 0x2, 0x0, 0x5 + }; + + //ClassLoader systemClassLoader = sun.awt.AppContext.getAppContext().getContextClassLoader(); + jclass cls = env->DefineClass("eos/EventInput/EventQueue", + nullptr, + reinterpret_cast(&bytes[0]), + static_cast(bytes.size())); + + if (cls) + { + // Apparently Order of Calls matters in Java-1.6. + // Must call RegisterNatives AFTER creating an instance of the class + // OR initialize the class first, by calling GetMethodID. + // This will crash on Java-1.6 if not done in the correct order. + jmethodID methodID = env->GetMethodID(cls, "", "()V"); + this->queue = env->NewObject(cls, methodID); + env->DeleteLocalRef(std::exchange(this->queue, env->NewGlobalRef(this->queue))); + + if (this->queue) + { + jfieldID fieldID = env->GetFieldID(cls, "nativeQueue", "J"); + env->SetLongField(this->queue, fieldID, reinterpret_cast(this)); + } + else + { + if (env->ExceptionCheck()) + { + env->ExceptionDescribe(); + env->ExceptionClear(); + } + } + + JNINativeMethod method = { + const_cast("dispatchEvent"), + const_cast("(Ljava/awt/AWTEvent;)V"), + reinterpret_cast(&RIEventQueue::DispatchEvent) + }; + + env->RegisterNatives(cls, &method, 1); + env->DeleteLocalRef(cls); + } + else + { + if (env->ExceptionCheck()) + { + env->ExceptionDescribe(); + env->ExceptionClear(); + } + } + + // AWTEvent + bytes = { + 0xCA, 0xFE, 0xBA, 0xBE, 0x0, 0x0, 0x0, 0x32, 0x0, 0x1E, 0x1, 0x0, 0x17, 0x65, 0x6F, 0x73, 0x2F, + 0x45, 0x76, 0x65, 0x6E, 0x74, 0x49, 0x6E, 0x70, 0x75, 0x74, 0x2F, 0x41, 0x57, 0x54, 0x45, 0x76, + 0x65, 0x6E, 0x74, 0x7, 0x0, 0x1, 0x1, 0x0, 0x11, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x61, 0x77, 0x74, + 0x2F, 0x41, 0x57, 0x54, 0x45, 0x76, 0x65, 0x6E, 0x74, 0x7, 0x0, 0x3, 0x1, 0x0, 0xD, 0x41, 0x57, + 0x54, 0x45, 0x76, 0x65, 0x6E, 0x74, 0x2E, 0x6A, 0x61, 0x76, 0x61, 0x1, 0x0, 0x8, 0x6F, 0x72, 0x69, + 0x67, 0x69, 0x6E, 0x61, 0x6C, 0x1, 0x0, 0x13, 0x4C, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x61, 0x77, 0x74, + 0x2F, 0x41, 0x57, 0x54, 0x45, 0x76, 0x65, 0x6E, 0x74, 0x3B, 0x1, 0x0, 0x6, 0x3C, 0x69, 0x6E, 0x69, + 0x74, 0x3E, 0x1, 0x0, 0x16, 0x28, 0x4C, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x61, 0x77, 0x74, 0x2F, 0x41, + 0x57, 0x54, 0x45, 0x76, 0x65, 0x6E, 0x74, 0x3B, 0x29, 0x56, 0x1, 0x0, 0x9, 0x67, 0x65, 0x74, 0x53, + 0x6F, 0x75, 0x72, 0x63, 0x65, 0x1, 0x0, 0x14, 0x28, 0x29, 0x4C, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x6C, + 0x61, 0x6E, 0x67, 0x2F, 0x4F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x3B, 0xC, 0x0, 0xA, 0x0, 0xB, 0xA, 0x0, + 0x4, 0x0, 0xC, 0x1, 0x0, 0x5, 0x67, 0x65, 0x74, 0x49, 0x44, 0x1, 0x0, 0x3, 0x28, 0x29, 0x49, 0xC, 0x0, + 0xE, 0x0, 0xF, 0xA, 0x0, 0x4, 0x0, 0x10, 0x1, 0x0, 0x16, 0x28, 0x4C, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x6C, + 0x61, 0x6E, 0x67, 0x2F, 0x4F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x3B, 0x49, 0x29, 0x56, 0xC, 0x0, 0x8, 0x0, + 0x12, 0xA, 0x0, 0x4, 0x0, 0x13, 0xC, 0x0, 0x6, 0x0, 0x7, 0x9, 0x0, 0x2, 0x0, 0x15, 0x1, 0x0, 0x4, 0x74, + 0x68, 0x69, 0x73, 0x1, 0x0, 0x19, 0x4C, 0x65, 0x6F, 0x73, 0x2F, 0x45, 0x76, 0x65, 0x6E, 0x74, 0x49, 0x6E, + 0x70, 0x75, 0x74, 0x2F, 0x41, 0x57, 0x54, 0x45, 0x76, 0x65, 0x6E, 0x74, 0x3B, 0x1, 0x0, 0x1, 0x65, 0x1, + 0x0, 0x4, 0x43, 0x6F, 0x64, 0x65, 0x1, 0x0, 0x12, 0x4C, 0x6F, 0x63, 0x61, 0x6C, 0x56, 0x61, 0x72, 0x69, + 0x61, 0x62, 0x6C, 0x65, 0x54, 0x61, 0x62, 0x6C, 0x65, 0x1, 0x0, 0xF, 0x4C, 0x69, 0x6E, 0x65, 0x4E, 0x75, + 0x6D, 0x62, 0x65, 0x72, 0x54, 0x61, 0x62, 0x6C, 0x65, 0x1, 0x0, 0xA, 0x53, 0x6F, 0x75, 0x72, 0x63, 0x65, + 0x46, 0x69, 0x6C, 0x65, 0x0, 0x21, 0x0, 0x2, 0x0, 0x4, 0x0, 0x0, 0x0, 0x1, 0x0, 0x2, 0x0, 0x6, 0x0, 0x7, + 0x0, 0x0, 0x0, 0x1, 0x0, 0x1, 0x0, 0x8, 0x0, 0x9, 0x0, 0x1, 0x0, 0x1A, 0x0, 0x0, 0x0, 0x4E, 0x0, 0x3, 0x0, + 0x2, 0x0, 0x0, 0x0, 0x12, 0x2A, 0x2B, 0xB6, 0x0, 0xD, 0x2B, 0xB6, 0x0, 0x11, 0xB7, 0x0, 0x14, 0x2A, 0x2B, + 0xB5, 0x0, 0x16, 0xB1, 0x0, 0x0, 0x0, 0x2, 0x0, 0x1B, 0x0, 0x0, 0x0, 0x16, 0x0, 0x2, 0x0, 0x0, 0x0, 0x12, + 0x0, 0x17, 0x0, 0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x12, 0x0, 0x19, 0x0, 0x7, 0x0, 0x1, 0x0, 0x1C, 0x0, 0x0, + 0x0, 0xE, 0x0, 0x3, 0x0, 0x0, 0x0, 0x5, 0x0, 0xC, 0x0, 0x6, 0x0, 0x11, 0x0, 0x7, 0x0, 0x1, 0x0, 0x1D, 0x0, + 0x0, 0x0, 0x2, 0x0, 0x5 + }; + + cls = env->DefineClass("eos/EventInput/AWTEvent", + nullptr, + reinterpret_cast(&bytes[0]), + static_cast(bytes.size())); + env->DeleteLocalRef(cls); + + if (env->ExceptionCheck()) + { + env->ExceptionDescribe(); + env->ExceptionClear(); + } + } + + void RIEventQueue::DispatchEvent(JNIEnv* env, jobject thiz, jobject event) + { + jclass cls = env->GetObjectClass(thiz); + if (cls) + { + // super.dispatchEvent + auto dispatch_event = [](JNIEnv* env, jclass cls, jobject thiz, jobject event) { + jclass super_class = env->GetSuperclass(cls); + + // super.dispatchEvent + if (super_class) + { + jmethodID methodID = env->GetMethodID(super_class, "dispatchEvent", "(Ljava/awt/AWTEvent;)V"); + env->CallNonvirtualVoidMethod(thiz, super_class, methodID, event); + env->DeleteLocalRef(super_class); + } + }; + + // If the class is an instance of our event type, pass it on + jclass event_class = env->FindClass("eos/EventInput/AWTEvent"); + if (event_class) + { + if (env->IsInstanceOf(event, event_class)) + { + jfieldID fieldID = env->GetFieldID(event_class, "original", "Ljava/awt/AWTEvent;"); + jobject original = env->GetObjectField(event, fieldID); + if (original) + { + // super.dispatchEvent(original) + dispatch_event(env, cls, thiz, original); + env->DeleteLocalRef(original); + env->DeleteLocalRef(cls); + } + env->DeleteLocalRef(event_class); + return; + } + env->DeleteLocalRef(event_class); + } + + jfieldID fieldID = env->GetFieldID(cls, "nativeQueue", "J"); + RIEventQueue* this_ = reinterpret_cast(env->GetLongField(thiz, fieldID)); + + if (this_ && (this_->is_blocking_keyboard_events || this_->is_blocking_mouse_events)) + { + // Block events on the canvas + jobject source = AWTEvent(event).getSource(env); + if (source) + { + jclass canvas_class = env->FindClass("java/awt/Canvas"); + if (canvas_class) + { + if (env->IsInstanceOf(source, canvas_class)) + { + // Check what kind of event it is.. + jclass key_event_class = env->FindClass("java/awt/event/KeyEvent"); + jclass mouse_event_class = env->FindClass("java/awt/event/MouseEvent"); + jclass focus_event_class = env->FindClass("java/awt/event/FocusEvent"); + + if (key_event_class && mouse_event_class && focus_event_class) + { + // BLOCKED EVENT + if ((this_->is_blocking_keyboard_events && env->IsInstanceOf(event, key_event_class)) || + (this_->is_blocking_mouse_events && env->IsInstanceOf(event, mouse_event_class)) || + env->IsInstanceOf(event, focus_event_class)) + { + env->DeleteLocalRef(focus_event_class); + env->DeleteLocalRef(mouse_event_class); + env->DeleteLocalRef(key_event_class); + + env->DeleteLocalRef(canvas_class); + env->DeleteLocalRef(source); + env->DeleteLocalRef(cls); + return; + } + } + + if (focus_event_class) + { + env->DeleteLocalRef(focus_event_class); + } + + if (mouse_event_class) + { + env->DeleteLocalRef(mouse_event_class); + } + + if (key_event_class) + { + env->DeleteLocalRef(key_event_class); + } + } + env->DeleteLocalRef(canvas_class); + } + env->DeleteLocalRef(source); + } + + // Block events on the window/frame + jclass window_class = env->FindClass("java/awt/event/WindowEvent"); + if (window_class) + { + if (env->IsInstanceOf(event, window_class)) + { + jclass event_class = env->GetObjectClass(event); + if (event_class) + { + jmethodID getIDMethodID = env->GetMethodID(event_class, "getID", "()I"); + jint event_id = env->CallIntMethod(event, getIDMethodID); + + if (event_id == WindowEvent::WindowEventCodes::WINDOW_ICONIFIED || + event_id == WindowEvent::WindowEventCodes::WINDOW_DEICONIFIED || + event_id == WindowEvent::WindowEventCodes::WINDOW_ACTIVATED || + event_id == WindowEvent::WindowEventCodes::WINDOW_DEACTIVATED || + event_id == WindowEvent::WindowEventCodes::WINDOW_GAINED_FOCUS || + event_id == WindowEvent::WindowEventCodes::WINDOW_LOST_FOCUS || + event_id == WindowEvent::WindowEventCodes::WINDOW_STATE_CHANGED) + { + env->DeleteLocalRef(event_class); + env->DeleteLocalRef(window_class); + env->DeleteLocalRef(cls); + return; + } + env->DeleteLocalRef(event_class); + } + } + env->DeleteLocalRef(window_class); + } + } + + // super.dispatchEvent(event) + dispatch_event(env, cls, thiz, event); + env->DeleteLocalRef(cls); + } + } + + RIEventQueue::RIEventQueue(JNIEnv* env) noexcept : EventQueue(env), is_blocking_keyboard_events(false), is_blocking_mouse_events(false) + { + this->DefineClass(); + } + + bool RIEventQueue::is_keyboard_input_enabled() const noexcept + { + return !is_blocking_keyboard_events; + } + + void RIEventQueue::set_keyboard_input_enabled(bool enabled) noexcept + { + is_blocking_keyboard_events = !enabled; + } + + bool RIEventQueue::is_mouse_input_enabled() const noexcept + { + return !is_blocking_mouse_events; + } + + void RIEventQueue::set_mouse_input_enabled(bool enabled) noexcept + { + is_blocking_mouse_events = !enabled; + } +} \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Java/RIEventQueue.hxx b/third-party/RemoteInput/RemoteInput/Java/RIEventQueue.hxx new file mode 100644 index 0000000..8165dea --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Java/RIEventQueue.hxx @@ -0,0 +1,49 @@ +// +// Created by Brandon on 2021-10-17. +// + +#ifndef RIEVENTQUEUE_HXX +#define RIEVENTQUEUE_HXX + +#include "JNI_Common.hxx" +#include "EventQueue.hxx" +#include "AWTEvent.hxx" +#include + +namespace java +{ + class RIEvent final: public AWTEvent + { + private: + JNIEnv* env; + static jobject Wrap(JNIEnv* env, jobject original) noexcept; + public: + RIEvent(JNIEnv* env, AWTEvent* original) noexcept; + RIEvent(JNIEnv* env, jobject original) noexcept; + RIEvent(RIEvent&& other) noexcept; + virtual ~RIEvent() noexcept final; + + RIEvent& operator = (RIEvent&& other) noexcept; + }; + + class RIEventQueue: public EventQueue + { + private: + bool is_blocking_keyboard_events; + bool is_blocking_mouse_events; + + void DefineClass(); + static void DispatchEvent(JNIEnv* env, jobject thiz, jobject event); + + public: + RIEventQueue(JNIEnv* env) noexcept; + + bool is_keyboard_input_enabled() const noexcept; + void set_keyboard_input_enabled(bool enabled) noexcept; + + bool is_mouse_input_enabled() const noexcept; + void set_mouse_input_enabled(bool enabled) noexcept; + }; +} + +#endif /* RIEVENTQUEUE_HXX */ diff --git a/third-party/RemoteInput/RemoteInput/Java/SunToolkit.cxx b/third-party/RemoteInput/RemoteInput/Java/SunToolkit.cxx new file mode 100644 index 0000000..337e3f6 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Java/SunToolkit.cxx @@ -0,0 +1,42 @@ +// +// Created by Brandon on 2021-10-21. +// + +#include "SunToolkit.hxx" +#include + +namespace java +{ + Window SunToolkit::getContainingWindow(Component* component) + { + JNIEnv* env = component->getEnv(); + jclass cls = env->FindClass("sun/awt/SunToolkit"); + if (cls) + { + static jmethodID methodId = env->GetStaticMethodID(cls, "getContainingWindow", "(Ljava/awt/Component;)Ljava/awt/Window;"); + if (methodId) + { + jobject containing_window = env->CallStaticObjectMethod(cls, methodId, component->get()); + env->DeleteLocalRef(cls); + env->DeleteLocalRef(std::exchange(containing_window, env->NewGlobalRef(containing_window))); + return {env, containing_window}; + } + env->DeleteLocalRef(cls); + } + + cls = env->FindClass("java/awt/Window"); + if (cls) + { + jobject comp = env->NewGlobalRef(component->get()); + while(comp && !env->IsInstanceOf(comp, cls)) + { + comp = env->NewGlobalRef(Component(env, comp).getParent().get()); + } + + env->DeleteLocalRef(cls); + return {env, comp}; + } + + return {env, nullptr}; + } +} \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Java/SunToolkit.hxx b/third-party/RemoteInput/RemoteInput/Java/SunToolkit.hxx new file mode 100644 index 0000000..d25dc27 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Java/SunToolkit.hxx @@ -0,0 +1,20 @@ +// +// Created by Brandon on 2021-10-21. +// + +#ifndef REMOTEINPUT_SUNTOOLKIT_HXX +#define REMOTEINPUT_SUNTOOLKIT_HXX + +#include "Toolkit.hxx" +#include "Window.hxx" + +namespace java +{ + class SunToolkit : public Toolkit + { + public: + static Window getContainingWindow(Component* component); + }; +} + +#endif //REMOTEINPUT_SUNTOOLKIT_HXX diff --git a/third-party/RemoteInput/RemoteInput/Java/Toolkit.cxx b/third-party/RemoteInput/RemoteInput/Java/Toolkit.cxx new file mode 100644 index 0000000..dc411c7 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Java/Toolkit.cxx @@ -0,0 +1,70 @@ +// +// Toolkit.cxx +// RemoteInput +// +// Created by Brandon on 2020-01-09. +// Copyright © 2020 XIO. All rights reserved. +// + +#include "Toolkit.hxx" +#include + +namespace java +{ + Toolkit::Toolkit(JNIEnv* env, jclass cls, jobject toolkit) noexcept : env(env), cls(cls), toolkit(toolkit) + { + if (!this->cls) + { + this->cls = env->GetObjectClass(toolkit); + } + + env->DeleteLocalRef(std::exchange(this->cls, static_cast(env->NewGlobalRef(this->cls)))); + env->DeleteLocalRef(std::exchange(this->toolkit, env->NewGlobalRef(this->toolkit))); + } + + Toolkit::~Toolkit() noexcept + { + if (cls) + { + env->DeleteGlobalRef(cls); + } + + if (toolkit) + { + env->DeleteGlobalRef(toolkit); + } + } + + Toolkit::Toolkit(Toolkit&& other) noexcept : env(other.env), cls(other.cls), toolkit(other.toolkit) + { + other.env = nullptr; + other.cls = nullptr; + other.toolkit = nullptr; + } + + Toolkit& Toolkit::operator = (Toolkit&& other) noexcept + { + this->env = other.env; + this->cls = other.cls; + this->toolkit = other.toolkit; + other.env = nullptr; + other.cls = nullptr; + other.toolkit = nullptr; + return *this; + } + + Toolkit Toolkit::getDefaultToolkit(JNIEnv* env) noexcept + { + jclass cls = env->FindClass("java/awt/Toolkit"); + static jmethodID methodId = env->GetStaticMethodID(cls, "getDefaultToolkit", "()Ljava/awt/Toolkit;"); + jobject toolkit = env->CallStaticObjectMethod(cls, methodId); + return Toolkit{env, cls, toolkit}; + } + + EventQueue Toolkit::getSystemEventQueue() const noexcept + { + static jmethodID methodId = env->GetMethodID(cls, "getSystemEventQueue", "()Ljava/awt/EventQueue;"); + jobject queue = env->CallObjectMethod(toolkit, methodId); + return EventQueue(env, env->FindClass("java/awt/EventQueue"), queue); + } +} \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Java/Toolkit.hxx b/third-party/RemoteInput/RemoteInput/Java/Toolkit.hxx new file mode 100644 index 0000000..359f0ab --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Java/Toolkit.hxx @@ -0,0 +1,41 @@ +// +// Toolkit.hxx +// RemoteInput +// +// Created by Brandon on 2020-01-09. +// Copyright © 2020 XIO. All rights reserved. +// + +#ifndef Toolkit_HXX_INCLUDED +#define Toolkit_HXX_INCLUDED + +#include "JNI_Common.hxx" +#include "EventQueue.hxx" + +namespace java +{ + class Toolkit + { + private: + JNIEnv* env; + jclass cls; + jobject toolkit; + + protected: + Toolkit(JNIEnv* env, jclass cls, jobject toolkit) noexcept; + + public: + Toolkit(Toolkit&& other) noexcept; + Toolkit(const Toolkit& other) = delete; + virtual ~Toolkit() noexcept; + + Toolkit& operator = (Toolkit&& other) noexcept; + Toolkit& operator = (const Toolkit& other) = delete; + + static Toolkit getDefaultToolkit(JNIEnv* env) noexcept; + + EventQueue getSystemEventQueue() const noexcept; + }; +} + +#endif /* Toolkit_HXX_INCLUDED */ diff --git a/third-party/RemoteInput/RemoteInput/Java/Window.cxx b/third-party/RemoteInput/RemoteInput/Java/Window.cxx new file mode 100644 index 0000000..705d8af --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Java/Window.cxx @@ -0,0 +1,25 @@ +// +// Created by Brandon on 2021-10-21. +// + +#include "Window.hxx" +#include + +namespace java +{ + Window::Window(JNIEnv* env, jobject window, bool canDelete) noexcept : Container(env, nullptr, window, canDelete) + { + this->cls = window ? env->GetObjectClass(window) : env->FindClass("java/awt/Window"); + env->DeleteLocalRef(std::exchange(this->cls, static_cast(env->NewGlobalRef(this->cls)))); + } + + Window::Window(JNIEnv* env, jclass cls, jobject window, bool canDelete) noexcept : Container(env, cls, window, canDelete) + { + } + + void Window::pack() const noexcept + { + static jmethodID methodId = env->GetMethodID(cls, "pack", "()V"); + env->CallVoidMethod(component, methodId); + } +} \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Java/Window.hxx b/third-party/RemoteInput/RemoteInput/Java/Window.hxx new file mode 100644 index 0000000..221d4e8 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Java/Window.hxx @@ -0,0 +1,23 @@ +// +// Created by Brandon on 2021-10-21. +// + +#ifndef REMOTEINPUT_WINDOW_HXX +#define REMOTEINPUT_WINDOW_HXX + + +#include "JNI_Common.hxx" +#include "Container.hxx" + +namespace java +{ + class Window : public Container + { + public: + Window(JNIEnv* env, jobject window, bool canDelete = true) noexcept; + Window(JNIEnv* env, jclass cls, jobject window, bool canDelete = true) noexcept; + void pack() const noexcept; + }; +} + +#endif //REMOTEINPUT_WINDOW_HXX diff --git a/third-party/RemoteInput/RemoteInput/Java/WindowEvent.cxx b/third-party/RemoteInput/RemoteInput/Java/WindowEvent.cxx new file mode 100644 index 0000000..76b84a9 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Java/WindowEvent.cxx @@ -0,0 +1,60 @@ +// +// Created by Brandon on 2021-10-21. +// + +#include "WindowEvent.hxx" +#include + +namespace java +{ + WindowEvent::WindowEvent(JNIEnv* env, Component* receiver, std::int32_t id, std::int32_t old_state, std::int32_t new_state) noexcept : AWTEvent(), env(env), cls(nullptr) + { + this->cls = env->FindClass("java/awt/event/WindowEvent"); + env->DeleteLocalRef(std::exchange(this->cls, static_cast(env->NewGlobalRef(this->cls)))); + + static jmethodID methodId = env->GetMethodID(cls, "", "(Ljava/awt/Component;III)V"); + self = env->NewObject(cls, methodId, receiver->get(), id, old_state, new_state); + env->DeleteLocalRef(std::exchange(self, static_cast(env->NewGlobalRef(self)))); + } + + WindowEvent::~WindowEvent() noexcept + { + env->DeleteGlobalRef(cls); + env->DeleteGlobalRef(self); + } + + void WindowEvent::Dispatch(JNIEnv* env, Component* receiver, std::int32_t id, std::int32_t old_state, std::int32_t new_state, bool is_system_generated) noexcept + { + jclass cls = env->FindClass("java/awt/event/WindowEvent"); + if (cls) + { + static jmethodID methodId = env->GetMethodID(cls, "", "(Ljava/awt/Window;III)V"); + jobject event = env->NewObject(cls, methodId, receiver->get(), id, old_state, new_state); + + if (event) + { + AWTEvent::SetSystemGenerated(env, event, is_system_generated); + receiver->dispatchEvent(event); + env->DeleteLocalRef(event); + } + + env->DeleteLocalRef(cls); + } + } + + void WindowEvent::Post(JNIEnv* env, Component* receiver, std::int32_t id, std::int32_t old_state, std::int32_t new_state, bool is_system_generated) noexcept + { + jclass cls = env->FindClass("java/awt/event/WindowEvent"); + if (cls) + { + static jmethodID methodId = env->GetMethodID(cls, "", "(Ljava/awt/Window;III)V"); + jobject event = env->NewObject(cls, methodId, receiver->get(), id, old_state, new_state); + if (event) + { + AWTEvent::Post(env, event, is_system_generated); + env->DeleteLocalRef(event); + } + env->DeleteLocalRef(cls); + } + } +} \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Java/WindowEvent.hxx b/third-party/RemoteInput/RemoteInput/Java/WindowEvent.hxx new file mode 100644 index 0000000..d77eff1 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Java/WindowEvent.hxx @@ -0,0 +1,40 @@ +// +// Created by Brandon on 2021-10-21. +// + +#ifndef REMOTEINPUT_WINDOWEVENT_HXX +#define REMOTEINPUT_WINDOWEVENT_HXX + +#include "JNI_Common.hxx" +#include "AWTEvent.hxx" +#include "Component.hxx" + +namespace java +{ + class WindowEvent final : public AWTEvent + { + private: + JNIEnv* env; + jclass cls; + + public: + WindowEvent(JNIEnv* env, Component* receiver, std::int32_t id, std::int32_t old_state, std::int32_t new_state) noexcept; + virtual ~WindowEvent() noexcept; + + static void Dispatch(JNIEnv* env, Component* receiver, std::int32_t id, std::int32_t old_state, std::int32_t new_state, bool is_system_generated = false) noexcept; + static void Post(JNIEnv* env, Component* receiver, std::int32_t id, std::int32_t old_state, std::int32_t new_state, bool is_system_generated = false) noexcept; + + enum WindowEventCodes : std::int32_t + { + WINDOW_ICONIFIED = 203, + WINDOW_DEICONIFIED = 204, + WINDOW_ACTIVATED = 205, + WINDOW_DEACTIVATED = 206, + WINDOW_GAINED_FOCUS = 207, + WINDOW_LOST_FOCUS = 208, + WINDOW_STATE_CHANGED = 209 + }; + }; +} + +#endif //REMOTEINPUT_WINDOWEVENT_HXX diff --git a/third-party/RemoteInput/RemoteInput/Platform/DebugConsole.cxx b/third-party/RemoteInput/RemoteInput/Platform/DebugConsole.cxx new file mode 100644 index 0000000..3b20c2f --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Platform/DebugConsole.cxx @@ -0,0 +1,58 @@ +#include "DebugConsole.hxx" +#include + +DebugConsole::DebugConsole() noexcept : input(nullptr), error(nullptr), output(nullptr), allocated_console(false) +{ + #if defined(_WIN32) || defined(_WIN64) + allocated_console = AllocConsole(); + #if defined(__STDC_LIB_EXT1__) && __STDC_WANT_LIB_EXT1__ == 1 + freopen_s(&input, "CONIN$", "r", stdin); + freopen_s(&error, "CONOUT$", "w", stderr); + freopen_s(&output, "CONOUT$", "w", stdout); + #else + input = std::freopen("CONIN$", "r", stdin); + error = std::freopen("CONOUT$", "w", stderr); + output = std::freopen("CONOUT$", "w", stdout); + #endif + #else + + #endif +} + +DebugConsole::DebugConsole(DebugConsole&& other) : input(other.input), error(other.error), output(other.output), allocated_console(other.allocated_console) +{ + other.input = nullptr; + other.error = nullptr; + other.output = nullptr; + other.allocated_console = false; +} + +DebugConsole::~DebugConsole() noexcept +{ + #if defined(_WIN32) || defined(_WIN64) + if (allocated_console) + { + FreeConsole(); + } + #endif + + std::fclose(input); + std::fclose(error); + std::fclose(output); +} + +DebugConsole& DebugConsole::operator=(DebugConsole &&other) +{ + std::fclose(input); + std::fclose(error); + std::fclose(output); + input = other.input; + error = other.error; + output = other.output; + allocated_console = other.allocated_console; + other.input = nullptr; + other.error = nullptr; + other.output = nullptr; + other.allocated_console = false; + return *this; +} \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Platform/DebugConsole.hxx b/third-party/RemoteInput/RemoteInput/Platform/DebugConsole.hxx new file mode 100644 index 0000000..6be63e1 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Platform/DebugConsole.hxx @@ -0,0 +1,28 @@ +#ifndef DEBUGCONSOLE_HXX_INCLUDED +#define DEBUGCONSOLE_HXX_INCLUDED + +#if defined(_WIN32) || defined(_WIN64) +#include +#endif + +#include + +class DebugConsole final +{ +private: + FILE* input; + FILE* error; + FILE* output; + bool allocated_console; + +public: + DebugConsole() noexcept; + DebugConsole(DebugConsole&& other); + DebugConsole(const DebugConsole& other) = delete; + ~DebugConsole() noexcept; + + DebugConsole& operator = (const DebugConsole& other) = delete; + DebugConsole& operator = (DebugConsole&& other); +}; + +#endif // DEBUGCONSOLE_HXX_INCLUDED diff --git a/third-party/RemoteInput/RemoteInput/Platform/JavaInternal.hxx b/third-party/RemoteInput/RemoteInput/Platform/JavaInternal.hxx new file mode 100644 index 0000000..f9b6abf --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Platform/JavaInternal.hxx @@ -0,0 +1,376 @@ +#ifndef JAVAINTERNAL_HXX_INCLUDED +#define JAVAINTERNAL_HXX_INCLUDED + +/* + * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * LICENSED UNDER GPL2. + */ + +#include "JNI_Common.hxx" + +typedef struct { + jint x1; + jint y1; + jint x2; + jint y2; +} SurfaceDataBounds; + +typedef struct { + SurfaceDataBounds bounds; + void *rasBase; + jint pixelBitOffset; + jint pixelStride; + jint scanStride; + unsigned int lutSize; + jint *lutBase; + unsigned char *invColorTable; + char *redErrTable; + char *grnErrTable; + char *bluErrTable; + int *invGrayTable; + union { + void *align; + char data[64]; + } priv; +} SurfaceDataRasInfo; + +typedef struct _SurfaceDataOps SurfaceDataOps; + +typedef jint LockFunc(JNIEnv *env, + SurfaceDataOps *ops, + SurfaceDataRasInfo *rasInfo, + jint lockflags); + +typedef void GetRasInfoFunc(JNIEnv *env, + SurfaceDataOps *ops, + SurfaceDataRasInfo *pRasInfo); + +typedef void ReleaseFunc(JNIEnv *env, + SurfaceDataOps *ops, + SurfaceDataRasInfo *pRasInfo); + +typedef void UnlockFunc(JNIEnv *env, + SurfaceDataOps *ops, + SurfaceDataRasInfo *pRasInfo); + +typedef void SetupFunc(JNIEnv *env, + SurfaceDataOps *ops); + +typedef void DisposeFunc(JNIEnv *env, + SurfaceDataOps *ops); + +#define SD_FAILURE -1 +#define SD_SUCCESS 0 +#define SD_SLOWLOCK 1 +#define SD_LOCK_READ (1 << 0) +#define SD_LOCK_WRITE (1 << 1) +#define SD_LOCK_RD_WR (SD_LOCK_READ | SD_LOCK_WRITE) +#define SD_LOCK_LUT (1 << 2) +#define SD_LOCK_INVCOLOR (1 << 3) +#define SD_LOCK_INVGRAY (1 << 4) +#define SD_LOCK_FASTEST (1 << 5) +#define SD_LOCK_PARTIAL (1 << 6) +#define SD_LOCK_PARTIAL_WRITE (SD_LOCK_WRITE | SD_LOCK_PARTIAL) +#define SD_LOCK_NEED_PIXELS (SD_LOCK_READ | SD_LOCK_PARTIAL) + + +struct _SurfaceDataOps { + LockFunc *Lock; + GetRasInfoFunc *GetRasInfo; + ReleaseFunc *Release; + UnlockFunc *Unlock; + SetupFunc *Setup; + DisposeFunc *Dispose; + jobject sdObject; +}; + + + +#if defined(_WIN32) || defined(_WIN64) +typedef struct _GDIWinSDOps GDIWinSDOps; + +typedef HDC GetDCFunc(JNIEnv *env, + GDIWinSDOps *wsdo, + jint flags, + jint *patrop, + jobject clip, + jobject comp, + jint color); + +typedef void ReleaseDCFunc(JNIEnv *env, + GDIWinSDOps *wsdo, + HDC hdc); + + +typedef void InvalidateSDFunc(JNIEnv *env, + GDIWinSDOps *wsdo); + +struct _GDIWinSDOps { + SurfaceDataOps sdOps; + LONG timeStamp; + jboolean invalid; + GetDCFunc *GetDC; + ReleaseDCFunc *ReleaseDC; + InvalidateSDFunc *InvalidateSD; + jint lockType; + jint lockFlags; + jobject peer; + HWND window; + RECT insets; + jint depth; + jint pixelStride; + DWORD pixelMasks[3]; + HBITMAP bitmap; + HBITMAP oldmap; + HDC bmdc; + int bmScanStride; + int bmWidth; + int bmHeight; + void *bmBuffer; + jboolean bmCopyToScreen; + void *brush; + jint brushclr; + void *pen; + jint penclr; + int x, y, w, h; + void *surfaceLock; + void *device; +}; + +typedef struct tagBitmapHeader { + BITMAPINFOHEADER bmiHeader; + union { + DWORD dwMasks[3]; + RGBQUAD palette[256]; + } colors; +} BmiType; + + +#include + +class IManagedResource { +public: + virtual BOOL IsDefaultPool() = 0; +protected: + IManagedResource() { pPrev = pNext = NULL; }; + virtual ~IManagedResource() { pPrev = pNext = NULL; }; + virtual void Release() = 0; +private: + IManagedResource& operator=(const IManagedResource&); + + IManagedResource* pPrev; + IManagedResource* pNext; +}; + +typedef struct _D3DSDOps D3DSDOps; + +class D3DResource; +struct _D3DSDOps { + SurfaceDataOps sdOps; + jint adapter; + jint width, height; + jint xoff, yoff; + D3DSWAPEFFECT swapEffect; + D3DResource *pResource; +}; + +class D3DResource : public IManagedResource { +public: + D3DResource(IDirect3DResource9 *pRes); + D3DResource(IDirect3DSwapChain9 *pSC); + IDirect3DResource9* GetResource(); + IDirect3DTexture9* GetTexture(); + IDirect3DSurface9* GetSurface(); + IDirect3DSwapChain9* GetSwapChain(); + D3DSDOps* GetSDOps(); + void SetSDOps(D3DSDOps *pOps); + D3DSURFACE_DESC* GetDesc(); + virtual BOOL IsDefaultPool(); + +protected: + virtual ~D3DResource(); + virtual void Release(); + void Init(IDirect3DResource9*, IDirect3DSwapChain9*); + +private: + D3DResource() {} + D3DResource& operator=(const D3DResource&); + + IDirect3DResource9* pResource; + IDirect3DSwapChain9* pSwapChain; + IDirect3DSurface9* pSurface; + IDirect3DTexture9* pTexture; + D3DSDOps* pOps; + D3DSURFACE_DESC desc; +}; +#endif + +typedef unsigned int juint; + +/* +typedef struct _OGLSDOps OGLSDOps; +typedef unsigned char jubyte; + +typedef struct { + GLenum src; + GLenum dst; +} OGLBlendRule; + +typedef struct { + GLenum format; + GLenum type; + jint alignment; + jboolean hasAlpha; + jboolean isPremult; +} OGLPixelFormat; + +OGLPixelFormat PixelFormats[] = { + {GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, 4, 1, 0}, // 0 - IntArgb + { GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, 4, 1, 1}, // 1 - IntArgbPre + { GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, 4, 0, 1}, // 2 - IntRgb + { GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, 4, 0, 1}, // 3 - IntRgbx + { GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, 4, 0, 1}, // 4 - IntBgr + { GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, 4, 0, 1}, // 5 - IntBgrx + { GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 2, 0, 1}, // 6 - Ushort565Rgb + { GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, 2, 0, 1}, // 7 - Ushort555Rgb + { GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, 2, 0, 1}, // 8 - Ushort555Rgbx + { GL_LUMINANCE, GL_UNSIGNED_BYTE, 1, 0, 1}, // 9 - ByteGray + { GL_LUMINANCE, GL_UNSIGNED_SHORT, 2, 0, 1}, // 10 - UshortGray + { GL_BGR, GL_UNSIGNED_BYTE, 1, 0, 1}, // 11 - ThreeByteBgr +}; + +typedef struct { + void *ctxInfo; + jint caps; + jint compState; + jfloat extraAlpha; + jint xorPixel; + jint pixel; + jubyte r; + jubyte g; + jubyte b; + jubyte a; + jint paintState; + jboolean useMask; + GLdouble *xformMatrix; + GLuint blitTextureID; + GLint textureFunction; + jboolean vertexCacheEnabled; +} OGLContext; + +struct _OGLSDOps { + SurfaceDataOps sdOps; + void *privOps; + jint drawableType; + GLenum activeBuffer; + jboolean isOpaque; + jboolean needsInit; + jint xOffset; + jint yOffset; + jint width; + jint height; + GLuint textureID; + jint textureWidth; + jint textureHeight; + GLenum textureTarget; + GLint textureFilter; + GLuint fbobjectID; + GLuint depthID; +};*/ + +typedef struct _CompositeInfo { + jint rule; + union { + jfloat extraAlpha; + jint xorPixel; + } details; + juint alphaMask; +} CompositeInfo; + +typedef void (JNICALL CompInfoFunc)(JNIEnv *env, + CompositeInfo *pCompInfo, + jobject Composite); + +typedef struct _SurfCompHdr { + char *Name; + jobject Object; +} SurfCompHdr; + +typedef struct _CompositeType { + SurfCompHdr hdr; + CompInfoFunc *getCompInfo; + jint dstflags; +} CompositeType; + +struct _NativePrimitive; + +typedef void (AnyFunc)(); + +typedef void (BlitFunc)(void *pSrc, void *pDst, + juint width, juint height, + SurfaceDataRasInfo *pSrcInfo, + SurfaceDataRasInfo *pDstInfo, + struct _NativePrimitive *pPrim, + CompositeInfo *pCompInfo); + +typedef void (BlitBgFunc)(void *pSrc, void *pDst, + juint width, juint height, jint bgpixel, + SurfaceDataRasInfo *pSrcInfo, + SurfaceDataRasInfo *pDstInfo, + struct _NativePrimitive *pPrim, + CompositeInfo *pCompInfo); + +typedef void (ScaleBlitFunc)(void *pSrc, void *pDst, + juint dstwidth, juint dstheight, + jint sxloc, jint syloc, + jint sxinc, jint syinc, jint scale, + SurfaceDataRasInfo *pSrcInfo, + SurfaceDataRasInfo *pDstInfo, + struct _NativePrimitive *pPrim, + CompositeInfo *pCompInfo); + +typedef void (MaskBlitFunc)(void *pDst, void *pSrc, + unsigned char *pMask, jint maskOff, jint maskScan, + jint width, jint height, + SurfaceDataRasInfo *pDstInfo, + SurfaceDataRasInfo *pSrcInfo, + struct _NativePrimitive *pPrim, + CompositeInfo *pCompInfo); + +typedef struct _NativePrimitive { + void *pPrimType; + void *pSrcType; + CompositeType *pCompType; + void *pDstType; + union { + AnyFunc *initializer; + BlitFunc *blit; + BlitBgFunc *blitbg; + ScaleBlitFunc *scaledblit; + AnyFunc *fillrect; + AnyFunc *fillspans; + AnyFunc *fillparallelogram; + void *drawparallelogram; + AnyFunc *drawline; + AnyFunc *maskfill; + MaskBlitFunc *maskblit; + AnyFunc *drawglyphlist; + AnyFunc *drawglyphlistaa; + AnyFunc *drawglyphlistlcd; + void *transformhelpers; + } funcs, funcs_c; + jint srcflags; + jint dstflags; +} NativePrimitive; + +typedef struct { + SurfaceDataBounds bounds; + jint endIndex; + jobject bands; + jint index; + jint numrects; + jint *pBands; +} RegionData; + +#endif // JAVAINTERNAL_HXX_INCLUDED diff --git a/third-party/RemoteInput/RemoteInput/Platform/NativeHooks.hxx b/third-party/RemoteInput/RemoteInput/Platform/NativeHooks.hxx new file mode 100644 index 0000000..2da4d66 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Platform/NativeHooks.hxx @@ -0,0 +1,6 @@ +#ifndef NATIVEHOOKS_HXX_INCLUDED +#define NATIVEHOOKS_HXX_INCLUDED + +void StartHook() noexcept; + +#endif // NATIVEHOOKS_HXX_INCLUDED diff --git a/third-party/RemoteInput/RemoteInput/Platform/NativeHooks_Darwin.cxx b/third-party/RemoteInput/RemoteInput/Platform/NativeHooks_Darwin.cxx new file mode 100644 index 0000000..3304d5f --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Platform/NativeHooks_Darwin.cxx @@ -0,0 +1,630 @@ +#include "NativeHooks.hxx" +#if defined(__APPLE__) + +#if defined(XCODE) +#include "main.hxx" +#else +#include "Thirdparty/Hook.hxx" +#endif + +#include +#include +#include +#include + +#include +#include +#include "JavaInternal.hxx" +#include "EIOS.hxx" +#include "Graphics.hxx" +#endif + +#if !defined(USE_DETOURS) +#define DYLD_INTERPOSE(_replacment,_replacee) \ +__attribute__((used)) static struct{ const void* replacment; const void* replacee; } _interpose_##_replacee \ +__attribute__ ((section ("__DATA,__interpose"))) = { (const void*)(unsigned long)&_replacment, (const void*)(unsigned long)&_replacee }; +#endif + +#if defined(__APPLE__) +std::unique_ptr native_hook{nullptr}; +std::unique_ptr opengl_blit_hook{nullptr}; +std::unique_ptr opengl_flush_buffer_hook{nullptr}; + +bool can_render(jint srctype, jint width, jint height) +{ + /* SRC_TYPE = OpenGLSurfaceData.PF_INT_RGBX = 3; //GL_RGBA, GL_UNSIGNED_INT_8_8_8_8 + * OGLPixelFormat pf = PixelFormats[srctype]; + * + * It's used to render java.awt.canvas.. + * */ + if (srctype == 3 /* OpenGLSurfaceData.PF_INT_RGBX */) + { + return true; + } + + //Arbitrarily chosen because the java.awt.canvas should never be smaller than this value. + //Technically stuff should be rendering with a minimum of a power of 2.. so 256 * 256 for example. + //This value can change if the renderer renders in 128 * 128 chunks but I haven't met a GPU that does that.. + //OR a Java implementation either. + // - January 7th, 2021 + return width >= 200 && height >= 200; +} + +void JavaNativeBlit(JNIEnv *env, jobject self, jobject srcData, jobject dstData, jobject comp, jobject clip, jint srcx, jint srcy, jint dstx, jint dsty, jint width, jint height) noexcept +{ + extern std::unique_ptr control_center; + if (!control_center || !can_render(-1, width, height)) + { + return native_hook->call(env, self, srcData, dstData, comp, clip, srcx, srcy, dstx, dsty, width, height); + } + + #define Region_IsRectangular(pRgnInfo) ((pRgnInfo)->endIndex == 0) + #define Region_IsEmpty(pRgnInfo) ((pRgnInfo)->bounds.x1 >= (pRgnInfo)->bounds.x2 || (pRgnInfo)->bounds.y1 >= (pRgnInfo)->bounds.y2) + #define Region_IntersectBounds(pRgnInfo, pDstBounds) SurfaceData_IntersectBounds(&(pRgnInfo)->bounds, pDstBounds) + + #define PtrAddBytes(p, b) ((void *) (((intptr_t) (p)) + (b))) + #define PtrCoord(p, x, xinc, y, yinc) PtrAddBytes(p, (y)*(yinc) + (x)*(xinc)) + + static NativePrimitive* (*GetNativePrim)(JNIEnv *env, jobject gp) = reinterpret_cast(dlsym(RTLD_DEFAULT, "GetNativePrim")); + static SurfaceDataOps* (*SurfaceData_GetOps)(JNIEnv *env, jobject sData) = reinterpret_cast(dlsym(RTLD_DEFAULT, "SurfaceData_GetOps")); + static jint (*Region_GetInfo)(JNIEnv *env, jobject region, RegionData *pRgnInfo) = reinterpret_cast(dlsym(RTLD_DEFAULT, "Region_GetInfo")); + static void (*SurfaceData_IntersectBounds)(SurfaceDataBounds *src, SurfaceDataBounds *dst) = reinterpret_cast(dlsym(RTLD_DEFAULT, "SurfaceData_IntersectBounds")); + static void (*SurfaceData_IntersectBlitBounds)(SurfaceDataBounds *src, SurfaceDataBounds *dst, jint dx, jint dy) = reinterpret_cast(dlsym(RTLD_NEXT, "SurfaceData_IntersectBlitBounds")); + static void (*Region_StartIteration)(JNIEnv *env, RegionData *pRgnInfo) = reinterpret_cast(dlsym(RTLD_DEFAULT, "Region_StartIteration")); + static jint (*Region_NextIteration)(RegionData *pRgnInfo, SurfaceDataBounds *pSpan) = reinterpret_cast(dlsym(RTLD_DEFAULT, "Region_NextIteration")); + static void (*Region_EndIteration)(JNIEnv *env, RegionData *pRgnInfo) = reinterpret_cast(dlsym(RTLD_DEFAULT, "Region_EndIteration")); + + if (!GetNativePrim || !SurfaceData_GetOps || !Region_GetInfo || !SurfaceData_IntersectBounds || !SurfaceData_IntersectBlitBounds || !Region_StartIteration || !Region_NextIteration || !Region_EndIteration || width <= 0 || height <= 0) + { + return native_hook->call(env, self, srcData, dstData, comp, clip, srcx, srcy, dstx, dsty, width, height); + } + + CompositeInfo compInfo = {0}; + NativePrimitive* pPrim = GetNativePrim(env, self); + if (!pPrim) + { + return native_hook->call(env, self, srcData, dstData, comp, clip, srcx, srcy, dstx, dsty, width, height); + } + + if (pPrim->pCompType->getCompInfo) + { + (*pPrim->pCompType->getCompInfo)(env, &compInfo, comp); + } + + RegionData clipInfo = {0}; + if (Region_GetInfo(env, clip, &clipInfo)) + { + return native_hook->call(env, self, srcData, dstData, comp, clip, srcx, srcy, dstx, dsty, width, height); + } + + SurfaceDataOps *srcOps = SurfaceData_GetOps(env, srcData); + SurfaceDataOps *dstOps = SurfaceData_GetOps(env, dstData); + + if (!srcOps || !dstOps) + { + return native_hook->call(env, self, srcData, dstData, comp, clip, srcx, srcy, dstx, dsty, width, height); + } + + SurfaceDataRasInfo srcInfo = {0}; + SurfaceDataRasInfo dstInfo = {0}; + srcInfo.bounds.x1 = srcx; + srcInfo.bounds.y1 = srcy; + srcInfo.bounds.x2 = srcx + width; + srcInfo.bounds.y2 = srcy + height; + dstInfo.bounds.x1 = dstx; + dstInfo.bounds.y1 = dsty; + dstInfo.bounds.x2 = dstx + width; + dstInfo.bounds.y2 = dsty + height; + srcx -= dstx; + srcy -= dsty; + + SurfaceData_IntersectBounds(&dstInfo.bounds, &clipInfo.bounds); + + if (srcOps->Lock(env, srcOps, &srcInfo, pPrim->srcflags) == SD_SUCCESS) + { + jint dstFlags = pPrim->dstflags; + if (!Region_IsRectangular(&clipInfo)) + { + dstFlags |= SD_LOCK_PARTIAL_WRITE; + } + + if (dstOps->Lock(env, dstOps, &dstInfo, dstFlags) == SD_SUCCESS) + { + SurfaceData_IntersectBlitBounds(&dstInfo.bounds, &srcInfo.bounds, srcx, srcy); + Region_IntersectBounds(&clipInfo, &dstInfo.bounds); + + if (!Region_IsEmpty(&clipInfo)) + { + srcOps->GetRasInfo(env, srcOps, &srcInfo); + dstOps->GetRasInfo(env, dstOps, &dstInfo); + + if (srcInfo.rasBase && dstInfo.rasBase) + { + SurfaceDataBounds span; + jint savesx = srcInfo.bounds.x1; + jint savedx = dstInfo.bounds.x1; + Region_StartIteration(env, &clipInfo); + + while (Region_NextIteration(&clipInfo, &span)) + { + void *pSrc = PtrCoord(srcInfo.rasBase, srcx + span.x1, srcInfo.pixelStride, srcy + span.y1, srcInfo.scanStride); + void *pDst = PtrCoord(dstInfo.rasBase, span.x1, dstInfo.pixelStride, span.y1, dstInfo.scanStride); + + srcInfo.bounds.x1 = srcx + span.x1; + dstInfo.bounds.x1 = span.x1; + (*pPrim->funcs.blit)(pSrc, pDst, span.x2 - span.x1, span.y2 - span.y1, &srcInfo, &dstInfo, pPrim, &compInfo); + } + + Region_EndIteration(env, &clipInfo); + srcInfo.bounds.x1 = savesx; + dstInfo.bounds.x1 = savedx; + + void* rasBase = reinterpret_cast(srcInfo.rasBase) + (srcInfo.scanStride * srcy) + (srcInfo.pixelStride * srcx); + + control_center->set_target_dimensions(width, height); + std::uint8_t* dest = control_center->get_image(); + ImageFormat format = control_center->get_image_format(); + + //Render to Shared Memory + if (dest) + { + copy_image_from_bgra(dest, rasBase, (srcInfo.scanStride / srcInfo.pixelStride), height, srcInfo.pixelStride, format); + } + + //Render Debug Graphics + if (control_center->get_debug_graphics()) + { + std::uint8_t* src = control_center->get_debug_image(); + if (src) + { + rasBase = reinterpret_cast(dstInfo.rasBase) + (dstInfo.scanStride * dsty) + (dstInfo.pixelStride * dstx); + draw_image(rasBase, src, (dstInfo.scanStride / dstInfo.pixelStride), height, dstInfo.pixelStride, format); + } + } + + //Render Cursor + std::int32_t x = -1; + std::int32_t y = -1; + control_center->get_applet_mouse_position(&x, &y); + + if (x > -1 && y > -1) + { + rasBase = reinterpret_cast(dstInfo.rasBase) + (dstInfo.scanStride * dsty) + (dstInfo.pixelStride * dstx); + draw_circle(x, y, 2, rasBase, width, height, dstInfo.pixelStride, true, 0xFF0000FF); + } + } + } + + if (dstOps->Unlock) + { + dstOps->Unlock(env, dstOps, &dstInfo); + } + } + + if (srcOps->Unlock) + { + srcOps->Unlock(env, srcOps, &srcInfo); + } + } + + if (dstOps->Release) + { + dstOps->Release(env, dstOps, &dstInfo); + } + + if (srcOps->Release) + { + srcOps->Release(env, srcOps, &srcInfo); + } +} + +void JavaNativeOGLBlit(JNIEnv *env, void *oglc, jlong pSrcOps, jlong pDstOps, jboolean xform, jint hint, jint srctype, jboolean texture, jint sx1, jint sy1, jint sx2, jint sy2, jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2) noexcept +{ + extern std::unique_ptr control_center; + if (control_center) + { + jint width = sx2 - sx1; + jint height = sy2 - sy1; + SurfaceDataOps *srcOps = (SurfaceDataOps *)pSrcOps; + + if (!srcOps || width <= 0 || height <= 0 || !can_render(srctype, width, height)) + { + if (opengl_blit_hook) + { + return opengl_blit_hook->call(env, oglc, pSrcOps, pDstOps, xform, hint, srctype, texture, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2); + } + return; + } + + SurfaceDataRasInfo srcInfo = {0}; + srcInfo.bounds.x1 = sx1; + srcInfo.bounds.y1 = sy1; + srcInfo.bounds.x2 = sx2; + srcInfo.bounds.y2 = sy2; + + if (srcOps->Lock(env, srcOps, &srcInfo, SD_LOCK_READ) == SD_SUCCESS) + { + srcOps->GetRasInfo(env, srcOps, &srcInfo); + if (srcInfo.rasBase) + { + void* rasBase = reinterpret_cast(srcInfo.rasBase) + (srcInfo.scanStride * sy1) + (srcInfo.pixelStride * sx1); + bool isRasterAligned = srcInfo.scanStride % srcInfo.pixelStride == 0; //!(srcInfo.scanStride & 0x03); + + control_center->set_target_dimensions(width, height); + std::uint8_t* dest = control_center->get_image(); + ImageFormat format = control_center->get_image_format(); + + //Render to Shared Memory + if (dest) + { + if (isRasterAligned) + { + copy_image_from_bgra(dest, rasBase, (srcInfo.scanStride / srcInfo.pixelStride), height, srcInfo.pixelStride, format); + } + else + { + for (int i = 0; i < height; ++i) + { + int offset = (srcInfo.scanStride / srcInfo.pixelStride) * i; + copy_image_from_bgra(dest + offset, rasBase, (srcInfo.scanStride / srcInfo.pixelStride), 1, srcInfo.pixelStride, format); + rasBase = static_cast(reinterpret_cast(rasBase) + srcInfo.scanStride); + } + } + } + + //Render Debug Graphics + if (control_center->get_debug_graphics()) + { + std::uint8_t* src = control_center->get_debug_image(); + if (src) + { + rasBase = reinterpret_cast(srcInfo.rasBase) + (srcInfo.scanStride * sy1) + (srcInfo.pixelStride * sx1); + draw_image(rasBase, src, (srcInfo.scanStride / srcInfo.pixelStride), height, srcInfo.pixelStride, format); + } + } + + //Render Cursor + std::int32_t x = -1; + std::int32_t y = -1; + control_center->get_applet_mouse_position(&x, &y); + + if (x > -1 && y > -1) + { + rasBase = reinterpret_cast(srcInfo.rasBase) + (srcInfo.scanStride * sy1) + (srcInfo.pixelStride * sx1); + draw_circle(x, y, 2, rasBase, width, height, srcInfo.pixelStride, true, 0xFF0000FF); + } + + if (srcOps->Release) + { + srcOps->Release(env, srcOps, &srcInfo); + } + } + + if (srcOps->Unlock) + { + srcOps->Unlock(env, srcOps, &srcInfo); + } + } + } + + if (opengl_blit_hook) + { + return opengl_blit_hook->call(env, oglc, pSrcOps, pDstOps, xform, hint, srctype, texture, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2); + } +} + +void JavaNativeOGLRenderQueueFlushBuffer(JNIEnv *env, jobject oglrq, jlong buf, jint limit) noexcept +{ + #define NEXT_VAL(buf, type) (((type *)((buf) += sizeof(type)))[-1]) + #define NEXT_INT(buf) NEXT_VAL(buf, jint) + #define NEXT_LONG(buf) NEXT_VAL(buf, jlong) + #define NEXT_DOUBLE(buf) NEXT_VAL(buf, jdouble) + + #define EXTRACT_VAL(packedval, offset, mask) \ + (((packedval) >> (offset)) & (mask)) + #define EXTRACT_BYTE(packedval, offset) \ + (unsigned char)EXTRACT_VAL(packedval, offset, 0xff) + #define EXTRACT_BOOLEAN(packedval, offset) \ + (jboolean)EXTRACT_VAL(packedval, offset, 0x1) + + const int BLIT = 31; + const int OFFSET_SRCTYPE = 16; + const int OFFSET_HINT = 8; + const int OFFSET_TEXTURE = 3; + const int OFFSET_RTT = 2; + const int OFFSET_XFORM = 1; + const int OFFSET_ISOBLIT = 0; + + jlong original_buffer = buf; + + std::uint8_t* buffer = reinterpret_cast(buf); + std::uint8_t* buffer_end = reinterpret_cast(buf) + limit; + + while(buffer < buffer_end) + { + jint opcode = NEXT_INT(buffer); + switch (opcode) + { + case BLIT: + { + jint packedParams = NEXT_INT(buffer); + jint sx1 = NEXT_INT(buffer); + jint sy1 = NEXT_INT(buffer); + jint sx2 = NEXT_INT(buffer); + jint sy2 = NEXT_INT(buffer); + jdouble dx1 = NEXT_DOUBLE(buffer); + jdouble dy1 = NEXT_DOUBLE(buffer); + jdouble dx2 = NEXT_DOUBLE(buffer); + jdouble dy2 = NEXT_DOUBLE(buffer); + jlong pSrc = NEXT_LONG(buffer); + jlong pDst = NEXT_LONG(buffer); + jint hint = EXTRACT_BYTE(packedParams, OFFSET_HINT); + jboolean texture = EXTRACT_BOOLEAN(packedParams, + OFFSET_TEXTURE); + jboolean rtt = EXTRACT_BOOLEAN(packedParams, + OFFSET_RTT); + jboolean xform = EXTRACT_BOOLEAN(packedParams, + OFFSET_XFORM); + jboolean isoblit = EXTRACT_BOOLEAN(packedParams, + OFFSET_ISOBLIT); + + if (!isoblit) + { + jint srctype = EXTRACT_BYTE(packedParams, OFFSET_SRCTYPE); + JavaNativeOGLBlit(env, nullptr, pSrc, pDst, xform, hint, srctype, texture, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2); + } + } + break; + + default: + break; + } + } + + return opengl_flush_buffer_hook->call(env, oglrq, original_buffer, limit); +} +#endif + +#if defined(__APPLE__) +void GeneratePixelBuffers(void* ctx, GLuint (&pbo)[2], GLint width, GLint height, GLint stride) noexcept +{ + static int w = 0; + static int h = 0; + + #if defined(__APPLE__) + CGLContextObj CGL_MACRO_CONTEXT = static_cast(ctx); + #endif + + //Buffer size changed + if (w != width || h != height) + { + w = width; + h = height; + + //If buffers already exist, clean them up + if (pbo[1] != 0) + { + glDeleteBuffers(2, pbo); + } + + //Generate buffers + glGenBuffers(2, pbo); + glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo[0]); + glBufferData(GL_PIXEL_PACK_BUFFER, width * height * stride, 0, GL_STREAM_READ); + + glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo[1]); + glBufferData(GL_PIXEL_PACK_BUFFER, width * height * stride, 0, GL_STREAM_READ); + glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); + } +} + +void ReadPixelBuffers(void* ctx, GLubyte* dest, GLuint (&pbo)[2], GLint width, GLint height, GLint stride, ImageFormat format) noexcept +{ + static int index = 0; + static int nextIndex = 0; + + #if defined(__APPLE__) + CGLContextObj CGL_MACRO_CONTEXT = static_cast(ctx); + #endif + + //Swap indices + index = (index + 1) % 2; + nextIndex = (index + 1) % 2; + + GLenum gl_format = [](ImageFormat format) -> GLenum { + switch(format) + { + case ImageFormat::BGR_BGRA: return GL_BGRA; + case ImageFormat::BGRA: return GL_BGRA; + case ImageFormat::RGBA: return GL_RGBA; + case ImageFormat::ARGB: return 0; // Not Supported + case ImageFormat::ABGR: return 0; // Not Supported + } + }(format); + + //Read back-buffer. + glPixelStorei(GL_UNPACK_ROW_LENGTH, width); + glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo[index]); + glReadPixels(0, 0, width, height, gl_format, GL_UNSIGNED_BYTE, nullptr); + glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo[nextIndex]); + + void* data = glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY); + + if (data) + { + //memcpy(dest, data, width * height * 4); + FlipImageBytes(data, reinterpret_cast(dest), width, height, 32); + glUnmapBuffer(GL_PIXEL_PACK_BUFFER); + } + else + { + glReadPixels(0, 0, width, height, gl_format, GL_UNSIGNED_BYTE, dest); + } + + //Restore state + glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); +} + +#if defined(USE_DETOURS) +CGLError mCGLFlushDrawable(CGLContextObj ctx) noexcept +{ + extern std::unique_ptr control_center; + + if (control_center) + { + static GLint ViewPort[4] = {0}; + static GLuint pbo[2] = {0}; + CGLContextObj CGL_MACRO_CONTEXT = ctx; + + glGetIntegerv(GL_VIEWPORT, ViewPort); + GLint width = ViewPort[2] - ViewPort[0]; + GLint height = ViewPort[3] - ViewPort[1]; + + if (can_render(-1, width, height)) + { + control_center->set_target_dimensions(width, height); + + //Render to Shared Memory + std::uint8_t* dest = control_center->get_image(); + ImageFormat format = control_center->get_image_format(); + if (dest) + { + GeneratePixelBuffers(ctx, pbo, width, height, 4); + ReadPixelBuffers(ctx, dest, pbo, width, height, 4, format); + //FlipImageVertically(width, height, dest); + } + + //Render Debug Graphics + if (control_center->get_debug_graphics()) + { + std::uint8_t* src = control_center->get_debug_image(); + if (src) + { + gl_draw_image(ctx, src, 0, 0, width, height, 4, format); + } + } + + //Render Cursor + std::int32_t x = -1; + std::int32_t y = -1; + control_center->get_applet_mouse_position(&x, &y); + + if (x > -1 && y > -1) + { + glColor4ub(0xFF, 0x00, 0x00, 0xFF); + gl_draw_point(ctx, x, height - y, 0, 4); + } + } + } + + return opengl_blit_hook->call(ctx); +} +#else +CGLError mCGLFlushDrawable(CGLContextObj ctx) noexcept +{ + extern std::unique_ptr control_center; + + if (control_center) + { + static GLint ViewPort[4] = {0}; + static GLuint pbo[2] = {0}; + CGLContextObj CGL_MACRO_CONTEXT = ctx; + + glGetIntegerv(GL_VIEWPORT, ViewPort); + GLint width = ViewPort[2] - ViewPort[0]; + GLint height = ViewPort[3] - ViewPort[1]; + + if (can_render(-1, width, height)) + { + control_center->set_target_dimensions(width, height); + + //Render to Shared Memory + std::uint8_t* dest = control_center->get_image(); + ImageFormat format = control_center->get_image_format(); + if (dest) + { + GeneratePixelBuffers(ctx, pbo, width, height, 4); + ReadPixelBuffers(ctx, dest, pbo, width, height, 4, format); + //FlipImageVertically(width, height, dest); + } + + //Render Debug Graphics + if (control_center->get_debug_graphics()) + { + std::uint8_t* src = control_center->get_debug_image(); + if (src) + { + gl_draw_image(ctx, src, 0, 0, width, height, 4, format); + } + } + + //Render Cursor + std::int32_t x = -1; + std::int32_t y = -1; + control_center->get_applet_mouse_position(&x, &y); + + if (x > -1 && y > -1) + { + glColor4ub(0xFF, 0x00, 0x00, 0xFF); + gl_draw_point(ctx, x, height - y, 0, 4); + } + } + } + + static decltype(CGLFlushDrawable)* o_CGLFlushDrawable = reinterpret_cast(dlsym(RTLD_NEXT, "CGLFlushDrawable")); + return o_CGLFlushDrawable(ctx); +} +#endif +#endif + +#if defined(__APPLE__) +void InitialiseHooks() noexcept +{ + #if defined(USE_DETOURS) + //Hook Native Blit + void* blit = dlsym(RTLD_DEFAULT, "Java_sun_java2d_loops_Blit_Blit"); + if (blit) + { + native_hook = std::make_unique(reinterpret_cast(blit), reinterpret_cast(JavaNativeBlit)); + native_hook->apply(); + } + + //Hook OpenGL Blit + #if defined(HOOK_OPENGL_BLIT) + blit = dlsym(RTLD_DEFAULT, "OGLBlitLoops_Blit"); + if (blit) + { + opengl_blit_hook = std::make_unique(reinterpret_cast(blit), reinterpret_cast(JavaNativeOGLBlit)); + opengl_blit_hook->apply(); + } + else + { + blit = dlsym(RTLD_DEFAULT, "Java_sun_java2d_opengl_OGLRenderQueue_flushBuffer"); + if (blit) + { + opengl_flush_buffer_hook = std::make_unique(reinterpret_cast(blit), reinterpret_cast(JavaNativeOGLRenderQueueFlushBuffer)); + opengl_flush_buffer_hook->apply(); + } + } + + if (!blit) + { + blit = dlsym(RTLD_DEFAULT, "CGLFlushDrawable"); + opengl_blit_hook = std::make_unique(reinterpret_cast(blit), reinterpret_cast(mCGLFlushDrawable)); + opengl_blit_hook->apply(); + } + #endif + #else + DYLD_INTERPOSE(mCGLFlushDrawable, CGLFlushDrawable); + #endif +} + +void StartHook() noexcept +{ + InitialiseHooks(); + + //Signal that all hooks are finished initializing.. + ControlCenter::signal_sync(getpid()); +} +#endif \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Platform/NativeHooks_Linux.cxx b/third-party/RemoteInput/RemoteInput/Platform/NativeHooks_Linux.cxx new file mode 100644 index 0000000..58e71db --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Platform/NativeHooks_Linux.cxx @@ -0,0 +1,639 @@ +#include "NativeHooks.hxx" +#if defined(__linux__) + +#include + +#include +#include +#include "JavaInternal.hxx" +#include "EIOS.hxx" +#include "Graphics.hxx" + +#define GL_GLEXT_PROTOTYPES +#include +#include +#include +#endif + + +#if defined(__linux__) +std::unique_ptr native_hook{nullptr}; +std::unique_ptr opengl_blit_hook{nullptr}; +std::unique_ptr opengl_flush_buffer_hook{nullptr}; + +bool can_render(jint srctype, jint width, jint height) +{ + /* SRC_TYPE = OpenGLSurfaceData.PF_INT_RGBX = 3; //GL_RGBA, GL_UNSIGNED_INT_8_8_8_8 + * OGLPixelFormat pf = PixelFormats[srctype]; + * + * It's used to render java.awt.canvas.. + * */ + if (srctype == 3 /* OpenGLSurfaceData.PF_INT_RGBX */) + { + return true; + } + + //Arbitrarily chosen because the java.awt.canvas should never be smaller than this value. + //Technically stuff should be rendering with a minimum of a power of 2.. so 256 * 256 for example. + //This value can change if the renderer renders in 128 * 128 chunks but I haven't met a GPU that does that.. + //OR a Java implementation either. + // - January 7th, 2021 + return width >= 200 && height >= 200; +} + +void JavaNativeBlit(JNIEnv *env, jobject self, jobject srcData, jobject dstData, jobject comp, jobject clip, jint srcx, jint srcy, jint dstx, jint dsty, jint width, jint height) noexcept +{ + extern std::unique_ptr control_center; + if (!control_center || !can_render(-1, width, height)) + { + return native_hook->call(env, self, srcData, dstData, comp, clip, srcx, srcy, dstx, dsty, width, height); + } + + #define Region_IsRectangular(pRgnInfo) ((pRgnInfo)->endIndex == 0) + #define Region_IsEmpty(pRgnInfo) ((pRgnInfo)->bounds.x1 >= (pRgnInfo)->bounds.x2 || (pRgnInfo)->bounds.y1 >= (pRgnInfo)->bounds.y2) + #define Region_IntersectBounds(pRgnInfo, pDstBounds) SurfaceData_IntersectBounds(&(pRgnInfo)->bounds, pDstBounds) + + #define PtrAddBytes(p, b) ((void *) (((intptr_t) (p)) + (b))) + #define PtrCoord(p, x, xinc, y, yinc) PtrAddBytes(p, (y)*(yinc) + (x)*(xinc)) + + static NativePrimitive* (*GetNativePrim)(JNIEnv *env, jobject gp) = reinterpret_cast(dlsym(RTLD_DEFAULT, "GetNativePrim")); + static SurfaceDataOps* (*SurfaceData_GetOps)(JNIEnv *env, jobject sData) = reinterpret_cast(dlsym(RTLD_DEFAULT, "SurfaceData_GetOps")); + static jint (*Region_GetInfo)(JNIEnv *env, jobject region, RegionData *pRgnInfo) = reinterpret_cast(dlsym(RTLD_DEFAULT, "Region_GetInfo")); + static void (*SurfaceData_IntersectBounds)(SurfaceDataBounds *src, SurfaceDataBounds *dst) = reinterpret_cast(dlsym(RTLD_DEFAULT, "SurfaceData_IntersectBounds")); + static void (*SurfaceData_IntersectBlitBounds)(SurfaceDataBounds *src, SurfaceDataBounds *dst, jint dx, jint dy) = reinterpret_cast(dlsym(RTLD_DEFAULT, "SurfaceData_IntersectBlitBounds")); + static void (*Region_StartIteration)(JNIEnv *env, RegionData *pRgnInfo) = reinterpret_cast(dlsym(RTLD_DEFAULT, "Region_StartIteration")); + static jint (*Region_NextIteration)(RegionData *pRgnInfo, SurfaceDataBounds *pSpan) = reinterpret_cast(dlsym(RTLD_DEFAULT, "Region_NextIteration")); + static void (*Region_EndIteration)(JNIEnv *env, RegionData *pRgnInfo) = reinterpret_cast(dlsym(RTLD_DEFAULT, "Region_EndIteration")); + + if (!GetNativePrim || !SurfaceData_GetOps || !Region_GetInfo || !SurfaceData_IntersectBounds || !SurfaceData_IntersectBlitBounds || !Region_StartIteration || !Region_NextIteration || !Region_EndIteration || width <= 0 || height <= 0) + { + return native_hook->call(env, self, srcData, dstData, comp, clip, srcx, srcy, dstx, dsty, width, height); + } + + CompositeInfo compInfo = {0}; + NativePrimitive* pPrim = GetNativePrim(env, self); + if (!pPrim) + { + return native_hook->call(env, self, srcData, dstData, comp, clip, srcx, srcy, dstx, dsty, width, height); + } + + if (pPrim->pCompType->getCompInfo) + { + (*pPrim->pCompType->getCompInfo)(env, &compInfo, comp); + } + + RegionData clipInfo = {0}; + if (Region_GetInfo(env, clip, &clipInfo)) + { + return native_hook->call(env, self, srcData, dstData, comp, clip, srcx, srcy, dstx, dsty, width, height); + } + + SurfaceDataOps *srcOps = SurfaceData_GetOps(env, srcData); + SurfaceDataOps *dstOps = SurfaceData_GetOps(env, dstData); + + if (!srcOps || !dstOps) + { + return native_hook->call(env, self, srcData, dstData, comp, clip, srcx, srcy, dstx, dsty, width, height); + } + + SurfaceDataRasInfo srcInfo = {0}; + SurfaceDataRasInfo dstInfo = {0}; + srcInfo.bounds.x1 = srcx; + srcInfo.bounds.y1 = srcy; + srcInfo.bounds.x2 = srcx + width; + srcInfo.bounds.y2 = srcy + height; + dstInfo.bounds.x1 = dstx; + dstInfo.bounds.y1 = dsty; + dstInfo.bounds.x2 = dstx + width; + dstInfo.bounds.y2 = dsty + height; + srcx -= dstx; + srcy -= dsty; + + SurfaceData_IntersectBounds(&dstInfo.bounds, &clipInfo.bounds); + + if (srcOps->Lock(env, srcOps, &srcInfo, pPrim->srcflags) == SD_SUCCESS) + { + jint dstFlags = pPrim->dstflags; + if (!Region_IsRectangular(&clipInfo)) + { + dstFlags |= SD_LOCK_PARTIAL_WRITE; + } + + if (dstOps->Lock(env, dstOps, &dstInfo, dstFlags) == SD_SUCCESS) + { + SurfaceData_IntersectBlitBounds(&dstInfo.bounds, &srcInfo.bounds, srcx, srcy); + Region_IntersectBounds(&clipInfo, &dstInfo.bounds); + + if (!Region_IsEmpty(&clipInfo)) + { + srcOps->GetRasInfo(env, srcOps, &srcInfo); + dstOps->GetRasInfo(env, dstOps, &dstInfo); + + if (srcInfo.rasBase && dstInfo.rasBase) + { + SurfaceDataBounds span; + jint savesx = srcInfo.bounds.x1; + jint savedx = dstInfo.bounds.x1; + Region_StartIteration(env, &clipInfo); + + while (Region_NextIteration(&clipInfo, &span)) + { + void *pSrc = PtrCoord(srcInfo.rasBase, srcx + span.x1, srcInfo.pixelStride, srcy + span.y1, srcInfo.scanStride); + void *pDst = PtrCoord(dstInfo.rasBase, span.x1, dstInfo.pixelStride, span.y1, dstInfo.scanStride); + + srcInfo.bounds.x1 = srcx + span.x1; + dstInfo.bounds.x1 = span.x1; + (*pPrim->funcs.blit)(pSrc, pDst, span.x2 - span.x1, span.y2 - span.y1, &srcInfo, &dstInfo, pPrim, &compInfo); + } + + Region_EndIteration(env, &clipInfo); + srcInfo.bounds.x1 = savesx; + dstInfo.bounds.x1 = savedx; + + void* rasBase = reinterpret_cast(srcInfo.rasBase) + (srcInfo.scanStride * srcy) + (srcInfo.pixelStride * srcx); + + control_center->set_target_dimensions(width, height); + std::uint8_t* dest = control_center->get_image(); + ImageFormat format = control_center->get_image_format(); + + //Render to Shared Memory + if (dest) + { + copy_image_from_bgra(dest, rasBase, (srcInfo.scanStride / srcInfo.pixelStride), height, srcInfo.pixelStride, format); + } + + //Render Debug Graphics + if (control_center->get_debug_graphics()) + { + std::uint8_t* src = control_center->get_debug_image(); + if (src) + { + rasBase = reinterpret_cast(dstInfo.rasBase) + (dstInfo.scanStride * dsty) + (dstInfo.pixelStride * dstx); + draw_image(rasBase, src, (dstInfo.scanStride / dstInfo.pixelStride), height, dstInfo.pixelStride, format); + } + } + + //Render Cursor + std::int32_t x = -1; + std::int32_t y = -1; + control_center->get_applet_mouse_position(&x, &y); + + if (x > -1 && y > -1) + { + rasBase = reinterpret_cast(dstInfo.rasBase) + (dstInfo.scanStride * dsty) + (dstInfo.pixelStride * dstx); + draw_circle(x, y, 2, rasBase, width, height, dstInfo.pixelStride, true, 0xFF0000FF); + } + } + } + + if (dstOps->Unlock) + { + dstOps->Unlock(env, dstOps, &dstInfo); + } + } + + if (srcOps->Unlock) + { + srcOps->Unlock(env, srcOps, &srcInfo); + } + } + + if (dstOps->Release) + { + dstOps->Release(env, dstOps, &dstInfo); + } + + if (srcOps->Release) + { + srcOps->Release(env, srcOps, &srcInfo); + } +} + +void JavaNativeOGLBlit(JNIEnv *env, void *oglc, jlong pSrcOps, jlong pDstOps, jboolean xform, jint hint, jint srctype, jboolean texture, jint sx1, jint sy1, jint sx2, jint sy2, jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2) noexcept +{ + extern std::unique_ptr control_center; + if (control_center) + { + jint width = sx2 - sx1; + jint height = sy2 - sy1; + SurfaceDataOps *srcOps = (SurfaceDataOps *)pSrcOps; + + if (!srcOps || width <= 0 || height <= 0 || !can_render(srctype, width, height)) + { + if (opengl_blit_hook) + { + return opengl_blit_hook->call(env, oglc, pSrcOps, pDstOps, xform, hint, srctype, texture, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2); + } + return; + } + + SurfaceDataRasInfo srcInfo = {0}; + srcInfo.bounds.x1 = sx1; + srcInfo.bounds.y1 = sy1; + srcInfo.bounds.x2 = sx2; + srcInfo.bounds.y2 = sy2; + + if (srcOps->Lock(env, srcOps, &srcInfo, SD_LOCK_READ) == SD_SUCCESS) + { + srcOps->GetRasInfo(env, srcOps, &srcInfo); + if (srcInfo.rasBase) + { + void* rasBase = reinterpret_cast(srcInfo.rasBase) + (srcInfo.scanStride * sy1) + (srcInfo.pixelStride * sx1); + bool isRasterAligned = srcInfo.scanStride % srcInfo.pixelStride == 0; //!(srcInfo.scanStride & 0x03); + + control_center->set_target_dimensions(width, height); + std::uint8_t* dest = control_center->get_image(); + ImageFormat format = control_center->get_image_format(); + + //Render to Shared Memory + if (dest) + { + if (isRasterAligned) + { + copy_image_from_bgra(dest, rasBase, (srcInfo.scanStride / srcInfo.pixelStride), height, srcInfo.pixelStride, format); + } + else + { + for (int i = 0; i < height; ++i) + { + int offset = (srcInfo.scanStride / srcInfo.pixelStride) * i; + copy_image_from_bgra(dest + offset, rasBase, (srcInfo.scanStride / srcInfo.pixelStride), 1, srcInfo.pixelStride, format); + rasBase = static_cast(reinterpret_cast(rasBase) + srcInfo.scanStride); + } + } + } + + //Render Debug Graphics + if (control_center->get_debug_graphics()) + { + std::uint8_t* src = control_center->get_debug_image(); + if (src) + { + rasBase = reinterpret_cast(srcInfo.rasBase) + (srcInfo.scanStride * sy1) + (srcInfo.pixelStride * sx1); + draw_image(rasBase, src, (srcInfo.scanStride / srcInfo.pixelStride), height, srcInfo.pixelStride, format); + } + } + + //Render Cursor + std::int32_t x = -1; + std::int32_t y = -1; + control_center->get_applet_mouse_position(&x, &y); + + if (x > -1 && y > -1) + { + rasBase = reinterpret_cast(srcInfo.rasBase) + (srcInfo.scanStride * sy1) + (srcInfo.pixelStride * sx1); + draw_circle(x, y, 2, rasBase, width, height, srcInfo.pixelStride, true, 0xFF0000FF); + } + + if (srcOps->Release) + { + srcOps->Release(env, srcOps, &srcInfo); + } + } + + if (srcOps->Unlock) + { + srcOps->Unlock(env, srcOps, &srcInfo); + } + } + } + + if (opengl_blit_hook) + { + return opengl_blit_hook->call(env, oglc, pSrcOps, pDstOps, xform, hint, srctype, texture, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2); + } +} + +void JavaNativeOGLRenderQueueFlushBuffer(JNIEnv *env, jobject oglrq, jlong buf, jint limit) noexcept +{ + #define NEXT_VAL(buf, type) (((type *)((buf) += sizeof(type)))[-1]) + #define NEXT_INT(buf) NEXT_VAL(buf, jint) + #define NEXT_LONG(buf) NEXT_VAL(buf, jlong) + #define NEXT_DOUBLE(buf) NEXT_VAL(buf, jdouble) + + #define EXTRACT_VAL(packedval, offset, mask) \ + (((packedval) >> (offset)) & (mask)) + #define EXTRACT_BYTE(packedval, offset) \ + (unsigned char)EXTRACT_VAL(packedval, offset, 0xff) + #define EXTRACT_BOOLEAN(packedval, offset) \ + (jboolean)EXTRACT_VAL(packedval, offset, 0x1) + + const int BLIT = 31; + const int OFFSET_SRCTYPE = 16; + const int OFFSET_HINT = 8; + const int OFFSET_TEXTURE = 3; + const int OFFSET_RTT = 2; + const int OFFSET_XFORM = 1; + const int OFFSET_ISOBLIT = 0; + + jlong original_buffer = buf; + + std::uint8_t* buffer = reinterpret_cast(buf); + std::uint8_t* buffer_end = reinterpret_cast(buf) + limit; + + while(buffer < buffer_end) + { + jint opcode = NEXT_INT(buffer); + switch (opcode) + { + case BLIT: + { + jint packedParams = NEXT_INT(buffer); + jint sx1 = NEXT_INT(buffer); + jint sy1 = NEXT_INT(buffer); + jint sx2 = NEXT_INT(buffer); + jint sy2 = NEXT_INT(buffer); + jdouble dx1 = NEXT_DOUBLE(buffer); + jdouble dy1 = NEXT_DOUBLE(buffer); + jdouble dx2 = NEXT_DOUBLE(buffer); + jdouble dy2 = NEXT_DOUBLE(buffer); + jlong pSrc = NEXT_LONG(buffer); + jlong pDst = NEXT_LONG(buffer); + jint hint = EXTRACT_BYTE(packedParams, OFFSET_HINT); + jboolean texture = EXTRACT_BOOLEAN(packedParams, + OFFSET_TEXTURE); + jboolean rtt = EXTRACT_BOOLEAN(packedParams, + OFFSET_RTT); + jboolean xform = EXTRACT_BOOLEAN(packedParams, + OFFSET_XFORM); + jboolean isoblit = EXTRACT_BOOLEAN(packedParams, + OFFSET_ISOBLIT); + + if (!isoblit) + { + jint srctype = EXTRACT_BYTE(packedParams, OFFSET_SRCTYPE); + JavaNativeOGLBlit(env, nullptr, pSrc, pDst, xform, hint, srctype, texture, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2); + } + } + break; + + default: + break; + } + } + + return opengl_flush_buffer_hook->call(env, oglrq, original_buffer, limit); +} +#endif + +#if defined(__linux__) +#if !defined(USE_DETOURS) +extern "C" [[gnu::visibility("default")]] Bool XShmPutImage(Display *display, Drawable d, GC gc, XImage *image, int src_x, int src_y, int dest_x, int dest_y, unsigned int width, unsigned int height, bool send_event) noexcept +{ + extern std::unique_ptr control_center; + if (control_center) + { + int bytes_per_pixel = image->bits_per_pixel / 8; + int stride = width * bytes_per_pixel; + void *rasBase = reinterpret_cast(image->data) + (stride * src_y) + (bytes_per_pixel * src_x); + + control_center->set_target_dimensions(width, height); + + //Render to Shared Memory + std::uint8_t* dest = control_center->get_image(); + ImageFormat format = control_center->get_image_format(); + if (dest) + { + copy_image_from_bgra(dest, rasBase, (stride / bytes_per_pixel), height, bytes_per_pixel, format); + } + + //Render Debug Graphics + if (control_center->get_debug_graphics()) + { + std::uint8_t* src = control_center->get_debug_image(); + if (src) + { + draw_image(rasBase, src, width, height, bytes_per_pixel, format); + } + } + + //Render Cursor + std::int32_t x = -1; + std::int32_t y = -1; + control_center->get_applet_mouse_position(&x, &y); + + if (x > -1 && y > -1) + { + draw_circle(x, y, 2, rasBase, width, height, bytes_per_pixel, true, 0xFF0000FF); + } + + /*for (std::size_t i = 0; i < image->height; ++i) + { + for (std::size_t j = 0; j < image->width; ++j) + { + std::uint8_t pixel = image->data[i * image->width + (image->height - j - 1)]; + *dest++ = pixel; + } + }*/ + } + + typedef Bool (*XShmPutImage_t)(Display*, Drawable, GC, XImage*, int, int, int, int, unsigned int, unsigned int, bool); + static XShmPutImage_t o_XShmPutImage = reinterpret_cast(dlsym(RTLD_NEXT, "XShmPutImage")); + return o_XShmPutImage(display, d, gc, image, src_x, src_y, dest_x, dest_y, width, height, send_event); +} +#endif +#endif + +#if defined(__linux__) +void GeneratePixelBuffers(GLXDrawable ctx, GLuint (&pbo)[2], GLint width, GLint height, GLint stride) noexcept +{ + static int w = 0; + static int h = 0; + + #if defined(__APPLE__) + CGLContextObj CGL_MACRO_CONTEXT = static_cast(ctx); + #endif + + //Buffer size changed + if (w != width || h != height) + { + w = width; + h = height; + + //If buffers already exist, clean them up + if (pbo[1] != 0) + { + glDeleteBuffers(2, pbo); + } + + //Generate buffers + glGenBuffers(2, pbo); + glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo[0]); + glBufferData(GL_PIXEL_PACK_BUFFER, width * height * stride, 0, GL_STREAM_READ); + + glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo[1]); + glBufferData(GL_PIXEL_PACK_BUFFER, width * height * stride, 0, GL_STREAM_READ); + glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); + } +} + +void ReadPixelBuffers(GLXDrawable ctx, GLubyte* dest, GLuint (&pbo)[2], GLint width, GLint height, GLint stride, ImageFormat format) noexcept +{ + static int index = 0; + static int nextIndex = 0; + + #if defined(__APPLE__) + CGLContextObj CGL_MACRO_CONTEXT = static_cast(ctx); + #endif + + GLenum gl_format = [](ImageFormat format) -> GLenum { + switch(format) + { + case ImageFormat::BGR_BGRA: return GL_BGRA; + case ImageFormat::BGRA: return GL_BGRA; + case ImageFormat::RGBA: return GL_RGBA; + case ImageFormat::ARGB: return 0; // Not Supported + case ImageFormat::ABGR: return 0; // Not Supported + default: return GL_BGRA; + } + }(format); + + //Swap indices + index = (index + 1) % 2; + nextIndex = (index + 1) % 2; + + //Read back-buffer. + glPixelStorei(GL_UNPACK_ROW_LENGTH, width); + glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo[index]); + glReadPixels(0, 0, width, height, gl_format, GL_UNSIGNED_BYTE, nullptr); + glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo[nextIndex]); + + void* data = glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY); + + if (data) + { + //memcpy(dest, data, width * height * 4); + FlipImageBytes(data, (void*&)dest, width, height, 32); + glUnmapBuffer(GL_PIXEL_PACK_BUFFER); + } + else + { + glReadPixels(0, 0, width, height, gl_format, GL_UNSIGNED_BYTE, dest); + } + + //Restore state + glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); +} + +#if defined(USE_DETOURS) +void glXSwapBuffersHook(Display* dpy, GLXDrawable drawable) noexcept +#else +extern "C" [[gnu::visibility("default")]] void glXSwapBuffers(Display* dpy, GLXDrawable drawable) noexcept +#endif +{ + extern std::unique_ptr control_center; + + if (control_center) + { + static GLint ViewPort[4] = {0}; + static GLuint pbo[2] = {0}; + + glGetIntegerv(GL_VIEWPORT, ViewPort); + GLint width = ViewPort[2] - ViewPort[0]; + GLint height = ViewPort[3] - ViewPort[1]; + + if (width >= 200 && height >= 200) + { + control_center->set_target_dimensions(width, height); + + //Render to Shared Memory + std::uint8_t* dest = control_center->get_image(); + ImageFormat format = control_center->get_image_format(); + if (dest) + { + GeneratePixelBuffers(drawable, pbo, width, height, 4); + ReadPixelBuffers(drawable, dest, pbo, width, height, 4, format); + //FlipImageVertically(width, height, dest); + } + + //Render Debug Graphics + if (control_center->get_debug_graphics()) + { + std::uint8_t* src = control_center->get_debug_image(); + if (src) + { + gl_draw_image(reinterpret_cast(drawable), src, 0, 0, width, height, 4, format); + } + } + + //Render Cursor + std::int32_t x = -1; + std::int32_t y = -1; + control_center->get_applet_mouse_position(&x, &y); + + if (x > -1 && y > -1) + { + glColor4ub(0xFF, 0x00, 0x00, 0xFF); + gl_draw_point(reinterpret_cast(drawable), x, y, 0, 4); + } + } + } + + #if defined(USE_DETOURS) + return opengl_blit_hook->call(dpy, drawable); + #else + static decltype(glXSwapBuffers)* o_glXSwapBuffers = reinterpret_cast(dlsym(RTLD_NEXT, "glXSwapBuffers")); + return o_glXSwapBuffers(dpy, drawable); + #endif +} +#endif + +#if defined(__linux__) +void InitialiseHooks() noexcept +{ + #if defined(USE_DETOURS) + //Hook Native Blit + void* blit = dlsym(RTLD_DEFAULT, "Java_sun_java2d_loops_Blit_Blit"); + if (blit) + { + native_hook = std::make_unique(reinterpret_cast(blit), reinterpret_cast(JavaNativeBlit)); + native_hook->apply(); + } + + //Hook OpenGL Blit + #if defined(HOOK_OPENGL_BLIT) + blit = dlsym(RTLD_DEFAULT, "OGLBlitLoops_Blit"); + if (blit) + { + opengl_blit_hook = std::make_unique(reinterpret_cast(blit), reinterpret_cast(JavaNativeOGLBlit)); + opengl_blit_hook->apply(); + } + else + { + blit = dlsym(RTLD_DEFAULT, "Java_sun_java2d_opengl_OGLRenderQueue_flushBuffer"); + if (blit) + { + opengl_flush_buffer_hook = std::make_unique(reinterpret_cast(blit), reinterpret_cast(JavaNativeOGLRenderQueueFlushBuffer)); + opengl_flush_buffer_hook->apply(); + } + } + + if (!blit) + { + blit = dlsym(RTLD_DEFAULT, "glXSwapBuffers"); + if (blit) + { + opengl_blit_hook = std::make_unique(reinterpret_cast(blit), reinterpret_cast(glXSwapBuffersHook)); + opengl_blit_hook->apply(); + } + } + #else + blit = dlsym(RTLD_NEXT, "glXSwapBuffers"); + if (blit) + { + opengl_blit_hook = std::make_unique(reinterpret_cast(blit), reinterpret_cast(glXSwapBuffersHook)); + opengl_blit_hook->apply(); + } + #endif + #endif +} + +void StartHook() noexcept +{ + InitialiseHooks(); + + //Signal that all hooks are finished initializing.. + ControlCenter::signal_sync(getpid()); +} +#endif \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Platform/NativeHooks_Windows.cxx b/third-party/RemoteInput/RemoteInput/Platform/NativeHooks_Windows.cxx new file mode 100644 index 0000000..1c3b785 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Platform/NativeHooks_Windows.cxx @@ -0,0 +1,1529 @@ +#include "NativeHooks.hxx" +#include +#include + +#if defined(_WIN32) || defined(_WIN64) +#if defined(_MSC_VER) +#include +#include +#include +#include + +typedef std::uintptr_t GLsizeiptr; +#define GL_PIXEL_PACK_BUFFER 0x88EB +#define GL_STREAM_READ 0x88E1 +#define GL_READ_ONLY 0x88B8 +#define GL_BGRA 0x80E1 +#define GL_RGBA 0x1908 +#else +#include +#include +#include +#include +#include +#endif + +#include +#include +#include +#include "JavaInternal.hxx" +#include "EIOS.hxx" +#include "Graphics.hxx" +#include "Thirdparty/Hook.hxx" +#endif + +#if defined(_WIN32) || defined(_WIN64) +std::unique_ptr native_hook{nullptr}; + +std::unique_ptr opengl_blit_hook{nullptr}; +std::unique_ptr opengl_flush_buffer_hook{nullptr}; +std::unique_ptr opengl_swap_hook{nullptr}; + +std::unique_ptr directx_xrgb_hook{nullptr}; +std::unique_ptr directx_argb_hook{nullptr}; +std::unique_ptr directx_d3d9_createdevice_hook{nullptr}; +std::unique_ptr directx_device9_endscene_hook{nullptr}; +std::unique_ptr directx_device9_reset_hook{nullptr}; +std::unique_ptr directx_device11_swapchain_present_hook{nullptr}; + +IDirect3DPixelShader9* debug_px_shader = nullptr; +ID3DXConstantTable* debug_px_shader_constants_table = nullptr; + +IDirect3DTexture9* debug_texture = nullptr; +ID3DXConstantTable* debug_constant_table = nullptr; +IDirect3DPixelShader9* debug_shader = nullptr; +IDirect3DVertexBuffer9* debug_vertex_buffer = nullptr; +IDirect3DDevice9* current_dx_device = nullptr; +#define SAFE_RELEASE(ptr) if (ptr) { ptr->Release(); ptr = nullptr; } +void HookD3D9Device(IDirect3DDevice9* pDevice, bool force = false) noexcept; + +#define EXTRA_SAFE_RELEASE(com_ptr) \ + if (com_ptr) \ + { \ + IDirect3DDevice9* device = nullptr; \ + HRESULT result = debug_texture->GetDevice(&device); \ + if (SUCCEEDED(result) && device) \ + { \ + device->Release(); \ + com_ptr->Release(); \ + com_ptr = nullptr; \ + } \ + else \ + { \ + com_ptr = nullptr; \ + } \ + } + +bool can_render(jint srctype, jint width, jint height) +{ + extern std::unique_ptr control_center; + if (!control_center) + { + return false; + } + + std::int32_t canvas_x = 0; + std::int32_t canvas_y = 0; + std::size_t canvas_width = 0; + std::size_t canvas_height = 0; + + control_center->get_canvas_dimensions(&canvas_x, &canvas_y, &canvas_width, &canvas_height); + if (width > canvas_width || height > canvas_height) + { + return false; + } + + //Arbitrarily chosen because the java.awt.canvas should never be smaller than this value. + //Technically stuff should be rendering with a minimum of a power of 2.. so 256 * 256 for example. + //This value can change if the renderer renders in 128 * 128 chunks but I haven't met a GPU that does that.. + //OR a Java implementation either. + // - January 7th, 2021 + return width >= 200 && height >= 200; +} + +void __stdcall JavaNativeBlit(JNIEnv *env, jobject self, jobject srcData, jobject dstData, jobject comp, jobject clip, jint srcx, jint srcy, jint dstx, jint dsty, jint width, jint height) noexcept +{ + extern std::unique_ptr control_center; + if (!control_center || !can_render(-1, width, height)) + { + return native_hook->call(env, self, srcData, dstData, comp, clip, srcx, srcy, dstx, dsty, width, height); + } + + #define Region_IsRectangular(pRgnInfo) ((pRgnInfo)->endIndex == 0) + #define Region_IsEmpty(pRgnInfo) ((pRgnInfo)->bounds.x1 >= (pRgnInfo)->bounds.x2 || (pRgnInfo)->bounds.y1 >= (pRgnInfo)->bounds.y2) + #define Region_IntersectBounds(pRgnInfo, pDstBounds) SurfaceData_IntersectBounds(&(pRgnInfo)->bounds, pDstBounds) + + #define PtrAddBytes(p, b) ((void *) (((intptr_t) (p)) + (b))) + #define PtrCoord(p, x, xinc, y, yinc) PtrAddBytes(p, (y)*(yinc) + (x)*(xinc)) + + auto GetProcAddress = [](HMODULE module, const char* stdcall_name, const char* cdecl_name) -> FARPROC { + FARPROC result = ::GetProcAddress(module, stdcall_name); + return result ? result : ::GetProcAddress(module, cdecl_name); + }; + + static HMODULE module = GetModuleHandle("awt.dll"); + static NativePrimitive* (__stdcall *GetNativePrim)(JNIEnv *env, jobject gp) = reinterpret_cast(GetProcAddress(module, "_GetNativePrim@8", "GetNativePrim")); + static SurfaceDataOps* (__stdcall *SurfaceData_GetOps)(JNIEnv *env, jobject sData) = reinterpret_cast(GetProcAddress(module, "_SurfaceData_GetOps@8", "SurfaceData_GetOps")); + static jint (__stdcall *Region_GetInfo)(JNIEnv *env, jobject region, RegionData *pRgnInfo) = reinterpret_cast(GetProcAddress(module, "_Region_GetInfo@12", "Region_GetInfo")); + static void (__stdcall *SurfaceData_IntersectBounds)(SurfaceDataBounds *src, SurfaceDataBounds *dst) = reinterpret_cast(GetProcAddress(module, "_SurfaceData_IntersectBounds@8", "SurfaceData_IntersectBounds")); + static void (__stdcall *SurfaceData_IntersectBlitBounds)(SurfaceDataBounds *src, SurfaceDataBounds *dst, jint dx, jint dy) = reinterpret_cast(GetProcAddress(module, "_SurfaceData_IntersectBlitBounds@16", "SurfaceData_IntersectBlitBounds")); + static void (__stdcall *Region_StartIteration)(JNIEnv *env, RegionData *pRgnInfo) = reinterpret_cast(GetProcAddress(module, "_Region_StartIteration@8", "Region_StartIteration")); + static jint (__stdcall *Region_NextIteration)(RegionData *pRgnInfo, SurfaceDataBounds *pSpan) = reinterpret_cast(GetProcAddress(module, "_Region_NextIteration@8", "Region_NextIteration")); + static void (__stdcall *Region_EndIteration)(JNIEnv *env, RegionData *pRgnInfo) = reinterpret_cast(GetProcAddress(module, "_Region_EndIteration@8", "Region_EndIteration")); + + if (!GetNativePrim || !SurfaceData_GetOps || !Region_GetInfo || !SurfaceData_IntersectBounds || !SurfaceData_IntersectBlitBounds || !Region_StartIteration || !Region_NextIteration || !Region_EndIteration || width <= 0 || height <= 0) + { + return native_hook->call(env, self, srcData, dstData, comp, clip, srcx, srcy, dstx, dsty, width, height); + } + + CompositeInfo compInfo = {0}; + NativePrimitive* pPrim = GetNativePrim(env, self); + if (!pPrim) + { + return native_hook->call(env, self, srcData, dstData, comp, clip, srcx, srcy, dstx, dsty, width, height); + } + + if (pPrim->pCompType->getCompInfo) + { + (*pPrim->pCompType->getCompInfo)(env, &compInfo, comp); + } + + RegionData clipInfo = {0}; + if (Region_GetInfo(env, clip, &clipInfo)) + { + return native_hook->call(env, self, srcData, dstData, comp, clip, srcx, srcy, dstx, dsty, width, height); + } + + SurfaceDataOps *srcOps = SurfaceData_GetOps(env, srcData); + SurfaceDataOps *dstOps = SurfaceData_GetOps(env, dstData); + + if (!srcOps || !dstOps) + { + return native_hook->call(env, self, srcData, dstData, comp, clip, srcx, srcy, dstx, dsty, width, height); + } + + SurfaceDataRasInfo srcInfo = {0}; + SurfaceDataRasInfo dstInfo = {0}; + srcInfo.bounds.x1 = srcx; + srcInfo.bounds.y1 = srcy; + srcInfo.bounds.x2 = srcx + width; + srcInfo.bounds.y2 = srcy + height; + dstInfo.bounds.x1 = dstx; + dstInfo.bounds.y1 = dsty; + dstInfo.bounds.x2 = dstx + width; + dstInfo.bounds.y2 = dsty + height; + srcx -= dstx; + srcy -= dsty; + + SurfaceData_IntersectBounds(&dstInfo.bounds, &clipInfo.bounds); + + if (srcOps->Lock(env, srcOps, &srcInfo, pPrim->srcflags) == SD_SUCCESS) + { + jint dstFlags = pPrim->dstflags; + if (!Region_IsRectangular(&clipInfo)) + { + dstFlags |= SD_LOCK_PARTIAL_WRITE; + } + + if (dstOps->Lock(env, dstOps, &dstInfo, dstFlags) == SD_SUCCESS) + { + SurfaceData_IntersectBlitBounds(&dstInfo.bounds, &srcInfo.bounds, srcx, srcy); + Region_IntersectBounds(&clipInfo, &dstInfo.bounds); + + if (!Region_IsEmpty(&clipInfo)) + { + srcOps->GetRasInfo(env, srcOps, &srcInfo); + dstOps->GetRasInfo(env, dstOps, &dstInfo); + + if (srcInfo.rasBase && dstInfo.rasBase) + { + SurfaceDataBounds span; + jint savesx = srcInfo.bounds.x1; + jint savedx = dstInfo.bounds.x1; + Region_StartIteration(env, &clipInfo); + + while (Region_NextIteration(&clipInfo, &span)) + { + void *pSrc = PtrCoord(srcInfo.rasBase, srcx + span.x1, srcInfo.pixelStride, srcy + span.y1, srcInfo.scanStride); + void *pDst = PtrCoord(dstInfo.rasBase, span.x1, dstInfo.pixelStride, span.y1, dstInfo.scanStride); + + srcInfo.bounds.x1 = srcx + span.x1; + dstInfo.bounds.x1 = span.x1; + (*pPrim->funcs.blit)(pSrc, pDst, span.x2 - span.x1, span.y2 - span.y1, &srcInfo, &dstInfo, pPrim, &compInfo); + } + + Region_EndIteration(env, &clipInfo); + srcInfo.bounds.x1 = savesx; + dstInfo.bounds.x1 = savedx; + + void* rasBase = reinterpret_cast(srcInfo.rasBase) + (srcInfo.scanStride * srcy) + (srcInfo.pixelStride * srcx); + + control_center->set_target_dimensions(width, height); + std::uint8_t* dest = control_center->get_image(); + ImageFormat format = control_center->get_image_format(); + + //Render to Shared Memory + if (dest) + { + copy_image_from_bgra(dest, rasBase, (srcInfo.scanStride / srcInfo.pixelStride), height, srcInfo.pixelStride, format); + } + + //Render Debug Graphics + if (control_center->get_debug_graphics()) + { + std::uint8_t* src = control_center->get_debug_image(); + if (src) + { + rasBase = reinterpret_cast(dstInfo.rasBase) + (dstInfo.scanStride * dsty) + (dstInfo.pixelStride * dstx); + draw_image(rasBase, src, (dstInfo.scanStride / dstInfo.pixelStride), height, dstInfo.pixelStride, format); + } + } + + //Render Cursor + std::int32_t x = -1; + std::int32_t y = -1; + control_center->get_applet_mouse_position(&x, &y); + + if (x > -1 && y > -1) + { + rasBase = reinterpret_cast(dstInfo.rasBase) + (dstInfo.scanStride * dsty) + (dstInfo.pixelStride * dstx); + draw_circle(x, y, 2, rasBase, width, height, dstInfo.pixelStride, true, 0xFF0000FF); + } + } + } + + if (dstOps->Unlock) + { + dstOps->Unlock(env, dstOps, &dstInfo); + } + } + + if (srcOps->Unlock) + { + srcOps->Unlock(env, srcOps, &srcInfo); + } + } + + if (dstOps->Release) + { + dstOps->Release(env, dstOps, &dstInfo); + } + + if (srcOps->Release) + { + srcOps->Release(env, srcOps, &srcInfo); + } +} + +void JavaNativeOGLBlit(JNIEnv *env, void *oglc, jlong pSrcOps, jlong pDstOps, jboolean xform, jint hint, jint srctype, jboolean texture, jint sx1, jint sy1, jint sx2, jint sy2, jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2) noexcept +{ + // NOT SURE! + // I believe when I wrote this code, srctype 2 is OpenGLSurface.IntRgb = 2; //GL_BGRA + // It might be that for Windows it renders OpenGL using BGRA and not RGBA! + if (srctype != 2) + { + if (opengl_blit_hook) + { + return opengl_blit_hook->call(env, oglc, pSrcOps, pDstOps, xform, hint, srctype, texture, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2); + } + return; + } + + extern std::unique_ptr control_center; + if (control_center) + { + jint width = sx2 - sx1; + jint height = sy2 - sy1; + SurfaceDataOps *srcOps = (SurfaceDataOps *)pSrcOps; + + if (!srcOps || width <= 0 || height <= 0) + { + if (opengl_blit_hook) + { + return opengl_blit_hook->call(env, oglc, pSrcOps, pDstOps, xform, hint, srctype, texture, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2); + } + return; + } + + SurfaceDataRasInfo srcInfo = {0}; + srcInfo.bounds.x1 = sx1; + srcInfo.bounds.y1 = sy1; + srcInfo.bounds.x2 = sx2; + srcInfo.bounds.y2 = sy2; + + if (srcOps->Lock(env, srcOps, &srcInfo, SD_LOCK_READ) == SD_SUCCESS) + { + srcOps->GetRasInfo(env, srcOps, &srcInfo); + if (srcInfo.rasBase) + { + void* rasBase = reinterpret_cast(srcInfo.rasBase) + (srcInfo.scanStride * sy1) + (srcInfo.pixelStride * sx1); + bool isRasterAligned = srcInfo.scanStride % srcInfo.pixelStride == 0; //!(srcInfo.scanStride & 0x03); + + control_center->set_target_dimensions(width, height); + std::uint8_t* dest = control_center->get_image(); + ImageFormat format = control_center->get_image_format(); + + //Render to Shared Memory + if (dest) + { + if (isRasterAligned) + { + copy_image_from_bgra(dest, rasBase, (srcInfo.scanStride / srcInfo.pixelStride), height, srcInfo.pixelStride, format); + } + else + { + for (int i = 0; i < height; ++i) + { + int offset = (srcInfo.scanStride / srcInfo.pixelStride) * i; + copy_image_from_bgra(dest + offset, rasBase, (srcInfo.scanStride / srcInfo.pixelStride), 1, srcInfo.pixelStride, format); + rasBase = static_cast(reinterpret_cast(rasBase) + srcInfo.scanStride); + } + } + } + + //Render Debug Graphics + if (control_center->get_debug_graphics()) + { + std::uint8_t* src = control_center->get_debug_image(); + if (src) + { + rasBase = reinterpret_cast(srcInfo.rasBase) + (srcInfo.scanStride * sy1) + (srcInfo.pixelStride * sx1); + draw_image(rasBase, src, (srcInfo.scanStride / srcInfo.pixelStride), height, srcInfo.pixelStride, format); + } + } + + //Render Cursor + std::int32_t x = -1; + std::int32_t y = -1; + control_center->get_applet_mouse_position(&x, &y); + + if (x > -1 && y > -1) + { + rasBase = reinterpret_cast(srcInfo.rasBase) + (srcInfo.scanStride * sy1) + (srcInfo.pixelStride * sx1); + draw_circle(x, y, 2, rasBase, width, height, srcInfo.pixelStride, true, 0xFF0000FF); + } + + if (srcOps->Release) + { + srcOps->Release(env, srcOps, &srcInfo); + } + } + + if (srcOps->Unlock) + { + srcOps->Unlock(env, srcOps, &srcInfo); + } + } + } + + if (opengl_blit_hook) + { + return opengl_blit_hook->call(env, oglc, pSrcOps, pDstOps, xform, hint, srctype, texture, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2); + } +} + +void __stdcall JavaNativeOGLRenderQueueFlushBuffer(JNIEnv *env, jobject oglrq, jlong buf, jint limit) noexcept +{ + #define NEXT_VAL(buf, type) (((type *)((buf) += sizeof(type)))[-1]) + #define NEXT_INT(buf) NEXT_VAL(buf, jint) + #define NEXT_LONG(buf) NEXT_VAL(buf, jlong) + #define NEXT_DOUBLE(buf) NEXT_VAL(buf, jdouble) + + #define EXTRACT_VAL(packedval, offset, mask) \ + (((packedval) >> (offset)) & (mask)) + #define EXTRACT_BYTE(packedval, offset) \ + (unsigned char)EXTRACT_VAL(packedval, offset, 0xff) + #define EXTRACT_BOOLEAN(packedval, offset) \ + (jboolean)EXTRACT_VAL(packedval, offset, 0x1) + + static const int BLIT = 31; + static const int OFFSET_SRCTYPE = 16; + static const int OFFSET_HINT = 8; + static const int OFFSET_TEXTURE = 3; + static const int OFFSET_RTT = 2; + static const int OFFSET_XFORM = 1; + static const int OFFSET_ISOBLIT = 0; + + jlong original_buffer = buf; + + std::uint8_t* buffer = reinterpret_cast(buf); + std::uint8_t* buffer_end = reinterpret_cast(buf) + limit; + + while(buffer < buffer_end) + { + jint opcode = NEXT_INT(buffer); + + switch (opcode) + { + case BLIT: + { + jint packedParams = NEXT_INT(buffer); + jint sx1 = NEXT_INT(buffer); + jint sy1 = NEXT_INT(buffer); + jint sx2 = NEXT_INT(buffer); + jint sy2 = NEXT_INT(buffer); + jdouble dx1 = NEXT_DOUBLE(buffer); + jdouble dy1 = NEXT_DOUBLE(buffer); + jdouble dx2 = NEXT_DOUBLE(buffer); + jdouble dy2 = NEXT_DOUBLE(buffer); + jlong pSrc = NEXT_LONG(buffer); + jlong pDst = NEXT_LONG(buffer); + jint hint = EXTRACT_BYTE(packedParams, OFFSET_HINT); + jboolean texture = EXTRACT_BOOLEAN(packedParams, + OFFSET_TEXTURE); + jboolean rtt = EXTRACT_BOOLEAN(packedParams, + OFFSET_RTT); + jboolean xform = EXTRACT_BOOLEAN(packedParams, + OFFSET_XFORM); + jboolean isoblit = EXTRACT_BOOLEAN(packedParams, + OFFSET_ISOBLIT); + + if (!isoblit) + { + jint srctype = EXTRACT_BYTE(packedParams, OFFSET_SRCTYPE); + JavaNativeOGLBlit(env, nullptr, pSrc, pDst, xform, hint, srctype, texture, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2); + } + } + break; + + default: + break; + } + } + + return opengl_flush_buffer_hook->call(env, oglrq, original_buffer, limit); +} + +void __stdcall JavaNativeGDIBlit(JNIEnv *env, jobject joSelf, jobject srcData, jobject dstData, jobject clip, jint srcx, jint srcy, jint dstx, jint dsty, jint width, jint height, jint rmask, jint gmask, jint bmask, jboolean needLut) noexcept +{ + extern std::unique_ptr control_center; + if (!control_center || !can_render(-1, width, height)) + { + //Original + return native_hook->call(env, joSelf, srcData, dstData, clip, srcx, srcy, dstx, dsty, width, height, rmask, gmask, bmask, needLut); + } + + auto GetProcAddress = [](HMODULE module, const char* stdcall_name, const char* cdecl_name) -> FARPROC { + FARPROC result = ::GetProcAddress(module, stdcall_name); + return result ? result : ::GetProcAddress(module, cdecl_name); + }; + + //Setup Function Pointers + static HMODULE module = GetModuleHandle("awt.dll"); + static SurfaceDataOps* (__stdcall *SurfaceData_GetOps)(JNIEnv *env, jobject sData) = reinterpret_cast(GetProcAddress(module, "_SurfaceData_GetOps@8", "SurfaceData_GetOps")); + static GDIWinSDOps* (__stdcall *GDIWindowSurfaceData_GetOps)(JNIEnv *env, jobject sData) = reinterpret_cast(GetProcAddress(module, "_GDIWindowSurfaceData_GetOps@8", "GDIWindowSurfaceData_GetOps")); + static void (__stdcall *SurfaceData_IntersectBlitBounds)(SurfaceDataBounds *src, SurfaceDataBounds *dst, jint dx, jint dy) = reinterpret_cast(GetProcAddress(module, "_SurfaceData_IntersectBlitBounds@16", "SurfaceData_IntersectBlitBounds")); + + if (!SurfaceData_GetOps || !GDIWindowSurfaceData_GetOps || !SurfaceData_IntersectBlitBounds) + { + //Original + return native_hook->call(env, joSelf, srcData, dstData, clip, srcx, srcy, dstx, dsty, width, height, rmask, gmask, bmask, needLut); + } + + //Setup Source Info + SurfaceDataOps* srcOps = SurfaceData_GetOps(env, srcData); + SurfaceDataRasInfo srcInfo = {0}; + srcInfo.bounds.x1 = srcx; + srcInfo.bounds.y1 = srcy; + srcInfo.bounds.x2 = srcx + width; + srcInfo.bounds.y2 = srcy + height; + + if (srcOps->Lock(env, srcOps, &srcInfo, needLut ? (SD_LOCK_READ | SD_LOCK_LUT) : SD_LOCK_READ) == SD_SUCCESS) + { + //Setup Destination Clipping Info + GDIWinSDOps* dstOps = GDIWindowSurfaceData_GetOps(env, dstData); + SurfaceDataBounds dstBounds = {dstx, dsty, dstx + width, dsty + height}; + SurfaceData_IntersectBlitBounds(&(srcInfo.bounds), &dstBounds, dstx - srcx, dsty - srcy); + srcx = srcInfo.bounds.x1; + srcy = srcInfo.bounds.y1; + dstx = dstBounds.x1; + dsty = dstBounds.y1; + width = srcInfo.bounds.x2 - srcInfo.bounds.x1; + height = srcInfo.bounds.y2 - srcInfo.bounds.y1; + + if (width > 0 && height > 0) + { + srcOps->GetRasInfo(env, srcOps, &srcInfo); + if (srcInfo.rasBase) + { + void* rasBase = reinterpret_cast(srcInfo.rasBase) + (srcInfo.scanStride * srcy) + (srcInfo.pixelStride * srcx); + bool isRasterAligned = !(srcInfo.scanStride & 0x03); + + control_center->set_target_dimensions(width, height); + std::uint8_t* dest = control_center->get_image(); + ImageFormat format = control_center->get_image_format(); + + //Render to Shared Memory + if (dest) + { + if (isRasterAligned) + { + copy_image_from_bgra(dest, rasBase, (srcInfo.scanStride / srcInfo.pixelStride), height, srcInfo.pixelStride, format); + } + else + { + for (int i = 0; i < height; ++i) + { + int offset = (srcInfo.scanStride / srcInfo.pixelStride) * i; + copy_image_from_bgra(dest + offset, rasBase, (srcInfo.scanStride / srcInfo.pixelStride), 1, srcInfo.pixelStride, format); + rasBase = static_cast(reinterpret_cast(rasBase) + srcInfo.scanStride); + } + } + } + + HDC hDC = dstOps->GetDC(env, dstOps, 0, nullptr, clip, nullptr, 0); + if (hDC) + { + //Clone Renderer + static jint screenWidth = 0; + static jint screenHeight = 0; + static std::unique_ptr screenBuffer = nullptr; + + if (screenWidth < width || screenHeight < height) + { + screenWidth = width; + screenHeight = height; + screenBuffer = std::make_unique((srcInfo.scanStride / srcInfo.pixelStride) * height * srcInfo.pixelStride); + } + + if (screenBuffer) + { + std::memcpy(screenBuffer.get(), rasBase, (srcInfo.scanStride / srcInfo.pixelStride) * height * srcInfo.pixelStride); + + //Render Debug Graphics + if (control_center->get_debug_graphics()) + { + std::uint8_t* src = control_center->get_debug_image(); + if (src) + { + draw_image(screenBuffer.get(), src, (srcInfo.scanStride / srcInfo.pixelStride), height, srcInfo.pixelStride, format); + } + } + + //Render Cursor + std::int32_t x = -1; + std::int32_t y = -1; + control_center->get_applet_mouse_position(&x, &y); + + if (x > -1 && y > -1) + { + static const std::int32_t radius = 2; + draw_circle(x, y, radius, screenBuffer.get(), (srcInfo.scanStride / srcInfo.pixelStride), height, 4, true, 0xFF0000FF); + } + } + + //Render Screen + BmiType bmi = {0}; + long dwHeight = srcInfo.bounds.y2 - srcInfo.bounds.y1; + bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); + bmi.bmiHeader.biWidth = srcInfo.scanStride / srcInfo.pixelStride; + bmi.bmiHeader.biHeight = isRasterAligned ? -dwHeight : -1; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = srcInfo.pixelStride * 8; + bmi.bmiHeader.biCompression = srcInfo.pixelStride != 2 ? BI_RGB : BI_BITFIELDS; + bmi.bmiHeader.biSizeImage = (bmi.bmiHeader.biWidth * dwHeight * srcInfo.pixelStride); + bmi.bmiHeader.biXPelsPerMeter = 0; + bmi.bmiHeader.biYPelsPerMeter = 0; + bmi.bmiHeader.biClrUsed = 0; + bmi.bmiHeader.biClrImportant = 0; + bmi.colors.dwMasks[0] = 0x00FF0000; + bmi.colors.dwMasks[1] = 0x0000FF00; + bmi.colors.dwMasks[2] = 0x000000FF; + + rasBase = screenBuffer.get(); + + if (isRasterAligned) + { + if (::IsWindowVisible(dstOps->window)) + { + SetDIBitsToDevice(hDC, dstx, dsty, width, height, 0, 0, 0, height, rasBase, reinterpret_cast(&bmi), DIB_RGB_COLORS); + } + } + else + { + for (jint i = 0; i < height; i += 1) + { + if (::IsWindowVisible(dstOps->window)) + { + SetDIBitsToDevice(hDC, dstx, dsty + i, width, 1, 0, 0, 0, 1, rasBase, reinterpret_cast(&bmi), DIB_RGB_COLORS); + rasBase = static_cast(reinterpret_cast(rasBase) + srcInfo.scanStride); + } + else + { + break; + } + } + } + + dstOps->ReleaseDC(env, dstOps, hDC); + } + + if (srcOps->Release) + { + srcOps->Release(env, srcOps, &srcInfo); + } + } + } + + if (srcOps->Unlock) + { + srcOps->Unlock(env, srcOps, &srcInfo); + } + } +} + +HRESULT __cdecl JavaDirectXCopyImageToIntArgbSurface(IDirect3DSurface9 *pSurface, SurfaceDataRasInfo *pDstInfo, jint srcx, jint srcy, jint srcWidth, jint srcHeight, jint dstx, jint dsty) noexcept +{ + extern std::unique_ptr control_center; + if (!control_center) + { + //Original + return directx_xrgb_hook->call(pSurface, pDstInfo, srcx, srcy, srcWidth, srcHeight, dstx, dsty); + } + + IDirect3DDevice9* pDevice = nullptr; + if (SUCCEEDED(pSurface->GetDevice(&pDevice)) && pDevice) + { + HookD3D9Device(pDevice); + pDevice->Release(); + } + + return directx_xrgb_hook->call(pSurface, pDstInfo, srcx, srcy, srcWidth, srcHeight, dstx, dsty); +} + +//Java_sun_java2d_d3d_D3DRenderQueue_flushBuffer -> D3DRQ_FlushBuffer -> sun_java2d_pipe_BufferedOpCodes_BLIT -> D3DBlitLoops_Blit -> D3DBlitSwToTexture -> D3DBL_CopyImageToIntXrgbSurface +//Java_sun_java2d_d3d_D3DRenderQueue_flushBuffer -> D3DRQ_FlushBuffer -> sun_java2d_pipe_BufferedOpCodes_BLIT -> D3DBlitLoops_Blit -> D3DBlitToSurfaceViaTexture -> D3DBL_CopyImageToIntXrgbSurface +HRESULT __cdecl JavaDirectXCopyImageToIntXrgbSurface(SurfaceDataRasInfo *pSrcInfo, int srctype, D3DResource *pDstSurfaceRes, jint srcx, jint srcy, jint srcWidth, jint srcHeight, jint dstx, jint dsty) noexcept +{ + extern std::unique_ptr control_center; + if (!control_center) + { + //Original + return directx_xrgb_hook->call(pSrcInfo, srctype, pDstSurfaceRes, srcx, srcy, srcWidth, srcHeight, dstx, dsty); + } + + //Retrieve data pointers and v-table from the D3DResource structure.. + auto get_data_pointer = [](D3DResource* base) -> void* { + return reinterpret_cast((reinterpret_cast(base) + sizeof(D3DResource)) - sizeof(D3DSURFACE_DESC) - (sizeof(void*) * 5)); + }; + + auto get_offset = [](void* data_ptr, std::uintptr_t index) -> void* { + return *reinterpret_cast(reinterpret_cast(data_ptr) + (sizeof(void*) * index)); + }; + + if (pDstSurfaceRes) + { + void* base_ptr = get_data_pointer(pDstSurfaceRes); + IDirect3DSurface9* pSurface = reinterpret_cast(get_offset(base_ptr, 2)); + + IDirect3DDevice9* pDevice = nullptr; + if (pSurface && SUCCEEDED(pSurface->GetDevice(&pDevice)) && pDevice) + { + HookD3D9Device(pDevice); + pDevice->Release(); + } + } + + return directx_xrgb_hook->call(pSrcInfo, srctype, pDstSurfaceRes, srcx, srcy, srcWidth, srcHeight, dstx, dsty); +} +#endif + +#if defined(_WIN32) || defined(_WIN64) +void (__stdcall *glGenBuffers) (GLsizei n, GLuint *buffers); +void (__stdcall *glDeleteBuffers) (GLsizei n, const GLuint* buffers); +void (__stdcall *glBindBuffer) (GLenum target, GLuint buffer); +void (__stdcall *glBufferData) (GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage); +void* (__stdcall *glMapBuffer)(GLenum target, GLenum access); +GLboolean (__stdcall *glUnmapBuffer)(GLenum target); + +bool IsGLExtensionsSupported(HDC hdc, std::string extension) noexcept +{ + typedef std::uintptr_t GLsizeiptr; + auto wglGetProcAddress = [](const char* a, const char* b) -> FARPROC { + FARPROC result = ::wglGetProcAddress(a); + return result ? result : ::wglGetProcAddress(b); + }; + + static std::vector extensions; + static const char* (__stdcall *wglGetExtensionsString)(HDC hdc) = reinterpret_cast(wglGetProcAddress("wglGetExtensionsString", "wglGetExtensionsStringARB")); + + if (wglGetExtensionsString && hdc && extensions.empty()) + { + auto split = [](const std::string& text, const std::string& delims) { + std::vector tokens; + std::size_t start = text.find_first_not_of(delims), end = 0; + + while((end = text.find_first_of(delims, start)) != std::string::npos) + { + tokens.push_back(text.substr(start, end - start)); + start = text.find_first_not_of(delims, end); + } + + if(start != std::string::npos) + { + tokens.push_back(text.substr(start)); + } + return tokens; + }; + + extensions = split(wglGetExtensionsString(hdc), " "); + std::sort(extensions.begin(), extensions.end()); + } + + return std::binary_search(extensions.begin(), extensions.end(), extension); +} + +bool LoadOpenGLExtensions() noexcept +{ + if (!glGenBuffers) + { + glGenBuffers = reinterpret_cast(wglGetProcAddress("glGenBuffers")); + glDeleteBuffers = reinterpret_cast(wglGetProcAddress("glDeleteBuffers")); + glBindBuffer = reinterpret_cast(wglGetProcAddress("glBindBuffer")); + glBufferData = reinterpret_cast(wglGetProcAddress("glBufferData")); + glMapBuffer = reinterpret_cast(wglGetProcAddress("glMapBuffer")); + glUnmapBuffer = reinterpret_cast(wglGetProcAddress("glUnmapBuffer")); + } + + return glGenBuffers && glDeleteBuffers && glBindBuffer && glBufferData && glMapBuffer && glUnmapBuffer; +} + +void GeneratePixelBuffers(void* ctx, GLuint (&pbo)[2], GLint width, GLint height, GLint stride) noexcept +{ + static int w = 0; + static int h = 0; + static void* current_ctx = nullptr; + + #if defined(__APPLE__) + CGLContextObj CGL_MACRO_CONTEXT = static_cast(ctx); + #endif + + //Buffer size changed + if (w != width || h != height || current_ctx != ctx) + { + w = width; + h = height; + current_ctx = ctx; + + //If buffers already exist, clean them up + if (pbo[1] != 0) + { + glDeleteBuffers(2, pbo); + pbo[0] = 0; + pbo[1] = 0; + } + + //Generate buffers + glGenBuffers(2, pbo); + glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo[0]); + glBufferData(GL_PIXEL_PACK_BUFFER, width * height * stride, nullptr, GL_STREAM_READ); + glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); + + glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo[1]); + glBufferData(GL_PIXEL_PACK_BUFFER, width * height * stride, nullptr, GL_STREAM_READ); + glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); + } +} + +void ReadPixelBuffers(void* ctx, GLubyte* dest, GLuint (&pbo)[2], GLint width, GLint height, GLint stride, ImageFormat format) noexcept +{ + static int index = 0; + static int nextIndex = 0; + static void* last_ctx = nullptr; + + #if defined(__APPLE__) + CGLContextObj CGL_MACRO_CONTEXT = static_cast(ctx); + #endif + + if (last_ctx != ctx) + { + last_ctx = ctx; + index = 0; + nextIndex = 0; + } + + GLenum gl_format = [](ImageFormat format) -> GLenum { + switch(format) + { + case ImageFormat::BGR_BGRA: return GL_BGRA; + case ImageFormat::BGRA: return GL_BGRA; + case ImageFormat::RGBA: return GL_RGBA; + case ImageFormat::ARGB: return 0; // Not Supported + case ImageFormat::ABGR: return 0; // Not Supported + default: return GL_BGRA; + } + }(format); + + //Swap indices + index = (index + 1) % 2; + nextIndex = (index + 1) % 2; + + //Read back-buffer. + glPixelStorei(GL_UNPACK_ROW_LENGTH, width); + glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo[index]); + glReadPixels(0, 0, width, height, gl_format, GL_UNSIGNED_BYTE, nullptr); + glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo[nextIndex]); + + void* data = glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY); + + if (data) + { + //memcpy(dest, data, width * height * 4); + FlipImageBytes(data, reinterpret_cast(dest), width, height, 32); + glUnmapBuffer(GL_PIXEL_PACK_BUFFER); + } + else + { + glReadPixels(0, 0, width, height, gl_format, GL_UNSIGNED_BYTE, dest); + } + + //Restore state + glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); +} + +void PushGLContext(HDC hdc, GLint width, GLint height) noexcept +{ + //Create a context if none exists.. + static std::unordered_map contexts; + int pixelformat = GetPixelFormat(hdc); + if (!contexts.count(pixelformat)) + { + HGLRC ctx = wglCreateContext(hdc); + contexts[pixelformat] = ctx; + } + + //Set the current context + wglMakeCurrent(hdc, contexts[pixelformat]); + + //Push current states + glPushAttrib(GL_ALL_ATTRIB_BITS); + glPushMatrix(); + glViewport(0, 0, width, height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, width, 0, height, -1, 1); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glDisable(GL_DEPTH_TEST); +} + +void PopGLContext(HDC hdc, HGLRC ctx) noexcept +{ + glPopMatrix(); + glPopAttrib(); + wglMakeCurrent(hdc, ctx); +} + +void __stdcall mglDrawPixels(GLsizei width, GLsizei height, GLenum format, GLenum type, const void* data) noexcept +{ + extern std::unique_ptr control_center; + + if (control_center) + { + int src_x = 0; + int src_y = 0; + int bytes_per_pixel = 4; //TODO: Parse format //can_render(format == GL_RGBA ? 3 : -1, width, height) + int stride = width * bytes_per_pixel; + void *rasBase = static_cast(const_cast(data)) + (stride * src_y) + (bytes_per_pixel * src_x); + + control_center->set_target_dimensions(width, height); + + //Render to Shared Memory + std::uint8_t* dest = control_center->get_image(); + ImageFormat format = control_center->get_image_format(); + if (dest) + { + copy_image_from_bgra(dest, rasBase, (stride / bytes_per_pixel), height, bytes_per_pixel, format); + } + + //Render Debug Graphics + if (control_center->get_debug_graphics()) + { + std::uint8_t* src = control_center->get_debug_image(); + if (src) + { + draw_image(rasBase, src, width, height, bytes_per_pixel, format); + } + } + + //Render Cursor + std::int32_t x = -1; + std::int32_t y = -1; + control_center->get_applet_mouse_position(&x, &y); + + if (x > -1 && y > -1) + { + draw_circle(x, y, 3, rasBase, width, height, bytes_per_pixel, true, 0xFF0000FF); + } + } + + if (opengl_swap_hook) + { + opengl_swap_hook->call(width, height, format, type, data); + } +} + +BOOL __stdcall mSwapBuffers(HDC hdc) noexcept +{ + extern std::unique_ptr control_center; + + if (control_center) + { + static GLint ViewPort[4] = {0}; + static GLuint pbo[2] = {0}; + + glGetIntegerv(GL_VIEWPORT, ViewPort); + GLint width = ViewPort[2] - ViewPort[0]; + GLint height = ViewPort[3] - ViewPort[1]; + + std::int32_t canvas_x = 0; + std::int32_t canvas_y = 0; + std::size_t canvas_width = 0; + std::size_t canvas_height = 0; + + control_center->get_canvas_dimensions(&canvas_x, &canvas_y, &canvas_width, &canvas_height); + control_center->set_target_dimensions(canvas_width, canvas_height); + + // Do not render on an invalid canvas + if (width != canvas_width || height != canvas_height) + { + if (opengl_swap_hook) + { + return opengl_swap_hook->call(hdc); + } + return false; + } + + //Check if extensions are supported + //This check is needed for renderers that do not support pixel buffer objects or vertex buffer objects + //static bool hasGLExtension = IsGLExtensionsSupported(hdc, "GL_ARB_vertex_buffer_object") || IsGLExtensionsSupported(hdc, "GL_ARB_pixel_buffer_object"); + + // The above extension check is unreliable! + // It's best to attempt to load the extensions and see if they exist. + static bool hasGLExtension = LoadOpenGLExtensions(); + + //Render to Shared Memory + std::uint8_t* dest = control_center->get_image(); + ImageFormat format = control_center->get_image_format(); + if (dest) + { + if (hasGLExtension) + { + //Performance Boost! :D + GeneratePixelBuffers(hdc, pbo, width, height, 4); + ReadPixelBuffers(hdc, dest, pbo, width, height, 4, format); + } + else + { + GLenum gl_format = [](ImageFormat format) -> GLenum { + switch(format) + { + case ImageFormat::BGR_BGRA: return GL_BGRA; + case ImageFormat::BGRA: return GL_BGRA; + case ImageFormat::RGBA: return GL_RGBA; + case ImageFormat::ARGB: return 0; // Not Supported + case ImageFormat::ABGR: return 0; // Not Supported + default: return GL_BGRA; + } + }(format); + + //Sad rendering implementation + glReadPixels(0, 0, width, height, gl_format, GL_UNSIGNED_BYTE, dest); + FlipImageVertically(width, height, dest); + } + } + + //Push Rendering Context + HGLRC old_ctx = wglGetCurrentContext(); + if (old_ctx) + { + PushGLContext(hdc, width, height); + } + + //Render Debug Graphics + if (control_center->get_debug_graphics()) + { + std::uint8_t* src = control_center->get_debug_image(); + if (src) + { + gl_draw_image(hdc, src, 0, 0, width, height, 4, format); + } + } + + //Render Cursor + std::int32_t x = -1; + std::int32_t y = -1; + control_center->get_applet_mouse_position(&x, &y); + + if (x > -1 && y > -1) + { + glColor4ub(0xFF, 0x00, 0x00, 0xFF); + gl_draw_point(hdc, x, y, 0, 4); + } + + //Pop Rendering Context + if (old_ctx) + { + PopGLContext(hdc, old_ctx); + } + } + + //Original + if (opengl_swap_hook) + { + return opengl_swap_hook->call(hdc); + } + return false; +} +#endif // defined + +#if defined(_WIN32) || defined(_WIN64) +HRESULT __stdcall D3D11Device_Present(IDXGISwapChain* pThis, UINT SyncInterval, UINT Flags) +{ + if (directx_device11_swapchain_present_hook) + { + return directx_device11_swapchain_present_hook->call(pThis, SyncInterval, Flags); + } + return E_FAIL; +} + +HRESULT __stdcall D3D9_CreateDevice(IDirect3D9* pD3D, UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS* pPresentationParameters, IDirect3DDevice9** ppReturnedDeviceInterface) noexcept +{ + if (directx_d3d9_createdevice_hook) + { + HRESULT result = directx_d3d9_createdevice_hook->call(pD3D, Adapter, DeviceType, hFocusWindow, BehaviorFlags, pPresentationParameters, ppReturnedDeviceInterface); + if (ppReturnedDeviceInterface && *ppReturnedDeviceInterface) + { + HookD3D9Device(*ppReturnedDeviceInterface); + } + return result; + } + + return E_FAIL; +} + +HRESULT __stdcall D3D9Device_EndScene(IDirect3DDevice9* device) noexcept +{ + extern std::unique_ptr control_center; + + if (control_center) + { + D3DVIEWPORT9 viewport; + if (SUCCEEDED(device->GetViewport(&viewport))) + { + std::int32_t x = 0; + std::int32_t y = 0; + std::int32_t width = static_cast(viewport.Width); + std::int32_t height = static_cast(viewport.Height); + + std::int32_t canvas_x = 0; + std::int32_t canvas_y = 0; + std::size_t canvas_width = 0; + std::size_t canvas_height = 0; + + control_center->get_canvas_dimensions(&canvas_x, &canvas_y, &canvas_width, &canvas_height); + control_center->set_target_dimensions(canvas_width, canvas_height); + + // Do not render on an invalid canvas + if (width != canvas_width || height != canvas_height) + { + if (directx_device9_endscene_hook) + { + return directx_device9_endscene_hook->call(device); + } + + return E_FAIL; + } + + bool minimized = false; + ImageFormat image_format = control_center->get_image_format(); + dx_read_pixels(device, control_center->get_image(), canvas_width, canvas_height, minimized, image_format); + + IDirect3DStateBlock9* block; + device->CreateStateBlock(D3DSBT_ALL, &block); + block->Capture(); + + device->SetRenderState(D3DRS_LIGHTING, FALSE); + device->SetRenderState(D3DRS_FOGENABLE, FALSE); + device->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE); + device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); + device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + device->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD); + device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); + device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); + + if (control_center->get_debug_graphics() && !minimized) + { + dx_load_texture(device, debug_texture, debug_px_shader, debug_px_shader_constants_table, image_format, control_center->get_debug_image(), canvas_width, canvas_height); + + if (debug_texture) + { + dx_draw_texture(device, debug_texture, debug_constant_table, debug_shader, debug_vertex_buffer, image_format, 0.0, 0.0, static_cast(canvas_width), static_cast(canvas_height)); + } + } + + //Render Cursor + if (!minimized) + { + x = -1; + y = -1; + control_center->get_applet_mouse_position(&x, &y); + + if (x > -1 && y > -1 && x <= width && y <= height) + { + device->SetTexture(0, nullptr); + dx_draw_point(device, static_cast(x), static_cast(y), 2.5f, D3DCOLOR_RGBA(0xFF, 0x00, 0x00, 0xFF)); + } + } + + device->SetRenderState(D3DRS_ZFUNC,D3DCMP_NEVER); + device->SetTexture(0, nullptr); + device->SetPixelShader(nullptr); + device->SetVertexShader(nullptr); + block->Apply(); + block->Release(); + } + } + + if (directx_device9_endscene_hook) + { + return directx_device9_endscene_hook->call(device); + } + + return E_FAIL; +} + +HRESULT __stdcall D3D9Device_Reset(IDirect3DDevice9* device, D3DPRESENT_PARAMETERS* pPresentationParameters) noexcept { + if (directx_device9_reset_hook) + { + HRESULT result = directx_device9_reset_hook->call(device, pPresentationParameters); + HookD3D9Device(device, true); + return result; + } + + return E_FAIL; +} + +void HookD3D9Device(IDirect3DDevice9* pDevice, bool force) noexcept +{ + if (!pDevice) + { + return; + } + + if (!force && current_dx_device == pDevice) + { + return; + } + + bool device_lost = false; + if (current_dx_device) + { + HRESULT result = current_dx_device->TestCooperativeLevel(); + device_lost = (result == D3DERR_DEVICELOST || result == D3DERR_DEVICENOTRESET); + + if (device_lost) + { + fprintf(stderr, "DEVICE LOST\b"); + } + } + + SAFE_RELEASE(current_dx_device); + current_dx_device = pDevice; + current_dx_device->AddRef(); + + auto* vTable = *reinterpret_cast(pDevice); + + // Release the debug interfaces + EXTRA_SAFE_RELEASE(debug_px_shader); + EXTRA_SAFE_RELEASE(debug_px_shader_constants_table); + EXTRA_SAFE_RELEASE(debug_vertex_buffer); + EXTRA_SAFE_RELEASE(debug_shader); + EXTRA_SAFE_RELEASE(debug_constant_table); + EXTRA_SAFE_RELEASE(debug_texture); + + // Hook EndScene + auto* endscene = reinterpret_cast(vTable[42]); + directx_device9_endscene_hook = std::make_unique(reinterpret_cast(endscene), reinterpret_cast(D3D9Device_EndScene)); + directx_device9_endscene_hook->apply(); + + // Hook Reset + auto* reset = reinterpret_cast(vTable[16]); + directx_device9_reset_hook = std::make_unique(reinterpret_cast(reset), reinterpret_cast(D3D9Device_Reset)); + directx_device9_reset_hook->apply(); + + // Validate that functions were actually hooked + if (!directx_device9_endscene_hook->is_enabled()) + { + current_dx_device = nullptr; + directx_device9_endscene_hook.reset(); + } + + if (!directx_device9_reset_hook->is_enabled()) + { + current_dx_device = nullptr; + directx_device9_reset_hook.reset(); + } +} +#endif // defined + +#if defined(_WIN32) || defined(_WIN64) +std::unique_ptr cp_hook; +BOOL WINAPI CreateProcessWHook(LPCWSTR lpApplicationName, LPWSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation) { + + std::wcout<hStdOutput && GetFileType(lpStartupInfo->hStdOutput) == FILE_TYPE_PIPE) + { + SECURITY_ATTRIBUTES attributes = {0}; + attributes.nLength = sizeof(SECURITY_ATTRIBUTES); + attributes.bInheritHandle = true; + attributes.lpSecurityDescriptor = nullptr; + + if (CreatePipe(&read, &write, &attributes, 0)) + { + SetHandleInformation(read, HANDLE_FLAG_INHERIT, 0); + + originalHandle = lpStartupInfo->hStdOutput; + lpStartupInfo->hStdOutput = write; + } + } + } + + BOOL res = cp_hook->call(lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation); + + if (originalHandle) + { + WaitForSingleObject(lpProcessInformation->hProcess, INFINITE); + CloseHandle(write); + + #ifdef USE_REAL_HARDWARE_ID + char buffer[256] = {0}; + DWORD dwRead = 0; + DWORD dwWritten = 0; + + std::string result; + + while (true) + { + bool success = ReadFile(read, buffer, 256, &dwRead, nullptr); + if (!success || dwRead == 0) + { + break; + } + + result += std::string(buffer, dwRead); + } + #else + + // Return Fake UUID-v4 + DWORD dwWritten = 0; + std::string result = "UUID"; // Header + + // Padding + for (int i = 0; i < 34; ++i) + { + result += '\x20'; + } + + result += "\xd\xd\n"; + result += "4DDF2995-1CC6-4E70-9463-D17914D9A13F"; // The UUID-v4 + result +="\x20\x20\xd\xd\n\xd\xd\n"; + #endif + + bool success = WriteFile(originalHandle, &result[0], result.size(), &dwWritten, nullptr); + if (!success) + { + std::cout<<"FAILED TO WRITE: "<((void *) GetProcAddress(mm, "CreateProcessW"), (void *) CreateProcessWHook); +// cp_hook->apply(); + + #if defined(USE_DETOURS) + HMODULE module = GetModuleHandle("awt.dll"); + + auto GetProcAddress = [](HMODULE module, const char* stdcall_name, const char* cdecl_name) -> FARPROC { + FARPROC result = ::GetProcAddress(module, stdcall_name); + return result ? result : ::GetProcAddress(module, cdecl_name); + }; + + //Hook X11 Blit + void* blit = reinterpret_cast(GetProcAddress(module, "_Java_sun_java2d_windows_GDIBlitLoops_nativeBlit@60", "Java_sun_java2d_windows_GDIBlitLoops_nativeBlit")); + if (blit) + { + native_hook = std::make_unique(reinterpret_cast(blit), reinterpret_cast(JavaNativeGDIBlit)); + native_hook->apply(); + } + else + { + //_Java_sun_java2d_loops_ScaledBlit_Scale@72 //Java_sun_java2d_loops_ScaledBlit_Scale + blit = reinterpret_cast(GetProcAddress(module, "_Java_sun_java2d_loops_Blit_Blit@48", "Java_sun_java2d_loops_Blit_Blit")); + if (blit) + { + native_hook = std::make_unique(reinterpret_cast(blit), reinterpret_cast(JavaNativeBlit)); + native_hook->apply(); + } + } + + // Some time between Java 20 and 23+, the following functions are no longer exported + // OGLBlitLoops_Blit + // Only Java_sun_java2d_opengl_OGLRenderQueue_flushBuffer is exported now + + // Hook OpenGL Blit + #if defined(HOOK_OPENGL_BLIT) + blit = reinterpret_cast(::GetProcAddress(module, "OGLBlitLoops_Blit")); + if (blit) + { + opengl_blit_hook = std::make_unique(reinterpret_cast(blit), reinterpret_cast(JavaNativeOGLBlit)); + opengl_blit_hook->apply(); + } + else + { + blit = reinterpret_cast(GetProcAddress(module, "_Java_sun_java2d_opengl_OGLRenderQueue_flushBuffer@20", "Java_sun_java2d_opengl_OGLRenderQueue_flushBuffer")); + if (blit) + { + opengl_flush_buffer_hook = std::make_unique(reinterpret_cast(blit), reinterpret_cast(JavaNativeOGLRenderQueueFlushBuffer)); + opengl_flush_buffer_hook->apply(); + } + } + #else + if (blit) + { + HMODULE module = GetModuleHandle("opengl32.dll"); + if (module) + { + #if defined(HOOK_OPENGL_SWAP) + blit = reinterpret_cast(::GetProcAddress(module, "wglSwapBuffers")); + opengl_swap_hook = std::make_unique(reinterpret_cast(blit), reinterpret_cast(mSwapBuffers)); + opengl_swap_hook->apply(); + #else + blit = reinterpret_cast(::GetProcAddress(module, "glDrawPixels")); + opengl_swap_hook = std::make_unique(reinterpret_cast(blit), reinterpret_cast(mglDrawPixels)); + opengl_swap_hook->apply(); + #endif + } + } + #endif + + // Some time between Java 20 and 23+, the following functions are no longer exported + // D3DBL_CopyImageToIntXrgbSurface + // D3DBL_CopySurfaceToIntArgbImage + // D3DBlitLoops_IsoBlit + // D3DBlitToSurfaceViaTexture + // Only Java_sun_java2d_d3d_D3DRenderQueue_flushBuffer is exported now + + // Hook DirectX Surface Blit + blit = reinterpret_cast(GetProcAddress(module, "?D3DBL_CopyImageToIntXrgbSurface@@YAJPAUSurfaceDataRasInfo@@HPAVD3DResource@@JJJJJJ@Z", "?D3DBL_CopyImageToIntXrgbSurface@@YAJPEAUSurfaceDataRasInfo@@HPEAVD3DResource@@JJJJJJ@Z")); + if (blit) + { + directx_xrgb_hook = std::make_unique(reinterpret_cast(blit), reinterpret_cast(JavaDirectXCopyImageToIntXrgbSurface)); + directx_xrgb_hook->apply(); + } + + blit = reinterpret_cast(GetProcAddress(module, "D3DBL_CopySurfaceToIntArgbImage@@YAJPAUIDirect3DSurface9@@PAUSurfaceDataRasInfo@@JJJJJJ@Z", "?D3DBL_CopySurfaceToIntArgbImage@@YAJPEAUIDirect3DSurface9@@PEAUSurfaceDataRasInfo@@JJJJJJ@Z")); + if (blit) + { + directx_argb_hook = std::make_unique(reinterpret_cast(blit), reinterpret_cast(JavaDirectXCopyImageToIntArgbSurface)); + directx_argb_hook->apply(); + } + + // Direct-X Hooks + + #if defined(HOOK_D3D11) + bool hook_d3d11 = true; + #else + bool hook_d3d11 = false; + #endif + + if (!GetModuleHandle(hook_d3d11 ? "d3d11.dll" : "d3d9.dll")) + { + return; + } + + extern std::unique_ptr control_center; + HWND main_window = reinterpret_cast(control_center->reflect_frame_native_handle()); + if (!main_window) + { + return; + } + + if (hook_d3d11) + { + HMODULE d3d11_module = GetModuleHandle("d3d11.dll"); + auto pD3D11CreateDeviceAndSwapChain = reinterpret_cast(::GetProcAddress(d3d11_module, "D3D11CreateDeviceAndSwapChain")); + + DXGI_SWAP_CHAIN_DESC sd {0}; + sd.BufferCount = 1; + sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + sd.OutputWindow = main_window; + sd.Windowed = true; + sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; + sd.SampleDesc.Count = 1; + + ID3D11Device* pDevice = nullptr; + IDXGISwapChain* pSwapChain = nullptr; + + HRESULT hr = pD3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, 0, nullptr, 0, D3D11_SDK_VERSION, &sd, &pSwapChain, &pDevice, nullptr, nullptr); + if (FAILED(hr)) + { + SAFE_RELEASE(pSwapChain); + SAFE_RELEASE(pDevice); + return; + } + + auto* vTable = *reinterpret_cast(pSwapChain); + + // Hook Present + auto* present = reinterpret_cast(vTable[8]); + directx_device11_swapchain_present_hook = std::make_unique(reinterpret_cast(present), reinterpret_cast(D3D11Device_Present)); + directx_device11_swapchain_present_hook->apply(); + + SAFE_RELEASE(pSwapChain); + SAFE_RELEASE(pDevice); + } + else + { + HMODULE d3d9_module = GetModuleHandle("d3d9.dll"); + auto pDirect3DCreate9 = reinterpret_cast(::GetProcAddress(d3d9_module, "Direct3DCreate9")); + + IDirect3D9* pD3D = pDirect3DCreate9(D3D_SDK_VERSION); + if (!pD3D) + { + return; + } + + // Hook Create Device which will automatically hook EndScene + auto* vTable = *reinterpret_cast(pD3D); + auto* create_device = reinterpret_cast(vTable[16]); + directx_d3d9_createdevice_hook = std::make_unique(reinterpret_cast(create_device), reinterpret_cast(D3D9_CreateDevice)); + directx_d3d9_createdevice_hook->apply(); + + // Hook Initial Device + D3DDISPLAYMODE display_mode; + HRESULT hr = pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &display_mode); + if (SUCCEEDED(hr)) + { + D3DPRESENT_PARAMETERS d3dpp = {0}; + d3dpp.Windowed = true; + d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; + d3dpp.BackBufferCount = 1; + d3dpp.BackBufferFormat = display_mode.Format; + d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE; + d3dpp.hDeviceWindow = main_window; + + IDirect3DDevice9* pDevice = nullptr; + pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, main_window, D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_MULTITHREADED, &d3dpp, &pDevice); + + // pDevice be hooked via CreateDevice hook + SAFE_RELEASE(pDevice); + } + + SAFE_RELEASE(pD3D); + } + #endif +} + +void StartHook() noexcept +{ + Hook::setup(); + InitialiseHooks(); + + //Signal that all hooks are finished initializing.. + ControlCenter::signal_sync(getpid()); +} + +/*void limit_render(std::function render, uint32_t max_frames) noexcept +{ + render(); + yield(std::chrono::milliseconds(1000 / max_frames)); +}*/ +#endif diff --git a/third-party/RemoteInput/RemoteInput/Platform/Platform.hxx b/third-party/RemoteInput/RemoteInput/Platform/Platform.hxx new file mode 100644 index 0000000..79b8429 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Platform/Platform.hxx @@ -0,0 +1,49 @@ +#ifndef PLATFORM_HXX_INCLUDED +#define PLATFORM_HXX_INCLUDED + +#include +#include +#include +#include +#include +#include "Reflection.hxx" + +void GetDesktopResolution(int &width, int &height) noexcept; +std::unique_ptr GetNativeReflector() noexcept; + +std::int32_t GetCurrentThreadID() noexcept; + +bool IsProcessAlive(std::int32_t pid) noexcept; +bool IsThreadAlive(std::int32_t tid) noexcept; +std::vector get_pids() noexcept; +std::vector get_pids(const char* process_name) noexcept; +std::int32_t InjectProcess(std::int32_t) noexcept; +std::vector InjectProcesses(const char* process_name) noexcept; +std::int32_t PIDFromWindow(void* window) noexcept; + +std::vector GetLoadedModuleNames(const char* partial_module_name) noexcept; + +#if !defined(_WIN32) && !defined(_WIN64) +void* GetModuleHandle(const char* module_name) noexcept; +#endif + +template +void yield_thread(std::chrono::duration time) noexcept +{ + /*auto start = std::chrono::high_resolution_clock::now(); + auto end = start + time; + do { + //std::this_thread::yield(); + std::this_thread::sleep_for(std::chrono::nanoseconds(1)); + } while (std::chrono::high_resolution_clock::now() < end);*/ + std::this_thread::sleep_for(time); +} + +template +std::int64_t elapsed_time(std::chrono::high_resolution_clock::time_point start) noexcept +{ + auto end = std::chrono::high_resolution_clock::now(); + return std::chrono::duration_cast(end - start).count(); +} + +#endif // PLATFORM_HXX_INCLUDED diff --git a/third-party/RemoteInput/RemoteInput/Platform/Platform_Darwin.mm b/third-party/RemoteInput/RemoteInput/Platform/Platform_Darwin.mm new file mode 100644 index 0000000..398bb80 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Platform/Platform_Darwin.mm @@ -0,0 +1,428 @@ +#include "Platform.hxx" + +#if defined(__APPLE__) +#define CUSTOM_INJECTOR + +#if defined(__cplusplus) +extern "C" { +#endif + +#include +#include + +#if defined(__cplusplus) +} +#endif + +#include +#if defined(CUSTOM_INJECTOR) +#include "Injection/Injector.hxx" +#else +#include "Thirdparty/Injector.hxx" +#endif +#include +#include +#include +#include +#include +#endif // defined + +#if defined(__APPLE__) +std::string StripPath(const std::string& path_to_strip) +{ + return std::filesystem::path(path_to_strip).filename().string(); +} + +void GetDesktopResolution(int &width, int &height) noexcept +{ + auto get_screen_resolution = [&]{ + NSRect frame = [[NSScreen mainScreen] frame]; + width = static_cast(frame.size.width); + height = static_cast(frame.size.height); + }; + + if (!NSThread.isMainThread) + { + return dispatch_sync(dispatch_get_main_queue(), ^{ + get_screen_resolution(); + }); + } + + get_screen_resolution(); +} + +std::int32_t GetCurrentThreadID() noexcept +{ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" + return syscall(SYS_thread_selfid); + #pragma clang diagnostic pop +} + +bool IsProcessAlive(std::int32_t pid) noexcept +{ + return !kill(pid, 0); +} + +bool IsThreadAlive(std::int32_t tid) noexcept +{ + return !kill(tid, 0); +} + +std::vector get_pids() noexcept +{ + /*for (NSRunningApplication *app in [[NSWorkspace sharedWorkspace] runningApplications]) + { + pids.push_back([app processIdentifier]); + }*/ + std::vector pids(2048); + pids.resize(proc_listpids(PROC_ALL_PIDS, 0, &pids[0], 2048 * sizeof(std::int32_t)) / sizeof(std::int32_t)); + std::vector(pids).swap(pids); + return pids; +} + +std::vector get_pids(const char* process_name) noexcept +{ + std::vector result; + std::vector pids = get_pids(); + + for (std::size_t i = 0; i < pids.size(); ++i) + { + struct proc_bsdinfo proc; + if (proc_pidinfo(pids[i], PROC_PIDTBSDINFO, 0, &proc, PROC_PIDTBSDINFO_SIZE) == PROC_PIDTBSDINFO_SIZE) + { + if (!strcmp(process_name, proc.pbi_name)) + { + result.push_back(pids[i]); + } + else + { + // MacOS sometimes uses the localized name as the process name instead of the executable name + // So the BSD pbi_name will not always work! + // Instead, we need to get the running application's localized name + // This is because a process can have two names on MacOS + @autoreleasepool { + NSRunningApplication *application = [NSRunningApplication runningApplicationWithProcessIdentifier:pids[i]]; + if (application) + { + NSString *localized_name = [application localizedName]; + if (localized_name) + { + if ([[NSString stringWithUTF8String:process_name] isEqualToString:localized_name]) + { + result.push_back(pids[i]); + } + } + } + } + } + } + } + return result; +} + +std::int32_t InjectProcess(std::int32_t pid) noexcept +{ + Dl_info info = {0}; + if (dladdr(reinterpret_cast(InjectProcess), &info)) + { + std::string path = std::string(PATH_MAX, '\0'); + if (realpath(info.dli_fname, &path[0])) + { + #if defined(CUSTOM_INJECTOR) + if (Injector::Inject(path.c_str(), pid, nullptr)) + { + return pid; + } + #else + extern std::vector> injectors; + + for (auto& injector : injectors) + { + if (injector && injector->get_pid() == pid) + { + if (injector->is_injected()) + { + return pid; + } + + return injector->Inject(path.c_str()) ? pid : -1; + } + } + + std::unique_ptr injector = std::make_unique(pid); + if (injector) + { + bool result = injector->Inject(path.c_str()); + injectors.push_back(std::move(injector)); + return result ? pid : -1; + } + #endif + } + } + return -1; +} + +std::vector InjectProcesses(const char* process_name) noexcept +{ + std::vector result; + std::vector pids = get_pids(process_name); + + for (std::int32_t pid : pids) + { + if (InjectProcess(pid) != -1) + { + result.push_back(pid); + } + } + return result; +} + +std::int32_t PIDFromWindow(void* window) noexcept +{ + NSArray *windowList = (NSArray *)CFBridgingRelease(CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly, kCGNullWindowID)); + + for (int i = 0; i < windowList.count; ++i) + { + CGWindowID windowId = [[windowList[i] valueForKey:(NSString *)kCGWindowNumber] intValue]; + if (windowId == (CGWindowID)reinterpret_cast(window)) + { + return ((NSNumber *)[windowList[i] valueForKey:(NSString *)kCGWindowOwnerPID]).intValue; + } + } + return 0; +} + +std::vector GetLoadedModuleNames(const char* partial_module_name) noexcept +{ + std::vector result; + std::uint32_t count = _dyld_image_count(); + for (std::uint32_t i = 0; i < count; ++i) + { + const char* name = _dyld_get_image_name(i); + if (name) + { + std::string module_name = StripPath(name); + if (!strcasestr(module_name.c_str(), partial_module_name)) + { + result.push_back(name); + } + } + } + + return result; +} + +void* GetModuleHandle(const char* module_name) noexcept +{ + std::uint32_t count = _dyld_image_count(); + for (std::uint32_t i = 0; i < count; ++i) + { + const char* name = _dyld_get_image_name(i); + if (name) + { + std::string stripped_name = StripPath(name); + if (strcasestr(stripped_name.c_str(), module_name)) + { + return dlopen(name, RTLD_NOLOAD); + + /*const struct mach_header* header = _dyld_get_image_header(i); + //std::intptr_t offset = _dyld_get_image_vmaddr_slide(i); + + const struct load_command* cmd = reinterpret_cast(reinterpret_cast(header) + sizeof(struct mach_header)); + if (header->magic == MH_MAGIC_64) + { + cmd = reinterpret_cast(reinterpret_cast(header) + sizeof(struct mach_header_64)); + } + + for (std::uint32_t j = 0; j < header->ncmds; ++j) + { + if (cmd->cmd == LC_SEGMENT) + { + const struct segment_command* seg = reinterpret_cast(cmd); + void* base_addr = reinterpret_cast(seg->vmaddr); //seg->vmaddr + offset; + + Dl_info info = {0}; + dladdr(base_addr, &info); + return dlopen(info.dli_fname, RTLD_NOLOAD); + } + + if (cmd->cmd == LC_SEGMENT_64) + { + const struct segment_command_64* seg = reinterpret_cast(cmd); + void* base_addr = reinterpret_cast(seg->vmaddr); //seg->vmaddr + offset; + + Dl_info info = {0}; + dladdr(base_addr, &info); + return dlopen(info.dli_fname, RTLD_NOLOAD); + } + + cmd = reinterpret_cast(reinterpret_cast(cmd) + cmd->cmdsize); + }*/ + } + } + } + return dlopen(module_name, RTLD_NOLOAD); +} +#endif // defined + + +#if defined(__APPLE__) +@interface NSView (MDRecursiveSubviews) +- (jobject)awtComponent:(JNIEnv*)env; +@end + +std::unique_ptr GetNativeReflector() noexcept +{ +// auto ModuleLoaded = [](std::string name) -> bool { +// void* lib = dlopen(name.c_str(), RTLD_GLOBAL | RTLD_NOLOAD); +// if (lib) +// { +// dlclose(lib); +// return true; +// } +// return false; +// }; + + auto TimeOut = [&](std::uint32_t time, std::function &&run) -> bool { + auto start = std::chrono::high_resolution_clock::now(); + while(!run()) + { + if (elapsed_time(start) >= time) + { + return false; + } + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + return true; + }; + +// if (!TimeOut(20, [&]{ return ModuleLoaded("libjvm.dylib"); })) { +// return nullptr; +// } + + auto IsValidFrame = [](JNIEnv* env, jobject object) -> bool { + jclass cls = env->FindClass("java/awt/Frame"); + bool result = env->IsInstanceOf(object, cls); + env->DeleteLocalRef(cls); + return result; + }; + + auto GetWindowViews = [&]() -> std::vector { + __block std::vector views; + dispatch_sync(dispatch_get_main_queue(), ^{ + for (NSWindow* window in NSApplication.sharedApplication.windows) + { + //NSLog(@"Window: %@ -- %@", NSStringFromClass([window class]), NSStringFromClass([window.contentView class])); + if ([window isKindOfClass:NSClassFromString(@"AWTWindow_Normal")] && [window.contentView isKindOfClass:NSClassFromString(@"AWTView")]) + { + NSView* view = window.contentView; + views.push_back(view); + } + } + }); + return views; + }; + + if (!TimeOut(5, [&]{ return JVM().getVM() != nullptr; })) + { + return nullptr; + } + + JVM vm = JVM(); + JNIEnv* env = nullptr; + if (vm.AttachCurrentThread(&env) == JNI_OK) + { + std::unique_ptr reflection; + bool hasReflection = TimeOut(20, [&]{ + jclass cls = env->FindClass("java/awt/Frame"); + if (!cls) + { + return false; + } + + jmethodID method = env->GetStaticMethodID(cls, "getFrames", "()[Ljava/awt/Frame;"); + if (!method) + { + return false; + } + + jobjectArray frames = static_cast(env->CallStaticObjectMethod(cls, method)); + env->DeleteLocalRef(cls); + + if (!frames) + { + return false; + } + + jsize size = env->GetArrayLength(frames); + for (jsize i = 0; i < size; ++i) + { + jobject frame = env->GetObjectArrayElement(frames, i); + if (frame) + { + if (IsValidFrame(env, frame)) + { + reflection = Reflection::Create(frame); + if (reflection) + { + env->DeleteLocalRef(frames); + return true; + } + } + + env->DeleteLocalRef(frame); + } + } + + env->DeleteLocalRef(frames); + return false; + }); + + bool hasReflection2 = !hasReflection && TimeOut(20, [&]{ + for (auto&& view : GetWindowViews()) + { + //TODO: Check if we can call "awt_GetComponent" from the JDK like on Linux + jobject frame = [view awtComponent:env]; //java.awt.Frame + if (frame) + { + if (IsValidFrame(env, frame)) + { + reflection = Reflection::Create(frame); + if (reflection) + { + return true; + } + } + } + } + return false; + }); + + if (hasReflection || hasReflection2) + { + return reflection; + } + } + + vm.DetachCurrentThread(); + return nullptr; +} + +void disable_app_nap() noexcept +{ + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + dispatch_async(dispatch_get_main_queue(), ^{ + static NSObject* app_nap_token __attribute__((used)) = [[NSProcessInfo processInfo] beginActivityWithOptions:NSActivityUserInitiatedAllowingIdleSystemSleep | NSActivityLatencyCritical reason:@"No nappy time"]; + + #if defined(DEBUG) + printf("Disable App-Nap: %p\n", app_nap_token); + #else + (void)app_nap_token; + #endif + }); + }); +} +#endif // defined diff --git a/third-party/RemoteInput/RemoteInput/Platform/Platform_Linux.cxx b/third-party/RemoteInput/RemoteInput/Platform/Platform_Linux.cxx new file mode 100644 index 0000000..b165d30 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Platform/Platform_Linux.cxx @@ -0,0 +1,632 @@ +#include "Platform.hxx" + +#if defined(__linux__) +#if defined(CUSTOM_INJECTOR) +#include "Injection/Injector.hxx" +#else +#include "Thirdparty/Injector.hxx" +#endif + +#include +#include +#include + +#include +#include +#include +#include + +#if __has_include("libproc.h") && __has_include("proc/readproc.h") +#include +#include +#else +#include +#include +#endif + +#include +#include +#include +#include +#include +#endif // defined + +#if defined(__linux__) +std::string StripPath(const std::string& path_to_strip) +{ + return std::filesystem::path(path_to_strip).filename().string(); +} + +void GetDesktopResolution(int &width, int &height) noexcept +{ + Display* display = XOpenDisplay(nullptr); + Screen* screen = DefaultScreenOfDisplay(display); + width = screen->width; + height = screen->height; + XCloseDisplay(display); +} + +std::int32_t GetCurrentThreadID() noexcept +{ + #ifdef SYS_gettid + return syscall(SYS_gettid); + #else + return gettid(); + #endif +} + +bool IsProcessAlive(std::int32_t pid) noexcept +{ + return !kill(pid, 0); +} + +bool IsThreadAlive(std::int32_t tid) noexcept +{ + return !syscall(SYS_tkill, tid, 0); +} + +#if __has_include("libproc.h") && __has_include("proc/readproc.h") +std::vector get_pids() noexcept +{ + std::vector pids; + + proc_t proc_info = {0}; + PROCTAB* proc_tab = openproc(PROC_FILLSTAT); + if (proc_tab) + { + while (readproc(proc_tab, &proc_info)) + { + //task id, the POSIX thread ID (see also: tgid) + pids.push_back(proc_info.tid); //proc_info.cmd + } + + closeproc(proc_tab); + } + + + std::vector(pids).swap(pids); + return pids; +} + +std::vector get_pids(const char* process_name) noexcept +{ + std::vector result; + + proc_t proc_info = {0}; + PROCTAB* proc_tab = openproc(PROC_FILLSTAT); + if (proc_tab) + { + while (readproc(proc_tab, &proc_info)) + { + //basename of executable file in call to exec(2) + if (!strcasecmp(process_name, proc_info.cmd)) + { + //task id, the POSIX thread ID (see also: tgid) + result.push_back(proc_info.tid); + } + } + + closeproc(proc_tab); + } + return result; +} +#else +std::vector get_pids(const char* process_name) noexcept +{ + std::vector result; + DIR* proc_dir = opendir("/proc"); + if (proc_dir) + { + auto is_number = [](const std::string& s) -> bool { + return !s.empty() && std::find_if(s.begin(), s.end(), [](unsigned char c) { return !std::isdigit(c); }) == s.end(); + }; + + #if defined(__x86_64__) || defined(__aarch64__) + while(struct dirent64* entry = readdir64(proc_dir)) + #else + while(struct dirent* entry = readdir(proc_dir)) + #endif + { + if (entry->d_type == DT_DIR) + { + if (is_number(entry->d_name)) + { + std::string pid = entry->d_name; + std::string path = std::string(PATH_MAX, '\0'); + ssize_t len = readlink(("/proc/" + pid + "/exe").c_str(), &path[0], PATH_MAX - 1); + + if (len != -1) + { + std::string::size_type pos = path.find_last_of("/\\"); + if (pos != std::string::npos) + { + //Strip path components + path = path.substr(pos + 1); + pos = path.find_last_of('.'); + + //Strip extension + path = pos > 0 && pos != std::string::npos ? path.substr(0, pos) : path; + + if (!strcasecmp(path.c_str(), process_name)) + { + result.push_back(std::stoi(entry->d_name)); + } + } + } + } + } + } + + closedir(proc_dir); + } + return result; +} + +std::vector get_pids() noexcept +{ + std::vector result; + DIR* proc_dir = opendir("/proc"); + if (proc_dir) + { + auto is_number = [](const std::string& s) -> bool { + return !s.empty() && std::find_if(s.begin(), s.end(), [](unsigned char c) { return !std::isdigit(c); }) == s.end(); + }; + + #if defined(__x86_64__) || defined(__aarch64__) + while(struct dirent64* entry = readdir64(proc_dir)) + #else + while(struct dirent* entry = readdir(proc_dir)) + #endif + { + if (entry->d_type == DT_DIR) + { + if (is_number(entry->d_name)) + { + std::int32_t pid = std::stoi(entry->d_name); + result.push_back(pid); + } + } + } + + closedir(proc_dir); + } + return result; +} +#endif + +std::int32_t InjectProcess(std::int32_t pid) noexcept +{ + Dl_info info = {0}; + if (dladdr(reinterpret_cast(InjectProcess), &info)) + { + std::string path = std::string(PATH_MAX, '\0'); + if (realpath(info.dli_fname, &path[0])) + { + #if defined(CUSTOM_INJECTOR) + if (Injector::Inject(path.c_str(), pid, nullptr)) + { + return pid; + } + #else + extern std::vector> injectors; + + for (auto& injector : injectors) + { + if (injector && injector->get_pid() == pid) + { + if (injector->is_injected()) + { + return pid; + } + + return injector->Inject(path.c_str()) ? pid : -1; + } + } + + std::unique_ptr injector = std::make_unique(pid); + if (injector) + { + bool result = injector->Inject(path.c_str()); + injectors.push_back(std::move(injector)); + return result ? pid : -1; + } + #endif + } + } + return -1; +} + +std::vector InjectProcesses(const char* process_name) noexcept +{ + std::vector result; + std::vector pids = get_pids(process_name); + for (std::int32_t pid : pids) + { + if (InjectProcess(pid) != -1) + { + result.push_back(pid); + } + } + return result; +} +#endif + +#if defined(__linux__) +int GetClassName(Display* display, Window window, char* lpClassName, int nMaxCount) noexcept +{ + if (!lpClassName || !nMaxCount) + { + return 0; + } + + XClassHint hint; + if (XGetClassHint(display, window, &hint)) + { + if (hint.res_name) + { + XFree(hint.res_name); + } + + if (hint.res_class) + { + memset(lpClassName, 0, nMaxCount * sizeof(char)); + + int length = strlen(hint.res_class); + int max_copy = (nMaxCount < length ? nMaxCount - 1 : length); + strncpy(lpClassName, hint.res_class, max_copy); + XFree(hint.res_class); + return max_copy; + } + } + return 0; +} + +int GetWindowText(Display* display, Window window, char* lpString, int nMaxCount) noexcept +{ + if (!lpString || !nMaxCount) + { + return 0; + } + + char* name = nullptr; + if (XFetchName(display, window, &name) > 0) + { + memset(lpString, 0, nMaxCount * sizeof(char)); + + int length = strlen(name); + int max_copy = (nMaxCount < length ? nMaxCount - 1 : length); + strncpy(lpString, name, max_copy); + XFree(name); + return max_copy; + } + return 0; +} + +void EnumWindows(Display* display, bool (*EnumWindowsProc)(Display*, Window, void*), void* other) noexcept +{ + if (!EnumWindowsProc) + { + return; + } + + Atom atomList = XInternAtom(display, "_NET_CLIENT_LIST", true); + Atom type = None; + int format = 0; + unsigned long nItems = 0; + unsigned long bytesAfter = 0; + unsigned char* property = nullptr; + if (XGetWindowProperty(display, XDefaultRootWindow(display), atomList, 0L, ~0L, false, AnyPropertyType, &type, &format, &nItems, &bytesAfter, &property) == Success) + { + if (property) + { + for (unsigned int i = 0; i < nItems; ++i) + { + Window* windows = reinterpret_cast(property); + if (!EnumWindowsProc(display, windows[i], other)) + { + break; + } + } + XFree(property); + } + } +} + +void EnumChildWindows(Display* display, Window parentWindow, bool (*EnumChildProc)(Display*, Window, void*), void* other) noexcept +{ + if (!EnumChildProc) + { + return; + } + + Window root_return; + Window parent_return; + Window* children_return; + unsigned int nchildren_return; + if (XQueryTree(display, parentWindow, &root_return, &parent_return, &children_return, &nchildren_return)) + { + for (unsigned int i = 0; i < nchildren_return; ++i) + { + if (!EnumChildProc(display, children_return[i], other)) + { + break; + } + + // Recursive search children + EnumChildWindows(display, children_return[i], EnumChildProc, other); + } + XFree(children_return); + } +} + +void GetWindowThreadProcessId(Display* display, Window window, std::int32_t* pid) noexcept +{ + if (!pid) { return; } + *pid = 0; + + Atom atomPID = XInternAtom(display, "_NET_WM_PID", True); + if (atomPID == None) + { + return; + } + + Atom type = None; + int format = 0; + unsigned long nItems = 0; + unsigned long bytesAfter = 0; + unsigned char* property = nullptr; + + if (XGetWindowProperty(display, window, atomPID, 0L, 1L, False, XA_CARDINAL, &type, &format, &nItems, &bytesAfter, &property) == 0) + { + if (property) + { + *pid = *reinterpret_cast(property); + XFree(property); + } + } +} + +std::vector GetWindowsFromProcessId(Display* display, std::int32_t pid) noexcept +{ + // Get Atom PID + Atom atomPID = XInternAtom(display, "_NET_WM_PID", True); + if (atomPID == None) + { + return std::vector(); + } + + std::function&)> GetWindowsFromPIDInternal = [&](Display* display, Window window, Atom atomPID, std::int32_t pid, std::vector& results) { + // Get Window PID + Atom type = None; + int format = 0; + unsigned long nItems = 0; + unsigned long bytesAfter = 0; + unsigned char* property = nullptr; + + if (XGetWindowProperty(display, window, atomPID, 0L, 1L, False, XA_CARDINAL, &type, &format, &nItems, &bytesAfter, &property) == 0) + { + if (property) + { + if (pid == *reinterpret_cast(property)) + { + results.emplace_back(window); + } + XFree(property); + } + } + + // Check all child windows + Window root_return; + Window parent_return; + Window* children_return; + unsigned int nchildren_return; + if (XQueryTree(display, window, &root_return, &parent_return, &children_return, &nchildren_return)) + { + for (unsigned int i = 0; i < nchildren_return; ++i) + { + // Recursive search children + GetWindowsFromPIDInternal(display, children_return[i], atomPID, pid, results); + } + XFree(children_return); + } + }; + + std::vector windows; + GetWindowsFromPIDInternal(display, XDefaultRootWindow(display), atomPID, pid, windows); + return windows; +} + +std::int32_t PIDFromWindow(void* window) noexcept +{ + std::int32_t pid = 0; + Display* display = XOpenDisplay(nullptr); + GetWindowThreadProcessId(display, reinterpret_cast(window), &pid); + XCloseDisplay(display); + return pid; +} + +std::vector GetLoadedModuleNames(const char* partial_module_name) noexcept +{ + struct Module { const char* module_name; std::vector result; }; + Module module_info = {0}; + module_info.module_name = partial_module_name; + + dl_iterate_phdr([](struct dl_phdr_info *info, size_t size, void *data) -> int { + if (info && info->dlpi_name) + { + Module* module_info = static_cast(data); + std::string module_name = StripPath(info->dlpi_name); + if (strcasestr(module_name.c_str(), module_info->module_name)) + { + module_info->result.push_back(info->dlpi_name); + } + } + return 0; + }, reinterpret_cast(&module_info)); + return module_info.result; +} + +void* GetModuleHandle(const char* module_name) noexcept +{ + struct Module { const char* module_name; void* result; }; + Module module_info = {0}; + module_info.module_name = module_name; + module_info.result = nullptr; + + dl_iterate_phdr([](struct dl_phdr_info *info, size_t size, void *data) -> int { + if (info && info->dlpi_name) + { + Module* module_info = static_cast(data); + std::string module_name = StripPath(info->dlpi_name); + if (strcasestr(module_name.c_str(), module_info->module_name)) + { + module_info->result = dlopen(module_info->module_name, RTLD_NOLOAD); + return module_info->result ? 1 : 0; + } + } + return 0; + }, &module_info); + return module_info.result ? module_info.result : dlopen(module_name, RTLD_NOLOAD); +} +#endif + +#if defined(__linux__) +std::array recognizedClassNames = { + "jagexappletviewer", // Jagex client + "net-runelite-client-RuneLite", // RuneLite client +}; +bool EnumWindowsProc(Display* display, Window window, void* other) noexcept +{ + std::int32_t pid = 0; + GetWindowThreadProcessId(display, window, &pid); + if (pid != getpid() || other == nullptr) + { + // wrong process ID, or a null `other` parameter: + return true; + } + char className[256] = {0}; + if (GetClassName(display, window, className, sizeof(className)) > 0) + { + const auto matchedClassName = std::find( + std::begin(recognizedClassNames), + std::end(recognizedClassNames), + std::string(className) + ); + if (matchedClassName != std::end(recognizedClassNames)) + { + // the correct window has been located, so assign this `Window` + // object to the `other` parameter, and return false to + // indicate that the operation was a success. + *reinterpret_cast(other) = window; + return false; + } + #if defined(DEBUG) + printf("excluded window with class name '%s'\n", className); + #endif + } + return true; +} + +std::unique_ptr GetNativeReflector() noexcept +{ + auto ModuleLoaded = [](std::string name) -> bool { + void* lib = dlopen(name.c_str(), RTLD_LAZY | RTLD_NOLOAD); + if (lib) + { + dlclose(lib); + return true; + } + return false; + }; + + auto TimeOut = [&](std::uint32_t time, std::function &&run) -> bool { + auto start = std::chrono::high_resolution_clock::now(); + while(!run()) + { + if (elapsed_time(start) >= time) + { + return false; + } + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + return true; + }; + + auto IsValidFrame = [](JNIEnv* env, jobject object) -> bool { + jclass cls = env->FindClass("java/awt/Frame"); + bool result = env->IsInstanceOf(object, cls); + env->DeleteLocalRef(cls); + return result; + }; + + if (!TimeOut(5, [&]{ return JVM().getVM() != nullptr; })) + { + return nullptr; + } + + auto GetMainWindow = [&]() -> Window { + Display* display = XOpenDisplay(nullptr); + if (display) + { + Window windowFrame = 0; + EnumWindows(display, EnumWindowsProc, &windowFrame); + XCloseDisplay(display); + return windowFrame; + } + return 0; + }; + + JVM vm = JVM(); + if (!vm) + { + return nullptr; + } + + JNIEnv* env = nullptr; + if (vm.AttachCurrentThread(&env) == JNI_OK) + { + std::unique_ptr reflection; + bool hasReflection = TimeOut(20, [&]{ + if (!ModuleLoaded("libawt_xawt.so")) + { + return false; + } + + auto awt_GetComponent = reinterpret_cast(dlsym(RTLD_DEFAULT, "awt_GetComponent")); + if (!awt_GetComponent) + { + return false; + } + + return TimeOut(20, [&]{ + void* windowFrame = reinterpret_cast(GetMainWindow()); + if (windowFrame) + { + jobject frame = awt_GetComponent(env, windowFrame); //java.awt.Frame + if (frame) + { + if (IsValidFrame(env, frame)) + { + reflection = Reflection::Create(frame); + if (reflection) + { + return true; + } + } + } + } + return false; + }); + }); + + if (hasReflection) + { + return reflection; + } + } + + vm.DetachCurrentThread(); + return nullptr; +} +#endif // defined diff --git a/third-party/RemoteInput/RemoteInput/Platform/Platform_Windows.cxx b/third-party/RemoteInput/RemoteInput/Platform/Platform_Windows.cxx new file mode 100644 index 0000000..f3fd17d --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Platform/Platform_Windows.cxx @@ -0,0 +1,568 @@ +#include "Platform.hxx" + +#if defined(_WIN32) || defined(_WIN64) +#include +#include +#include +#include "Thirdparty/Hook.hxx" +#if defined(CUSTOM_INJECTOR) +#include "Injection/Injector.hxx" +#else +#include "Thirdparty/Injector.hxx" +#endif + +#include +#include +#include +#endif // defined + +#if defined(_WIN32) || defined(_WIN64) +int GetDpi(HWND hwnd) +{ + HDC hDC = GetDC(hwnd); + int ydpi = GetDeviceCaps(hDC, LOGPIXELSY); + ReleaseDC(hwnd, hDC); + return ydpi; +} + +void ReverseScaleDPI(HWND hwnd, int &width, int &height) +{ + HDC hDC = GetDC(hwnd); + int xdpi = GetDeviceCaps(hDC, LOGPIXELSX); + int ydpi = GetDeviceCaps(hDC, LOGPIXELSY); + ReleaseDC(hwnd, hDC); + + // Reverse DPI Scaling + width = static_cast(96.0 * width / xdpi); + height = static_cast(96.0 * height / ydpi); +} + +void GetDesktopResolution(int &width, int &height) noexcept +{ + const HWND hDesktop = GetDesktopWindow(); + RECT desktop = {0}; + GetWindowRect(hDesktop, &desktop); + width = desktop.right; + height = desktop.bottom; + + /* + // DPI Scaling + HMODULE user32 = GetModuleHandleA("User32.dll"); + HMODULE shcore = GetModuleHandleA("Shcore.dll"); + + DPI_AWARENESS_CONTEXT (*GetWindowDpiAwarenessContext)(HWND) = reinterpret_cast(GetProcAddress(user32, "GetWindowDpiAwarenessContext")); + DPI_AWARENESS (*GetAwarenessFromDpiAwarenessContext)(DPI_AWARENESS_CONTEXT) = reinterpret_cast(GetProcAddress(user32, "GetAwarenessFromDpiAwarenessContext")); + HRESULT (*GetDpiForMonitor)(HMONITOR, MONITOR_DPI_TYPE, UINT*, UINT*) = reinterpret_cast(GetProcAddress(shcore, "GetDpiForMonitor")); + + if (GetWindowDpiAwarenessContext && GetAwarenessFromDpiAwarenessContext && GetDpiForMonitor) + { + DPI_AWARENESS_CONTEXT context = GetWindowDpiAwarenessContext(hDesktop); + if (context) + { + DPI_AWARENESS awareness = GetAwarenessFromDpiAwarenessContext(context); + if (awareness != DPI_AWARENESS_INVALID && awareness != DPI_AWARENESS_UNAWARE) + { + UINT xdpi = 0; + UINT ydpi = 0; + HMONITOR hMonitor = MonitorFromWindow(hDesktop, MONITOR_DEFAULTTONEAREST); + if (GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &xdpi, &ydpi) == S_OK) + { + width = static_cast(width / (xdpi / 96.0)); + height = static_cast(height / (ydpi / 96.0)); + } + } + } + } + */ +} + +std::int32_t GetCurrentThreadID() noexcept +{ + return GetCurrentThreadId(); +} + +bool IsProcessAlive(std::int32_t pid) noexcept +{ + HANDLE process = OpenProcess(SYNCHRONIZE, FALSE, pid); + if (process) + { + DWORD ret = WaitForSingleObject(process, 0); + CloseHandle(process); + return ret == WAIT_TIMEOUT; + } + return false; +} + +bool IsThreadAlive(std::int32_t tid) noexcept +{ + HANDLE thread = OpenThread(SYNCHRONIZE, FALSE, tid); + if (thread) + { + DWORD ret = WaitForSingleObject(thread, 0); + CloseHandle(thread); + return ret == WAIT_TIMEOUT; + } + return false; +} + +std::vector get_pids() noexcept +{ + std::vector result; + PROCESSENTRY32 processInfo = {0}; + processInfo.dwSize = sizeof(processInfo); + + HANDLE processesSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (processesSnapshot != INVALID_HANDLE_VALUE) + { + if (Process32First(processesSnapshot, &processInfo)) + { + result.push_back(processInfo.th32ProcessID); + + while (Process32Next(processesSnapshot, &processInfo)) + { + result.push_back(processInfo.th32ProcessID); + } + } + CloseHandle(processesSnapshot); + } + return result; +} + +std::vector get_pids(const char* process_name) noexcept +{ + std::vector result; + PROCESSENTRY32 processInfo = {0}; + processInfo.dwSize = sizeof(processInfo); + + HANDLE processesSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (processesSnapshot != INVALID_HANDLE_VALUE) + { + if (Process32First(processesSnapshot, &processInfo)) + { + if (!_stricmp(process_name, processInfo.szExeFile)) + { + result.push_back(processInfo.th32ProcessID); + } + + while (Process32Next(processesSnapshot, &processInfo)) + { + if (!_stricmp(process_name, processInfo.szExeFile)) + { + result.push_back(processInfo.th32ProcessID); + } + } + } + + CloseHandle(processesSnapshot); + } + return result; +} + +PROCESSENTRY32 GetProcessInfo(std::int32_t pid) noexcept +{ + HANDLE processesSnapshot = nullptr; + if((processesSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)) == INVALID_HANDLE_VALUE) + { + return {0}; + } + + PROCESSENTRY32 processInfo = {0}; + processInfo.dwSize = sizeof(PROCESSENTRY32); + while(Process32Next(processesSnapshot, &processInfo)) + { + if(processInfo.th32ProcessID == static_cast(pid)) + { + CloseHandle(processesSnapshot); + return processInfo; + } + } + CloseHandle(processesSnapshot); + return {0}; +} + +std::vector GetLoadedModuleNames(const char* partial_module_name) noexcept +{ + auto find_substring = [](std::string_view haystack, std::string_view needle) -> bool { + auto it = std::search(haystack.begin(), haystack.end(), needle.begin(), needle.end(), [](char a, char b) { + return std::toupper(a) == std::toupper(b); + }); + return it != haystack.end(); + }; + + HANDLE modulesSnapshot = nullptr; + if ((modulesSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId())) == INVALID_HANDLE_VALUE) + { + return {}; + } + + std::vector result; + MODULEENTRY32 moduleInfo = {0}; + moduleInfo.dwSize = sizeof(MODULEENTRY32); + while (Module32Next(modulesSnapshot, &moduleInfo)) + { + if (find_substring(moduleInfo.szModule, partial_module_name)) + { + result.push_back(moduleInfo.szModule); + } + } + + CloseHandle(modulesSnapshot); + return result; +} + +MODULEENTRY32 GetModuleInfo(std::int32_t pid, const char* module_name) noexcept +{ + HANDLE modulesSnapshot = nullptr; + if ((modulesSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid)) == INVALID_HANDLE_VALUE) + { + return {0}; + } + + MODULEENTRY32 moduleInfo = {0}; + moduleInfo.dwSize = sizeof(MODULEENTRY32); + while (Module32Next(modulesSnapshot, &moduleInfo)) + { + if (!_stricmp(module_name, moduleInfo.szModule)) + { + CloseHandle(modulesSnapshot); + return moduleInfo; + } + } + + CloseHandle(modulesSnapshot); + return {0}; +} + +MODULEENTRY32 GetModuleInfo(std::int32_t pid, HMODULE module_handle) noexcept +{ + HANDLE modulesSnapshot = nullptr; + if ((modulesSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid)) == INVALID_HANDLE_VALUE) + { + return {0}; + } + + MODULEENTRY32 moduleInfo = {0}; + moduleInfo.dwSize = sizeof(MODULEENTRY32); + while (Module32Next(modulesSnapshot, &moduleInfo)) + { + if (module_handle == moduleInfo.hModule) + { + CloseHandle(modulesSnapshot); + return moduleInfo; + } + } + + CloseHandle(modulesSnapshot); + return {0}; +} + +void PrintProcessInfo(std::int32_t pid) noexcept +{ + PROCESSENTRY32 Proc32 = GetProcessInfo(pid); + if (Proc32.th32ProcessID != 0) + { + printf(" =======================================================\n"); + printf(" Process Name: %s\n", Proc32.szExeFile); + printf(" =======================================================\n\n"); + printf(" Process ID: %lu\n", Proc32.th32ProcessID); + printf(" Thread Count: %lu\n", Proc32.cntThreads); + printf(" Priority Base: %lu\n", Proc32.pcPriClassBase); + printf(" Parent Process ID: %lu\n\n", Proc32.th32ParentProcessID); + printf(" =======================================================\n"); + } +} + +char* realpath(const char* path, char* resolved_path) +{ + auto MapWindowsErrorToErrno = [](DWORD error) -> std::int32_t { + switch (error) + { + case ERROR_FILE_NOT_FOUND: + case ERROR_PATH_NOT_FOUND: + return ENOENT; + case ERROR_ACCESS_DENIED: + return EACCES; + case ERROR_INVALID_PARAMETER: + return EINVAL; + case ERROR_ALREADY_EXISTS: + return EEXIST; + default: + return EIO; + } + }; + + if (!path || !resolved_path) + { + errno = EINVAL; + return nullptr; + } + + HANDLE hFile = CreateFile( + path, + GENERIC_READ, + FILE_SHARE_READ, + nullptr, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, + nullptr + ); + + if (hFile == INVALID_HANDLE_VALUE) + { + errno = MapWindowsErrorToErrno(GetLastError()); + return nullptr; + } + + DWORD result = GetFinalPathNameByHandle(hFile, resolved_path, MAX_PATH, FILE_NAME_NORMALIZED); + CloseHandle(hFile); + + if (result == 0) + { + errno = MapWindowsErrorToErrno(GetLastError()); + return nullptr; + } + + return resolved_path; +} + +bool InjectSelf(std::int32_t pid) noexcept +{ + if (IsProcessAlive(pid)) + { + std::string file; + file.resize(MAX_PATH); + extern HMODULE module; + + if (GetModuleFileName(module, &file[0], MAX_PATH) == 0) + { + return false; + } + + #if defined(CUSTOM_INJECTOR) + return Injector::Inject(file, pid, nullptr); + #else + extern std::vector> injectors; + + for (auto& injector : injectors) + { + if (injector && injector->get_pid() == pid) + { + if (injector->is_injected()) + { + return true; + } + + return injector->Inject(file);; + } + } + + std::unique_ptr injector = std::make_unique(pid); + if (injector) + { + bool result = injector->Inject(file); + injectors.push_back(std::move(injector)); + return true; + } + #endif + } + return false; +} + +std::int32_t InjectProcess(std::int32_t pid) noexcept +{ + extern HMODULE module; + MODULEENTRY32 info = GetModuleInfo(pid, module); + if (info.dwSize == 0) + { + if (InjectSelf(pid)) + { + return pid; + } + } + return -1; +} + +std::vector InjectProcesses(const char* process_name) noexcept +{ + std::vector result; + std::vector pids = get_pids(process_name); + for (std::int32_t pid : pids) + { + if (InjectProcess(pid) != -1) + { + result.push_back(pid); + } + } + return result; +} + +std::int32_t PIDFromWindow(void* window) noexcept +{ + DWORD pid = 0; + GetWindowThreadProcessId(static_cast(window), &pid); + return static_cast(pid); +} +#endif // defined + +#if defined(_WIN32) || defined(_WIN64) +BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam) noexcept +{ + DWORD pid = 0; + GetWindowThreadProcessId(hwnd, &pid); + if (pid == GetCurrentProcessId() && lParam) + { + char className[MAX_PATH] = {0}; + int result = GetClassName(hwnd, className, sizeof(className)); + if (result != 0 && std::string(className) == "SunAwtFrame") + { + *reinterpret_cast(lParam) = hwnd; + return FALSE; + } + } + return TRUE; +} + +std::unique_ptr GetNativeReflector() noexcept +{ + auto TimeOut = [&](std::uint32_t time, std::function &&run) -> bool { + auto start = std::chrono::high_resolution_clock::now(); + while(!run()) + { + if (elapsed_time(start) >= time) + { + return false; + } + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + return true; + }; + + auto IsValidFrame = [](JNIEnv* env, jobject object) -> bool { + jclass cls = env->FindClass("java/awt/Frame"); + bool result = env->IsInstanceOf(object, cls); + env->DeleteLocalRef(cls); + return result; + }; + + auto GetMainWindow = [&] { + HWND windowFrame = nullptr; + EnumWindows(EnumWindowsProc, reinterpret_cast(&windowFrame)); + return windowFrame; + }; + + HMODULE awt = nullptr; + if (!TimeOut(20, [&]{ return (awt = GetModuleHandle("awt.dll")); })) + { + return nullptr; + } + + if (!TimeOut(5, [&]{ return JVM().getVM() != nullptr; })) + { + return nullptr; + } + + JVM vm = JVM(); + if (!vm) + { + return nullptr; + } + + JNIEnv* env = nullptr; + if (vm.AttachCurrentThread(&env) == JNI_OK) + { + std::unique_ptr reflection; + bool hasReflection = TimeOut(20, [&]{ + jclass cls = env->FindClass("java/awt/Frame"); + if (!cls) + { + return false; + } + + jmethodID method = env->GetStaticMethodID(cls, "getFrames", "()[Ljava/awt/Frame;"); + if (!method) + { + return false; + } + + jobjectArray frames = static_cast(env->CallStaticObjectMethod(cls, method)); + env->DeleteLocalRef(cls); + if (!frames) + { + return false; + } + + jsize size = env->GetArrayLength(frames); + for (jsize i = 0; i < size; ++i) + { + jobject frame = env->GetObjectArrayElement(frames, i); + if (frame) + { + if (IsValidFrame(env, frame)) + { + reflection = Reflection::Create(frame); + if (reflection) + { + env->DeleteLocalRef(frames); + return true; + } + } + + env->DeleteLocalRef(frame); + } + } + + env->DeleteLocalRef(frames); + return false; + }); + + bool hasReflection2 = !hasReflection && TimeOut(20, [&]{ + auto DSGetComponent = reinterpret_cast(GetProcAddress(awt, "_DSGetComponent@8")); + if (!DSGetComponent) + { + DSGetComponent = reinterpret_cast(GetProcAddress(awt, "DSGetComponent")); + } + + if (!DSGetComponent) + { + int ordinal = 146; + char* procName = reinterpret_cast(static_cast(static_cast(ordinal))); + DSGetComponent = reinterpret_cast(GetProcAddress(awt, procName)); + } + + if (!DSGetComponent) + { + return false; + } + + return TimeOut(20, [&]{ + HWND windowFrame = GetMainWindow(); + if (windowFrame) + { + jobject frame = DSGetComponent(reflection->getEnv(), windowFrame); //java.awt.Frame + if (frame) + { + if (IsValidFrame(env, frame)) + { + reflection = Reflection::Create(frame); + if (reflection) + { + return true; + } + } + } + } + return false; + }); + }); + + if (hasReflection || hasReflection2) + { + return reflection; + } + } + + vm.DetachCurrentThread(); + return nullptr; +} +#endif // defined diff --git a/third-party/RemoteInput/RemoteInput/Platform/RemoteInputBootstrap_Darwin.cxx b/third-party/RemoteInput/RemoteInput/Platform/RemoteInputBootstrap_Darwin.cxx new file mode 100644 index 0000000..b1ce02c --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Platform/RemoteInputBootstrap_Darwin.cxx @@ -0,0 +1,75 @@ +// +// RemoteInputBootstrap_Darwin.cpp +// RemoteInputBootstrap +// +// Created by Brandon on 2020-03-17. +// Copyright © 2020 XIO. All rights reserved. +// + +#if defined(MACH_INJECT) +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define EXPORT [[gnu::visibility("default")]] + +#ifdef __cplusplus +extern "C" { +#endif + +void _pthread_set_self(void*); +EXPORT void LoadLibrary(ptrdiff_t offset, void *param, size_t psize, void *dummy); + +#ifdef __cplusplus +} +#endif + +void LoadLibrary(ptrdiff_t offset, void* param, size_t psize, void* userp) +{ + _pthread_set_self(userp); + + int policy = 0; + pthread_attr_t attr = {}; + pthread_t thread = nullptr; + struct sched_param sched = {}; + + pthread_attr_init(&attr); + pthread_attr_getschedpolicy(&attr, &policy); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + + sched.sched_priority = sched_get_priority_max(policy); + pthread_attr_setschedparam(&attr, &sched); + + pthread_create(&thread, &attr, [](void* param) -> void* { + void* lib = dlopen(static_cast(param), RTLD_NOW | RTLD_NOLOAD); + if (lib) + { + dlclose(lib); + return nullptr; + } + + lib = dlopen(static_cast(param), RTLD_NOW); + if (!lib) + { + fprintf(stderr, "Could not load module: %s\n", dlerror()); + } + return nullptr; + }, param); + + pthread_attr_destroy(&attr); + thread_suspend(mach_thread_self()); +} +#endif \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Plugin/ControlCenter.cxx b/third-party/RemoteInput/RemoteInput/Plugin/ControlCenter.cxx new file mode 100644 index 0000000..142e67b --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Plugin/ControlCenter.cxx @@ -0,0 +1,2011 @@ +#include "ControlCenter.hxx" +#include +#include +#include +#include +#include "Platform.hxx" +#include "Graphics.hxx" +#include "ReflectionHook.hxx" +#include "RemoteVM.hxx" + +auto create_command_processor = [](auto processor) -> std::thread { + std::thread thread = std::thread([processor]{ + #if defined(_WIN32) || defined(_WIN64) + HANDLE this_process = GetCurrentProcess(); + SetPriorityClass(this_process, NORMAL_PRIORITY_CLASS); + + HANDLE this_thread = GetCurrentThread(); + SetThreadPriority(this_thread, THREAD_PRIORITY_HIGHEST); + #else + pthread_t this_thread = pthread_self(); + if (this_thread) + { + struct sched_param params = {0}; + params.sched_priority = sched_get_priority_max(SCHED_FIFO); + pthread_setschedparam(this_thread, SCHED_FIFO, ¶ms); + } + #endif // defined + + processor(); + }); + + thread.detach(); + return thread; +}; + +ControlCenter::ControlCenter(std::int32_t pid, bool is_controller, std::unique_ptr reflector) : pid(pid), is_controller(is_controller), stopped(is_controller), command_signal(), response_signal(), sync_signal(), main_reflector(std::move(reflector)), io_controller(), remote_vm() +{ + if (pid <= 0) + { + throw std::runtime_error("Invalid Process ID"); + } + + if (!init_maps()) + { + throw std::runtime_error("Cannot Initialize Maps"); + } + + if (!init_signals()) + { + throw std::runtime_error("Cannot Initialize Signals"); + } + + if (!init_wait()) + { + throw std::runtime_error("Cannot Initialize Wait Signal"); + } + + if (!is_controller) + { + this->set_parent_process_id(-1); + this->set_parent_thread_id(-1); + + if (this->main_reflector) + { + create_command_processor([this]{ + if (this->main_reflector->Attach()) + { + this->io_controller = std::make_unique(this->main_reflector.get()); + this->remote_vm = std::make_unique(this->main_reflector->getEnv(), this); + + while(!stopped) + { + if (!command_signal || !response_signal || !memory_map) + { + break; + } + + command_signal->wait(); + + /*while(!command_signal->try_wait()) + { + std::this_thread::sleep_for(std::chrono::nanoseconds(1)); + }*/ + + if (stopped) + { + response_signal->signal(); + break; + } + + JNIEnv* env = this->main_reflector->getEnv(); + env->PushLocalFrame(150'000); + process_command(); + response_signal->signal(); + env->PopLocalFrame(nullptr); + } + + if (this->remote_vm) + { + this->remote_vm.reset(); + } + + if (this->io_controller) + { + this->io_controller.reset(); + } + + if (this->main_reflector) + { + this->main_reflector.reset(); + } + } + }); + } + } +} + +ControlCenter::~ControlCenter() +{ + //terminate(); + + if (io_controller) + { + io_controller.reset(); + } + + if (sync_signal) + { + sync_signal.reset(); + } + + if (command_signal) + { + command_signal.reset(); + } + + if (response_signal) + { + response_signal.reset(); + } + + if (memory_map) + { + memory_map.reset(); + } +} + +void ControlCenter::terminate() noexcept +{ + if (is_controller) + { + stop_all_processing(); + } + else + { + if (!stopped) + { + stopped = true; + + if (command_signal) + { + command_signal->signal(); + } + + if (response_signal) + { + response_signal->wait(); + } + } + } +} + +void ControlCenter::process_command() noexcept +{ + ImageData& image_data = memory_map->data(); + Stream& stream = image_data.data_stream(); + image_data.prepare_for_read(); + image_data.prepare_for_write(); + + switch(image_data.command()) + { + case EIOSCommand::COMMAND_NONE: + break; + + case EIOSCommand::KILL_APPLICATION: + { + stopped = true; + response_signal->signal(); + + if (this->io_controller) + { + this->io_controller.reset(); + } + + if (this->main_reflector) + { + this->main_reflector.reset(); + } + + std::exit(0); + } + break; + + case EIOSCommand::GET_IMAGE_DIMENSIONS: + { + image_data.set_image_width(get_image_width()); + image_data.set_image_height(get_image_height()); + } + break; + + case EIOSCommand::GET_TARGET_DIMENSIONS: + { + image_data.set_target_width(get_target_width()); + image_data.set_target_height(get_target_height()); + } + break; + + case EIOSCommand::HAS_FOCUS: + { + bool result = io_controller->has_focus(); + stream.write(result); + } + break; + + case EIOSCommand::GAIN_FOCUS: + { + io_controller->gain_focus(); + } + break; + + case EIOSCommand::LOSE_FOCUS: + { + io_controller->lose_focus(); + } + break; + + case EIOSCommand::IS_KEYBOARD_INPUT_ENABLED: + { + bool result = io_controller->is_keyboard_input_enabled(); + stream.write(result); + } + break; + + case EIOSCommand::SET_KEYBOARD_INPUT_ENABLED: + { + bool enabled = stream.read(); + io_controller->set_keyboard_input_enabled(enabled); + } + break; + + case EIOSCommand::IS_MOUSE_INPUT_ENABLED: + { + bool result = io_controller->is_mouse_input_enabled(); + stream.write(result); + } + break; + + case EIOSCommand::SET_MOUSE_INPUT_ENABLED: + { + bool enabled = stream.read(); + io_controller->set_mouse_input_enabled(enabled); + } + break; + + case EIOSCommand::GET_MOUSE: + { + std::int32_t x = -1; + std::int32_t y = -1; + + io_controller->get_mouse_position(&x, &y); + stream.write(x); + stream.write(y); + } + break; + + case EIOSCommand::GET_REAL_MOUSE: + { + std::int32_t x = -1; + std::int32_t y = -1; + + io_controller->get_real_mouse_position(&x, &y); + stream.write(x); + stream.write(y); + } + break; + + case EIOSCommand::MOVE_MOUSE: + { + std::int32_t x = stream.read(); + std::int32_t y = stream.read(); + io_controller->move_mouse(x, y); + } + break; + + case EIOSCommand::HOLD_MOUSE: + { + std::int32_t button = stream.read(); + io_controller->hold_mouse(button); + } + break; + + case EIOSCommand::RELEASE_MOUSE: + { + std::int32_t button = stream.read(); + io_controller->release_mouse(button); + } + break; + + case EIOSCommand::SCROLL_MOUSE: + { + std::int32_t lines = stream.read(); + io_controller->scroll_mouse(lines); + } + break; + + case EIOSCommand::IS_MOUSE_HELD: + { + std::int32_t button = stream.read(); + bool result = io_controller->is_mouse_held(button); + stream.write(result); + } + break; + + case EIOSCommand::SEND_STRING: + { + std::string string = stream.read(); + std::int32_t keywait = stream.read(); + std::int32_t keymodwait = stream.read(); + io_controller->send_string(string, keywait, keymodwait); + } + break; + + case EIOSCommand::SEND_KEY: + { + char key = stream.read(); + std::int32_t key_down_time = stream.read(); + std::int32_t key_up_time = stream.read(); + std::int32_t modifier_down_time = stream.read(); + std::int32_t modifier_up_time = stream.read(); + io_controller->send_key(key, key_down_time, key_up_time, modifier_down_time, modifier_up_time); + } + break; + + case EIOSCommand::KEY_SEND: + { + std::string string = stream.read(); + std::vector sleeps; + + // Simba sends string.length*4 of sleep times to control the speed of typing + sleeps.resize(string.length() * 4); + stream.read(sleeps.data(), (string.length() * 4) * sizeof(int32_t)); + + io_controller->key_send(string, sleeps); + } + break; + + case EIOSCommand::HOLD_KEY: + { + std::int32_t keycode = stream.read(); + io_controller->hold_key(keycode); + } + break; + + case EIOSCommand::RELEASE_KEY: + { + std::int32_t keycode = stream.read(); + io_controller->release_key(keycode); + } + break; + + case EIOSCommand::IS_KEY_HELD: + { + std::int32_t keycode = stream.read(); + bool result = io_controller->is_key_held(keycode); + stream.write(result); + } + break; + + case EIOSCommand::GET_KEYBOARD_SPEED: + { + std::int32_t speed = io_controller->get_keyboard_speed(); + stream.write(speed); + } + break; + + case EIOSCommand::SET_KEYBOARD_SPEED: + { + std::int32_t speed = stream.read(); + io_controller->set_keyboard_speed(speed); + } + break; + + case EIOSCommand::GET_KEYBOARD_REPEAT_DELAY: + { + std::int32_t delay = io_controller->get_keyboard_repeat_delay(); + stream.write(delay); + } + break; + + case EIOSCommand::SET_KEYBOARD_REPEAT_DELAY: + { + std::int32_t delay = stream.read(); + io_controller->set_keyboard_repeat_delay(delay); + } + break; + + case EIOSCommand::STOP_ALL_PROCESSING: + { + io_controller->stop_all_processing(); + } + break; + + case EIOSCommand::GET_UI_SCALING: + { + fprintf(stderr, "WHY ARE YOU REMOTELY CALLING THIS FUNCTION?!\n"); + std::exit(0); + } + break; + + case EIOSCommand::SET_UI_SCALING: + { + fprintf(stderr, "WHY ARE YOU REMOTELY CALLING THIS FUNCTION?!\n"); + std::exit(0); + } + break; + + case EIOSCommand::REFLECT_OBJECT: + { + ReflectionHook hook; + stream >> hook; + + jobject result = main_reflector->getField(hook.object, hook); + stream.write(result); + } + break; + + case EIOSCommand::REFLECT_COMPARE_OBJECTS: + { + jobject first = stream.read(); + jobject second = stream.read(); + bool result = main_reflector->getEnv()->IsSameObject(first, second); + stream.write(result); + } + break; + + case EIOSCommand::REFLECT_INSTANCE_OF: + { + bool result = false; + jobject object = stream.read(); + std::string className = stream.read(); + + jclass cls = main_reflector->LoadClass(className.c_str()); + if (!cls) + { + result = main_reflector->IsDecendentOf(object, className.c_str()); + } + else + { + result = main_reflector->getEnv()->IsInstanceOf(object, cls); + } + + stream.write(result); + } + break; + + case EIOSCommand::REFLECT_RELEASE_OBJECT: + { + jobject object = stream.read(); + if (object) + { + main_reflector->getEnv()->DeleteGlobalRef(object); + } + } + break; + + case EIOSCommand::REFLECT_RELEASE_OBJECTS: + { + std::vector objects = stream.read>(); + for (std::size_t i = 0; i < objects.size(); ++i) + { + jobject object = stream.read(); + if (objects[i]) + { + main_reflector->getEnv()->DeleteGlobalRef(objects[i]); + } + } + } + break; + + case EIOSCommand::REFLECT_CHAR: + { + ReflectionHook hook; + stream >> hook; + + jchar utf16_char = main_reflector->getPrimitive(hook.object, hook); + char result = std::wstring_convert, char16_t>().to_bytes(utf16_char)[0]; + stream.write(result); + } + break; + + case EIOSCommand::REFLECT_BYTE: + { + ReflectionHook hook; + stream >> hook; + + std::uint8_t result = main_reflector->getPrimitive(hook.object, hook); + stream.write(result); + } + break; + + case EIOSCommand::REFLECT_BOOLEAN: + { + ReflectionHook hook; + stream >> hook; + + bool result = main_reflector->getPrimitive(hook.object, hook); + stream.write(result); + } + break; + + case EIOSCommand::REFLECT_SHORT: + { + ReflectionHook hook; + stream >> hook; + + std::int16_t result = main_reflector->getPrimitive(hook.object, hook); + stream.write(result); + } + break; + + case EIOSCommand::REFLECT_INT: + { + ReflectionHook hook; + stream >> hook; + + std::int32_t result = main_reflector->getPrimitive(hook.object, hook); + stream.write(result); + } + break; + + case EIOSCommand::REFLECT_LONG: + { + ReflectionHook hook; + stream >> hook; + + std::int64_t result = main_reflector->getPrimitive(hook.object, hook); + stream.write(result); + } + break; + + case EIOSCommand::REFLECT_FLOAT: + { + ReflectionHook hook; + stream >> hook; + + float result = main_reflector->getPrimitive(hook.object, hook); + stream.write(result); + } + break; + + case EIOSCommand::REFLECT_DOUBLE: + { + ReflectionHook hook; + stream >> hook; + + double result = main_reflector->getPrimitive(hook.object, hook); + stream.write(result); + } + break; + + case EIOSCommand::REFLECT_STRING: + { + ReflectionHook hook; + stream >> hook; + stream.write(main_reflector->getField(hook.object, hook)); + } + break; + + case EIOSCommand::REFLECT_ARRAY: + { + ReflectionHook hook; + stream >> hook; + + jarray result = main_reflector->getField(hook.object, hook); + stream.write(result); + } + break; + + case EIOSCommand::REFLECT_ARRAY_SIZE: + { + jobjectArray array = stream.read(); + std::size_t length = main_reflector->getEnv()->GetArrayLength(array); + stream.write(length); + } + break; + + case EIOSCommand::REFLECT_ARRAY_WITH_SIZE: + { + ReflectionHook hook; + stream >> hook; + + jarray array = main_reflector->getField(hook.object, hook); + if (array) + { + std::size_t length = main_reflector->getEnv()->GetArrayLength(array); + stream.write(length); + stream.write(array); + } + else + { + stream.write(0); + stream.write(nullptr); + } + } + break; + + case EIOSCommand::REFLECT_ARRAY_ALL: + { + jarray array = stream.read(); + ReflectionType type = stream.read(); + std::size_t dimensions = stream.read(); + process_reflect_array_all(stream, array, type, dimensions); + } + break; + + case EIOSCommand::REFLECT_ARRAY_WITH_INDEX_SIZE: + { + jarray array = stream.read(); + ReflectionType type = stream.read(); + std::size_t length = stream.read(); + std::size_t dimensions = stream.read(); + process_reflect_array_index_length(stream, array, type, length, dimensions); + } + break; + + case EIOSCommand::REFLECT_ARRAY_INDICES: + { + jarray array = stream.read(); + process_reflect_array_indices(stream, array); + } + break; + + case EIOSCommand::REFLECT_CLASS_NAME: + { + jobject object = stream.read(); + std::string result = main_reflector->GetClassName(object); + stream.write(result); + } + break; + + case EIOSCommand::REFLECT_CLASS_TYPE: + { + jobject object = stream.read(); + std::string result = main_reflector->GetClassType(object); + stream.write(result); + } + break; + + case EIOSCommand::REFLECT_CLASS_LOADER: + { + jobject object = stream.read(); + std::string result = main_reflector->GetClassType(object); + + // auto cls = make_safe_local(env, env->GetObjectClass(reflection->applet)); + // jmethodID mid = env->GetMethodID(cls.get(), "getClass", "()Ljava/lang/Class;"); + // auto clsObj = make_safe_local(env, env->CallObjectMethod(reflection->applet, mid)); + // cls.reset(env->GetObjectClass(clsObj.get())); + // + // //Get Canvas's ClassLoader. + // mid = env->GetMethodID(cls.get(), "getClassLoader", "()Ljava/lang/ClassLoader;"); + // reflection->classLoader = env->NewGlobalRef(make_safe_local(env, env->CallObjectMethod(clsObj.get(), mid)).get()); + // reflection->cache = std::make_shared(env, reflection->classLoader); + + stream.write(result); + } + break; + + case EIOSCommand::REMOTE_VM_INSTRUCTION: + { + this->remote_vm->process_command(&image_data); + } + break; + } +} + +void ControlCenter::send_array_response_index_length(Stream &stream, jarray array, ReflectionType type, std::size_t index, std::size_t length) const noexcept +{ + if (!array) + { + return; + } + + if (length == 0) + { + return; + } + + switch (type) + { + case ReflectionType::CHAR: + { + JNIEnv* env = main_reflector->getEnv(); + char16_t* result = static_cast(env->GetPrimitiveArrayCritical(array, nullptr)); + std::string utf8_result = std::wstring_convert, char16_t>().to_bytes(result); + env->ReleasePrimitiveArrayCritical(array, result, JNI_ABORT); + stream.write(utf8_result.c_str() + (index * sizeof(char)), length * sizeof(char)); + } + break; + + case ReflectionType::BYTE: + case ReflectionType::BOOL: + case ReflectionType::SHORT: + case ReflectionType::INT: + case ReflectionType::LONG: + case ReflectionType::FLOAT: + case ReflectionType::DOUBLE: + { + JNIEnv* env = main_reflector->getEnv(); + std::size_t element_size = reflect_size_for_type(type); + char* result = static_cast(env->GetPrimitiveArrayCritical(array, nullptr)); + stream.write(result + (index * element_size), length * element_size); + env->ReleasePrimitiveArrayCritical(array, result, JNI_ABORT); + } + break; + + case ReflectionType::STRING: + { + JNIEnv* env = main_reflector->getEnv(); + for (std::size_t i = 0; i < length; ++i) + { + jobject element = env->GetObjectArrayElement(static_cast(array), index + i); + stream.write(main_reflector->getField(static_cast(element))); + } + } + break; + + case ReflectionType::OBJECT: + { + std::vector objects(length); + JNIEnv* env = this->main_reflector->getEnv(); + for (std::size_t i = 0; i < length; ++i) + { + jobject result = env->GetObjectArrayElement(static_cast(array), index + i); + objects[i] = result ? env->NewGlobalRef(result) : nullptr; + } + stream.write(&objects[0], objects.size() * sizeof(jobject)); + } + break; + + case ReflectionType::ARRAY: + { + std::vector objects(length); + JNIEnv* env = this->main_reflector->getEnv(); + for (std::size_t i = 0; i < length; ++i) + { + jobject result = env->GetObjectArrayElement(static_cast(array), index + i); + objects[i] = result ? env->NewGlobalRef(result) : nullptr; + } + stream.write(&objects[0], objects.size() * sizeof(jobject)); + } + break; + } +} + +void ControlCenter::send_array_response_indices(Stream &stream, jarray array, ReflectionType type, std::int32_t* indices, std::size_t length) const noexcept +{ + if (!array) + { + return; + } + + if (length == 0) + { + return; + } + + switch (type) + { + case ReflectionType::CHAR: + { + JNIEnv* env = main_reflector->getEnv(); + char16_t* result = static_cast(env->GetPrimitiveArrayCritical(array, nullptr)); + std::string utf8_result = std::wstring_convert, char16_t>().to_bytes(result); + env->ReleasePrimitiveArrayCritical(array, result, JNI_ABORT); + + for (std::size_t i = 0; i < length; ++i) + { + stream.write(utf8_result.c_str() + (indices[i] * sizeof(char)), sizeof(char)); + } + } + break; + + case ReflectionType::BYTE: + case ReflectionType::BOOL: + case ReflectionType::SHORT: + case ReflectionType::INT: + case ReflectionType::LONG: + case ReflectionType::FLOAT: + case ReflectionType::DOUBLE: + { + JNIEnv* env = main_reflector->getEnv(); + std::size_t element_size = reflect_size_for_type(type); + + char* result = static_cast(env->GetPrimitiveArrayCritical(array, nullptr)); + for (std::size_t i = 0; i < length; ++i) + { + stream.write(result + (indices[i] * element_size), element_size); + } + env->ReleasePrimitiveArrayCritical(array, result, JNI_ABORT); + } + break; + + case ReflectionType::STRING: + { + for (std::size_t i = 0; i < length; ++i) + { + jobject element = this->main_reflector->getEnv()->GetObjectArrayElement(static_cast(array), indices[i]); + stream.write(this->main_reflector->getField(static_cast(element))); + } + } + break; + + case ReflectionType::OBJECT: + case ReflectionType::ARRAY: + { + std::vector objects(length); + for (std::size_t i = 0; i < length; ++i) + { + jobject result = this->main_reflector->getEnv()->GetObjectArrayElement(static_cast(array), indices[i]); + objects[i] = result ? this->main_reflector->getEnv()->NewGlobalRef(result) : nullptr; + } + stream.write(&objects[0], objects.size() * sizeof(jobject)); + } + break; + } +} + +void ControlCenter::process_reflect_array_indices(Stream &stream, jarray array) const noexcept +{ + ReflectionType type = stream.read(); + std::vector indices = stream.read>(); + stream.write(indices.size()); + send_array_response_indices(stream, array, type, &indices[0], indices.size()); +} + +void ControlCenter::process_reflect_array_index_length(Stream &stream, jarray array, ReflectionType type, std::size_t length, std::size_t dimensions) const noexcept +{ + if (!array) + { + stream.write(0); + return; + } + + if (dimensions == 1) + { + JNIEnv* env = this->main_reflector->getEnv(); + std::size_t index = stream.read(); + std::size_t real_length = env->GetArrayLength(array); + std::size_t ideal_length = std::min(length, real_length); + + if (index >= real_length) + { + fprintf(stderr, "Index out of bounds\n"); + stream.write(0); + return; + } + + stream.write(ideal_length); + send_array_response_index_length(stream, array, type, index, ideal_length); + return; + } + + JNIEnv* env = this->main_reflector->getEnv(); + + for (std::size_t i = 0; i < dimensions - 1; ++i) + { + std::size_t index = stream.read(); + + if (array) + { + std::size_t real_length = env->GetArrayLength(array); + if (index >= real_length) + { + fprintf(stderr, "Index out of bounds\n"); + stream.write(0); + return; + } + + array = static_cast(env->GetObjectArrayElement(static_cast(array), index)); + } + } + + if (!array) + { + stream.write(0); + return; + } + + std::size_t real_length = env->GetArrayLength(array); + std::size_t ideal_length = std::min(length, real_length); + std::size_t index = stream.read(); + + if (index >= real_length) + { + fprintf(stderr, "Index out of bounds\n"); + stream.write(0); + return; + } + + stream.write(ideal_length); + send_array_response_index_length(stream, array, type, index, ideal_length); +} + +void ControlCenter::process_reflect_array_all(Stream &stream, jarray array, ReflectionType type, std::size_t dimensions) const noexcept +{ + if (!array) + { + stream.write(0); + return; + } + + if (dimensions == 1) + { + JNIEnv* env = main_reflector->getEnv(); + std::size_t length = env->GetArrayLength(array); + stream.write(length); + this->send_array_response_index_length(stream, array, type, 0, length); + return; + } + + JNIEnv* env = main_reflector->getEnv(); + std::size_t length = env->GetArrayLength(array); + stream.write(length); + + for (std::size_t i = 0; i < length; ++i) + { + jarray result = static_cast(env->GetObjectArrayElement(static_cast(array), i)); + process_reflect_array_all(stream, result, type, dimensions - 1); + } +} + +bool ControlCenter::init_maps() noexcept +{ + char mapName[256] = {0}; + #if defined(_WIN32) || defined(_WIN64) + snprintf(mapName, sizeof(mapName) - 1, "Local\\RemoteInput_%d", pid); + #else + snprintf(mapName, sizeof(mapName) - 1, "RemoteInput_%d", pid); + #endif + + if (is_controller) + { + //Open existing map to retrieve its dimensions.. + memory_map = std::make_unique>(mapName, sizeof(ImageData), MemoryMapStream::open_mode::read); + if (memory_map && memory_map->is_mapped()) + { + int image_width = memory_map->data().image_width(); + int image_height = memory_map->data().image_height(); + + if (image_width && image_height) + { + std::size_t image_size = image_width * image_height * 4 * sizeof(std::uint8_t); + std::size_t debug_size = image_width * image_height * 4 * sizeof(std::uint8_t); + std::size_t extra_size = (1024 * sizeof(std::int32_t)); + std::size_t map_size = sizeof(EIOSData) + image_size + debug_size + extra_size; + + //Open only.. + memory_map = std::make_unique>(mapName, map_size, MemoryMapStream::open_mode::read | MemoryMapStream::open_mode::write); + return memory_map && memory_map->is_mapped(); + } + } + return false; + } + + int image_width = 0; + int image_height = 0; + GetDesktopResolution(image_width, image_height); + + std::size_t image_size = image_width * image_height * 4 * sizeof(std::uint8_t); + std::size_t debug_size = image_width * image_height * 4 * sizeof(std::uint8_t); + std::size_t extra_size = (1024 * sizeof(std::int32_t)); + std::size_t map_size = sizeof(EIOSData) + image_size + debug_size + extra_size; + + //Create only.. + memory_map = std::make_unique>(mapName, map_size, MemoryMapStream::open_mode::read | MemoryMapStream::open_mode::write | MemoryMapStream::open_mode::create); + bool result = memory_map && memory_map->is_mapped(); + if (result) + { + set_image_dimensions(image_width, image_height); + set_target_dimensions(image_width, image_height); + } + return result; +} + +bool ControlCenter::init_signals() noexcept +{ + #if defined(_WIN32) || defined(_WIN64) + char readName[256] = {0}; + char writeName[256] = {0}; + snprintf(readName, sizeof(readName) - 1, "Local\\RemoteInput_ControlCenter_EventRead_%d", pid); + snprintf(writeName, sizeof(writeName) - 1, "Local\\RemoteInput_ControlCenter_EventWrite_%d", pid); + #elif defined(__linux__) + char readName[256] = {0}; + char writeName[256] = {0}; + snprintf(readName, sizeof(readName) - 1, "/RemoteInput_ControlCenter_EventRead_%d", pid); + snprintf(writeName, sizeof(writeName) - 1, "/RemoteInput_ControlCenter_EventWrite_%d", pid); + #else + char readName[31] = {0}; //PSHMNAMELEN from + char writeName[31] = {0}; + snprintf(readName, sizeof(readName) - 1, "/RI_EventRead_%d", pid); + snprintf(writeName, sizeof(writeName) - 1, "/RI_EventWrite_%d", pid); + #endif + + command_signal = std::make_unique(readName); + response_signal = std::make_unique(writeName); + return command_signal && response_signal; +} + +bool ControlCenter::init_wait() noexcept +{ + #if defined(_WIN32) || defined(_WIN64) + char signalName[256] = {0}; + snprintf(signalName, sizeof(signalName) - 1, "Local\\RemoteInput_ControlCenter_SyncSignal_%d", pid); + #elif defined(__linux__) + char signalName[256] = {0}; + snprintf(signalName, sizeof(signalName) - 1, "/RemoteInput_ControlCenter_SyncSignal_%d", pid); + #else + char signalName[31] = {0}; //PSHMNAMELEN from + snprintf(signalName, sizeof(signalName) - 1, "/RI_SyncSignal_%d", pid); + #endif + + sync_signal = std::make_unique(signalName); + return sync_signal != nullptr; +} + +void ControlCenter::kill_zombie_process(std::int32_t pid) noexcept +{ + #if defined(_WIN32) || defined(_WIN64) + + #elif defined(__linux__) + char buffer[256] = {0}; + + //Kill Memory Maps + snprintf(buffer, sizeof(buffer) - 1, "RemoteInput_%d", pid); + shm_unlink(buffer); + + //Kill Signals + snprintf(buffer, sizeof(buffer) - 1, "/RemoteInput_ControlCenter_EventRead_%d", pid); + sem_unlink(buffer); + + snprintf(buffer, sizeof(buffer) - 1, "/RemoteInput_ControlCenter_EventWrite_%d", pid); + sem_unlink(buffer); + #else + char buffer[256] = {0}; + + //Kill Memory Maps + snprintf(buffer, sizeof(buffer) - 1, "RemoteInput_%d", pid); + shm_unlink(buffer); + + //Kill Signals + snprintf(buffer, sizeof(buffer) - 1, "/RI_EventRead_%d", pid); + sem_unlink(buffer); + + snprintf(buffer, sizeof(buffer) - 1, "/RI_EventWrite_%d", pid); + sem_unlink(buffer); + #endif +} + +bool ControlCenter::hasReflector() const noexcept +{ + return main_reflector != nullptr; +} + +ImageData* ControlCenter::get_image_data() const noexcept +{ + return memory_map && memory_map->is_mapped() ? &memory_map->data() : nullptr; +} + +std::int32_t ControlCenter::parent_process_id() const noexcept +{ + return memory_map && memory_map->is_mapped() ? get_image_data()->parent_process_id() : -1; +} + +std::int32_t ControlCenter::parent_thread_id() const noexcept +{ + return memory_map && memory_map->is_mapped() ? get_image_data()->parent_thread_id() : -1; +} + +std::int32_t ControlCenter::get_image_width() const noexcept +{ + return memory_map && memory_map->is_mapped() ? get_image_data()->image_width() : -1; +} + +std::int32_t ControlCenter::get_image_height() const noexcept +{ + return memory_map && memory_map->is_mapped() ? get_image_data()->image_height() : -1; +} + +std::int32_t ControlCenter::get_target_width() const noexcept +{ + return memory_map && memory_map->is_mapped() ? get_image_data()->target_width() : -1; +} + +std::int32_t ControlCenter::get_target_height() const noexcept +{ + return memory_map && memory_map->is_mapped() ? get_image_data()->target_height() : -1; +} + +std::uint8_t* ControlCenter::get_image() const noexcept +{ + ImageData* image_data = get_image_data(); + return image_data ? image_data->image_buffer() : nullptr; +} + +std::uint8_t* ControlCenter::get_debug_image() const noexcept +{ + ImageData* image_data = get_image_data(); + return image_data ? image_data->debug_image_buffer() : nullptr; +} + +bool ControlCenter::get_debug_graphics() const noexcept +{ + if (memory_map && memory_map->is_mapped()) + { + return get_image_data()->debug_graphics(); + } + return false; +} + +void ControlCenter::set_debug_graphics(bool enabled) const noexcept +{ + if (memory_map && memory_map->is_mapped()) + { + get_image_data()->set_debug_graphics(enabled); + } +} + +bool ControlCenter::get_graphics_scaling() const noexcept +{ + return memory_map && memory_map->is_mapped() ? get_image_data()->ui_scaling() : false; +} + +void ControlCenter::set_graphics_scaling(bool enabled) const noexcept +{ + if (memory_map && memory_map->is_mapped()) + { + get_image_data()->set_ui_scaling(enabled); + } +} + +void ControlCenter::set_parent_process_id(std::int32_t pid) const noexcept +{ + if (memory_map && memory_map->is_mapped()) + { + get_image_data()->set_parent_process_id(pid); + } +} + +void ControlCenter::set_parent_thread_id(std::int32_t tid) const noexcept +{ + if (memory_map && memory_map->is_mapped()) + { + get_image_data()->set_parent_thread_id(tid); + } +} + +void ControlCenter::signal_sync(std::int32_t pid) noexcept +{ + #if defined(_WIN32) || defined(_WIN64) + char signalName[256] = {0}; + snprintf(signalName, sizeof(signalName) - 1, "Local\\RemoteInput_ControlCenter_SyncSignal_%d", pid); + #elif defined(__linux__) + char signalName[256] = {0}; + snprintf(signalName, sizeof(signalName) - 1, "/RemoteInput_ControlCenter_SyncSignal_%d", pid); + #else + char signalName[31] = {0}; //PSHMNAMELEN from + snprintf(signalName, sizeof(signalName) - 1, "/RI_SyncSignal_%d", pid); + #endif + + auto sync_signal = std::make_unique(signalName); + + if (!sync_signal->is_signalled()) + { + sync_signal->signal(); + } +} + +void ControlCenter::wait_for_sync(std::int32_t pid) noexcept +{ + #if defined(_WIN32) || defined(_WIN64) + char signalName[256] = {0}; + snprintf(signalName, sizeof(signalName) - 1, "Local\\RemoteInput_ControlCenter_SyncSignal_%d", pid); + #elif defined(__linux__) + char signalName[256] = {0}; + snprintf(signalName, sizeof(signalName) - 1, "/RemoteInput_ControlCenter_SyncSignal_%d", pid); + #else + char signalName[31] = {0}; //PSHMNAMELEN from + snprintf(signalName, sizeof(signalName) - 1, "/RI_SyncSignal_%d", pid); + #endif + + auto sync_signal = std::make_unique(signalName); + + if (!sync_signal->is_signalled()) + { + //We shall wait no longer than 5 seconds for initialization of sync signals. + sync_signal->try_wait_for(std::chrono::seconds(5)); + } +} + +bool ControlCenter::controller_exists(std::int32_t pid) noexcept +{ + if (pid == getpid()) + { + return false; + } + + char mapName[256] = {0}; + #if defined(_WIN32) || defined(_WIN64) + snprintf(mapName, sizeof(mapName) - 1, "Local\\RemoteInput_%d", pid); + #else + snprintf(mapName, sizeof(mapName) - 1, "RemoteInput_%d", pid); + #endif + + return MemoryMapStream(mapName, 0, MemoryMapStream::open_mode::read).is_mapped(); +} + +bool ControlCenter::controller_is_paired(std::int32_t pid, bool* exists) noexcept +{ + if (pid == getpid()) + { + *exists = false; + return true; + } + + char mapName[256] = {0}; + #if defined(_WIN32) || defined(_WIN64) + snprintf(mapName, sizeof(mapName) - 1, "Local\\RemoteInput_%d", pid); + #else + snprintf(mapName, sizeof(mapName) - 1, "RemoteInput_%d", pid); + #endif + + *exists = false; + auto memory_map = MemoryMapStream(mapName, sizeof(EIOSData), MemoryMapStream::open_mode::read | MemoryMapStream::open_mode::write); + if (memory_map.is_mapped()) + { + *exists = true; + ImageData &data = memory_map.data(); + + if (data.parent_process_id() != -1 && !IsProcessAlive(data.parent_process_id())) + { + data.set_parent_process_id(-1); + data.set_parent_thread_id(-1); + } + + if (data.parent_thread_id() != -1 && !IsThreadAlive(data.parent_thread_id())) + { + data.set_parent_thread_id(-1); + } + + return data.parent_thread_id() != -1 && data.parent_thread_id() != GetCurrentThreadID(); + } + + return false; +} + +void ControlCenter::kill_process(std::int32_t pid) const noexcept +{ + send_command([](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::KILL_APPLICATION); + }); +} + +void ControlCenter::set_image_dimensions(std::int32_t width, std::int32_t height) const noexcept +{ + if (memory_map && memory_map->is_mapped()) + { + ImageData* image_data = get_image_data(); + image_data->set_image_width(width); + image_data->set_image_height(height); + } +} + +void ControlCenter::set_target_dimensions(std::int32_t width, std::int32_t height) const noexcept +{ + if (memory_map && memory_map->is_mapped()) + { + ImageData* image_data = get_image_data(); + if (image_data->ui_scaling()) + { + std::int32_t x = 0; + std::int32_t y = 0; + std::size_t w = 0; + std::size_t h = 0; + + get_applet_dimensions(&x, &y, &w, &h); + + if (w > 0 && h > 0) + { + image_data->set_target_width(w); + image_data->set_target_height(h); + } + else + { + image_data->set_target_width(width); + image_data->set_target_height(height); + } + } + else + { + image_data->set_target_width(width); + image_data->set_target_height(height); + } + } +} + +void ControlCenter::get_image_dimensions(std::int32_t* width, std::int32_t* height) const noexcept +{ + if (memory_map && memory_map->is_mapped()) + { + *width = get_image_width(); + *height = get_image_height(); + } + else + { + *width = -1; + *height = -1; + } +} + +void ControlCenter::get_target_dimensions(std::int32_t* width, std::int32_t* height) const noexcept +{ + if (memory_map && memory_map->is_mapped()) + { + *width = get_target_width(); + *height = get_target_height(); + } + else + { + *width = -1; + *height = -1; + } +} + +ImageFormat ControlCenter::get_image_format() const noexcept +{ + return memory_map && memory_map->is_mapped() ? get_image_data()->image_format() : ImageFormat::BGR_BGRA; +} + +void ControlCenter::set_image_format(ImageFormat format) noexcept +{ + if (memory_map && memory_map->is_mapped()) + { + get_image_data()->set_image_format(format); + } +} + +bool ControlCenter::has_focus() const noexcept +{ + bool result = send_command([](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::HAS_FOCUS); + }); + + if (result) + { + return get_image_data()->data_stream().read(); + } + return false; +} + +void ControlCenter::gain_focus() const noexcept +{ + send_command([](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::GAIN_FOCUS); + }); +} + +void ControlCenter::lose_focus() const noexcept +{ + send_command([](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::LOSE_FOCUS); + }); +} + +bool ControlCenter::is_keyboard_input_enabled() const noexcept +{ + bool result = send_command([](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::IS_KEYBOARD_INPUT_ENABLED); + }); + + if (result) + { + return get_image_data()->data_stream().read(); + } + + return false; +} + +void ControlCenter::set_keyboard_input_enabled(bool enabled) const noexcept +{ + send_command([enabled](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::SET_KEYBOARD_INPUT_ENABLED); + stream.write(enabled); + }); +} + +bool ControlCenter::is_mouse_input_enabled() const noexcept +{ + bool result = send_command([](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::IS_MOUSE_INPUT_ENABLED); + }); + + if (result) + { + return get_image_data()->data_stream().read(); + } + + return false; +} + +void ControlCenter::set_mouse_input_enabled(bool enabled) const noexcept +{ + send_command([enabled](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::SET_MOUSE_INPUT_ENABLED); + stream.write(enabled); + }); +} + +void ControlCenter::get_mouse_position(std::int32_t* x, std::int32_t* y) const noexcept +{ + bool result = send_command([](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::GET_MOUSE); + }); + + if (result) + { + Stream& stream = get_image_data()->data_stream(); + *x = stream.read(); + *y = stream.read(); + } +} + +void ControlCenter::get_real_mouse_position(std::int32_t* x, std::int32_t* y) const noexcept +{ + bool result = send_command([](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::GET_REAL_MOUSE); + }); + + if (result) + { + Stream& stream = get_image_data()->data_stream(); + *x = stream.read(); + *y = stream.read(); + } +} + +void ControlCenter::move_mouse(std::int32_t x, std::int32_t y) const noexcept +{ + send_command([x, y](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::MOVE_MOUSE); + stream.write(x); + stream.write(y); + }); +} + +void ControlCenter::hold_mouse(std::int32_t button) const noexcept +{ + send_command([button](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::HOLD_MOUSE); + stream.write(button); + }); +} + +void ControlCenter::release_mouse(std::int32_t button) const noexcept +{ + send_command([button](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::RELEASE_MOUSE); + stream.write(button); + }); +} + +void ControlCenter::scroll_mouse(std::int32_t lines) const noexcept +{ + send_command([lines](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::SCROLL_MOUSE); + stream.write(lines); + }); +} + +bool ControlCenter::is_mouse_held(std::int32_t button) const noexcept +{ + bool result = send_command([button](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::IS_MOUSE_HELD); + stream.write(button); + }); + + if (result) + { + return get_image_data()->data_stream().read(); + } + return false; +} + +void ControlCenter::send_string(const char* string, std::int32_t keywait, std::int32_t keymodwait) const noexcept +{ + send_command([string, keywait, keymodwait](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::SEND_STRING); + image_data->data_stream() << string; + stream.write(keywait); + stream.write(keymodwait); + }); +} + +void ControlCenter::send_key(char key, std::int32_t key_down_time, std::int32_t key_up_time, std::int32_t modifier_down_time, std::int32_t modifier_up_time) const noexcept +{ + send_command([key, key_down_time, key_up_time, modifier_down_time, modifier_up_time](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::SEND_KEY); + stream.write(key); + stream.write(key_down_time); + stream.write(key_up_time); + stream.write(modifier_down_time); + stream.write(modifier_up_time); + }); +} + +void ControlCenter::key_send(const char* string, std::int32_t len, std::int32_t* sleeptimes) const noexcept +{ + send_command([string, len, sleeptimes](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::KEY_SEND); + image_data->data_stream() << std::string(string, len); + stream.write(sleeptimes, (len * 4) * sizeof(int32_t)); + }); +} + +void ControlCenter::hold_key(std::int32_t key) const noexcept +{ + send_command([key](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::HOLD_KEY); + stream.write(key); + }); +} + +void ControlCenter::release_key(std::int32_t key) const noexcept +{ + send_command([key](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::RELEASE_KEY); + stream.write(key); + }); +} + +bool ControlCenter::is_key_held(std::int32_t key) const noexcept +{ + bool result = send_command([key](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::IS_KEY_HELD); + stream.write(key); + }); + + if (result) + { + return get_image_data()->data_stream().read(); + } + return false; +} + +std::int32_t ControlCenter::get_keyboard_speed() const noexcept +{ + bool result = send_command([](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::GET_KEYBOARD_SPEED); + }); + + if (result) + { + return get_image_data()->data_stream().read(); + } + return -1; +} + +void ControlCenter::set_keyboard_speed(std::int32_t speed) const noexcept +{ + send_command([speed](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::SET_KEYBOARD_SPEED); + stream.write(speed); + }); +} + +std::int32_t ControlCenter::get_keyboard_repeat_delay() const noexcept +{ + bool result = send_command([](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::GET_KEYBOARD_REPEAT_DELAY); + }); + + if (result) + { + return get_image_data()->data_stream().read(); + } + return -1; +} + +void ControlCenter::set_keyboard_repeat_delay(std::int32_t delay) const noexcept +{ + send_command([delay](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::SET_KEYBOARD_REPEAT_DELAY); + stream.write(delay); + }); +} + +void ControlCenter::stop_all_processing() const noexcept +{ + send_command([](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::STOP_ALL_PROCESSING); + }); +} + +bool ControlCenter::reflect_is_objects_equal(const jobject first, const jobject second) const noexcept +{ + bool result = send_command([&](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::REFLECT_COMPARE_OBJECTS); + stream.write(first); + stream.write(second); + }); + + if (result) + { + return get_image_data()->data_stream().read(); + } + return false; +} + +bool ControlCenter::reflect_instance_of(const jobject object, std::string cls) const noexcept +{ + bool result = send_command([&](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::REFLECT_INSTANCE_OF); + stream.write(object); + stream.write(cls); + }); + + if (result) + { + return get_image_data()->data_stream().read(); + } + return false; +} + +jobject ControlCenter::reflect_object(const ReflectionHook &hook) const noexcept +{ + bool result = send_command([&](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::REFLECT_OBJECT); + stream << hook; + }); + + if (result) + { + return get_image_data()->data_stream().read(); + } + return nullptr; +} + +void ControlCenter::reflect_release_object(const jobject object) const noexcept +{ + send_command([&](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::REFLECT_RELEASE_OBJECT); + stream.write(object); + }); +} + +void ControlCenter::reflect_release_objects(const jobject* array, std::size_t length) const noexcept +{ + send_command([&](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::REFLECT_RELEASE_OBJECTS); + stream.write(length); + stream.write(array, length * sizeof(jobject)); + }); +} + +char ControlCenter::reflect_char(const ReflectionHook &hook) const noexcept +{ + bool result = send_command([&](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::REFLECT_CHAR); + stream << hook; + }); + + if (result) + { + return get_image_data()->data_stream().read(); + } + return '\0'; +} + +std::uint8_t ControlCenter::reflect_byte(const ReflectionHook &hook) const noexcept +{ + bool result = send_command([&](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::REFLECT_BYTE); + stream << hook; + }); + + if (result) + { + return get_image_data()->data_stream().read(); + } + return 0; +} + +bool ControlCenter::reflect_boolean(const ReflectionHook &hook) const noexcept +{ + bool result = send_command([&](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::REFLECT_BOOLEAN); + stream << hook; + }); + + if (result) + { + return get_image_data()->data_stream().read(); + } + return false; +} + +std::int16_t ControlCenter::reflect_short(const ReflectionHook &hook) const noexcept +{ + bool result = send_command([&](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::REFLECT_SHORT); + stream << hook; + }); + + if (result) + { + return get_image_data()->data_stream().read(); + } + return -1; +} + +std::int32_t ControlCenter::reflect_int(const ReflectionHook &hook) const noexcept +{ + bool result = send_command([&](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::REFLECT_INT); + stream << hook; + }); + + if (result) + { + return get_image_data()->data_stream().read(); + } + return -1; +} + +std::int64_t ControlCenter::reflect_long(const ReflectionHook &hook) const noexcept +{ + bool result = send_command([&](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::REFLECT_LONG); + stream << hook; + }); + + if (result) + { + return get_image_data()->data_stream().read(); + } + return -1; +} + +float ControlCenter::reflect_float(const ReflectionHook &hook) const noexcept +{ + bool result = send_command([&](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::REFLECT_FLOAT); + stream << hook; + }); + + if (result) + { + return get_image_data()->data_stream().read(); + } + return -1; +} + +double ControlCenter::reflect_double(const ReflectionHook &hook) const noexcept +{ + bool result = send_command([&](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::REFLECT_DOUBLE); + stream << hook; + }); + + if (result) + { + return get_image_data()->data_stream().read(); + } + return -1; +} + +std::string ControlCenter::reflect_string(const ReflectionHook &hook) const noexcept +{ + bool result = send_command([&](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::REFLECT_STRING); + stream << hook; + }); + + if (result) + { + return get_image_data()->data_stream().read(); + } + return ""; +} + +jarray ControlCenter::reflect_array(const ReflectionHook &hook) const noexcept +{ + bool result = send_command([&](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::REFLECT_ARRAY); + stream << hook; + }); + + if (result) + { + return get_image_data()->data_stream().read(); + } + return nullptr; +} + +jarray ControlCenter::reflect_array(const ReflectionHook &hook, std::size_t* output_size) const noexcept +{ + bool result = send_command([&](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::REFLECT_ARRAY_WITH_SIZE); + stream << hook; + }); + + if (result) + { + Stream& stream = get_image_data()->data_stream(); + *output_size = stream.read(); + return stream.read(); + } + return nullptr; +} + +std::size_t ControlCenter::reflect_array_size(const jarray array) const noexcept +{ + bool result = send_command([&](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::REFLECT_ARRAY_SIZE); + stream.write(array); + }); + + if (result) + { + return get_image_data()->data_stream().read(); + } + return 0; +} + +ImageData* ControlCenter::reflect_array_all(const jarray array, ReflectionType type, std::size_t dimensions) const noexcept +{ + bool result = send_command([&](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::REFLECT_ARRAY_ALL); + stream.write(array); + stream.write(type); + stream.write(dimensions); + }); + + if (result) + { + return get_image_data(); + } + return 0; +} + +ImageData* ControlCenter::reflect_array_indices(const jarray array, ReflectionType type, std::int32_t* indices, std::size_t length) const noexcept +{ + bool result = send_command([&](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::REFLECT_ARRAY_INDICES); + stream.write(array); + stream.write(type); + stream.write(length); + stream.write(indices, length * sizeof(std::int32_t)); + }); + + if (result) + { + return get_image_data(); + } + return nullptr; +} + +std::string ControlCenter::reflect_class_name(const jobject object) const noexcept +{ + bool result = send_command([&](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::REFLECT_CLASS_NAME); + stream.write(object); + }); + + if (result) + { + Stream& stream = get_image_data()->data_stream(); + return stream.read(); + } + return std::string(); +} + +std::string ControlCenter::reflect_class_type(const jobject object) const noexcept +{ + bool result = send_command([&](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::REFLECT_CLASS_TYPE); + stream.write(object); + }); + + if (result) + { + Stream& stream = get_image_data()->data_stream(); + return stream.read(); + } + return std::string(); +} + +jobject ControlCenter::reflect_class_loader(const jobject object) const noexcept +{ + bool result = send_command([&](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::REFLECT_CLASS_LOADER); + stream.write(object); + }); + + if (result) + { + Stream& stream = get_image_data()->data_stream(); + return stream.read(); + } + return nullptr; +} + +std::size_t ControlCenter::reflect_size_for_type(ReflectionType type) noexcept +{ + static_assert(sizeof(jchar) == sizeof(char16_t) && + sizeof(jboolean) == sizeof(bool) && + sizeof(jshort) == sizeof(short) && + sizeof(jint) == sizeof(std::int32_t) && + sizeof(jlong) == sizeof(std::int64_t) && + sizeof(jfloat) == sizeof(float) && + sizeof(jdouble) == sizeof(double) && + sizeof(jstring) == sizeof(void*) && + sizeof(jobject) == sizeof(void*) && + sizeof(jarray) == sizeof(void*), + "Size of primitive types must be equal"); + + static std::size_t mapping[] = { + sizeof(jchar), + sizeof(jbyte), + sizeof(jboolean), + sizeof(jshort), + sizeof(jint), + sizeof(jlong), + sizeof(jfloat), + sizeof(jdouble), + sizeof(jstring), + sizeof(jobject), + sizeof(jarray) + }; + + return mapping[static_cast::type>(type)]; +} + +jlong ControlCenter::reflect_frame_native_handle() const noexcept +{ + return main_reflector->getFrameHandle(); +} + +jlong ControlCenter::reflect_applet_native_handle() const noexcept +{ + return main_reflector->getNativeHandle(reflect_applet().get()); +} + +jlong ControlCenter::reflect_canvas_native_handle() const noexcept +{ + return main_reflector->getNativeHandle(reflect_canvas().get()); +} + +java::Applet ControlCenter::reflect_applet() const noexcept +{ + return {main_reflector->getEnv(), main_reflector->getApplet(), false}; +} + +java::Component ControlCenter::reflect_canvas() const noexcept +{ + java::Applet applet{main_reflector->getEnv(), main_reflector->getApplet(), false}; + return applet.getComponent(0); +} + +std::unique_ptr ControlCenter::create_remote_vm() noexcept +{ + return std::make_unique(nullptr, + this, + [this](std::function &&fn) -> bool { + return send_command(std::forward>(fn)); + }, + &ControlCenter::get_image_data); +} + +void ControlCenter::get_applet_dimensions(std::int32_t* x, std::int32_t* y, std::size_t* width, std::size_t* height) const noexcept +{ + if (io_controller) + { + io_controller->get_applet_dimensions(*x, *y, *width, *height); + } +} + +void ControlCenter::get_canvas_dimensions(std::int32_t* x, std::int32_t* y, std::size_t* width, std::size_t* height) const noexcept +{ + if (io_controller) + { + io_controller->get_canvas_dimensions(*x, *y, *width, *height); + } +} + +void ControlCenter::get_applet_mouse_position(std::int32_t* x, std::int32_t* y) const noexcept +{ + if (io_controller) + { + io_controller->get_applet_mouse_position(*x, *y); + } +} diff --git a/third-party/RemoteInput/RemoteInput/Plugin/ControlCenter.hxx b/third-party/RemoteInput/RemoteInput/Plugin/ControlCenter.hxx new file mode 100644 index 0000000..fdf743d --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Plugin/ControlCenter.hxx @@ -0,0 +1,225 @@ +#ifndef ControlCenter_HXX_INCLUDED +#define ControlCenter_HXX_INCLUDED + +#include +#include + +#include "Event.hxx" +#include "Semaphore.hxx" +#include "Signal.hxx" +#include "Reflection.hxx" +#include "Applet.hxx" +#include "MemoryMapStream.hxx" +#include "InputOutput.hxx" +#include "TypeTraits.hxx" +#include "EIOSTypes.hxx" +#include "ImageData.hxx" +#include "Platform.hxx" +#include "Synchronization.hxx" +#include "Atomics.hxx" + +class RemoteVM; + +class ControlCenter final { +private: + using Signal = ::Signal; + //using Signal = ::Signal>; + + std::int32_t pid; + bool is_controller; + std::atomic_bool stopped; + std::unique_ptr command_signal; + std::unique_ptr response_signal; + std::unique_ptr sync_signal; + std::unique_ptr main_reflector; + std::unique_ptr> memory_map; + std::unique_ptr io_controller; + std::unique_ptr remote_vm; + + bool init_maps() noexcept; + bool init_signals() noexcept; + bool init_wait() noexcept; + void process_command() noexcept; + + ImageData* get_image_data() const noexcept; + + template + bool send_command(Func &&sender) const noexcept; + + void send_array_response_index_length(Stream &stream, jarray array, ReflectionType type, std::size_t index, std::size_t length) const noexcept; + void send_array_response_indices(Stream &stream, jarray array, ReflectionType type, std::int32_t* indices, std::size_t length) const noexcept; + void process_reflect_array_indices(Stream &stream, jarray array) const noexcept; + void process_reflect_array_index_length(Stream &stream, jarray array, ReflectionType type, std::size_t length, std::size_t dimensions) const noexcept; + void process_reflect_array_all(Stream &stream, jarray array, ReflectionType type, std::size_t dimensions) const noexcept; + +public: + ControlCenter(std::int32_t pid, bool is_controller, std::unique_ptr reflector); + ~ControlCenter(); + + void terminate() noexcept; + bool hasReflector() const noexcept; + void set_parent_process_id(std::int32_t pid) const noexcept; + void set_parent_thread_id(std::int32_t tid) const noexcept; + void set_image_dimensions(std::int32_t width, std::int32_t height) const noexcept; + void set_target_dimensions(std::int32_t width, std::int32_t height) const noexcept; + + static void signal_sync(std::int32_t pid) noexcept; + static void wait_for_sync(std::int32_t pid) noexcept; + static bool controller_exists(std::int32_t pid) noexcept; + static bool controller_is_paired(std::int32_t pid, bool* exists) noexcept; + void kill_process(std::int32_t pid) const noexcept; + static void kill_zombie_process(std::int32_t pid) noexcept; + + std::int32_t parent_process_id() const noexcept; + std::int32_t parent_thread_id() const noexcept; + std::int32_t get_image_width() const noexcept; + std::int32_t get_image_height() const noexcept; + std::int32_t get_target_width() const noexcept; + std::int32_t get_target_height() const noexcept; + std::uint8_t* get_image() const noexcept; + std::uint8_t* get_debug_image() const noexcept; + bool get_debug_graphics() const noexcept; + void set_debug_graphics(bool enabled) const noexcept; + bool get_graphics_scaling() const noexcept; + void set_graphics_scaling(bool enabled) const noexcept; + + bool has_focus() const noexcept; + void gain_focus() const noexcept; + void lose_focus() const noexcept; + bool is_keyboard_input_enabled() const noexcept; + void set_keyboard_input_enabled(bool enabled) const noexcept; + bool is_mouse_input_enabled() const noexcept; + void set_mouse_input_enabled(bool enabled) const noexcept; + void get_image_dimensions(std::int32_t* width, std::int32_t* height) const noexcept; + void get_target_dimensions(std::int32_t* width, std::int32_t* height) const noexcept; + ImageFormat get_image_format() const noexcept; + void set_image_format(ImageFormat format) noexcept; + void get_mouse_position(std::int32_t* x, std::int32_t* y) const noexcept; + void get_real_mouse_position(std::int32_t* x, std::int32_t* y) const noexcept; + void move_mouse(std::int32_t x, std::int32_t y) const noexcept; + void hold_mouse(std::int32_t button) const noexcept; + void release_mouse(std::int32_t button) const noexcept; + void scroll_mouse(std::int32_t lines) const noexcept; + bool is_mouse_held(std::int32_t button) const noexcept; + void send_string(const char* string, std::int32_t keywait, std::int32_t keymodwait) const noexcept; + void send_key(char key, std::int32_t key_down_time, std::int32_t key_up_time, std::int32_t modifier_down_time, std::int32_t modifier_up_time) const noexcept; + void key_send(const char* string, std::int32_t len, std::int32_t* sleeptimes) const noexcept; + void hold_key(std::int32_t key) const noexcept; + void release_key(std::int32_t key) const noexcept; + bool is_key_held(std::int32_t key) const noexcept; + std::int32_t get_keyboard_speed() const noexcept; + void set_keyboard_speed(std::int32_t speed) const noexcept; + std::int32_t get_keyboard_repeat_delay() const noexcept; + void set_keyboard_repeat_delay(std::int32_t delay) const noexcept; + void stop_all_processing() const noexcept; + + bool reflect_is_objects_equal(const jobject first, const jobject second) const noexcept; + bool reflect_instance_of(const jobject object, std::string cls) const noexcept; + jobject reflect_object(const ReflectionHook &hook) const noexcept; + void reflect_release_object(const jobject object) const noexcept; + void reflect_release_objects(const jobject* array, std::size_t length) const noexcept; + char reflect_char(const ReflectionHook &hook) const noexcept; + std::uint8_t reflect_byte(const ReflectionHook &hook) const noexcept; + bool reflect_boolean(const ReflectionHook &hook) const noexcept; + std::int16_t reflect_short(const ReflectionHook &hook) const noexcept; + std::int32_t reflect_int(const ReflectionHook &hook) const noexcept; + std::int64_t reflect_long(const ReflectionHook &hook) const noexcept; + float reflect_float(const ReflectionHook &hook) const noexcept; + double reflect_double(const ReflectionHook &hook) const noexcept; + std::string reflect_string(const ReflectionHook &hook) const noexcept; + + jarray reflect_array(const ReflectionHook &hook) const noexcept; + jarray reflect_array(const ReflectionHook &hook, std::size_t* output_size) const noexcept; + std::size_t reflect_array_size(const jarray array) const noexcept; + ImageData* reflect_array_all(const jarray array, ReflectionType type, std::size_t dimensions) const noexcept; + + template + typename std::enable_if::value, ImageData*>::type reflect_array(const jarray array, const ReflectionType type, const std::size_t length, const Index... index) const noexcept; + + template + typename std::enable_if::value, ImageData*>::type reflect_array(const ReflectionHook &hook, const std::size_t length, const ReflectionType type, const Index... index) const noexcept; + + + ImageData* reflect_array_indices(const jarray array, ReflectionType type, std::int32_t* indices, std::size_t length) const noexcept; + + std::string reflect_class_name(const jobject object) const noexcept; + std::string reflect_class_type(const jobject object) const noexcept; + jobject reflect_class_loader(const jobject object) const noexcept; + + static std::size_t reflect_size_for_type(ReflectionType type) noexcept; + + jlong reflect_frame_native_handle() const noexcept; + jlong reflect_applet_native_handle() const noexcept; + jlong reflect_canvas_native_handle() const noexcept; + java::Applet reflect_applet() const noexcept; + java::Component reflect_canvas() const noexcept; + std::unique_ptr create_remote_vm() noexcept; + + void get_applet_dimensions(std::int32_t* x, std::int32_t* y, std::size_t* width, std::size_t* height) const noexcept; + void get_canvas_dimensions(std::int32_t* x, std::int32_t* y, std::size_t* width, std::size_t* height) const noexcept; + void get_applet_mouse_position(std::int32_t* x, std::int32_t* y) const noexcept; +}; + +template +bool ControlCenter::send_command(Func &&sender) const noexcept +{ + ImageData* image_data = get_image_data(); + if (!image_data) + { + return false; + } + + image_data->prepare_for_read(); + image_data->prepare_for_write(); + + std::forward(sender)(image_data->data_stream(), image_data); + command_signal->signal(); + + static const std::int64_t timeout = 5000; + auto start = std::chrono::high_resolution_clock::now(); + while(elapsed_time(start) < timeout) + { + if (response_signal->try_wait()) + { + return true; + } + + //6 is a 1ns higher than default resolution on MacOS and Linux. + //Spikes around 10% CPU usage for thousands of calls per second. + std::this_thread::sleep_for(std::chrono::nanoseconds(6)); + } + + return false; +} + +template +typename std::enable_if::value, ImageData*>::type ControlCenter::reflect_array(const jarray array, const ReflectionType type, const std::size_t length, const Args... index) const noexcept +{ + send_command([&](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::REFLECT_ARRAY_WITH_INDEX_SIZE); + stream.write(array); + stream.write(type); + stream.write(length); + stream.write(sizeof...(index)); // dimensions + (stream.write(index), ...); // x, y, z, w, ... + }); + + return get_image_data(); +} + +template +typename std::enable_if::value, ImageData*>::type ControlCenter::reflect_array(const ReflectionHook &hook, const std::size_t length, const ReflectionType type, const Args... index) const noexcept +{ + send_command([&](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::REFLECT_ARRAY_WITH_INDEX_SIZE); + image_data->data_stream() << hook; + stream.write(type); + stream.write(length); + stream.write(sizeof...(index)); // dimensions + (stream.write(index), ...); // x, y, z, w, ... + }); + + return get_image_data(); +} + +#endif /* ControlCenter_HXX_INCLUDED */ diff --git a/third-party/RemoteInput/RemoteInput/Plugin/Graphics.cxx b/third-party/RemoteInput/RemoteInput/Plugin/Graphics.cxx new file mode 100644 index 0000000..353fa52 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Plugin/Graphics.cxx @@ -0,0 +1,1908 @@ +#include "Graphics.hxx" +#include "Thirdparty/Hook.hxx" +#include +#include + +#if defined(_WIN32) || defined(_WIN64) +#if defined(_MSC_VER) +#include +#include + +#define GL_PIXEL_PACK_BUFFER 0x88EB +#define GL_STREAM_READ 0x88E1 +#define GL_READ_ONLY 0x88B8 +#define GL_CLAMP_TO_EDGE 0x812F +#define GL_BGRA 0x80E1 +#else +#include +#include +#endif +#elif defined(__APPLE__) +#include +#include +#include +#include +#else +#include +#include +#include +#endif + + +#if !defined(GL_TEXTURE_RECTANGLE) +#define GL_TEXTURE_RECTANGLE 0x84F5 +#endif + +#if !defined(GL_PROGRAM_POINT_SIZE) +#define GL_PROGRAM_POINT_SIZE 0x8642 +#endif + +#if defined(__APPLE__) +#define GL_VERTEX_ARRAY_BINDING GL_VERTEX_ARRAY_BINDING_APPLE +#define glGenVertexArrays glGenVertexArraysAPPLE +#define glBindVertexArray glBindVertexArrayAPPLE +#endif + + +// ARGB To Format + +std::uint32_t argb_to_abgr(std::uint32_t argb_colour) +{ + std::uint8_t a = (argb_colour >> 24) & 0xFF; + std::uint8_t r = (argb_colour >> 16) & 0xFF; + std::uint8_t g = (argb_colour >> 8) & 0xFF; + std::uint8_t b = (argb_colour >> 0) & 0xFF; + return (a << 24) | (b << 16) | (g << 8) | r; +} + +std::uint32_t argb_to_bgra(std::uint32_t argb_colour) +{ + std::uint8_t a = (argb_colour >> 24) & 0xFF; + std::uint8_t r = (argb_colour >> 16) & 0xFF; + std::uint8_t g = (argb_colour >> 8) & 0xFF; + std::uint8_t b = (argb_colour >> 0) & 0xFF; + return (b << 24) | (g << 16) | (r << 8) | a; +} + +std::uint32_t argb_to_rgba(std::uint32_t argb_colour) +{ + std::uint8_t a = (argb_colour >> 24) & 0xFF; + std::uint8_t r = (argb_colour >> 16) & 0xFF; + std::uint8_t g = (argb_colour >> 8) & 0xFF; + std::uint8_t b = (argb_colour >> 0) & 0xFF; + return (r << 24) | (g << 16) | (b << 8) | a; +} + +// ABGR to Format + +std::uint32_t abgr_to_argb(std::uint32_t abgr_colour) +{ + std::uint8_t a = (abgr_colour >> 24) & 0xFF; + std::uint8_t b = (abgr_colour >> 16) & 0xFF; + std::uint8_t g = (abgr_colour >> 8) & 0xFF; + std::uint8_t r = (abgr_colour >> 0) & 0xFF; + return (a << 24) | (r << 16) | (g << 8) | b; +} + +std::uint32_t abgr_to_bgra(std::uint32_t abgr_colour) +{ + std::uint8_t a = (abgr_colour >> 24) & 0xFF; + std::uint8_t b = (abgr_colour >> 16) & 0xFF; + std::uint8_t g = (abgr_colour >> 8) & 0xFF; + std::uint8_t r = (abgr_colour >> 0) & 0xFF; + return (a << 24) | (b << 16) | (g << 8) | r; +} + +std::uint32_t abgr_to_rgba(std::uint32_t abgr_colour) +{ + std::uint8_t a = (abgr_colour >> 24) & 0xFF; + std::uint8_t b = (abgr_colour >> 16) & 0xFF; + std::uint8_t g = (abgr_colour >> 8) & 0xFF; + std::uint8_t r = (abgr_colour >> 0) & 0xFF; + return (r << 24) | (g << 16) | (b << 8) | a; +} + +// RGBA to Format + +std::uint32_t rgba_to_argb(std::uint32_t rgba_colour) +{ + std::uint8_t r = (rgba_colour >> 24) & 0xFF; + std::uint8_t g = (rgba_colour >> 16) & 0xFF; + std::uint8_t b = (rgba_colour >> 8) & 0xFF; + std::uint8_t a = (rgba_colour >> 0) & 0xFF; + return (a << 24) | (r << 16) | (g << 8) | b; +} + +std::uint32_t rgba_to_abgr(std::uint32_t rgba_colour) +{ + std::uint8_t r = (rgba_colour >> 24) & 0xFF; + std::uint8_t g = (rgba_colour >> 16) & 0xFF; + std::uint8_t b = (rgba_colour >> 8) & 0xFF; + std::uint8_t a = (rgba_colour >> 0) & 0xFF; + return (a << 24) | (b << 16) | (g << 8) | r; +} + +std::uint32_t rgba_to_bgra(std::uint32_t rgba_colour) +{ + std::uint8_t r = (rgba_colour >> 24) & 0xFF; + std::uint8_t g = (rgba_colour >> 16) & 0xFF; + std::uint8_t b = (rgba_colour >> 8) & 0xFF; + std::uint8_t a = (rgba_colour >> 0) & 0xFF; + return (b << 24) | (g << 16) | (r << 8) | a; +} + +// BGRA to Format + +std::uint32_t bgra_to_abgr(std::uint32_t bgra_colour) +{ + std::uint8_t b = (bgra_colour >> 24) & 0xFF; + std::uint8_t g = (bgra_colour >> 16) & 0xFF; + std::uint8_t r = (bgra_colour >> 8) & 0xFF; + std::uint8_t a = (bgra_colour >> 0) & 0xFF; + return (a << 24) | (b << 16) | (g << 8) | r; +} + +std::uint32_t bgra_to_argb(std::uint32_t bgra_colour) +{ + std::uint8_t b = (bgra_colour >> 24) & 0xFF; + std::uint8_t g = (bgra_colour >> 16) & 0xFF; + std::uint8_t r = (bgra_colour >> 8) & 0xFF; + std::uint8_t a = (bgra_colour >> 0) & 0xFF; + return (a << 24) | (r << 16) | (g << 8) | b; +} + +std::uint32_t bgra_to_rgba(std::uint32_t bgra_colour) +{ + std::uint8_t b = (bgra_colour >> 24) & 0xFF; + std::uint8_t g = (bgra_colour >> 16) & 0xFF; + std::uint8_t r = (bgra_colour >> 8) & 0xFF; + std::uint8_t a = (bgra_colour >> 0) & 0xFF; + return (r << 24) | (g << 16) | (b << 8) | a; +} + +// Other + +std::uint32_t RGBA(std::uint8_t r, std::uint8_t g, std::uint8_t b, std::uint8_t a) noexcept +{ + return (r << 24) + (g << 16) + (b << 8) + (a); +} + +void RGBA(std::uint32_t colour, std::uint8_t& r, std::uint8_t& g, std::uint8_t& b, std::uint8_t& a) noexcept +{ + r = (colour >> 24) & 0xFF; + g = (colour >> 16) & 0xFF; + b = (colour >> 8) & 0xFF; + a = (colour & 0xFF); +} + +template +void convert_pixels(S source, D dest, std::int32_t width, std::int32_t height, std::int32_t stride) +{ + if constexpr(std::is_same::value) + { + std::memcpy(dest, source, width * height * stride); + } + else if constexpr(std::is_same::value) + { + for (std::int32_t i = 0; i < width * height * stride; i += stride) + { + dest->a = *reinterpret_cast(source) == 0x00 ? 0x00 : 0xFF; + if (dest->a != 0x00) + { + dest->r = source->r; + dest->g = source->g; + dest->b = source->b; + } + ++source; + ++dest; + } + } + else + { + for (std::int32_t i = 0; i < width * height * stride; i += stride) + { + dest->r = source->r; + dest->g = source->g; + dest->b = source->b; + dest->a = source->a; + ++source; + ++dest; + } + } +}; + +template +void alpha_blend_pixels(S source, D dest, std::int32_t width, std::int32_t height, std::int32_t stride) +{ + auto blend_alpha = [](std::uint8_t back, std::uint8_t front, std::uint8_t alpha) -> std::uint8_t { + return ((front * alpha) + (back * (0xFF - alpha))) / 0xFF; + }; + + for (std::int32_t i = 0; i < width * height * stride; i += stride) + { + if constexpr(std::is_same::value && std::is_same::value) + { + dest->a = *reinterpret_cast(source) == 0x00 ? 0x00 : 0xFF; + if (dest->a != 0x00) + { + dest->r = source->r; + dest->g = source->g; + dest->b = source->b; + } + } + else + { + // Pre-Multiplied Alpha + dest->r = blend_alpha(dest->r, source->r, source->a); + dest->g = blend_alpha(dest->g, source->g, source->a); + dest->b = blend_alpha(dest->b, source->b, source->a); + dest->a = 0xFF; + } + + ++source; + ++dest; + } +} + +void FlipImageBytes(void* In, void* Out, std::int32_t width, std::int32_t height, std::uint32_t Bpp) noexcept +{ + std::size_t Chunk = (Bpp > 24 ? width * 4 : width * 3 + width % 4); + std::uint8_t* Destination = static_cast(Out); + std::uint8_t* Source = static_cast(In) + Chunk * (height - 1); + + while(Source != In) + { + //std::memcpy(Destination, Source, Chunk); + for (std::size_t i = 0; i < Chunk; ++i) + { + *(Destination + i) = *(Source + i); + } + + Destination += Chunk; + Source -= Chunk; + } +} + +void FlipImageVertically(std::int32_t width, std::int32_t height, std::uint8_t* data) noexcept +{ + bgra_t pixel = {0}; + for (std::int32_t y = 0; y < height / 2; ++y) + { + for (std::int32_t x = 0; x < width; ++x) + { + std::uint32_t top = (x + y * width) * sizeof(bgra_t); + std::uint32_t bottom = (x + (height - y - 1) * width) * sizeof(bgra_t); + + std::memcpy(&pixel, data + top, sizeof(bgra_t)); + std::memcpy(data + top, data + bottom, sizeof(bgra_t)); + std::memcpy(data + bottom, &pixel, sizeof(bgra_t)); + } + } +} + +void FlipImageVertically2(std::int32_t width, std::int32_t height, std::uint8_t* data) noexcept +{ + const std::size_t stride = width * 4; + std::unique_ptr row = std::make_unique(stride); + + for (std::uint8_t* it = data, *jt = &data[(height - 1) * stride]; it < jt; it += stride, jt -= stride) + { + std::memcpy(row.get(), it, stride); + std::memcpy(it, jt, stride); + std::memcpy(jt, row.get(), stride); + } +} + +void draw_circle(std::int32_t x, std::int32_t y, std::int32_t radius, void* buffer, std::int32_t width, std::int32_t height, std::int32_t stride, bool filled, std::int32_t abgr_colour) noexcept +{ + bgra_t draw_colour = {0}; + RGBA(abgr_colour, draw_colour.r, draw_colour.g, draw_colour.b, draw_colour.a); + + auto set_pixel = [&](int x, int y) { + std::uint8_t* ptr = static_cast(buffer); + bgra* pixel = reinterpret_cast(&ptr[(y * width + x) * stride]); + *pixel = draw_colour; + }; + + int radiusSq = radius * radius; + int area = (radiusSq << 2) + (radius << 2) + 1; + int rr = (radius << 1) + 1; + + for (int i = 0; i < area; ++i) + { + int xx = (i % rr) - radius; + int yy = (i / rr) - radius; + + //clamp + if (x + xx >= 0 && y + yy >= 0 && x + xx < width && y + yy < height) + { + if (filled) + { + if (xx * xx + yy * yy < radiusSq + radius) + { + set_pixel(x + xx, y + yy); + } + } + else + { + if (xx * xx + yy * yy > radiusSq - radius && xx * xx + yy * yy < radiusSq + radius) + { + set_pixel(x + xx, y + yy); + } + } + } + } +} + +void copy_image_to_bgra(void* dest_buffer, void* source_buffer, std::int32_t width, std::int32_t height, std::int32_t stride, ImageFormat source_format) noexcept +{ + switch (source_format) + { + case ImageFormat::BGR_BGRA: + convert_pixels(static_cast(source_buffer), static_cast(dest_buffer), width, height, stride); + break; + + case ImageFormat::BGRA: + convert_pixels(static_cast(source_buffer), static_cast(dest_buffer), width, height, stride); + break; + + case ImageFormat::RGBA: + convert_pixels(static_cast(source_buffer), static_cast(dest_buffer), width, height, stride); + break; + + case ImageFormat::ARGB: + convert_pixels(static_cast(source_buffer), static_cast(dest_buffer), width, height, stride); + break; + + case ImageFormat::ABGR: + convert_pixels(static_cast(source_buffer), static_cast(dest_buffer), width, height, stride); + break; + } +} + +void copy_image_from_bgra(void* dest_buffer, void* source_buffer, std::int32_t width, std::int32_t height, std::int32_t stride, ImageFormat destination_format) noexcept +{ + switch (destination_format) + { + case ImageFormat::BGR_BGRA: + convert_pixels(static_cast(source_buffer), static_cast(dest_buffer), width, height, stride); + break; + + case ImageFormat::BGRA: + convert_pixels(static_cast(source_buffer), static_cast(dest_buffer), width, height, stride); + break; + + case ImageFormat::RGBA: + convert_pixels(static_cast(source_buffer), static_cast(dest_buffer), width, height, stride); + break; + + case ImageFormat::ARGB: + convert_pixels(static_cast(source_buffer), static_cast(dest_buffer), width, height, stride); + break; + + case ImageFormat::ABGR: + convert_pixels(static_cast(source_buffer), static_cast(dest_buffer), width, height, stride); + break; + } +} + +void draw_image(void* dest_buffer, void* source_buffer, std::int32_t width, std::int32_t height, std::int32_t stride, ImageFormat format) noexcept +{ + switch (format) + { + case ImageFormat::BGR_BGRA: + alpha_blend_pixels(static_cast(source_buffer), static_cast(dest_buffer), width, height, stride); + break; + + case ImageFormat::BGRA: + alpha_blend_pixels(static_cast(source_buffer), static_cast(dest_buffer), width, height, stride); + break; + + case ImageFormat::RGBA: + alpha_blend_pixels(static_cast(source_buffer), static_cast(dest_buffer), width, height, stride); + break; + + case ImageFormat::ARGB: + alpha_blend_pixels(static_cast(source_buffer), static_cast(dest_buffer), width, height, stride); + break; + + case ImageFormat::ABGR: + alpha_blend_pixels(static_cast(source_buffer), static_cast(dest_buffer), width, height, stride); + break; + } +} + +// OpenGL 3.1+ function pointers +#if !defined(__APPLE__) + #if defined(_WIN32) || defined(_WIN64) + #define STDCALL __stdcall + #else + #define STDCALL + #endif +static void (STDCALL *glGenVertexArrays)(GLsizei n, GLuint *arrays) = nullptr; +static void (STDCALL *glBindVertexArray)(GLuint array) = nullptr; +static void (STDCALL *glDeleteVertexArrays)(GLsizei n, const GLuint *arrays) = nullptr; +static void (STDCALL *glGenBuffers)(GLsizei n, GLuint *buffers) = nullptr; +static void (STDCALL *glBindBuffer)(GLenum target, GLuint buffer) = nullptr; +static void (STDCALL *glDeleteBuffers)(GLsizei n, const GLuint *buffers) = nullptr; +static void (STDCALL *glBufferData)(GLenum target, GLsizeiptr size, const void *data, GLenum usage) = nullptr; +static GLuint (STDCALL *glCreateShader)(GLenum type) = nullptr; +static void (STDCALL *glShaderSource)(GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length) = nullptr; +static void (STDCALL *glCompileShader)(GLuint shader) = nullptr; +static void (STDCALL *glGetShaderiv)(GLuint shader, GLenum pname, GLint *params) = nullptr; +static GLuint (STDCALL *glCreateProgram)(void) = nullptr; +static void (STDCALL *glAttachShader)(GLuint program, GLuint shader) = nullptr; +static void (STDCALL *glLinkProgram)(GLuint program) = nullptr; +static void (STDCALL *glUseProgram)(GLuint program) = nullptr; +static void (STDCALL *glDeleteProgram)(GLuint program) = nullptr; +static void (STDCALL *glDeleteShader)(GLuint shader) = nullptr; +static GLint (STDCALL *glGetUniformLocation)(GLuint program, const GLchar *name) = nullptr; +static void (STDCALL *glUniform1i)(GLint location, GLint v0) = nullptr; +static void (STDCALL *glUniform1f)(GLint location, GLfloat v0) = nullptr; +static void (STDCALL *glUniform4fv)(GLint location, GLsizei count, const GLfloat *value) = nullptr; +static void (STDCALL *glUniformMatrix4fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) = nullptr; +static GLint (STDCALL *glGetAttribLocation)(GLuint program, const GLchar *name) = nullptr; +static void (STDCALL *glEnableVertexAttribArray)(GLuint index) = nullptr; +static void (STDCALL *glVertexAttribPointer)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer) = nullptr; +static void (STDCALL *glDisableVertexAttribArray)(GLuint index) = nullptr; + +bool LoadModernGLFunctions() noexcept +{ + if (glGenVertexArrays) + { + return true; + } + + #if defined(_WIN32) || defined(_WIN64) + #define xGetProcAddress wglGetProcAddress + #elif !defined(__APPLE__) + #define xGetProcAddress(procName) glXGetProcAddress(reinterpret_cast(procName)) + #endif + + glGenVertexArrays = reinterpret_cast(xGetProcAddress("glGenVertexArrays")); + glBindVertexArray = reinterpret_cast(xGetProcAddress("glBindVertexArray")); + glDeleteVertexArrays = reinterpret_cast(xGetProcAddress("glDeleteVertexArrays")); + glGenBuffers = reinterpret_cast(xGetProcAddress("glGenBuffers")); + glBindBuffer = reinterpret_cast(xGetProcAddress("glBindBuffer")); + glDeleteBuffers = reinterpret_cast(xGetProcAddress("glDeleteBuffers")); + glBufferData = reinterpret_cast(xGetProcAddress("glBufferData")); + glCreateShader = reinterpret_cast(xGetProcAddress("glCreateShader")); + glShaderSource = reinterpret_cast(xGetProcAddress("glShaderSource")); + glCompileShader = reinterpret_cast(xGetProcAddress("glCompileShader")); + glGetShaderiv = reinterpret_cast(xGetProcAddress("glGetShaderiv")); + glCreateProgram = reinterpret_cast(xGetProcAddress("glCreateProgram")); + glAttachShader = reinterpret_cast(xGetProcAddress("glAttachShader")); + glLinkProgram = reinterpret_cast(xGetProcAddress("glLinkProgram")); + glUseProgram = reinterpret_cast(xGetProcAddress("glUseProgram")); + glDeleteProgram = reinterpret_cast(xGetProcAddress("glDeleteProgram")); + glDeleteShader = reinterpret_cast(xGetProcAddress("glDeleteShader")); + glGetUniformLocation = reinterpret_cast(xGetProcAddress("glGetUniformLocation")); + glUniform1i = reinterpret_cast(xGetProcAddress("glUniform1i")); + glUniform1f = reinterpret_cast(xGetProcAddress("glUniform1f")); + glUniform4fv = reinterpret_cast(xGetProcAddress("glUniform4fv")); + glUniformMatrix4fv = reinterpret_cast(xGetProcAddress("glUniformMatrix4fv")); + glGetAttribLocation = reinterpret_cast(xGetProcAddress("glGetAttribLocation")); + glEnableVertexAttribArray = reinterpret_cast(xGetProcAddress("glEnableVertexAttribArray")); + glVertexAttribPointer = reinterpret_cast(xGetProcAddress("glVertexAttribPointer")); + glDisableVertexAttribArray = reinterpret_cast(xGetProcAddress("glDisableVertexAttribArray")); + + return glGenVertexArrays && glBindVertexArray && glGenBuffers && glBindBuffer && glBufferData && glCreateShader && glCreateProgram && glUseProgram && glUniform4fv; +} +#else +bool LoadModernGLFunctions() noexcept +{ + return true; +} +#endif + +GLuint CompileGLShader(void* ctx, const char* vertex_src, const char* fragment_src) noexcept +{ + #if defined(__APPLE__) + CGLContextObj CGL_MACRO_CONTEXT = static_cast(ctx); + #endif + + GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vertex_shader, 1, &vertex_src, nullptr); + glCompileShader(vertex_shader); + + GLint success = 0; + glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &success); + if (!success) + { + glDeleteShader(vertex_shader); + return 0; + } + + GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fragment_shader, 1, &fragment_src, nullptr); + glCompileShader(fragment_shader); + + glGetShaderiv(fragment_shader, GL_OBJECT_COMPILE_STATUS_ARB, &success); + if (!success) + { + glDeleteShader(vertex_shader); + glDeleteShader(fragment_shader); + return 0; + } + + GLuint program = glCreateProgram(); + glAttachShader(program, vertex_shader); + glAttachShader(program, fragment_shader); + glLinkProgram(program); + + glDeleteShader(vertex_shader); + glDeleteShader(fragment_shader); + + return program; +} + +void gl_draw_point_modern(void* ctx, float x, float y, float z, float radius, GLint viewport_width, GLint viewport_height) noexcept +{ + #if defined(__APPLE__) + CGLContextObj CGL_MACRO_CONTEXT = static_cast(ctx); + #endif + + static const char* vertex_shader_src = R"( + #version 150 core + in vec2 position; + uniform mat4 projection; + uniform float pointSize; + void main() { + gl_Position = projection * vec4(position, 0.0, 1.0); + gl_PointSize = pointSize; + } + )"; + + static const char* fragment_shader_src = R"( + #version 150 core + uniform vec4 color; + out vec4 fragColor; + void main() { + fragColor = color; + } + )"; + + // Create shader program once + static GLuint shader_program = 0; + static GLuint vao = 0; + static GLuint vbo = 0; + + if (shader_program == 0) + { + shader_program = CompileGLShader(ctx, vertex_shader_src, fragment_shader_src); + if (shader_program == 0) + { + return; + } + + // Create VAO and VBO + glGenVertexArrays(1, &vao); + glGenBuffers(1, &vbo); + } + + // Orthographic projection matrix + float projection[16] = { + 2.0f / viewport_width, 0.0f, 0.0f, 0.0f, + 0.0f, -2.0f / viewport_height, 0.0f, 0.0f, + 0.0f, 0.0f, -1.0f, 0.0f, + -1.0f, 1.0f, 0.0f, 1.0f + }; + + // Save current GL state + GLint last_program, last_array_buffer, last_vertex_array; + glGetIntegerv(GL_CURRENT_PROGRAM, &last_program); + glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer); + glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array); + + GLboolean last_blend = glIsEnabled(GL_BLEND); + GLboolean last_depth = glIsEnabled(GL_DEPTH_TEST); + GLboolean last_program_point_size = glIsEnabled(GL_PROGRAM_POINT_SIZE); + GLint last_blend_src, last_blend_dst; + glGetIntegerv(GL_BLEND_SRC_ALPHA, &last_blend_src); + glGetIntegerv(GL_BLEND_DST_ALPHA, &last_blend_dst); + + // Set up rendering state + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_DEPTH_TEST); + glEnable(GL_PROGRAM_POINT_SIZE); + + glUseProgram(shader_program); + + // Set uniforms + GLint proj_loc = glGetUniformLocation(shader_program, "projection"); + glUniformMatrix4fv(proj_loc, 1, GL_FALSE, projection); + + GLint size_loc = glGetUniformLocation(shader_program, "pointSize"); + glUniform1f(size_loc, radius); + + GLint color_loc = glGetUniformLocation(shader_program, "color"); + GLfloat current_color[4]; + glGetFloatv(GL_CURRENT_COLOR, current_color); + glUniform4fv(color_loc, 1, current_color); + + // Point position + float vertices[] = {x, y}; + + // Bind VAO and upload vertex data + glBindVertexArray(vao); + + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_DYNAMIC_DRAW); + + // Set vertex attributes + GLint pos_attrib = glGetAttribLocation(shader_program, "position"); + glEnableVertexAttribArray(pos_attrib); + glVertexAttribPointer(pos_attrib, 2, GL_FLOAT, GL_FALSE, 0, nullptr); + + // Draw point + glDrawArrays(GL_POINTS, 0, 1); + + // Restore GL state + glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); + glBindVertexArray(last_vertex_array); + + glUseProgram(last_program); + + if (!last_blend) + { + glDisable(GL_BLEND); + } + + if (last_depth) + { + glEnable(GL_DEPTH_TEST); + } + + if (!last_program_point_size) + { + glDisable(GL_PROGRAM_POINT_SIZE); + } + + glBlendFunc(last_blend_src, last_blend_dst); +} + +void gl_draw_point(void* ctx, float x, float y, float z, float radius) noexcept +{ + #if defined(__APPLE__) + CGLContextObj CGL_MACRO_CONTEXT = static_cast(ctx); + #endif + + // Check for Modern OpenGL 3.1+ + static bool modern_gl_attempted = false; + static bool modern_gl_available = false; + + if (!modern_gl_attempted) + { + modern_gl_available = LoadModernGLFunctions(); + modern_gl_attempted = true; + } + + if (modern_gl_available) + { + GLint viewport[4]; + glGetIntegerv(GL_VIEWPORT, viewport); + gl_draw_point_modern(ctx, x, y, z, radius, viewport[2], viewport[3]); + return; + } + + // Fall back to legacy OpenGL + GLfloat prev_point_size = 1.0f; + glGetFloatv(GL_POINT_SIZE, &prev_point_size); + + GLboolean is_blend = glIsEnabled(GL_BLEND); + GLboolean is_texture_2d = glIsEnabled(GL_TEXTURE_2D); + GLboolean is_texture_rectangle = glIsEnabled(GL_TEXTURE_RECTANGLE); + GLboolean is_point_smooth = glIsEnabled(GL_POINT_SMOOTH); + + // Set up fixed-function state + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_TEXTURE_2D); + glDisable(GL_TEXTURE_RECTANGLE); + glEnable(GL_POINT_SMOOTH); + glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); + + // Save current matrices + GLint viewport[4]; + glGetIntegerv(GL_VIEWPORT, viewport); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glOrtho(0, viewport[2], viewport[3], 0, -1, 1); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + // Draw point in pixel coordinates + glPointSize(radius); + glBegin(GL_POINTS); + glVertex3f(x, y, z); + glEnd(); + + // Restore matrices + glPopMatrix(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + + // Restore state + glPointSize(prev_point_size); + + if (!is_blend) + { + glDisable(GL_BLEND); + } + + if (is_texture_2d) + { + glEnable(GL_TEXTURE_2D); + } + + if (is_texture_rectangle) + { + glEnable(GL_TEXTURE_RECTANGLE); + } + + if (!is_point_smooth) + { + glDisable(GL_POINT_SMOOTH); + } +} + +void gl_draw_image_modern(void* ctx, void* source_buffer, float x, float y, std::int32_t width, std::int32_t height, std::int32_t stride, ImageFormat format, GLint viewport_width, GLint viewport_height) noexcept +{ + #if defined(__APPLE__) + CGLContextObj CGL_MACRO_CONTEXT = static_cast(ctx); + #endif + + static const char* vertex_shader_src = R"( + #version 150 core + in vec2 position; + in vec2 texCoord; + out vec2 fragTexCoord; + uniform mat4 projection; + void main() { + fragTexCoord = texCoord; + gl_Position = projection * vec4(position, 0.0, 1.0); + } + )"; + + static const char* fragment_shader_src = R"( + #version 150 core + in vec2 fragTexCoord; + out vec4 fragColor; + uniform sampler2D textureSampler; + void main() { + fragColor = texture(textureSampler, fragTexCoord); + } + )"; + + GLenum gl_format = [](ImageFormat format) -> GLenum { + switch(format) + { + case ImageFormat::BGR_BGRA: return GL_BGRA; + case ImageFormat::BGRA: return GL_BGRA; + case ImageFormat::RGBA: return GL_RGBA; + case ImageFormat::ARGB: return 0; + case ImageFormat::ABGR: return 0; + default: return GL_BGRA; + } + }(format); + + auto convert = [](S* source, std::int32_t width, std::int32_t height, std::int32_t stride, ImageFormat format) { + if constexpr(std::is_same::value) + { + for (std::int32_t i = 0; i < width * height * stride; i += stride) + { + source->a = *reinterpret_cast(source) == 0x00 ? 0x00 : 0xFF; + ++source; + } + } + }; + + switch (format) + { + case ImageFormat::BGR_BGRA: + convert(static_cast(source_buffer), width, height, stride, format); + break; + case ImageFormat::BGRA: + convert(static_cast(source_buffer), width, height, stride, format); + break; + case ImageFormat::RGBA: + convert(static_cast(source_buffer), width, height, stride, format); + break; + case ImageFormat::ARGB: + convert(static_cast(source_buffer), width, height, stride, format); + break; + case ImageFormat::ABGR: + convert(static_cast(source_buffer), width, height, stride, format); + break; + default: + convert(static_cast(source_buffer), width, height, stride, format); + break; + } + + // Create resources once + static GLuint shader_program = 0; + static GLuint vao = 0; + static GLuint vbo = 0; + static GLuint texture_id = 0; + static std::int32_t tex_width = 0; + static std::int32_t tex_height = 0; + + if (shader_program == 0) + { + shader_program = CompileGLShader(ctx, vertex_shader_src, fragment_shader_src); + if (shader_program == 0) + { + return; + } + + // Create VAO and VBO + glGenVertexArrays(1, &vao); + glGenBuffers(1, &vbo); + } + + if (texture_id == 0 || tex_width != width || tex_height != height) + { + if (texture_id != 0) + { + glDeleteTextures(1, &texture_id); + } + + tex_width = width; + tex_height = height; + + glGenTextures(1, &texture_id); + glBindTexture(GL_TEXTURE_2D, texture_id); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, gl_format, GL_UNSIGNED_BYTE, source_buffer); + } + else + { + glBindTexture(GL_TEXTURE_2D, texture_id); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, gl_format, GL_UNSIGNED_BYTE, source_buffer); + } + + // Rendering + float x1 = x; + float y1 = y; + float x2 = x + width; + float y2 = y + height; + + float vertices[] = { + // Position // TexCoord + x1, y1, 0.0f, 1.0f, // Bottom-left + x2, y1, 1.0f, 1.0f, // Bottom-right + x1, y2, 0.0f, 0.0f, // Top-left + + x2, y1, 1.0f, 1.0f, // Bottom-right + x2, y2, 1.0f, 0.0f, // Top-right + x1, y2, 0.0f, 0.0f // Top-left + }; + + // Orthographic projection matrix (maps screen coords to NDC) + // Bottom-left origin (OpenGL default): Y increases upward + float projection[16] = { + 2.0f / viewport_width, 0.0f, 0.0f, 0.0f, + 0.0f, 2.0f / viewport_height, 0.0f, 0.0f, + 0.0f, 0.0f, -1.0f, 0.0f, + -1.0f, -1.0f, 0.0f, 1.0f + }; + + // Save current GL state + GLint last_program, last_texture, last_array_buffer, last_vertex_array; + glGetIntegerv(GL_CURRENT_PROGRAM, &last_program); + glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); + glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer); + glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array); + + GLboolean last_blend = glIsEnabled(GL_BLEND); + GLboolean last_depth = glIsEnabled(GL_DEPTH_TEST); + GLint last_blend_src, last_blend_dst; + glGetIntegerv(GL_BLEND_SRC_ALPHA, &last_blend_src); + glGetIntegerv(GL_BLEND_DST_ALPHA, &last_blend_dst); + + // Set up rendering state + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_DEPTH_TEST); + + glUseProgram(shader_program); + + // Set Shaders + GLint proj_loc = glGetUniformLocation(shader_program, "projection"); + glUniformMatrix4fv(proj_loc, 1, GL_FALSE, projection); + + GLint tex_loc = glGetUniformLocation(shader_program, "textureSampler"); + glUniform1i(tex_loc, 0); + + // Bind VAO and upload vertex data + glBindVertexArray(vao); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_DYNAMIC_DRAW); + + // Set vertex attributes + GLint pos_attrib = glGetAttribLocation(shader_program, "position"); + GLint tex_attrib = glGetAttribLocation(shader_program, "texCoord"); + + glEnableVertexAttribArray(pos_attrib); + glVertexAttribPointer(pos_attrib, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0); + + glEnableVertexAttribArray(tex_attrib); + glVertexAttribPointer(tex_attrib, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float))); + + // Draw + glDrawArrays(0x0004, 0, 6); // glTRIANGLES + + // Restore GL state + glBindTexture(GL_TEXTURE_2D, last_texture); + glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); + glBindVertexArray(last_vertex_array); + + glUseProgram(last_program); + + if (!last_blend) + { + glDisable(GL_BLEND); + } + + if (last_depth) + { + glEnable(GL_DEPTH_TEST); + } + + glBlendFunc(last_blend_src, last_blend_dst); +} + +void gl_draw_image(void* ctx, void* source_buffer, float x, float y, std::int32_t width, std::int32_t height, std::int32_t stride, ImageFormat format) noexcept +{ + #if defined(__APPLE__) + CGLContextObj CGL_MACRO_CONTEXT = static_cast(ctx); + #endif + + // Check for Modern OpenGL 3.1+ + static bool modern_gl_attempted = false; + static bool modern_gl_available = false; + + if (!modern_gl_attempted) + { + modern_gl_available = LoadModernGLFunctions(); + modern_gl_attempted = true; + } + + if (modern_gl_available) + { + GLint viewport[4]; + glGetIntegerv(GL_VIEWPORT, viewport); + gl_draw_image_modern(ctx, source_buffer, x, y, width, height, stride, format, viewport[2], viewport[3]); + return; + } + + // Fall back to legacy OpenGL + GLenum gl_format = [](ImageFormat format) -> GLenum { + switch(format) + { + case ImageFormat::BGR_BGRA: return GL_BGRA; + case ImageFormat::BGRA: return GL_BGRA; + case ImageFormat::RGBA: return GL_RGBA; + case ImageFormat::ARGB: return 0; // Not Supported + case ImageFormat::ABGR: return 0; // Not Supported + default: return GL_BGRA; + } + }(format); + + auto convert = [](S* source, std::int32_t width, std::int32_t height, std::int32_t stride, ImageFormat format) { + if constexpr(std::is_same::value) + { + for (std::int32_t i = 0; i < width * height * stride; i += stride) + { + source->a = *reinterpret_cast(source) == 0x00 ? 0x00 : 0xFF; + ++source; + } + } + }; + + //Backup + bool GLBlend = glIsEnabled(GL_BLEND); + bool GLTexture2D = glIsEnabled(GL_TEXTURE_2D); + bool GLRectangle = glIsEnabled(GL_TEXTURE_RECTANGLE); + bool PointSmooth = glIsEnabled(GL_POINT_SMOOTH); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glDisable(GL_TEXTURE_2D); + glEnable(GL_POINT_SMOOTH); + glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); + + glPushMatrix(); + glLoadIdentity(); + + //Load Texture + switch (format) + { + case ImageFormat::BGR_BGRA: + convert(static_cast(source_buffer), width, height, stride, format); + break; + + case ImageFormat::BGRA: + convert(static_cast(source_buffer), width, height, stride, format); + break; + + case ImageFormat::RGBA: + convert(static_cast(source_buffer), width, height, stride, format); + break; + + case ImageFormat::ARGB: + convert(static_cast(source_buffer), width, height, stride, format); + break; + + case ImageFormat::ABGR: + convert(static_cast(source_buffer), width, height, stride, format); + break; + + default: + convert(static_cast(source_buffer), width, height, stride, format); + break; + } + + static GLuint ID = 0; + static std::int32_t w = 0; + static std::int32_t h = 0; + + GLenum target = GL_TEXTURE_RECTANGLE; + + if (ID == 0 || w != width || h != height) + { + if (ID != 0) + { + glDeleteTextures(1, &ID); + ID = 0; + } + + w = width; + h = height; + + glGenTextures(1, &ID); + glBindTexture(target, ID); + glPixelStorei(GL_UNPACK_ROW_LENGTH, width); + glTexImage2D(target, 0, GL_RGBA, width, height, 0, gl_format, GL_UNSIGNED_BYTE, source_buffer); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + glTexParameteri(target, GL_TEXTURE_WRAP_S, target == GL_TEXTURE_2D ? GL_REPEAT : GL_CLAMP_TO_EDGE); + glTexParameteri(target, GL_TEXTURE_WRAP_T, target == GL_TEXTURE_2D ? GL_REPEAT : GL_CLAMP_TO_EDGE); + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } + else + { + glBindTexture(target, ID); + glPixelStorei(GL_UNPACK_ROW_LENGTH, width); + glTexSubImage2D(target, 0, 0, 0, width, height, gl_format, GL_UNSIGNED_BYTE, source_buffer); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + glBindTexture(target, 0); + } + + + float x1 = x; + float y1 = y; + float x2 = width; + float y2 = height; + width = target == GL_TEXTURE_2D ? 1 : width; + height = target == GL_TEXTURE_2D ? 1 : height; + + //Render Texture + glEnable(target); + glBindTexture(target, ID); + glColor4ub(0xFF, 0xFF, 0xFF, 0xFF); + glBegin(GL_QUADS); + glTexCoord2f(0, height); + glVertex2f(x1, y1); + glTexCoord2f(0, 0); + glVertex2f(x1, y2); + glTexCoord2f(width, 0); + glVertex2f(x2, y2); + glTexCoord2f(width, height); + glVertex2f(x2, y1); + glEnd(); + glBindTexture(target, 0); + glDisable(target); + + + //Restore + glPopMatrix(); + + if (!GLBlend) + { + glDisable(GL_BLEND); + } + + if (GLTexture2D) + { + glEnable(GL_TEXTURE_2D); + } + + if (GLRectangle) + { + glEnable(GL_TEXTURE_RECTANGLE); + } + + if (!PointSmooth) + { + glDisable(GL_POINT_SMOOTH); + } +} + +#if defined(_WIN32) || defined(_WIN64) +#define VERTEX_FVF_TEX D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1 + +struct D3DVertex +{ + float X, Y, Z, RHW; + unsigned int Colour; + float U, V; +}; + +HMODULE dx_get_d3dx9_module() noexcept +{ + // [24, 43] d3dx9 versions + char d3dx9_version[32] = {0}; + for (int i = 43; i > 23; --i) + { + sprintf(d3dx9_version, "d3dx9_%d.dll", i); + + HMODULE module = GetModuleHandle(d3dx9_version); + if (!module) + { + module = LoadLibrary(d3dx9_version); + } + + if (module) + { + return module; + } + } + + return nullptr; +} + +IDirect3DPixelShader9* dx_texture_render_shader(IDirect3DDevice9* device, ID3DXConstantTable* &table) +{ + static const char* shader_code = R"( + sampler2D tex; + int format_id; + + struct PS_INPUT { + float2 texCoord : TEXCOORD0; + }; + + float4 PS_Main(PS_INPUT input) : COLOR { + float4 color = tex2D(tex, input.texCoord); + float4 output = color; + + if (format_id == 0) { + if (color.r == 0 && color.g == 0 && color.b == 0) { + output = float4(color.r, color.g, color.b, 0.0); + } + else { + output = float4(color.r, color.g, color.b, 1.0); + } + } + else if (format_id == 1) { + output = color; + } + else if (format_id == 2) { + output = float4(color.b, color.g, color.r, color.a); + } + else if (format_id == 3) { + output = float4(color.g, color.r, color.a, color.b); + } + else if (format_id == 4) { + output = float4(color.a, color.r, color.g, color.b); + } + + return output; + } + )"; + + if (!table) + { + return dx_compile_shader(device, table, shader_code); + } + return nullptr; +} + +IDirect3DPixelShader9* dx_read_backbuffer_shader(IDirect3DDevice9* device, ID3DXConstantTable** table) +{ + static const char* shader_code = R"( + sampler2D screen_texture; + int format_id; + + struct PS_INPUT { + float2 texCoord : TEXCOORD0; + }; + + float4 PS_Main(PS_INPUT input) : COLOR { + float4 color = tex2D(screen_texture, input.texCoord); + float4 output = color; + + if (format_id == 0) { + if (color.r == 0 && color.g == 0 && color.b == 0) { + output = float4(color.r, color.g, color.b, 0.0); + } + else { + output = float4(color.r, color.g, color.b, 1.0); + } + } + else if (format_id == 1) { + output = color; + } + else if (format_id == 2) { + output = float4(color.r, color.g, color.b, color.a); + } + else if (format_id == 3) { + output = float4(color.a, color.r, color.g, color.b); + } + else if (format_id == 4) { + output = float4(color.a, color.b, color.g, color.r); + } + + return output; + } + )"; + + static ID3DXConstantTable* constant_table = nullptr; + static IDirect3DPixelShader9* shader = dx_compile_shader(device, constant_table, shader_code); + + if (table) + { + *table = constant_table; + } + + return shader; +} + +IDirect3DPixelShader9* dx_compile_shader(IDirect3DDevice9* device, ID3DXConstantTable* &constant_table, const std::string &shader_string) noexcept +{ + HMODULE module = dx_get_d3dx9_module(); + if (!module) + { + return nullptr; + } + + auto* pD3DXCompileShader = reinterpret_cast(GetProcAddress(module, "D3DXCompileShader")); + if (!pD3DXCompileShader) + { + return nullptr; + } + + ID3DXBuffer* shader_buffer = nullptr; + ID3DXBuffer* error_buffer = nullptr; + + HRESULT hr = pD3DXCompileShader( + shader_string.c_str(), + shader_string.length(), + nullptr, + nullptr, + "PS_Main", + "ps_2_0", + #if DEBUG + D3DXSHADER_DEBUG, + #else + D3DXSHADER_OPTIMIZATION_LEVEL3, + #endif + &shader_buffer, + &error_buffer, + &constant_table + ); + + if (FAILED(hr)) + { + if (error_buffer) + { + error_buffer->Release(); + } + + return nullptr; + } + + if (shader_buffer) + { + IDirect3DPixelShader9* pixel_shader = nullptr; + hr = device->CreatePixelShader(reinterpret_cast(shader_buffer->GetBufferPointer()), &pixel_shader); + shader_buffer->Release(); + + if (FAILED(hr)) + { + if (error_buffer) + { + error_buffer->Release(); + } + return nullptr; + } + + return pixel_shader; + } + + return nullptr; +} + +void dx_apply_shader(IDirect3DDevice9* device, IDirect3DPixelShader9* shader, ID3DXConstantTable* constant_table, ImageFormat format) noexcept +{ + std::int32_t format_id = [](ImageFormat format) -> std::int32_t { + switch (format) + { + case ImageFormat::BGR_BGRA: return 0; + case ImageFormat::BGRA: return 1; + case ImageFormat::RGBA: return 2; + case ImageFormat::ARGB: return 3; + case ImageFormat::ABGR: return 4; + } + return 0; // Default to BGR_BGRA + }(format); + + if (constant_table) + { + D3DXHANDLE format_handle = constant_table->GetConstantByName(nullptr, "format_id"); + if (!format_handle) + { + return; + } + + constant_table->SetInt(device, format_handle, format_id); + } + + device->SetPixelShader(shader); +} + +void dx_load_texture(IDirect3DDevice9* device, IDirect3DTexture9* &texture, IDirect3DPixelShader9* &shader, ID3DXConstantTable* &constants_table, ImageFormat image_format, std::uint8_t* buffer, std::int32_t width, std::int32_t height) noexcept +{ + static std::int32_t Width = 0; + static std::int32_t Height = 0; + + if (Width != width || Height != height) + { + if (texture) + { + texture->Release(); + texture = nullptr; + } + + Width = width; + Height = height; + } + + if (!texture) + { + HRESULT hr = device->CreateTexture(width, height, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &texture, nullptr); + if (FAILED(hr)) + { + if (texture) + { + texture->Release(); + texture = nullptr; + } + return; + } + + Width = width; + Height = height; + } + + D3DLOCKED_RECT rect; + if (FAILED(texture->LockRect(0, &rect, nullptr, D3DLOCK_DISCARD))) + { + if (texture) + { + texture->Release(); + texture = nullptr; + } + return; + } + + if (!shader && !constants_table) + { + shader = dx_texture_render_shader(device, constants_table); + } + + std::uint8_t* dest = static_cast(rect.pBits); + int pitch = rect.Pitch; + + if (shader) + { + for (int i = 0; i < height; ++i) + { + std::memcpy(dest + i * pitch, buffer + i * width * 4, width * 4); + } + } + else + { + for (int i = 0; i < height; ++i) + { + copy_image_to_bgra(dest + i * pitch, buffer + i * width * 4, width, 1, 4, image_format); + } + } + + texture->UnlockRect(0); +} + +void dx_draw_texture(IDirect3DDevice9* device, IDirect3DTexture9* texture, ID3DXConstantTable* &constant_table, IDirect3DPixelShader9* &shader, IDirect3DVertexBuffer9* &vertex_buffer, ImageFormat image_format, float X1, float Y1, float X2, float Y2) noexcept +{ + static std::int32_t Width = static_cast(X2 - X1); + static std::int32_t Height = static_cast(Y2 - Y1); + + // Compile the shader just once + if (!constant_table && !shader) + { + constant_table = nullptr; + shader = dx_texture_render_shader(device, constant_table); + } + + float UOffset = 0.5f / (X2 - X1); + float VOffset = 0.5f / (Y2 - Y1); + + D3DVertex vertices[] = { + {X1, Y1, 1.0f, 1.0f, D3DCOLOR_RGBA(255, 255, 255, 255), 0.0f + UOffset, 0.0f + VOffset}, + {X2, Y1, 1.0f, 1.0f, D3DCOLOR_RGBA(255, 255, 255, 255), 1.0f + UOffset, 0.0f + VOffset}, + {X1, Y2, 1.0f, 1.0f, D3DCOLOR_RGBA(255, 255, 255, 255), 0.0f + UOffset, 1.0f + VOffset}, + {X2, Y2, 1.0f, 1.0f, D3DCOLOR_RGBA(255, 255, 255, 255), 1.0f + UOffset, 1.0f + VOffset} + }; + + if (!vertex_buffer) + { + HRESULT res = device->CreateVertexBuffer(4 * sizeof(D3DVertex), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1, D3DPOOL_DEFAULT, &vertex_buffer, nullptr); + if (FAILED(res)) + { + if (vertex_buffer) + { + vertex_buffer->Release(); + vertex_buffer = nullptr; + } + } + else + { + void *pVertices; + if (SUCCEEDED(vertex_buffer->Lock(0, sizeof(vertices), (void **) &pVertices, D3DLOCK_DISCARD))) + { + std::memcpy(pVertices, vertices, sizeof(vertices)); + vertex_buffer->Unlock(); + } + } + } + else + { + if (Width != static_cast(X2 - X1) || Height != static_cast(Y2 - Y1)) + { + Width = static_cast(X2 - X1); + Height = static_cast(Y2 - Y1); + + void *pVertices; + if (SUCCEEDED(vertex_buffer->Lock(0, sizeof(vertices), (void **) &pVertices, D3DLOCK_DISCARD))) + { + std::memcpy(pVertices, vertices, sizeof(vertices)); + vertex_buffer->Unlock(); + } + } + } + + if (shader) + { + dx_apply_shader(device, shader, constant_table, image_format); + } + + device->SetFVF(VERTEX_FVF_TEX); + device->SetTexture(0, texture); + + if (vertex_buffer) + { + device->SetStreamSource(0, vertex_buffer, 0, sizeof(D3DVertex)); + device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); + device->SetStreamSource(0, nullptr, 0, 0); + } + else + { + device->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, vertices, sizeof(D3DVertex)); + } + + device->SetTexture(0, nullptr); + + if (shader) + { + device->SetPixelShader(nullptr); + } +} + +#if defined(DX_CLAMP_IMAGE_SUPPORT) +void dx_read_pixels(IDirect3DDevice9* device, void* buffer, std::int32_t x, std::int32_t y, std::int32_t w, std::int32_t h, std::int32_t width, std::int32_t height, bool& minimized, ImageFormat image_format) noexcept +{ + // Compile the shader just once + static ID3DXConstantTable* constant_table = nullptr; + static IDirect3DPixelShader9* shader = nullptr; //dx_read_backbuffer_shader(device, &constant_table); + + IDirect3DSurface9* render_target = nullptr; + IDirect3DSurface9* dest_target = nullptr; + IDirect3DSurface9* sub_region_surface = nullptr; + + #define SAFE_RELEASE(ptr) if (ptr) ptr->Release() + + HRESULT result = device->GetRenderTarget(0, &render_target); + if (FAILED(result)) + { + SAFE_RELEASE(render_target); + return; + } + + D3DSURFACE_DESC descriptor = {}; + render_target->GetDesc(&descriptor); + width = static_cast(descriptor.Width); + height = static_cast(descriptor.Height); + + D3DFORMAT format = descriptor.Format; + + HDC DC = nullptr; + render_target->GetDC(&DC); + minimized = IsIconic(WindowFromDC(DC)); + render_target->ReleaseDC(DC); + + #ifdef CLAMP_SUB_REGION + // Clamp sub-region within the back-buffer + x = std::max(0, std::min(x, width)); + y = std::max(0, std::min(y, height)); + w = std::min(w, width - x); + h = std::min(h, height - y); + if (w <= 0 || h <= 0) + { + SAFE_RELEASE(render_target); + return; + } + + // Sub-Region bounds + RECT src_rect = {x, y, x + w, y + h}; + #else + RECT src_rect = {x, y, w, h}; + #endif + + result = device->CreateOffscreenPlainSurface(w, h, format, D3DPOOL_SYSTEMMEM, &dest_target, nullptr); + if (FAILED(result)) + { + SAFE_RELEASE(dest_target); + SAFE_RELEASE(render_target); + return; + } + + if (shader) + { + // Create a sub-region rendering target + result = device->CreateRenderTarget(w, h, format, D3DMULTISAMPLE_NONE, 0, FALSE, &sub_region_surface, nullptr); + if (FAILED(result)) + { + SAFE_RELEASE(sub_region_surface); + SAFE_RELEASE(dest_target); + SAFE_RELEASE(render_target); + return; + } + + // Copy the pixels into the sub-region rendering target + result = device->StretchRect(render_target, &src_rect, sub_region_surface, nullptr, D3DTEXF_NONE); + if (FAILED(result)) + { + SAFE_RELEASE(sub_region_surface); + SAFE_RELEASE(dest_target); + SAFE_RELEASE(render_target); + return; + } + + // Create intermediate input texture for shader processing + IDirect3DTexture9* input_texture = nullptr; + result = device->CreateTexture(w, h, 1, D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &input_texture, nullptr); + if (FAILED(result)) + { + SAFE_RELEASE(sub_region_surface); + SAFE_RELEASE(dest_target); + SAFE_RELEASE(render_target); + return; + } + + // Render the Sub-Region's Surface to the Texture + IDirect3DSurface9* input_surface = nullptr; + input_texture->GetSurfaceLevel(0, &input_surface); + result = device->StretchRect(sub_region_surface, nullptr, input_surface, nullptr, D3DTEXF_NONE); + if (FAILED(result)) + { + SAFE_RELEASE(input_surface); + SAFE_RELEASE(input_texture); + SAFE_RELEASE(sub_region_surface); + SAFE_RELEASE(dest_target); + SAFE_RELEASE(render_target); + return; + } + + // Create intermediate output texture for shader processing + IDirect3DTexture9* output_texture = nullptr; + result = device->CreateTexture(w, h, 1, D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &output_texture, nullptr); + if (FAILED(result)) + { + SAFE_RELEASE(input_surface); + SAFE_RELEASE(input_texture); + SAFE_RELEASE(sub_region_surface); + SAFE_RELEASE(dest_target); + SAFE_RELEASE(render_target); + return; + } + + IDirect3DSurface9* output_surface = nullptr; + output_texture->GetSurfaceLevel(0, &output_surface); + + // Set the render target to the output texture + device->SetRenderTarget(0, output_surface); + + // Apply the pixel format shader + dx_apply_shader(device, shader, constant_table, image_format); + + // Draw the input texture to the output texture with the shader applied + float UOffset = 0.5f / static_cast(w); + float VOffset = 0.5f / static_cast(h); + + D3DVertex vertices[] = { + {0.0f, 0.0f, 1.0f, 1.0f, D3DCOLOR_RGBA(255, 255, 255, 255), 0.0f + UOffset, 0.0f + VOffset}, + {static_cast(w), 0.0f, 1.0f, 1.0f, D3DCOLOR_RGBA(255, 255, 255, 255), 1.0f + UOffset, 0.0f + VOffset}, + {0.0f, static_cast(h), 1.0f, 1.0f, D3DCOLOR_RGBA(255, 255, 255, 255), 0.0f + UOffset, 1.0f + VOffset}, + {static_cast(w), static_cast(h), 1.0f, 1.0f, D3DCOLOR_RGBA(255, 255, 255, 255), 1.0f + UOffset, 1.0f + VOffset} + }; + + device->SetFVF(VERTEX_FVF_TEX); + device->SetTexture(0, input_texture); + + // Render the input texture to the output texture + device->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, vertices, sizeof(D3DVertex)); // NO IDEA WHY. THIS LINE STOPS RENDERING ENTIRELY + + // Reset render target and shader + device->SetRenderTarget(0, render_target); + device->SetPixelShader(nullptr); + + // Copy the final result to destination + result = device->GetRenderTargetData(output_surface, dest_target); + if (SUCCEEDED(result)) + { + D3DLOCKED_RECT rect; + if (SUCCEEDED(dest_target->LockRect(&rect, nullptr, D3DLOCK_READONLY))) + { + std::memcpy(buffer, rect.pBits, w * h * 4); + dest_target->UnlockRect(); + } + } + + SAFE_RELEASE(output_surface); + SAFE_RELEASE(output_texture); + SAFE_RELEASE(input_surface); + SAFE_RELEASE(input_texture); + SAFE_RELEASE(sub_region_surface); + } + else + { + // Create a sub-region rendering target + result = device->CreateRenderTarget(w, h, format, D3DMULTISAMPLE_NONE, 0, FALSE, &sub_region_surface, nullptr); + if (FAILED(result)) + { + SAFE_RELEASE(dest_target); + SAFE_RELEASE(render_target); + return; + } + + // Copy the pixels into the sub-region rendering target + result = device->StretchRect(render_target, &src_rect, sub_region_surface, nullptr, D3DTEXF_NONE); + if (FAILED(result)) + { + SAFE_RELEASE(sub_region_surface); + SAFE_RELEASE(dest_target); + SAFE_RELEASE(render_target); + return; + } + + // Copy the final result to destination + result = device->GetRenderTargetData(sub_region_surface, dest_target); + if (SUCCEEDED(result)) + { + D3DLOCKED_RECT rect; + if (SUCCEEDED(dest_target->LockRect(&rect, nullptr, D3DLOCK_READONLY))) + { + //std::memcpy(buffer, rect.pBits, width * height * 4); + copy_image_from_bgra(buffer, rect.pBits, w, h, 4, image_format); + dest_target->UnlockRect(); + } + } + + SAFE_RELEASE(sub_region_surface); + } + + SAFE_RELEASE(dest_target); + SAFE_RELEASE(render_target); +} +#else +void dx_read_pixels(IDirect3DDevice9* device, void* buffer, std::int32_t width, std::int32_t height, bool& minimized, ImageFormat image_format) noexcept +{ + // Compile the shader just once + static ID3DXConstantTable* constant_table = nullptr; + static IDirect3DPixelShader9* shader = nullptr; //dx_read_backbuffer_shader(device, &constant_table); + + IDirect3DSurface9* render_target = nullptr; + IDirect3DSurface9* dest_target = nullptr; + + #define SAFE_RELEASE(ptr) if (ptr) ptr->Release() + + HRESULT result = device->GetRenderTarget(0, &render_target); + if (FAILED(result)) + { + SAFE_RELEASE(render_target); + return; + } + + D3DSURFACE_DESC descriptor = {}; + render_target->GetDesc(&descriptor); + width = static_cast(descriptor.Width); + height = static_cast(descriptor.Height); + + D3DFORMAT format = descriptor.Format; + + HDC DC = nullptr; + render_target->GetDC(&DC); + minimized = IsIconic(WindowFromDC(DC)); + render_target->ReleaseDC(DC); + + result = device->CreateOffscreenPlainSurface(width, height, format, D3DPOOL_SYSTEMMEM, &dest_target, nullptr); + if (FAILED(result)) + { + SAFE_RELEASE(dest_target); + SAFE_RELEASE(render_target); + return; + } + + if (shader) + { + // Texture to store converted pixels + IDirect3DTexture9* backbuffer_texture = nullptr; + result = device->CreateTexture(width, height, 1, D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &backbuffer_texture, nullptr); + if (FAILED(result)) + { + SAFE_RELEASE(backbuffer_texture); + SAFE_RELEASE(dest_target); + SAFE_RELEASE(render_target); + return; + } + + // Render the Screen to the Texture + IDirect3DSurface9* backbuffer_texture_surface = nullptr; + backbuffer_texture->GetSurfaceLevel(0, &backbuffer_texture_surface); + + result = device->StretchRect(render_target, nullptr, backbuffer_texture_surface, nullptr, D3DTEXF_NONE); + if (FAILED(result)) + { + SAFE_RELEASE(backbuffer_texture_surface); + SAFE_RELEASE(backbuffer_texture); + SAFE_RELEASE(dest_target); + SAFE_RELEASE(render_target); + return; + } + + // Create a texture to render the back-buffer with the shader applied + IDirect3DTexture9* final_texture = nullptr; + result = device->CreateTexture(width, height, 1, D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &final_texture, nullptr); + if (FAILED(result)) + { + SAFE_RELEASE(final_texture); + SAFE_RELEASE(backbuffer_texture_surface); + SAFE_RELEASE(backbuffer_texture); + SAFE_RELEASE(dest_target); + SAFE_RELEASE(render_target); + return; + } + + IDirect3DSurface9* final_texture_surface = nullptr; + final_texture->GetSurfaceLevel(0, &final_texture_surface); + + // Set the render target to the final texture + device->SetRenderTarget(0, final_texture_surface); + + // Apply the pixel format shader + dx_apply_shader(device, shader, constant_table, image_format); + + // Draw the backbuffer_texture to the final_texture with the shader applied + float UOffset = 0.5f / static_cast(width); + float VOffset = 0.5f / static_cast(height); + + D3DVertex vertices[] = { + {0.0f, 0.0f, 1.0f, 1.0f, D3DCOLOR_RGBA(255, 255, 255, 255), 0.0f + UOffset, 0.0f + VOffset}, + {static_cast(width), 0.0f, 1.0f, 1.0f, D3DCOLOR_RGBA(255, 255, 255, 255), 1.0f + UOffset, 0.0f + VOffset}, + {0.0f, static_cast(height), 1.0f, 1.0f, D3DCOLOR_RGBA(255, 255, 255, 255), 0.0f + UOffset, 1.0f + VOffset}, + {static_cast(width), static_cast(height), 1.0f, 1.0f, D3DCOLOR_RGBA(255, 255, 255, 255), 1.0f + UOffset, 1.0f + VOffset} + }; + + device->SetFVF(VERTEX_FVF_TEX); + device->SetTexture(0, backbuffer_texture); + + // Render the backbuffer_texture to the final_texture + device->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, vertices, sizeof(D3DVertex)); // NO IDEA WHY. THIS LINE STOPS RENDERING ENTIRELY + + // Reset render target and shader + device->SetRenderTarget(0, render_target); + device->SetPixelShader(nullptr); + + // Copy the final texture to destination + result = device->GetRenderTargetData(final_texture_surface, dest_target); + if (FAILED(result)) + { + SAFE_RELEASE(final_texture_surface); + SAFE_RELEASE(final_texture); + SAFE_RELEASE(backbuffer_texture_surface); + SAFE_RELEASE(backbuffer_texture); + SAFE_RELEASE(dest_target); + SAFE_RELEASE(render_target); + return; + } + + D3DLOCKED_RECT rect; + if (SUCCEEDED(dest_target->LockRect(&rect, nullptr, D3DLOCK_READONLY))) + { + std::memcpy(buffer, rect.pBits, width * height * 4); + dest_target->UnlockRect(); + } + + SAFE_RELEASE(final_texture_surface); + SAFE_RELEASE(final_texture); + SAFE_RELEASE(backbuffer_texture_surface); + SAFE_RELEASE(backbuffer_texture); + } + else + { + // Copy render target to destination + result = device->GetRenderTargetData(render_target, dest_target); + + if (FAILED(result)) + { + SAFE_RELEASE(dest_target); + SAFE_RELEASE(render_target); + return; + } + + D3DLOCKED_RECT rect; + if (SUCCEEDED(dest_target->LockRect(&rect, nullptr, D3DLOCK_READONLY))) + { + //std::memcpy(buffer, rect.pBits, width * height * 4); + copy_image_from_bgra(buffer, rect.pBits, width, height, 4, image_format); + dest_target->UnlockRect(); + } + } + + SAFE_RELEASE(dest_target); + SAFE_RELEASE(render_target); +} +#endif + +void dx_draw_point(IDirect3DDevice9* device, float cx, float cy, float radius, D3DCOLOR colour) +{ + static const int resolution = 10; + D3DVertex Vertices[resolution]; + + for (int i = 0; i < resolution; ++i) + { + Vertices[i].X = cx + radius * std::cos(3.141592654f * (static_cast(i) / (resolution / 2.0f))); + Vertices[i].Y = cy + radius * std::sin(3.141592654f * (static_cast(i) / (resolution / 2.0f))); + Vertices[i].Z = 0.0f; + Vertices[i].RHW = 1.0f; + Vertices[i].Colour = colour; + Vertices[i].U = 0.0f; + Vertices[i].V = 0.0f; + } + + device->SetFVF(VERTEX_FVF_TEX); + device->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, resolution - 2, Vertices, sizeof(D3DVertex)); +} +#endif \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Plugin/Graphics.hxx b/third-party/RemoteInput/RemoteInput/Plugin/Graphics.hxx new file mode 100644 index 0000000..c8364a0 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Plugin/Graphics.hxx @@ -0,0 +1,97 @@ +#ifndef Graphics_HXX_INCLUDED +#define Graphics_HXX_INCLUDED + +#include "EIOSTypes.hxx" +#include +#include +#include +#include + +#if defined(_WIN32) || defined(_WIN64) +#include +#include +#include +#endif + +//[[gnu::optimize(0)]] +//[[clang::optimize(0)]] //optnone + +#if defined(_MSC_VER) +#pragma optimize("s", on) +#else +#pragma GCC push_options +#pragma GCC optimize ("-O3") +#endif + +typedef struct bgr_bgra_t +{ + std::uint8_t b; + std::uint8_t g; + std::uint8_t r; + std::uint8_t a; +} bgr_bgra; + +typedef struct abgr_t +{ + std::uint8_t a; + std::uint8_t b; + std::uint8_t g; + std::uint8_t r; +} abgr; + +typedef struct argb_t +{ + std::uint8_t a; + std::uint8_t r; + std::uint8_t g; + std::uint8_t b; +} argb; + +typedef struct rgba_t +{ + std::uint8_t r; + std::uint8_t g; + std::uint8_t b; + std::uint8_t a; +} rgba; + +typedef struct bgra_t +{ + std::uint8_t b; + std::uint8_t g; + std::uint8_t r; + std::uint8_t a; +} bgra; + +void FlipImageBytes(void* In, void* Out, std::int32_t width, std::int32_t height, std::uint32_t Bpp) noexcept; +void FlipImageVertically(std::int32_t width, std::int32_t height, std::uint8_t* data) noexcept; +void FlipImageVertically2(std::int32_t width, std::int32_t height, std::uint8_t* data) noexcept; + +//Native +void draw_circle(std::int32_t x, std::int32_t y, std::int32_t radius, void* buffer, std::int32_t width, std::int32_t height, std::int32_t stride, bool filled, std::int32_t abgr_colour) noexcept; +void copy_image_to_bgra(void* dest_buffer, void* source_buffer, std::int32_t width, std::int32_t height, std::int32_t stride, ImageFormat source_format) noexcept; +void copy_image_from_bgra(void* dest_buffer, void* source_buffer, std::int32_t width, std::int32_t height, std::int32_t stride, ImageFormat destination_format) noexcept; +void draw_image(void* dest_buffer, void* source_buffer, std::int32_t width, std::int32_t height, std::int32_t stride, ImageFormat format) noexcept; + +//OpenGL +void gl_draw_point(void* ctx, float x, float y, float z, float radius) noexcept; +void gl_draw_image(void* ctx, void* source_buffer, float x, float y, std::int32_t width, std::int32_t height, std::int32_t stride, ImageFormat format) noexcept; + +//DirectX + +#if defined(_WIN32) || defined(_WIN64) +IDirect3DPixelShader9* dx_compile_shader(IDirect3DDevice9* device, ID3DXConstantTable* &constant_table, const std::string &shader_string) noexcept; +void dx_apply_shader(IDirect3DDevice9* device, IDirect3DPixelShader9* shader, ID3DXConstantTable* constant_table, ImageFormat format) noexcept; +void dx_load_texture(IDirect3DDevice9* device, IDirect3DTexture9* &texture, IDirect3DPixelShader9* &shader, ID3DXConstantTable* &constants_table, ImageFormat image_format, std::uint8_t* buffer, std::int32_t width, std::int32_t height) noexcept; +void dx_draw_texture(IDirect3DDevice9* device, IDirect3DTexture9* texture, ID3DXConstantTable* &constant_table, IDirect3DPixelShader9* &shader, IDirect3DVertexBuffer9* &vertex_buffer, ImageFormat image_format, float X1, float Y1, float X2, float Y2) noexcept; +void dx_read_pixels(IDirect3DDevice9* device, void* buffer, std::int32_t width, std::int32_t height, bool& minimized, ImageFormat image_format) noexcept; +void dx_draw_point(IDirect3DDevice9* device, float cx, float cy, float radius, D3DCOLOR colour); +#endif + +#if defined(_MSC_VER) +#pragma optimize("", on) +#else +#pragma GCC pop_options +#endif + +#endif /* Graphics_HXX_INCLUDED */ diff --git a/third-party/RemoteInput/RemoteInput/Plugin/ImageData.cxx b/third-party/RemoteInput/RemoteInput/Plugin/ImageData.cxx new file mode 100644 index 0000000..1096668 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Plugin/ImageData.cxx @@ -0,0 +1,160 @@ +// +// Created by Brandon on 2023-03-22. +// + +#include "ImageData.hxx" +#include + +ImageData::ImageData(void* buffer, std::size_t size) : stream(), image_data(static_cast(buffer), [](EIOSData* data) { }) //data(std::construct_at(static_cast(buffer)), [](EIOSData* data) { std::destroy_at(data); }) +{ + std::size_t offset = offsetof(EIOSData, data); + stream = std::make_unique(image_data->data, size - offset); + stream->seekg(0, std::ios::beg); + stream->seekp(0, std::ios::beg); +} + +ImageData::~ImageData() +{ + // std::launder(reinterpret_cast(buffer))->~ImageData(); +} + +void ImageData::prepare_for_read() const +{ + stream->seekg(0, std::ios::beg); +} + +void ImageData::prepare_for_write() const +{ + stream->seekp(0, std::ios::beg); +} + +std::int32_t ImageData::parent_process_id() const +{ + return image_data->parent_process_id; +} + +std::int32_t ImageData::parent_thread_id() const +{ + return image_data->parent_thread_id; +} + +std::int32_t ImageData::image_width() const +{ + return image_data->image_width; +} + +std::int32_t ImageData::image_height() const +{ + return image_data->image_height; +} + +std::int32_t ImageData::target_width() const +{ + return image_data->target_width; +} + +std::int32_t ImageData::target_height() const +{ + return image_data->target_height; +} + +bool ImageData::debug_graphics() const +{ + return image_data->debug_graphics; +} + +bool ImageData::ui_scaling() const +{ + return image_data->ui_scaling; +} + +ImageFormat ImageData::image_format() const +{ + return image_data->image_format; +} + +EIOSCommand ImageData::command() const +{ + return image_data->command; +} + +void ImageData::set_parent_process_id(std::int32_t pid) +{ + image_data->parent_process_id = pid; +} + +void ImageData::set_parent_thread_id(std::int32_t tid) +{ + image_data->parent_thread_id = tid; +} + +void ImageData::set_image_width(std::int32_t width) +{ + image_data->image_width = width; +} + +void ImageData::set_image_height(std::int32_t height) +{ + image_data->image_height = height; +} + +void ImageData::set_target_width(std::int32_t width) +{ + image_data->target_width = width; +} + +void ImageData::set_target_height(std::int32_t height) +{ + image_data->target_height = height; +} + +void ImageData::set_debug_graphics(bool debug_graphics) +{ + image_data->debug_graphics = debug_graphics; +} + +void ImageData::set_ui_scaling(bool ui_scaling) +{ + image_data->ui_scaling = ui_scaling; +} + +void ImageData::set_image_format(ImageFormat format) +{ + image_data->image_format = format; +} + +void ImageData::set_command(EIOSCommand command) +{ + image_data->command = command; +} + +void* ImageData::data_buffer(std::ios_base::openmode mode) const noexcept +{ + if (mode & std::ios_base::in) + { + return image_data->data + stream->tellg(); + } + else if (mode & std::ios_base::out) + { + return image_data->data + stream->tellp(); + } + else + { + return nullptr; + } +} + +std::uint8_t* ImageData::image_buffer() const noexcept +{ + return reinterpret_cast(image_data.get()) + sizeof(EIOSData); +} + +std::uint8_t* ImageData::debug_image_buffer() const noexcept +{ + return image_buffer() + image_width() * image_height() * 4; +} + +Stream& ImageData::data_stream() const noexcept +{ + return *stream; +} \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Plugin/ImageData.hxx b/third-party/RemoteInput/RemoteInput/Plugin/ImageData.hxx new file mode 100644 index 0000000..6a51eaa --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Plugin/ImageData.hxx @@ -0,0 +1,57 @@ +// +// Created by Brandon on 2023-03-22. +// + +#ifndef IMAGEDATA_HXX_INCLUDED +#define IMAGEDATA_HXX_INCLUDED + +#include +#include "EIOSTypes.hxx" +#include "Stream.hxx" + +struct EIOSData; +enum class EIOSCommand: std::uint32_t; + +class ImageData +{ +private: + std::unique_ptr stream; + std::unique_ptr image_data; + +public: + ImageData(void* buffer, std::size_t size); + ~ImageData(); + + void prepare_for_read() const; + void prepare_for_write() const; + + std::int32_t parent_process_id() const; + std::int32_t parent_thread_id() const; + std::int32_t image_width() const; + std::int32_t image_height() const; + std::int32_t target_width() const; + std::int32_t target_height() const; + bool debug_graphics() const; + bool ui_scaling() const; + ImageFormat image_format() const; + EIOSCommand command() const; + + void set_parent_process_id(std::int32_t pid); + void set_parent_thread_id(std::int32_t tid); + void set_image_width(std::int32_t width); + void set_image_height(std::int32_t height); + void set_target_width(std::int32_t width); + void set_target_height(std::int32_t height); + void set_debug_graphics(bool debug_graphics); + void set_ui_scaling(bool ui_scaling); + void set_image_format(ImageFormat format); + void set_command(EIOSCommand command); + + void* data_buffer(std::ios_base::openmode mode) const noexcept; + + std::uint8_t* image_buffer() const noexcept; + std::uint8_t* debug_image_buffer() const noexcept; + Stream& data_stream() const noexcept; +}; + +#endif /* IMAGEDATA_HXX_INCLUDED */ diff --git a/third-party/RemoteInput/RemoteInput/Plugin/InputOutput.cxx b/third-party/RemoteInput/RemoteInput/Plugin/InputOutput.cxx new file mode 100644 index 0000000..dfe6d03 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Plugin/InputOutput.cxx @@ -0,0 +1,1491 @@ +// +// InputOutput.cxx +// RemoteInput +// +// Created by Brandon on 2019-12-28. +// Copyright © 2019 XIO. All rights reserved. +// + +#include "InputOutput.hxx" +#include "ControlCenter.hxx" +#include "Platform.hxx" +#include "Random.hxx" +#include "Applet.hxx" +#include "InputEvent.hxx" +#include "FocusEvent.hxx" +#include "KeyEvent.hxx" +#include "MouseEvent.hxx" +#include "MouseWheelEvent.hxx" +#include "PointerInfo.hxx" +#include "SunToolkit.hxx" +#include "Toolkit.hxx" +#include "Window.hxx" +#include "WindowEvent.hxx" + +#if !defined(_MSC_VER) +#warning "WINDOWS BS" +#endif + +#ifdef VK_SHIFT +#undef VK_SHIFT +#undef VK_LSHIFT +#undef VK_RSHIFT +#undef VK_CONTROL +#undef VK_APPS +#undef VK_TAB +#undef VK_BACK +#undef VK_RETURN +#endif // VK_SHIFT + +enum ControlKeys : std::uint32_t +{ + VK_FUNCTION = 0x0, + VK_NUM_LOCK = 0x90, + VK_SHIFT = 0x10, + VK_LSHIFT = 0xA0, + VK_RSHIFT = 0xA1, + VK_CONTROL = 0x11, + VK_LEFT_CONTROL = 0xA2, + VK_RIGHT_CONTROL = 0xA3, + VK_LEFT_ALT = 0xA4, + VK_RIGHT_ALT = 0xA5, + + VK_COMMAND = 0x9D, //VK_META + VK_LEFT_WINDOWS = 0x5B, + VK_RIGHT_WINDOWS = 0x5C, + VK_APPS = 0x5D, + VK_TAB = 0x09, + VK_BACK = 0x08, + VK_RETURN = 0x0D +}; + +static ControlKeys control_keys[] = { + ControlKeys::VK_FUNCTION, + ControlKeys::VK_NUM_LOCK, + ControlKeys::VK_LSHIFT, + ControlKeys::VK_RSHIFT, + ControlKeys::VK_LEFT_CONTROL, + ControlKeys::VK_RIGHT_CONTROL, + ControlKeys::VK_LEFT_ALT, + ControlKeys::VK_RIGHT_ALT, + ControlKeys::VK_COMMAND, + ControlKeys::VK_LEFT_WINDOWS, + ControlKeys::VK_RIGHT_WINDOWS, + ControlKeys::VK_APPS, + ControlKeys::VK_TAB, + ControlKeys::VK_BACK, + ControlKeys::VK_RETURN +}; + +static java::KeyEvent::KeyCodes control_keys_locations[] = { + java::KeyEvent::KeyCodes::KEY_LOCATION_UNKNOWN, + java::KeyEvent::KeyCodes::KEY_LOCATION_STANDARD, + java::KeyEvent::KeyCodes::KEY_LOCATION_LEFT, + java::KeyEvent::KeyCodes::KEY_LOCATION_LEFT, + java::KeyEvent::KeyCodes::KEY_LOCATION_RIGHT, + java::KeyEvent::KeyCodes::KEY_LOCATION_LEFT, + java::KeyEvent::KeyCodes::KEY_LOCATION_LEFT, + java::KeyEvent::KeyCodes::KEY_LOCATION_RIGHT, + java::KeyEvent::KeyCodes::KEY_LOCATION_LEFT, + java::KeyEvent::KeyCodes::KEY_LOCATION_RIGHT, + java::KeyEvent::KeyCodes::KEY_LOCATION_LEFT, + java::KeyEvent::KeyCodes::KEY_LOCATION_LEFT, + java::KeyEvent::KeyCodes::KEY_LOCATION_RIGHT, + java::KeyEvent::KeyCodes::KEY_LOCATION_RIGHT, + java::KeyEvent::KeyCodes::KEY_LOCATION_STANDARD, + java::KeyEvent::KeyCodes::KEY_LOCATION_STANDARD, + java::KeyEvent::KeyCodes::KEY_LOCATION_STANDARD +}; + +InputOutput::InputOutput(Reflection* reflection) noexcept : vm(nullptr), applet(reflection->getApplet()), mutex(), input_thread(new ThreadPool(2)), event_queue(nullptr), currently_held_key(-1), held_keys(), x(-1), y(-1), w(-1), h(-1), click_count(0), keyboard_speed(0), keyboard_repeat_delay(0), mouse_buttons() +{ + reflection->getEnv()->GetJavaVM(&vm); + + event_queue = std::make_unique(reflection->getEnv()); + x = std::numeric_limits::min(); + y = std::numeric_limits::min(); + + auto scale_to_range = [](float value, float fromMin, float fromMax, float toMin, float toMax) -> float { + return (((toMax - toMin) * (value - fromMin)) / (fromMax - fromMin)) + toMin; + }; + + #if defined(_WIN32) || defined(_WIN64) + #if defined(WINDOWS_SYSTEM_INPUT_INFO) + //0 (approximately 250 ms delay) through 3 (approximately 1 second delay). + std::int32_t keyboard_delay = 0; + SystemParametersInfo(SPI_GETKEYBOARDDELAY, 0, reinterpret_cast(&keyboard_delay), 0); + + //Small variance in delay due to electrical signal, lag, hardware, etc.. + std::int32_t min_delay = Random::instance()->generate_random_int(248, 252); + std::int32_t max_delay = Random::instance()->generate_random_int(998, 1002); + this->keyboard_repeat_delay = scale_to_range(keyboard_delay, 0, 3, min_delay, max_delay); + + //0 (approximately 2.5 repetitions per second) through 31 (approximately 30 repetitions per second). + //The actual repeat rates are hardware-dependent and may vary from a linear scale by as much as 20% + std::int32_t keyboard_repeat_speed = 0; + SystemParametersInfo(SPI_GETKEYBOARDSPEED, 0, reinterpret_cast(&keyboard_repeat_speed), 0); + + float repeat_rate = scale_to_range(keyboard_repeat_speed, 0, 31, 2.5, 30); + repeat_rate += repeat_rate * Random::instance()->generate_random_float(0.0, 0.20); + this->keyboard_speed = round(60.0 / repeat_rate); + #else + //Windows default is 250ms delay and 24ms repeat rate + this->keyboard_repeat_delay = scale_to_range(0, 0, 3, 250, 1000) + Random::instance()->generate_random_int(-2, 2); + + float repeat_rate = scale_to_range(0, 0, 31, 2.5, 30); + repeat_rate += repeat_rate * Random::instance()->generate_random_float(0.0, 0.20); + this->keyboard_speed = round(60.0 / repeat_rate); + #endif + #elif defined(__APPLE__) + //MacOS default is 225ms delay and 15ms repeat rate + this->keyboard_repeat_delay = scale_to_range(15, 15, 120, 225, 1800) + Random::instance()->generate_random_int(-2, 2); + + float repeat_rate = scale_to_range(1, 2, 120, 30, 1800); + repeat_rate += repeat_rate * Random::instance()->generate_random_float(0.0, 0.20); + this->keyboard_speed = round(repeat_rate); + #else + //Linux default is 660ms delay and 25ms repeat rate + this->keyboard_repeat_delay = scale_to_range(44, 15, 120, 225, 1800) + Random::instance()->generate_random_int(-2, 2); + + float repeat_rate = scale_to_range(2, 2, 120, 25, 1800); + repeat_rate += repeat_rate * Random::instance()->generate_random_float(0.0, 0.20); + this->keyboard_speed = round(repeat_rate); + #endif + + java::EventQueue queue = java::Toolkit::getDefaultToolkit(reflection->getEnv()).getSystemEventQueue(); + queue.push(this->event_queue.get()); +} + +InputOutput::~InputOutput() noexcept +{ + // Purposely do NOT join the thread or reset! This will hang upon dll unload. + // We `release` it instead to prevent this. + //this->input_thread->terminate(); + this->input_thread.release(); + this->event_queue.reset(); + this->vm = nullptr; + this->applet = nullptr; +} + +void InputOutput::handle_resize(java::Component* component) noexcept +{ + component->getSize(this->w, this->h); + + /*bool isMouseInsideComponent = !(this->x < 0 || this->y < 0 || this->x > static_cast(this->w) || this->y > static_cast(this->h)); + if (isMouseInsideComponent) + { + component->getMousePosition(this->x, this->y); + + if (this->x == -1 || this->y == -1) + { + JNIEnv* env = component->getEnv(); + + std::int32_t x = this->x; + std::int32_t y = this->y; + + java::PointerInfo info = java::PointerInfo::getPointerInfo(env); + info.getLocation(x, y); + info.PointToScreen(env, x, y,component); + + isMouseInsideComponent = !(x < 0 || y < 0 || x > static_cast(this->w) || y > static_cast(this->h)); + if (isMouseInsideComponent) + { + this->x = x; + this->y = y; + } + } + }*/ +} + +void InputOutput::hold_key(std::int32_t code) noexcept +{ + extern std::unique_ptr control_center; + if (!control_center) + { + return; + } + + //Key is not already held.. + if (!this->is_key_held(code)) + { + //Key is a control key + if (std::find(std::begin(control_keys), std::end(control_keys), code) != std::end(control_keys)) + { + //Control Keys only generate a single held event.. + held_keys.push_back(code); + + //PostEvent + java::Component receiver = control_center->reflect_canvas(); + + JNIEnv* env = receiver.getEnv(); + if (!this->has_focus(&receiver)) + { + this->gain_focus(&receiver); + } + + std::int64_t when = std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); + std::int32_t modifiers = GetActiveKeyModifiers(); + std::int32_t keycode = GetJavaKeyCode(code); + std::int32_t location = GetKeyLocation(code); + + java::KeyEvent::Post(env, + &receiver, + java::KeyEvent::KeyCodes::KEY_PRESSED, + when, + modifiers, + keycode, + static_cast(java::KeyEvent::KeyCodes::CHAR_UNDEFINED), + location); + } + else + { + //Key already being pressed so just replace it + if (currently_held_key != -1 && this->keyboard_speed >= 0 && this->keyboard_repeat_delay >= 0) + { + currently_held_key = code; + held_keys.push_back(code); + + //Post Event + java::Component receiver = control_center->reflect_canvas(); + if (!this->has_focus(&receiver)) + { + this->gain_focus(&receiver); + } + + JNIEnv* env = receiver.getEnv(); + std::int64_t when = std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); + std::int32_t modifiers = GetActiveKeyModifiers(); + std::int32_t keycode = GetJavaKeyCode(code); + std::int32_t location = GetKeyLocation(code); + + java::KeyEvent::Post(env, + &receiver, + java::KeyEvent::KeyCodes::KEY_PRESSED, + when, + modifiers, + keycode, + NativeKeyCodeToChar(code, modifiers), + location); + + java::KeyEvent::Post(env, + &receiver, + java::KeyEvent::KeyCodes::KEY_TYPED, + when, + modifiers, + 0, + NativeKeyCodeToChar(code, modifiers), + java::KeyEvent::KeyCodes::KEY_LOCATION_UNKNOWN); + + if (this->keyboard_repeat_delay > 0) + { + yield_thread(std::chrono::milliseconds(this->keyboard_repeat_delay)); + } + } + else + { + currently_held_key = code; + held_keys.push_back(code); + + //Post Event + java::Component receiver = control_center->reflect_canvas(); + if (!this->has_focus(&receiver)) + { + this->gain_focus(&receiver); + } + + JNIEnv* env = receiver.getEnv(); + std::int64_t when = std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); + std::int32_t modifiers = GetActiveKeyModifiers(); + std::int32_t keycode = GetJavaKeyCode(code); + std::int32_t location = GetKeyLocation(code); + + java::KeyEvent::Post(env, + &receiver, + java::KeyEvent::KeyCodes::KEY_PRESSED, + when, + modifiers, + keycode, + NativeKeyCodeToChar(code, modifiers), + location); + + java::KeyEvent::Post(env, + &receiver, + java::KeyEvent::KeyCodes::KEY_TYPED, + when, + modifiers, + 0, + NativeKeyCodeToChar(code, modifiers), + java::KeyEvent::KeyCodes::KEY_LOCATION_UNKNOWN); + + if (this->keyboard_speed >= 0 && this->keyboard_repeat_delay >= 0) + { + input_thread->add_task([&](std::atomic_bool &stopped) { + if (this->keyboard_repeat_delay > 0) + { + yield_thread(std::chrono::milliseconds(this->keyboard_repeat_delay)); + } + + JNIEnv* env = nullptr; + this->vm->AttachCurrentThreadAsDaemon(reinterpret_cast(&env), nullptr); + + java::Applet applet{env, this->applet, false}; + java::Component receiver = applet.getComponent(0); + + while (!stopped) + { + if (currently_held_key == -1) + { + break; + } + + if (this->keyboard_speed > 0) + { + yield_thread(std::chrono::milliseconds(this->keyboard_speed)); + } + + std::int32_t code = currently_held_key; + + //Post Event + if (!this->has_focus(&receiver)) + { + this->gain_focus(&receiver); + } + + std::int64_t when = std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); + std::int32_t modifiers = GetActiveKeyModifiers(); + std::int32_t keycode = GetJavaKeyCode(code); + std::int32_t location = GetKeyLocation(code); + + java::KeyEvent::Post(env, + &receiver, + java::KeyEvent::KeyCodes::KEY_PRESSED, + when, + modifiers, + keycode, + NativeKeyCodeToChar(code, modifiers), + location); + + java::KeyEvent::Post(env, + &receiver, + java::KeyEvent::KeyCodes::KEY_TYPED, + when, + modifiers, + 0, + NativeKeyCodeToChar(code, modifiers), + java::KeyEvent::KeyCodes::KEY_LOCATION_UNKNOWN); + } + }); + } + } + } + } +} + +void InputOutput::release_key(std::int32_t code) noexcept +{ + extern std::unique_ptr control_center; + if (!control_center) + { + return; + } + + if (auto it = std::find(held_keys.begin(), held_keys.end(), code); it != held_keys.end()) + { + if (std::find(std::begin(control_keys), std::end(control_keys), code) != std::end(control_keys)) + { + held_keys.erase(it); //held_keys.erase(std::remove(held_keys.begin(), held_keys.end(), code), held_keys.end()); + + //Post Event + java::Component receiver = control_center->reflect_canvas(); + if (!this->has_focus(&receiver)) + { + this->gain_focus(&receiver); + } + + JNIEnv* env = receiver.getEnv(); + std::int64_t when = std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); + std::int32_t modifiers = GetActiveKeyModifiers(); + std::int32_t keycode = GetJavaKeyCode(code); + std::int32_t location = GetKeyLocation(code); + + java::KeyEvent::Post(env, + &receiver, + java::KeyEvent::KeyCodes::KEY_RELEASED, + when, + modifiers, + keycode, + static_cast(java::KeyEvent::KeyCodes::CHAR_UNDEFINED), + location); + } + else + { + //Remove the held key from the list.. + held_keys.erase(it); + + //Find the next non-control held-key.. + auto jt = std::find_if(held_keys.crbegin(), held_keys.crend(), [](std::int32_t key){ + return std::find(std::begin(control_keys), std::end(control_keys), key) == std::end(control_keys); + }); + + //Set the next currently held key to the first non-control key.. + currently_held_key = jt != held_keys.crend() ? *jt : -1; + + //Post Event + java::Component receiver = control_center->reflect_canvas(); + if (!this->has_focus(&receiver)) + { + this->gain_focus(&receiver); + } + + JNIEnv* env = receiver.getEnv(); + std::int64_t when = std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); + std::int32_t modifiers = GetActiveKeyModifiers(); + std::int32_t keycode = GetJavaKeyCode(code); + std::int32_t location = GetKeyLocation(code); + + java::KeyEvent::Post(env, + &receiver, + java::KeyEvent::KeyCodes::KEY_RELEASED, + when, + modifiers, + keycode, + NativeKeyCodeToChar(code, modifiers), + location); + } + } +} + +bool InputOutput::is_key_held(std::int32_t code) const noexcept +{ + return std::find(std::begin(held_keys), std::end(held_keys), code) != std::end(held_keys); +} + +bool InputOutput::any_key_held(std::array&& keys) const noexcept +{ + return std::any_of(std::cbegin(held_keys), std::cend(held_keys), [&](std::int32_t key){ + return std::find(std::cbegin(keys), std::cend(keys), key) != std::cend(keys); + }); +} + +void InputOutput::send_string(std::string string, std::int32_t keywait, std::int32_t keymodwait) const noexcept +{ + extern std::unique_ptr control_center; + if (!control_center) + { + return; + } + + java::Component receiver = control_center->reflect_canvas(); + JNIEnv* env = receiver.getEnv(); + + if (!this->has_focus(&receiver)) + { + this->gain_focus(&receiver); + } + + bool isShiftDown = false; + + for (std::size_t i = 0; i < string.length(); ++i) + { + char c = string[i]; + char n = i == string.length() - 1 ? '\0' : string[i + 1]; + std::int32_t modifiers = this->ModifiersForChar(c); + + //Modifier Key + if (modifiers & java::InputEvent::InputEventMasks::SHIFT_DOWN_MASK) + { + if (!isShiftDown) + { + isShiftDown = true; + + std::int32_t code = VK_LSHIFT; + std::int64_t when = std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); + std::int32_t modifiers = java::InputEvent::InputEventMasks::SHIFT_DOWN_MASK; + std::int32_t keycode = GetJavaKeyCode(code); + std::int32_t location = GetKeyLocation(code); + + java::KeyEvent::Post(env, + &receiver, + java::KeyEvent::KeyCodes::KEY_PRESSED, + when, + modifiers, + keycode, + static_cast(java::KeyEvent::KeyCodes::CHAR_UNDEFINED), + location); + + yield_thread(std::chrono::milliseconds(lround(Random::instance()->generate_random_float(1.0, 1.1) * keymodwait))); + } + } + + //Character Key + std::int32_t code = static_cast(c); + std::int64_t when = std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); + std::int32_t keycode = CharToJavaKeyCode(code); + std::int32_t location = GetKeyLocation(code); + + java::KeyEvent::Post(env, + &receiver, + java::KeyEvent::KeyCodes::KEY_PRESSED, + when, + modifiers, + keycode, + static_cast(c), + location); + + java::KeyEvent::Post(env, + &receiver, + java::KeyEvent::KeyCodes::KEY_TYPED, + when, + modifiers, + 0, + static_cast(c), + java::KeyEvent::KeyCodes::KEY_LOCATION_UNKNOWN); + + yield_thread(std::chrono::milliseconds(lround(Random::instance()->generate_random_float(1.0, 1.1) * keywait))); + when = std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); + + java::KeyEvent::Post(env, + &receiver, + java::KeyEvent::KeyCodes::KEY_RELEASED, + when, + modifiers, + keycode, + static_cast(c), + location); + + yield_thread(std::chrono::milliseconds(lround(Random::instance()->generate_random_float(1.0, 1.1) * keywait))); + + //Modifier Key + if ((isShiftDown && i == string.length() - 1) || (n != '\0' && !(this->ModifiersForChar(n) & java::InputEvent::InputEventMasks::SHIFT_DOWN_MASK))) + { + isShiftDown = false; + + std::int32_t code = VK_LSHIFT; + std::int64_t when = std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); + std::int32_t modifiers = java::InputEvent::InputEventMasks::SHIFT_DOWN_MASK; + std::int32_t keycode = GetJavaKeyCode(code); + std::int32_t location = GetKeyLocation(code); + + java::KeyEvent::Post(env, + &receiver, + java::KeyEvent::KeyCodes::KEY_RELEASED, + when, + modifiers, + keycode, + static_cast(java::KeyEvent::KeyCodes::CHAR_UNDEFINED), + location); + + yield_thread(std::chrono::milliseconds(lround(Random::instance()->generate_random_float(1.0, 1.1) * keymodwait))); + } + } +} + +void InputOutput::send_key(char key, std::int32_t key_down_time, std::int32_t key_up_time, std::int32_t modifier_down_time, std::int32_t modifier_up_time) const noexcept +{ + extern std::unique_ptr control_center; + if (!control_center) + { + return; + } + + java::Component receiver = control_center->reflect_canvas(); + JNIEnv* env = receiver.getEnv(); + + if (!this->has_focus(&receiver)) + { + this->gain_focus(&receiver); + } + + std::int32_t modifiers = this->ModifiersForChar(key); + + //Modifier Key is already pressed + bool isShiftDown = GetActiveKeyModifiers() & java::InputEvent::InputEventMasks::SHIFT_DOWN_MASK; + + //Modifier Key + if (!isShiftDown && (modifiers & java::InputEvent::InputEventMasks::SHIFT_DOWN_MASK)) + { + std::int32_t code = VK_LSHIFT; + std::int64_t when = std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); + std::int32_t modifiers = java::InputEvent::InputEventMasks::SHIFT_DOWN_MASK; + std::int32_t keycode = GetJavaKeyCode(code); + std::int32_t location = GetKeyLocation(code); + + java::KeyEvent::Post(env, + &receiver, + java::KeyEvent::KeyCodes::KEY_PRESSED, + when, + modifiers, + keycode, + static_cast(java::KeyEvent::KeyCodes::CHAR_UNDEFINED), + location); + + yield_thread(std::chrono::milliseconds(modifier_down_time)); + } + + //Character Key + std::int32_t code = static_cast(key); + std::int64_t when = std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); + std::int32_t keycode = CharToJavaKeyCode(code); + std::int32_t location = GetKeyLocation(code); + + java::KeyEvent::Post(env, + &receiver, + java::KeyEvent::KeyCodes::KEY_PRESSED, + when, + modifiers, + keycode, + static_cast(key), + location); + + java::KeyEvent::Post(env, + &receiver, + java::KeyEvent::KeyCodes::KEY_TYPED, + when, + modifiers, + 0, + static_cast(key), + java::KeyEvent::KeyCodes::KEY_LOCATION_UNKNOWN); + + yield_thread(std::chrono::milliseconds(key_down_time)); + when = std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); + + java::KeyEvent::Post(env, + &receiver, + java::KeyEvent::KeyCodes::KEY_RELEASED, + when, + modifiers, + keycode, + static_cast(key), + location); + + yield_thread(std::chrono::milliseconds(key_up_time)); + + //Modifier Key + if (!isShiftDown && (modifiers & java::InputEvent::InputEventMasks::SHIFT_DOWN_MASK)) + { + std::int32_t code = VK_LSHIFT; + std::int64_t when = std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); + std::int32_t modifiers = java::InputEvent::InputEventMasks::SHIFT_DOWN_MASK; + std::int32_t keycode = GetJavaKeyCode(code); + std::int32_t location = GetKeyLocation(code); + + java::KeyEvent::Post(env, + &receiver, + java::KeyEvent::KeyCodes::KEY_RELEASED, + when, + modifiers, + keycode, + static_cast(java::KeyEvent::KeyCodes::CHAR_UNDEFINED), + location); + + yield_thread(std::chrono::milliseconds(modifier_up_time)); + } +} + +void InputOutput::key_send(std::string string, std::vector sleeptimes) const noexcept +{ + extern std::unique_ptr control_center; + if (!control_center) + { + return; + } + + java::Component receiver = control_center->reflect_canvas(); + JNIEnv* env = receiver.getEnv(); + + if (!this->has_focus(&receiver)) + { + this->gain_focus(&receiver); + } + + bool isShiftDown = false; + std::int32_t sleepindex = 0; + + for (std::size_t i = 0; i < string.length(); ++i) + { + char c = string[i]; + char n = i == string.length() - 1 ? '\0' : string[i + 1]; + std::int32_t modifiers = this->ModifiersForChar(c); + + //Modifier Key + if (modifiers & java::InputEvent::InputEventMasks::SHIFT_DOWN_MASK) + { + if (!isShiftDown) + { + isShiftDown = true; + + std::int32_t code = VK_LSHIFT; + std::int64_t when = std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); + std::int32_t modifiers = java::InputEvent::InputEventMasks::SHIFT_DOWN_MASK; + std::int32_t keycode = GetJavaKeyCode(code); + std::int32_t location = GetKeyLocation(code); + + java::KeyEvent::Post(env, + &receiver, + java::KeyEvent::KeyCodes::KEY_PRESSED, + when, + modifiers, + keycode, + static_cast(java::KeyEvent::KeyCodes::CHAR_UNDEFINED), + location); + + yield_thread(std::chrono::milliseconds(sleeptimes[sleepindex++])); + } + } + + //Character Key + std::int32_t code = static_cast(c); + std::int64_t when = std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); + std::int32_t keycode = CharToJavaKeyCode(code); + std::int32_t location = GetKeyLocation(code); + + java::KeyEvent::Post(env, + &receiver, + java::KeyEvent::KeyCodes::KEY_PRESSED, + when, + modifiers, + keycode, + static_cast(c), + location); + + java::KeyEvent::Post(env, + &receiver, + java::KeyEvent::KeyCodes::KEY_TYPED, + when, + modifiers, + 0, + static_cast(c), + java::KeyEvent::KeyCodes::KEY_LOCATION_UNKNOWN); + + yield_thread(std::chrono::milliseconds(sleeptimes[sleepindex++])); + when = std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); + + java::KeyEvent::Post(env, + &receiver, + java::KeyEvent::KeyCodes::KEY_RELEASED, + when, + modifiers, + keycode, + static_cast(c), + location); + + yield_thread(std::chrono::milliseconds(sleeptimes[sleepindex++])); + + //Modifier Key + if ((isShiftDown && i == string.length() - 1) || (n != '\0' && !(this->ModifiersForChar(n) & java::InputEvent::InputEventMasks::SHIFT_DOWN_MASK))) + { + isShiftDown = false; + + std::int32_t code = VK_LSHIFT; + std::int64_t when = std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); + std::int32_t modifiers = java::InputEvent::InputEventMasks::SHIFT_DOWN_MASK; + std::int32_t keycode = GetJavaKeyCode(code); + std::int32_t location = GetKeyLocation(code); + + java::KeyEvent::Post(env, + &receiver, + java::KeyEvent::KeyCodes::KEY_RELEASED, + when, + modifiers, + keycode, + static_cast(java::KeyEvent::KeyCodes::CHAR_UNDEFINED), + location); + + yield_thread(std::chrono::milliseconds(sleeptimes[sleepindex++])); + } + } +} + +bool InputOutput::has_focus() const noexcept +{ + extern std::unique_ptr control_center; + if (!control_center) + { + return false; + } + + java::Component component = control_center->reflect_canvas(); + return component.hasFocus(); +} + +bool InputOutput::has_focus(java::Component* component) const noexcept +{ + return component->hasFocus(); +} + +void InputOutput::gain_focus() const noexcept +{ + extern std::unique_ptr control_center; + if (!control_center) + { + return; + } + + java::Component component = control_center->reflect_canvas(); + this->gain_focus(&component); +} + +void InputOutput::gain_focus(java::Component* component) const noexcept +{ + JNIEnv* env = component->getEnv(); + java::Window window = java::SunToolkit::getContainingWindow(component); + + if (window.get()) + { + java::WindowEvent::Post(env, &window, java::WindowEvent::WindowEventCodes::WINDOW_ACTIVATED, 0, 0); + std::this_thread::sleep_for(std::chrono::milliseconds(Random::instance()->generate_random_int(100, 250))); + java::WindowEvent::Post(env, &window, java::WindowEvent::WindowEventCodes::WINDOW_GAINED_FOCUS, 0, 0); + std::this_thread::sleep_for(std::chrono::milliseconds(Random::instance()->generate_random_int(100, 250))); + } + + java::FocusEvent::Post(env, component, java::FocusEvent::FocusCodes::FOCUS_GAINED, false, java::FocusEvent::Cause::ACTIVATION); + + auto now = std::chrono::high_resolution_clock::now(); + while (!component->hasFocus() && elapsed_time(now) < 2) + { + yield_thread(std::chrono::milliseconds(Random::instance()->generate_random_int(100, 250))); + } +} + +void InputOutput::lose_focus() const noexcept +{ + extern std::unique_ptr control_center; + if (!control_center) + { + return; + } + + java::Component component = control_center->reflect_canvas(); + this->lose_focus(&component); +} + +void InputOutput::lose_focus(java::Component* component) const noexcept +{ + JNIEnv* env = component->getEnv(); + java::Window window = java::SunToolkit::getContainingWindow(component); + + if (window.get()) + { + java::WindowEvent::Post(env, &window, java::WindowEvent::WindowEventCodes::WINDOW_DEACTIVATED, 0, 0); + std::this_thread::sleep_for(std::chrono::milliseconds(Random::instance()->generate_random_int(100, 250))); + } + + java::FocusEvent::Post(env, component, java::FocusEvent::FocusCodes::FOCUS_LOST, true, java::FocusEvent::Cause::ACTIVATION); + + auto now = std::chrono::high_resolution_clock::now(); + while (component->hasFocus() && elapsed_time(now) < 2) + { + yield_thread(std::chrono::milliseconds(Random::instance()->generate_random_int(100, 250))); + } +} + +bool InputOutput::is_keyboard_input_enabled() const noexcept +{ + return event_queue->is_keyboard_input_enabled(); +} + +void InputOutput::set_keyboard_input_enabled(bool enabled) const noexcept +{ + event_queue->set_keyboard_input_enabled(enabled); + + if (!enabled) + { + if (!this->has_focus()) + { + this->gain_focus(); + } + } +} + +bool InputOutput::is_mouse_input_enabled() const noexcept +{ + return event_queue->is_mouse_input_enabled(); +} + +void InputOutput::set_mouse_input_enabled(bool enabled) const noexcept +{ + event_queue->set_mouse_input_enabled(enabled); + + if (!enabled) + { + if (!this->has_focus()) + { + this->gain_focus(); + } + } +} + +std::int32_t InputOutput::get_keyboard_speed() const noexcept +{ + return this->keyboard_speed; +} + +void InputOutput::set_keyboard_speed(std::int32_t speed) noexcept +{ + this->keyboard_speed = speed; +} + +std::int32_t InputOutput::get_keyboard_repeat_delay() const noexcept +{ + return this->keyboard_repeat_delay; +} + +void InputOutput::set_keyboard_repeat_delay(std::int32_t delay) noexcept +{ + this->keyboard_repeat_delay = delay; +} + +void InputOutput::get_mouse_position(std::int32_t* x, std::int32_t* y) noexcept +{ + extern std::unique_ptr control_center; + if (!control_center) + { + return; + } + + if (this->x == std::numeric_limits::min() || this->y == std::numeric_limits::min()) + { + java::Component receiver = control_center->reflect_canvas(); + receiver.getMousePosition(this->x, this->y); + receiver.getSize(this->w, this->h); + + if (this->x == -1 || this->y == -1) + { + java::Component receiver = control_center->reflect_canvas(); + JNIEnv* env = receiver.getEnv(); + + std::int32_t x = this->x; + std::int32_t y = this->y; + + java::PointerInfo info = java::PointerInfo::getPointerInfo(env); + info.getLocation(x, y); + info.PointToScreen(env, x, y, &receiver); + + this->x = x; + this->y = y; + } + } + else if (!has_focus() && (!is_keyboard_input_enabled() || !is_mouse_input_enabled())) + { + java::Component receiver = control_center->reflect_canvas(); + this->handle_resize(&receiver); + } + + *x = this->x; + *y = this->y; +} + +void InputOutput::get_real_mouse_position(std::int32_t* x, std::int32_t* y) const noexcept +{ + extern std::unique_ptr control_center; + if (!control_center) + { + return; + } + + java::Component receiver = control_center->reflect_canvas(); + JNIEnv* env = receiver.getEnv(); + + java::PointerInfo info = java::PointerInfo::getPointerInfo(env); + info.getLocation(*x, *y); + info.PointToScreen(env, *x, *y, &receiver); +} + +void InputOutput::move_mouse(std::int32_t x, std::int32_t y) noexcept +{ + extern std::unique_ptr control_center; + if (!control_center) + { + return; + } + + java::Component receiver = control_center->reflect_canvas(); + JNIEnv* env = receiver.getEnv(); + this->handle_resize(&receiver); + + std::int64_t when = std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); + + bool isRequestedPositionInsideComponent = !(x < 0 || y < 0 || x > static_cast(this->w) || y > static_cast(this->h)); + bool isMouseInsideComponent = !(this->x < 0 || this->y < 0 || this->x > static_cast(this->w) || this->y > static_cast(this->h)); + bool isDragging = mouse_buttons[0] || mouse_buttons[1] || mouse_buttons[2]; + + //Button priority is 1 (left), 3 (right), 2 (middle) + std::int32_t button = mouse_buttons[0] ? 1 : mouse_buttons[2] ? 3 : mouse_buttons[1] ? 2 : 0; + std::int32_t buttonMask = (mouse_buttons[0] ? java::InputEvent::GetDownMaskForButton(mouse_buttons[0]) : 0) | + (mouse_buttons[1] ? java::InputEvent::GetDownMaskForButton(mouse_buttons[1]) : 0) | + (mouse_buttons[2] ? java::InputEvent::GetDownMaskForButton(mouse_buttons[2]) : 0); + + //Key extended masks + buttonMask |= GetActiveKeyModifiers(); + + if (isRequestedPositionInsideComponent && !isMouseInsideComponent) + { + //MOUSE_ENTERED + this->x = x; this->y = y; + java::MouseEvent::Post(env, &receiver, java::MouseEvent::MouseEventCodes::MOUSE_ENTERED, when, buttonMask, x, y, 0, false, 0); + java::MouseEvent::Post(env, &receiver, java::MouseEvent::MouseEventCodes::MOUSE_MOVED, when, buttonMask, x, y, 0, false, 0); + } + else if (!isRequestedPositionInsideComponent && isMouseInsideComponent) + { + //MOUSE_EXITED + this->x = x; this->y = y; + java::MouseEvent::Post(env, &receiver, java::MouseEvent::MouseEventCodes::MOUSE_EXITED, when, buttonMask, x, y, 0, false, 0); + } + else if (isRequestedPositionInsideComponent && isMouseInsideComponent) + { + //MOUSE_MOVED OR MOUSE_DRAGGED + if (isDragging) + { + this->x = x; this->y = y; + java::MouseEvent::Post(env, &receiver, java::MouseEvent::MouseEventCodes::MOUSE_DRAGGED, when, buttonMask, x, y, click_count, false, button); + } + else + { + this->x = x; this->y = y; + java::MouseEvent::Post(env, &receiver, java::MouseEvent::MouseEventCodes::MOUSE_MOVED, when, buttonMask, x, y, 0, false, 0); + } + } + else if (!isRequestedPositionInsideComponent && !isMouseInsideComponent) + { + //MOUSE_DRAGGED OUTSIDE + this->x = x; this->y = y; + if (isDragging) + { + java::MouseEvent::Post(env, &receiver, java::MouseEvent::MouseEventCodes::MOUSE_DRAGGED, when, buttonMask, x, y, click_count, false, button); + } + } +} + +void InputOutput::hold_mouse(std::int32_t button) noexcept +{ + if (!this->is_mouse_held(button)) + { + extern std::unique_ptr control_center; + if (!control_center) + { + return; + } + + java::Component receiver = control_center->reflect_canvas(); + JNIEnv* env = receiver.getEnv(); + this->handle_resize(&receiver); + + bool isRequestedPositionInsideComponent = !(this->x < 0 || this->y < 0 || this->x > static_cast(this->w) || this->y > static_cast(this->h)); + if (isRequestedPositionInsideComponent) + { + std::int64_t when = std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); + + //Button priority is 1 (left), 3 (right), 2 (middle) + mouse_buttons[SimbaMouseButtonToJava(button) - 1] = true; + std::int32_t button = mouse_buttons[0] ? 1 : mouse_buttons[2] ? 3 : mouse_buttons[1] ? 2 : 0; + std::int32_t buttonMask = (mouse_buttons[0] ? java::InputEvent::GetDownMaskForButton(mouse_buttons[0]) : 0) | + (mouse_buttons[1] ? java::InputEvent::GetDownMaskForButton(mouse_buttons[1]) : 0) | + (mouse_buttons[2] ? java::InputEvent::GetDownMaskForButton(mouse_buttons[2]) : 0); + + //Gain Focus + if (!this->has_focus(&receiver)) + { + this->gain_focus(&receiver); + } + + //Key extended masks + buttonMask |= GetActiveKeyModifiers(); + java::MouseEvent::Post(env, &receiver, java::MouseEvent::MouseEventCodes::MOUSE_PRESSED, when, buttonMask, this->x, this->y, click_count, false, button); + } + else + { + //Lose Focus + if (this->has_focus(&receiver)) + { + this->lose_focus(&receiver); + } + } + } +} + +void InputOutput::release_mouse(std::int32_t button) noexcept +{ + if (this->is_mouse_held(button)) + { + extern std::unique_ptr control_center; + if (!control_center) + { + return; + } + + java::Component receiver = control_center->reflect_canvas(); + JNIEnv* env = receiver.getEnv(); + this->handle_resize(&receiver); + + std::int64_t when = std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); + + bool isRequestedPositionInsideComponent = !(this->x < 0 || this->y < 0 || this->x > static_cast(this->w) || this->y > static_cast(this->h)); + bool isMouseInsideComponent = !(this->x < 0 || this->y < 0 || this->x > static_cast(this->w) || this->y > static_cast(this->h)); + bool isDragging = mouse_buttons[0] || mouse_buttons[1] || mouse_buttons[2]; + + //Button priority is 1 (left), 3 (right), 2 (middle) + mouse_buttons[SimbaMouseButtonToJava(button) - 1] = false; + + std::int32_t button = mouse_buttons[0] ? 1 : mouse_buttons[2] ? 3 : mouse_buttons[1] ? 2 : 0; + std::int32_t buttonMask = (mouse_buttons[0] ? java::InputEvent::GetDownMaskForButton(mouse_buttons[0]) : 0) | + (mouse_buttons[1] ? java::InputEvent::GetDownMaskForButton(mouse_buttons[1]) : 0) | + (mouse_buttons[2] ? java::InputEvent::GetDownMaskForButton(mouse_buttons[2]) : 0); + + //Key extended masks + buttonMask |= GetActiveKeyModifiers(); + + //MOUSE_RELEASED + java::MouseEvent::Post(env, &receiver, java::MouseEvent::MouseEventCodes::MOUSE_RELEASED, when, buttonMask, this->x, this->y, click_count, false, button); + + if (!isDragging && isRequestedPositionInsideComponent && isMouseInsideComponent) + { + //MOUSE_CLICKED + java::MouseEvent::Post(env, &receiver, java::MouseEvent::MouseEventCodes::MOUSE_CLICKED, when, buttonMask, this->x, this->y, click_count, false, button); + } + } +} + +void InputOutput::scroll_mouse(std::int32_t lines) noexcept +{ + extern std::unique_ptr control_center; + if (!control_center) + { + return; + } + + java::Component receiver = control_center->reflect_canvas(); + JNIEnv* env = receiver.getEnv(); + this->handle_resize(&receiver); + + bool isRequestedPositionInsideComponent = !(this->x < 0 || this->y < 0 || this->x > static_cast(this->w) || this->y > static_cast(this->h)); + bool isMouseInsideComponent = !(this->x < 0 || this->y < 0 || this->x > static_cast(this->w) || this->y > static_cast(this->h)); + + if (isRequestedPositionInsideComponent && isMouseInsideComponent) + { + //Relative Mouse Position + std::int32_t cx = 0; + std::int32_t cy = 0; + receiver.getLocationOnScreen(cx, cy); + + if (cx == -1 || cy == -1) + { + cx = 0; + cy = 0; + } + + std::int64_t when = std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); + std::int32_t modifiers = GetActiveKeyModifiers(); + + //Technically, this should be gausian curve or bell-curve or parabola as the wheel speeds up and slows down.. + double precision = lines > 0 ? Random::instance()->generate_random_int(1, 9) : lines < 0 ? Random::instance()->generate_random_int(-9, -1) : Random::instance()->generate_random_double(-1, 1); + precision /= 10.0; + + java::MouseWheelEvent::Post(env, + &receiver, + java::MouseEvent::MouseEventCodes::MOUSE_WHEEL, + when, + modifiers, + this->x, + this->y, + cx + this->x, + cy + this->y, + 0, + false, + java::MouseWheelEvent::MouseWheelEventCodes::WHEEL_UNIT_SCROLL, + 1, + lines, + lines + precision); + } + else if (isRequestedPositionInsideComponent && !isMouseInsideComponent) + { + std::int64_t when = std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); + + bool isDragging = mouse_buttons[0] || mouse_buttons[1] || mouse_buttons[2]; + if (isDragging) + { + return; //Developer is an idiot. We shouldn't even have to be here in the first place.. + } + + //Button priority is 1 (left), 3 (right), 2 (middle) + std::int32_t buttonMask = (mouse_buttons[0] ? java::InputEvent::GetDownMaskForButton(mouse_buttons[0]) : 0) | + (mouse_buttons[1] ? java::InputEvent::GetDownMaskForButton(mouse_buttons[1]) : 0) | + (mouse_buttons[2] ? java::InputEvent::GetDownMaskForButton(mouse_buttons[2]) : 0); + + //Key extended masks + buttonMask |= GetActiveKeyModifiers(); + + + //MOUSE_ENTERED + java::MouseEvent::Post(env, &receiver, java::MouseEvent::MouseEventCodes::MOUSE_ENTERED, when, buttonMask, this->x, this->y, 0, false, 0); + java::MouseEvent::Post(env, &receiver, java::MouseEvent::MouseEventCodes::MOUSE_MOVED, when, buttonMask, this->x, this->y, 0, false, 0); + + // Recursive call + scroll_mouse(lines); + } +} + +bool InputOutput::is_mouse_held(std::int32_t button) const noexcept +{ + switch (button) + { + case 1: return mouse_buttons[0]; + case 2: return mouse_buttons[1]; + case 3: return mouse_buttons[2]; + default: return false; + } +} + +void InputOutput::stop_all_processing() noexcept +{ + std::int32_t x = 0; + std::int32_t y = 0; + + this->get_mouse_position(&x, &y); + this->release_mouse(0); + this->release_mouse(1); + this->release_mouse(2); + + for (std::int32_t key : std::vector(held_keys.begin(), held_keys.end())) + { + this->release_key(key); + } + + this->set_keyboard_input_enabled(true); + this->set_mouse_input_enabled(true); +} + +jchar InputOutput::NativeKeyCodeToChar(std::int32_t keycode, std::int32_t modifiers) const noexcept +{ + static const std::int32_t mapping[256] = { //char16_t + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0008, 0x0009, 0x0000, 0x0000, 0x0000, 0x000A, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0020, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, + 0x0078, 0x0079, 0x007A, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x003B, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0060, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x005B, 0x005C, 0x005D, 0x0027, 0x0000, + 0x0000, 0x0000, 0x005C, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 + }; + + if (modifiers != 0) + { + static const std::string normal_chars = "`1234567890-=qwertyuiop[]\\asdfghjkl;'zxcvbnm,./"; + static const std::string shift_chars = "~!@#$%^&*()_+QWERTYUIOP{}|ASDFGHJKL:\"ZXCVBNM<>?"; + if (auto index = normal_chars.find(static_cast(mapping[keycode])); index != std::string::npos) + { + return static_cast(shift_chars[index]); + } + } + return static_cast(mapping[keycode]); +} + +std::int32_t InputOutput::CharToJavaKeyCode(char c) const noexcept +{ + //Convert char to Java VK-Codes + //0x00 and 0xFF are not type-able characters.. + static const std::uint32_t mapping[256] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0008, 0x0009, 0x000A, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x001B, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0020, 0x0205, 0x0098, 0x0208, 0x0203, 0x0000, 0x0096, 0x00DE, + 0x0207, 0x020A, 0x0097, 0x0209, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x0201, 0x003B, 0x0099, 0x003D, 0x00A0, 0x0000, + 0x0200, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, + 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x0202, 0x020B, + 0x00C0, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, + 0x0058, 0x0059, 0x005A, 0x00A1, 0xFFFF, 0x00A2, 0xFFFF, 0x007F, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0206, 0xFFFF, 0xFFFF, 0x0000, 0xFFFF, 0x0000, 0xFFFF, + 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000, 0x0000, 0x0000, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000 + }; + return mapping[static_cast(c)]; +} + +std::int32_t InputOutput::GetJavaKeyCode(std::int32_t native_key_code) const noexcept +{ + //Convert WinAPI VK-Codes to Java VK-Codes + //0x00 and 0xFF are not type-able characters.. + static const std::uint32_t windows_mapping[256] = { + 0x0000, 0x0000, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0008, 0x0009, 0x0000, 0x0000, 0x000C, 0x000A, 0x0000, 0x0000, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0000, 0x0000, + 0x0018, 0x0019, 0x0000, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, + 0x0028, 0x0000, 0x0000, 0x0000, 0x009A, 0x009B, 0x007F, 0x009C, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, + 0x0058, 0x0059, 0x005A, 0x020C, 0x020C, 0x020D, 0x0000, 0x0000, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, + 0x0078, 0x0079, 0x007A, 0x007B, 0xF000, 0xF001, 0xF002, 0xF003, + 0xF004, 0xF005, 0xF006, 0xF007, 0xF008, 0xF009, 0xF00A, 0xF00B, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0090, 0x0091, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0010, 0x0010, 0x0011, 0x0011, 0x0012, 0x0012, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x003B, 0x0209, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0083, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x005B, 0x005C, 0x005D, 0x00DE, 0x0205, + 0x0000, 0x0000, 0x0081, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x000C, 0x0000 + }; + return windows_mapping[native_key_code]; +} + +std::int32_t InputOutput::GetKeyLocation(std::int32_t keycode) const noexcept +{ + if (std::find(std::begin(control_keys), std::end(control_keys), keycode) != std::end(control_keys)) + { + return control_keys_locations[keycode]; + } + return java::KeyEvent::KeyCodes::KEY_LOCATION_STANDARD; +} + +std::int32_t InputOutput::GetActiveKeyModifiers() const noexcept +{ + std::int32_t modifiers = 0; + + //Key extended masks + if (this->any_key_held({VK_SHIFT, VK_LSHIFT, VK_RSHIFT})) + { + modifiers |= java::InputEvent::InputEventMasks::SHIFT_DOWN_MASK; + } + + if (this->any_key_held({VK_CONTROL, VK_LEFT_CONTROL, VK_RIGHT_CONTROL})) + { + modifiers |= java::InputEvent::InputEventMasks::CTRL_DOWN_MASK; + } + + if (this->is_key_held(VK_LEFT_ALT)) + { + modifiers |= java::InputEvent::InputEventMasks::ALT_DOWN_MASK; + } + + if (this->is_key_held(VK_RIGHT_ALT)) + { + modifiers |= java::InputEvent::InputEventMasks::ALT_GRAPH_DOWN_MASK; + } + + if (this->any_key_held({VK_LEFT_WINDOWS, VK_RIGHT_WINDOWS, VK_COMMAND})) + { + modifiers |= java::InputEvent::InputEventMasks::META_DOWN_MASK; + } + return modifiers; +} + +std::int32_t InputOutput::ModifiersForChar(char c) const noexcept +{ + std::int32_t modifiers = 0; + static const std::string shift_chars = "~!@#$%^&*()_+QWERTYUIOP{}|ASDFGHJKL:\"ZXCVBNM<>?"; + if (shift_chars.find(c) != std::string::npos) + { + modifiers |= java::InputEvent::InputEventMasks::SHIFT_DOWN_MASK; + } + return modifiers; +} + +std::int32_t InputOutput::SimbaMouseButtonToJava(std::int32_t button) const noexcept +{ + //Java Button priority is 1 (left), 3 (right), 2 (middle) + //Simba Button priority is 1 (left), 0 (right), 2 (middle) + return button == 1 ? 1 : button == 0 ? 3 : button == 2 ? 2 : button; +} + +void InputOutput::get_applet_dimensions(std::int32_t &x, std::int32_t &y, std::size_t &width, std::size_t &height) const noexcept +{ + JNIEnv* env = nullptr; + if (this->vm->AttachCurrentThreadAsDaemon(reinterpret_cast(&env), nullptr) == JNI_OK) + { + java::Applet receiver{env, this->applet, false}; + //Component receiver = applet.getComponent(0); + + receiver.getLocation(x, y); + receiver.getSize(width, height); + } + else + { + x = -1; + y = -1; + width = static_cast(this->w); + height = static_cast(this->h); + } +} + +void InputOutput::get_canvas_dimensions(std::int32_t &x, std::int32_t &y, std::size_t &width, std::size_t &height) const noexcept +{ + JNIEnv* env = nullptr; + if (this->vm->AttachCurrentThreadAsDaemon(reinterpret_cast(&env), nullptr) == JNI_OK) + { + java::Applet receiver{env, this->applet, false}; + java::Component canvas = receiver.getComponent(0); + + canvas.getLocation(x, y); + canvas.getSize(width, height); + } + else + { + x = -1; + y = -1; + width = static_cast(this->w); + height = static_cast(this->h); + } +} + +void InputOutput::get_applet_mouse_position(std::int32_t &x, std::int32_t &y) const noexcept +{ + x = this->x; + y = this->y; +} diff --git a/third-party/RemoteInput/RemoteInput/Plugin/InputOutput.hxx b/third-party/RemoteInput/RemoteInput/Plugin/InputOutput.hxx new file mode 100644 index 0000000..dd19bfc --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Plugin/InputOutput.hxx @@ -0,0 +1,101 @@ +// +// InputOutput.hxx +// RemoteInput +// +// Created by Brandon on 2019-12-28. +// Copyright © 2019 XIO. All rights reserved. +// + +#ifndef INPUTOUTPUT_HXX_INCLUDED +#define INPUTOUTPUT_HXX_INCLUDED + +#include +#include +#include +#include +#include +#include "DetachedThreadPool.hxx" +#include "ThreadPool.hxx" +#include "Reflection.hxx" +#include "Component.hxx" +#include "RIEventQueue.hxx" +#include "JVM.hxx" + +class InputOutput final +{ +private: + JavaVM* vm; + jobject applet; + std::mutex mutex; + std::unique_ptr input_thread; + std::unique_ptr event_queue; + + // MARK: - Input Variables + std::atomic currently_held_key; + std::list held_keys; + std::int32_t x; + std::int32_t y; + std::size_t w; + std::size_t h; + std::int32_t click_count; + std::size_t keyboard_speed; + std::size_t keyboard_repeat_delay; + bool mouse_buttons[3]; + + // MARK: - Functions + std::int32_t CharToJavaKeyCode(char c) const noexcept; + jchar NativeKeyCodeToChar(std::int32_t keycode, std::int32_t modifiers) const noexcept; + std::int32_t GetJavaKeyCode(std::int32_t native_key_code) const noexcept; + std::int32_t GetKeyLocation(std::int32_t keycode) const noexcept; + std::int32_t GetActiveKeyModifiers() const noexcept; + std::int32_t ModifiersForChar(char c) const noexcept; + std::int32_t SimbaMouseButtonToJava(std::int32_t button) const noexcept; + + bool has_focus(java::Component* component) const noexcept; + void gain_focus(java::Component* component) const noexcept; + void lose_focus(java::Component* component) const noexcept; + + bool any_key_held(std::array&& keys) const noexcept; + void handle_resize(java::Component* component) noexcept; + +public: + explicit InputOutput(Reflection* reflection) noexcept; + ~InputOutput() noexcept; + + bool has_focus() const noexcept; + void gain_focus() const noexcept; + void lose_focus() const noexcept; + bool is_keyboard_input_enabled() const noexcept; + void set_keyboard_input_enabled(bool enabled) const noexcept; + bool is_mouse_input_enabled() const noexcept; + void set_mouse_input_enabled(bool enabled) const noexcept; + + std::int32_t get_keyboard_speed() const noexcept; + void set_keyboard_speed(std::int32_t speed) noexcept; + std::int32_t get_keyboard_repeat_delay() const noexcept; + void set_keyboard_repeat_delay(std::int32_t delay) noexcept; + + void hold_key(std::int32_t code) noexcept; + void release_key(std::int32_t code) noexcept; + bool is_key_held(std::int32_t code) const noexcept; + void send_string(std::string string, std::int32_t keywait, std::int32_t keymodwait) const noexcept; + void send_key(char key, std::int32_t key_down_time, std::int32_t key_up_time, std::int32_t modifier_down_time, std::int32_t modifier_up_time) const noexcept; + void key_send(std::string string, std::vector sleeptimes) const noexcept; + + void get_mouse_position(std::int32_t* x, std::int32_t* y) noexcept; + void get_real_mouse_position(std::int32_t* x, std::int32_t* y) const noexcept; + void move_mouse(std::int32_t x, std::int32_t y) noexcept; + void hold_mouse(std::int32_t button) noexcept; + void release_mouse(std::int32_t button) noexcept; + void scroll_mouse(std::int32_t lines) noexcept; + bool is_mouse_held(std::int32_t button) const noexcept; + void stop_all_processing() noexcept; + +private: + friend class ControlCenter; + void get_applet_dimensions(std::int32_t &x, std::int32_t &y, std::size_t &width, std::size_t &height) const noexcept; + void get_canvas_dimensions(std::int32_t &x, std::int32_t &y, std::size_t &width, std::size_t &height) const noexcept; + void get_applet_mouse_position(std::int32_t &x, std::int32_t &y) const noexcept; +}; + +#endif /* INPUTOUTPUT_HXX_INCLUDED */ diff --git a/third-party/RemoteInput/RemoteInput/Plugin/JVM/JVMCache.cxx b/third-party/RemoteInput/RemoteInput/Plugin/JVM/JVMCache.cxx new file mode 100644 index 0000000..35c58ef --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Plugin/JVM/JVMCache.cxx @@ -0,0 +1,134 @@ +// +// Created by Brandon on 2023-03-25. +// + +#include "JVMCache.hxx" + +JVMCache::JVMCache(JNIEnv* env, jobject class_loader) : class_loader(class_loader), load_class_method(nullptr), class_cache(), field_cache() +{ + if (class_loader) + { + jclass class_loader_class = env->GetObjectClass(class_loader); + if (class_loader_class) + { + load_class_method = env->GetMethodID(class_loader_class, "loadClass", "(Ljava/lang/String;Z)Ljava/lang/Class;"); + env->DeleteLocalRef(class_loader_class); + } + } +} + +JVMCache::~JVMCache() +{ + clear(); +} + +JVMCache::JVMCache(JVMCache&& other) : class_loader(other.class_loader), load_class_method(other.load_class_method), class_cache(std::move(other.class_cache)), field_cache(std::move(other.field_cache)) +{ + other.class_loader = nullptr; + other.load_class_method = nullptr; + other.class_cache.clear(); + other.field_cache.clear(); +} + +JVMCache& JVMCache::operator = (JVMCache&& other) +{ + class_loader = other.class_loader; + load_class_method = other.load_class_method; + class_cache = std::move(other.class_cache); + field_cache = std::move(other.field_cache); + + other.class_loader = nullptr; + other.load_class_method = nullptr; + other.class_cache.clear(); + other.field_cache.clear(); + return *this; +} + +jclass JVMCache::GetClass(JNIEnv* env, std::string_view name) noexcept +{ + if (auto it = class_cache.find(name); it != class_cache.end()) + { + return it->second.get(); + } + + jstring class_name = env->NewStringUTF(name.data()); + if (class_name) + { + jclass clazz = static_cast(env->CallObjectMethod(class_loader, load_class_method, class_name, true)); + env->DeleteLocalRef(class_name); + + if (clazz) + { + using value_type = decltype(class_cache)::value_type::second_type; + + env->DeleteLocalRef(std::exchange(clazz, static_cast(env->NewGlobalRef(clazz)))); + class_cache.emplace(name, value_type(clazz, [env](jclass cls) { + //env->DeleteGlobalRef(cls); + })); + return clazz; + } + } + + return nullptr; +} + +jfieldID JVMCache::GetFieldID(JNIEnv* env, jclass clazz, std::string_view name, std::string_view sig, bool is_static) noexcept +{ + std::size_t key = field_hash(clazz, name, sig); + if (auto it = field_cache.find(key); it != field_cache.end()) + { + return it->second; + } + + jfieldID field = is_static ? env->GetStaticFieldID(clazz, name.data(), sig.data()) : env->GetFieldID(clazz, name.data(), sig.data()); + if (field) + { + field_cache[key] = field; + return field; + } + return nullptr; +} + +void JVMCache::clear() +{ + field_cache.clear(); + + for (auto &pair : class_cache) + { + pair.second.release(); + } +} + +std::size_t JVMCache::field_hash(jclass clazz, std::string_view field_name, std::string_view signature) noexcept +{ + std::size_t hash = 0; + hash_combine(hash, clazz); + hash_combine(hash, field_name); + hash_combine(hash, signature); + return hash; +} + +/*std::size_t JVMCache::field_hash(const char* field_name, const char* signature) noexcept +{ + std::size_t field_hash = std::hash()(field_name); + std::size_t desc_hash = std::hash()(signature); + + std::size_t hashes[] = {field_hash, desc_hash}; + + std::size_t hash_one = (5381 << 16) + 5381; + std::size_t hash_two = hash_one; + + for(std::size_t i = 0; i < sizeof(hashes) / sizeof(std::size_t); ++i) + { + if (i % 2 == 0) + { + hash_one = ((hash_one << 5) + hash_one + (hash_one >> 27)) ^ hashes[i]; + } + else + { + hash_two = ((hash_two << 5) + hash_two + (hash_two >> 27)) ^ hashes[i]; + } + } + + return hash_one + (hash_two * 1566083941); +}*/ \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Plugin/JVM/JVMCache.hxx b/third-party/RemoteInput/RemoteInput/Plugin/JVM/JVMCache.hxx new file mode 100644 index 0000000..af175d1 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Plugin/JVM/JVMCache.hxx @@ -0,0 +1,45 @@ +// +// Created by Brandon on 2023-03-25. +// + +#ifndef REMOTEINPUT_JVMCACHE_HXX +#define REMOTEINPUT_JVMCACHE_HXX + +#include +#include +#include +#include +#include +#include "ReflectionHook.hxx" +#include "JVM.hxx" +#include "TypeTraits.hxx" + +class JVMCache +{ +public: + JVMCache(JNIEnv* env, jobject class_loader); + JVMCache(const JVMCache&) = delete; + JVMCache(JVMCache&&); + ~JVMCache(); + + JVMCache& operator = (const JVMCache&) = delete; + JVMCache& operator = (JVMCache&&); + + jclass GetClass(JNIEnv* env, std::string_view name) noexcept; + jfieldID GetFieldID(JNIEnv* env, jclass clazz, std::string_view name, std::string_view sig, bool is_static) noexcept; + + void clear(); + +private: + jobject class_loader; + jmethodID load_class_method; + + std::unordered_map::type, std::function>, string_hash, std::equal_to<>> class_cache; + std::unordered_map field_cache; + + + static std::size_t field_hash(jclass clazz, std::string_view field_name, std::string_view signature) noexcept; +}; + + +#endif //REMOTEINPUT_JVMCACHE_HXX diff --git a/third-party/RemoteInput/RemoteInput/Plugin/JVM/RemoteVM.cxx b/third-party/RemoteInput/RemoteInput/Plugin/JVM/RemoteVM.cxx new file mode 100644 index 0000000..3b076ac --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Plugin/JVM/RemoteVM.cxx @@ -0,0 +1,2805 @@ +// +// Created by Brandon on 2020-12-08. +// + +#include "RemoteVM.hxx" +#include "EIOS.hxx" + +enum class RemoteVMCommand: std::uint32_t +{ + ALLOCATE_MEMORY, + READ_MEMORY, + WRITE_MEMORY, + FREE_MEMORY, + + GET_VERSION, + DEFINE_CLASS, + FIND_CLASS, + FROM_REFLECTED_METHOD, + FROM_REFLECTED_FIELD, + TO_REFLECTED_METHOD, + GET_SUPER_CLASS, + IS_ASSIGNABLE_FROM, + TO_REFLECTED_FIELD, + THROW, + THROW_NEW, + GET_EXCEPTION_MESSAGE, + FATAL_ERROR, + DELETE_GLOBAL_REF, + IS_SAME_OBJECT, + ALLOC_OBJECT, + NEW_OBJECT, + GET_OBJECT_CLASS, + IS_INSTANCE_OF, + GET_METHOD_ID, + CALL_OBJECT_METHOD, + CALL_BOOLEAN_METHOD, + CALL_BYTE_METHOD, + CALL_CHAR_METHOD, + CALL_SHORT_METHOD, + CALL_INT_METHOD, + CALL_LONG_METHOD, + CALL_FLOAT_METHOD, + CALL_DOUBLE_METHOD, + CALL_VOID_METHOD, + CALL_NON_VIRTUAL_OBJECT_METHOD, + CALL_NON_VIRTUAL_BOOLEAN_METHOD, + CALL_NON_VIRTUAL_BYTE_METHOD, + CALL_NON_VIRTUAL_CHAR_METHOD, + CALL_NON_VIRTUAL_SHORT_METHOD, + CALL_NON_VIRTUAL_INT_METHOD, + CALL_NON_VIRTUAL_LONG_METHOD, + CALL_NON_VIRTUAL_FLOAT_METHOD, + CALL_NON_VIRTUAL_DOUBLE_METHOD, + CALL_NON_VIRTUAL_VOID_METHOD, + GET_FIELD_ID, + GET_OBJECT_FIELD, + GET_BOOLEAN_FIELD, + GET_BYTE_FIELD, + GET_CHAR_FIELD, + GET_SHORT_FIELD, + GET_INT_FIELD, + GET_LONG_FIELD, + GET_FLOAT_FIELD, + GET_DOUBLE_FIELD, + SET_OBJECT_FIELD, + SET_BOOLEAN_FIELD, + SET_BYTE_FIELD, + SET_CHAR_FIELD, + SET_SHORT_FIELD, + SET_INT_FIELD, + SET_LONG_FIELD, + SET_FLOAT_FIELD, + SET_DOUBLE_FIELD, + GET_STATIC_METHOD_ID, + CALL_STATIC_OBJECT_METHOD, + CALL_STATIC_BOOLEAN_METHOD, + CALL_STATIC_BYTE_METHOD, + CALL_STATIC_CHAR_METHOD, + CALL_STATIC_SHORT_METHOD, + CALL_STATIC_INT_METHOD, + CALL_STATIC_LONG_METHOD, + CALL_STATIC_FLOAT_METHOD, + CALL_STATIC_DOUBLE_METHOD, + CALL_STATIC_VOID_METHOD, + GET_STATIC_FIELD_ID, + GET_STATIC_OBJECT_FIELD, + GET_STATIC_BOOLEAN_FIELD, + GET_STATIC_BYTE_FIELD, + GET_STATIC_CHAR_FIELD, + GET_STATIC_SHORT_FIELD, + GET_STATIC_INT_FIELD, + GET_STATIC_LONG_FIELD, + GET_STATIC_FLOAT_FIELD, + GET_STATIC_DOUBLE_FIELD, + SET_STATIC_OBJECT_FIELD, + SET_STATIC_BOOLEAN_FIELD, + SET_STATIC_BYTE_FIELD, + SET_STATIC_CHAR_FIELD, + SET_STATIC_SHORT_FIELD, + SET_STATIC_INT_FIELD, + SET_STATIC_LONG_FIELD, + SET_STATIC_FLOAT_FIELD, + SET_STATIC_DOUBLE_FIELD, + NEW_STRING, + GET_STRING_LENGTH, + GET_STRING_CHARS, + NEW_STRING_UTF, + GET_STRING_UTF_LENGTH, + GET_STRING_UTF_CHARS, + GET_ARRAY_LENGTH, + NEW_OBJECT_ARRAY, + GET_OBJECT_ARRAY_ELEMENTS, + SET_OBJECT_ARRAY_ELEMENTS, + NEW_BOOLEAN_ARRAY, + NEW_BYTE_ARRAY, + NEW_CHAR_ARRAY, + NEW_SHORT_ARRAY, + NEW_INT_ARRAY, + NEW_LONG_ARRAY, + NEW_FLOAT_ARRAY, + NEW_DOUBLE_ARRAY, + GET_BOOLEAN_ARRAY_ELEMENTS, + GET_BYTE_ARRAY_ELEMENTS, + GET_CHAR_ARRAY_ELEMENTS, + GET_SHORT_ARRAY_ELEMENTS, + GET_INT_ARRAY_ELEMENTS, + GET_LONG_ARRAY_ELEMENTS, + GET_FLOAT_ARRAY_ELEMENTS, + GET_DOUBLE_ARRAY_ELEMENTS, + GET_BOOLEAN_ARRAY_REGION, + GET_BYTE_ARRAY_REGION, + GET_CHAR_ARRAY_REGION, + GET_SHORT_ARRAY_REGION, + GET_INT_ARRAY_REGION, + GET_LONG_ARRAY_REGION, + GET_FLOAT_ARRAY_REGION, + GET_DOUBLE_ARRAY_REGION, + SET_BOOLEAN_ARRAY_REGION, + SET_BYTE_ARRAY_REGION, + SET_CHAR_ARRAY_REGION, + SET_SHORT_ARRAY_REGION, + SET_INT_ARRAY_REGION, + SET_LONG_ARRAY_REGION, + SET_FLOAT_ARRAY_REGION, + SET_DOUBLE_ARRAY_REGION, + MONITOR_ENTER, + MONITOR_EXIT, + GET_JAVA_VM, + NEW_DIRECT_BYTE_BUFFER, + GET_DIRECT_BUFFER_ADDRESS, + GET_DIRECT_BUFFER_CAPACITY, + GET_OBJECT_REF_TYPE +}; + +// Overloads for Stream to support jvalue + +Stream& operator << (Stream& stream, const jvalue &value) noexcept +{ + stream.write(&value, sizeof(jvalue)); + return stream; +} + +Stream& operator >> (Stream& stream, jvalue &value) noexcept +{ + stream.read(&value, sizeof(jvalue)); + return stream; +} + +// Helpers for reading + +template +auto Remote_VM_Read(ImageData* image_data) +{ + if constexpr (sizeof...(Args) == 1) + { + return image_data->data_stream().read...>>>(); + } + else + { + return std::tuple...>{image_data->data_stream().read>()...}; + } +} + +// Helpers for writing + +template +void Remote_VM_Write(Stream& stream, RemoteVMCommand command, Args&&... args) +{ + stream << command; + (stream.write(std::forward(args)), ...); +} + +template +T RemoteVM::local_to_global(T object) const noexcept +{ + if (object) + { + T result = static_cast(env->NewGlobalRef(object)); + env->DeleteLocalRef(object); + return result; + } + return nullptr; +} + +template +R RemoteVM::SendCommand(RemoteVMCommand command, Args&&... args) const noexcept +{ + auto result = send_command([&](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::REMOTE_VM_INSTRUCTION); + Remote_VM_Write(stream, command, std::forward(args)...); + }); + + if constexpr (!std::is_void_v) + { + if (result) + { + ImageData* image_data = (control_center->*get_image_data)(); + return Remote_VM_Read(image_data); + } + } + return R(); +} + +template +R RemoteVM::ExecuteCommand(ImageData* image_data, R (RemoteVM::*func)(Args...) const noexcept) const noexcept +{ + auto args = Remote_VM_Read(image_data); + + if constexpr (sizeof...(Args) == 1) + { + return std::invoke(func, this, std::move(args)); + } + else + { + return std::apply(func, std::tuple_cat(std::make_tuple(this), std::move(args))); + } +} + +RemoteVM::RemoteVM(JNIEnv* env, + ControlCenter* control_center) noexcept : env(env), control_center(control_center) {} + +RemoteVM::RemoteVM(JNIEnv* env, + ControlCenter* control_center, + std::function)>&& send_command, + ImageData* (ControlCenter::*get_image_data)() const) noexcept : env(env), control_center(control_center), send_command(send_command), get_image_data(get_image_data) {} +RemoteVM::~RemoteVM() = default; + +RemoteVM::RemoteVM(RemoteVM&& other) noexcept : env(other.env), control_center(other.control_center), send_command(other.send_command), get_image_data(other.get_image_data) +{ + other.env = nullptr; + other.control_center = nullptr; + other.send_command = nullptr; + other.get_image_data = nullptr; +} + +RemoteVM& RemoteVM::operator = (RemoteVM&& other) noexcept +{ + env = other.env; + control_center = other.control_center; + send_command = other.send_command; + get_image_data = other.get_image_data; + other.env = nullptr; + other.control_center = nullptr; + other.send_command = nullptr; + other.get_image_data = nullptr; + return *this; +} + +std::size_t RemoteVM::MaxMemoryChunkSize() const noexcept +{ + std::size_t max_size = sizeof(EIOSData::data); + max_size -= sizeof(std::underlying_type); + max_size -= sizeof(void*); + max_size -= sizeof(std::size_t); + return max_size; +} + +void* RemoteVM::AllocateMemory(std::size_t size) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::ALLOCATE_MEMORY, size); + } + return malloc(size); +} + +bool RemoteVM::ReadMemory(void* destination, void* source, std::size_t size) const noexcept +{ + if (this->send_command) + { + auto result = send_command([&](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::REMOTE_VM_INSTRUCTION); + image_data->data_stream().write(RemoteVMCommand::READ_MEMORY); + image_data->data_stream().write(source); + image_data->data_stream().write(size); + }); + + if (result) + { + (control_center->*get_image_data)()->data_stream().read(destination, size); + return true; + } + return false; + } + + std::memcpy(destination, source, size); + return true; +} + +bool RemoteVM::WriteMemory(void* destination, void* source, std::size_t size) const noexcept +{ + if (this->send_command) + { + auto result = send_command([&](Stream &stream, ImageData* image_data) { + image_data->set_command(EIOSCommand::REMOTE_VM_INSTRUCTION); + image_data->data_stream().write(RemoteVMCommand::WRITE_MEMORY); + image_data->data_stream().write(destination); + image_data->data_stream().write(size); + image_data->data_stream().write(source, size); + }); + + return result; + } + + std::memcpy(destination, source, size); + return true; +} + +void RemoteVM::FreeMemory(void* memory) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::FREE_MEMORY, memory); + } + + free(memory); +} + +jint RemoteVM::GetVersion() const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::GET_VERSION); + } + + return env->GetVersion(); +} + +jclass RemoteVM::DefineClass(const std::string &name, jobject loader, const jbyte* buf, jsize len) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::DEFINE_CLASS, name, loader, buf, len); + } + + return local_to_global(env->DefineClass(name.c_str(), loader, buf, len)); +} + +jclass RemoteVM::FindClass(const std::string &name) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::FIND_CLASS, name); + } + return local_to_global(env->FindClass(name.c_str())); +} + +jmethodID RemoteVM::FromReflectedMethod(jobject method) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::FROM_REFLECTED_METHOD, method); + } + return env->FromReflectedMethod(method); +} + +jfieldID RemoteVM::FromReflectedField(jobject field) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::FROM_REFLECTED_FIELD, field); + } + return env->FromReflectedField(field); +} + +jobject RemoteVM::ToReflectedMethod(jclass cls, jmethodID methodID, jboolean isStatic) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::TO_REFLECTED_METHOD, cls, methodID, isStatic); + } + return local_to_global(env->ToReflectedMethod(cls, methodID, isStatic)); +} + +jclass RemoteVM::GetSuperclass(jclass sub) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::GET_SUPER_CLASS, sub); + } + return local_to_global(env->GetSuperclass(sub)); +} + +jboolean RemoteVM::IsAssignableFrom(jclass sub, jclass sup) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::IS_ASSIGNABLE_FROM, sub, sup); + } + return env->IsAssignableFrom(sub, sup); +} + +jobject RemoteVM::ToReflectedField(jclass cls, jfieldID fieldID, jboolean isStatic) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::TO_REFLECTED_FIELD, cls, fieldID, isStatic); + } + return local_to_global(env->ToReflectedField(cls, fieldID, isStatic)); +} + +jint RemoteVM::Throw(jthrowable obj) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::THROW, obj); + } + return env->Throw(obj); +} + +jint RemoteVM::ThrowNew(jclass clazz, const std::string &msg) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::THROW_NEW, clazz, std::string(msg)); + } + return env->ThrowNew(clazz, msg.c_str()); +} + +std::string RemoteVM::GetExceptionMessage() const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::GET_EXCEPTION_MESSAGE); + } + + std::string result = std::string(); + if (env->ExceptionCheck()) + { + jthrowable throwable = env->ExceptionOccurred(); + if (throwable) + { + env->ExceptionClear(); + jclass clazz = env->GetObjectClass(throwable); + if (clazz) + { + jmethodID getMessage = env->GetMethodID(clazz, "getMessage", "()Ljava/lang/String;"); + jstring message = static_cast(env->CallObjectMethod(throwable, getMessage)); + if (message) + { + jsize length = env->GetStringLength(message); + if (length > 0) + { + const char* str = env->GetStringUTFChars(message, nullptr); + result = std::string(str, length); + env->ReleaseStringUTFChars(message, str); + } + env->DeleteLocalRef(message); + } + env->DeleteLocalRef(clazz); + } + env->DeleteLocalRef(throwable); + } + } + return result; +} + +void RemoteVM::FatalError(const std::string &msg) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::FATAL_ERROR, msg); + } + return env->FatalError(msg.c_str()); +} + +void RemoteVM::DeleteGlobalRef(jobject gref) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::DELETE_GLOBAL_REF, gref); + } + return env->DeleteGlobalRef(gref); +} + +jboolean RemoteVM::IsSameObject(jobject obj1, jobject obj2) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::IS_SAME_OBJECT, obj1, obj2); + } + return env->IsSameObject(obj1, obj2); +} + +jobject RemoteVM::AllocObject(jclass clazz) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::ALLOC_OBJECT, clazz); + } + return local_to_global(env->AllocObject(clazz)); +} + +jobject RemoteVM::NewObject(jclass clazz, jmethodID methodID, const std::vector &args) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::NEW_OBJECT, clazz, methodID, args); + } + return local_to_global(env->NewObjectA(clazz, methodID, args.data())); +} + +jclass RemoteVM::GetObjectClass(jobject obj) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::GET_OBJECT_CLASS, obj); + } + return local_to_global(env->GetObjectClass(obj)); +} + +jboolean RemoteVM::IsInstanceOf(jobject obj, jclass clazz) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::IS_INSTANCE_OF, obj, clazz); + } + return env->IsInstanceOf(obj, clazz); +} + +jmethodID RemoteVM::GetMethodID(jclass clazz, const std::string &name, const std::string &sig) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::GET_METHOD_ID, clazz, name, sig); + } + return env->GetMethodID(clazz, name.c_str(), sig.c_str()); +} + +jobject RemoteVM::CallObjectMethod(jobject obj, jmethodID methodID, const std::vector &args) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::CALL_OBJECT_METHOD, obj, methodID, args); + } + return local_to_global(env->CallObjectMethodA(obj, methodID, args.data())); +} + +jboolean RemoteVM::CallBooleanMethod(jobject obj, jmethodID methodID, const std::vector &args) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::CALL_BOOLEAN_METHOD, obj, methodID, args); + } + return env->CallBooleanMethodA(obj, methodID, args.data()); +} + +jbyte RemoteVM::CallByteMethod(jobject obj, jmethodID methodID, const std::vector &args) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::CALL_BYTE_METHOD, obj, methodID, args); + } + return env->CallByteMethodA(obj, methodID, args.data()); +} + +jchar RemoteVM::CallCharMethod(jobject obj, jmethodID methodID, const std::vector &args) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::CALL_CHAR_METHOD, obj, methodID, args); + } + return env->CallCharMethodA(obj, methodID, args.data()); +} + +jshort RemoteVM::CallShortMethod(jobject obj, jmethodID methodID, const std::vector &args) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::CALL_SHORT_METHOD, obj, methodID, args); + } + return env->CallShortMethodA(obj, methodID, args.data()); +} + +jint RemoteVM::CallIntMethod(jobject obj, jmethodID methodID, const std::vector &args) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::CALL_INT_METHOD, obj, methodID, args); + } + return env->CallIntMethodA(obj, methodID, args.data()); +} + +jlong RemoteVM::CallLongMethod(jobject obj, jmethodID methodID, const std::vector &args) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::CALL_LONG_METHOD, obj, methodID, args); + } + return env->CallLongMethodA(obj, methodID, args.data()); +} + +jfloat RemoteVM::CallFloatMethod(jobject obj, jmethodID methodID, const std::vector &args) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::CALL_FLOAT_METHOD, obj, methodID, args); + } + return env->CallFloatMethodA(obj, methodID, args.data()); +} + +jdouble RemoteVM::CallDoubleMethod(jobject obj, jmethodID methodID, const std::vector &args) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::CALL_DOUBLE_METHOD, obj, methodID, args); + } + return env->CallDoubleMethodA(obj, methodID, args.data()); +} + +void RemoteVM::CallVoidMethod(jobject obj, jmethodID methodID, const std::vector &args) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::CALL_VOID_METHOD, obj, methodID, args); + } + return env->CallVoidMethodA(obj, methodID, args.data()); +} + +jobject RemoteVM::CallNonvirtualObjectMethod(jobject obj, jclass clazz, jmethodID methodID, const std::vector &args) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::CALL_NON_VIRTUAL_OBJECT_METHOD, obj, clazz, methodID, args); + } + return local_to_global(env->CallNonvirtualObjectMethodA(obj, clazz, methodID, args.data())); +} + +jboolean RemoteVM::CallNonvirtualBooleanMethod(jobject obj, jclass clazz, jmethodID methodID, const std::vector &args) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::CALL_NON_VIRTUAL_BOOLEAN_METHOD, obj, clazz, methodID, args); + } + return env->CallNonvirtualBooleanMethodA(obj, clazz, methodID, args.data()); +} + +jbyte RemoteVM::CallNonvirtualByteMethod(jobject obj, jclass clazz, jmethodID methodID, const std::vector &args) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::CALL_NON_VIRTUAL_BYTE_METHOD, obj, clazz, methodID, args); + } + return env->CallNonvirtualByteMethodA(obj, clazz, methodID, args.data()); +} + +jchar RemoteVM::CallNonvirtualCharMethod(jobject obj, jclass clazz, jmethodID methodID, const std::vector &args) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::CALL_NON_VIRTUAL_CHAR_METHOD, obj, clazz, methodID, args); + } + return env->CallNonvirtualCharMethodA(obj, clazz, methodID, args.data()); +} + +jshort RemoteVM::CallNonvirtualShortMethod(jobject obj, jclass clazz, jmethodID methodID, const std::vector &args) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::CALL_NON_VIRTUAL_SHORT_METHOD, obj, clazz, methodID, args); + } + return env->CallNonvirtualShortMethodA(obj, clazz, methodID, args.data()); +} + +jint RemoteVM::CallNonvirtualIntMethod(jobject obj, jclass clazz, jmethodID methodID, const std::vector &args) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::CALL_NON_VIRTUAL_INT_METHOD, obj, clazz, methodID, args); + } + return env->CallNonvirtualIntMethodA(obj, clazz, methodID, args.data()); +} + +jlong RemoteVM::CallNonvirtualLongMethod(jobject obj, jclass clazz, jmethodID methodID, const std::vector &args) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::CALL_NON_VIRTUAL_LONG_METHOD, obj, clazz, methodID, args); + } + return env->CallNonvirtualLongMethodA(obj, clazz, methodID, args.data()); +} + +jfloat RemoteVM::CallNonvirtualFloatMethod(jobject obj, jclass clazz, jmethodID methodID, const std::vector &args) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::CALL_NON_VIRTUAL_FLOAT_METHOD, obj, clazz, methodID, args); + } + return env->CallNonvirtualFloatMethodA(obj, clazz, methodID, args.data()); +} + +jdouble RemoteVM::CallNonvirtualDoubleMethod(jobject obj, jclass clazz, jmethodID methodID, const std::vector &args) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::CALL_NON_VIRTUAL_DOUBLE_METHOD, obj, clazz, methodID, args); + } + return env->CallNonvirtualDoubleMethodA(obj, clazz, methodID, args.data()); +} + +void RemoteVM::CallNonvirtualVoidMethod(jobject obj, jclass clazz, jmethodID methodID, const std::vector &args) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::CALL_NON_VIRTUAL_VOID_METHOD, obj, clazz, methodID, args); + } + return env->CallNonvirtualVoidMethodA(obj, clazz, methodID, args.data()); +} + +jfieldID RemoteVM::GetFieldID(jclass clazz, const std::string &name, const std::string &sig) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::GET_FIELD_ID, clazz, name, sig); + } + return env->GetFieldID(clazz, name.c_str(), sig.c_str()); +} + +jobject RemoteVM::GetObjectField(jobject obj, jfieldID fieldID) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::GET_OBJECT_FIELD, obj, fieldID); + } + return local_to_global(env->GetObjectField(obj, fieldID)); +} + +jboolean RemoteVM::GetBooleanField(jobject obj, jfieldID fieldID) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::GET_BOOLEAN_FIELD, obj, fieldID); + } + return env->GetBooleanField(obj, fieldID); +} + +jbyte RemoteVM::GetByteField(jobject obj, jfieldID fieldID) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::GET_BYTE_FIELD, obj, fieldID); + } + return env->GetByteField(obj, fieldID); +} + +jchar RemoteVM::GetCharField(jobject obj, jfieldID fieldID) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::GET_CHAR_FIELD, obj, fieldID); + } + return env->GetCharField(obj, fieldID); +} + +jshort RemoteVM::GetShortField(jobject obj, jfieldID fieldID) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::GET_SHORT_FIELD, obj, fieldID); + } + return env->GetShortField(obj, fieldID); +} + +jint RemoteVM::GetIntField(jobject obj, jfieldID fieldID) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::GET_INT_FIELD, obj, fieldID); + } + return env->GetIntField(obj, fieldID); +} + +jlong RemoteVM::GetLongField(jobject obj, jfieldID fieldID) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::GET_LONG_FIELD, obj, fieldID); + } + return env->GetLongField(obj, fieldID); +} + +jfloat RemoteVM::GetFloatField(jobject obj, jfieldID fieldID) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::GET_FLOAT_FIELD, obj, fieldID); + } + return env->GetFloatField(obj, fieldID); +} + +jdouble RemoteVM::GetDoubleField(jobject obj, jfieldID fieldID) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::GET_DOUBLE_FIELD, obj, fieldID); + } + return env->GetDoubleField(obj, fieldID); +} + +void RemoteVM::SetObjectField(jobject obj, jfieldID fieldID, jobject val) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::SET_OBJECT_FIELD, obj, fieldID, val); + } + return env->SetObjectField(obj, fieldID, val); +} + +void RemoteVM::SetBooleanField(jobject obj, jfieldID fieldID, jboolean val) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::SET_BOOLEAN_FIELD, obj, fieldID, val); + } + return env->SetBooleanField(obj, fieldID, val); +} + +void RemoteVM::SetByteField(jobject obj, jfieldID fieldID, jbyte val) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::SET_BYTE_FIELD, obj, fieldID, val); + } + return env->SetByteField(obj, fieldID, val); +} + +void RemoteVM::SetCharField(jobject obj, jfieldID fieldID, jchar val) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::SET_CHAR_FIELD, obj, fieldID, val); + } + return env->SetCharField(obj, fieldID, val); +} + +void RemoteVM::SetShortField(jobject obj, jfieldID fieldID, jshort val) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::SET_SHORT_FIELD, obj, fieldID, val); + } + return env->SetShortField(obj, fieldID, val); +} + +void RemoteVM::SetIntField(jobject obj, jfieldID fieldID, jint val) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::SET_INT_FIELD, obj, fieldID, val); + } + return env->SetIntField(obj, fieldID, val); +} + +void RemoteVM::SetLongField(jobject obj, jfieldID fieldID, jlong val) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::SET_LONG_FIELD, obj, fieldID, val); + } + return env->SetLongField(obj, fieldID, val); +} + +void RemoteVM::SetFloatField(jobject obj, jfieldID fieldID, jfloat val) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::SET_FLOAT_FIELD, obj, fieldID, val); + } + return env->SetFloatField(obj, fieldID, val); +} + +void RemoteVM::SetDoubleField(jobject obj, jfieldID fieldID, jdouble val) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::SET_DOUBLE_FIELD, obj, fieldID, val); + } + return env->SetDoubleField(obj, fieldID, val); +} + +jmethodID RemoteVM::GetStaticMethodID(jclass clazz, const std::string &name, const std::string &sig) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::GET_STATIC_METHOD_ID, clazz, name, sig); + } + return env->GetStaticMethodID(clazz, name.c_str(), sig.c_str()); +} + +jobject RemoteVM::CallStaticObjectMethod(jclass clazz, jmethodID methodID, const std::vector &args) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::CALL_STATIC_OBJECT_METHOD, clazz, methodID, args); + } + return local_to_global(env->CallStaticObjectMethodA(clazz, methodID, args.data())); +} + +jboolean RemoteVM::CallStaticBooleanMethod(jclass clazz, jmethodID methodID, const std::vector &args) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::CALL_STATIC_BOOLEAN_METHOD, clazz, methodID, args); + } + return env->CallStaticBooleanMethodA(clazz, methodID, args.data()); +} + +jbyte RemoteVM::CallStaticByteMethod(jclass clazz, jmethodID methodID, const std::vector &args) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::CALL_STATIC_BYTE_METHOD, clazz, methodID, args); + } + return env->CallStaticByteMethodA(clazz, methodID, args.data()); +} + +jchar RemoteVM::CallStaticCharMethod(jclass clazz, jmethodID methodID, const std::vector &args) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::CALL_STATIC_CHAR_METHOD, clazz, methodID, args); + } + return env->CallStaticCharMethodA(clazz, methodID, args.data()); +} + +jshort RemoteVM::CallStaticShortMethod(jclass clazz, jmethodID methodID, const std::vector &args) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::CALL_STATIC_SHORT_METHOD, clazz, methodID, args); + } + return env->CallStaticShortMethodA(clazz, methodID, args.data()); +} + +jint RemoteVM::CallStaticIntMethod(jclass clazz, jmethodID methodID, const std::vector &args) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::CALL_STATIC_INT_METHOD, clazz, methodID, args); + } + + return env->CallStaticIntMethodA(clazz, methodID, args.data()); +} + +jlong RemoteVM::CallStaticLongMethod(jclass clazz, jmethodID methodID, const std::vector &args) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::CALL_STATIC_LONG_METHOD, clazz, methodID, args); + } + return env->CallStaticLongMethodA(clazz, methodID, args.data()); +} + +jfloat RemoteVM::CallStaticFloatMethod(jclass clazz, jmethodID methodID, const std::vector &args) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::CALL_STATIC_FLOAT_METHOD, clazz, methodID, args); + } + return env->CallStaticFloatMethodA(clazz, methodID, args.data()); +} + +jdouble RemoteVM::CallStaticDoubleMethod(jclass clazz, jmethodID methodID, const std::vector &args) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::CALL_STATIC_DOUBLE_METHOD, clazz, methodID, args); + } + return env->CallStaticDoubleMethodA(clazz, methodID, args.data()); +} + +void RemoteVM::CallStaticVoidMethod(jclass clazz, jmethodID methodID, const std::vector &args) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::CALL_STATIC_VOID_METHOD, clazz, methodID, args); + } + return env->CallStaticVoidMethodA(clazz, methodID, args.data()); +} + +jfieldID RemoteVM::GetStaticFieldID(jclass clazz, const std::string &name, const std::string &sig) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::GET_STATIC_FIELD_ID, clazz, name, sig); + } + + return env->GetStaticFieldID(clazz, name.c_str(), sig.c_str()); +} + +jobject RemoteVM::GetStaticObjectField(jclass clazz, jfieldID fieldID) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::GET_STATIC_OBJECT_FIELD, clazz, fieldID); + } + return local_to_global(env->GetStaticObjectField(clazz, fieldID)); +} + +jboolean RemoteVM::GetStaticBooleanField(jclass clazz, jfieldID fieldID) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::GET_STATIC_BOOLEAN_FIELD, clazz, fieldID); + } + return env->GetStaticBooleanField(clazz, fieldID); +} + +jbyte RemoteVM::GetStaticByteField(jclass clazz, jfieldID fieldID) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::GET_STATIC_BYTE_FIELD, clazz, fieldID); + } + return env->GetStaticByteField(clazz, fieldID); +} + +jchar RemoteVM::GetStaticCharField(jclass clazz, jfieldID fieldID) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::GET_STATIC_CHAR_FIELD, clazz, fieldID); + } + return env->GetStaticCharField(clazz, fieldID); +} + +jshort RemoteVM::GetStaticShortField(jclass clazz, jfieldID fieldID) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::GET_STATIC_SHORT_FIELD, clazz, fieldID); + } + return env->GetStaticShortField(clazz, fieldID); +} + +jint RemoteVM::GetStaticIntField(jclass clazz, jfieldID fieldID) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::GET_STATIC_INT_FIELD, clazz, fieldID); + } + return env->GetStaticIntField(clazz, fieldID); +} + +jlong RemoteVM::GetStaticLongField(jclass clazz, jfieldID fieldID) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::GET_STATIC_LONG_FIELD, clazz, fieldID); + } + return env->GetStaticLongField(clazz, fieldID); +} + +jfloat RemoteVM::GetStaticFloatField(jclass clazz, jfieldID fieldID) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::GET_STATIC_FLOAT_FIELD, clazz, fieldID); + } + return env->GetStaticFloatField(clazz, fieldID); +} + +jdouble RemoteVM::GetStaticDoubleField(jclass clazz, jfieldID fieldID) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::GET_STATIC_DOUBLE_FIELD, clazz, fieldID); + } + return env->GetStaticDoubleField(clazz, fieldID); +} + +void RemoteVM::SetStaticObjectField(jclass clazz, jfieldID fieldID, jobject value) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::SET_STATIC_OBJECT_FIELD, clazz, fieldID, value); + } + return env->SetStaticObjectField(clazz, fieldID, value); +} + +void RemoteVM::SetStaticBooleanField(jclass clazz, jfieldID fieldID, jboolean value) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::SET_STATIC_BOOLEAN_FIELD, clazz, fieldID, value); + } + return env->SetStaticBooleanField(clazz, fieldID, value); +} + +void RemoteVM::SetStaticByteField(jclass clazz, jfieldID fieldID, jbyte value) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::SET_STATIC_BYTE_FIELD, clazz, fieldID, value); + } + return env->SetStaticByteField(clazz, fieldID, value); +} + +void RemoteVM::SetStaticCharField(jclass clazz, jfieldID fieldID, jchar value) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::SET_STATIC_CHAR_FIELD, clazz, fieldID, value); + } + return env->SetStaticCharField(clazz, fieldID, value); +} + +void RemoteVM::SetStaticShortField(jclass clazz, jfieldID fieldID, jshort value) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::SET_STATIC_SHORT_FIELD, clazz, fieldID, value); + } + return env->SetStaticShortField(clazz, fieldID, value); +} + +void RemoteVM::SetStaticIntField(jclass clazz, jfieldID fieldID, jint value) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::SET_STATIC_INT_FIELD, clazz, fieldID, value); + } + return env->SetStaticIntField(clazz, fieldID, value); +} + +void RemoteVM::SetStaticLongField(jclass clazz, jfieldID fieldID, jlong value) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::SET_STATIC_LONG_FIELD, clazz, fieldID, value); + } + return env->SetStaticLongField(clazz, fieldID, value); +} + +void RemoteVM::SetStaticFloatField(jclass clazz, jfieldID fieldID, jfloat value) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::SET_STATIC_FLOAT_FIELD, clazz, fieldID, value); + } + return env->SetStaticFloatField(clazz, fieldID, value); +} + +void RemoteVM::SetStaticDoubleField(jclass clazz, jfieldID fieldID, jdouble value) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::SET_STATIC_DOUBLE_FIELD, clazz, fieldID, value); + } + return env->SetStaticDoubleField(clazz, fieldID, value); +} + +jstring RemoteVM::NewString(const std::wstring &unicode) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::NEW_STRING, unicode); + } + return local_to_global(env->NewString(reinterpret_cast(unicode.c_str()), static_cast(unicode.size()))); +} + +jsize RemoteVM::GetStringLength(jstring str) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::GET_STRING_LENGTH, str); + } + return env->GetStringLength(str); +} + +std::wstring RemoteVM::GetStringChars(jstring str) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::GET_STRING_CHARS, str); + } + + const jchar* chars = env->GetStringChars(str, nullptr); + if (chars) + { + jsize length = env->GetStringLength(str); + if (length > 0) + { + std::wstring result = std::wstring(reinterpret_cast(chars), length); + env->ReleaseStringChars(str, chars); + return result; + } + + env->ReleaseStringChars(str, chars); + } + return {}; +} + +jstring RemoteVM::NewStringUTF(const std::string &utf) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::NEW_STRING_UTF, utf); + } + return local_to_global(env->NewStringUTF(utf.c_str())); +} + +jsize RemoteVM::GetStringUTFLength(jstring str) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::GET_STRING_UTF_LENGTH, str); + } + return env->GetStringUTFLength(str); +} + +std::string RemoteVM::GetStringUTFChars(jstring str) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::GET_STRING_UTF_CHARS, str); + } + + const char* chars = env->GetStringUTFChars(str, nullptr); + if (chars) + { + jsize length = env->GetStringUTFLength(str); + if (length > 0) + { + std::string result = std::string(chars, length); + env->ReleaseStringUTFChars(str, chars); + return result; + } + + env->ReleaseStringUTFChars(str, chars); + } + return {}; +} + +jsize RemoteVM::GetArrayLength(jarray array) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::GET_ARRAY_LENGTH, array); + } + return env->GetArrayLength(array); +} + +jobjectArray RemoteVM::NewObjectArray(jsize len, jclass clazz, jobject init) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::NEW_OBJECT_ARRAY, len, clazz, init); + } + return local_to_global(env->NewObjectArray(len, clazz, init)); +} + +jbooleanArray RemoteVM::NewBooleanArray(jsize len) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::NEW_BOOLEAN_ARRAY, len); + } + return local_to_global(env->NewBooleanArray(len)); +} + +jbyteArray RemoteVM::NewByteArray(jsize len) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::NEW_BYTE_ARRAY, len); + } + return local_to_global(env->NewByteArray(len)); +} + +jcharArray RemoteVM::NewCharArray(jsize len) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::NEW_CHAR_ARRAY, len); + } + return local_to_global(env->NewCharArray(len)); +} + +jshortArray RemoteVM::NewShortArray(jsize len) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::NEW_SHORT_ARRAY, len); + } + return local_to_global(env->NewShortArray(len)); +} + +jintArray RemoteVM::NewIntArray(jsize len) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::NEW_INT_ARRAY, len); + } + return local_to_global(env->NewIntArray(len)); +} + +jlongArray RemoteVM::NewLongArray(jsize len) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::NEW_LONG_ARRAY, len); + } + return local_to_global(env->NewLongArray(len)); +} + +jfloatArray RemoteVM::NewFloatArray(jsize len) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::NEW_FLOAT_ARRAY, len); + } + return local_to_global(env->NewFloatArray(len)); +} + +jdoubleArray RemoteVM::NewDoubleArray(jsize len) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::NEW_DOUBLE_ARRAY, len); + } + return local_to_global(env->NewDoubleArray(len)); +} + +std::vector RemoteVM::GetObjectArrayElements(jobjectArray array) const noexcept +{ + if (this->send_command) + { + return SendCommand>(RemoteVMCommand::GET_OBJECT_ARRAY_ELEMENTS, array); + } + + std::vector result; + jsize length = env->GetArrayLength(array); + result.reserve(length); + for (jsize i = 0; i < length; ++i) + { + jobject local = env->GetObjectArrayElement(array, i); + result.push_back(local ? env->NewGlobalRef(local) : nullptr); + env->DeleteLocalRef(local); + } + return result; +} + +void RemoteVM::SetObjectArrayElements(jobjectArray array, jsize index, const std::vector &elements) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::SET_OBJECT_ARRAY_ELEMENTS, array, index, elements); + } + + for (std::size_t i = 0; i < elements.size(); ++i) + { + env->SetObjectArrayElement(array, static_cast(i) + index, elements[i]); + } +} + +std::vector RemoteVM::GetBooleanArrayElements(jbooleanArray array) const noexcept +{ + if (this->send_command) + { + return SendCommand>(RemoteVMCommand::GET_BOOLEAN_ARRAY_ELEMENTS, array); + } + + std::vector elements; + elements.resize(env->GetArrayLength(array)); + if (!elements.empty()) + { + jboolean* items = env->GetBooleanArrayElements(array, nullptr); + if (items) + { + std::memcpy(&elements[0], items, sizeof(jboolean) * elements.size()); + env->ReleaseBooleanArrayElements(array, items, JNI_ABORT); + return elements; + } + } + return {}; +} + +std::vector RemoteVM::GetByteArrayElements(jbyteArray array) const noexcept +{ + if (this->send_command) + { + return SendCommand>(RemoteVMCommand::GET_BYTE_ARRAY_ELEMENTS, array); + } + + std::vector elements; + elements.resize(env->GetArrayLength(array)); + if (!elements.empty()) + { + jbyte* items = env->GetByteArrayElements(array, nullptr); + if (items) + { + std::memcpy(&elements[0], items, sizeof(jbyte) * elements.size()); + env->ReleaseByteArrayElements(array, items, JNI_ABORT); + return elements; + } + } + return {}; +} + +std::vector RemoteVM::GetCharArrayElements(jcharArray array) const noexcept +{ + if (this->send_command) + { + return SendCommand>(RemoteVMCommand::GET_CHAR_ARRAY_ELEMENTS, array); + } + + std::vector elements; + elements.resize(env->GetArrayLength(array)); + if (!elements.empty()) + { + jchar* items = env->GetCharArrayElements(array, nullptr); + if (items) + { + std::memcpy(&elements[0], items, sizeof(jchar) * elements.size()); + env->ReleaseCharArrayElements(array, items, JNI_ABORT); + return elements; + } + } + return {}; +} + +std::vector RemoteVM::GetShortArrayElements(jshortArray array) const noexcept +{ + if (this->send_command) + { + return SendCommand>(RemoteVMCommand::GET_SHORT_ARRAY_ELEMENTS, array); + } + + std::vector elements; + elements.resize(env->GetArrayLength(array)); + if (!elements.empty()) + { + jshort* items = env->GetShortArrayElements(array, nullptr); + if (items) + { + std::memcpy(&elements[0], items, sizeof(jshort) * elements.size()); + env->ReleaseShortArrayElements(array, items, JNI_ABORT); + return elements; + } + } + return {}; +} + +std::vector RemoteVM::GetIntArrayElements(jintArray array) const noexcept +{ + if (this->send_command) + { + return SendCommand>(RemoteVMCommand::GET_INT_ARRAY_ELEMENTS, array); + } + + std::vector elements; + elements.resize(env->GetArrayLength(array)); + if (!elements.empty()) + { + jint* items = env->GetIntArrayElements(array, nullptr); + if (items) + { + std::memcpy(&elements[0], items, sizeof(jint) * elements.size()); + env->ReleaseIntArrayElements(array, items, JNI_ABORT); + return elements; + } + } + return {}; +} + +std::vector RemoteVM::GetLongArrayElements(jlongArray array) const noexcept +{ + if (this->send_command) + { + return SendCommand>(RemoteVMCommand::GET_LONG_ARRAY_ELEMENTS, array); + } + + std::vector elements; + elements.resize(env->GetArrayLength(array)); + if (!elements.empty()) + { + jlong* items = env->GetLongArrayElements(array, nullptr); + if (items) + { + std::memcpy(&elements[0], items, sizeof(jlong) * elements.size()); + env->ReleaseLongArrayElements(array, items, JNI_ABORT); + return elements; + } + } + return {}; +} + +std::vector RemoteVM::GetFloatArrayElements(jfloatArray array) const noexcept +{ + if (this->send_command) + { + return SendCommand>(RemoteVMCommand::GET_FLOAT_ARRAY_ELEMENTS, array); + } + + std::vector elements; + elements.resize(env->GetArrayLength(array)); + if (!elements.empty()) + { + jfloat* items = env->GetFloatArrayElements(array, nullptr); + if (items) + { + std::memcpy(&elements[0], items, sizeof(jfloat) * elements.size()); + env->ReleaseFloatArrayElements(array, items, JNI_ABORT); + return elements; + } + } + return {}; +} + +std::vector RemoteVM::GetDoubleArrayElements(jdoubleArray array) const noexcept +{ + if (this->send_command) + { + return SendCommand>(RemoteVMCommand::GET_DOUBLE_ARRAY_ELEMENTS, array); + } + + std::vector elements; + elements.resize(env->GetArrayLength(array)); + if (!elements.empty()) + { + jdouble* items = env->GetDoubleArrayElements(array, nullptr); + if (items) + { + std::memcpy(&elements[0], items, sizeof(jdouble) * elements.size()); + env->ReleaseDoubleArrayElements(array, items, JNI_ABORT); + return elements; + } + } + return {}; +} + +std::vector RemoteVM::GetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len) const noexcept +{ + if (this->send_command) + { + return SendCommand>(RemoteVMCommand::GET_BOOLEAN_ARRAY_REGION, array, start, len); + } + + std::vector elements; + elements.resize(env->GetArrayLength(array)); + if (!elements.empty()) + { + env->GetBooleanArrayRegion(array, start, len, &elements[0]); + return elements; + } + return elements; +} + +std::vector RemoteVM::GetByteArrayRegion(jbyteArray array, jsize start, jsize len) const noexcept +{ + if (this->send_command) + { + return SendCommand>(RemoteVMCommand::GET_BYTE_ARRAY_REGION, array, start, len); + } + + std::vector elements; + elements.resize(env->GetArrayLength(array)); + if (!elements.empty()) + { + env->GetByteArrayRegion(array, start, len, &elements[0]); + return elements; + } + return elements; +} + +std::vector RemoteVM::GetCharArrayRegion(jcharArray array, jsize start, jsize len) const noexcept +{ + if (this->send_command) + { + return SendCommand>(RemoteVMCommand::GET_CHAR_ARRAY_REGION, array, start, len); + } + + std::vector elements; + elements.resize(env->GetArrayLength(array)); + if (!elements.empty()) + { + env->GetCharArrayRegion(array, start, len, &elements[0]); + return elements; + } + return {}; +} + +std::vector RemoteVM::GetShortArrayRegion(jshortArray array, jsize start, jsize len) const noexcept +{ + if (this->send_command) + { + return SendCommand>(RemoteVMCommand::GET_SHORT_ARRAY_REGION, array, start, len); + } + + std::vector elements; + elements.resize(env->GetArrayLength(array)); + if (!elements.empty()) + { + env->GetShortArrayRegion(array, start, len, &elements[0]); + return elements; + } + return {}; +} + +std::vector RemoteVM::GetIntArrayRegion(jintArray array, jsize start, jsize len) const noexcept +{ + if (this->send_command) + { + return SendCommand>(RemoteVMCommand::GET_INT_ARRAY_REGION, array, start, len); + } + + std::vector elements; + elements.resize(env->GetArrayLength(array)); + if (!elements.empty()) + { + env->GetIntArrayRegion(array, start, len, &elements[0]); + return elements; + } + return {}; +} + +std::vector RemoteVM::GetLongArrayRegion(jlongArray array, jsize start, jsize len) const noexcept +{ + if (this->send_command) + { + return SendCommand>(RemoteVMCommand::GET_LONG_ARRAY_REGION, array, start, len); + } + + std::vector elements; + elements.resize(env->GetArrayLength(array)); + if (!elements.empty()) + { + env->GetLongArrayRegion(array, start, len, &elements[0]); + return elements; + } + return {}; +} + +std::vector RemoteVM::GetFloatArrayRegion(jfloatArray array, jsize start, jsize len) const noexcept +{ + if (this->send_command) + { + return SendCommand>(RemoteVMCommand::GET_FLOAT_ARRAY_REGION, array, start, len); + } + + std::vector elements; + elements.resize(env->GetArrayLength(array)); + if (!elements.empty()) + { + env->GetFloatArrayRegion(array, start, len, &elements[0]); + return elements; + } + return {}; +} + +std::vector RemoteVM::GetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len) const noexcept +{ + if (this->send_command) + { + return SendCommand>(RemoteVMCommand::GET_DOUBLE_ARRAY_REGION, array, start, len); + } + + std::vector elements; + elements.resize(env->GetArrayLength(array)); + if (!elements.empty()) + { + env->GetDoubleArrayRegion(array, start, len, &elements[0]); + return elements; + } + return {}; +} + +void RemoteVM::SetBooleanArrayRegion(jbooleanArray array, jsize start, const std::vector& buf) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::SET_BOOLEAN_ARRAY_REGION, array, start, buf); + } + return env->SetBooleanArrayRegion(array, start, static_cast(buf.size()), buf.data()); +} + +void RemoteVM::SetByteArrayRegion(jbyteArray array, jsize start, const std::vector& buf) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::SET_BYTE_ARRAY_REGION, array, start, buf); + } + return env->SetByteArrayRegion(array, start, static_cast(buf.size()), buf.data()); +} + +void RemoteVM::SetCharArrayRegion(jcharArray array, jsize start, const std::vector& buf) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::SET_CHAR_ARRAY_REGION, array, start, buf); + } + return env->SetCharArrayRegion(array, start, static_cast(buf.size()), buf.data()); +} + +void RemoteVM::SetShortArrayRegion(jshortArray array, jsize start, const std::vector& buf) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::SET_SHORT_ARRAY_REGION, array, start, buf); + } + return env->SetShortArrayRegion(array, start, static_cast(buf.size()), buf.data()); +} + +void RemoteVM::SetIntArrayRegion(jintArray array, jsize start, const std::vector& buf) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::SET_INT_ARRAY_REGION, array, start, buf); + } + return env->SetIntArrayRegion(array, start, static_cast(buf.size()), buf.data()); +} + +void RemoteVM::SetLongArrayRegion(jlongArray array, jsize start, const std::vector& buf) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::SET_LONG_ARRAY_REGION, array, start, buf); + } + return env->SetLongArrayRegion(array, start, static_cast(buf.size()), buf.data()); +} + +void RemoteVM::SetFloatArrayRegion(jfloatArray array, jsize start, const std::vector& buf) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::SET_FLOAT_ARRAY_REGION, array, start, buf); + } + return env->SetFloatArrayRegion(array, start, static_cast(buf.size()), buf.data()); +} + +void RemoteVM::SetDoubleArrayRegion(jdoubleArray array, jsize start, const std::vector& buf) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::SET_DOUBLE_ARRAY_REGION, array, start, buf); + } + return env->SetDoubleArrayRegion(array, start, static_cast(buf.size()), buf.data()); +} + +jint RemoteVM::MonitorEnter(jobject obj) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::MONITOR_ENTER, obj); + } + return env->MonitorEnter(obj); +} + +jint RemoteVM::MonitorExit(jobject obj) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::MONITOR_EXIT, obj); + } + return env->MonitorExit(obj); +} + +JavaVM* RemoteVM::GetJavaVM() const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::GET_JAVA_VM); + } + + JavaVM* jvm = nullptr; + return env->GetJavaVM(&jvm) == 0 ? jvm : nullptr; +} + +jobject RemoteVM::NewDirectByteBuffer(void* address, jlong capacity) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::NEW_DIRECT_BYTE_BUFFER, address, capacity); + } + return local_to_global(env->NewDirectByteBuffer(address, capacity)); +} + +void* RemoteVM::GetDirectBufferAddress(jobject buf) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::GET_DIRECT_BUFFER_ADDRESS, buf); + } + return env->GetDirectBufferAddress(buf); +} + +jlong RemoteVM::GetDirectBufferCapacity(jobject buf) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::GET_DIRECT_BUFFER_CAPACITY, buf); + } + return env->GetDirectBufferCapacity(buf); +} + +jobjectRefType RemoteVM::GetObjectRefType(jobject obj) const noexcept +{ + if (this->send_command) + { + return SendCommand(RemoteVMCommand::GET_OBJECT_REF_TYPE, obj); + } + return env->GetObjectRefType(obj); +} + +bool RemoteVM::is_remote() const noexcept +{ + return !env && control_center && send_command && get_image_data; +} + +void RemoteVM::process_command(ImageData* image_data) const noexcept +{ + switch (image_data->data_stream().read()) + { + case RemoteVMCommand::ALLOCATE_MEMORY: + { + void* result = this->ExecuteCommand(image_data, &RemoteVM::AllocateMemory); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::READ_MEMORY: + { + void* source = Remote_VM_Read(image_data); + jsize size = Remote_VM_Read(image_data); + bool result = this->ReadMemory(image_data->data_buffer(std::ios::out), source, size); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::WRITE_MEMORY: + { + void* destinaton = Remote_VM_Read(image_data); + jsize size = Remote_VM_Read(image_data); + bool result = this->WriteMemory(destinaton, image_data->data_buffer(std::ios::in), size); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::FREE_MEMORY: + { + this->ExecuteCommand(image_data, &RemoteVM::FreeMemory); + } + break; + + case RemoteVMCommand::GET_VERSION: + { + jint result = this->ExecuteCommand(image_data, &RemoteVM::GetVersion); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::DEFINE_CLASS: + { + std::string name = Remote_VM_Read(image_data); + jobject loader = Remote_VM_Read(image_data); + jbyte* buf = Remote_VM_Read(image_data); + jsize len = Remote_VM_Read(image_data); + jclass result = this->DefineClass(name, loader, buf == nullptr ? static_cast(image_data->data_buffer( + std::ios::in)) : buf, len); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::FIND_CLASS: + { + jobject result = this->ExecuteCommand(image_data, &RemoteVM::FindClass); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::FROM_REFLECTED_METHOD: + { + jmethodID result = this->ExecuteCommand(image_data, &RemoteVM::FromReflectedMethod); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::FROM_REFLECTED_FIELD: + { + jfieldID result = this->ExecuteCommand(image_data, &RemoteVM::FromReflectedField); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::TO_REFLECTED_METHOD: + { + jobject result = this->ExecuteCommand(image_data, &RemoteVM::ToReflectedMethod); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_SUPER_CLASS: + { + jclass result = this->ExecuteCommand(image_data, &RemoteVM::GetSuperclass); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::IS_ASSIGNABLE_FROM: + { + bool result = this->ExecuteCommand(image_data, &RemoteVM::IsAssignableFrom); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::TO_REFLECTED_FIELD: + { + jobject result = this->ExecuteCommand(image_data, &RemoteVM::ToReflectedField); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::THROW: + { + jint result = this->ExecuteCommand(image_data, &RemoteVM::Throw); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::THROW_NEW: + { + jint result = this->ExecuteCommand(image_data, &RemoteVM::ThrowNew); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_EXCEPTION_MESSAGE: + { + std::string result = this->ExecuteCommand(image_data, &RemoteVM::GetExceptionMessage); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::FATAL_ERROR: + { + this->ExecuteCommand(image_data, &RemoteVM::FatalError); + } + break; + + case RemoteVMCommand::DELETE_GLOBAL_REF: + { + this->ExecuteCommand(image_data, &RemoteVM::DeleteGlobalRef); + } + break; + + case RemoteVMCommand::IS_SAME_OBJECT: + { + jboolean result = this->ExecuteCommand(image_data, &RemoteVM::IsSameObject); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::ALLOC_OBJECT: + { + jobject result = this->ExecuteCommand(image_data, &RemoteVM::AllocObject); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::NEW_OBJECT: + { + jobject result = this->ExecuteCommand(image_data, &RemoteVM::NewObject); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_OBJECT_CLASS: + { + jclass result = this->ExecuteCommand(image_data, &RemoteVM::GetObjectClass); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::IS_INSTANCE_OF: + { + jboolean result = this->ExecuteCommand(image_data, &RemoteVM::IsInstanceOf); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_METHOD_ID: + { + jmethodID result = this->ExecuteCommand(image_data, &RemoteVM::GetMethodID); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::CALL_OBJECT_METHOD: + { + jobject result = this->ExecuteCommand(image_data, &RemoteVM::CallObjectMethod); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::CALL_BOOLEAN_METHOD: + { + jboolean result = this->ExecuteCommand(image_data, &RemoteVM::CallBooleanMethod); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::CALL_BYTE_METHOD: + { + jbyte result = this->ExecuteCommand(image_data, &RemoteVM::CallByteMethod); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::CALL_CHAR_METHOD: + { + jchar result = this->ExecuteCommand(image_data, &RemoteVM::CallCharMethod); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::CALL_SHORT_METHOD: + { + jshort result = this->ExecuteCommand(image_data, &RemoteVM::CallShortMethod); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::CALL_INT_METHOD: + { + jint result = this->ExecuteCommand(image_data, &RemoteVM::CallIntMethod); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::CALL_LONG_METHOD: + { + jlong result = this->ExecuteCommand(image_data, &RemoteVM::CallLongMethod); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::CALL_FLOAT_METHOD: + { + jfloat result = this->ExecuteCommand(image_data, &RemoteVM::CallFloatMethod); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::CALL_DOUBLE_METHOD: + { + jdouble result = this->ExecuteCommand(image_data, &RemoteVM::CallDoubleMethod); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::CALL_VOID_METHOD: + { + this->ExecuteCommand(image_data, &RemoteVM::CallVoidMethod); + } + break; + + case RemoteVMCommand::CALL_NON_VIRTUAL_OBJECT_METHOD: + { + jobject result = this->ExecuteCommand(image_data, &RemoteVM::CallNonvirtualObjectMethod); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::CALL_NON_VIRTUAL_BOOLEAN_METHOD: + { + jboolean result = this->ExecuteCommand(image_data, &RemoteVM::CallNonvirtualBooleanMethod); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::CALL_NON_VIRTUAL_BYTE_METHOD: + { + jbyte result = this->ExecuteCommand(image_data, &RemoteVM::CallNonvirtualByteMethod); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::CALL_NON_VIRTUAL_CHAR_METHOD: + { + jchar result = this->ExecuteCommand(image_data, &RemoteVM::CallNonvirtualCharMethod); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::CALL_NON_VIRTUAL_SHORT_METHOD: + { + jshort result = this->ExecuteCommand(image_data, &RemoteVM::CallNonvirtualShortMethod); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::CALL_NON_VIRTUAL_INT_METHOD: + { + jint result = this->ExecuteCommand(image_data, &RemoteVM::CallNonvirtualIntMethod); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::CALL_NON_VIRTUAL_LONG_METHOD: + { + jlong result = this->ExecuteCommand(image_data, &RemoteVM::CallNonvirtualLongMethod); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::CALL_NON_VIRTUAL_FLOAT_METHOD: + { + jfloat result = this->ExecuteCommand(image_data, &RemoteVM::CallNonvirtualFloatMethod); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::CALL_NON_VIRTUAL_DOUBLE_METHOD: + { + jdouble result = this->ExecuteCommand(image_data, &RemoteVM::CallNonvirtualDoubleMethod); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::CALL_NON_VIRTUAL_VOID_METHOD: + { + this->ExecuteCommand(image_data, &RemoteVM::CallNonvirtualVoidMethod); + } + break; + + case RemoteVMCommand::GET_FIELD_ID: + { + jfieldID result = this->ExecuteCommand(image_data, &RemoteVM::GetFieldID); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_OBJECT_FIELD: + { + jobject result = this->ExecuteCommand(image_data, &RemoteVM::GetObjectField); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_BOOLEAN_FIELD: + { + jboolean result = this->ExecuteCommand(image_data, &RemoteVM::GetBooleanField); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_BYTE_FIELD: + { + jbyte result = this->ExecuteCommand(image_data, &RemoteVM::GetByteField); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_CHAR_FIELD: + { + jchar result = this->ExecuteCommand(image_data, &RemoteVM::GetCharField); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_SHORT_FIELD: + { + jshort result = this->ExecuteCommand(image_data, &RemoteVM::GetShortField); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_INT_FIELD: + { + jint result = this->ExecuteCommand(image_data, &RemoteVM::GetIntField); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_LONG_FIELD: + { + jlong result = this->ExecuteCommand(image_data, &RemoteVM::GetLongField); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_FLOAT_FIELD: + { + jfloat result = this->ExecuteCommand(image_data, &RemoteVM::GetFloatField); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_DOUBLE_FIELD: + { + jdouble result = this->ExecuteCommand(image_data, &RemoteVM::GetDoubleField); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::SET_OBJECT_FIELD: + { + this->ExecuteCommand(image_data, &RemoteVM::SetObjectField); + } + break; + + case RemoteVMCommand::SET_BOOLEAN_FIELD: + { + this->ExecuteCommand(image_data, &RemoteVM::SetBooleanField); + } + break; + + case RemoteVMCommand::SET_BYTE_FIELD: + { + this->ExecuteCommand(image_data, &RemoteVM::SetByteField); + } + break; + + case RemoteVMCommand::SET_CHAR_FIELD: + { + this->ExecuteCommand(image_data, &RemoteVM::SetCharField); + } + break; + + case RemoteVMCommand::SET_SHORT_FIELD: + { + this->ExecuteCommand(image_data, &RemoteVM::SetShortField); + } + break; + + case RemoteVMCommand::SET_INT_FIELD: + { + this->ExecuteCommand(image_data, &RemoteVM::SetIntField); + } + break; + + case RemoteVMCommand::SET_LONG_FIELD: + { + this->ExecuteCommand(image_data, &RemoteVM::SetLongField); + } + break; + + case RemoteVMCommand::SET_FLOAT_FIELD: + { + this->ExecuteCommand(image_data, &RemoteVM::SetFloatField); + } + break; + + case RemoteVMCommand::SET_DOUBLE_FIELD: + { + this->ExecuteCommand(image_data, &RemoteVM::SetDoubleField); + } + break; + + case RemoteVMCommand::GET_STATIC_METHOD_ID: + { + jmethodID result = this->ExecuteCommand(image_data, &RemoteVM::GetStaticMethodID); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::CALL_STATIC_OBJECT_METHOD: + { + jobject result = this->ExecuteCommand(image_data, &RemoteVM::CallStaticObjectMethod); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::CALL_STATIC_BOOLEAN_METHOD: + { + jboolean result = this->ExecuteCommand(image_data, &RemoteVM::CallStaticBooleanMethod); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::CALL_STATIC_BYTE_METHOD: + { + jbyte result = this->ExecuteCommand(image_data, &RemoteVM::CallStaticByteMethod); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::CALL_STATIC_CHAR_METHOD: + { + jchar result = this->ExecuteCommand(image_data, &RemoteVM::CallStaticCharMethod); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::CALL_STATIC_SHORT_METHOD: + { + jshort result = this->ExecuteCommand(image_data, &RemoteVM::CallStaticShortMethod); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::CALL_STATIC_INT_METHOD: + { + jint result = this->ExecuteCommand(image_data, &RemoteVM::CallStaticIntMethod); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::CALL_STATIC_LONG_METHOD: + { + jlong result = this->ExecuteCommand(image_data, &RemoteVM::CallStaticLongMethod); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::CALL_STATIC_FLOAT_METHOD: + { + jfloat result = this->ExecuteCommand(image_data, &RemoteVM::CallStaticFloatMethod); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::CALL_STATIC_DOUBLE_METHOD: + { + jdouble result = this->ExecuteCommand(image_data, &RemoteVM::CallStaticDoubleMethod); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::CALL_STATIC_VOID_METHOD: + { + this->ExecuteCommand(image_data, &RemoteVM::CallStaticVoidMethod); + } + break; + + case RemoteVMCommand::GET_STATIC_FIELD_ID: + { + jfieldID result = this->ExecuteCommand(image_data, &RemoteVM::GetStaticFieldID); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_STATIC_OBJECT_FIELD: + { + jobject result = this->ExecuteCommand(image_data, &RemoteVM::GetStaticObjectField); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_STATIC_BOOLEAN_FIELD: + { + jboolean result = this->ExecuteCommand(image_data, &RemoteVM::GetStaticBooleanField); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_STATIC_BYTE_FIELD: + { + jbyte result = this->ExecuteCommand(image_data, &RemoteVM::GetStaticByteField); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_STATIC_CHAR_FIELD: + { + jchar result = this->ExecuteCommand(image_data, &RemoteVM::GetStaticCharField); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_STATIC_SHORT_FIELD: + { + jshort result = this->ExecuteCommand(image_data, &RemoteVM::GetStaticShortField); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_STATIC_INT_FIELD: + { + jint result = this->ExecuteCommand(image_data, &RemoteVM::GetStaticIntField); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_STATIC_LONG_FIELD: + { + jlong result = this->ExecuteCommand(image_data, &RemoteVM::GetStaticLongField); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_STATIC_FLOAT_FIELD: + { + jfloat result = this->ExecuteCommand(image_data, &RemoteVM::GetStaticFloatField); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_STATIC_DOUBLE_FIELD: + { + jdouble result = this->ExecuteCommand(image_data, &RemoteVM::GetStaticDoubleField); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::SET_STATIC_OBJECT_FIELD: + { + this->ExecuteCommand(image_data, &RemoteVM::SetStaticObjectField); + } + break; + + case RemoteVMCommand::SET_STATIC_BOOLEAN_FIELD: + { + this->ExecuteCommand(image_data, &RemoteVM::SetStaticBooleanField); + } + break; + + case RemoteVMCommand::SET_STATIC_BYTE_FIELD: + { + this->ExecuteCommand(image_data, &RemoteVM::SetStaticByteField); + } + break; + + case RemoteVMCommand::SET_STATIC_CHAR_FIELD: + { + this->ExecuteCommand(image_data, &RemoteVM::SetStaticCharField); + } + break; + + case RemoteVMCommand::SET_STATIC_SHORT_FIELD: + { + this->ExecuteCommand(image_data, &RemoteVM::SetStaticShortField); + } + break; + + case RemoteVMCommand::SET_STATIC_INT_FIELD: + { + this->ExecuteCommand(image_data, &RemoteVM::SetStaticIntField); + } + break; + + case RemoteVMCommand::SET_STATIC_LONG_FIELD: + { + this->ExecuteCommand(image_data, &RemoteVM::SetStaticLongField); + } + break; + + case RemoteVMCommand::SET_STATIC_FLOAT_FIELD: + { + this->ExecuteCommand(image_data, &RemoteVM::SetStaticFloatField); + } + break; + + case RemoteVMCommand::SET_STATIC_DOUBLE_FIELD: + { + this->ExecuteCommand(image_data, &RemoteVM::SetStaticDoubleField); + } + break; + + case RemoteVMCommand::NEW_STRING: + { + jstring result = this->ExecuteCommand(image_data, &RemoteVM::NewString); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_STRING_LENGTH: + { + jsize result = this->ExecuteCommand(image_data, &RemoteVM::GetStringLength); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_STRING_CHARS: + { + std::wstring result = this->ExecuteCommand(image_data, &RemoteVM::GetStringChars); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::NEW_STRING_UTF: + { + jstring result = this->ExecuteCommand(image_data, &RemoteVM::NewStringUTF); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_STRING_UTF_LENGTH: + { + jsize result = this->ExecuteCommand(image_data, &RemoteVM::GetStringUTFLength); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_STRING_UTF_CHARS: + { + std::string result = this->ExecuteCommand(image_data, &RemoteVM::GetStringUTFChars); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_ARRAY_LENGTH: + { + jsize result = this->ExecuteCommand(image_data, &RemoteVM::GetArrayLength); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::NEW_OBJECT_ARRAY: + { + jobjectArray result = this->ExecuteCommand(image_data, &RemoteVM::NewObjectArray); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_OBJECT_ARRAY_ELEMENTS: + { + std::vector result = this->ExecuteCommand(image_data, &RemoteVM::GetObjectArrayElements); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::SET_OBJECT_ARRAY_ELEMENTS: + { + this->ExecuteCommand(image_data, &RemoteVM::SetObjectArrayElements); + } + break; + + case RemoteVMCommand::NEW_BOOLEAN_ARRAY: + { + jbooleanArray result = this->ExecuteCommand(image_data, &RemoteVM::NewBooleanArray); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::NEW_BYTE_ARRAY: + { + jbyteArray result = this->ExecuteCommand(image_data, &RemoteVM::NewByteArray); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::NEW_CHAR_ARRAY: + { + jcharArray result = this->ExecuteCommand(image_data, &RemoteVM::NewCharArray); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::NEW_SHORT_ARRAY: + { + jshortArray result = this->ExecuteCommand(image_data, &RemoteVM::NewShortArray); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::NEW_INT_ARRAY: + { + jintArray result = this->ExecuteCommand(image_data, &RemoteVM::NewIntArray); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::NEW_LONG_ARRAY: + { + jlongArray result = this->ExecuteCommand(image_data, &RemoteVM::NewLongArray); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::NEW_FLOAT_ARRAY: + { + jfloatArray result = this->ExecuteCommand(image_data, &RemoteVM::NewFloatArray); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::NEW_DOUBLE_ARRAY: + { + jdoubleArray result = this->ExecuteCommand(image_data, &RemoteVM::NewDoubleArray); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_BOOLEAN_ARRAY_ELEMENTS: + { + std::vector result = this->ExecuteCommand(image_data, &RemoteVM::GetBooleanArrayElements); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_BYTE_ARRAY_ELEMENTS: + { + std::vector result = this->ExecuteCommand(image_data, &RemoteVM::GetByteArrayElements); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_CHAR_ARRAY_ELEMENTS: + { + std::vector result = this->ExecuteCommand(image_data, &RemoteVM::GetCharArrayElements); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_SHORT_ARRAY_ELEMENTS: + { + std::vector result = this->ExecuteCommand(image_data, &RemoteVM::GetShortArrayElements); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_INT_ARRAY_ELEMENTS: + { + std::vector result = this->ExecuteCommand(image_data, &RemoteVM::GetIntArrayElements); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_LONG_ARRAY_ELEMENTS: + { + std::vector result = this->ExecuteCommand(image_data, &RemoteVM::GetLongArrayElements); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_FLOAT_ARRAY_ELEMENTS: + { + std::vector result = this->ExecuteCommand(image_data, &RemoteVM::GetFloatArrayElements); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_DOUBLE_ARRAY_ELEMENTS: + { + std::vector result = this->ExecuteCommand(image_data, &RemoteVM::GetDoubleArrayElements); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_BOOLEAN_ARRAY_REGION: + { + std::vector result = this->ExecuteCommand(image_data, &RemoteVM::GetBooleanArrayRegion); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_BYTE_ARRAY_REGION: + { + std::vector result = this->ExecuteCommand(image_data, &RemoteVM::GetByteArrayRegion); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_CHAR_ARRAY_REGION: + { + std::vector result = this->ExecuteCommand(image_data, &RemoteVM::GetCharArrayRegion); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_SHORT_ARRAY_REGION: + { + std::vector result = this->ExecuteCommand(image_data, &RemoteVM::GetShortArrayRegion); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_INT_ARRAY_REGION: + { + std::vector result = this->ExecuteCommand(image_data, &RemoteVM::GetIntArrayRegion); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_LONG_ARRAY_REGION: + { + std::vector result = this->ExecuteCommand(image_data, &RemoteVM::GetLongArrayRegion); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_FLOAT_ARRAY_REGION: + { + std::vector result = this->ExecuteCommand(image_data, &RemoteVM::GetFloatArrayRegion); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_DOUBLE_ARRAY_REGION: + { + std::vector result = this->ExecuteCommand(image_data, &RemoteVM::GetDoubleArrayRegion); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::SET_BOOLEAN_ARRAY_REGION: + { + this->ExecuteCommand(image_data, &RemoteVM::SetBooleanArrayRegion); + } + break; + + case RemoteVMCommand::SET_BYTE_ARRAY_REGION: + { + this->ExecuteCommand(image_data, &RemoteVM::SetByteArrayRegion); + } + break; + + case RemoteVMCommand::SET_CHAR_ARRAY_REGION: + { + this->ExecuteCommand(image_data, &RemoteVM::SetCharArrayRegion); + } + break; + + case RemoteVMCommand::SET_SHORT_ARRAY_REGION: + { + this->ExecuteCommand(image_data, &RemoteVM::SetShortArrayRegion); + } + break; + + case RemoteVMCommand::SET_INT_ARRAY_REGION: + { + this->ExecuteCommand(image_data, &RemoteVM::SetIntArrayRegion); + } + break; + + case RemoteVMCommand::SET_LONG_ARRAY_REGION: + { + this->ExecuteCommand(image_data, &RemoteVM::SetLongArrayRegion); + } + break; + + case RemoteVMCommand::SET_FLOAT_ARRAY_REGION: + { + this->ExecuteCommand(image_data, &RemoteVM::SetFloatArrayRegion); + } + break; + + case RemoteVMCommand::SET_DOUBLE_ARRAY_REGION: + { + this->ExecuteCommand(image_data, &RemoteVM::SetDoubleArrayRegion); + } + break; + + case RemoteVMCommand::MONITOR_ENTER: + { + jint result = this->ExecuteCommand(image_data, &RemoteVM::MonitorEnter); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::MONITOR_EXIT: + { + jint result = this->ExecuteCommand(image_data, &RemoteVM::MonitorExit); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_JAVA_VM: + { + JavaVM* result = this->ExecuteCommand(image_data, &RemoteVM::GetJavaVM); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::NEW_DIRECT_BYTE_BUFFER: + { + jobject result = this->ExecuteCommand(image_data, &RemoteVM::NewDirectByteBuffer); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_DIRECT_BUFFER_ADDRESS: + { + void* result = this->ExecuteCommand(image_data, &RemoteVM::GetDirectBufferAddress); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_DIRECT_BUFFER_CAPACITY: + { + jlong result = this->ExecuteCommand(image_data, &RemoteVM::GetDirectBufferCapacity); + image_data->data_stream().write(result); + } + break; + + case RemoteVMCommand::GET_OBJECT_REF_TYPE: + { + jobjectRefType result = this->ExecuteCommand(image_data, &RemoteVM::GetObjectRefType); + image_data->data_stream().write(result); + } + break; + } +} \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Plugin/JVM/RemoteVM.hxx b/third-party/RemoteInput/RemoteInput/Plugin/JVM/RemoteVM.hxx new file mode 100644 index 0000000..c5ae886 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Plugin/JVM/RemoteVM.hxx @@ -0,0 +1,204 @@ +// +// Created by Brandon on 2020-12-08. +// + +#ifndef REMOTEINPUT_REMOTEVM_HXX +#define REMOTEINPUT_REMOTEVM_HXX + +#include +#include +#include +#include + +#include "JNI_Common.hxx" +#include "TypeTraits.hxx" +#include "ImageData.hxx" + +class ControlCenter; +struct EIOSData; +enum class RemoteVMCommand: std::uint32_t; + +class RemoteVM final +{ +private: + JNIEnv* env; + ControlCenter* control_center; + std::function)> send_command; + ImageData* (ControlCenter::*get_image_data)() const; + + template + T local_to_global(T object) const noexcept; + + template + R SendCommand(RemoteVMCommand command, Args&&... args) const noexcept; + + template + R ExecuteCommand(ImageData* image_data, R (RemoteVM::*func)(Args...) const noexcept) const noexcept; + +public: + // MSVC will crash when initializing `send_command` and `get_image_data`. So this convenience function is here. + // MSVC seems to not like the pointer to method. + RemoteVM(JNIEnv* env, ControlCenter* control_center) noexcept; + + RemoteVM(JNIEnv* env, + ControlCenter* control_center, + std::function)>&& send_command, + ImageData* (ControlCenter::*get_image_data)() const) noexcept; + ~RemoteVM(); + + RemoteVM(const RemoteVM& other) = delete; + RemoteVM(RemoteVM&& other) noexcept ; + + RemoteVM& operator = (const RemoteVM& other) = delete; + RemoteVM& operator = (RemoteVM&& other) noexcept; + + std::size_t MaxMemoryChunkSize() const noexcept; + void* AllocateMemory(std::size_t size) const noexcept; + bool ReadMemory(void* destination, void* source, std::size_t size) const noexcept; + bool WriteMemory(void* destination, void* source, std::size_t size) const noexcept; + void FreeMemory(void* memory) const noexcept; + + jint GetVersion() const noexcept; + jclass DefineClass(const std::string &name, jobject loader, const jbyte* buf, jsize len) const noexcept; + jclass FindClass(const std::string &name) const noexcept; + jmethodID FromReflectedMethod(jobject method) const noexcept; + jfieldID FromReflectedField(jobject field) const noexcept; + jobject ToReflectedMethod(jclass cls, jmethodID methodID, jboolean isStatic) const noexcept; + jclass GetSuperclass(jclass sub) const noexcept; + jboolean IsAssignableFrom(jclass sub, jclass sup) const noexcept; + jobject ToReflectedField(jclass cls, jfieldID fieldID, jboolean isStatic) const noexcept; + jint Throw(jthrowable obj) const noexcept; + jint ThrowNew(jclass clazz, const std::string &msg) const noexcept; + std::string GetExceptionMessage() const noexcept; + void FatalError(const std::string &msg) const noexcept; + void DeleteGlobalRef(jobject gref) const noexcept; + jboolean IsSameObject(jobject obj1, jobject obj2) const noexcept; + jobject AllocObject(jclass clazz) const noexcept; + jobject NewObject(jclass clazz, jmethodID methodID, const std::vector &args) const noexcept; + jclass GetObjectClass(jobject obj) const noexcept; + jboolean IsInstanceOf(jobject obj, jclass clazz) const noexcept; + jmethodID GetMethodID(jclass clazz, const std::string &name, const std::string &sig) const noexcept; + jobject CallObjectMethod(jobject obj, jmethodID methodID, const std::vector &args) const noexcept; + jboolean CallBooleanMethod(jobject obj, jmethodID methodID, const std::vector &args) const noexcept; + jbyte CallByteMethod(jobject obj, jmethodID methodID, const std::vector &args) const noexcept; + jchar CallCharMethod(jobject obj, jmethodID methodID, const std::vector &args) const noexcept; + jshort CallShortMethod(jobject obj, jmethodID methodID, const std::vector &args) const noexcept; + jint CallIntMethod(jobject obj, jmethodID methodID, const std::vector &args) const noexcept; + jlong CallLongMethod(jobject obj, jmethodID methodID, const std::vector &args) const noexcept; + jfloat CallFloatMethod(jobject obj, jmethodID methodID, const std::vector &args) const noexcept; + jdouble CallDoubleMethod(jobject obj, jmethodID methodID, const std::vector &args) const noexcept; + void CallVoidMethod(jobject obj, jmethodID methodID, const std::vector &args) const noexcept; + jobject CallNonvirtualObjectMethod(jobject obj, jclass clazz, jmethodID methodID, const std::vector &args) const noexcept; + jboolean CallNonvirtualBooleanMethod(jobject obj, jclass clazz, jmethodID methodID, const std::vector &args) const noexcept; + jbyte CallNonvirtualByteMethod(jobject obj, jclass clazz, jmethodID methodID, const std::vector &args) const noexcept; + jchar CallNonvirtualCharMethod(jobject obj, jclass clazz, jmethodID methodID, const std::vector &args) const noexcept; + jshort CallNonvirtualShortMethod(jobject obj, jclass clazz, jmethodID methodID, const std::vector &args) const noexcept; + jint CallNonvirtualIntMethod(jobject obj, jclass clazz, jmethodID methodID, const std::vector &args) const noexcept; + jlong CallNonvirtualLongMethod(jobject obj, jclass clazz, jmethodID methodID, const std::vector &args) const noexcept; + jfloat CallNonvirtualFloatMethod(jobject obj, jclass clazz, jmethodID methodID, const std::vector &args) const noexcept; + jdouble CallNonvirtualDoubleMethod(jobject obj, jclass clazz, jmethodID methodID, const std::vector &args) const noexcept; + void CallNonvirtualVoidMethod(jobject obj, jclass clazz, jmethodID methodID, const std::vector &args) const noexcept; + jfieldID GetFieldID(jclass clazz, const std::string &name, const std::string &sig) const noexcept; + jobject GetObjectField(jobject obj, jfieldID fieldID) const noexcept; + jboolean GetBooleanField(jobject obj, jfieldID fieldID) const noexcept; + jbyte GetByteField(jobject obj, jfieldID fieldID) const noexcept; + jchar GetCharField(jobject obj, jfieldID fieldID) const noexcept; + jshort GetShortField(jobject obj, jfieldID fieldID) const noexcept; + jint GetIntField(jobject obj, jfieldID fieldID) const noexcept; + jlong GetLongField(jobject obj, jfieldID fieldID) const noexcept; + jfloat GetFloatField(jobject obj, jfieldID fieldID) const noexcept; + jdouble GetDoubleField(jobject obj, jfieldID fieldID) const noexcept; + void SetObjectField(jobject obj, jfieldID fieldID, jobject val) const noexcept; + void SetBooleanField(jobject obj, jfieldID fieldID, jboolean val) const noexcept; + void SetByteField(jobject obj, jfieldID fieldID, jbyte val) const noexcept; + void SetCharField(jobject obj, jfieldID fieldID, jchar val) const noexcept; + void SetShortField(jobject obj, jfieldID fieldID, jshort val) const noexcept; + void SetIntField(jobject obj, jfieldID fieldID, jint val) const noexcept; + void SetLongField(jobject obj, jfieldID fieldID, jlong val) const noexcept; + void SetFloatField(jobject obj, jfieldID fieldID, jfloat val) const noexcept; + void SetDoubleField(jobject obj, jfieldID fieldID, jdouble val) const noexcept; + jmethodID GetStaticMethodID(jclass clazz, const std::string &name, const std::string &sig) const noexcept; + jobject CallStaticObjectMethod(jclass clazz, jmethodID methodID, const std::vector &args) const noexcept; + jboolean CallStaticBooleanMethod(jclass clazz, jmethodID methodID, const std::vector &args) const noexcept; + jbyte CallStaticByteMethod(jclass clazz, jmethodID methodID, const std::vector &args) const noexcept; + jchar CallStaticCharMethod(jclass clazz, jmethodID methodID, const std::vector &args) const noexcept; + jshort CallStaticShortMethod(jclass clazz, jmethodID methodID, const std::vector &args) const noexcept; + jint CallStaticIntMethod(jclass clazz, jmethodID methodID, const std::vector &args) const noexcept; + jlong CallStaticLongMethod(jclass clazz, jmethodID methodID, const std::vector &args) const noexcept; + jfloat CallStaticFloatMethod(jclass clazz, jmethodID methodID, const std::vector &args) const noexcept; + jdouble CallStaticDoubleMethod(jclass clazz, jmethodID methodID, const std::vector &args) const noexcept; + void CallStaticVoidMethod(jclass clazz, jmethodID methodID, const std::vector &args) const noexcept; + jfieldID GetStaticFieldID(jclass clazz, const std::string &name, const std::string &sig) const noexcept; + jobject GetStaticObjectField(jclass clazz, jfieldID fieldID) const noexcept; + jboolean GetStaticBooleanField(jclass clazz, jfieldID fieldID) const noexcept; + jbyte GetStaticByteField(jclass clazz, jfieldID fieldID) const noexcept; + jchar GetStaticCharField(jclass clazz, jfieldID fieldID) const noexcept; + jshort GetStaticShortField(jclass clazz, jfieldID fieldID) const noexcept; + jint GetStaticIntField(jclass clazz, jfieldID fieldID) const noexcept; + jlong GetStaticLongField(jclass clazz, jfieldID fieldID) const noexcept; + jfloat GetStaticFloatField(jclass clazz, jfieldID fieldID) const noexcept; + jdouble GetStaticDoubleField(jclass clazz, jfieldID fieldID) const noexcept; + void SetStaticObjectField(jclass clazz, jfieldID fieldID, jobject value) const noexcept; + void SetStaticBooleanField(jclass clazz, jfieldID fieldID, jboolean value) const noexcept; + void SetStaticByteField(jclass clazz, jfieldID fieldID, jbyte value) const noexcept; + void SetStaticCharField(jclass clazz, jfieldID fieldID, jchar value) const noexcept; + void SetStaticShortField(jclass clazz, jfieldID fieldID, jshort value) const noexcept; + void SetStaticIntField(jclass clazz, jfieldID fieldID, jint value) const noexcept; + void SetStaticLongField(jclass clazz, jfieldID fieldID, jlong value) const noexcept; + void SetStaticFloatField(jclass clazz, jfieldID fieldID, jfloat value) const noexcept; + void SetStaticDoubleField(jclass clazz, jfieldID fieldID, jdouble value) const noexcept; + jstring NewString(const std::wstring &unicode) const noexcept; + jsize GetStringLength(jstring str) const noexcept; + std::wstring GetStringChars(jstring str) const noexcept; + jstring NewStringUTF(const std::string &utf) const noexcept; + jsize GetStringUTFLength(jstring str) const noexcept; + std::string GetStringUTFChars(jstring str) const noexcept; + jsize GetArrayLength(jarray array) const noexcept; + jobjectArray NewObjectArray(jsize len, jclass clazz, jobject init) const noexcept; + jbooleanArray NewBooleanArray(jsize len) const noexcept; + jbyteArray NewByteArray(jsize len) const noexcept; + jcharArray NewCharArray(jsize len) const noexcept; + jshortArray NewShortArray(jsize len) const noexcept; + jintArray NewIntArray(jsize len) const noexcept; + jlongArray NewLongArray(jsize len) const noexcept; + jfloatArray NewFloatArray(jsize len) const noexcept; + jdoubleArray NewDoubleArray(jsize len) const noexcept; + std::vector GetObjectArrayElements(jobjectArray array) const noexcept; + void SetObjectArrayElements(jobjectArray array, jsize index, const std::vector &elements) const noexcept; + std::vector GetBooleanArrayElements(jbooleanArray array) const noexcept; + std::vector GetByteArrayElements(jbyteArray array) const noexcept; + std::vector GetCharArrayElements(jcharArray array) const noexcept; + std::vector GetShortArrayElements(jshortArray array) const noexcept; + std::vector GetIntArrayElements(jintArray array) const noexcept; + std::vector GetLongArrayElements(jlongArray array) const noexcept; + std::vector GetFloatArrayElements(jfloatArray array) const noexcept; + std::vector GetDoubleArrayElements(jdoubleArray array) const noexcept; + std::vector GetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len) const noexcept; + std::vector GetByteArrayRegion(jbyteArray array, jsize start, jsize len) const noexcept; + std::vector GetCharArrayRegion(jcharArray array, jsize start, jsize len) const noexcept; + std::vector GetShortArrayRegion(jshortArray array, jsize start, jsize len) const noexcept; + std::vector GetIntArrayRegion(jintArray array, jsize start, jsize len) const noexcept; + std::vector GetLongArrayRegion(jlongArray array, jsize start, jsize len) const noexcept; + std::vector GetFloatArrayRegion(jfloatArray array, jsize start, jsize len) const noexcept; + std::vector GetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len) const noexcept; + void SetBooleanArrayRegion(jbooleanArray array, jsize start, const std::vector& buf) const noexcept; + void SetByteArrayRegion(jbyteArray array, jsize start, const std::vector& buf) const noexcept; + void SetCharArrayRegion(jcharArray array, jsize start, const std::vector& buf) const noexcept; + void SetShortArrayRegion(jshortArray array, jsize start, const std::vector& buf) const noexcept; + void SetIntArrayRegion(jintArray array, jsize start, const std::vector& buf) const noexcept; + void SetLongArrayRegion(jlongArray array, jsize start, const std::vector& buf) const noexcept; + void SetFloatArrayRegion(jfloatArray array, jsize start, const std::vector& buf) const noexcept; + void SetDoubleArrayRegion(jdoubleArray array, jsize start, const std::vector& buf) const noexcept; + jint MonitorEnter(jobject obj) const noexcept; + jint MonitorExit(jobject obj) const noexcept; + JavaVM* GetJavaVM() const noexcept; + jobject NewDirectByteBuffer(void* address, jlong capacity) const noexcept; + void* GetDirectBufferAddress(jobject buf) const noexcept; + jlong GetDirectBufferCapacity(jobject buf) const noexcept; + jobjectRefType GetObjectRefType(jobject obj) const noexcept; + + bool is_remote() const noexcept; + void process_command(ImageData* image_data) const noexcept; +}; + +#endif //REMOTEINPUT_REMOTEVM_HXX diff --git a/third-party/RemoteInput/RemoteInput/Plugin/NativePlugin.cxx b/third-party/RemoteInput/RemoteInput/Plugin/NativePlugin.cxx new file mode 100644 index 0000000..b962d3b --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Plugin/NativePlugin.cxx @@ -0,0 +1,94 @@ +// +// Created by Brandon on 2022-09-21. +// + +#include "NativePlugin.hxx" + +#include +#include +#include + +#include "Platform.hxx" +#include "ControlCenter.hxx" + +// MARK: - EXPORTS + +void EIOS_Inject(const char* process_name) noexcept +{ + if (process_name) + { + std::vector pids = InjectProcesses(process_name); + for (std::int32_t pid : pids) + { + if (pid != -1) + { + ControlCenter::wait_for_sync(pid); + } + } + } +} + +void EIOS_Inject_PID(std::int32_t pid) noexcept +{ + if (pid != -1) + { + if (InjectProcess(pid) == pid) + { + ControlCenter::wait_for_sync(pid); + } + } +} + +EIOS* EIOS_From_PID(std::int32_t pid) noexcept +{ + extern std::unordered_map clients; + if (clients.count(pid)) + { + return clients[pid]; + } + return nullptr; +} + +void Reflect_Release_Object(EIOS* eios, jobject object) noexcept +{ + if (eios) + { + //Make sure we're not freeing null objects.. + if (object) + { + eios->control_center->reflect_release_object(object); + } + } +} + +void Reflect_Release_Objects(EIOS* eios, jobject* objects, std::size_t amount) noexcept +{ + if (eios) + { + //Make sure we're not freeing null objects.. + if (objects && amount > 0) + { + //This is the fastest possible way to remove duplicates.. + //Do NOT use `unordered_set` constructor instead of the for-loop. + //It is slower than emplace/insert. + std::unordered_set set; + for (std::size_t i = 0; i < amount; ++i) + { + set.insert(objects[i]); + } + + //Remove null objects + set.erase(nullptr); + + //Create a contiguous array of objects for the client. + std::vector objects; + objects.assign(set.begin(), set.end()); + + //Make sure we're not freeing null objects.. + if (objects.size()) + { + eios->control_center->reflect_release_objects(objects.data(), objects.size()); + } + } + } +} \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Plugin/NativePlugin.hxx b/third-party/RemoteInput/RemoteInput/Plugin/NativePlugin.hxx new file mode 100644 index 0000000..d016bda --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Plugin/NativePlugin.hxx @@ -0,0 +1,29 @@ +// +// Created by Brandon on 2022-09-21. +// + +#ifndef REMOTEINPUT_NATIVEPLUGIN_HXX +#define REMOTEINPUT_NATIVEPLUGIN_HXX + +#include +#include +#include + +#include "EIOS.hxx" + +#ifdef __cplusplus +extern "C" +{ +#endif + +EXPORT void EIOS_Inject(const char* process_name) noexcept; +EXPORT void EIOS_Inject_PID(std::int32_t pid) noexcept; +EXPORT EIOS* EIOS_From_PID(std::int32_t pid) noexcept; +EXPORT void Reflect_Release_Object(EIOS* eios, jobject object) noexcept; +EXPORT void Reflect_Release_Objects(EIOS* eios, jobject* objects, std::size_t amount) noexcept; + +#ifdef __cplusplus +} +#endif + +#endif //REMOTEINPUT_NATIVEPLUGIN_HXX diff --git a/third-party/RemoteInput/RemoteInput/Plugin/Plugin.cxx b/third-party/RemoteInput/RemoteInput/Plugin/Plugin.cxx new file mode 100644 index 0000000..423df68 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Plugin/Plugin.cxx @@ -0,0 +1,195 @@ +// +// Plugin.cxx +// RemoteInput +// +// Created by Brandon on 2019-12-21. +// Copyright © 2019 XIO. All rights reserved. +// + +#include "Plugin.hxx" +#include + +#include "Atomics.hxx" +#include "Platform.hxx" +#include "NativeHooks.hxx" +#include "ControlCenter.hxx" +#include "EIOS.hxx" +#include "DebugConsole.hxx" +#include "Thirdparty/Hook.hxx" +#include "Thirdparty/Injector.hxx" + +#if defined(_WIN32) || defined(_WIN64) +HMODULE module = nullptr; +#endif + +std::vector> injectors; +std::unique_ptr control_center; +std::unique_ptr console; + +#if defined(_WIN32) || defined(_WIN64) +std::unique_ptr exit_process; +#endif + +// MARK: - MAIN + +#if defined(_WIN32) || defined(_WIN64) +[[gnu::stdcall]] void __exit_process(unsigned int exit_code) +{ + #ifdef DEBUG + console.reset(); + #endif + control_center.reset(); + exit_process->call(exit_code); +} + +[[gnu::stdcall]] void __load() +{ + #if defined(DEBUG) + printf("ATTACHED TO: %d\n", getpid()); + #endif + + //Increase our reference count by 1.. + //So that if someone calls `FreeLibrary` before the thread exists, we won't get a crash. + //Later on we will call `FreeLibraryAndExitThread` + HMODULE this_module = nullptr; + GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, reinterpret_cast(module), &this_module); + + std::thread([](HMODULE module){ + auto main_reflector = GetNativeReflector(); + if (main_reflector) + { + #if defined(DEBUG) + console = std::make_unique(); + #endif + + control_center = std::make_unique(getpid(), false, std::move(main_reflector)); + StartHook(); + } + + //exit_process = std::make_unique((void *) GetProcAddress(GetModuleHandleA("kernel32.dll"), "ExitProcess"), (void *) __exit_process); + //exit_process->apply(); + + //Decrease our reference count by 1.. + //So if `FreeLibrary` was called previous, our count reaches 0 and we'll be freed. + FreeLibraryAndExitThread(module, 0); + }, this_module).detach(); +} + +[[gnu::stdcall]] void __unload() +{ + #if defined(DEBUG) + printf("DETACHED FROM: %d\n", getpid()); + #endif +} +#elif defined(__APPLE__) +void __exit_process(int exit_code) +{ + #ifdef DEBUG + console.reset(); + #endif + control_center.reset(); +} + +[[gnu::constructor]] void __load() +{ + #if defined(DEBUG) + printf("ATTACHED TO: %d\n", getpid()); + #endif + + extern void disable_app_nap(); + + std::thread([&] { + #if defined(DEBUG) + console = std::make_unique(); + #endif + + disable_app_nap(); + + auto reflector = GetNativeReflector(); + if (reflector) + { + control_center = std::make_unique(getpid(), false, std::move(reflector)); + StartHook(); + } + + std::atexit([]{ __exit_process(0); }); + }).detach(); +} + +[[gnu::destructor]] void __unload() +{ + #if defined(DEBUG) + printf("DETACHED FROM: %d\n", getpid()); + #endif +} +#else +void __exit_process(int exit_code) +{ + #ifdef DEBUG + console.reset(); + #endif + control_center.reset(); +} + +[[gnu::constructor]] void __load() +{ + #if defined(DEBUG) + printf("ATTACHED TO: %d\n", getpid()); + #endif + + /*Dl_info this_info = {0}; + dladdr(reinterpret_cast(&__load), &this_info); + void* this_module = dlopen(this_info.dli_fname, RTLD_LAZY);*/ + + std::thread([&] { + auto main_reflector = GetNativeReflector(); + if (main_reflector) + { + control_center = std::make_unique(getpid(), false, std::move(main_reflector)); + StartHook(); + } + + std::atexit([]{ __exit_process(0); }); + }).detach(); +} + +[[gnu::destructor]] void __unload() +{ + #if defined(DEBUG) + printf("DETACHED FROM: %d\n", getpid()); + #endif +} +#endif // defined + + +#if defined(_WIN32) || defined(_WIN64) +extern "C" EXPORT BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: + { + module = hinstDLL; + DisableThreadLibraryCalls(hinstDLL); + + __load(); + } + break; + + case DLL_PROCESS_DETACH: + { + __unload(); + } + break; + + case DLL_THREAD_ATTACH: + //printf("ATTACHED\n"); + break; + + case DLL_THREAD_DETACH: + //printf("DETACHED\n"); + break; + } + return TRUE; // succesful +} +#endif // defined diff --git a/third-party/RemoteInput/RemoteInput/Plugin/Plugin.hxx b/third-party/RemoteInput/RemoteInput/Plugin/Plugin.hxx new file mode 100644 index 0000000..2886fe8 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Plugin/Plugin.hxx @@ -0,0 +1,21 @@ +// +// Plugin.hxx +// RemoteInput +// +// Created by Brandon on 2019-12-21. +// Copyright © 2019 XIO. All rights reserved. +// + +#ifndef PLUGIN_HXX_INCLUDED +#define PLUGIN_HXX_INCLUDED + +#include +#include +#include + +#if defined(_WIN32) || defined(_WIN64) +#include +extern HMODULE module; +#endif + +#endif /* PLUGIN_HXX_INCLUDED */ diff --git a/third-party/RemoteInput/RemoteInput/Plugin/Python/Python.cxx b/third-party/RemoteInput/RemoteInput/Plugin/Python/Python.cxx new file mode 100644 index 0000000..7b9a12c --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Plugin/Python/Python.cxx @@ -0,0 +1,5384 @@ +// +// Created by Brandon on 2023-02-16. +// + +#include "Python.hxx" + +#if !defined(USE_PYBIND11) +#include "Platform.hxx" + +void Python::steal(Python& a, Python& b) +{ + a.module = b.module; + b.module = nullptr; + + a.Py_NoneStruct_Ptr = b.Py_NoneStruct_Ptr; + b.Py_NoneStruct_Ptr = nullptr; + + a.Py_TrueStruct_Ptr = b.Py_TrueStruct_Ptr; + b.Py_TrueStruct_Ptr = nullptr; + + a.Py_FalseStruct_Ptr = b.Py_FalseStruct_Ptr; + b.Py_FalseStruct_Ptr = nullptr; + + a.Py_ListType_Ptr = b.Py_ListType_Ptr; + b.Py_ListType_Ptr = nullptr; + + a.PyAIter_Check_Ptr = b.PyAIter_Check_Ptr; + b.PyAIter_Check_Ptr = nullptr; + a.PyArg_Parse_Ptr = b.PyArg_Parse_Ptr; + b.PyArg_Parse_Ptr = nullptr; + a.PyArg_ParseTuple_Ptr = b.PyArg_ParseTuple_Ptr; + b.PyArg_ParseTuple_Ptr = nullptr; + a.PyArg_ParseTupleAndKeywords_Ptr = b.PyArg_ParseTupleAndKeywords_Ptr; + b.PyArg_ParseTupleAndKeywords_Ptr = nullptr; + a.PyArg_UnpackTuple_Ptr = b.PyArg_UnpackTuple_Ptr; + b.PyArg_UnpackTuple_Ptr = nullptr; + a.PyArg_VaParse_Ptr = b.PyArg_VaParse_Ptr; + b.PyArg_VaParse_Ptr = nullptr; + a.PyArg_VaParseTupleAndKeywords_Ptr = b.PyArg_VaParseTupleAndKeywords_Ptr; + b.PyArg_VaParseTupleAndKeywords_Ptr = nullptr; + a.PyArg_ValidateKeywordArguments_Ptr = b.PyArg_ValidateKeywordArguments_Ptr; + b.PyArg_ValidateKeywordArguments_Ptr = nullptr; + a.PyBool_FromLong_Ptr = b.PyBool_FromLong_Ptr; + b.PyBool_FromLong_Ptr = nullptr; + + #if HAS_PYTHON_VERSION(0x030B1000) + a.PyBuffer_FillContiguousStrides_Ptr = b.PyBuffer_FillContiguousStrides_Ptr; + b.PyBuffer_FillContiguousStrides_Ptr = nullptr; + a.PyBuffer_FillInfo_Ptr = b.PyBuffer_FillInfo_Ptr; + b.PyBuffer_FillInfo_Ptr = nullptr; + a.PyBuffer_FromContiguous_Ptr = b.PyBuffer_FromContiguous_Ptr; + b.PyBuffer_FromContiguous_Ptr = nullptr; + a.PyBuffer_GetPointer_Ptr = b.PyBuffer_GetPointer_Ptr; + b.PyBuffer_GetPointer_Ptr = nullptr; + a.PyBuffer_IsContiguous_Ptr = b.PyBuffer_IsContiguous_Ptr; + b.PyBuffer_IsContiguous_Ptr = nullptr; + a.PyBuffer_Release_Ptr = b.PyBuffer_Release_Ptr; + b.PyBuffer_Release_Ptr = nullptr; + a.PyBuffer_SizeFromFormat_Ptr = b.PyBuffer_SizeFromFormat_Ptr; + b.PyBuffer_SizeFromFormat_Ptr = nullptr; + a.PyBuffer_ToContiguous_Ptr = b.PyBuffer_ToContiguous_Ptr; + b.PyBuffer_ToContiguous_Ptr = nullptr; + #endif + + a.PyByteArray_AsString_Ptr = b.PyByteArray_AsString_Ptr; + b.PyByteArray_AsString_Ptr = nullptr; + a.PyByteArray_Concat_Ptr = b.PyByteArray_Concat_Ptr; + b.PyByteArray_Concat_Ptr = nullptr; + a.PyByteArray_FromObject_Ptr = b.PyByteArray_FromObject_Ptr; + b.PyByteArray_FromObject_Ptr = nullptr; + a.PyByteArray_FromStringAndSize_Ptr = b.PyByteArray_FromStringAndSize_Ptr; + b.PyByteArray_FromStringAndSize_Ptr = nullptr; + a.PyByteArray_Resize_Ptr = b.PyByteArray_Resize_Ptr; + b.PyByteArray_Resize_Ptr = nullptr; + a.PyByteArray_Size_Ptr = b.PyByteArray_Size_Ptr; + b.PyByteArray_Size_Ptr = nullptr; + a.PyBytes_AsString_Ptr = b.PyBytes_AsString_Ptr; + b.PyBytes_AsString_Ptr = nullptr; + a.PyBytes_AsStringAndSize_Ptr = b.PyBytes_AsStringAndSize_Ptr; + b.PyBytes_AsStringAndSize_Ptr = nullptr; + a.PyBytes_Concat_Ptr = b.PyBytes_Concat_Ptr; + b.PyBytes_Concat_Ptr = nullptr; + a.PyBytes_ConcatAndDel_Ptr = b.PyBytes_ConcatAndDel_Ptr; + b.PyBytes_ConcatAndDel_Ptr = nullptr; + a.PyBytes_DecodeEscape_Ptr = b.PyBytes_DecodeEscape_Ptr; + b.PyBytes_DecodeEscape_Ptr = nullptr; + a.PyBytes_FromFormat_Ptr = b.PyBytes_FromFormat_Ptr; + b.PyBytes_FromFormat_Ptr = nullptr; + a.PyBytes_FromFormatV_Ptr = b.PyBytes_FromFormatV_Ptr; + b.PyBytes_FromFormatV_Ptr = nullptr; + a.PyBytes_FromObject_Ptr = b.PyBytes_FromObject_Ptr; + b.PyBytes_FromObject_Ptr = nullptr; + a.PyBytes_FromString_Ptr = b.PyBytes_FromString_Ptr; + b.PyBytes_FromString_Ptr = nullptr; + a.PyBytes_FromStringAndSize_Ptr = b.PyBytes_FromStringAndSize_Ptr; + b.PyBytes_FromStringAndSize_Ptr = nullptr; + a.PyBytes_Repr_Ptr = b.PyBytes_Repr_Ptr; + b.PyBytes_Repr_Ptr = nullptr; + a.PyBytes_Size_Ptr = b.PyBytes_Size_Ptr; + b.PyBytes_Size_Ptr = nullptr; + a.PyCFunction_Call_Ptr = b.PyCFunction_Call_Ptr; + b.PyCFunction_Call_Ptr = nullptr; + a.PyCFunction_GetFlags_Ptr = b.PyCFunction_GetFlags_Ptr; + b.PyCFunction_GetFlags_Ptr = nullptr; + a.PyCFunction_GetFunction_Ptr = b.PyCFunction_GetFunction_Ptr; + b.PyCFunction_GetFunction_Ptr = nullptr; + a.PyCFunction_GetSelf_Ptr = b.PyCFunction_GetSelf_Ptr; + b.PyCFunction_GetSelf_Ptr = nullptr; + #ifdef PyCFunction_New + a.PyCFunction_New_Ptr = b.PyCFunction_New_Ptr; + b.PyCFunction_New_Ptr = nullptr; + #endif + #if HAS_PYTHON_VERSION(0x03090000) + a.PyCFunction_NewEx_Ptr = b.PyCFunction_NewEx_Ptr; + b.PyCFunction_NewEx_Ptr = nullptr; + #endif + a.PyCMethod_New_Ptr = b.PyCMethod_New_Ptr; + b.PyCMethod_New_Ptr = nullptr; + a.PyCallIter_New_Ptr = b.PyCallIter_New_Ptr; + b.PyCallIter_New_Ptr = nullptr; + a.PyCallable_Check_Ptr = b.PyCallable_Check_Ptr; + b.PyCallable_Check_Ptr = nullptr; + a.PyCapsule_GetContext_Ptr = b.PyCapsule_GetContext_Ptr; + b.PyCapsule_GetContext_Ptr = nullptr; + a.PyCapsule_GetDestructor_Ptr = b.PyCapsule_GetDestructor_Ptr; + b.PyCapsule_GetDestructor_Ptr = nullptr; + a.PyCapsule_GetName_Ptr = b.PyCapsule_GetName_Ptr; + b.PyCapsule_GetName_Ptr = nullptr; + a.PyCapsule_GetPointer_Ptr = b.PyCapsule_GetPointer_Ptr; + b.PyCapsule_GetPointer_Ptr = nullptr; + a.PyCapsule_Import_Ptr = b.PyCapsule_Import_Ptr; + b.PyCapsule_Import_Ptr = nullptr; + a.PyCapsule_IsValid_Ptr = b.PyCapsule_IsValid_Ptr; + b.PyCapsule_IsValid_Ptr = nullptr; + a.PyCapsule_New_Ptr = b.PyCapsule_New_Ptr; + b.PyCapsule_New_Ptr = nullptr; + a.PyCapsule_SetContext_Ptr = b.PyCapsule_SetContext_Ptr; + b.PyCapsule_SetContext_Ptr = nullptr; + a.PyCapsule_SetDestructor_Ptr = b.PyCapsule_SetDestructor_Ptr; + b.PyCapsule_SetDestructor_Ptr = nullptr; + a.PyCapsule_SetName_Ptr = b.PyCapsule_SetName_Ptr; + b.PyCapsule_SetName_Ptr = nullptr; + a.PyCapsule_SetPointer_Ptr = b.PyCapsule_SetPointer_Ptr; + b.PyCapsule_SetPointer_Ptr = nullptr; + a.PyComplex_FromDoubles_Ptr = b.PyComplex_FromDoubles_Ptr; + b.PyComplex_FromDoubles_Ptr = nullptr; + a.PyComplex_ImagAsDouble_Ptr = b.PyComplex_ImagAsDouble_Ptr; + b.PyComplex_ImagAsDouble_Ptr = nullptr; + a.PyComplex_RealAsDouble_Ptr = b.PyComplex_RealAsDouble_Ptr; + b.PyComplex_RealAsDouble_Ptr = nullptr; + a.PyDescr_NewClassMethod_Ptr = b.PyDescr_NewClassMethod_Ptr; + b.PyDescr_NewClassMethod_Ptr = nullptr; + a.PyDescr_NewGetSet_Ptr = b.PyDescr_NewGetSet_Ptr; + b.PyDescr_NewGetSet_Ptr = nullptr; + a.PyDescr_NewMember_Ptr = b.PyDescr_NewMember_Ptr; + b.PyDescr_NewMember_Ptr = nullptr; + a.PyDescr_NewMethod_Ptr = b.PyDescr_NewMethod_Ptr; + b.PyDescr_NewMethod_Ptr = nullptr; + a.PyDictProxy_New_Ptr = b.PyDictProxy_New_Ptr; + b.PyDictProxy_New_Ptr = nullptr; + a.PyDict_Clear_Ptr = b.PyDict_Clear_Ptr; + b.PyDict_Clear_Ptr = nullptr; + a.PyDict_Contains_Ptr = b.PyDict_Contains_Ptr; + b.PyDict_Contains_Ptr = nullptr; + a.PyDict_Copy_Ptr = b.PyDict_Copy_Ptr; + b.PyDict_Copy_Ptr = nullptr; + a.PyDict_DelItem_Ptr = b.PyDict_DelItem_Ptr; + b.PyDict_DelItem_Ptr = nullptr; + a.PyDict_DelItemString_Ptr = b.PyDict_DelItemString_Ptr; + b.PyDict_DelItemString_Ptr = nullptr; + a.PyDict_GetItem_Ptr = b.PyDict_GetItem_Ptr; + b.PyDict_GetItem_Ptr = nullptr; + a.PyDict_GetItemString_Ptr = b.PyDict_GetItemString_Ptr; + b.PyDict_GetItemString_Ptr = nullptr; + a.PyDict_GetItemWithError_Ptr = b.PyDict_GetItemWithError_Ptr; + b.PyDict_GetItemWithError_Ptr = nullptr; + a.PyDict_Items_Ptr = b.PyDict_Items_Ptr; + b.PyDict_Items_Ptr = nullptr; + a.PyDict_Keys_Ptr = b.PyDict_Keys_Ptr; + b.PyDict_Keys_Ptr = nullptr; + a.PyDict_Merge_Ptr = b.PyDict_Merge_Ptr; + b.PyDict_Merge_Ptr = nullptr; + a.PyDict_MergeFromSeq2_Ptr = b.PyDict_MergeFromSeq2_Ptr; + b.PyDict_MergeFromSeq2_Ptr = nullptr; + a.PyDict_New_Ptr = b.PyDict_New_Ptr; + b.PyDict_New_Ptr = nullptr; + a.PyDict_Next_Ptr = b.PyDict_Next_Ptr; + b.PyDict_Next_Ptr = nullptr; + a.PyDict_SetItem_Ptr = b.PyDict_SetItem_Ptr; + b.PyDict_SetItem_Ptr = nullptr; + a.PyDict_SetItemString_Ptr = b.PyDict_SetItemString_Ptr; + b.PyDict_SetItemString_Ptr = nullptr; + a.PyDict_Size_Ptr = b.PyDict_Size_Ptr; + b.PyDict_Size_Ptr = nullptr; + a.PyDict_Update_Ptr = b.PyDict_Update_Ptr; + b.PyDict_Update_Ptr = nullptr; + a.PyDict_Values_Ptr = b.PyDict_Values_Ptr; + b.PyDict_Values_Ptr = nullptr; + a.PyErr_BadArgument_Ptr = b.PyErr_BadArgument_Ptr; + b.PyErr_BadArgument_Ptr = nullptr; + a._PyErr_BadInternalCall_Ptr = b._PyErr_BadInternalCall_Ptr; + b._PyErr_BadInternalCall_Ptr = nullptr; + a.PyErr_CheckSignals_Ptr = b.PyErr_CheckSignals_Ptr; + b.PyErr_CheckSignals_Ptr = nullptr; + a.PyErr_Clear_Ptr = b.PyErr_Clear_Ptr; + b.PyErr_Clear_Ptr = nullptr; + a.PyErr_Display_Ptr = b.PyErr_Display_Ptr; + b.PyErr_Display_Ptr = nullptr; + a.PyErr_ExceptionMatches_Ptr = b.PyErr_ExceptionMatches_Ptr; + b.PyErr_ExceptionMatches_Ptr = nullptr; + a.PyErr_Fetch_Ptr = b.PyErr_Fetch_Ptr; + b.PyErr_Fetch_Ptr = nullptr; + a.PyErr_Format_Ptr = b.PyErr_Format_Ptr; + b.PyErr_Format_Ptr = nullptr; + a.PyErr_FormatV_Ptr = b.PyErr_FormatV_Ptr; + b.PyErr_FormatV_Ptr = nullptr; + a.PyErr_GetExcInfo_Ptr = b.PyErr_GetExcInfo_Ptr; + b.PyErr_GetExcInfo_Ptr = nullptr; + a.PyErr_GetHandledException_Ptr = b.PyErr_GetHandledException_Ptr; + b.PyErr_GetHandledException_Ptr = nullptr; + a.PyErr_GivenExceptionMatches_Ptr = b.PyErr_GivenExceptionMatches_Ptr; + b.PyErr_GivenExceptionMatches_Ptr = nullptr; + a.PyErr_NewException_Ptr = b.PyErr_NewException_Ptr; + b.PyErr_NewException_Ptr = nullptr; + a.PyErr_NewExceptionWithDoc_Ptr = b.PyErr_NewExceptionWithDoc_Ptr; + b.PyErr_NewExceptionWithDoc_Ptr = nullptr; + a.PyErr_NoMemory_Ptr = b.PyErr_NoMemory_Ptr; + b.PyErr_NoMemory_Ptr = nullptr; + a.PyErr_NormalizeException_Ptr = b.PyErr_NormalizeException_Ptr; + b.PyErr_NormalizeException_Ptr = nullptr; + a.PyErr_Occurred_Ptr = b.PyErr_Occurred_Ptr; + b.PyErr_Occurred_Ptr = nullptr; + a.PyErr_Print_Ptr = b.PyErr_Print_Ptr; + b.PyErr_Print_Ptr = nullptr; + a.PyErr_PrintEx_Ptr = b.PyErr_PrintEx_Ptr; + b.PyErr_PrintEx_Ptr = nullptr; + a.PyErr_ProgramText_Ptr = b.PyErr_ProgramText_Ptr; + b.PyErr_ProgramText_Ptr = nullptr; + a.PyErr_ResourceWarning_Ptr = b.PyErr_ResourceWarning_Ptr; + b.PyErr_ResourceWarning_Ptr = nullptr; + a.PyErr_Restore_Ptr = b.PyErr_Restore_Ptr; + b.PyErr_Restore_Ptr = nullptr; + a.PyErr_SetExcInfo_Ptr = b.PyErr_SetExcInfo_Ptr; + b.PyErr_SetExcInfo_Ptr = nullptr; + a.PyErr_SetFromErrno_Ptr = b.PyErr_SetFromErrno_Ptr; + b.PyErr_SetFromErrno_Ptr = nullptr; + a.PyErr_SetFromErrnoWithFilename_Ptr = b.PyErr_SetFromErrnoWithFilename_Ptr; + b.PyErr_SetFromErrnoWithFilename_Ptr = nullptr; + a.PyErr_SetFromErrnoWithFilenameObject_Ptr = b.PyErr_SetFromErrnoWithFilenameObject_Ptr; + b.PyErr_SetFromErrnoWithFilenameObject_Ptr = nullptr; + a.PyErr_SetFromErrnoWithFilenameObjects_Ptr = b.PyErr_SetFromErrnoWithFilenameObjects_Ptr; + b.PyErr_SetFromErrnoWithFilenameObjects_Ptr = nullptr; + a.PyErr_SetHandledException_Ptr = b.PyErr_SetHandledException_Ptr; + b.PyErr_SetHandledException_Ptr = nullptr; + a.PyErr_SetImportError_Ptr = b.PyErr_SetImportError_Ptr; + b.PyErr_SetImportError_Ptr = nullptr; + a.PyErr_SetImportErrorSubclass_Ptr = b.PyErr_SetImportErrorSubclass_Ptr; + b.PyErr_SetImportErrorSubclass_Ptr = nullptr; + a.PyErr_SetInterrupt_Ptr = b.PyErr_SetInterrupt_Ptr; + b.PyErr_SetInterrupt_Ptr = nullptr; + a.PyErr_SetInterruptEx_Ptr = b.PyErr_SetInterruptEx_Ptr; + b.PyErr_SetInterruptEx_Ptr = nullptr; + a.PyErr_SetNone_Ptr = b.PyErr_SetNone_Ptr; + b.PyErr_SetNone_Ptr = nullptr; + a.PyErr_SetObject_Ptr = b.PyErr_SetObject_Ptr; + b.PyErr_SetObject_Ptr = nullptr; + a.PyErr_SetString_Ptr = b.PyErr_SetString_Ptr; + b.PyErr_SetString_Ptr = nullptr; + a.PyErr_SyntaxLocation_Ptr = b.PyErr_SyntaxLocation_Ptr; + b.PyErr_SyntaxLocation_Ptr = nullptr; + a.PyErr_SyntaxLocationEx_Ptr = b.PyErr_SyntaxLocationEx_Ptr; + b.PyErr_SyntaxLocationEx_Ptr = nullptr; + a.PyErr_WarnEx_Ptr = b.PyErr_WarnEx_Ptr; + b.PyErr_WarnEx_Ptr = nullptr; + a.PyErr_WarnExplicit_Ptr = b.PyErr_WarnExplicit_Ptr; + b.PyErr_WarnExplicit_Ptr = nullptr; + a.PyErr_WarnFormat_Ptr = b.PyErr_WarnFormat_Ptr; + b.PyErr_WarnFormat_Ptr = nullptr; + a.PyErr_WriteUnraisable_Ptr = b.PyErr_WriteUnraisable_Ptr; + b.PyErr_WriteUnraisable_Ptr = nullptr; + a.PyEval_AcquireLock_Ptr = b.PyEval_AcquireLock_Ptr; + b.PyEval_AcquireLock_Ptr = nullptr; + a.PyEval_AcquireThread_Ptr = b.PyEval_AcquireThread_Ptr; + b.PyEval_AcquireThread_Ptr = nullptr; + a.PyEval_CallFunction_Ptr = b.PyEval_CallFunction_Ptr; + b.PyEval_CallFunction_Ptr = nullptr; + a.PyEval_CallMethod_Ptr = b.PyEval_CallMethod_Ptr; + b.PyEval_CallMethod_Ptr = nullptr; + a.PyEval_CallObjectWithKeywords_Ptr = b.PyEval_CallObjectWithKeywords_Ptr; + b.PyEval_CallObjectWithKeywords_Ptr = nullptr; + a.PyEval_EvalCode_Ptr = b.PyEval_EvalCode_Ptr; + b.PyEval_EvalCode_Ptr = nullptr; + a.PyEval_EvalCodeEx_Ptr = b.PyEval_EvalCodeEx_Ptr; + b.PyEval_EvalCodeEx_Ptr = nullptr; + #if HAS_PYTHON_VERSION(0x03090000) + a.PyEval_EvalFrame_Ptr = b.PyEval_EvalFrame_Ptr; + b.PyEval_EvalFrame_Ptr = nullptr; + a.PyEval_EvalFrameEx_Ptr = b.PyEval_EvalFrameEx_Ptr; + b.PyEval_EvalFrameEx_Ptr = nullptr; + #endif + a.PyEval_GetBuiltins_Ptr = b.PyEval_GetBuiltins_Ptr; + b.PyEval_GetBuiltins_Ptr = nullptr; + #if HAS_PYTHON_VERSION(0x03090000) + a.PyEval_GetFrame_Ptr = b.PyEval_GetFrame_Ptr; + b.PyEval_GetFrame_Ptr = nullptr; + #endif + a.PyEval_GetFuncDesc_Ptr = b.PyEval_GetFuncDesc_Ptr; + b.PyEval_GetFuncDesc_Ptr = nullptr; + a.PyEval_GetFuncName_Ptr = b.PyEval_GetFuncName_Ptr; + b.PyEval_GetFuncName_Ptr = nullptr; + a.PyEval_GetGlobals_Ptr = b.PyEval_GetGlobals_Ptr; + b.PyEval_GetGlobals_Ptr = nullptr; + a.PyEval_GetLocals_Ptr = b.PyEval_GetLocals_Ptr; + b.PyEval_GetLocals_Ptr = nullptr; + a.PyEval_InitThreads_Ptr = b.PyEval_InitThreads_Ptr; + b.PyEval_InitThreads_Ptr = nullptr; + a.PyEval_ReleaseLock_Ptr = b.PyEval_ReleaseLock_Ptr; + b.PyEval_ReleaseLock_Ptr = nullptr; + a.PyEval_ReleaseThread_Ptr = b.PyEval_ReleaseThread_Ptr; + b.PyEval_ReleaseThread_Ptr = nullptr; + a.PyEval_RestoreThread_Ptr = b.PyEval_RestoreThread_Ptr; + b.PyEval_RestoreThread_Ptr = nullptr; + a.PyEval_SaveThread_Ptr = b.PyEval_SaveThread_Ptr; + b.PyEval_SaveThread_Ptr = nullptr; + a.PyEval_ThreadsInitialized_Ptr = b.PyEval_ThreadsInitialized_Ptr; + b.PyEval_ThreadsInitialized_Ptr = nullptr; + a.PyExceptionClass_Name_Ptr = b.PyExceptionClass_Name_Ptr; + b.PyExceptionClass_Name_Ptr = nullptr; + a.PyException_GetCause_Ptr = b.PyException_GetCause_Ptr; + b.PyException_GetCause_Ptr = nullptr; + a.PyException_GetContext_Ptr = b.PyException_GetContext_Ptr; + b.PyException_GetContext_Ptr = nullptr; + a.PyException_GetTraceback_Ptr = b.PyException_GetTraceback_Ptr; + b.PyException_GetTraceback_Ptr = nullptr; + a.PyException_SetCause_Ptr = b.PyException_SetCause_Ptr; + b.PyException_SetCause_Ptr = nullptr; + a.PyException_SetContext_Ptr = b.PyException_SetContext_Ptr; + b.PyException_SetContext_Ptr = nullptr; + a.PyException_SetTraceback_Ptr = b.PyException_SetTraceback_Ptr; + b.PyException_SetTraceback_Ptr = nullptr; + a.PyFile_FromFd_Ptr = b.PyFile_FromFd_Ptr; + b.PyFile_FromFd_Ptr = nullptr; + a.PyFile_GetLine_Ptr = b.PyFile_GetLine_Ptr; + b.PyFile_GetLine_Ptr = nullptr; + a.PyFile_WriteObject_Ptr = b.PyFile_WriteObject_Ptr; + b.PyFile_WriteObject_Ptr = nullptr; + a.PyFile_WriteString_Ptr = b.PyFile_WriteString_Ptr; + b.PyFile_WriteString_Ptr = nullptr; + a.PyFloat_AsDouble_Ptr = b.PyFloat_AsDouble_Ptr; + b.PyFloat_AsDouble_Ptr = nullptr; + a.PyFloat_FromDouble_Ptr = b.PyFloat_FromDouble_Ptr; + b.PyFloat_FromDouble_Ptr = nullptr; + a.PyFloat_FromString_Ptr = b.PyFloat_FromString_Ptr; + b.PyFloat_FromString_Ptr = nullptr; + a.PyFloat_GetInfo_Ptr = b.PyFloat_GetInfo_Ptr; + b.PyFloat_GetInfo_Ptr = nullptr; + a.PyFloat_GetMax_Ptr = b.PyFloat_GetMax_Ptr; + b.PyFloat_GetMax_Ptr = nullptr; + a.PyFloat_GetMin_Ptr = b.PyFloat_GetMin_Ptr; + b.PyFloat_GetMin_Ptr = nullptr; + a.PyFrozenSet_New_Ptr = b.PyFrozenSet_New_Ptr; + b.PyFrozenSet_New_Ptr = nullptr; + a.PyGC_Collect_Ptr = b.PyGC_Collect_Ptr; + b.PyGC_Collect_Ptr = nullptr; + a.PyGC_Disable_Ptr = b.PyGC_Disable_Ptr; + b.PyGC_Disable_Ptr = nullptr; + a.PyGC_Enable_Ptr = b.PyGC_Enable_Ptr; + b.PyGC_Enable_Ptr = nullptr; + a.PyGC_IsEnabled_Ptr = b.PyGC_IsEnabled_Ptr; + b.PyGC_IsEnabled_Ptr = nullptr; + a.PyGILState_Ensure_Ptr = b.PyGILState_Ensure_Ptr; + b.PyGILState_Ensure_Ptr = nullptr; + a.PyGILState_GetThisThreadState_Ptr = b.PyGILState_GetThisThreadState_Ptr; + b.PyGILState_GetThisThreadState_Ptr = nullptr; + a.PyGILState_Release_Ptr = b.PyGILState_Release_Ptr; + b.PyGILState_Release_Ptr = nullptr; + a.PyImport_AddModule_Ptr = b.PyImport_AddModule_Ptr; + b.PyImport_AddModule_Ptr = nullptr; + a.PyImport_AddModuleObject_Ptr = b.PyImport_AddModuleObject_Ptr; + b.PyImport_AddModuleObject_Ptr = nullptr; + a.PyImport_AppendInittab_Ptr = b.PyImport_AppendInittab_Ptr; + b.PyImport_AppendInittab_Ptr = nullptr; + a.PyImport_ExecCodeModule_Ptr = b.PyImport_ExecCodeModule_Ptr; + b.PyImport_ExecCodeModule_Ptr = nullptr; + a.PyImport_ExecCodeModuleEx_Ptr = b.PyImport_ExecCodeModuleEx_Ptr; + b.PyImport_ExecCodeModuleEx_Ptr = nullptr; + a.PyImport_ExecCodeModuleObject_Ptr = b.PyImport_ExecCodeModuleObject_Ptr; + b.PyImport_ExecCodeModuleObject_Ptr = nullptr; + a.PyImport_ExecCodeModuleWithPathnames_Ptr = b.PyImport_ExecCodeModuleWithPathnames_Ptr; + b.PyImport_ExecCodeModuleWithPathnames_Ptr = nullptr; + a.PyImport_GetImporter_Ptr = b.PyImport_GetImporter_Ptr; + b.PyImport_GetImporter_Ptr = nullptr; + a.PyImport_GetMagicNumber_Ptr = b.PyImport_GetMagicNumber_Ptr; + b.PyImport_GetMagicNumber_Ptr = nullptr; + a.PyImport_GetMagicTag_Ptr = b.PyImport_GetMagicTag_Ptr; + b.PyImport_GetMagicTag_Ptr = nullptr; + a.PyImport_GetModule_Ptr = b.PyImport_GetModule_Ptr; + b.PyImport_GetModule_Ptr = nullptr; + a.PyImport_GetModuleDict_Ptr = b.PyImport_GetModuleDict_Ptr; + b.PyImport_GetModuleDict_Ptr = nullptr; + a.PyImport_Import_Ptr = b.PyImport_Import_Ptr; + b.PyImport_Import_Ptr = nullptr; + a.PyImport_ImportFrozenModule_Ptr = b.PyImport_ImportFrozenModule_Ptr; + b.PyImport_ImportFrozenModule_Ptr = nullptr; + a.PyImport_ImportFrozenModuleObject_Ptr = b.PyImport_ImportFrozenModuleObject_Ptr; + b.PyImport_ImportFrozenModuleObject_Ptr = nullptr; + a.PyImport_ImportModule_Ptr = b.PyImport_ImportModule_Ptr; + b.PyImport_ImportModule_Ptr = nullptr; + a.PyImport_ImportModuleLevel_Ptr = b.PyImport_ImportModuleLevel_Ptr; + b.PyImport_ImportModuleLevel_Ptr = nullptr; + a.PyImport_ImportModuleLevelObject_Ptr = b.PyImport_ImportModuleLevelObject_Ptr; + b.PyImport_ImportModuleLevelObject_Ptr = nullptr; + a.PyImport_ImportModuleNoBlock_Ptr = b.PyImport_ImportModuleNoBlock_Ptr; + b.PyImport_ImportModuleNoBlock_Ptr = nullptr; + a.PyImport_ReloadModule_Ptr = b.PyImport_ReloadModule_Ptr; + b.PyImport_ReloadModule_Ptr = nullptr; + a.PyIndex_Check_Ptr = b.PyIndex_Check_Ptr; + b.PyIndex_Check_Ptr = nullptr; + a.PyInterpreterState_Clear_Ptr = b.PyInterpreterState_Clear_Ptr; + b.PyInterpreterState_Clear_Ptr = nullptr; + a.PyInterpreterState_Delete_Ptr = b.PyInterpreterState_Delete_Ptr; + b.PyInterpreterState_Delete_Ptr = nullptr; + a.PyInterpreterState_Get_Ptr = b.PyInterpreterState_Get_Ptr; + b.PyInterpreterState_Get_Ptr = nullptr; + a.PyInterpreterState_GetDict_Ptr = b.PyInterpreterState_GetDict_Ptr; + b.PyInterpreterState_GetDict_Ptr = nullptr; + a.PyInterpreterState_GetID_Ptr = b.PyInterpreterState_GetID_Ptr; + b.PyInterpreterState_GetID_Ptr = nullptr; + a.PyInterpreterState_New_Ptr = b.PyInterpreterState_New_Ptr; + b.PyInterpreterState_New_Ptr = nullptr; + a.PyIter_Check_Ptr = b.PyIter_Check_Ptr; + b.PyIter_Check_Ptr = nullptr; + a.PyIter_Next_Ptr = b.PyIter_Next_Ptr; + b.PyIter_Next_Ptr = nullptr; + #if HAS_PYTHON_VERSION(0x030A0000) + a.PyIter_Send_Ptr = b.PyIter_Send_Ptr; + b.PyIter_Send_Ptr = nullptr; + #endif + a.PyList_Append_Ptr = b.PyList_Append_Ptr; + b.PyList_Append_Ptr = nullptr; + a.PyList_AsTuple_Ptr = b.PyList_AsTuple_Ptr; + b.PyList_AsTuple_Ptr = nullptr; + a.PyList_GetItem_Ptr = b.PyList_GetItem_Ptr; + b.PyList_GetItem_Ptr = nullptr; + a.PyList_GetSlice_Ptr = b.PyList_GetSlice_Ptr; + b.PyList_GetSlice_Ptr = nullptr; + a.PyList_Insert_Ptr = b.PyList_Insert_Ptr; + b.PyList_Insert_Ptr = nullptr; + a.PyList_New_Ptr = b.PyList_New_Ptr; + b.PyList_New_Ptr = nullptr; + a.PyList_Reverse_Ptr = b.PyList_Reverse_Ptr; + b.PyList_Reverse_Ptr = nullptr; + a.PyList_SetItem_Ptr = b.PyList_SetItem_Ptr; + b.PyList_SetItem_Ptr = nullptr; + a.PyList_SetSlice_Ptr = b.PyList_SetSlice_Ptr; + b.PyList_SetSlice_Ptr = nullptr; + a.PyList_Size_Ptr = b.PyList_Size_Ptr; + b.PyList_Size_Ptr = nullptr; + a.PyList_Sort_Ptr = b.PyList_Sort_Ptr; + b.PyList_Sort_Ptr = nullptr; + a.PyLong_AsDouble_Ptr = b.PyLong_AsDouble_Ptr; + b.PyLong_AsDouble_Ptr = nullptr; + a.PyLong_AsLong_Ptr = b.PyLong_AsLong_Ptr; + b.PyLong_AsLong_Ptr = nullptr; + a.PyLong_AsLongAndOverflow_Ptr = b.PyLong_AsLongAndOverflow_Ptr; + b.PyLong_AsLongAndOverflow_Ptr = nullptr; + a.PyLong_AsLongLong_Ptr = b.PyLong_AsLongLong_Ptr; + b.PyLong_AsLongLong_Ptr = nullptr; + a.PyLong_AsLongLongAndOverflow_Ptr = b.PyLong_AsLongLongAndOverflow_Ptr; + b.PyLong_AsLongLongAndOverflow_Ptr = nullptr; + a.PyLong_AsSize_t_Ptr = b.PyLong_AsSize_t_Ptr; + b.PyLong_AsSize_t_Ptr = nullptr; + a.PyLong_AsSsize_t_Ptr = b.PyLong_AsSsize_t_Ptr; + b.PyLong_AsSsize_t_Ptr = nullptr; + a.PyLong_AsUnsignedLong_Ptr = b.PyLong_AsUnsignedLong_Ptr; + b.PyLong_AsUnsignedLong_Ptr = nullptr; + a.PyLong_AsUnsignedLongLong_Ptr = b.PyLong_AsUnsignedLongLong_Ptr; + b.PyLong_AsUnsignedLongLong_Ptr = nullptr; + a.PyLong_AsUnsignedLongLongMask_Ptr = b.PyLong_AsUnsignedLongLongMask_Ptr; + b.PyLong_AsUnsignedLongLongMask_Ptr = nullptr; + a.PyLong_AsUnsignedLongMask_Ptr = b.PyLong_AsUnsignedLongMask_Ptr; + b.PyLong_AsUnsignedLongMask_Ptr = nullptr; + a.PyLong_AsVoidPtr_Ptr = b.PyLong_AsVoidPtr_Ptr; + b.PyLong_AsVoidPtr_Ptr = nullptr; + a.PyLong_FromDouble_Ptr = b.PyLong_FromDouble_Ptr; + b.PyLong_FromDouble_Ptr = nullptr; + a.PyLong_FromLong_Ptr = b.PyLong_FromLong_Ptr; + b.PyLong_FromLong_Ptr = nullptr; + a.PyLong_FromLongLong_Ptr = b.PyLong_FromLongLong_Ptr; + b.PyLong_FromLongLong_Ptr = nullptr; + a.PyLong_FromSize_t_Ptr = b.PyLong_FromSize_t_Ptr; + b.PyLong_FromSize_t_Ptr = nullptr; + a.PyLong_FromSsize_t_Ptr = b.PyLong_FromSsize_t_Ptr; + b.PyLong_FromSsize_t_Ptr = nullptr; + a.PyLong_FromString_Ptr = b.PyLong_FromString_Ptr; + b.PyLong_FromString_Ptr = nullptr; + a.PyLong_FromUnsignedLong_Ptr = b.PyLong_FromUnsignedLong_Ptr; + b.PyLong_FromUnsignedLong_Ptr = nullptr; + a.PyLong_FromUnsignedLongLong_Ptr = b.PyLong_FromUnsignedLongLong_Ptr; + b.PyLong_FromUnsignedLongLong_Ptr = nullptr; + a.PyLong_FromVoidPtr_Ptr = b.PyLong_FromVoidPtr_Ptr; + b.PyLong_FromVoidPtr_Ptr = nullptr; + a.PyLong_GetInfo_Ptr = b.PyLong_GetInfo_Ptr; + b.PyLong_GetInfo_Ptr = nullptr; + a.PyMapping_Check_Ptr = b.PyMapping_Check_Ptr; + b.PyMapping_Check_Ptr = nullptr; + a.PyMapping_GetItemString_Ptr = b.PyMapping_GetItemString_Ptr; + b.PyMapping_GetItemString_Ptr = nullptr; + a.PyMapping_HasKey_Ptr = b.PyMapping_HasKey_Ptr; + b.PyMapping_HasKey_Ptr = nullptr; + a.PyMapping_HasKeyString_Ptr = b.PyMapping_HasKeyString_Ptr; + b.PyMapping_HasKeyString_Ptr = nullptr; + a.PyMapping_Items_Ptr = b.PyMapping_Items_Ptr; + b.PyMapping_Items_Ptr = nullptr; + a.PyMapping_Keys_Ptr = b.PyMapping_Keys_Ptr; + b.PyMapping_Keys_Ptr = nullptr; + a.PyMapping_Length_Ptr = b.PyMapping_Length_Ptr; + b.PyMapping_Length_Ptr = nullptr; + a.PyMapping_SetItemString_Ptr = b.PyMapping_SetItemString_Ptr; + b.PyMapping_SetItemString_Ptr = nullptr; + a.PyMapping_Size_Ptr = b.PyMapping_Size_Ptr; + b.PyMapping_Size_Ptr = nullptr; + a.PyMapping_Values_Ptr = b.PyMapping_Values_Ptr; + b.PyMapping_Values_Ptr = nullptr; + a.PyMem_Calloc_Ptr = b.PyMem_Calloc_Ptr; + b.PyMem_Calloc_Ptr = nullptr; + a.PyMem_Free_Ptr = b.PyMem_Free_Ptr; + b.PyMem_Free_Ptr = nullptr; + a.PyMem_Malloc_Ptr = b.PyMem_Malloc_Ptr; + b.PyMem_Malloc_Ptr = nullptr; + a.PyMem_Realloc_Ptr = b.PyMem_Realloc_Ptr; + b.PyMem_Realloc_Ptr = nullptr; + #if HAS_PYTHON_VERSION(0x030B1000) + a.PyMemoryView_FromBuffer_Ptr = b.PyMemoryView_FromBuffer_Ptr; + b.PyMemoryView_FromBuffer_Ptr = nullptr; + #endif + a.PyMemoryView_FromMemory_Ptr = b.PyMemoryView_FromMemory_Ptr; + b.PyMemoryView_FromMemory_Ptr = nullptr; + a.PyMemoryView_FromObject_Ptr = b.PyMemoryView_FromObject_Ptr; + b.PyMemoryView_FromObject_Ptr = nullptr; + a.PyMemoryView_GetContiguous_Ptr = b.PyMemoryView_GetContiguous_Ptr; + b.PyMemoryView_GetContiguous_Ptr = nullptr; + a.PyModuleDef_Init_Ptr = b.PyModuleDef_Init_Ptr; + b.PyModuleDef_Init_Ptr = nullptr; + a.PyModule_AddFunctions_Ptr = b.PyModule_AddFunctions_Ptr; + b.PyModule_AddFunctions_Ptr = nullptr; + a.PyModule_AddIntConstant_Ptr = b.PyModule_AddIntConstant_Ptr; + b.PyModule_AddIntConstant_Ptr = nullptr; + a.PyModule_AddObject_Ptr = b.PyModule_AddObject_Ptr; + b.PyModule_AddObject_Ptr = nullptr; + a.PyModule_AddObjectRef_Ptr = b.PyModule_AddObjectRef_Ptr; + b.PyModule_AddObjectRef_Ptr = nullptr; + a.PyModule_AddStringConstant_Ptr = b.PyModule_AddStringConstant_Ptr; + b.PyModule_AddStringConstant_Ptr = nullptr; + a.PyModule_AddType_Ptr = b.PyModule_AddType_Ptr; + b.PyModule_AddType_Ptr = nullptr; + a.PyModule_Create2_Ptr = b.PyModule_Create2_Ptr; + b.PyModule_Create2_Ptr = nullptr; + a.PyModule_ExecDef_Ptr = b.PyModule_ExecDef_Ptr; + b.PyModule_ExecDef_Ptr = nullptr; + a.PyModule_FromDefAndSpec2_Ptr = b.PyModule_FromDefAndSpec2_Ptr; + b.PyModule_FromDefAndSpec2_Ptr = nullptr; + a.PyModule_GetDef_Ptr = b.PyModule_GetDef_Ptr; + b.PyModule_GetDef_Ptr = nullptr; + a.PyModule_GetDict_Ptr = b.PyModule_GetDict_Ptr; + b.PyModule_GetDict_Ptr = nullptr; + a.PyModule_GetFilename_Ptr = b.PyModule_GetFilename_Ptr; + b.PyModule_GetFilename_Ptr = nullptr; + a.PyModule_GetFilenameObject_Ptr = b.PyModule_GetFilenameObject_Ptr; + b.PyModule_GetFilenameObject_Ptr = nullptr; + a.PyModule_GetName_Ptr = b.PyModule_GetName_Ptr; + b.PyModule_GetName_Ptr = nullptr; + a.PyModule_GetNameObject_Ptr = b.PyModule_GetNameObject_Ptr; + b.PyModule_GetNameObject_Ptr = nullptr; + a.PyModule_GetState_Ptr = b.PyModule_GetState_Ptr; + b.PyModule_GetState_Ptr = nullptr; + a.PyModule_New_Ptr = b.PyModule_New_Ptr; + b.PyModule_New_Ptr = nullptr; + a.PyModule_NewObject_Ptr = b.PyModule_NewObject_Ptr; + b.PyModule_NewObject_Ptr = nullptr; + a.PyModule_SetDocString_Ptr = b.PyModule_SetDocString_Ptr; + b.PyModule_SetDocString_Ptr = nullptr; + a.PyNumber_Absolute_Ptr = b.PyNumber_Absolute_Ptr; + b.PyNumber_Absolute_Ptr = nullptr; + a.PyNumber_Add_Ptr = b.PyNumber_Add_Ptr; + b.PyNumber_Add_Ptr = nullptr; + a.PyNumber_And_Ptr = b.PyNumber_And_Ptr; + b.PyNumber_And_Ptr = nullptr; + a.PyNumber_AsSsize_t_Ptr = b.PyNumber_AsSsize_t_Ptr; + b.PyNumber_AsSsize_t_Ptr = nullptr; + a.PyNumber_Check_Ptr = b.PyNumber_Check_Ptr; + b.PyNumber_Check_Ptr = nullptr; + a.PyNumber_Divmod_Ptr = b.PyNumber_Divmod_Ptr; + b.PyNumber_Divmod_Ptr = nullptr; + a.PyNumber_Float_Ptr = b.PyNumber_Float_Ptr; + b.PyNumber_Float_Ptr = nullptr; + a.PyNumber_FloorDivide_Ptr = b.PyNumber_FloorDivide_Ptr; + b.PyNumber_FloorDivide_Ptr = nullptr; + a.PyNumber_InPlaceAdd_Ptr = b.PyNumber_InPlaceAdd_Ptr; + b.PyNumber_InPlaceAdd_Ptr = nullptr; + a.PyNumber_InPlaceAnd_Ptr = b.PyNumber_InPlaceAnd_Ptr; + b.PyNumber_InPlaceAnd_Ptr = nullptr; + a.PyNumber_InPlaceFloorDivide_Ptr = b.PyNumber_InPlaceFloorDivide_Ptr; + b.PyNumber_InPlaceFloorDivide_Ptr = nullptr; + a.PyNumber_InPlaceLshift_Ptr = b.PyNumber_InPlaceLshift_Ptr; + b.PyNumber_InPlaceLshift_Ptr = nullptr; + a.PyNumber_InPlaceMatrixMultiply_Ptr = b.PyNumber_InPlaceMatrixMultiply_Ptr; + b.PyNumber_InPlaceMatrixMultiply_Ptr = nullptr; + a.PyNumber_InPlaceMultiply_Ptr = b.PyNumber_InPlaceMultiply_Ptr; + b.PyNumber_InPlaceMultiply_Ptr = nullptr; + a.PyNumber_InPlaceOr_Ptr = b.PyNumber_InPlaceOr_Ptr; + b.PyNumber_InPlaceOr_Ptr = nullptr; + a.PyNumber_InPlacePower_Ptr = b.PyNumber_InPlacePower_Ptr; + b.PyNumber_InPlacePower_Ptr = nullptr; + a.PyNumber_InPlaceRemainder_Ptr = b.PyNumber_InPlaceRemainder_Ptr; + b.PyNumber_InPlaceRemainder_Ptr = nullptr; + a.PyNumber_InPlaceRshift_Ptr = b.PyNumber_InPlaceRshift_Ptr; + b.PyNumber_InPlaceRshift_Ptr = nullptr; + a.PyNumber_InPlaceSubtract_Ptr = b.PyNumber_InPlaceSubtract_Ptr; + b.PyNumber_InPlaceSubtract_Ptr = nullptr; + a.PyNumber_InPlaceTrueDivide_Ptr = b.PyNumber_InPlaceTrueDivide_Ptr; + b.PyNumber_InPlaceTrueDivide_Ptr = nullptr; + a.PyNumber_InPlaceXor_Ptr = b.PyNumber_InPlaceXor_Ptr; + b.PyNumber_InPlaceXor_Ptr = nullptr; + a.PyNumber_Index_Ptr = b.PyNumber_Index_Ptr; + b.PyNumber_Index_Ptr = nullptr; + a.PyNumber_Invert_Ptr = b.PyNumber_Invert_Ptr; + b.PyNumber_Invert_Ptr = nullptr; + a.PyNumber_Long_Ptr = b.PyNumber_Long_Ptr; + b.PyNumber_Long_Ptr = nullptr; + a.PyNumber_Lshift_Ptr = b.PyNumber_Lshift_Ptr; + b.PyNumber_Lshift_Ptr = nullptr; + a.PyNumber_MatrixMultiply_Ptr = b.PyNumber_MatrixMultiply_Ptr; + b.PyNumber_MatrixMultiply_Ptr = nullptr; + a.PyNumber_Multiply_Ptr = b.PyNumber_Multiply_Ptr; + b.PyNumber_Multiply_Ptr = nullptr; + a.PyNumber_Negative_Ptr = b.PyNumber_Negative_Ptr; + b.PyNumber_Negative_Ptr = nullptr; + a.PyNumber_Or_Ptr = b.PyNumber_Or_Ptr; + b.PyNumber_Or_Ptr = nullptr; + a.PyNumber_Positive_Ptr = b.PyNumber_Positive_Ptr; + b.PyNumber_Positive_Ptr = nullptr; + a.PyNumber_Power_Ptr = b.PyNumber_Power_Ptr; + b.PyNumber_Power_Ptr = nullptr; + a.PyNumber_Remainder_Ptr = b.PyNumber_Remainder_Ptr; + b.PyNumber_Remainder_Ptr = nullptr; + a.PyNumber_Rshift_Ptr = b.PyNumber_Rshift_Ptr; + b.PyNumber_Rshift_Ptr = nullptr; + a.PyNumber_Subtract_Ptr = b.PyNumber_Subtract_Ptr; + b.PyNumber_Subtract_Ptr = nullptr; + a.PyNumber_ToBase_Ptr = b.PyNumber_ToBase_Ptr; + b.PyNumber_ToBase_Ptr = nullptr; + a.PyNumber_TrueDivide_Ptr = b.PyNumber_TrueDivide_Ptr; + b.PyNumber_TrueDivide_Ptr = nullptr; + a.PyNumber_Xor_Ptr = b.PyNumber_Xor_Ptr; + b.PyNumber_Xor_Ptr = nullptr; + a.PyOS_AfterFork_Ptr = b.PyOS_AfterFork_Ptr; + b.PyOS_AfterFork_Ptr = nullptr; + a.PyOS_AfterFork_Child_Ptr = b.PyOS_AfterFork_Child_Ptr; + b.PyOS_AfterFork_Child_Ptr = nullptr; + a.PyOS_AfterFork_Parent_Ptr = b.PyOS_AfterFork_Parent_Ptr; + b.PyOS_AfterFork_Parent_Ptr = nullptr; + a.PyOS_BeforeFork_Ptr = b.PyOS_BeforeFork_Ptr; + b.PyOS_BeforeFork_Ptr = nullptr; + a.PyOS_FSPath_Ptr = b.PyOS_FSPath_Ptr; + b.PyOS_FSPath_Ptr = nullptr; + a.PyOS_InterruptOccurred_Ptr = b.PyOS_InterruptOccurred_Ptr; + b.PyOS_InterruptOccurred_Ptr = nullptr; + a.PyOS_double_to_string_Ptr = b.PyOS_double_to_string_Ptr; + b.PyOS_double_to_string_Ptr = nullptr; + a.PyOS_getsig_Ptr = b.PyOS_getsig_Ptr; + b.PyOS_getsig_Ptr = nullptr; + a.PyOS_mystricmp_Ptr = b.PyOS_mystricmp_Ptr; + b.PyOS_mystricmp_Ptr = nullptr; + a.PyOS_mystrnicmp_Ptr = b.PyOS_mystrnicmp_Ptr; + b.PyOS_mystrnicmp_Ptr = nullptr; + a.PyOS_setsig_Ptr = b.PyOS_setsig_Ptr; + b.PyOS_setsig_Ptr = nullptr; + a.PyOS_snprintf_Ptr = b.PyOS_snprintf_Ptr; + b.PyOS_snprintf_Ptr = nullptr; + a.PyOS_string_to_double_Ptr = b.PyOS_string_to_double_Ptr; + b.PyOS_string_to_double_Ptr = nullptr; + a.PyOS_strtol_Ptr = b.PyOS_strtol_Ptr; + b.PyOS_strtol_Ptr = nullptr; + a.PyOS_strtoul_Ptr = b.PyOS_strtoul_Ptr; + b.PyOS_strtoul_Ptr = nullptr; + a.PyOS_vsnprintf_Ptr = b.PyOS_vsnprintf_Ptr; + b.PyOS_vsnprintf_Ptr = nullptr; + a.PyObject_ASCII_Ptr = b.PyObject_ASCII_Ptr; + b.PyObject_ASCII_Ptr = nullptr; + a.PyObject_AsCharBuffer_Ptr = b.PyObject_AsCharBuffer_Ptr; + b.PyObject_AsCharBuffer_Ptr = nullptr; + a.PyObject_AsFileDescriptor_Ptr = b.PyObject_AsFileDescriptor_Ptr; + b.PyObject_AsFileDescriptor_Ptr = nullptr; + a.PyObject_AsReadBuffer_Ptr = b.PyObject_AsReadBuffer_Ptr; + b.PyObject_AsReadBuffer_Ptr = nullptr; + a.PyObject_AsWriteBuffer_Ptr = b.PyObject_AsWriteBuffer_Ptr; + b.PyObject_AsWriteBuffer_Ptr = nullptr; + a.PyObject_Bytes_Ptr = b.PyObject_Bytes_Ptr; + b.PyObject_Bytes_Ptr = nullptr; + a.PyObject_Call_Ptr = b.PyObject_Call_Ptr; + b.PyObject_Call_Ptr = nullptr; + a.PyObject_CallFunction_Ptr = b.PyObject_CallFunction_Ptr; + b.PyObject_CallFunction_Ptr = nullptr; + a.PyObject_CallFunctionObjArgs_Ptr = b.PyObject_CallFunctionObjArgs_Ptr; + b.PyObject_CallFunctionObjArgs_Ptr = nullptr; + a.PyObject_CallMethod_Ptr = b.PyObject_CallMethod_Ptr; + b.PyObject_CallMethod_Ptr = nullptr; + a.PyObject_CallMethodObjArgs_Ptr = b.PyObject_CallMethodObjArgs_Ptr; + b.PyObject_CallMethodObjArgs_Ptr = nullptr; + a.PyObject_CallNoArgs_Ptr = b.PyObject_CallNoArgs_Ptr; + b.PyObject_CallNoArgs_Ptr = nullptr; + a.PyObject_CallObject_Ptr = b.PyObject_CallObject_Ptr; + b.PyObject_CallObject_Ptr = nullptr; + a.PyObject_Calloc_Ptr = b.PyObject_Calloc_Ptr; + b.PyObject_Calloc_Ptr = nullptr; + a.PyObject_CheckBuffer_Ptr = b.PyObject_CheckBuffer_Ptr; + b.PyObject_CheckBuffer_Ptr = nullptr; + a.PyObject_CheckReadBuffer_Ptr = b.PyObject_CheckReadBuffer_Ptr; + b.PyObject_CheckReadBuffer_Ptr = nullptr; + a.PyObject_ClearWeakRefs_Ptr = b.PyObject_ClearWeakRefs_Ptr; + b.PyObject_ClearWeakRefs_Ptr = nullptr; + a.PyObject_CopyData_Ptr = b.PyObject_CopyData_Ptr; + b.PyObject_CopyData_Ptr = nullptr; + a.PyObject_DelItem_Ptr = b.PyObject_DelItem_Ptr; + b.PyObject_DelItem_Ptr = nullptr; + a.PyObject_DelItemString_Ptr = b.PyObject_DelItemString_Ptr; + b.PyObject_DelItemString_Ptr = nullptr; + a.PyObject_Dir_Ptr = b.PyObject_Dir_Ptr; + b.PyObject_Dir_Ptr = nullptr; + a.PyObject_Format_Ptr = b.PyObject_Format_Ptr; + b.PyObject_Format_Ptr = nullptr; + a.PyObject_Free_Ptr = b.PyObject_Free_Ptr; + b.PyObject_Free_Ptr = nullptr; + a.PyObject_GC_Del_Ptr = b.PyObject_GC_Del_Ptr; + b.PyObject_GC_Del_Ptr = nullptr; + a.PyObject_GC_IsFinalized_Ptr = b.PyObject_GC_IsFinalized_Ptr; + b.PyObject_GC_IsFinalized_Ptr = nullptr; + a.PyObject_GC_IsTracked_Ptr = b.PyObject_GC_IsTracked_Ptr; + b.PyObject_GC_IsTracked_Ptr = nullptr; + a.PyObject_GC_Track_Ptr = b.PyObject_GC_Track_Ptr; + b.PyObject_GC_Track_Ptr = nullptr; + a.PyObject_GC_UnTrack_Ptr = b.PyObject_GC_UnTrack_Ptr; + b.PyObject_GC_UnTrack_Ptr = nullptr; + a.PyObject_GenericGetAttr_Ptr = b.PyObject_GenericGetAttr_Ptr; + b.PyObject_GenericGetAttr_Ptr = nullptr; + a.PyObject_GenericGetDict_Ptr = b.PyObject_GenericGetDict_Ptr; + b.PyObject_GenericGetDict_Ptr = nullptr; + a.PyObject_GenericSetAttr_Ptr = b.PyObject_GenericSetAttr_Ptr; + b.PyObject_GenericSetAttr_Ptr = nullptr; + a.PyObject_GenericSetDict_Ptr = b.PyObject_GenericSetDict_Ptr; + b.PyObject_GenericSetDict_Ptr = nullptr; + a.PyObject_GetAIter_Ptr = b.PyObject_GetAIter_Ptr; + b.PyObject_GetAIter_Ptr = nullptr; + a.PyObject_GetAttr_Ptr = b.PyObject_GetAttr_Ptr; + b.PyObject_GetAttr_Ptr = nullptr; + a.PyObject_GetAttrString_Ptr = b.PyObject_GetAttrString_Ptr; + b.PyObject_GetAttrString_Ptr = nullptr; + #if HAS_PYTHON_VERSION(0x030B1000) + a.PyObject_GetBuffer_Ptr = b.PyObject_GetBuffer_Ptr; + b.PyObject_GetBuffer_Ptr = nullptr; + #endif + a.PyObject_GetItem_Ptr = b.PyObject_GetItem_Ptr; + b.PyObject_GetItem_Ptr = nullptr; + a.PyObject_GetIter_Ptr = b.PyObject_GetIter_Ptr; + b.PyObject_GetIter_Ptr = nullptr; + a.PyObject_HasAttr_Ptr = b.PyObject_HasAttr_Ptr; + b.PyObject_HasAttr_Ptr = nullptr; + a.PyObject_HasAttrString_Ptr = b.PyObject_HasAttrString_Ptr; + b.PyObject_HasAttrString_Ptr = nullptr; + a.PyObject_Hash_Ptr = b.PyObject_Hash_Ptr; + b.PyObject_Hash_Ptr = nullptr; + a.PyObject_HashNotImplemented_Ptr = b.PyObject_HashNotImplemented_Ptr; + b.PyObject_HashNotImplemented_Ptr = nullptr; + a.PyObject_Init_Ptr = b.PyObject_Init_Ptr; + b.PyObject_Init_Ptr = nullptr; + a.PyObject_InitVar_Ptr = b.PyObject_InitVar_Ptr; + b.PyObject_InitVar_Ptr = nullptr; + a.PyObject_IsInstance_Ptr = b.PyObject_IsInstance_Ptr; + b.PyObject_IsInstance_Ptr = nullptr; + a.PyObject_IsSubclass_Ptr = b.PyObject_IsSubclass_Ptr; + b.PyObject_IsSubclass_Ptr = nullptr; + a.PyObject_IsTrue_Ptr = b.PyObject_IsTrue_Ptr; + b.PyObject_IsTrue_Ptr = nullptr; + a.PyObject_Length_Ptr = b.PyObject_Length_Ptr; + b.PyObject_Length_Ptr = nullptr; + a.PyObject_Malloc_Ptr = b.PyObject_Malloc_Ptr; + b.PyObject_Malloc_Ptr = nullptr; + a.PyObject_Not_Ptr = b.PyObject_Not_Ptr; + b.PyObject_Not_Ptr = nullptr; + a.PyObject_Realloc_Ptr = b.PyObject_Realloc_Ptr; + b.PyObject_Realloc_Ptr = nullptr; + a.PyObject_Repr_Ptr = b.PyObject_Repr_Ptr; + b.PyObject_Repr_Ptr = nullptr; + a.PyObject_RichCompare_Ptr = b.PyObject_RichCompare_Ptr; + b.PyObject_RichCompare_Ptr = nullptr; + a.PyObject_RichCompareBool_Ptr = b.PyObject_RichCompareBool_Ptr; + b.PyObject_RichCompareBool_Ptr = nullptr; + a.PyObject_SelfIter_Ptr = b.PyObject_SelfIter_Ptr; + b.PyObject_SelfIter_Ptr = nullptr; + a.PyObject_SetAttr_Ptr = b.PyObject_SetAttr_Ptr; + b.PyObject_SetAttr_Ptr = nullptr; + a.PyObject_SetAttrString_Ptr = b.PyObject_SetAttrString_Ptr; + b.PyObject_SetAttrString_Ptr = nullptr; + a.PyObject_SetItem_Ptr = b.PyObject_SetItem_Ptr; + b.PyObject_SetItem_Ptr = nullptr; + a.PyObject_Size_Ptr = b.PyObject_Size_Ptr; + b.PyObject_Size_Ptr = nullptr; + a.PyObject_Str_Ptr = b.PyObject_Str_Ptr; + b.PyObject_Str_Ptr = nullptr; + a.PyObject_Type_Ptr = b.PyObject_Type_Ptr; + b.PyObject_Type_Ptr = nullptr; + #if HAS_PYTHON_VERSION(0x030B0000) + a.PyObject_TypeCheck_Ptr = b.PyObject_TypeCheck_Ptr; + b.PyObject_TypeCheck_Ptr = nullptr; + #endif + a.PySeqIter_New_Ptr = b.PySeqIter_New_Ptr; + b.PySeqIter_New_Ptr = nullptr; + a.PySequence_Check_Ptr = b.PySequence_Check_Ptr; + b.PySequence_Check_Ptr = nullptr; + a.PySequence_Concat_Ptr = b.PySequence_Concat_Ptr; + b.PySequence_Concat_Ptr = nullptr; + a.PySequence_Contains_Ptr = b.PySequence_Contains_Ptr; + b.PySequence_Contains_Ptr = nullptr; + a.PySequence_Count_Ptr = b.PySequence_Count_Ptr; + b.PySequence_Count_Ptr = nullptr; + a.PySequence_DelItem_Ptr = b.PySequence_DelItem_Ptr; + b.PySequence_DelItem_Ptr = nullptr; + a.PySequence_DelSlice_Ptr = b.PySequence_DelSlice_Ptr; + b.PySequence_DelSlice_Ptr = nullptr; + a.PySequence_Fast_Ptr = b.PySequence_Fast_Ptr; + b.PySequence_Fast_Ptr = nullptr; + a.PySequence_GetItem_Ptr = b.PySequence_GetItem_Ptr; + b.PySequence_GetItem_Ptr = nullptr; + a.PySequence_GetSlice_Ptr = b.PySequence_GetSlice_Ptr; + b.PySequence_GetSlice_Ptr = nullptr; + a.PySequence_In_Ptr = b.PySequence_In_Ptr; + b.PySequence_In_Ptr = nullptr; + a.PySequence_InPlaceConcat_Ptr = b.PySequence_InPlaceConcat_Ptr; + b.PySequence_InPlaceConcat_Ptr = nullptr; + a.PySequence_InPlaceRepeat_Ptr = b.PySequence_InPlaceRepeat_Ptr; + b.PySequence_InPlaceRepeat_Ptr = nullptr; + a.PySequence_Index_Ptr = b.PySequence_Index_Ptr; + b.PySequence_Index_Ptr = nullptr; + a.PySequence_Length_Ptr = b.PySequence_Length_Ptr; + b.PySequence_Length_Ptr = nullptr; + a.PySequence_List_Ptr = b.PySequence_List_Ptr; + b.PySequence_List_Ptr = nullptr; + a.PySequence_Repeat_Ptr = b.PySequence_Repeat_Ptr; + b.PySequence_Repeat_Ptr = nullptr; + a.PySequence_SetItem_Ptr = b.PySequence_SetItem_Ptr; + b.PySequence_SetItem_Ptr = nullptr; + a.PySequence_SetSlice_Ptr = b.PySequence_SetSlice_Ptr; + b.PySequence_SetSlice_Ptr = nullptr; + a.PySequence_Size_Ptr = b.PySequence_Size_Ptr; + b.PySequence_Size_Ptr = nullptr; + a.PySequence_Tuple_Ptr = b.PySequence_Tuple_Ptr; + b.PySequence_Tuple_Ptr = nullptr; + a.PySet_Add_Ptr = b.PySet_Add_Ptr; + b.PySet_Add_Ptr = nullptr; + a.PySet_Clear_Ptr = b.PySet_Clear_Ptr; + b.PySet_Clear_Ptr = nullptr; + a.PySet_Contains_Ptr = b.PySet_Contains_Ptr; + b.PySet_Contains_Ptr = nullptr; + a.PySet_Discard_Ptr = b.PySet_Discard_Ptr; + b.PySet_Discard_Ptr = nullptr; + a.PySet_New_Ptr = b.PySet_New_Ptr; + b.PySet_New_Ptr = nullptr; + a.PySet_Pop_Ptr = b.PySet_Pop_Ptr; + b.PySet_Pop_Ptr = nullptr; + a.PySet_Size_Ptr = b.PySet_Size_Ptr; + b.PySet_Size_Ptr = nullptr; + a.PySlice_AdjustIndices_Ptr = b.PySlice_AdjustIndices_Ptr; + b.PySlice_AdjustIndices_Ptr = nullptr; + a.PySlice_GetIndices_Ptr = b.PySlice_GetIndices_Ptr; + b.PySlice_GetIndices_Ptr = nullptr; + #ifdef PySlice_GetIndicesEx + a.PySlice_GetIndicesEx_Ptr = b.PySlice_GetIndicesEx_Ptr; + b.PySlice_GetIndicesEx_Ptr = nullptr; + #endif + a.PySlice_New_Ptr = b.PySlice_New_Ptr; + b.PySlice_New_Ptr = nullptr; + a.PySlice_Unpack_Ptr = b.PySlice_Unpack_Ptr; + b.PySlice_Unpack_Ptr = nullptr; + a.PyState_AddModule_Ptr = b.PyState_AddModule_Ptr; + b.PyState_AddModule_Ptr = nullptr; + a.PyState_FindModule_Ptr = b.PyState_FindModule_Ptr; + b.PyState_FindModule_Ptr = nullptr; + a.PyState_RemoveModule_Ptr = b.PyState_RemoveModule_Ptr; + b.PyState_RemoveModule_Ptr = nullptr; + a.PyStructSequence_GetItem_Ptr = b.PyStructSequence_GetItem_Ptr; + b.PyStructSequence_GetItem_Ptr = nullptr; + a.PyStructSequence_New_Ptr = b.PyStructSequence_New_Ptr; + b.PyStructSequence_New_Ptr = nullptr; + a.PyStructSequence_NewType_Ptr = b.PyStructSequence_NewType_Ptr; + b.PyStructSequence_NewType_Ptr = nullptr; + a.PyStructSequence_SetItem_Ptr = b.PyStructSequence_SetItem_Ptr; + b.PyStructSequence_SetItem_Ptr = nullptr; + a.PySys_AddWarnOption_Ptr = b.PySys_AddWarnOption_Ptr; + b.PySys_AddWarnOption_Ptr = nullptr; + a.PySys_AddWarnOptionUnicode_Ptr = b.PySys_AddWarnOptionUnicode_Ptr; + b.PySys_AddWarnOptionUnicode_Ptr = nullptr; + a.PySys_AddXOption_Ptr = b.PySys_AddXOption_Ptr; + b.PySys_AddXOption_Ptr = nullptr; + a.PySys_FormatStderr_Ptr = b.PySys_FormatStderr_Ptr; + b.PySys_FormatStderr_Ptr = nullptr; + a.PySys_FormatStdout_Ptr = b.PySys_FormatStdout_Ptr; + b.PySys_FormatStdout_Ptr = nullptr; + a.PySys_GetObject_Ptr = b.PySys_GetObject_Ptr; + b.PySys_GetObject_Ptr = nullptr; + a.PySys_GetXOptions_Ptr = b.PySys_GetXOptions_Ptr; + b.PySys_GetXOptions_Ptr = nullptr; + a.PySys_HasWarnOptions_Ptr = b.PySys_HasWarnOptions_Ptr; + b.PySys_HasWarnOptions_Ptr = nullptr; + a.PySys_ResetWarnOptions_Ptr = b.PySys_ResetWarnOptions_Ptr; + b.PySys_ResetWarnOptions_Ptr = nullptr; + a.PySys_SetArgv_Ptr = b.PySys_SetArgv_Ptr; + b.PySys_SetArgv_Ptr = nullptr; + a.PySys_SetArgvEx_Ptr = b.PySys_SetArgvEx_Ptr; + b.PySys_SetArgvEx_Ptr = nullptr; + a.PySys_SetObject_Ptr = b.PySys_SetObject_Ptr; + b.PySys_SetObject_Ptr = nullptr; + a.PySys_SetPath_Ptr = b.PySys_SetPath_Ptr; + b.PySys_SetPath_Ptr = nullptr; + a.PySys_WriteStderr_Ptr = b.PySys_WriteStderr_Ptr; + b.PySys_WriteStderr_Ptr = nullptr; + a.PySys_WriteStdout_Ptr = b.PySys_WriteStdout_Ptr; + b.PySys_WriteStdout_Ptr = nullptr; + a.PyThreadState_Clear_Ptr = b.PyThreadState_Clear_Ptr; + b.PyThreadState_Clear_Ptr = nullptr; + a.PyThreadState_Delete_Ptr = b.PyThreadState_Delete_Ptr; + b.PyThreadState_Delete_Ptr = nullptr; + a.PyThreadState_Get_Ptr = b.PyThreadState_Get_Ptr; + b.PyThreadState_Get_Ptr = nullptr; + a.PyThreadState_GetDict_Ptr = b.PyThreadState_GetDict_Ptr; + b.PyThreadState_GetDict_Ptr = nullptr; + #if HAS_PYTHON_VERSION(0x03090000) + a.PyThreadState_GetFrame_Ptr = b.PyThreadState_GetFrame_Ptr; + b.PyThreadState_GetFrame_Ptr = nullptr; + #endif + a.PyThreadState_GetID_Ptr = b.PyThreadState_GetID_Ptr; + b.PyThreadState_GetID_Ptr = nullptr; + a.PyThreadState_GetInterpreter_Ptr = b.PyThreadState_GetInterpreter_Ptr; + b.PyThreadState_GetInterpreter_Ptr = nullptr; + a.PyThreadState_New_Ptr = b.PyThreadState_New_Ptr; + b.PyThreadState_New_Ptr = nullptr; + a.PyThreadState_SetAsyncExc_Ptr = b.PyThreadState_SetAsyncExc_Ptr; + b.PyThreadState_SetAsyncExc_Ptr = nullptr; + a.PyThreadState_Swap_Ptr = b.PyThreadState_Swap_Ptr; + b.PyThreadState_Swap_Ptr = nullptr; + a.PyThread_GetInfo_Ptr = b.PyThread_GetInfo_Ptr; + b.PyThread_GetInfo_Ptr = nullptr; + a.PyThread_ReInitTLS_Ptr = b.PyThread_ReInitTLS_Ptr; + b.PyThread_ReInitTLS_Ptr = nullptr; + a.PyThread_acquire_lock_Ptr = b.PyThread_acquire_lock_Ptr; + b.PyThread_acquire_lock_Ptr = nullptr; + a.PyThread_acquire_lock_timed_Ptr = b.PyThread_acquire_lock_timed_Ptr; + b.PyThread_acquire_lock_timed_Ptr = nullptr; + a.PyThread_allocate_lock_Ptr = b.PyThread_allocate_lock_Ptr; + b.PyThread_allocate_lock_Ptr = nullptr; + a.PyThread_create_key_Ptr = b.PyThread_create_key_Ptr; + b.PyThread_create_key_Ptr = nullptr; + a.PyThread_delete_key_Ptr = b.PyThread_delete_key_Ptr; + b.PyThread_delete_key_Ptr = nullptr; + a.PyThread_delete_key_value_Ptr = b.PyThread_delete_key_value_Ptr; + b.PyThread_delete_key_value_Ptr = nullptr; + a.PyThread_exit_thread_Ptr = b.PyThread_exit_thread_Ptr; + b.PyThread_exit_thread_Ptr = nullptr; + a.PyThread_free_lock_Ptr = b.PyThread_free_lock_Ptr; + b.PyThread_free_lock_Ptr = nullptr; + a.PyThread_get_key_value_Ptr = b.PyThread_get_key_value_Ptr; + b.PyThread_get_key_value_Ptr = nullptr; + a.PyThread_get_stacksize_Ptr = b.PyThread_get_stacksize_Ptr; + b.PyThread_get_stacksize_Ptr = nullptr; + a.PyThread_get_thread_ident_Ptr = b.PyThread_get_thread_ident_Ptr; + b.PyThread_get_thread_ident_Ptr = nullptr; + a.PyThread_get_thread_native_id_Ptr = b.PyThread_get_thread_native_id_Ptr; + b.PyThread_get_thread_native_id_Ptr = nullptr; + a.PyThread_init_thread_Ptr = b.PyThread_init_thread_Ptr; + b.PyThread_init_thread_Ptr = nullptr; + a.PyThread_release_lock_Ptr = b.PyThread_release_lock_Ptr; + b.PyThread_release_lock_Ptr = nullptr; + a.PyThread_set_key_value_Ptr = b.PyThread_set_key_value_Ptr; + b.PyThread_set_key_value_Ptr = nullptr; + a.PyThread_set_stacksize_Ptr = b.PyThread_set_stacksize_Ptr; + b.PyThread_set_stacksize_Ptr = nullptr; + a.PyThread_start_new_thread_Ptr = b.PyThread_start_new_thread_Ptr; + b.PyThread_start_new_thread_Ptr = nullptr; + #if HAS_PYTHON_VERSION(0x03070000) + a.PyThread_tss_alloc_Ptr = b.PyThread_tss_alloc_Ptr; + b.PyThread_tss_alloc_Ptr = nullptr; + a.PyThread_tss_create_Ptr = b.PyThread_tss_create_Ptr; + b.PyThread_tss_create_Ptr = nullptr; + a.PyThread_tss_delete_Ptr = b.PyThread_tss_delete_Ptr; + b.PyThread_tss_delete_Ptr = nullptr; + a.PyThread_tss_free_Ptr = b.PyThread_tss_free_Ptr; + b.PyThread_tss_free_Ptr = nullptr; + a.PyThread_tss_get_Ptr = b.PyThread_tss_get_Ptr; + b.PyThread_tss_get_Ptr = nullptr; + a.PyThread_tss_is_created_Ptr = b.PyThread_tss_is_created_Ptr; + b.PyThread_tss_is_created_Ptr = nullptr; + a.PyThread_tss_set_Ptr = b.PyThread_tss_set_Ptr; + b.PyThread_tss_set_Ptr = nullptr; + #endif + #if HAS_PYTHON_VERSION(0x03090000) + a.PyTraceBack_Here_Ptr = b.PyTraceBack_Here_Ptr; + b.PyTraceBack_Here_Ptr = nullptr; + #endif + a.PyTraceBack_Print_Ptr = b.PyTraceBack_Print_Ptr; + b.PyTraceBack_Print_Ptr = nullptr; + a.PyTuple_GetItem_Ptr = b.PyTuple_GetItem_Ptr; + b.PyTuple_GetItem_Ptr = nullptr; + a.PyTuple_GetSlice_Ptr = b.PyTuple_GetSlice_Ptr; + b.PyTuple_GetSlice_Ptr = nullptr; + a.PyTuple_New_Ptr = b.PyTuple_New_Ptr; + b.PyTuple_New_Ptr = nullptr; + a.PyTuple_Pack_Ptr = b.PyTuple_Pack_Ptr; + b.PyTuple_Pack_Ptr = nullptr; + a.PyTuple_SetItem_Ptr = b.PyTuple_SetItem_Ptr; + b.PyTuple_SetItem_Ptr = nullptr; + a.PyTuple_Size_Ptr = b.PyTuple_Size_Ptr; + b.PyTuple_Size_Ptr = nullptr; + a.PyType_ClearCache_Ptr = b.PyType_ClearCache_Ptr; + b.PyType_ClearCache_Ptr = nullptr; + #if !defined(Py_LIMITED_API) + a.PyType_Name_Ptr = b.PyType_Name_Ptr; + b.PyType_Name_Ptr = nullptr; + #endif + a.PyType_FromModuleAndSpec_Ptr = b.PyType_FromModuleAndSpec_Ptr; + b.PyType_FromModuleAndSpec_Ptr = nullptr; + a.PyType_FromSpec_Ptr = b.PyType_FromSpec_Ptr; + b.PyType_FromSpec_Ptr = nullptr; + a.PyType_FromSpecWithBases_Ptr = b.PyType_FromSpecWithBases_Ptr; + b.PyType_FromSpecWithBases_Ptr = nullptr; + a.PyType_GenericAlloc_Ptr = b.PyType_GenericAlloc_Ptr; + b.PyType_GenericAlloc_Ptr = nullptr; + a.PyType_GenericNew_Ptr = b.PyType_GenericNew_Ptr; + b.PyType_GenericNew_Ptr = nullptr; + a.PyType_GetFlags_Ptr = b.PyType_GetFlags_Ptr; + b.PyType_GetFlags_Ptr = nullptr; + a.PyType_GetModule_Ptr = b.PyType_GetModule_Ptr; + b.PyType_GetModule_Ptr = nullptr; + a.PyType_GetModuleState_Ptr = b.PyType_GetModuleState_Ptr; + b.PyType_GetModuleState_Ptr = nullptr; + a.PyType_GetName_Ptr = b.PyType_GetName_Ptr; + b.PyType_GetName_Ptr = nullptr; + a.PyType_GetQualName_Ptr = b.PyType_GetQualName_Ptr; + b.PyType_GetQualName_Ptr = nullptr; + a.PyType_GetSlot_Ptr = b.PyType_GetSlot_Ptr; + b.PyType_GetSlot_Ptr = nullptr; + a.PyType_IsSubtype_Ptr = b.PyType_IsSubtype_Ptr; + b.PyType_IsSubtype_Ptr = nullptr; + a.PyType_Modified_Ptr = b.PyType_Modified_Ptr; + b.PyType_Modified_Ptr = nullptr; + a.PyType_Ready_Ptr = b.PyType_Ready_Ptr; + b.PyType_Ready_Ptr = nullptr; + a.PyUnicodeDecodeError_Create_Ptr = b.PyUnicodeDecodeError_Create_Ptr; + b.PyUnicodeDecodeError_Create_Ptr = nullptr; + a.PyUnicodeDecodeError_GetEncoding_Ptr = b.PyUnicodeDecodeError_GetEncoding_Ptr; + b.PyUnicodeDecodeError_GetEncoding_Ptr = nullptr; + a.PyUnicodeDecodeError_GetEnd_Ptr = b.PyUnicodeDecodeError_GetEnd_Ptr; + b.PyUnicodeDecodeError_GetEnd_Ptr = nullptr; + a.PyUnicodeDecodeError_GetObject_Ptr = b.PyUnicodeDecodeError_GetObject_Ptr; + b.PyUnicodeDecodeError_GetObject_Ptr = nullptr; + a.PyUnicodeDecodeError_GetReason_Ptr = b.PyUnicodeDecodeError_GetReason_Ptr; + b.PyUnicodeDecodeError_GetReason_Ptr = nullptr; + a.PyUnicodeDecodeError_GetStart_Ptr = b.PyUnicodeDecodeError_GetStart_Ptr; + b.PyUnicodeDecodeError_GetStart_Ptr = nullptr; + a.PyUnicodeDecodeError_SetEnd_Ptr = b.PyUnicodeDecodeError_SetEnd_Ptr; + b.PyUnicodeDecodeError_SetEnd_Ptr = nullptr; + a.PyUnicodeDecodeError_SetReason_Ptr = b.PyUnicodeDecodeError_SetReason_Ptr; + b.PyUnicodeDecodeError_SetReason_Ptr = nullptr; + a.PyUnicodeDecodeError_SetStart_Ptr = b.PyUnicodeDecodeError_SetStart_Ptr; + b.PyUnicodeDecodeError_SetStart_Ptr = nullptr; + a.PyUnicodeEncodeError_GetEncoding_Ptr = b.PyUnicodeEncodeError_GetEncoding_Ptr; + b.PyUnicodeEncodeError_GetEncoding_Ptr = nullptr; + a.PyUnicodeEncodeError_GetEnd_Ptr = b.PyUnicodeEncodeError_GetEnd_Ptr; + b.PyUnicodeEncodeError_GetEnd_Ptr = nullptr; + a.PyUnicodeEncodeError_GetObject_Ptr = b.PyUnicodeEncodeError_GetObject_Ptr; + b.PyUnicodeEncodeError_GetObject_Ptr = nullptr; + a.PyUnicodeEncodeError_GetReason_Ptr = b.PyUnicodeEncodeError_GetReason_Ptr; + b.PyUnicodeEncodeError_GetReason_Ptr = nullptr; + a.PyUnicodeEncodeError_GetStart_Ptr = b.PyUnicodeEncodeError_GetStart_Ptr; + b.PyUnicodeEncodeError_GetStart_Ptr = nullptr; + a.PyUnicodeEncodeError_SetEnd_Ptr = b.PyUnicodeEncodeError_SetEnd_Ptr; + b.PyUnicodeEncodeError_SetEnd_Ptr = nullptr; + a.PyUnicodeEncodeError_SetReason_Ptr = b.PyUnicodeEncodeError_SetReason_Ptr; + b.PyUnicodeEncodeError_SetReason_Ptr = nullptr; + a.PyUnicodeEncodeError_SetStart_Ptr = b.PyUnicodeEncodeError_SetStart_Ptr; + b.PyUnicodeEncodeError_SetStart_Ptr = nullptr; + a.PyUnicodeTranslateError_GetEnd_Ptr = b.PyUnicodeTranslateError_GetEnd_Ptr; + b.PyUnicodeTranslateError_GetEnd_Ptr = nullptr; + a.PyUnicodeTranslateError_GetObject_Ptr = b.PyUnicodeTranslateError_GetObject_Ptr; + b.PyUnicodeTranslateError_GetObject_Ptr = nullptr; + a.PyUnicodeTranslateError_GetReason_Ptr = b.PyUnicodeTranslateError_GetReason_Ptr; + b.PyUnicodeTranslateError_GetReason_Ptr = nullptr; + a.PyUnicodeTranslateError_GetStart_Ptr = b.PyUnicodeTranslateError_GetStart_Ptr; + b.PyUnicodeTranslateError_GetStart_Ptr = nullptr; + a.PyUnicodeTranslateError_SetEnd_Ptr = b.PyUnicodeTranslateError_SetEnd_Ptr; + b.PyUnicodeTranslateError_SetEnd_Ptr = nullptr; + a.PyUnicodeTranslateError_SetReason_Ptr = b.PyUnicodeTranslateError_SetReason_Ptr; + b.PyUnicodeTranslateError_SetReason_Ptr = nullptr; + a.PyUnicodeTranslateError_SetStart_Ptr = b.PyUnicodeTranslateError_SetStart_Ptr; + b.PyUnicodeTranslateError_SetStart_Ptr = nullptr; + a.PyUnicode_Append_Ptr = b.PyUnicode_Append_Ptr; + b.PyUnicode_Append_Ptr = nullptr; + a.PyUnicode_AppendAndDel_Ptr = b.PyUnicode_AppendAndDel_Ptr; + b.PyUnicode_AppendAndDel_Ptr = nullptr; + a.PyUnicode_AsASCIIString_Ptr = b.PyUnicode_AsASCIIString_Ptr; + b.PyUnicode_AsASCIIString_Ptr = nullptr; + a.PyUnicode_AsCharmapString_Ptr = b.PyUnicode_AsCharmapString_Ptr; + b.PyUnicode_AsCharmapString_Ptr = nullptr; + a.PyUnicode_AsDecodedObject_Ptr = b.PyUnicode_AsDecodedObject_Ptr; + b.PyUnicode_AsDecodedObject_Ptr = nullptr; + a.PyUnicode_AsDecodedUnicode_Ptr = b.PyUnicode_AsDecodedUnicode_Ptr; + b.PyUnicode_AsDecodedUnicode_Ptr = nullptr; + a.PyUnicode_AsEncodedObject_Ptr = b.PyUnicode_AsEncodedObject_Ptr; + b.PyUnicode_AsEncodedObject_Ptr = nullptr; + a.PyUnicode_AsEncodedString_Ptr = b.PyUnicode_AsEncodedString_Ptr; + b.PyUnicode_AsEncodedString_Ptr = nullptr; + a.PyUnicode_AsEncodedUnicode_Ptr = b.PyUnicode_AsEncodedUnicode_Ptr; + b.PyUnicode_AsEncodedUnicode_Ptr = nullptr; + a.PyUnicode_AsLatin1String_Ptr = b.PyUnicode_AsLatin1String_Ptr; + b.PyUnicode_AsLatin1String_Ptr = nullptr; + a.PyUnicode_AsRawUnicodeEscapeString_Ptr = b.PyUnicode_AsRawUnicodeEscapeString_Ptr; + b.PyUnicode_AsRawUnicodeEscapeString_Ptr = nullptr; + a.PyUnicode_AsUCS4_Ptr = b.PyUnicode_AsUCS4_Ptr; + b.PyUnicode_AsUCS4_Ptr = nullptr; + a.PyUnicode_AsUCS4Copy_Ptr = b.PyUnicode_AsUCS4Copy_Ptr; + b.PyUnicode_AsUCS4Copy_Ptr = nullptr; + a.PyUnicode_AsUTF16String_Ptr = b.PyUnicode_AsUTF16String_Ptr; + b.PyUnicode_AsUTF16String_Ptr = nullptr; + a.PyUnicode_AsUTF32String_Ptr = b.PyUnicode_AsUTF32String_Ptr; + b.PyUnicode_AsUTF32String_Ptr = nullptr; + a.PyUnicode_AsUTF8AndSize_Ptr = b.PyUnicode_AsUTF8AndSize_Ptr; + b.PyUnicode_AsUTF8AndSize_Ptr = nullptr; + a.PyUnicode_AsUTF8String_Ptr = b.PyUnicode_AsUTF8String_Ptr; + b.PyUnicode_AsUTF8String_Ptr = nullptr; + a.PyUnicode_AsUnicodeEscapeString_Ptr = b.PyUnicode_AsUnicodeEscapeString_Ptr; + b.PyUnicode_AsUnicodeEscapeString_Ptr = nullptr; + a.PyUnicode_AsWideChar_Ptr = b.PyUnicode_AsWideChar_Ptr; + b.PyUnicode_AsWideChar_Ptr = nullptr; + a.PyUnicode_AsWideCharString_Ptr = b.PyUnicode_AsWideCharString_Ptr; + b.PyUnicode_AsWideCharString_Ptr = nullptr; + a.PyUnicode_BuildEncodingMap_Ptr = b.PyUnicode_BuildEncodingMap_Ptr; + b.PyUnicode_BuildEncodingMap_Ptr = nullptr; + a.PyUnicode_Compare_Ptr = b.PyUnicode_Compare_Ptr; + b.PyUnicode_Compare_Ptr = nullptr; + a.PyUnicode_CompareWithASCIIString_Ptr = b.PyUnicode_CompareWithASCIIString_Ptr; + b.PyUnicode_CompareWithASCIIString_Ptr = nullptr; + a.PyUnicode_Concat_Ptr = b.PyUnicode_Concat_Ptr; + b.PyUnicode_Concat_Ptr = nullptr; + a.PyUnicode_Contains_Ptr = b.PyUnicode_Contains_Ptr; + b.PyUnicode_Contains_Ptr = nullptr; + a.PyUnicode_Count_Ptr = b.PyUnicode_Count_Ptr; + b.PyUnicode_Count_Ptr = nullptr; + a.PyUnicode_Decode_Ptr = b.PyUnicode_Decode_Ptr; + b.PyUnicode_Decode_Ptr = nullptr; + a.PyUnicode_DecodeASCII_Ptr = b.PyUnicode_DecodeASCII_Ptr; + b.PyUnicode_DecodeASCII_Ptr = nullptr; + a.PyUnicode_DecodeCharmap_Ptr = b.PyUnicode_DecodeCharmap_Ptr; + b.PyUnicode_DecodeCharmap_Ptr = nullptr; + a.PyUnicode_DecodeFSDefault_Ptr = b.PyUnicode_DecodeFSDefault_Ptr; + b.PyUnicode_DecodeFSDefault_Ptr = nullptr; + a.PyUnicode_DecodeFSDefaultAndSize_Ptr = b.PyUnicode_DecodeFSDefaultAndSize_Ptr; + b.PyUnicode_DecodeFSDefaultAndSize_Ptr = nullptr; + a.PyUnicode_DecodeLatin1_Ptr = b.PyUnicode_DecodeLatin1_Ptr; + b.PyUnicode_DecodeLatin1_Ptr = nullptr; + a.PyUnicode_DecodeLocale_Ptr = b.PyUnicode_DecodeLocale_Ptr; + b.PyUnicode_DecodeLocale_Ptr = nullptr; + a.PyUnicode_DecodeLocaleAndSize_Ptr = b.PyUnicode_DecodeLocaleAndSize_Ptr; + b.PyUnicode_DecodeLocaleAndSize_Ptr = nullptr; + a.PyUnicode_DecodeRawUnicodeEscape_Ptr = b.PyUnicode_DecodeRawUnicodeEscape_Ptr; + b.PyUnicode_DecodeRawUnicodeEscape_Ptr = nullptr; + a.PyUnicode_DecodeUTF16_Ptr = b.PyUnicode_DecodeUTF16_Ptr; + b.PyUnicode_DecodeUTF16_Ptr = nullptr; + a.PyUnicode_DecodeUTF16Stateful_Ptr = b.PyUnicode_DecodeUTF16Stateful_Ptr; + b.PyUnicode_DecodeUTF16Stateful_Ptr = nullptr; + a.PyUnicode_DecodeUTF32_Ptr = b.PyUnicode_DecodeUTF32_Ptr; + b.PyUnicode_DecodeUTF32_Ptr = nullptr; + a.PyUnicode_DecodeUTF32Stateful_Ptr = b.PyUnicode_DecodeUTF32Stateful_Ptr; + b.PyUnicode_DecodeUTF32Stateful_Ptr = nullptr; + a.PyUnicode_DecodeUTF7_Ptr = b.PyUnicode_DecodeUTF7_Ptr; + b.PyUnicode_DecodeUTF7_Ptr = nullptr; + a.PyUnicode_DecodeUTF7Stateful_Ptr = b.PyUnicode_DecodeUTF7Stateful_Ptr; + b.PyUnicode_DecodeUTF7Stateful_Ptr = nullptr; + a.PyUnicode_DecodeUTF8_Ptr = b.PyUnicode_DecodeUTF8_Ptr; + b.PyUnicode_DecodeUTF8_Ptr = nullptr; + a.PyUnicode_DecodeUTF8Stateful_Ptr = b.PyUnicode_DecodeUTF8Stateful_Ptr; + b.PyUnicode_DecodeUTF8Stateful_Ptr = nullptr; + a.PyUnicode_DecodeUnicodeEscape_Ptr = b.PyUnicode_DecodeUnicodeEscape_Ptr; + b.PyUnicode_DecodeUnicodeEscape_Ptr = nullptr; + a.PyUnicode_EncodeFSDefault_Ptr = b.PyUnicode_EncodeFSDefault_Ptr; + b.PyUnicode_EncodeFSDefault_Ptr = nullptr; + a.PyUnicode_EncodeLocale_Ptr = b.PyUnicode_EncodeLocale_Ptr; + b.PyUnicode_EncodeLocale_Ptr = nullptr; + a.PyUnicode_FSConverter_Ptr = b.PyUnicode_FSConverter_Ptr; + b.PyUnicode_FSConverter_Ptr = nullptr; + a.PyUnicode_FSDecoder_Ptr = b.PyUnicode_FSDecoder_Ptr; + b.PyUnicode_FSDecoder_Ptr = nullptr; + a.PyUnicode_Find_Ptr = b.PyUnicode_Find_Ptr; + b.PyUnicode_Find_Ptr = nullptr; + a.PyUnicode_FindChar_Ptr = b.PyUnicode_FindChar_Ptr; + b.PyUnicode_FindChar_Ptr = nullptr; + a.PyUnicode_Format_Ptr = b.PyUnicode_Format_Ptr; + b.PyUnicode_Format_Ptr = nullptr; + a.PyUnicode_FromEncodedObject_Ptr = b.PyUnicode_FromEncodedObject_Ptr; + b.PyUnicode_FromEncodedObject_Ptr = nullptr; + a.PyUnicode_FromFormat_Ptr = b.PyUnicode_FromFormat_Ptr; + b.PyUnicode_FromFormat_Ptr = nullptr; + a.PyUnicode_FromFormatV_Ptr = b.PyUnicode_FromFormatV_Ptr; + b.PyUnicode_FromFormatV_Ptr = nullptr; + a.PyUnicode_FromObject_Ptr = b.PyUnicode_FromObject_Ptr; + b.PyUnicode_FromObject_Ptr = nullptr; + a.PyUnicode_FromOrdinal_Ptr = b.PyUnicode_FromOrdinal_Ptr; + b.PyUnicode_FromOrdinal_Ptr = nullptr; + a.PyUnicode_FromString_Ptr = b.PyUnicode_FromString_Ptr; + b.PyUnicode_FromString_Ptr = nullptr; + a.PyUnicode_FromStringAndSize_Ptr = b.PyUnicode_FromStringAndSize_Ptr; + b.PyUnicode_FromStringAndSize_Ptr = nullptr; + a.PyUnicode_FromWideChar_Ptr = b.PyUnicode_FromWideChar_Ptr; + b.PyUnicode_FromWideChar_Ptr = nullptr; + a.PyUnicode_GetDefaultEncoding_Ptr = b.PyUnicode_GetDefaultEncoding_Ptr; + b.PyUnicode_GetDefaultEncoding_Ptr = nullptr; + a.PyUnicode_GetLength_Ptr = b.PyUnicode_GetLength_Ptr; + b.PyUnicode_GetLength_Ptr = nullptr; + a.PyUnicode_GetSize_Ptr = b.PyUnicode_GetSize_Ptr; + b.PyUnicode_GetSize_Ptr = nullptr; + a.PyUnicode_InternFromString_Ptr = b.PyUnicode_InternFromString_Ptr; + b.PyUnicode_InternFromString_Ptr = nullptr; + a.PyUnicode_InternImmortal_Ptr = b.PyUnicode_InternImmortal_Ptr; + b.PyUnicode_InternImmortal_Ptr = nullptr; + a.PyUnicode_InternInPlace_Ptr = b.PyUnicode_InternInPlace_Ptr; + b.PyUnicode_InternInPlace_Ptr = nullptr; + a.PyUnicode_IsIdentifier_Ptr = b.PyUnicode_IsIdentifier_Ptr; + b.PyUnicode_IsIdentifier_Ptr = nullptr; + a.PyUnicode_Join_Ptr = b.PyUnicode_Join_Ptr; + b.PyUnicode_Join_Ptr = nullptr; + a.PyUnicode_Partition_Ptr = b.PyUnicode_Partition_Ptr; + b.PyUnicode_Partition_Ptr = nullptr; + a.PyUnicode_RPartition_Ptr = b.PyUnicode_RPartition_Ptr; + b.PyUnicode_RPartition_Ptr = nullptr; + a.PyUnicode_RSplit_Ptr = b.PyUnicode_RSplit_Ptr; + b.PyUnicode_RSplit_Ptr = nullptr; + a.PyUnicode_ReadChar_Ptr = b.PyUnicode_ReadChar_Ptr; + b.PyUnicode_ReadChar_Ptr = nullptr; + a.PyUnicode_Replace_Ptr = b.PyUnicode_Replace_Ptr; + b.PyUnicode_Replace_Ptr = nullptr; + a.PyUnicode_Resize_Ptr = b.PyUnicode_Resize_Ptr; + b.PyUnicode_Resize_Ptr = nullptr; + a.PyUnicode_RichCompare_Ptr = b.PyUnicode_RichCompare_Ptr; + b.PyUnicode_RichCompare_Ptr = nullptr; + a.PyUnicode_Split_Ptr = b.PyUnicode_Split_Ptr; + b.PyUnicode_Split_Ptr = nullptr; + a.PyUnicode_Splitlines_Ptr = b.PyUnicode_Splitlines_Ptr; + b.PyUnicode_Splitlines_Ptr = nullptr; + a.PyUnicode_Substring_Ptr = b.PyUnicode_Substring_Ptr; + b.PyUnicode_Substring_Ptr = nullptr; + a.PyUnicode_Tailmatch_Ptr = b.PyUnicode_Tailmatch_Ptr; + b.PyUnicode_Tailmatch_Ptr = nullptr; + a.PyUnicode_Translate_Ptr = b.PyUnicode_Translate_Ptr; + b.PyUnicode_Translate_Ptr = nullptr; + a.PyUnicode_WriteChar_Ptr = b.PyUnicode_WriteChar_Ptr; + b.PyUnicode_WriteChar_Ptr = nullptr; + a.PyWeakref_GetObject_Ptr = b.PyWeakref_GetObject_Ptr; + b.PyWeakref_GetObject_Ptr = nullptr; + a.PyWeakref_NewProxy_Ptr = b.PyWeakref_NewProxy_Ptr; + b.PyWeakref_NewProxy_Ptr = nullptr; + a.PyWeakref_NewRef_Ptr = b.PyWeakref_NewRef_Ptr; + b.PyWeakref_NewRef_Ptr = nullptr; + a.PyWrapper_New_Ptr = b.PyWrapper_New_Ptr; + b.PyWrapper_New_Ptr = nullptr; + a.Py_AddPendingCall_Ptr = b.Py_AddPendingCall_Ptr; + b.Py_AddPendingCall_Ptr = nullptr; + a.Py_AtExit_Ptr = b.Py_AtExit_Ptr; + b.Py_AtExit_Ptr = nullptr; + a.Py_BuildValue_Ptr = b.Py_BuildValue_Ptr; + b.Py_BuildValue_Ptr = nullptr; + a.Py_BytesMain_Ptr = b.Py_BytesMain_Ptr; + b.Py_BytesMain_Ptr = nullptr; + a.Py_CompileString_Ptr = b.Py_CompileString_Ptr; + b.Py_CompileString_Ptr = nullptr; + a.Py_DecRef_Ptr = b.Py_DecRef_Ptr; + b.Py_DecRef_Ptr = nullptr; + a.Py_DecodeLocale_Ptr = b.Py_DecodeLocale_Ptr; + b.Py_DecodeLocale_Ptr = nullptr; + a.Py_EncodeLocale_Ptr = b.Py_EncodeLocale_Ptr; + b.Py_EncodeLocale_Ptr = nullptr; + a.Py_EndInterpreter_Ptr = b.Py_EndInterpreter_Ptr; + b.Py_EndInterpreter_Ptr = nullptr; + a.Py_EnterRecursiveCall_Ptr = b.Py_EnterRecursiveCall_Ptr; + b.Py_EnterRecursiveCall_Ptr = nullptr; + a.Py_Exit_Ptr = b.Py_Exit_Ptr; + b.Py_Exit_Ptr = nullptr; + a.Py_FatalError_Ptr = b.Py_FatalError_Ptr; + b.Py_FatalError_Ptr = nullptr; + a.Py_Finalize_Ptr = b.Py_Finalize_Ptr; + b.Py_Finalize_Ptr = nullptr; + a.Py_FinalizeEx_Ptr = b.Py_FinalizeEx_Ptr; + b.Py_FinalizeEx_Ptr = nullptr; + a.Py_GenericAlias_Ptr = b.Py_GenericAlias_Ptr; + b.Py_GenericAlias_Ptr = nullptr; + a.Py_GetBuildInfo_Ptr = b.Py_GetBuildInfo_Ptr; + b.Py_GetBuildInfo_Ptr = nullptr; + a.Py_GetCompiler_Ptr = b.Py_GetCompiler_Ptr; + b.Py_GetCompiler_Ptr = nullptr; + a.Py_GetCopyright_Ptr = b.Py_GetCopyright_Ptr; + b.Py_GetCopyright_Ptr = nullptr; + a.Py_GetExecPrefix_Ptr = b.Py_GetExecPrefix_Ptr; + b.Py_GetExecPrefix_Ptr = nullptr; + a.Py_GetPath_Ptr = b.Py_GetPath_Ptr; + b.Py_GetPath_Ptr = nullptr; + a.Py_GetPlatform_Ptr = b.Py_GetPlatform_Ptr; + b.Py_GetPlatform_Ptr = nullptr; + a.Py_GetPrefix_Ptr = b.Py_GetPrefix_Ptr; + b.Py_GetPrefix_Ptr = nullptr; + a.Py_GetProgramFullPath_Ptr = b.Py_GetProgramFullPath_Ptr; + b.Py_GetProgramFullPath_Ptr = nullptr; + a.Py_GetProgramName_Ptr = b.Py_GetProgramName_Ptr; + b.Py_GetProgramName_Ptr = nullptr; + a.Py_GetPythonHome_Ptr = b.Py_GetPythonHome_Ptr; + b.Py_GetPythonHome_Ptr = nullptr; + a.Py_GetRecursionLimit_Ptr = b.Py_GetRecursionLimit_Ptr; + b.Py_GetRecursionLimit_Ptr = nullptr; + a.Py_GetVersion_Ptr = b.Py_GetVersion_Ptr; + b.Py_GetVersion_Ptr = nullptr; + a.Py_IncRef_Ptr = b.Py_IncRef_Ptr; + b.Py_IncRef_Ptr = nullptr; + a.Py_Initialize_Ptr = b.Py_Initialize_Ptr; + b.Py_Initialize_Ptr = nullptr; + a.Py_InitializeEx_Ptr = b.Py_InitializeEx_Ptr; + b.Py_InitializeEx_Ptr = nullptr; + a.Py_Is_Ptr = b.Py_Is_Ptr; + b.Py_Is_Ptr = nullptr; + a.Py_IsInitialized_Ptr = b.Py_IsInitialized_Ptr; + b.Py_IsInitialized_Ptr = nullptr; + a.Py_LeaveRecursiveCall_Ptr = b.Py_LeaveRecursiveCall_Ptr; + b.Py_LeaveRecursiveCall_Ptr = nullptr; + a.Py_Main_Ptr = b.Py_Main_Ptr; + b.Py_Main_Ptr = nullptr; + a.Py_MakePendingCalls_Ptr = b.Py_MakePendingCalls_Ptr; + b.Py_MakePendingCalls_Ptr = nullptr; + a.Py_NewInterpreter_Ptr = b.Py_NewInterpreter_Ptr; + b.Py_NewInterpreter_Ptr = nullptr; + a.Py_NewRef_Ptr = b.Py_NewRef_Ptr; + b.Py_NewRef_Ptr = nullptr; + a.Py_ReprEnter_Ptr = b.Py_ReprEnter_Ptr; + b.Py_ReprEnter_Ptr = nullptr; + a.Py_ReprLeave_Ptr = b.Py_ReprLeave_Ptr; + b.Py_ReprLeave_Ptr = nullptr; + a.Py_SetPath_Ptr = b.Py_SetPath_Ptr; + b.Py_SetPath_Ptr = nullptr; + a.Py_SetProgramName_Ptr = b.Py_SetProgramName_Ptr; + b.Py_SetProgramName_Ptr = nullptr; + a.Py_SetPythonHome_Ptr = b.Py_SetPythonHome_Ptr; + b.Py_SetPythonHome_Ptr = nullptr; + a.Py_SetRecursionLimit_Ptr = b.Py_SetRecursionLimit_Ptr; + b.Py_SetRecursionLimit_Ptr = nullptr; + a.Py_VaBuildValue_Ptr = b.Py_VaBuildValue_Ptr; + b.Py_VaBuildValue_Ptr = nullptr; + #if HAS_PYTHON_VERSION(0x030A0000) + a.Py_XNewRef_Ptr = b.Py_XNewRef_Ptr; + b.Py_XNewRef_Ptr = nullptr; + #endif +} + +Python::Python() : module(nullptr) +{ + #if defined(_WIN32) || defined(_WIN64) + //extern HMODULE GetModuleHandle(const char* module_name) noexcept; + #else + extern void* GetModuleHandle(const char* module_name) noexcept; + #endif + + #if defined(_WIN32) || defined(_WIN64) + std::vector module_names = GetLoadedModuleNames("python3"); + if (module_names.empty()) + { + return; + } + + for (const auto &module_name : module_names) + { + if (module = GetModuleHandle(module_name.c_str()); module != nullptr) + { + break; + } + } + + auto address_of = [](HMODULE module, const char* fn_name) { + #if defined(_WIN32) || defined(_WIN64) + return GetProcAddress(module, fn_name); + #else + return dlsym(module, fn_name); + #endif + }; + #else + module = RTLD_DEFAULT; + auto address_of = [](void* module, const char* fn_name) { + #if defined(_WIN32) || defined(_WIN64) + return GetProcAddress(module, fn_name); + #else + return dlsym(module, fn_name); + #endif + }; + #endif + + /// WARNING: PYTHON PRIVATE API! + Py_NoneStruct_Ptr = reinterpret_cast(address_of(module, "_Py_NoneStruct")); + Py_TrueStruct_Ptr = reinterpret_cast(address_of(module, "_Py_TrueStruct")); + Py_FalseStruct_Ptr = reinterpret_cast(address_of(module, "_Py_FalseStruct")); + Py_ListType_Ptr = reinterpret_cast(address_of(module, "PyList_Type")); + + PyAIter_Check_Ptr = reinterpret_cast(address_of(module, "PyAIter_Check")); + PyArg_Parse_Ptr = reinterpret_cast(address_of(module, "PyArg_Parse")); + PyArg_ParseTuple_Ptr = reinterpret_cast(address_of(module, "PyArg_ParseTuple")); + PyArg_ParseTupleAndKeywords_Ptr = reinterpret_cast(address_of(module, "PyArg_ParseTupleAndKeywords")); + PyArg_UnpackTuple_Ptr = reinterpret_cast(address_of(module, "PyArg_UnpackTuple")); + PyArg_VaParse_Ptr = reinterpret_cast(address_of(module, "PyArg_VaParse")); + PyArg_VaParseTupleAndKeywords_Ptr = reinterpret_cast(address_of(module, "PyArg_VaParseTupleAndKeywords")); + PyArg_ValidateKeywordArguments_Ptr = reinterpret_cast(address_of(module, "PyArg_ValidateKeywordArguments")); + PyBool_FromLong_Ptr = reinterpret_cast(address_of(module, "PyBool_FromLong")); + #if HAS_PYTHON_VERSION(0x030B1000) + PyBuffer_FillContiguousStrides_Ptr = reinterpret_cast(address_of(module, "PyBuffer_FillContiguousStrides")); + PyBuffer_FillInfo_Ptr = reinterpret_cast(address_of(module, "PyBuffer_FillInfo")); + PyBuffer_FromContiguous_Ptr = reinterpret_cast(address_of(module, "PyBuffer_FromContiguous")); + PyBuffer_GetPointer_Ptr = reinterpret_cast(address_of(module, "PyBuffer_GetPointer")); + PyBuffer_IsContiguous_Ptr = reinterpret_cast(address_of(module, "PyBuffer_IsContiguous")); + PyBuffer_Release_Ptr = reinterpret_cast(address_of(module, "PyBuffer_Release")); + PyBuffer_SizeFromFormat_Ptr = reinterpret_cast(address_of(module, "PyBuffer_SizeFromFormat")); + PyBuffer_ToContiguous_Ptr = reinterpret_cast(address_of(module, "PyBuffer_ToContiguous")); + #endif + PyByteArray_AsString_Ptr = reinterpret_cast(address_of(module, "PyByteArray_AsString")); + PyByteArray_Concat_Ptr = reinterpret_cast(address_of(module, "PyByteArray_Concat")); + PyByteArray_FromObject_Ptr = reinterpret_cast(address_of(module, "PyByteArray_FromObject")); + PyByteArray_FromStringAndSize_Ptr = reinterpret_cast(address_of(module, "PyByteArray_FromStringAndSize")); + PyByteArray_Resize_Ptr = reinterpret_cast(address_of(module, "PyByteArray_Resize")); + PyByteArray_Size_Ptr = reinterpret_cast(address_of(module, "PyByteArray_Size")); + PyBytes_AsString_Ptr = reinterpret_cast(address_of(module, "PyBytes_AsString")); + PyBytes_AsStringAndSize_Ptr = reinterpret_cast(address_of(module, "PyBytes_AsStringAndSize")); + PyBytes_Concat_Ptr = reinterpret_cast(address_of(module, "PyBytes_Concat")); + PyBytes_ConcatAndDel_Ptr = reinterpret_cast(address_of(module, "PyBytes_ConcatAndDel")); + PyBytes_DecodeEscape_Ptr = reinterpret_cast(address_of(module, "PyBytes_DecodeEscape")); + PyBytes_FromFormat_Ptr = reinterpret_cast(address_of(module, "PyBytes_FromFormat")); + PyBytes_FromFormatV_Ptr = reinterpret_cast(address_of(module, "PyBytes_FromFormatV")); + PyBytes_FromObject_Ptr = reinterpret_cast(address_of(module, "PyBytes_FromObject")); + PyBytes_FromString_Ptr = reinterpret_cast(address_of(module, "PyBytes_FromString")); + PyBytes_FromStringAndSize_Ptr = reinterpret_cast(address_of(module, "PyBytes_FromStringAndSize")); + PyBytes_Repr_Ptr = reinterpret_cast(address_of(module, "PyBytes_Repr")); + PyBytes_Size_Ptr = reinterpret_cast(address_of(module, "PyBytes_Size")); + PyCFunction_Call_Ptr = reinterpret_cast(address_of(module, "PyCFunction_Call")); + PyCFunction_GetFlags_Ptr = reinterpret_cast(address_of(module, "PyCFunction_GetFlags")); + PyCFunction_GetFunction_Ptr = reinterpret_cast(address_of(module, "PyCFunction_GetFunction")); + PyCFunction_GetSelf_Ptr = reinterpret_cast(address_of(module, "PyCFunction_GetSelf")); + PyCFunction_New_Ptr = reinterpret_cast(address_of(module, "PyCFunction_New")); + #if HAS_PYTHON_VERSION(0x03090000) + PyCFunction_NewEx_Ptr = reinterpret_cast(address_of(module, "PyCFunction_NewEx")); + #endif + PyCMethod_New_Ptr = reinterpret_cast(address_of(module, "PyCMethod_New")); + PyCallIter_New_Ptr = reinterpret_cast(address_of(module, "PyCallIter_New")); + PyCallable_Check_Ptr = reinterpret_cast(address_of(module, "PyCallable_Check")); + PyCapsule_GetContext_Ptr = reinterpret_cast(address_of(module, "PyCapsule_GetContext")); + PyCapsule_GetDestructor_Ptr = reinterpret_cast(address_of(module, "PyCapsule_GetDestructor")); + PyCapsule_GetName_Ptr = reinterpret_cast(address_of(module, "PyCapsule_GetName")); + PyCapsule_GetPointer_Ptr = reinterpret_cast(address_of(module, "PyCapsule_GetPointer")); + PyCapsule_Import_Ptr = reinterpret_cast(address_of(module, "PyCapsule_Import")); + PyCapsule_IsValid_Ptr = reinterpret_cast(address_of(module, "PyCapsule_IsValid")); + PyCapsule_New_Ptr = reinterpret_cast(address_of(module, "PyCapsule_New")); + PyCapsule_SetContext_Ptr = reinterpret_cast(address_of(module, "PyCapsule_SetContext")); + PyCapsule_SetDestructor_Ptr = reinterpret_cast(address_of(module, "PyCapsule_SetDestructor")); + PyCapsule_SetName_Ptr = reinterpret_cast(address_of(module, "PyCapsule_SetName")); + PyCapsule_SetPointer_Ptr = reinterpret_cast(address_of(module, "PyCapsule_SetPointer")); + PyComplex_FromDoubles_Ptr = reinterpret_cast(address_of(module, "PyComplex_FromDoubles")); + PyComplex_ImagAsDouble_Ptr = reinterpret_cast(address_of(module, "PyComplex_ImagAsDouble")); + PyComplex_RealAsDouble_Ptr = reinterpret_cast(address_of(module, "PyComplex_RealAsDouble")); + PyDescr_NewClassMethod_Ptr = reinterpret_cast(address_of(module, "PyDescr_NewClassMethod")); + PyDescr_NewGetSet_Ptr = reinterpret_cast(address_of(module, "PyDescr_NewGetSet")); + PyDescr_NewMember_Ptr = reinterpret_cast(address_of(module, "PyDescr_NewMember")); + PyDescr_NewMethod_Ptr = reinterpret_cast(address_of(module, "PyDescr_NewMethod")); + PyDictProxy_New_Ptr = reinterpret_cast(address_of(module, "PyDictProxy_New")); + PyDict_Clear_Ptr = reinterpret_cast(address_of(module, "PyDict_Clear")); + PyDict_Contains_Ptr = reinterpret_cast(address_of(module, "PyDict_Contains")); + PyDict_Copy_Ptr = reinterpret_cast(address_of(module, "PyDict_Copy")); + PyDict_DelItem_Ptr = reinterpret_cast(address_of(module, "PyDict_DelItem")); + PyDict_DelItemString_Ptr = reinterpret_cast(address_of(module, "PyDict_DelItemString")); + PyDict_GetItem_Ptr = reinterpret_cast(address_of(module, "PyDict_GetItem")); + PyDict_GetItemString_Ptr = reinterpret_cast(address_of(module, "PyDict_GetItemString")); + PyDict_GetItemWithError_Ptr = reinterpret_cast(address_of(module, "PyDict_GetItemWithError")); + PyDict_Items_Ptr = reinterpret_cast(address_of(module, "PyDict_Items")); + PyDict_Keys_Ptr = reinterpret_cast(address_of(module, "PyDict_Keys")); + PyDict_Merge_Ptr = reinterpret_cast(address_of(module, "PyDict_Merge")); + PyDict_MergeFromSeq2_Ptr = reinterpret_cast(address_of(module, "PyDict_MergeFromSeq2")); + PyDict_New_Ptr = reinterpret_cast(address_of(module, "PyDict_New")); + PyDict_Next_Ptr = reinterpret_cast(address_of(module, "PyDict_Next")); + PyDict_SetItem_Ptr = reinterpret_cast(address_of(module, "PyDict_SetItem")); + PyDict_SetItemString_Ptr = reinterpret_cast(address_of(module, "PyDict_SetItemString")); + PyDict_Size_Ptr = reinterpret_cast(address_of(module, "PyDict_Size")); + PyDict_Update_Ptr = reinterpret_cast(address_of(module, "PyDict_Update")); + PyDict_Values_Ptr = reinterpret_cast(address_of(module, "PyDict_Values")); + PyErr_BadArgument_Ptr = reinterpret_cast(address_of(module, "PyErr_BadArgument")); + #ifdef PyErr_BadInternalCall + _PyErr_BadInternalCall_Ptr = reinterpret_cast(address_of(module, "_PyErr_BadInternalCall")); + #endif + PyErr_CheckSignals_Ptr = reinterpret_cast(address_of(module, "PyErr_CheckSignals")); + PyErr_Clear_Ptr = reinterpret_cast(address_of(module, "PyErr_Clear")); + PyErr_Display_Ptr = reinterpret_cast(address_of(module, "PyErr_Display")); + PyErr_ExceptionMatches_Ptr = reinterpret_cast(address_of(module, "PyErr_ExceptionMatches")); + PyErr_Fetch_Ptr = reinterpret_cast(address_of(module, "PyErr_Fetch")); + PyErr_Format_Ptr = reinterpret_cast(address_of(module, "PyErr_Format")); + PyErr_FormatV_Ptr = reinterpret_cast(address_of(module, "PyErr_FormatV")); + PyErr_GetExcInfo_Ptr = reinterpret_cast(address_of(module, "PyErr_GetExcInfo")); + PyErr_GetHandledException_Ptr = reinterpret_cast(address_of(module, "PyErr_GetHandledException")); + PyErr_GivenExceptionMatches_Ptr = reinterpret_cast(address_of(module, "PyErr_GivenExceptionMatches")); + PyErr_NewException_Ptr = reinterpret_cast(address_of(module, "PyErr_NewException")); + PyErr_NewExceptionWithDoc_Ptr = reinterpret_cast(address_of(module, "PyErr_NewExceptionWithDoc")); + PyErr_NoMemory_Ptr = reinterpret_cast(address_of(module, "PyErr_NoMemory")); + PyErr_NormalizeException_Ptr = reinterpret_cast(address_of(module, "PyErr_NormalizeException")); + PyErr_Occurred_Ptr = reinterpret_cast(address_of(module, "PyErr_Occurred")); + PyErr_Print_Ptr = reinterpret_cast(address_of(module, "PyErr_Print")); + PyErr_PrintEx_Ptr = reinterpret_cast(address_of(module, "PyErr_PrintEx")); + PyErr_ProgramText_Ptr = reinterpret_cast(address_of(module, "PyErr_ProgramText")); + PyErr_ResourceWarning_Ptr = reinterpret_cast(address_of(module, "PyErr_ResourceWarning")); + PyErr_Restore_Ptr = reinterpret_cast(address_of(module, "PyErr_Restore")); + PyErr_SetExcInfo_Ptr = reinterpret_cast(address_of(module, "PyErr_SetExcInfo")); + PyErr_SetFromErrno_Ptr = reinterpret_cast(address_of(module, "PyErr_SetFromErrno")); + PyErr_SetFromErrnoWithFilename_Ptr = reinterpret_cast(address_of(module, "PyErr_SetFromErrnoWithFilename")); + PyErr_SetFromErrnoWithFilenameObject_Ptr = reinterpret_cast(address_of(module, "PyErr_SetFromErrnoWithFilenameObject")); + PyErr_SetFromErrnoWithFilenameObjects_Ptr = reinterpret_cast(address_of(module, "PyErr_SetFromErrnoWithFilenameObjects")); + PyErr_SetHandledException_Ptr = reinterpret_cast(address_of(module, "PyErr_SetHandledException")); + PyErr_SetImportError_Ptr = reinterpret_cast(address_of(module, "PyErr_SetImportError")); + PyErr_SetImportErrorSubclass_Ptr = reinterpret_cast(address_of(module, "PyErr_SetImportErrorSubclass")); + PyErr_SetInterrupt_Ptr = reinterpret_cast(address_of(module, "PyErr_SetInterrupt")); + PyErr_SetInterruptEx_Ptr = reinterpret_cast(address_of(module, "PyErr_SetInterruptEx")); + PyErr_SetNone_Ptr = reinterpret_cast(address_of(module, "PyErr_SetNone")); + PyErr_SetObject_Ptr = reinterpret_cast(address_of(module, "PyErr_SetObject")); + PyErr_SetString_Ptr = reinterpret_cast(address_of(module, "PyErr_SetString")); + PyErr_SyntaxLocation_Ptr = reinterpret_cast(address_of(module, "PyErr_SyntaxLocation")); + PyErr_SyntaxLocationEx_Ptr = reinterpret_cast(address_of(module, "PyErr_SyntaxLocationEx")); + PyErr_WarnEx_Ptr = reinterpret_cast(address_of(module, "PyErr_WarnEx")); + PyErr_WarnExplicit_Ptr = reinterpret_cast(address_of(module, "PyErr_WarnExplicit")); + PyErr_WarnFormat_Ptr = reinterpret_cast(address_of(module, "PyErr_WarnFormat")); + PyErr_WriteUnraisable_Ptr = reinterpret_cast(address_of(module, "PyErr_WriteUnraisable")); + PyEval_AcquireLock_Ptr = reinterpret_cast(address_of(module, "PyEval_AcquireLock")); + PyEval_AcquireThread_Ptr = reinterpret_cast(address_of(module, "PyEval_AcquireThread")); + PyEval_CallFunction_Ptr = reinterpret_cast(address_of(module, "PyEval_CallFunction")); + PyEval_CallMethod_Ptr = reinterpret_cast(address_of(module, "PyEval_CallMethod")); + PyEval_CallObjectWithKeywords_Ptr = reinterpret_cast(address_of(module, "PyEval_CallObjectWithKeywords")); + PyEval_EvalCode_Ptr = reinterpret_cast(address_of(module, "PyEval_EvalCode")); + PyEval_EvalCodeEx_Ptr = reinterpret_cast(address_of(module, "PyEval_EvalCodeEx")); + #if HAS_PYTHON_VERSION(0x03090000) + PyEval_EvalFrame_Ptr = reinterpret_cast(address_of(module, "PyEval_EvalFrame")); + PyEval_EvalFrameEx_Ptr = reinterpret_cast(address_of(module, "PyEval_EvalFrameEx")); + #endif + PyEval_GetBuiltins_Ptr = reinterpret_cast(address_of(module, "PyEval_GetBuiltins")); + #if HAS_PYTHON_VERSION(0x03090000) + PyEval_GetFrame_Ptr = reinterpret_cast(address_of(module, "PyEval_GetFrame")); + #endif + PyEval_GetFuncDesc_Ptr = reinterpret_cast(address_of(module, "PyEval_GetFuncDesc")); + PyEval_GetFuncName_Ptr = reinterpret_cast(address_of(module, "PyEval_GetFuncName")); + PyEval_GetGlobals_Ptr = reinterpret_cast(address_of(module, "PyEval_GetGlobals")); + PyEval_GetLocals_Ptr = reinterpret_cast(address_of(module, "PyEval_GetLocals")); + PyEval_InitThreads_Ptr = reinterpret_cast(address_of(module, "PyEval_InitThreads")); + PyEval_ReleaseLock_Ptr = reinterpret_cast(address_of(module, "PyEval_ReleaseLock")); + PyEval_ReleaseThread_Ptr = reinterpret_cast(address_of(module, "PyEval_ReleaseThread")); + PyEval_RestoreThread_Ptr = reinterpret_cast(address_of(module, "PyEval_RestoreThread")); + PyEval_SaveThread_Ptr = reinterpret_cast(address_of(module, "PyEval_SaveThread")); + PyEval_ThreadsInitialized_Ptr = reinterpret_cast(address_of(module, "PyEval_ThreadsInitialized")); + PyExceptionClass_Name_Ptr = reinterpret_cast(address_of(module, "PyExceptionClass_Name")); + PyException_GetCause_Ptr = reinterpret_cast(address_of(module, "PyException_GetCause")); + PyException_GetContext_Ptr = reinterpret_cast(address_of(module, "PyException_GetContext")); + PyException_GetTraceback_Ptr = reinterpret_cast(address_of(module, "PyException_GetTraceback")); + PyException_SetCause_Ptr = reinterpret_cast(address_of(module, "PyException_SetCause")); + PyException_SetContext_Ptr = reinterpret_cast(address_of(module, "PyException_SetContext")); + PyException_SetTraceback_Ptr = reinterpret_cast(address_of(module, "PyException_SetTraceback")); + PyFile_FromFd_Ptr = reinterpret_cast(address_of(module, "PyFile_FromFd")); + PyFile_GetLine_Ptr = reinterpret_cast(address_of(module, "PyFile_GetLine")); + PyFile_WriteObject_Ptr = reinterpret_cast(address_of(module, "PyFile_WriteObject")); + PyFile_WriteString_Ptr = reinterpret_cast(address_of(module, "PyFile_WriteString")); + PyFloat_AsDouble_Ptr = reinterpret_cast(address_of(module, "PyFloat_AsDouble")); + PyFloat_FromDouble_Ptr = reinterpret_cast(address_of(module, "PyFloat_FromDouble")); + PyFloat_FromString_Ptr = reinterpret_cast(address_of(module, "PyFloat_FromString")); + PyFloat_GetInfo_Ptr = reinterpret_cast(address_of(module, "PyFloat_GetInfo")); + PyFloat_GetMax_Ptr = reinterpret_cast(address_of(module, "PyFloat_GetMax")); + PyFloat_GetMin_Ptr = reinterpret_cast(address_of(module, "PyFloat_GetMin")); + PyFrozenSet_New_Ptr = reinterpret_cast(address_of(module, "PyFrozenSet_New")); + PyGC_Collect_Ptr = reinterpret_cast(address_of(module, "PyGC_Collect")); + PyGC_Disable_Ptr = reinterpret_cast(address_of(module, "PyGC_Disable")); + PyGC_Enable_Ptr = reinterpret_cast(address_of(module, "PyGC_Enable")); + PyGC_IsEnabled_Ptr = reinterpret_cast(address_of(module, "PyGC_IsEnabled")); + PyGILState_Ensure_Ptr = reinterpret_cast(address_of(module, "PyGILState_Ensure")); + PyGILState_GetThisThreadState_Ptr = reinterpret_cast(address_of(module, "PyGILState_GetThisThreadState")); + PyGILState_Release_Ptr = reinterpret_cast(address_of(module, "PyGILState_Release")); + PyImport_AddModule_Ptr = reinterpret_cast(address_of(module, "PyImport_AddModule")); + PyImport_AddModuleObject_Ptr = reinterpret_cast(address_of(module, "PyImport_AddModuleObject")); + PyImport_AppendInittab_Ptr = reinterpret_cast(address_of(module, "PyImport_AppendInittab")); + PyImport_ExecCodeModule_Ptr = reinterpret_cast(address_of(module, "PyImport_ExecCodeModule")); + PyImport_ExecCodeModuleEx_Ptr = reinterpret_cast(address_of(module, "PyImport_ExecCodeModuleEx")); + PyImport_ExecCodeModuleObject_Ptr = reinterpret_cast(address_of(module, "PyImport_ExecCodeModuleObject")); + PyImport_ExecCodeModuleWithPathnames_Ptr = reinterpret_cast(address_of(module, "PyImport_ExecCodeModuleWithPathnames")); + PyImport_GetImporter_Ptr = reinterpret_cast(address_of(module, "PyImport_GetImporter")); + PyImport_GetMagicNumber_Ptr = reinterpret_cast(address_of(module, "PyImport_GetMagicNumber")); + PyImport_GetMagicTag_Ptr = reinterpret_cast(address_of(module, "PyImport_GetMagicTag")); + PyImport_GetModule_Ptr = reinterpret_cast(address_of(module, "PyImport_GetModule")); + PyImport_GetModuleDict_Ptr = reinterpret_cast(address_of(module, "PyImport_GetModuleDict")); + PyImport_Import_Ptr = reinterpret_cast(address_of(module, "PyImport_Import")); + PyImport_ImportFrozenModule_Ptr = reinterpret_cast(address_of(module, "PyImport_ImportFrozenModule")); + PyImport_ImportFrozenModuleObject_Ptr = reinterpret_cast(address_of(module, "PyImport_ImportFrozenModuleObject")); + PyImport_ImportModule_Ptr = reinterpret_cast(address_of(module, "PyImport_ImportModule")); + PyImport_ImportModuleLevel_Ptr = reinterpret_cast(address_of(module, "PyImport_ImportModuleLevel")); + PyImport_ImportModuleLevelObject_Ptr = reinterpret_cast(address_of(module, "PyImport_ImportModuleLevelObject")); + PyImport_ImportModuleNoBlock_Ptr = reinterpret_cast(address_of(module, "PyImport_ImportModuleNoBlock")); + PyImport_ReloadModule_Ptr = reinterpret_cast(address_of(module, "PyImport_ReloadModule")); + PyIndex_Check_Ptr = reinterpret_cast(address_of(module, "PyIndex_Check")); + PyInterpreterState_Clear_Ptr = reinterpret_cast(address_of(module, "PyInterpreterState_Clear")); + PyInterpreterState_Delete_Ptr = reinterpret_cast(address_of(module, "PyInterpreterState_Delete")); + PyInterpreterState_Get_Ptr = reinterpret_cast(address_of(module, "PyInterpreterState_Get")); + PyInterpreterState_GetDict_Ptr = reinterpret_cast(address_of(module, "PyInterpreterState_GetDict")); + PyInterpreterState_GetID_Ptr = reinterpret_cast(address_of(module, "PyInterpreterState_GetID")); + PyInterpreterState_New_Ptr = reinterpret_cast(address_of(module, "PyInterpreterState_New")); + PyIter_Check_Ptr = reinterpret_cast(address_of(module, "PyIter_Check")); + PyIter_Next_Ptr = reinterpret_cast(address_of(module, "PyIter_Next")); + #if HAS_PYTHON_VERSION(0x030A0000) + PyIter_Send_Ptr = reinterpret_cast(address_of(module, "PyIter_Send")); + #endif + PyList_Append_Ptr = reinterpret_cast(address_of(module, "PyList_Append")); + PyList_AsTuple_Ptr = reinterpret_cast(address_of(module, "PyList_AsTuple")); + PyList_GetItem_Ptr = reinterpret_cast(address_of(module, "PyList_GetItem")); + PyList_GetSlice_Ptr = reinterpret_cast(address_of(module, "PyList_GetSlice")); + PyList_Insert_Ptr = reinterpret_cast(address_of(module, "PyList_Insert")); + PyList_New_Ptr = reinterpret_cast(address_of(module, "PyList_New")); + PyList_Reverse_Ptr = reinterpret_cast(address_of(module, "PyList_Reverse")); + PyList_SetItem_Ptr = reinterpret_cast(address_of(module, "PyList_SetItem")); + PyList_SetSlice_Ptr = reinterpret_cast(address_of(module, "PyList_SetSlice")); + PyList_Size_Ptr = reinterpret_cast(address_of(module, "PyList_Size")); + PyList_Sort_Ptr = reinterpret_cast(address_of(module, "PyList_Sort")); + PyLong_AsDouble_Ptr = reinterpret_cast(address_of(module, "PyLong_AsDouble")); + PyLong_AsLong_Ptr = reinterpret_cast(address_of(module, "PyLong_AsLong")); + PyLong_AsLongAndOverflow_Ptr = reinterpret_cast(address_of(module, "PyLong_AsLongAndOverflow")); + PyLong_AsLongLong_Ptr = reinterpret_cast(address_of(module, "PyLong_AsLongLong")); + PyLong_AsLongLongAndOverflow_Ptr = reinterpret_cast(address_of(module, "PyLong_AsLongLongAndOverflow")); + PyLong_AsSize_t_Ptr = reinterpret_cast(address_of(module, "PyLong_AsSize_t")); + PyLong_AsSsize_t_Ptr = reinterpret_cast(address_of(module, "PyLong_AsSsize_t")); + PyLong_AsUnsignedLong_Ptr = reinterpret_cast(address_of(module, "PyLong_AsUnsignedLong")); + PyLong_AsUnsignedLongLong_Ptr = reinterpret_cast(address_of(module, "PyLong_AsUnsignedLongLong")); + PyLong_AsUnsignedLongLongMask_Ptr = reinterpret_cast(address_of(module, "PyLong_AsUnsignedLongLongMask")); + PyLong_AsUnsignedLongMask_Ptr = reinterpret_cast(address_of(module, "PyLong_AsUnsignedLongMask")); + PyLong_AsVoidPtr_Ptr = reinterpret_cast(address_of(module, "PyLong_AsVoidPtr")); + PyLong_FromDouble_Ptr = reinterpret_cast(address_of(module, "PyLong_FromDouble")); + PyLong_FromLong_Ptr = reinterpret_cast(address_of(module, "PyLong_FromLong")); + PyLong_FromLongLong_Ptr = reinterpret_cast(address_of(module, "PyLong_FromLongLong")); + PyLong_FromSize_t_Ptr = reinterpret_cast(address_of(module, "PyLong_FromSize_t")); + PyLong_FromSsize_t_Ptr = reinterpret_cast(address_of(module, "PyLong_FromSsize_t")); + PyLong_FromString_Ptr = reinterpret_cast(address_of(module, "PyLong_FromString")); + PyLong_FromUnsignedLong_Ptr = reinterpret_cast(address_of(module, "PyLong_FromUnsignedLong")); + PyLong_FromUnsignedLongLong_Ptr = reinterpret_cast(address_of(module, "PyLong_FromUnsignedLongLong")); + PyLong_FromVoidPtr_Ptr = reinterpret_cast(address_of(module, "PyLong_FromVoidPtr")); + PyLong_GetInfo_Ptr = reinterpret_cast(address_of(module, "PyLong_GetInfo")); + PyMapping_Check_Ptr = reinterpret_cast(address_of(module, "PyMapping_Check")); + PyMapping_GetItemString_Ptr = reinterpret_cast(address_of(module, "PyMapping_GetItemString")); + PyMapping_HasKey_Ptr = reinterpret_cast(address_of(module, "PyMapping_HasKey")); + PyMapping_HasKeyString_Ptr = reinterpret_cast(address_of(module, "PyMapping_HasKeyString")); + PyMapping_Items_Ptr = reinterpret_cast(address_of(module, "PyMapping_Items")); + PyMapping_Keys_Ptr = reinterpret_cast(address_of(module, "PyMapping_Keys")); + #ifdef PyMapping_Length + PyMapping_Length_Ptr = reinterpret_cast(address_of(module, "PyMapping_Size")); + #else + PyMapping_Length_Ptr = nullptr; + #endif + PyMapping_SetItemString_Ptr = reinterpret_cast(address_of(module, "PyMapping_SetItemString")); + PyMapping_Size_Ptr = reinterpret_cast(address_of(module, "PyMapping_Size")); + PyMapping_Values_Ptr = reinterpret_cast(address_of(module, "PyMapping_Values")); + PyMem_Calloc_Ptr = reinterpret_cast(address_of(module, "PyMem_Calloc")); + PyMem_Free_Ptr = reinterpret_cast(address_of(module, "PyMem_Free")); + PyMem_Malloc_Ptr = reinterpret_cast(address_of(module, "PyMem_Malloc")); + PyMem_Realloc_Ptr = reinterpret_cast(address_of(module, "PyMem_Realloc")); + #if HAS_PYTHON_VERSION(0x030B1000) + PyMemoryView_FromBuffer_Ptr = reinterpret_cast(address_of(module, "PyMemoryView_FromBuffer")); + #endif + PyMemoryView_FromMemory_Ptr = reinterpret_cast(address_of(module, "PyMemoryView_FromMemory")); + PyMemoryView_FromObject_Ptr = reinterpret_cast(address_of(module, "PyMemoryView_FromObject")); + PyMemoryView_GetContiguous_Ptr = reinterpret_cast(address_of(module, "PyMemoryView_GetContiguous")); + PyModuleDef_Init_Ptr = reinterpret_cast(address_of(module, "PyModuleDef_Init")); + PyModule_AddFunctions_Ptr = reinterpret_cast(address_of(module, "PyModule_AddFunctions")); + PyModule_AddIntConstant_Ptr = reinterpret_cast(address_of(module, "PyModule_AddIntConstant")); + PyModule_AddObject_Ptr = reinterpret_cast(address_of(module, "PyModule_AddObject")); + PyModule_AddObjectRef_Ptr = reinterpret_cast(address_of(module, "PyModule_AddObjectRef")); + PyModule_AddStringConstant_Ptr = reinterpret_cast(address_of(module, "PyModule_AddStringConstant")); + PyModule_AddType_Ptr = reinterpret_cast(address_of(module, "PyModule_AddType")); + PyModule_Create2_Ptr = reinterpret_cast(address_of(module, "PyModule_Create2")); + PyModule_ExecDef_Ptr = reinterpret_cast(address_of(module, "PyModule_ExecDef")); + PyModule_FromDefAndSpec2_Ptr = reinterpret_cast(address_of(module, "PyModule_FromDefAndSpec2")); + PyModule_GetDef_Ptr = reinterpret_cast(address_of(module, "PyModule_GetDef")); + PyModule_GetDict_Ptr = reinterpret_cast(address_of(module, "PyModule_GetDict")); + PyModule_GetFilename_Ptr = reinterpret_cast(address_of(module, "PyModule_GetFilename")); + PyModule_GetFilenameObject_Ptr = reinterpret_cast(address_of(module, "PyModule_GetFilenameObject")); + PyModule_GetName_Ptr = reinterpret_cast(address_of(module, "PyModule_GetName")); + PyModule_GetNameObject_Ptr = reinterpret_cast(address_of(module, "PyModule_GetNameObject")); + PyModule_GetState_Ptr = reinterpret_cast(address_of(module, "PyModule_GetState")); + PyModule_New_Ptr = reinterpret_cast(address_of(module, "PyModule_New")); + PyModule_NewObject_Ptr = reinterpret_cast(address_of(module, "PyModule_NewObject")); + PyModule_SetDocString_Ptr = reinterpret_cast(address_of(module, "PyModule_SetDocString")); + PyNumber_Absolute_Ptr = reinterpret_cast(address_of(module, "PyNumber_Absolute")); + PyNumber_Add_Ptr = reinterpret_cast(address_of(module, "PyNumber_Add")); + PyNumber_And_Ptr = reinterpret_cast(address_of(module, "PyNumber_And")); + PyNumber_AsSsize_t_Ptr = reinterpret_cast(address_of(module, "PyNumber_AsSsize_t")); + PyNumber_Check_Ptr = reinterpret_cast(address_of(module, "PyNumber_Check")); + PyNumber_Divmod_Ptr = reinterpret_cast(address_of(module, "PyNumber_Divmod")); + PyNumber_Float_Ptr = reinterpret_cast(address_of(module, "PyNumber_Float")); + PyNumber_FloorDivide_Ptr = reinterpret_cast(address_of(module, "PyNumber_FloorDivide")); + PyNumber_InPlaceAdd_Ptr = reinterpret_cast(address_of(module, "PyNumber_InPlaceAdd")); + PyNumber_InPlaceAnd_Ptr = reinterpret_cast(address_of(module, "PyNumber_InPlaceAnd")); + PyNumber_InPlaceFloorDivide_Ptr = reinterpret_cast(address_of(module, "PyNumber_InPlaceFloorDivide")); + PyNumber_InPlaceLshift_Ptr = reinterpret_cast(address_of(module, "PyNumber_InPlaceLshift")); + PyNumber_InPlaceMatrixMultiply_Ptr = reinterpret_cast(address_of(module, "PyNumber_InPlaceMatrixMultiply")); + PyNumber_InPlaceMultiply_Ptr = reinterpret_cast(address_of(module, "PyNumber_InPlaceMultiply")); + PyNumber_InPlaceOr_Ptr = reinterpret_cast(address_of(module, "PyNumber_InPlaceOr")); + PyNumber_InPlacePower_Ptr = reinterpret_cast(address_of(module, "PyNumber_InPlacePower")); + PyNumber_InPlaceRemainder_Ptr = reinterpret_cast(address_of(module, "PyNumber_InPlaceRemainder")); + PyNumber_InPlaceRshift_Ptr = reinterpret_cast(address_of(module, "PyNumber_InPlaceRshift")); + PyNumber_InPlaceSubtract_Ptr = reinterpret_cast(address_of(module, "PyNumber_InPlaceSubtract")); + PyNumber_InPlaceTrueDivide_Ptr = reinterpret_cast(address_of(module, "PyNumber_InPlaceTrueDivide")); + PyNumber_InPlaceXor_Ptr = reinterpret_cast(address_of(module, "PyNumber_InPlaceXor")); + PyNumber_Index_Ptr = reinterpret_cast(address_of(module, "PyNumber_Index")); + PyNumber_Invert_Ptr = reinterpret_cast(address_of(module, "PyNumber_Invert")); + PyNumber_Long_Ptr = reinterpret_cast(address_of(module, "PyNumber_Long")); + PyNumber_Lshift_Ptr = reinterpret_cast(address_of(module, "PyNumber_Lshift")); + PyNumber_MatrixMultiply_Ptr = reinterpret_cast(address_of(module, "PyNumber_MatrixMultiply")); + PyNumber_Multiply_Ptr = reinterpret_cast(address_of(module, "PyNumber_Multiply")); + PyNumber_Negative_Ptr = reinterpret_cast(address_of(module, "PyNumber_Negative")); + PyNumber_Or_Ptr = reinterpret_cast(address_of(module, "PyNumber_Or")); + PyNumber_Positive_Ptr = reinterpret_cast(address_of(module, "PyNumber_Positive")); + PyNumber_Power_Ptr = reinterpret_cast(address_of(module, "PyNumber_Power")); + PyNumber_Remainder_Ptr = reinterpret_cast(address_of(module, "PyNumber_Remainder")); + PyNumber_Rshift_Ptr = reinterpret_cast(address_of(module, "PyNumber_Rshift")); + PyNumber_Subtract_Ptr = reinterpret_cast(address_of(module, "PyNumber_Subtract")); + PyNumber_ToBase_Ptr = reinterpret_cast(address_of(module, "PyNumber_ToBase")); + PyNumber_TrueDivide_Ptr = reinterpret_cast(address_of(module, "PyNumber_TrueDivide")); + PyNumber_Xor_Ptr = reinterpret_cast(address_of(module, "PyNumber_Xor")); + PyOS_AfterFork_Ptr = reinterpret_cast(address_of(module, "PyOS_AfterFork")); + PyOS_AfterFork_Child_Ptr = reinterpret_cast(address_of(module, "PyOS_AfterFork_Child")); + PyOS_AfterFork_Parent_Ptr = reinterpret_cast(address_of(module, "PyOS_AfterFork_Parent")); + PyOS_BeforeFork_Ptr = reinterpret_cast(address_of(module, "PyOS_BeforeFork")); + PyOS_FSPath_Ptr = reinterpret_cast(address_of(module, "PyOS_FSPath")); + PyOS_InterruptOccurred_Ptr = reinterpret_cast(address_of(module, "PyOS_InterruptOccurred")); + PyOS_double_to_string_Ptr = reinterpret_cast(address_of(module, "PyOS_double_to_string")); + PyOS_getsig_Ptr = reinterpret_cast(address_of(module, "PyOS_getsig")); + PyOS_mystricmp_Ptr = reinterpret_cast(address_of(module, "PyOS_mystricmp")); + PyOS_mystrnicmp_Ptr = reinterpret_cast(address_of(module, "PyOS_mystrnicmp")); + PyOS_setsig_Ptr = reinterpret_cast(address_of(module, "PyOS_setsig")); + PyOS_snprintf_Ptr = reinterpret_cast(address_of(module, "PyOS_snprintf")); + PyOS_string_to_double_Ptr = reinterpret_cast(address_of(module, "PyOS_string_to_double")); + PyOS_strtol_Ptr = reinterpret_cast(address_of(module, "PyOS_strtol")); + PyOS_strtoul_Ptr = reinterpret_cast(address_of(module, "PyOS_strtoul")); + PyOS_vsnprintf_Ptr = reinterpret_cast(address_of(module, "PyOS_vsnprintf")); + PyObject_ASCII_Ptr = reinterpret_cast(address_of(module, "PyObject_ASCII")); + PyObject_AsCharBuffer_Ptr = reinterpret_cast(address_of(module, "PyObject_AsCharBuffer")); + PyObject_AsFileDescriptor_Ptr = reinterpret_cast(address_of(module, "PyObject_AsFileDescriptor")); + PyObject_AsReadBuffer_Ptr = reinterpret_cast(address_of(module, "PyObject_AsReadBuffer")); + PyObject_AsWriteBuffer_Ptr = reinterpret_cast(address_of(module, "PyObject_AsWriteBuffer")); + PyObject_Bytes_Ptr = reinterpret_cast(address_of(module, "PyObject_Bytes")); + PyObject_Call_Ptr = reinterpret_cast(address_of(module, "PyObject_Call")); + PyObject_CallFunction_Ptr = reinterpret_cast(address_of(module, "PyObject_CallFunction")); + PyObject_CallFunctionObjArgs_Ptr = reinterpret_cast(address_of(module, "PyObject_CallFunctionObjArgs")); + PyObject_CallMethod_Ptr = reinterpret_cast(address_of(module, "PyObject_CallMethod")); + PyObject_CallMethodObjArgs_Ptr = reinterpret_cast(address_of(module, "PyObject_CallMethodObjArgs")); + PyObject_CallNoArgs_Ptr = reinterpret_cast(address_of(module, "PyObject_CallNoArgs")); + PyObject_CallObject_Ptr = reinterpret_cast(address_of(module, "PyObject_CallObject")); + PyObject_Calloc_Ptr = reinterpret_cast(address_of(module, "PyObject_Calloc")); + PyObject_CheckBuffer_Ptr = reinterpret_cast(address_of(module, "PyObject_CheckBuffer")); + PyObject_CheckReadBuffer_Ptr = reinterpret_cast(address_of(module, "PyObject_CheckReadBuffer")); + PyObject_ClearWeakRefs_Ptr = reinterpret_cast(address_of(module, "PyObject_ClearWeakRefs")); + PyObject_CopyData_Ptr = reinterpret_cast(address_of(module, "PyObject_CopyData")); + PyObject_DelItem_Ptr = reinterpret_cast(address_of(module, "PyObject_DelItem")); + PyObject_DelItemString_Ptr = reinterpret_cast(address_of(module, "PyObject_DelItemString")); + PyObject_Dir_Ptr = reinterpret_cast(address_of(module, "PyObject_Dir")); + PyObject_Format_Ptr = reinterpret_cast(address_of(module, "PyObject_Format")); + PyObject_Free_Ptr = reinterpret_cast(address_of(module, "PyObject_Free")); + PyObject_GC_Del_Ptr = reinterpret_cast(address_of(module, "PyObject_GC_Del")); + PyObject_GC_IsFinalized_Ptr = reinterpret_cast(address_of(module, "PyObject_GC_IsFinalized")); + PyObject_GC_IsTracked_Ptr = reinterpret_cast(address_of(module, "PyObject_GC_IsTracked")); + PyObject_GC_Track_Ptr = reinterpret_cast(address_of(module, "PyObject_GC_Track")); + PyObject_GC_UnTrack_Ptr = reinterpret_cast(address_of(module, "PyObject_GC_UnTrack")); + PyObject_GenericGetAttr_Ptr = reinterpret_cast(address_of(module, "PyObject_GenericGetAttr")); + PyObject_GenericGetDict_Ptr = reinterpret_cast(address_of(module, "PyObject_GenericGetDict")); + PyObject_GenericSetAttr_Ptr = reinterpret_cast(address_of(module, "PyObject_GenericSetAttr")); + PyObject_GenericSetDict_Ptr = reinterpret_cast(address_of(module, "PyObject_GenericSetDict")); + PyObject_GetAIter_Ptr = reinterpret_cast(address_of(module, "PyObject_GetAIter")); + PyObject_GetAttr_Ptr = reinterpret_cast(address_of(module, "PyObject_GetAttr")); + PyObject_GetAttrString_Ptr = reinterpret_cast(address_of(module, "PyObject_GetAttrString")); + #if HAS_PYTHON_VERSION(0x030B1000) + PyObject_GetBuffer_Ptr = reinterpret_cast(address_of(module, "PyObject_GetBuffer")); + #endif + PyObject_GetItem_Ptr = reinterpret_cast(address_of(module, "PyObject_GetItem")); + PyObject_GetIter_Ptr = reinterpret_cast(address_of(module, "PyObject_GetIter")); + PyObject_HasAttr_Ptr = reinterpret_cast(address_of(module, "PyObject_HasAttr")); + PyObject_HasAttrString_Ptr = reinterpret_cast(address_of(module, "PyObject_HasAttrString")); + PyObject_Hash_Ptr = reinterpret_cast(address_of(module, "PyObject_Hash")); + PyObject_HashNotImplemented_Ptr = reinterpret_cast(address_of(module, "PyObject_HashNotImplemented")); + PyObject_Init_Ptr = reinterpret_cast(address_of(module, "PyObject_Init")); + PyObject_InitVar_Ptr = reinterpret_cast(address_of(module, "PyObject_InitVar")); + PyObject_IsInstance_Ptr = reinterpret_cast(address_of(module, "PyObject_IsInstance")); + PyObject_IsSubclass_Ptr = reinterpret_cast(address_of(module, "PyObject_IsSubclass")); + PyObject_IsTrue_Ptr = reinterpret_cast(address_of(module, "PyObject_IsTrue")); + PyObject_Length_Ptr = reinterpret_cast(address_of(module, "PyObject_Length")); + PyObject_Malloc_Ptr = reinterpret_cast(address_of(module, "PyObject_Malloc")); + PyObject_Not_Ptr = reinterpret_cast(address_of(module, "PyObject_Not")); + PyObject_Realloc_Ptr = reinterpret_cast(address_of(module, "PyObject_Realloc")); + PyObject_Repr_Ptr = reinterpret_cast(address_of(module, "PyObject_Repr")); + PyObject_RichCompare_Ptr = reinterpret_cast(address_of(module, "PyObject_RichCompare")); + PyObject_RichCompareBool_Ptr = reinterpret_cast(address_of(module, "PyObject_RichCompareBool")); + PyObject_SelfIter_Ptr = reinterpret_cast(address_of(module, "PyObject_SelfIter")); + PyObject_SetAttr_Ptr = reinterpret_cast(address_of(module, "PyObject_SetAttr")); + PyObject_SetAttrString_Ptr = reinterpret_cast(address_of(module, "PyObject_SetAttrString")); + PyObject_SetItem_Ptr = reinterpret_cast(address_of(module, "PyObject_SetItem")); + PyObject_Size_Ptr = reinterpret_cast(address_of(module, "PyObject_Size")); + PyObject_Str_Ptr = reinterpret_cast(address_of(module, "PyObject_Str")); + PyObject_Type_Ptr = reinterpret_cast(address_of(module, "PyObject_Type")); + #if HAS_PYTHON_VERSION(0x030B0000) + PyObject_TypeCheck_Ptr = reinterpret_cast(address_of(module, "PyObject_TypeCheck")); + #endif + PySeqIter_New_Ptr = reinterpret_cast(address_of(module, "PySeqIter_New")); + PySequence_Check_Ptr = reinterpret_cast(address_of(module, "PySequence_Check")); + PySequence_Concat_Ptr = reinterpret_cast(address_of(module, "PySequence_Concat")); + PySequence_Contains_Ptr = reinterpret_cast(address_of(module, "PySequence_Contains")); + PySequence_Count_Ptr = reinterpret_cast(address_of(module, "PySequence_Count")); + PySequence_DelItem_Ptr = reinterpret_cast(address_of(module, "PySequence_DelItem")); + PySequence_DelSlice_Ptr = reinterpret_cast(address_of(module, "PySequence_DelSlice")); + PySequence_Fast_Ptr = reinterpret_cast(address_of(module, "PySequence_Fast")); + PySequence_GetItem_Ptr = reinterpret_cast(address_of(module, "PySequence_GetItem")); + PySequence_GetSlice_Ptr = reinterpret_cast(address_of(module, "PySequence_GetSlice")); + PySequence_In_Ptr = reinterpret_cast(address_of(module, "PySequence_In")); + PySequence_InPlaceConcat_Ptr = reinterpret_cast(address_of(module, "PySequence_InPlaceConcat")); + PySequence_InPlaceRepeat_Ptr = reinterpret_cast(address_of(module, "PySequence_InPlaceRepeat")); + PySequence_Index_Ptr = reinterpret_cast(address_of(module, "PySequence_Index")); + PySequence_Length_Ptr = reinterpret_cast(address_of(module, "PySequence_Length")); + PySequence_List_Ptr = reinterpret_cast(address_of(module, "PySequence_List")); + PySequence_Repeat_Ptr = reinterpret_cast(address_of(module, "PySequence_Repeat")); + PySequence_SetItem_Ptr = reinterpret_cast(address_of(module, "PySequence_SetItem")); + PySequence_SetSlice_Ptr = reinterpret_cast(address_of(module, "PySequence_SetSlice")); + PySequence_Size_Ptr = reinterpret_cast(address_of(module, "PySequence_Size")); + PySequence_Tuple_Ptr = reinterpret_cast(address_of(module, "PySequence_Tuple")); + PySet_Add_Ptr = reinterpret_cast(address_of(module, "PySet_Add")); + PySet_Clear_Ptr = reinterpret_cast(address_of(module, "PySet_Clear")); + PySet_Contains_Ptr = reinterpret_cast(address_of(module, "PySet_Contains")); + PySet_Discard_Ptr = reinterpret_cast(address_of(module, "PySet_Discard")); + PySet_New_Ptr = reinterpret_cast(address_of(module, "PySet_New")); + PySet_Pop_Ptr = reinterpret_cast(address_of(module, "PySet_Pop")); + PySet_Size_Ptr = reinterpret_cast(address_of(module, "PySet_Size")); + PySlice_AdjustIndices_Ptr = reinterpret_cast(address_of(module, "PySlice_AdjustIndices")); + PySlice_GetIndices_Ptr = reinterpret_cast(address_of(module, "PySlice_GetIndices")); + #ifdef PySlice_GetIndicesEx + PySlice_GetIndicesEx_Ptr = reinterpret_cast(address_of(module, "PySlice_GetIndicesEx")); + #endif + PySlice_New_Ptr = reinterpret_cast(address_of(module, "PySlice_New")); + PySlice_Unpack_Ptr = reinterpret_cast(address_of(module, "PySlice_Unpack")); + PyState_AddModule_Ptr = reinterpret_cast(address_of(module, "PyState_AddModule")); + PyState_FindModule_Ptr = reinterpret_cast(address_of(module, "PyState_FindModule")); + PyState_RemoveModule_Ptr = reinterpret_cast(address_of(module, "PyState_RemoveModule")); + PyStructSequence_GetItem_Ptr = reinterpret_cast(address_of(module, "PyStructSequence_GetItem")); + PyStructSequence_New_Ptr = reinterpret_cast(address_of(module, "PyStructSequence_New")); + PyStructSequence_NewType_Ptr = reinterpret_cast(address_of(module, "PyStructSequence_NewType")); + PyStructSequence_SetItem_Ptr = reinterpret_cast(address_of(module, "PyStructSequence_SetItem")); + PySys_AddWarnOption_Ptr = reinterpret_cast(address_of(module, "PySys_AddWarnOption")); + PySys_AddWarnOptionUnicode_Ptr = reinterpret_cast(address_of(module, "PySys_AddWarnOptionUnicode")); + PySys_AddXOption_Ptr = reinterpret_cast(address_of(module, "PySys_AddXOption")); + PySys_FormatStderr_Ptr = reinterpret_cast(address_of(module, "PySys_FormatStderr")); + PySys_FormatStdout_Ptr = reinterpret_cast(address_of(module, "PySys_FormatStdout")); + PySys_GetObject_Ptr = reinterpret_cast(address_of(module, "PySys_GetObject")); + PySys_GetXOptions_Ptr = reinterpret_cast(address_of(module, "PySys_GetXOptions")); + PySys_HasWarnOptions_Ptr = reinterpret_cast(address_of(module, "PySys_HasWarnOptions")); + PySys_ResetWarnOptions_Ptr = reinterpret_cast(address_of(module, "PySys_ResetWarnOptions")); + PySys_SetArgv_Ptr = reinterpret_cast(address_of(module, "PySys_SetArgv")); + PySys_SetArgvEx_Ptr = reinterpret_cast(address_of(module, "PySys_SetArgvEx")); + PySys_SetObject_Ptr = reinterpret_cast(address_of(module, "PySys_SetObject")); + PySys_SetPath_Ptr = reinterpret_cast(address_of(module, "PySys_SetPath")); + PySys_WriteStderr_Ptr = reinterpret_cast(address_of(module, "PySys_WriteStderr")); + PySys_WriteStdout_Ptr = reinterpret_cast(address_of(module, "PySys_WriteStdout")); + PyThreadState_Clear_Ptr = reinterpret_cast(address_of(module, "PyThreadState_Clear")); + PyThreadState_Delete_Ptr = reinterpret_cast(address_of(module, "PyThreadState_Delete")); + PyThreadState_Get_Ptr = reinterpret_cast(address_of(module, "PyThreadState_Get")); + PyThreadState_GetDict_Ptr = reinterpret_cast(address_of(module, "PyThreadState_GetDict")); + #if HAS_PYTHON_VERSION(0x03090000) + PyThreadState_GetFrame_Ptr = reinterpret_cast(address_of(module, "PyThreadState_GetFrame")); + #endif + PyThreadState_GetID_Ptr = reinterpret_cast(address_of(module, "PyThreadState_GetID")); + PyThreadState_GetInterpreter_Ptr = reinterpret_cast(address_of(module, "PyThreadState_GetInterpreter")); + PyThreadState_New_Ptr = reinterpret_cast(address_of(module, "PyThreadState_New")); + PyThreadState_SetAsyncExc_Ptr = reinterpret_cast(address_of(module, "PyThreadState_SetAsyncExc")); + PyThreadState_Swap_Ptr = reinterpret_cast(address_of(module, "PyThreadState_Swap")); + PyThread_GetInfo_Ptr = reinterpret_cast(address_of(module, "PyThread_GetInfo")); + PyThread_ReInitTLS_Ptr = reinterpret_cast(address_of(module, "PyThread_ReInitTLS")); + PyThread_acquire_lock_Ptr = reinterpret_cast(address_of(module, "PyThread_acquire_lock")); + PyThread_acquire_lock_timed_Ptr = reinterpret_cast(address_of(module, "PyThread_acquire_lock_timed")); + PyThread_allocate_lock_Ptr = reinterpret_cast(address_of(module, "PyThread_allocate_lock")); + PyThread_create_key_Ptr = reinterpret_cast(address_of(module, "PyThread_create_key")); + PyThread_delete_key_Ptr = reinterpret_cast(address_of(module, "PyThread_delete_key")); + PyThread_delete_key_value_Ptr = reinterpret_cast(address_of(module, "PyThread_delete_key_value")); + PyThread_exit_thread_Ptr = reinterpret_cast(address_of(module, "PyThread_exit_thread")); + PyThread_free_lock_Ptr = reinterpret_cast(address_of(module, "PyThread_free_lock")); + PyThread_get_key_value_Ptr = reinterpret_cast(address_of(module, "PyThread_get_key_value")); + PyThread_get_stacksize_Ptr = reinterpret_cast(address_of(module, "PyThread_get_stacksize")); + PyThread_get_thread_ident_Ptr = reinterpret_cast(address_of(module, "PyThread_get_thread_ident")); + PyThread_get_thread_native_id_Ptr = reinterpret_cast(address_of(module, "PyThread_get_thread_native_id")); + PyThread_init_thread_Ptr = reinterpret_cast(address_of(module, "PyThread_init_thread")); + PyThread_release_lock_Ptr = reinterpret_cast(address_of(module, "PyThread_release_lock")); + PyThread_set_key_value_Ptr = reinterpret_cast(address_of(module, "PyThread_set_key_value")); + PyThread_set_stacksize_Ptr = reinterpret_cast(address_of(module, "PyThread_set_stacksize")); + PyThread_start_new_thread_Ptr = reinterpret_cast(address_of(module, "PyThread_start_new_thread")); + #if HAS_PYTHON_VERSION(0x03070000) + PyThread_tss_alloc_Ptr = reinterpret_cast(address_of(module, "PyThread_tss_alloc")); + PyThread_tss_create_Ptr = reinterpret_cast(address_of(module, "PyThread_tss_create")); + PyThread_tss_delete_Ptr = reinterpret_cast(address_of(module, "PyThread_tss_delete")); + PyThread_tss_free_Ptr = reinterpret_cast(address_of(module, "PyThread_tss_free")); + PyThread_tss_get_Ptr = reinterpret_cast(address_of(module, "PyThread_tss_get")); + PyThread_tss_is_created_Ptr = reinterpret_cast(address_of(module, "PyThread_tss_is_created")); + PyThread_tss_set_Ptr = reinterpret_cast(address_of(module, "PyThread_tss_set")); + #endif + #if HAS_PYTHON_VERSION(0x03090000) + PyTraceBack_Here_Ptr = reinterpret_cast(address_of(module, "PyTraceBack_Here")); + #endif + PyTraceBack_Print_Ptr = reinterpret_cast(address_of(module, "PyTraceBack_Print")); + PyTuple_GetItem_Ptr = reinterpret_cast(address_of(module, "PyTuple_GetItem")); + PyTuple_GetSlice_Ptr = reinterpret_cast(address_of(module, "PyTuple_GetSlice")); + PyTuple_New_Ptr = reinterpret_cast(address_of(module, "PyTuple_New")); + PyTuple_Pack_Ptr = reinterpret_cast(address_of(module, "PyTuple_Pack")); + PyTuple_SetItem_Ptr = reinterpret_cast(address_of(module, "PyTuple_SetItem")); + PyTuple_Size_Ptr = reinterpret_cast(address_of(module, "PyTuple_Size")); + PyType_ClearCache_Ptr = reinterpret_cast(address_of(module, "PyType_ClearCache")); + #if !defined(Py_LIMITED_API) + PyType_Name_Ptr = reinterpret_cast(address_of(module, "_PyType_Name")); + #endif + PyType_FromModuleAndSpec_Ptr = reinterpret_cast(address_of(module, "PyType_FromModuleAndSpec")); + PyType_FromSpec_Ptr = reinterpret_cast(address_of(module, "PyType_FromSpec")); + PyType_FromSpecWithBases_Ptr = reinterpret_cast(address_of(module, "PyType_FromSpecWithBases")); + PyType_GenericAlloc_Ptr = reinterpret_cast(address_of(module, "PyType_GenericAlloc")); + PyType_GenericNew_Ptr = reinterpret_cast(address_of(module, "PyType_GenericNew")); + PyType_GetFlags_Ptr = reinterpret_cast(address_of(module, "PyType_GetFlags")); + PyType_GetModule_Ptr = reinterpret_cast(address_of(module, "PyType_GetModule")); + PyType_GetModuleState_Ptr = reinterpret_cast(address_of(module, "PyType_GetModuleState")); + PyType_GetName_Ptr = reinterpret_cast(address_of(module, "PyType_GetName")); + PyType_GetQualName_Ptr = reinterpret_cast(address_of(module, "PyType_GetQualName")); + PyType_GetSlot_Ptr = reinterpret_cast(address_of(module, "PyType_GetSlot")); + PyType_IsSubtype_Ptr = reinterpret_cast(address_of(module, "PyType_IsSubtype")); + PyType_Modified_Ptr = reinterpret_cast(address_of(module, "PyType_Modified")); + PyType_Ready_Ptr = reinterpret_cast(address_of(module, "PyType_Ready")); + PyUnicodeDecodeError_Create_Ptr = reinterpret_cast(address_of(module, "PyUnicodeDecodeError_Create")); + PyUnicodeDecodeError_GetEncoding_Ptr = reinterpret_cast(address_of(module, "PyUnicodeDecodeError_GetEncoding")); + PyUnicodeDecodeError_GetEnd_Ptr = reinterpret_cast(address_of(module, "PyUnicodeDecodeError_GetEnd")); + PyUnicodeDecodeError_GetObject_Ptr = reinterpret_cast(address_of(module, "PyUnicodeDecodeError_GetObject")); + PyUnicodeDecodeError_GetReason_Ptr = reinterpret_cast(address_of(module, "PyUnicodeDecodeError_GetReason")); + PyUnicodeDecodeError_GetStart_Ptr = reinterpret_cast(address_of(module, "PyUnicodeDecodeError_GetStart")); + PyUnicodeDecodeError_SetEnd_Ptr = reinterpret_cast(address_of(module, "PyUnicodeDecodeError_SetEnd")); + PyUnicodeDecodeError_SetReason_Ptr = reinterpret_cast(address_of(module, "PyUnicodeDecodeError_SetReason")); + PyUnicodeDecodeError_SetStart_Ptr = reinterpret_cast(address_of(module, "PyUnicodeDecodeError_SetStart")); + PyUnicodeEncodeError_GetEncoding_Ptr = reinterpret_cast(address_of(module, "PyUnicodeEncodeError_GetEncoding")); + PyUnicodeEncodeError_GetEnd_Ptr = reinterpret_cast(address_of(module, "PyUnicodeEncodeError_GetEnd")); + PyUnicodeEncodeError_GetObject_Ptr = reinterpret_cast(address_of(module, "PyUnicodeEncodeError_GetObject")); + PyUnicodeEncodeError_GetReason_Ptr = reinterpret_cast(address_of(module, "PyUnicodeEncodeError_GetReason")); + PyUnicodeEncodeError_GetStart_Ptr = reinterpret_cast(address_of(module, "PyUnicodeEncodeError_GetStart")); + PyUnicodeEncodeError_SetEnd_Ptr = reinterpret_cast(address_of(module, "PyUnicodeEncodeError_SetEnd")); + PyUnicodeEncodeError_SetReason_Ptr = reinterpret_cast(address_of(module, "PyUnicodeEncodeError_SetReason")); + PyUnicodeEncodeError_SetStart_Ptr = reinterpret_cast(address_of(module, "PyUnicodeEncodeError_SetStart")); + PyUnicodeTranslateError_GetEnd_Ptr = reinterpret_cast(address_of(module, "PyUnicodeTranslateError_GetEnd")); + PyUnicodeTranslateError_GetObject_Ptr = reinterpret_cast(address_of(module, "PyUnicodeTranslateError_GetObject")); + PyUnicodeTranslateError_GetReason_Ptr = reinterpret_cast(address_of(module, "PyUnicodeTranslateError_GetReason")); + PyUnicodeTranslateError_GetStart_Ptr = reinterpret_cast(address_of(module, "PyUnicodeTranslateError_GetStart")); + PyUnicodeTranslateError_SetEnd_Ptr = reinterpret_cast(address_of(module, "PyUnicodeTranslateError_SetEnd")); + PyUnicodeTranslateError_SetReason_Ptr = reinterpret_cast(address_of(module, "PyUnicodeTranslateError_SetReason")); + PyUnicodeTranslateError_SetStart_Ptr = reinterpret_cast(address_of(module, "PyUnicodeTranslateError_SetStart")); + PyUnicode_Append_Ptr = reinterpret_cast(address_of(module, "PyUnicode_Append")); + PyUnicode_AppendAndDel_Ptr = reinterpret_cast(address_of(module, "PyUnicode_AppendAndDel")); + PyUnicode_AsASCIIString_Ptr = reinterpret_cast(address_of(module, "PyUnicode_AsASCIIString")); + PyUnicode_AsCharmapString_Ptr = reinterpret_cast(address_of(module, "PyUnicode_AsCharmapString")); + PyUnicode_AsDecodedObject_Ptr = reinterpret_cast(address_of(module, "PyUnicode_AsDecodedObject")); + PyUnicode_AsDecodedUnicode_Ptr = reinterpret_cast(address_of(module, "PyUnicode_AsDecodedUnicode")); + PyUnicode_AsEncodedObject_Ptr = reinterpret_cast(address_of(module, "PyUnicode_AsEncodedObject")); + PyUnicode_AsEncodedString_Ptr = reinterpret_cast(address_of(module, "PyUnicode_AsEncodedString")); + PyUnicode_AsEncodedUnicode_Ptr = reinterpret_cast(address_of(module, "PyUnicode_AsEncodedUnicode")); + PyUnicode_AsLatin1String_Ptr = reinterpret_cast(address_of(module, "PyUnicode_AsLatin1String")); + PyUnicode_AsRawUnicodeEscapeString_Ptr = reinterpret_cast(address_of(module, "PyUnicode_AsRawUnicodeEscapeString")); + PyUnicode_AsUCS4_Ptr = reinterpret_cast(address_of(module, "PyUnicode_AsUCS4")); + PyUnicode_AsUCS4Copy_Ptr = reinterpret_cast(address_of(module, "PyUnicode_AsUCS4Copy")); + PyUnicode_AsUTF16String_Ptr = reinterpret_cast(address_of(module, "PyUnicode_AsUTF16String")); + PyUnicode_AsUTF32String_Ptr = reinterpret_cast(address_of(module, "PyUnicode_AsUTF32String")); + PyUnicode_AsUTF8AndSize_Ptr = reinterpret_cast(address_of(module, "PyUnicode_AsUTF8AndSize")); + PyUnicode_AsUTF8String_Ptr = reinterpret_cast(address_of(module, "PyUnicode_AsUTF8String")); + PyUnicode_AsUnicodeEscapeString_Ptr = reinterpret_cast(address_of(module, "PyUnicode_AsUnicodeEscapeString")); + PyUnicode_AsWideChar_Ptr = reinterpret_cast(address_of(module, "PyUnicode_AsWideChar")); + PyUnicode_AsWideCharString_Ptr = reinterpret_cast(address_of(module, "PyUnicode_AsWideCharString")); + PyUnicode_BuildEncodingMap_Ptr = reinterpret_cast(address_of(module, "PyUnicode_BuildEncodingMap")); + PyUnicode_Compare_Ptr = reinterpret_cast(address_of(module, "PyUnicode_Compare")); + PyUnicode_CompareWithASCIIString_Ptr = reinterpret_cast(address_of(module, "PyUnicode_CompareWithASCIIString")); + PyUnicode_Concat_Ptr = reinterpret_cast(address_of(module, "PyUnicode_Concat")); + PyUnicode_Contains_Ptr = reinterpret_cast(address_of(module, "PyUnicode_Contains")); + PyUnicode_Count_Ptr = reinterpret_cast(address_of(module, "PyUnicode_Count")); + PyUnicode_Decode_Ptr = reinterpret_cast(address_of(module, "PyUnicode_Decode")); + PyUnicode_DecodeASCII_Ptr = reinterpret_cast(address_of(module, "PyUnicode_DecodeASCII")); + PyUnicode_DecodeCharmap_Ptr = reinterpret_cast(address_of(module, "PyUnicode_DecodeCharmap")); + PyUnicode_DecodeFSDefault_Ptr = reinterpret_cast(address_of(module, "PyUnicode_DecodeFSDefault")); + PyUnicode_DecodeFSDefaultAndSize_Ptr = reinterpret_cast(address_of(module, "PyUnicode_DecodeFSDefaultAndSize")); + PyUnicode_DecodeLatin1_Ptr = reinterpret_cast(address_of(module, "PyUnicode_DecodeLatin1")); + PyUnicode_DecodeLocale_Ptr = reinterpret_cast(address_of(module, "PyUnicode_DecodeLocale")); + PyUnicode_DecodeLocaleAndSize_Ptr = reinterpret_cast(address_of(module, "PyUnicode_DecodeLocaleAndSize")); + PyUnicode_DecodeRawUnicodeEscape_Ptr = reinterpret_cast(address_of(module, "PyUnicode_DecodeRawUnicodeEscape")); + PyUnicode_DecodeUTF16_Ptr = reinterpret_cast(address_of(module, "PyUnicode_DecodeUTF16")); + PyUnicode_DecodeUTF16Stateful_Ptr = reinterpret_cast(address_of(module, "PyUnicode_DecodeUTF16Stateful")); + PyUnicode_DecodeUTF32_Ptr = reinterpret_cast(address_of(module, "PyUnicode_DecodeUTF32")); + PyUnicode_DecodeUTF32Stateful_Ptr = reinterpret_cast(address_of(module, "PyUnicode_DecodeUTF32Stateful")); + PyUnicode_DecodeUTF7_Ptr = reinterpret_cast(address_of(module, "PyUnicode_DecodeUTF7")); + PyUnicode_DecodeUTF7Stateful_Ptr = reinterpret_cast(address_of(module, "PyUnicode_DecodeUTF7Stateful")); + PyUnicode_DecodeUTF8_Ptr = reinterpret_cast(address_of(module, "PyUnicode_DecodeUTF8")); + PyUnicode_DecodeUTF8Stateful_Ptr = reinterpret_cast(address_of(module, "PyUnicode_DecodeUTF8Stateful")); + PyUnicode_DecodeUnicodeEscape_Ptr = reinterpret_cast(address_of(module, "PyUnicode_DecodeUnicodeEscape")); + PyUnicode_EncodeFSDefault_Ptr = reinterpret_cast(address_of(module, "PyUnicode_EncodeFSDefault")); + PyUnicode_EncodeLocale_Ptr = reinterpret_cast(address_of(module, "PyUnicode_EncodeLocale")); + PyUnicode_FSConverter_Ptr = reinterpret_cast(address_of(module, "PyUnicode_FSConverter")); + PyUnicode_FSDecoder_Ptr = reinterpret_cast(address_of(module, "PyUnicode_FSDecoder")); + PyUnicode_Find_Ptr = reinterpret_cast(address_of(module, "PyUnicode_Find")); + PyUnicode_FindChar_Ptr = reinterpret_cast(address_of(module, "PyUnicode_FindChar")); + PyUnicode_Format_Ptr = reinterpret_cast(address_of(module, "PyUnicode_Format")); + PyUnicode_FromEncodedObject_Ptr = reinterpret_cast(address_of(module, "PyUnicode_FromEncodedObject")); + PyUnicode_FromFormat_Ptr = reinterpret_cast(address_of(module, "PyUnicode_FromFormat")); + PyUnicode_FromFormatV_Ptr = reinterpret_cast(address_of(module, "PyUnicode_FromFormatV")); + PyUnicode_FromObject_Ptr = reinterpret_cast(address_of(module, "PyUnicode_FromObject")); + PyUnicode_FromOrdinal_Ptr = reinterpret_cast(address_of(module, "PyUnicode_FromOrdinal")); + PyUnicode_FromString_Ptr = reinterpret_cast(address_of(module, "PyUnicode_FromString")); + PyUnicode_FromStringAndSize_Ptr = reinterpret_cast(address_of(module, "PyUnicode_FromStringAndSize")); + PyUnicode_FromWideChar_Ptr = reinterpret_cast(address_of(module, "PyUnicode_FromWideChar")); + PyUnicode_GetDefaultEncoding_Ptr = reinterpret_cast(address_of(module, "PyUnicode_GetDefaultEncoding")); + PyUnicode_GetLength_Ptr = reinterpret_cast(address_of(module, "PyUnicode_GetLength")); + PyUnicode_GetSize_Ptr = reinterpret_cast(address_of(module, "PyUnicode_GetSize")); + PyUnicode_InternFromString_Ptr = reinterpret_cast(address_of(module, "PyUnicode_InternFromString")); + PyUnicode_InternImmortal_Ptr = reinterpret_cast(address_of(module, "PyUnicode_InternImmortal")); + PyUnicode_InternInPlace_Ptr = reinterpret_cast(address_of(module, "PyUnicode_InternInPlace")); + PyUnicode_IsIdentifier_Ptr = reinterpret_cast(address_of(module, "PyUnicode_IsIdentifier")); + PyUnicode_Join_Ptr = reinterpret_cast(address_of(module, "PyUnicode_Join")); + PyUnicode_Partition_Ptr = reinterpret_cast(address_of(module, "PyUnicode_Partition")); + PyUnicode_RPartition_Ptr = reinterpret_cast(address_of(module, "PyUnicode_RPartition")); + PyUnicode_RSplit_Ptr = reinterpret_cast(address_of(module, "PyUnicode_RSplit")); + PyUnicode_ReadChar_Ptr = reinterpret_cast(address_of(module, "PyUnicode_ReadChar")); + PyUnicode_Replace_Ptr = reinterpret_cast(address_of(module, "PyUnicode_Replace")); + PyUnicode_Resize_Ptr = reinterpret_cast(address_of(module, "PyUnicode_Resize")); + PyUnicode_RichCompare_Ptr = reinterpret_cast(address_of(module, "PyUnicode_RichCompare")); + PyUnicode_Split_Ptr = reinterpret_cast(address_of(module, "PyUnicode_Split")); + PyUnicode_Splitlines_Ptr = reinterpret_cast(address_of(module, "PyUnicode_Splitlines")); + PyUnicode_Substring_Ptr = reinterpret_cast(address_of(module, "PyUnicode_Substring")); + PyUnicode_Tailmatch_Ptr = reinterpret_cast(address_of(module, "PyUnicode_Tailmatch")); + PyUnicode_Translate_Ptr = reinterpret_cast(address_of(module, "PyUnicode_Translate")); + PyUnicode_WriteChar_Ptr = reinterpret_cast(address_of(module, "PyUnicode_WriteChar")); + PyWeakref_GetObject_Ptr = reinterpret_cast(address_of(module, "PyWeakref_GetObject")); + PyWeakref_NewProxy_Ptr = reinterpret_cast(address_of(module, "PyWeakref_NewProxy")); + PyWeakref_NewRef_Ptr = reinterpret_cast(address_of(module, "PyWeakref_NewRef")); + PyWrapper_New_Ptr = reinterpret_cast(address_of(module, "PyWrapper_New")); + Py_AddPendingCall_Ptr = reinterpret_cast(address_of(module, "Py_AddPendingCall")); + Py_AtExit_Ptr = reinterpret_cast(address_of(module, "Py_AtExit")); + Py_BuildValue_Ptr = reinterpret_cast(address_of(module, "Py_BuildValue")); + Py_BytesMain_Ptr = reinterpret_cast(address_of(module, "Py_BytesMain")); + Py_CompileString_Ptr = reinterpret_cast(address_of(module, "Py_CompileString")); + Py_DecRef_Ptr = reinterpret_cast(address_of(module, "Py_DecRef")); + Py_DecodeLocale_Ptr = reinterpret_cast(address_of(module, "Py_DecodeLocale")); + Py_EncodeLocale_Ptr = reinterpret_cast(address_of(module, "Py_EncodeLocale")); + Py_EndInterpreter_Ptr = reinterpret_cast(address_of(module, "Py_EndInterpreter")); + Py_EnterRecursiveCall_Ptr = reinterpret_cast(address_of(module, "Py_EnterRecursiveCall")); + Py_Exit_Ptr = reinterpret_cast(address_of(module, "Py_Exit")); + Py_FatalError_Ptr = reinterpret_cast(address_of(module, "Py_FatalError")); + Py_Finalize_Ptr = reinterpret_cast(address_of(module, "Py_Finalize")); + Py_FinalizeEx_Ptr = reinterpret_cast(address_of(module, "Py_FinalizeEx")); + Py_GenericAlias_Ptr = reinterpret_cast(address_of(module, "Py_GenericAlias")); + Py_GetBuildInfo_Ptr = reinterpret_cast(address_of(module, "Py_GetBuildInfo")); + Py_GetCompiler_Ptr = reinterpret_cast(address_of(module, "Py_GetCompiler")); + Py_GetCopyright_Ptr = reinterpret_cast(address_of(module, "Py_GetCopyright")); + Py_GetExecPrefix_Ptr = reinterpret_cast(address_of(module, "Py_GetExecPrefix")); + Py_GetPath_Ptr = reinterpret_cast(address_of(module, "Py_GetPath")); + Py_GetPlatform_Ptr = reinterpret_cast(address_of(module, "Py_GetPlatform")); + Py_GetPrefix_Ptr = reinterpret_cast(address_of(module, "Py_GetPrefix")); + Py_GetProgramFullPath_Ptr = reinterpret_cast(address_of(module, "Py_GetProgramFullPath")); + Py_GetProgramName_Ptr = reinterpret_cast(address_of(module, "Py_GetProgramName")); + Py_GetPythonHome_Ptr = reinterpret_cast(address_of(module, "Py_GetPythonHome")); + Py_GetRecursionLimit_Ptr = reinterpret_cast(address_of(module, "Py_GetRecursionLimit")); + Py_GetVersion_Ptr = reinterpret_cast(address_of(module, "Py_GetVersion")); + Py_IncRef_Ptr = reinterpret_cast(address_of(module, "Py_IncRef")); + Py_Initialize_Ptr = reinterpret_cast(address_of(module, "Py_Initialize")); + Py_InitializeEx_Ptr = reinterpret_cast(address_of(module, "Py_InitializeEx")); + Py_Is_Ptr = reinterpret_cast(address_of(module, "Py_Is")); + Py_IsInitialized_Ptr = reinterpret_cast(address_of(module, "Py_IsInitialized")); + Py_LeaveRecursiveCall_Ptr = reinterpret_cast(address_of(module, "Py_LeaveRecursiveCall")); + Py_Main_Ptr = reinterpret_cast(address_of(module, "Py_Main")); + Py_MakePendingCalls_Ptr = reinterpret_cast(address_of(module, "Py_MakePendingCalls")); + Py_NewInterpreter_Ptr = reinterpret_cast(address_of(module, "Py_NewInterpreter")); + Py_NewRef_Ptr = reinterpret_cast(address_of(module, "Py_NewRef")); + Py_ReprEnter_Ptr = reinterpret_cast(address_of(module, "Py_ReprEnter")); + Py_ReprLeave_Ptr = reinterpret_cast(address_of(module, "Py_ReprLeave")); + Py_SetPath_Ptr = reinterpret_cast(address_of(module, "Py_SetPath")); + Py_SetProgramName_Ptr = reinterpret_cast(address_of(module, "Py_SetProgramName")); + Py_SetPythonHome_Ptr = reinterpret_cast(address_of(module, "Py_SetPythonHome")); + Py_SetRecursionLimit_Ptr = reinterpret_cast(address_of(module, "Py_SetRecursionLimit")); + Py_VaBuildValue_Ptr = reinterpret_cast(address_of(module, "Py_VaBuildValue")); + #if HAS_PYTHON_VERSION(0x030A0000) + Py_XNewRef_Ptr = reinterpret_cast(address_of(module, "Py_XNewRef")); + #endif +} + +Python::Python(Python &&other) +{ + steal(*this, other); +} + +Python& Python::operator = (Python&& other) +{ + steal(*this, other); + return *this; +} + +bool Python::IsPythonLoaded() const +{ + return module != nullptr; +} + +int Python::PyAIter_Check(PyObject* o) +{ + return (*PyAIter_Check_Ptr)(o); +} + +int Python::PyArg_ParseTuple(PyObject* args, const char* format, ...) +{ + va_list arguments; + va_start(arguments, format); + int result = PyArg_VaParse(args, format, arguments); + va_end(arguments); + return result; +} + +int Python::PyArg_ParseTupleAndKeywords(PyObject* args, PyObject* kw, const char* format, char* keywords[], ...) +{ + va_list arguments; + va_start(arguments, keywords); + int result = PyArg_VaParseTupleAndKeywords(args, kw, format, keywords, arguments); + va_end(arguments); + return result; +} + +int Python::PyArg_VaParse(PyObject* args, const char* format, va_list vargs) +{ + return (*PyArg_VaParse_Ptr)(args, format, vargs); +} + +int Python::PyArg_VaParseTupleAndKeywords(PyObject* args, PyObject* kw, const char* format, char* keywords[], va_list vargs) +{ + return (*PyArg_VaParseTupleAndKeywords_Ptr)(args, kw, format, keywords, vargs); +} + +int Python::PyArg_ValidateKeywordArguments(PyObject* keywords) +{ + return (*PyArg_ValidateKeywordArguments_Ptr)(keywords); +} + +PyObject* Python::PyBool_FromLong(long v) +{ + return (*PyBool_FromLong_Ptr)(v); +} + +#if HAS_PYTHON_VERSION(0x030B1000) +void Python::PyBuffer_FillContiguousStrides(int ndims, Py_ssize_t* shape, Py_ssize_t* strides, int itemsize, char fort) +{ + return (*PyBuffer_FillContiguousStrides_Ptr)(ndims, shape, strides, itemsize, fort); +} + +int Python::PyBuffer_FillInfo(Py_buffer* view, PyObject* o, void* buf, Py_ssize_t len, int readonly, int flags) +{ + return (*PyBuffer_FillInfo_Ptr)(view, o, buf, len, readonly, flags); +} + +int Python::PyBuffer_FromContiguous(const Py_buffer* view, const void* buf, Py_ssize_t len, char order) +{ + return (*PyBuffer_FromContiguous_Ptr)(view, buf, len, order); +} + +void* Python::PyBuffer_GetPointer(const Py_buffer* view, const Py_ssize_t* indices) +{ + return (*PyBuffer_GetPointer_Ptr)(view, indices); +} + +int Python::PyBuffer_IsContiguous(const Py_buffer* view, char fort) +{ + return (*PyBuffer_IsContiguous_Ptr)(view, fort); +} + +void Python::PyBuffer_Release(Py_buffer* view) +{ + return (*PyBuffer_Release_Ptr)(view); +} + +Py_ssize_t Python::PyBuffer_SizeFromFormat(const char* format) +{ + return (*PyBuffer_SizeFromFormat_Ptr)(format); +} + +int Python::PyBuffer_ToContiguous(void* buf, const Py_buffer* view, Py_ssize_t len, char order) +{ + return (*PyBuffer_ToContiguous_Ptr)(buf, view, len, order); +} +#endif + +char* Python::PyByteArray_AsString(PyObject* bytearray) +{ + return (*PyByteArray_AsString_Ptr)(bytearray); +} + +PyObject* Python::PyByteArray_Concat(PyObject* a, PyObject* b) +{ + return (*PyByteArray_Concat_Ptr)(a, b); +} + +PyObject* Python::PyByteArray_FromObject(PyObject* bytearray) +{ + return (*PyByteArray_FromObject_Ptr)(bytearray); +} + +PyObject* Python::PyByteArray_FromStringAndSize(const char* string, Py_ssize_t len) +{ + return (*PyByteArray_FromStringAndSize_Ptr)(string, len); +} + +int Python::PyByteArray_Resize(PyObject* bytearray, Py_ssize_t len) +{ + return (*PyByteArray_Resize_Ptr)(bytearray, len); +} + +Py_ssize_t Python::PyByteArray_Size(PyObject* bytearray) +{ + return (*PyByteArray_Size_Ptr)(bytearray); +} + +char* Python::PyBytes_AsString(PyObject* bytearray) +{ + return (*PyBytes_AsString_Ptr)(bytearray); +} + +int Python::PyBytes_AsStringAndSize(PyObject* obj, char** s, Py_ssize_t* len) +{ + return (*PyBytes_AsStringAndSize_Ptr)(obj, s, len); +} + +void Python::PyBytes_Concat(PyObject** bytes, PyObject* newpart) +{ + return (*PyBytes_Concat_Ptr)(bytes, newpart); +} + +void Python::PyBytes_ConcatAndDel(PyObject** bytes, PyObject* newpart) +{ + return (*PyBytes_ConcatAndDel_Ptr)(bytes, newpart); +} + +PyObject* Python::PyBytes_DecodeEscape(const char* arg1, Py_ssize_t arg2, const char* arg3, Py_ssize_t arg4, const char* arg5) +{ + return (*PyBytes_DecodeEscape_Ptr)(arg1, arg2, arg3, arg4, arg5); +} + +PyObject* Python::PyBytes_FromFormat(const char *format, ...) +{ + va_list arguments; + va_start(arguments, format); + PyObject* result = PyBytes_FromFormatV(format, arguments); + va_end(arguments); + return result; +} + +PyObject* Python::PyBytes_FromFormatV(const char* format, va_list vargs) +{ + return (*PyBytes_FromFormatV_Ptr)(format, vargs); +} + +PyObject* Python::PyBytes_FromObject(PyObject* o) +{ + return (*PyBytes_FromObject_Ptr)(o); +} + +PyObject* Python::PyBytes_FromString(const char* string) +{ + return (*PyBytes_FromString_Ptr)(string); +} + +PyObject* Python::PyBytes_FromStringAndSize(const char* string, Py_ssize_t len) +{ + return (*PyBytes_FromStringAndSize_Ptr)(string, len); +} + +PyObject* Python::PyBytes_Repr(PyObject* o, int smartquotes) +{ + return (*PyBytes_Repr_Ptr)(o, smartquotes); +} + +Py_ssize_t Python::PyBytes_Size(PyObject* o) +{ + return (*PyBytes_Size_Ptr)(o); +} + +PyObject* Python::PyCFunction_Call(PyObject* func, PyObject* arg, PyObject* kw) +{ + return (*PyCFunction_Call_Ptr)(func, arg, kw); +} + +int Python::PyCFunction_GetFlags(PyObject* op) +{ + return (*PyCFunction_GetFlags_Ptr)(op); +} + +PyCFunction Python::PyCFunction_GetFunction(PyObject* op) +{ + return (*PyCFunction_GetFunction_Ptr)(op); +} + +PyObject* Python::PyCFunction_GetSelf(PyObject* op) +{ + return (*PyCFunction_GetSelf_Ptr)(op); +} + +PyObject* (Python::PyCFunction_New)(PyMethodDef* ml, PyObject* self) +{ + #if HAS_PYTHON_VERSION(0x03050000) + return PyCFunction_NewEx(ml, self, nullptr); + #else + return (*PyCMethod_New_Ptr)(ml, self, nullptr, nullptr); + #endif +} + +#if HAS_PYTHON_VERSION(0x03050000) +PyObject* (Python::PyCFunction_NewEx)(PyMethodDef* ml, PyObject* self, PyObject* module) +{ + #if HAS_PYTHON_VERSION(0x03090000) + return (*PyCFunction_NewEx_Ptr)(ml, self, module); + #else + return (*PyCMethod_New_Ptr)(ml, self, module, nullptr); + #endif +} +#endif + +PyObject* Python::PyCMethod_New(PyMethodDef* ml, PyObject* self, PyObject* module, PyTypeObject* type) +{ + return (*PyCMethod_New_Ptr)(ml, self, module, type); +} + +PyObject* Python::PyCallIter_New(PyObject* callable, PyObject* sentinel) +{ + return (*PyCallIter_New_Ptr)(callable, sentinel); +} + +int Python::PyCallable_Check(PyObject* o) +{ + return (*PyCallable_Check_Ptr)(o); +} + +void* Python::PyCapsule_GetContext(PyObject* capsule) +{ + return (*PyCapsule_GetContext_Ptr)(capsule); +} + +PyCapsule_Destructor Python::PyCapsule_GetDestructor(PyObject* capsule) +{ + return (*PyCapsule_GetDestructor_Ptr)(capsule); +} + +const char* Python::PyCapsule_GetName(PyObject* capsule) +{ + return (*PyCapsule_GetName_Ptr)(capsule); +} + +void* Python::PyCapsule_GetPointer(PyObject* capsule, const char* name) +{ + return (*PyCapsule_GetPointer_Ptr)(capsule, name); +} + +void* Python::PyCapsule_Import(const char* name, int no_block) +{ + return (*PyCapsule_Import_Ptr)(name, no_block); +} + +int Python::PyCapsule_IsValid(PyObject* capsule, const char* name) +{ + return (*PyCapsule_IsValid_Ptr)(capsule, name); +} + +PyObject* Python::PyCapsule_New(void* pointer, const char* name, PyCapsule_Destructor destructor) +{ + return (*PyCapsule_New_Ptr)(pointer, name, destructor); +} + +int Python::PyCapsule_SetContext(PyObject* capsule, void* context) +{ + return (*PyCapsule_SetContext_Ptr)(capsule, context); +} + +int Python::PyCapsule_SetDestructor(PyObject* capsule, PyCapsule_Destructor destructor) +{ + return (*PyCapsule_SetDestructor_Ptr)(capsule, destructor); +} + +int Python::PyCapsule_SetName(PyObject* capsule, const char* name) +{ + return (*PyCapsule_SetName_Ptr)(capsule, name); +} + +int Python::PyCapsule_SetPointer(PyObject* capsule, void* pointer) +{ + return (*PyCapsule_SetPointer_Ptr)(capsule, pointer); +} + +PyObject* Python::PyComplex_FromDoubles(double real, double imag) +{ + return (*PyComplex_FromDoubles_Ptr)(real, imag); +} + +double Python::PyComplex_ImagAsDouble(PyObject* op) +{ + return (*PyComplex_ImagAsDouble_Ptr)(op); +} + +double Python::PyComplex_RealAsDouble(PyObject* op) +{ + return (*PyComplex_RealAsDouble_Ptr)(op); +} + +PyObject* Python::PyDescr_NewClassMethod(PyTypeObject* type, PyMethodDef* method) +{ + return (*PyDescr_NewClassMethod_Ptr)(type, method); +} + +PyObject* Python::PyDescr_NewGetSet(PyTypeObject* type, PyGetSetDef* getset) +{ + return (*PyDescr_NewGetSet_Ptr)(type, getset); +} + +PyObject* Python::PyDescr_NewMember(PyTypeObject* type, PyMemberDef* meth) +{ + return (*PyDescr_NewMember_Ptr)(type, meth); +} + +PyObject* Python::PyDescr_NewMethod(PyTypeObject* type, PyMethodDef* meth) +{ + return (*PyDescr_NewMethod_Ptr)(type, meth); +} + +PyObject* Python::PyDictProxy_New(PyObject* mapping) +{ + return (*PyDictProxy_New_Ptr)(mapping); +} + +void Python::PyDict_Clear(PyObject* mp) +{ + return (*PyDict_Clear_Ptr)(mp); +} + +int Python::PyDict_Contains(PyObject* mp, PyObject* key) +{ + return (*PyDict_Contains_Ptr)(mp, key); +} + +PyObject* Python::PyDict_Copy(PyObject* mp) +{ + return (*PyDict_Copy_Ptr)(mp); +} + +int Python::PyDict_DelItem(PyObject* mp, PyObject* key) +{ + return (*PyDict_DelItem_Ptr)(mp, key); +} + +int Python::PyDict_DelItemString(PyObject* dp, const char* key) +{ + return (*PyDict_DelItemString_Ptr)(dp, key); +} + +PyObject* Python::PyDict_GetItem(PyObject* mp, PyObject* key) +{ + return (*PyDict_GetItem_Ptr)(mp, key); +} + +PyObject* Python::PyDict_GetItemString(PyObject* dp, const char* key) +{ + return (*PyDict_GetItemString_Ptr)(dp, key); +} + +PyObject* Python::PyDict_GetItemWithError(PyObject* mp, PyObject* key) +{ + return (*PyDict_GetItemWithError_Ptr)(mp, key); +} + +PyObject* Python::PyDict_Items(PyObject* mp) +{ + return (*PyDict_Items_Ptr)(mp); +} + +PyObject* Python::PyDict_Keys(PyObject* mp) +{ + return (*PyDict_Keys_Ptr)(mp); +} + +int Python::PyDict_Merge(PyObject* mp, PyObject* other, int override) +{ + return (*PyDict_Merge_Ptr)(mp, other, override); +} + +int Python::PyDict_MergeFromSeq2(PyObject* d, PyObject* seq2, int override) +{ + return (*PyDict_MergeFromSeq2_Ptr)(d, seq2, override); +} + +PyObject* Python::PyDict_New() +{ + return (*PyDict_New_Ptr)(); +} + +int Python::PyDict_Next(PyObject* mp, Py_ssize_t* pos, PyObject** key, PyObject** value) +{ + return (*PyDict_Next_Ptr)(mp, pos, key, value); +} + +int Python::PyDict_SetItem(PyObject* mp, PyObject* key, PyObject* item) +{ + return (*PyDict_SetItem_Ptr)(mp, key, item); +} + +int Python::PyDict_SetItemString(PyObject* dp, const char* key, PyObject* item) +{ + return (*PyDict_SetItemString_Ptr)(dp, key, item); +} + +Py_ssize_t Python::PyDict_Size(PyObject* mp) +{ + return (*PyDict_Size_Ptr)(mp); +} + +int Python::PyDict_Update(PyObject* mp, PyObject* other) +{ + return (*PyDict_Update_Ptr)(mp, other); +} + +PyObject* Python::PyDict_Values(PyObject* mp) +{ + return (*PyDict_Values_Ptr)(mp); +} + +int Python::PyErr_BadArgument() +{ + return (*PyErr_BadArgument_Ptr)(); +} + +#ifdef PyErr_BadInternalCall +void (Python::PyErr_BadInternalCall)() +{ + return (*_PyErr_BadInternalCall_Ptr)(__FILE__, __LINE__); +} +#endif + +int Python::PyErr_CheckSignals() +{ + return (*PyErr_CheckSignals_Ptr)(); +} + +void Python::PyErr_Clear() +{ + return (*PyErr_Clear_Ptr)(); +} + +void Python::PyErr_Display(PyObject* exc, PyObject* val, PyObject* tb) +{ + return (*PyErr_Display_Ptr)(exc, val, tb); +} + +int Python::PyErr_ExceptionMatches(PyObject* exc) +{ + return (*PyErr_ExceptionMatches_Ptr)(exc); +} + +void Python::PyErr_Fetch(PyObject** ptype, PyObject** pvalue, PyObject** ptraceback) +{ + return (*PyErr_Fetch_Ptr)(ptype, pvalue, ptraceback); +} + +PyObject* Python::PyErr_Format(PyObject* exception, const char* format, ...) +{ + va_list arguments; + va_start(arguments, format); + PyObject* result = PyErr_FormatV(exception, format, arguments); + va_end(arguments); + return result; +} + +PyObject* Python::PyErr_FormatV(PyObject* exception, const char* format, va_list vargs) +{ + return (*PyErr_FormatV_Ptr)(exception, format, vargs); +} + +void Python::PyErr_GetExcInfo(PyObject** ptype, PyObject** pvalue, PyObject** ptraceback) +{ + return (*PyErr_GetExcInfo_Ptr)(ptype, pvalue, ptraceback); +} + +PyObject* Python::PyErr_GetHandledException() +{ + return (*PyErr_GetHandledException_Ptr)(); +} + +int Python::PyErr_GivenExceptionMatches(PyObject* given, PyObject* exc) +{ + return (*PyErr_GivenExceptionMatches_Ptr)(given, exc); +} + +PyObject* Python::PyErr_NewException(const char* name, PyObject* base, PyObject* dict) +{ + return (*PyErr_NewException_Ptr)(name, base, dict); +} + +PyObject* Python::PyErr_NewExceptionWithDoc(const char* name, const char* doc, PyObject* base, PyObject* dict) +{ + return (*PyErr_NewExceptionWithDoc_Ptr)(name, doc, base, dict); +} + +PyObject* Python::PyErr_NoMemory() +{ + return (*PyErr_NoMemory_Ptr)(); +} + +void Python::PyErr_NormalizeException(PyObject** exc, PyObject** val, PyObject** tb) +{ + return (*PyErr_NormalizeException_Ptr)(exc, val, tb); +} + +PyObject* Python::PyErr_Occurred() +{ + return (*PyErr_Occurred_Ptr)(); +} + +void Python::PyErr_Print() +{ + return (*PyErr_Print_Ptr)(); +} + +void Python::PyErr_PrintEx(int set_sys_last_vars) +{ + return (*PyErr_PrintEx_Ptr)(set_sys_last_vars); +} + +PyObject* Python::PyErr_ProgramText(const char* filename, int lineno) +{ + return (*PyErr_ProgramText_Ptr)(filename, lineno); +} + +void Python::PyErr_Restore(PyObject* type, PyObject* value, PyObject* traceback) +{ + return (*PyErr_Restore_Ptr)(type, value, traceback); +} + +void Python::PyErr_SetExcInfo(PyObject* type, PyObject* value, PyObject* traceback) +{ + return (*PyErr_SetExcInfo_Ptr)(type, value, traceback); +} + +PyObject* Python::PyErr_SetFromErrno(PyObject* type) +{ + return (*PyErr_SetFromErrno_Ptr)(type); +} + +PyObject *Python::PyErr_SetFromErrnoWithFilename(PyObject *type, const char *filename) +{ + return (*PyErr_SetFromErrnoWithFilename_Ptr)(type, filename); +} + +PyObject* Python::PyErr_SetFromErrnoWithFilenameObject(PyObject* type, PyObject* filenameObject) +{ + return (*PyErr_SetFromErrnoWithFilenameObject_Ptr)(type, filenameObject); +} + +PyObject* Python::PyErr_SetFromErrnoWithFilenameObjects(PyObject* type, PyObject* filenameObject, PyObject* filenameObject2) +{ + return (*PyErr_SetFromErrnoWithFilenameObjects_Ptr)(type, filenameObject, filenameObject2); +} + +PyObject* Python::PyErr_SetImportError(PyObject *msg, PyObject *name, PyObject *path) +{ + return (*PyErr_SetImportError_Ptr)(msg, name, path); +} + +void Python::PyErr_SetHandledException(PyObject* exc) +{ + return (*PyErr_SetHandledException_Ptr)(exc); +} + +PyObject* Python::PyErr_SetImportErrorSubclass(PyObject* exception, PyObject* msg, PyObject* name, PyObject* path) +{ + return (*PyErr_SetImportErrorSubclass_Ptr)(exception, msg, name, path); +} + +void Python::PyErr_SetInterrupt() +{ + return (*PyErr_SetInterrupt_Ptr)(); +} + +int Python::PyErr_SetInterruptEx(int signum) +{ + return (*PyErr_SetInterruptEx_Ptr)(signum); +} + +void Python::PyErr_SetNone(PyObject* type) +{ + return (*PyErr_SetNone_Ptr)(type); +} + +void Python::PyErr_SetObject(PyObject* type, PyObject* value) +{ + return (*PyErr_SetObject_Ptr)(type, value); +} + +void Python::PyErr_SetString(PyObject* exception, const char* string) +{ + return (*PyErr_SetString_Ptr)(exception, string); +} + +void Python::PyErr_SyntaxLocation(const char* filename, int lineno) +{ + return (*PyErr_SyntaxLocation_Ptr)(filename, lineno); +} + +void Python::PyErr_SyntaxLocationEx(const char* filename, int lineno, int col_offset) +{ + return (*PyErr_SyntaxLocationEx_Ptr)(filename, lineno, col_offset); +} + +int Python::PyErr_WarnEx(PyObject* category, const char* message, Py_ssize_t stack_level) +{ + return (*PyErr_WarnEx_Ptr)(category, message, stack_level); +} + +int Python::PyErr_WarnExplicit(PyObject* category, const char* message, const char* filename, int lineno, const char* module, PyObject* registry) +{ + return (*PyErr_WarnExplicit_Ptr)(category, message, filename, lineno, module, registry); +} + +void Python::PyErr_WriteUnraisable(PyObject* obj) +{ + return (*PyErr_WriteUnraisable_Ptr)(obj); +} + +void Python::PyEval_AcquireLock() +{ + return (*PyEval_AcquireLock_Ptr)(); +} + +void Python::PyEval_AcquireThread(PyThreadState* tstate) +{ + return (*PyEval_AcquireThread_Ptr)(tstate); +} + +PyObject* Python::PyEval_CallObjectWithKeywords(PyObject* callable, PyObject* args, PyObject* kwargs) +{ + return (*PyEval_CallObjectWithKeywords_Ptr)(callable, args, kwargs); +} + +PyObject* Python::PyEval_EvalCode(PyObject* co, PyObject* globals, PyObject* locals) +{ + return (*PyEval_EvalCode_Ptr)(co, globals, locals); +} + +PyObject* Python::PyEval_EvalCodeEx(PyObject* co, PyObject* globals, PyObject* locals, PyObject*const* args, int argc, PyObject*const* kwds, int kwdc, PyObject*const* defs, int defc, PyObject* kwdefs, PyObject* closure) +{ + return (*PyEval_EvalCodeEx_Ptr)(co, globals, locals, args, argc, kwds, kwdc, defs, defc, kwdefs, closure); +} + +#if HAS_PYTHON_VERSION(0x03090000) +PyObject* Python::PyEval_EvalFrame(PyFrameObject* f) +{ + return (*PyEval_EvalFrame_Ptr)(f); +} + +PyObject* Python::PyEval_EvalFrameEx(PyFrameObject* f, int exc) +{ + return (*PyEval_EvalFrameEx_Ptr)(f, exc); +} +#endif + +PyObject* Python::PyEval_GetBuiltins() +{ + return (*PyEval_GetBuiltins_Ptr)(); +} + +#if HAS_PYTHON_VERSION(0x03090000) +PyFrameObject* Python::PyEval_GetFrame() +{ + return (*PyEval_GetFrame_Ptr)(); +} +#endif + +const char* Python::PyEval_GetFuncDesc(PyObject* func) +{ + return (*PyEval_GetFuncDesc_Ptr)(func); +} + +const char* Python::PyEval_GetFuncName(PyObject* func) +{ + return (*PyEval_GetFuncName_Ptr)(func); +} + +PyObject* Python::PyEval_GetGlobals() +{ + return (*PyEval_GetGlobals_Ptr)(); +} + +PyObject* Python::PyEval_GetLocals() +{ + return (*PyEval_GetLocals_Ptr)(); +} + +void Python::PyEval_InitThreads() +{ + return (*PyEval_InitThreads_Ptr)(); +} + +void Python::PyEval_ReleaseLock() +{ + return (*PyEval_ReleaseLock_Ptr)(); +} + +void Python::PyEval_ReleaseThread(PyThreadState* tstate) +{ + return (*PyEval_ReleaseThread_Ptr)(tstate); +} + +void Python::PyEval_RestoreThread(PyThreadState* tstate) +{ + return (*PyEval_RestoreThread_Ptr)(tstate); +} + +PyThreadState* Python::PyEval_SaveThread() +{ + return (*PyEval_SaveThread_Ptr)(); +} + +int Python::PyEval_ThreadsInitialized() +{ + return (*PyEval_ThreadsInitialized_Ptr)(); +} + +const char* (Python::PyExceptionClass_Name)(PyObject* exc) +{ + return (*PyExceptionClass_Name_Ptr)(exc); +} + +PyObject* Python::PyException_GetCause(PyObject* exc) +{ + return (*PyException_GetCause_Ptr)(exc); +} + +PyObject* Python::PyException_GetContext(PyObject* exc) +{ + return (*PyException_GetContext_Ptr)(exc); +} + +PyObject* Python::PyException_GetTraceback(PyObject* exc) +{ + return (*PyException_GetTraceback_Ptr)(exc); +} + +void Python::PyException_SetCause(PyObject* exc, PyObject* cause) +{ + return (*PyException_SetCause_Ptr)(exc, cause); +} + +void Python::PyException_SetContext(PyObject* exc, PyObject* context) +{ + return (*PyException_SetContext_Ptr)(exc, context); +} + +int Python::PyException_SetTraceback(PyObject* exc, PyObject* traceback) +{ + return (*PyException_SetTraceback_Ptr)(exc, traceback); +} + +PyObject* Python::PyFile_FromFd(int fd, const char* name, const char* mode, int buffering, const char* encoding, const char* errors, const char* newline, int closefd) +{ + return (*PyFile_FromFd_Ptr)(fd, name, mode, buffering, encoding, errors, newline, closefd); +} + +PyObject* Python::PyFile_GetLine(PyObject* p, int n) +{ + return (*PyFile_GetLine_Ptr)(p, n); +} + +int Python::PyFile_WriteObject(PyObject* obj, PyObject* p, int flags) +{ + return (*PyFile_WriteObject_Ptr)(obj, p, flags); +} + +int Python::PyFile_WriteString(const char* s, PyObject* p) +{ + return (*PyFile_WriteString_Ptr)(s, p); +} + +double Python::PyFloat_AsDouble(PyObject* pyfloat) +{ + return (*PyFloat_AsDouble_Ptr)(pyfloat); +} + +PyObject* Python::PyFloat_FromDouble(double dbl) +{ + return (*PyFloat_FromDouble_Ptr)(dbl); +} + +PyObject* Python::PyFloat_FromString(PyObject* str) +{ + return (*PyFloat_FromString_Ptr)(str); +} + +PyObject* Python::PyFloat_GetInfo() +{ + return (*PyFloat_GetInfo_Ptr)(); +} + +double Python::PyFloat_GetMax() +{ + return (*PyFloat_GetMax_Ptr)(); +} + +double Python::PyFloat_GetMin() +{ + return (*PyFloat_GetMin_Ptr)(); +} + +PyObject* Python::PyFrozenSet_New(PyObject* iterable) +{ + return (*PyFrozenSet_New_Ptr)(iterable); +} + +Py_ssize_t Python::PyGC_Collect() +{ + return (*PyGC_Collect_Ptr)(); +} + +int Python::PyGC_Disable() +{ + return (*PyGC_Disable_Ptr)(); +} + +int Python::PyGC_Enable() +{ + return (*PyGC_Enable_Ptr)(); +} + +int Python::PyGC_IsEnabled() +{ + return (*PyGC_IsEnabled_Ptr)(); +} + +PyGILState_STATE Python::PyGILState_Ensure() +{ + return (*PyGILState_Ensure_Ptr)(); +} + +PyThreadState* Python::PyGILState_GetThisThreadState() +{ + return (*PyGILState_GetThisThreadState_Ptr)(); +} + +void Python::PyGILState_Release(PyGILState_STATE state) +{ + return (*PyGILState_Release_Ptr)(state); +} + +PyObject* Python::PyImport_AddModule(const char* name) +{ + return (*PyImport_AddModule_Ptr)(name); +} + +PyObject* Python::PyImport_AddModuleObject(PyObject* name) +{ + return (*PyImport_AddModuleObject_Ptr)(name); +} + +int Python::PyImport_AppendInittab(const char* name, PyObject*(*initfunc)()) +{ + return (*PyImport_AppendInittab_Ptr)(name, initfunc); +} + +PyObject* Python::PyImport_ExecCodeModule(const char* name, PyObject* co) +{ + return (*PyImport_ExecCodeModule_Ptr)(name, co); +} + +PyObject* Python::PyImport_ExecCodeModuleEx(const char* name, PyObject* co, const char* pathname) +{ + return (*PyImport_ExecCodeModuleEx_Ptr)(name, co, pathname); +} + +PyObject* Python::PyImport_ExecCodeModuleObject(PyObject* name, PyObject* co, PyObject* pathname, PyObject* cpathname) +{ + return (*PyImport_ExecCodeModuleObject_Ptr)(name, co, pathname, cpathname); +} + +PyObject* Python::PyImport_ExecCodeModuleWithPathnames(const char* name, PyObject* co, const char* pathname, const char* cpathname) +{ + return (*PyImport_ExecCodeModuleWithPathnames_Ptr)(name, co, pathname, cpathname); +} + +PyObject* Python::PyImport_GetImporter(PyObject* path) +{ + return (*PyImport_GetImporter_Ptr)(path); +} + +long Python::PyImport_GetMagicNumber() +{ + return (*PyImport_GetMagicNumber_Ptr)(); +} + +const char* Python::PyImport_GetMagicTag() +{ + return (*PyImport_GetMagicTag_Ptr)(); +} + +PyObject* Python::PyImport_GetModule(PyObject *name) +{ + return (*PyImport_GetModule_Ptr)(name); +} + +PyObject* Python::PyImport_GetModuleDict() +{ + return (*PyImport_GetModuleDict_Ptr)(); +} + +PyObject* Python::PyImport_Import(PyObject* name) +{ + return (*PyImport_Import_Ptr)(name); +} + +PyObject* Python::PyImport_ImportModule(const char* name) +{ + return (*PyImport_ImportModule_Ptr)(name); +} + +int Python::PyImport_ImportFrozenModule(const char* name) +{ + return (*PyImport_ImportFrozenModule_Ptr)(name); +} + +int Python::PyImport_ImportFrozenModuleObject(PyObject* name) +{ + return (*PyImport_ImportFrozenModuleObject_Ptr)(name); +} + +PyObject* Python::PyImport_ImportModuleLevel(const char* name, PyObject* globals, PyObject* locals, PyObject* fromlist, int level) +{ + return (*PyImport_ImportModuleLevel_Ptr)(name, globals, locals, fromlist, level); +} + +PyObject* Python::PyImport_ImportModuleLevelObject(PyObject* name, PyObject* globals, PyObject* locals, PyObject* fromlist, int level) +{ + return (*PyImport_ImportModuleLevelObject_Ptr)(name, globals, locals, fromlist, level); +} + +PyObject* Python::PyImport_ImportModuleNoBlock(const char* name) +{ + return (*PyImport_ImportModuleNoBlock_Ptr)(name); +} + +PyObject* Python::PyImport_ReloadModule(PyObject* m) +{ + return (*PyImport_ReloadModule_Ptr)(m); +} + +int (Python::PyIndex_Check)(PyObject* o) +{ + return (*PyIndex_Check_Ptr)(o); +} + +void Python::PyInterpreterState_Clear(PyInterpreterState* state) +{ + return (*PyInterpreterState_Clear_Ptr)(state); +} + +void Python::PyInterpreterState_Delete(PyInterpreterState* state) +{ + return (*PyInterpreterState_Delete_Ptr)(state); +} + +PyInterpreterState* Python::PyInterpreterState_Get() +{ + return (*PyInterpreterState_Get_Ptr)(); +} + +PyObject* Python::PyInterpreterState_GetDict(PyInterpreterState* state) +{ + return (*PyInterpreterState_GetDict_Ptr)(state); +} + +int64_t Python::PyInterpreterState_GetID(PyInterpreterState* state) +{ + return (*PyInterpreterState_GetID_Ptr)(state); +} + +PyInterpreterState* Python::PyInterpreterState_New() +{ + return (*PyInterpreterState_New_Ptr)(); +} + +int (Python::PyIter_Check)(PyObject* o) +{ + return (*PyIter_Check_Ptr)(o); +} + +PyObject* Python::PyIter_Next(PyObject* o) +{ + return (*PyIter_Next_Ptr)(o); +} + +#if HAS_PYTHON_VERSION(0x030A0000) +PySendResult Python::PyIter_Send(PyObject* iter, PyObject* arg, PyObject** presult) +{ + return (*PyIter_Send_Ptr)(iter, arg, presult); +} +#endif + +int Python::PyList_Append(PyObject* list, PyObject* item) +{ + return (*PyList_Append_Ptr)(list, item); +} + +PyObject* Python::PyList_AsTuple(PyObject* list) +{ + return (*PyList_AsTuple_Ptr)(list); +} + +PyObject* Python::PyList_GetItem(PyObject* list, Py_ssize_t index) +{ + return (*PyList_GetItem_Ptr)(list, index); +} + +PyObject* Python::PyList_GetSlice(PyObject* list, Py_ssize_t low, Py_ssize_t high) +{ + return (*PyList_GetSlice_Ptr)(list, low, high); +} + +int Python::PyList_Insert(PyObject* list, Py_ssize_t index, PyObject* item) +{ + return (*PyList_Insert_Ptr)(list, index, item); +} + +PyObject* Python::PyList_New(Py_ssize_t size) +{ + return (*PyList_New_Ptr)(size); +} + +int Python::PyList_Reverse(PyObject* list) +{ + return (*PyList_Reverse_Ptr)(list); +} + +int Python::PyList_SetItem(PyObject* list, Py_ssize_t index, PyObject* item) +{ + return (*PyList_SetItem_Ptr)(list, index, item); +} + +int Python::PyList_SetSlice(PyObject* list, Py_ssize_t low, Py_ssize_t high, PyObject* item) +{ + return (*PyList_SetSlice_Ptr)(list, low, high, item); +} + +Py_ssize_t Python::PyList_Size(PyObject* list) +{ + return (*PyList_Size_Ptr)(list); +} + +int Python::PyList_Sort(PyObject* list) +{ + return (*PyList_Sort_Ptr)(list); +} + +double Python::PyLong_AsDouble(PyObject* pylong) +{ + return (*PyLong_AsDouble_Ptr)(pylong); +} + +long Python::PyLong_AsLong(PyObject* pylong) +{ + return (*PyLong_AsLong_Ptr)(pylong); +} + +long Python::PyLong_AsLongAndOverflow(PyObject* pylong, int* overflow) +{ + return (*PyLong_AsLongAndOverflow_Ptr)(pylong, overflow); +} + +long long Python::PyLong_AsLongLong(PyObject* pylong) +{ + return (*PyLong_AsLongLong_Ptr)(pylong); +} + +long long Python::PyLong_AsLongLongAndOverflow(PyObject* pylong, int* overflow) +{ + return (*PyLong_AsLongLongAndOverflow_Ptr)(pylong, overflow); +} + +size_t Python::PyLong_AsSize_t(PyObject* pylong) +{ + return (*PyLong_AsSize_t_Ptr)(pylong); +} + +Py_ssize_t Python::PyLong_AsSsize_t(PyObject* pylong) +{ + return (*PyLong_AsSsize_t_Ptr)(pylong); +} + +unsigned long Python::PyLong_AsUnsignedLong(PyObject* pylong) +{ + return (*PyLong_AsUnsignedLong_Ptr)(pylong); +} + +unsigned long long Python::PyLong_AsUnsignedLongLong(PyObject* pylong) +{ + return (*PyLong_AsUnsignedLongLong_Ptr)(pylong); +} + +unsigned long long Python::PyLong_AsUnsignedLongLongMask(PyObject* pylong) +{ + return (*PyLong_AsUnsignedLongLongMask_Ptr)(pylong); +} + +unsigned long Python::PyLong_AsUnsignedLongMask(PyObject* pylong) +{ + return (*PyLong_AsUnsignedLongMask_Ptr)(pylong); +} + +void* Python::PyLong_AsVoidPtr(PyObject* pylong) +{ + return (*PyLong_AsVoidPtr_Ptr)(pylong); +} + +PyObject* Python::PyLong_FromDouble(double dbl) +{ + return (*PyLong_FromDouble_Ptr)(dbl); +} + +PyObject* Python::PyLong_FromLong(long lng) +{ + return (*PyLong_FromLong_Ptr)(lng); +} + +PyObject* Python::PyLong_FromLongLong(long long longlong) +{ + return (*PyLong_FromLongLong_Ptr)(longlong); +} + +PyObject* Python::PyLong_FromSize_t(size_t size) +{ + return (*PyLong_FromSize_t_Ptr)(size); +} + +PyObject* Python::PyLong_FromSsize_t(Py_ssize_t size) +{ + return (*PyLong_FromSsize_t_Ptr)(size); +} + +PyObject* Python::PyLong_FromString(const char* str, char** pend, int base) +{ + return (*PyLong_FromString_Ptr)(str, pend, base); +} + +PyObject* Python::PyLong_FromUnsignedLong(unsigned long ulong) +{ + return (*PyLong_FromUnsignedLong_Ptr)(ulong); +} + +PyObject* Python::PyLong_FromUnsignedLongLong(unsigned long long ulonglong) +{ + return (*PyLong_FromUnsignedLongLong_Ptr)(ulonglong); +} + +PyObject* Python::PyLong_FromVoidPtr(void* ptr) +{ + return (*PyLong_FromVoidPtr_Ptr)(ptr); +} + +PyObject* Python::PyLong_GetInfo() +{ + return (*PyLong_GetInfo_Ptr)(); +} + +int Python::PyMapping_Check(PyObject* o) +{ + return (*PyMapping_Check_Ptr)(o); +} + +PyObject* Python::PyMapping_GetItemString(PyObject* o, const char* key) +{ + return (*PyMapping_GetItemString_Ptr)(o, key); +} + +int Python::PyMapping_HasKey(PyObject* o, PyObject* key) +{ + return (*PyMapping_HasKey_Ptr)(o, key); +} + +int Python::PyMapping_HasKeyString(PyObject* o, const char* key) +{ + return (*PyMapping_HasKeyString_Ptr)(o, key); +} + +PyObject* Python::PyMapping_Items(PyObject* o) +{ + return (*PyMapping_Items_Ptr)(o); +} + +PyObject* Python::PyMapping_Keys(PyObject* o) +{ + return (*PyMapping_Keys_Ptr)(o); +} + +Py_ssize_t Python::PyMapping_Length(PyObject* o) +{ + return (*PyMapping_Length_Ptr)(o); +} + +int Python::PyMapping_SetItemString(PyObject* o, const char* key, PyObject* value) +{ + return (*PyMapping_SetItemString_Ptr)(o, key, value); +} + +Py_ssize_t Python::PyMapping_Size(PyObject* o) +{ + return (*PyMapping_Size_Ptr)(o); +} + +PyObject* Python::PyMapping_Values(PyObject* o) +{ + return (*PyMapping_Values_Ptr)(o); +} + +void* Python::PyMem_Calloc(size_t nelem, size_t elsize) +{ + return (*PyMem_Calloc_Ptr)(nelem, elsize); +} + +void Python::PyMem_Free(void* ptr) +{ + return (*PyMem_Free_Ptr)(ptr); +} + +void* Python::PyMem_Malloc(size_t size) +{ + return (*PyMem_Malloc_Ptr)(size); +} + +void* Python::PyMem_Realloc(void* ptr, size_t new_size) +{ + return (*PyMem_Realloc_Ptr)(ptr, new_size); +} + +#if HAS_PYTHON_VERSION(0x030B1000) +PyObject* Python::PyMemoryView_FromBuffer(const Py_buffer* info) +{ + return (*PyMemoryView_FromBuffer_Ptr)(info); +} +#endif + +PyObject* Python::PyMemoryView_FromMemory(char* mem, Py_ssize_t size, int flags) +{ + return (*PyMemoryView_FromMemory_Ptr)(mem, size, flags); +} + +PyObject* Python::PyMemoryView_FromObject(PyObject* base) +{ + return (*PyMemoryView_FromObject_Ptr)(base); +} + +PyObject* Python::PyMemoryView_GetContiguous(PyObject* base, int buffertype, char order) +{ + return (*PyMemoryView_GetContiguous_Ptr)(base, buffertype, order); +} + +PyObject* Python::PyModuleDef_Init(PyModuleDef* def) +{ + return (*PyModuleDef_Init_Ptr)(def); +} + +int Python::PyModule_AddFunctions(PyObject* module, PyMethodDef* functions) +{ + return (*PyModule_AddFunctions_Ptr)(module, functions); +} + +int Python::PyModule_AddIntConstant(PyObject* module, const char* name, long value) +{ + return (*PyModule_AddIntConstant_Ptr)(module, name, value); +} + +int Python::PyModule_AddObject(PyObject* module, const char* name, PyObject* value) +{ + return (*PyModule_AddObject_Ptr)(module, name, value); +} + +int Python::PyModule_AddObjectRef(PyObject* mod, const char* name, PyObject* value) +{ + return (*PyModule_AddObjectRef_Ptr)(mod, name, value); +} + +int Python::PyModule_AddStringConstant(PyObject* module, const char* name, const char* value) +{ + return (*PyModule_AddStringConstant_Ptr)(module, name, value); +} + +int Python::PyModule_AddType(PyObject* module, PyTypeObject* type) +{ + return (*PyModule_AddType_Ptr)(module, type); +} + +PyObject* Python::PyModule_Create2(PyModuleDef* def, int apiver) +{ + return (*PyModule_Create2_Ptr)(def, apiver); +} + +int Python::PyModule_ExecDef(PyObject* module, PyModuleDef* def) +{ + return (*PyModule_ExecDef_Ptr)(module, def); +} + +PyObject* Python::PyModule_FromDefAndSpec2(PyModuleDef* def, PyObject* spec, int module_api_version) +{ + return (*PyModule_FromDefAndSpec2_Ptr)(def, spec, module_api_version); +} + +PyModuleDef* Python::PyModule_GetDef(PyObject* module) +{ + return (*PyModule_GetDef_Ptr)(module); +} + +PyObject* Python::PyModule_GetDict(PyObject* module) +{ + return (*PyModule_GetDict_Ptr)(module); +} + +const char* Python::PyModule_GetFilename(PyObject* module) +{ + return (*PyModule_GetFilename_Ptr)(module); +} + +PyObject* Python::PyModule_GetFilenameObject(PyObject* module) +{ + return (*PyModule_GetFilenameObject_Ptr)(module); +} + +const char* Python::PyModule_GetName(PyObject* module) +{ + return (*PyModule_GetName_Ptr)(module); +} + +PyObject* Python::PyModule_GetNameObject(PyObject* module) +{ + return (*PyModule_GetNameObject_Ptr)(module); +} + +void* Python::PyModule_GetState(PyObject* module) +{ + return (*PyModule_GetState_Ptr)(module); +} + +PyObject* Python::PyModule_New(const char* name) +{ + return (*PyModule_New_Ptr)(name); +} + +PyObject* Python::PyModule_NewObject(PyObject* name) +{ + return (*PyModule_NewObject_Ptr)(name); +} + +int Python::PyModule_SetDocString(PyObject* module, const char* docs) +{ + return (*PyModule_SetDocString_Ptr)(module, docs); +} + +PyObject* Python::PyNumber_Absolute(PyObject* o) +{ + return (*PyNumber_Absolute_Ptr)(o); +} + +PyObject* Python::PyNumber_Add(PyObject* o1, PyObject* o2) +{ + return (*PyNumber_Add_Ptr)(o1, o2); +} + +PyObject* Python::PyNumber_And(PyObject* o1, PyObject* o2) +{ + return (*PyNumber_And_Ptr)(o1, o2); +} + +Py_ssize_t Python::PyNumber_AsSsize_t(PyObject* o, PyObject* exc) +{ + return (*PyNumber_AsSsize_t_Ptr)(o, exc); +} + +int Python::PyNumber_Check(PyObject* o) +{ + return (*PyNumber_Check_Ptr)(o); +} + +PyObject* Python::PyNumber_Divmod(PyObject* o1, PyObject* o2) +{ + return (*PyNumber_Divmod_Ptr)(o1, o2); +} + +PyObject* Python::PyNumber_Float(PyObject* o) +{ + return (*PyNumber_Float_Ptr)(o); +} + +PyObject* Python::PyNumber_FloorDivide(PyObject* o1, PyObject* o2) +{ + return (*PyNumber_FloorDivide_Ptr)(o1, o2); +} + +PyObject* Python::PyNumber_InPlaceAdd(PyObject* o1, PyObject* o2) +{ + return (*PyNumber_InPlaceAdd_Ptr)(o1, o2); +} + +PyObject* Python::PyNumber_InPlaceAnd(PyObject* o1, PyObject* o2) +{ + return (*PyNumber_InPlaceAnd_Ptr)(o1, o2); +} + +PyObject* Python::PyNumber_InPlaceFloorDivide(PyObject* o1, PyObject* o2) +{ + return (*PyNumber_InPlaceFloorDivide_Ptr)(o1, o2); +} + +PyObject* Python::PyNumber_InPlaceLshift(PyObject* o1, PyObject* o2) +{ + return (*PyNumber_InPlaceLshift_Ptr)(o1, o2); +} + +PyObject* Python::PyNumber_InPlaceMatrixMultiply(PyObject* o1, PyObject* o2) +{ + return (*PyNumber_InPlaceMatrixMultiply_Ptr)(o1, o2); +} + +PyObject* Python::PyNumber_InPlaceMultiply(PyObject* o1, PyObject* o2) +{ + return (*PyNumber_InPlaceMultiply_Ptr)(o1, o2); +} + +PyObject* Python::PyNumber_InPlaceOr(PyObject* o1, PyObject* o2) +{ + return (*PyNumber_InPlaceOr_Ptr)(o1, o2); +} + +PyObject* Python::PyNumber_InPlacePower(PyObject* o1, PyObject* o2, PyObject* o3) +{ + return (*PyNumber_InPlacePower_Ptr)(o1, o2, o3); +} + +PyObject* Python::PyNumber_InPlaceRemainder(PyObject* o1, PyObject* o2) +{ + return (*PyNumber_InPlaceRemainder_Ptr)(o1, o2); +} + +PyObject* Python::PyNumber_InPlaceRshift(PyObject* o1, PyObject* o2) +{ + return (*PyNumber_InPlaceRshift_Ptr)(o1, o2); +} + +PyObject* Python::PyNumber_InPlaceSubtract(PyObject* o1, PyObject* o2) +{ + return (*PyNumber_InPlaceSubtract_Ptr)(o1, o2); +} + +PyObject* Python::PyNumber_InPlaceTrueDivide(PyObject* o1, PyObject* o2) +{ + return (*PyNumber_InPlaceTrueDivide_Ptr)(o1, o2); +} + +PyObject* Python::PyNumber_InPlaceXor(PyObject* o1, PyObject* o2) +{ + return (*PyNumber_InPlaceXor_Ptr)(o1, o2); +} + +PyObject* Python::PyNumber_Index(PyObject* o) +{ + return (*PyNumber_Index_Ptr)(o); +} + +PyObject* Python::PyNumber_Invert(PyObject* o) +{ + return (*PyNumber_Invert_Ptr)(o); +} + +PyObject* Python::PyNumber_Long(PyObject* o) +{ + return (*PyNumber_Long_Ptr)(o); +} + +PyObject* Python::PyNumber_Lshift(PyObject* o1, PyObject* o2) +{ + return (*PyNumber_Lshift_Ptr)(o1, o2); +} + +PyObject* Python::PyNumber_MatrixMultiply(PyObject* o1, PyObject* o2) +{ + return (*PyNumber_MatrixMultiply_Ptr)(o1, o2); +} + +PyObject* Python::PyNumber_Multiply(PyObject* o1, PyObject* o2) +{ + return (*PyNumber_Multiply_Ptr)(o1, o2); +} + +PyObject* Python::PyNumber_Negative(PyObject* o) +{ + return (*PyNumber_Negative_Ptr)(o); +} + +PyObject* Python::PyNumber_Or(PyObject* o1, PyObject* o2) +{ + return (*PyNumber_Or_Ptr)(o1, o2); +} + +PyObject* Python::PyNumber_Positive(PyObject* o) +{ + return (*PyNumber_Positive_Ptr)(o); +} + +PyObject* Python::PyNumber_Power(PyObject* o1, PyObject* o2, PyObject* o3) +{ + return (*PyNumber_Power_Ptr)(o1, o2, o3); +} + +PyObject* Python::PyNumber_Remainder(PyObject* o1, PyObject* o2) +{ + return (*PyNumber_Remainder_Ptr)(o1, o2); +} + +PyObject* Python::PyNumber_Rshift(PyObject* o1, PyObject* o2) +{ + return (*PyNumber_Rshift_Ptr)(o1, o2); +} + +PyObject* Python::PyNumber_Subtract(PyObject* o1, PyObject* o2) +{ + return (*PyNumber_Subtract_Ptr)(o1, o2); +} + +PyObject* Python::PyNumber_ToBase(PyObject* n, int base) +{ + return (*PyNumber_ToBase_Ptr)(n, base); +} + +PyObject* Python::PyNumber_TrueDivide(PyObject* o1, PyObject* o2) +{ + return (*PyNumber_TrueDivide_Ptr)(o1, o2); +} + +PyObject* Python::PyNumber_Xor(PyObject* o1, PyObject* o2) +{ + return (*PyNumber_Xor_Ptr)(o1, o2); +} + +void Python::PyOS_AfterFork() +{ + return (*PyOS_AfterFork_Ptr)(); +} + +void Python::PyOS_AfterFork_Child() +{ + return (*PyOS_AfterFork_Child_Ptr)(); +} + +void Python::PyOS_AfterFork_Parent() +{ + return (*PyOS_AfterFork_Parent_Ptr)(); +} + +void Python::PyOS_BeforeFork() +{ + return (*PyOS_BeforeFork_Ptr)(); +} + +PyObject* Python::PyOS_FSPath(PyObject* path) +{ + return (*PyOS_FSPath_Ptr)(path); +} + +int Python::PyOS_InterruptOccurred() +{ + return (*PyOS_InterruptOccurred_Ptr)(); +} + +char* Python::PyOS_double_to_string(double val, char format_code, int precision, int flags, int* type) +{ + return (*PyOS_double_to_string_Ptr)(val, format_code, precision, flags, type); +} + +PyOS_sighandler_t Python::PyOS_getsig(int i) +{ + return (*PyOS_getsig_Ptr)(i); +} + +int Python::PyOS_mystricmp(const char* a, const char* b) +{ + return (*PyOS_mystricmp_Ptr)(a, b); +} + +int Python::PyOS_mystrnicmp(const char* a, const char* b, Py_ssize_t max_len) +{ + return (*PyOS_mystrnicmp_Ptr)(a, b, max_len); +} + +PyOS_sighandler_t Python::PyOS_setsig(int i, PyOS_sighandler_t handler) +{ + return (*PyOS_setsig_Ptr)(i, handler); +} + +double Python::PyOS_string_to_double(const char* str, char** endptr, PyObject* overflow_exception) +{ + return (*PyOS_string_to_double_Ptr)(str, endptr, overflow_exception); +} + +long Python::PyOS_strtol(const char* str, char** endptr, int base) +{ + return (*PyOS_strtol_Ptr)(str, endptr, base); +} + +unsigned long Python::PyOS_strtoul(const char* str, char** endptr, int base) +{ + return (*PyOS_strtoul_Ptr)(str, endptr, base); +} + +int Python::PyOS_vsnprintf(char* str, size_t size, const char* format, va_list va) +{ + return (*PyOS_vsnprintf_Ptr)(str, size, format, va); +} + +PyObject* Python::PyObject_ASCII(PyObject* o) +{ + return (*PyObject_ASCII_Ptr)(o); +} + +int Python::PyObject_AsCharBuffer(PyObject* obj, const char** buffer, Py_ssize_t* buffer_len) +{ + return (*PyObject_AsCharBuffer_Ptr)(obj, buffer, buffer_len); +} + +int Python::PyObject_AsFileDescriptor(PyObject* o) +{ + return (*PyObject_AsFileDescriptor_Ptr)(o); +} + +int Python::PyObject_AsReadBuffer(PyObject* obj, const void** buffer, Py_ssize_t* buffer_len) +{ + return (*PyObject_AsReadBuffer_Ptr)(obj, buffer, buffer_len); +} + +int Python::PyObject_AsWriteBuffer(PyObject* obj, void** buffer, Py_ssize_t* buffer_len) +{ + return (*PyObject_AsWriteBuffer_Ptr)(obj, buffer, buffer_len); +} + +PyObject* Python::PyObject_Bytes(PyObject* o) +{ + return (*PyObject_Bytes_Ptr)(o); +} + +PyObject* Python::PyObject_Call(PyObject* callable, PyObject* args, PyObject* kwargs) +{ + return (*PyObject_Call_Ptr)(callable, args, kwargs); +} + +PyObject* Python::PyObject_CallNoArgs(PyObject* func) +{ + return (*PyObject_CallNoArgs_Ptr)(func); +} + +PyObject* Python::PyObject_CallObject(PyObject* callable, PyObject* args) +{ + return (*PyObject_CallObject_Ptr)(callable, args); +} + +void* Python::PyObject_Calloc(size_t nelem, size_t elsize) +{ + return (*PyObject_Calloc_Ptr)(nelem, elsize); +} + +int Python::PyObject_CheckBuffer(PyObject* obj) +{ + return (*PyObject_CheckBuffer_Ptr)(obj); +} + +int Python::PyObject_CheckReadBuffer(PyObject* obj) +{ + return (*PyObject_CheckReadBuffer_Ptr)(obj); +} + +void Python::PyObject_ClearWeakRefs(PyObject* obj) +{ + return (*PyObject_ClearWeakRefs_Ptr)(obj); +} + +int Python::PyObject_CopyData(PyObject* dest, PyObject* src) +{ + return (*PyObject_CopyData_Ptr)(dest, src); +} + +int Python::PyObject_DelItem(PyObject* o, PyObject* key) +{ + return (*PyObject_DelItem_Ptr)(o, key); +} + +int Python::PyObject_DelItemString(PyObject* o, const char* key) +{ + return (*PyObject_DelItemString_Ptr)(o, key); +} + +PyObject* Python::PyObject_Dir(PyObject* obj) +{ + return (*PyObject_Dir_Ptr)(obj); +} + +PyObject* Python::PyObject_Format(PyObject* obj, PyObject* format_spec) +{ + return (*PyObject_Format_Ptr)(obj, format_spec); +} + +void Python::PyObject_Free(void* ptr) +{ + return (*PyObject_Free_Ptr)(ptr); +} + +void Python::PyObject_GC_Del(void* op) +{ + return (*PyObject_GC_Del_Ptr)(op); +} + +int Python::PyObject_GC_IsFinalized(PyObject* op) +{ + return (*PyObject_GC_IsFinalized_Ptr)(op); +} + +int Python::PyObject_GC_IsTracked(PyObject* op) +{ + return (*PyObject_GC_IsTracked_Ptr)(op); +} + +void Python::PyObject_GC_Track(void* op) +{ + return (*PyObject_GC_Track_Ptr)(op); +} + +void Python::PyObject_GC_UnTrack(void* op) +{ + return (*PyObject_GC_UnTrack_Ptr)(op); +} + +PyObject* Python::PyObject_GenericGetAttr(PyObject* o, PyObject* name) +{ + return (*PyObject_GenericGetAttr_Ptr)(o, name); +} + +PyObject* Python::PyObject_GenericGetDict(PyObject* o, void* context) +{ + return (*PyObject_GenericGetDict_Ptr)(o, context); +} + +int Python::PyObject_GenericSetAttr(PyObject* o, PyObject* name, PyObject* value) +{ + return (*PyObject_GenericSetAttr_Ptr)(o, name, value); +} + +int Python::PyObject_GenericSetDict(PyObject* o, PyObject* value, void* context) +{ + return (*PyObject_GenericSetDict_Ptr)(o, value, context); +} + +PyObject* Python::PyObject_GetAIter(PyObject* o) +{ + return (*PyObject_GetAIter_Ptr)(o); +} + +PyObject* Python::PyObject_GetAttr(PyObject* o, PyObject* attr_name) +{ + return (*PyObject_GetAttr_Ptr)(o, attr_name); +} + +PyObject* Python::PyObject_GetAttrString(PyObject* o, const char* attr) +{ + return (*PyObject_GetAttrString_Ptr)(o, attr); +} + +#if HAS_PYTHON_VERSION(0x030B1000) +int Python::PyObject_GetBuffer(PyObject* obj, Py_buffer* view, int flags) +{ + return (*PyObject_GetBuffer_Ptr)(obj, view, flags); +} +#endif + +PyObject* Python::PyObject_GetItem(PyObject* o, PyObject* key) +{ + return (*PyObject_GetItem_Ptr)(o, key); +} + +PyObject* Python::PyObject_GetIter(PyObject* o) +{ + return (*PyObject_GetIter_Ptr)(o); +} + +int Python::PyObject_HasAttr(PyObject* o, PyObject* attr_name) +{ + return (*PyObject_HasAttr_Ptr)(o, attr_name); +} + +int Python::PyObject_HasAttrString(PyObject* o , const char* attr) +{ + return (*PyObject_HasAttrString_Ptr)(o, attr); +} + +Py_hash_t Python::PyObject_Hash(PyObject* o) +{ + return (*PyObject_Hash_Ptr)(o); +} + +Py_hash_t Python::PyObject_HashNotImplemented(PyObject* o) +{ + return (*PyObject_HashNotImplemented_Ptr)(o); +} + +PyObject* Python::PyObject_Init(PyObject* o, PyTypeObject* type) +{ + return (*PyObject_Init_Ptr)(o, type); +} + +PyVarObject* Python::PyObject_InitVar(PyVarObject* op, PyTypeObject* type, Py_ssize_t size) +{ + return (*PyObject_InitVar_Ptr)(op, type, size); +} + +int Python::PyObject_IsInstance(PyObject* object, PyObject* typeorclass) +{ + return (*PyObject_IsInstance_Ptr)(object, typeorclass); +} + +int Python::PyObject_IsSubclass(PyObject* object, PyObject* typeorclass) +{ + return (*PyObject_IsSubclass_Ptr)(object, typeorclass); +} + +int Python::PyObject_IsTrue(PyObject* o) +{ + return (*PyObject_IsTrue_Ptr)(o); +} + +Py_ssize_t Python::PyObject_Length(PyObject* o) +{ + return (*PyObject_Length_Ptr)(o); +} + +void* Python::PyObject_Malloc(size_t size) +{ + return (*PyObject_Malloc_Ptr)(size); +} + +int Python::PyObject_Not(PyObject* o) +{ + return (*PyObject_Not_Ptr)(o); +} + +void* Python::PyObject_Realloc(void* ptr, size_t new_size) +{ + return (*PyObject_Realloc_Ptr)(ptr, new_size); +} + +PyObject* Python::PyObject_Repr(PyObject* o) +{ + return (*PyObject_Repr_Ptr)(o); +} + +PyObject* Python::PyObject_RichCompare(PyObject* a, PyObject* b, int opid) +{ + return (*PyObject_RichCompare_Ptr)(a, b, opid); +} + +int Python::PyObject_RichCompareBool(PyObject* a, PyObject* b, int opid) +{ + return (*PyObject_RichCompareBool_Ptr)(a, b, opid); +} + +PyObject* Python::PyObject_SelfIter(PyObject* o) +{ + return (*PyObject_SelfIter_Ptr)(o); +} + +int Python::PyObject_SetAttr(PyObject* o, PyObject* attr_name, PyObject* v) +{ + return (*PyObject_SetAttr_Ptr)(o, attr_name, v); +} + +int Python::PyObject_SetAttrString(PyObject* o, const char* attr_name, PyObject* value) +{ + return (*PyObject_SetAttrString_Ptr)(o, attr_name, value); +} + +int Python::PyObject_SetItem(PyObject* o, PyObject* key, PyObject* v) +{ + return (*PyObject_SetItem_Ptr)(o, key, v); +} + +Py_ssize_t Python::PyObject_Size(PyObject* o) +{ + return (*PyObject_Size_Ptr)(o); +} + +PyObject* Python::PyObject_Str(PyObject* o) +{ + return (*PyObject_Str_Ptr)(o); +} + +PyObject* Python::PyObject_Type(PyObject* o) +{ + return (*PyObject_Type_Ptr)(o); +} + +#if HAS_PYTHON_VERSION(0x030B0000) +int (Python::PyObject_TypeCheck)(PyObject* o, PyTypeObject* type) +{ + return (*PyObject_TypeCheck_Ptr)(o, type); +} +#endif + +PyObject* Python::PySeqIter_New(PyObject* o) +{ + return (*PySeqIter_New_Ptr)(o); +} + +int Python::PySequence_Check(PyObject* o) +{ + return (*PySequence_Check_Ptr)(o); +} + +PyObject* Python::PySequence_Concat(PyObject* o1, PyObject* o2) +{ + return (*PySequence_Concat_Ptr)(o1, o2); +} + +int Python::PySequence_Contains(PyObject* seq, PyObject* ob) +{ + return (*PySequence_Contains_Ptr)(seq, ob); +} + +Py_ssize_t Python::PySequence_Count(PyObject* o, PyObject* value) +{ + return (*PySequence_Count_Ptr)(o, value); +} + +int Python::PySequence_DelItem(PyObject* o, Py_ssize_t i) +{ + return (*PySequence_DelItem_Ptr)(o, i); +} + +int Python::PySequence_DelSlice(PyObject* o, Py_ssize_t i1, Py_ssize_t i2) +{ + return (*PySequence_DelSlice_Ptr)(o, i1, i2); +} + +PyObject* Python::PySequence_Fast(PyObject* o, const char* m) +{ + return (*PySequence_Fast_Ptr)(o, m); +} + +PyObject* Python::PySequence_GetItem(PyObject* o, Py_ssize_t i) +{ + return (*PySequence_GetItem_Ptr)(o, i); +} + +PyObject* Python::PySequence_GetSlice(PyObject* o, Py_ssize_t i1, Py_ssize_t i2) +{ + return (*PySequence_GetSlice_Ptr)(o, i1, i2); +} + +int Python::PySequence_In(PyObject* o, PyObject* value) +{ + return (*PySequence_In_Ptr)(o, value); +} + +PyObject* Python::PySequence_InPlaceConcat(PyObject* o1, PyObject* o2) +{ + return (*PySequence_InPlaceConcat_Ptr)(o1, o2); +} + +PyObject* Python::PySequence_InPlaceRepeat(PyObject* o, Py_ssize_t count) +{ + return (*PySequence_InPlaceRepeat_Ptr)(o, count); +} + +Py_ssize_t Python::PySequence_Index(PyObject* o, PyObject* value) +{ + return (*PySequence_Index_Ptr)(o, value); +} + +Py_ssize_t Python::PySequence_Length(PyObject* o) +{ + return (*PySequence_Length_Ptr)(o); +} + +PyObject* Python::PySequence_List(PyObject* o) +{ + return (*PySequence_List_Ptr)(o); +} + +PyObject* Python::PySequence_Repeat(PyObject* o, Py_ssize_t count) +{ + return (*PySequence_Repeat_Ptr)(o, count); +} + +int Python::PySequence_SetItem(PyObject* o, Py_ssize_t i, PyObject* v) +{ + return (*PySequence_SetItem_Ptr)(o, i, v); +} + +int Python::PySequence_SetSlice(PyObject* o, Py_ssize_t i1, Py_ssize_t i2, PyObject* v) +{ + return (*PySequence_SetSlice_Ptr)(o, i1, i2, v); +} + +Py_ssize_t Python::PySequence_Size(PyObject* o) +{ + return (*PySequence_Size_Ptr)(o); +} + +PyObject* Python::PySequence_Tuple(PyObject* o) +{ + return (*PySequence_Tuple_Ptr)(o); +} + +int Python::PySet_Add(PyObject* set, PyObject* key) +{ + return (*PySet_Add_Ptr)(set, key); +} + +int Python::PySet_Clear(PyObject* set) +{ + return (*PySet_Clear_Ptr)(set); +} + +int Python::PySet_Contains(PyObject* anyset, PyObject* key) +{ + return (*PySet_Contains_Ptr)(anyset, key); +} + +int Python::PySet_Discard(PyObject* set, PyObject* key) +{ + return (*PySet_Discard_Ptr)(set, key); +} + +PyObject* Python::PySet_New(PyObject* iterable) +{ + return (*PySet_New_Ptr)(iterable); +} + +PyObject* Python::PySet_Pop(PyObject* set) +{ + return (*PySet_Pop_Ptr)(set); +} + +Py_ssize_t Python::PySet_Size(PyObject* anyset) +{ + return (*PySet_Size_Ptr)(anyset); +} + +Py_ssize_t Python::PySlice_AdjustIndices(Py_ssize_t length, Py_ssize_t* start, Py_ssize_t* stop, Py_ssize_t step) +{ + return (*PySlice_AdjustIndices_Ptr)(length, start, stop, step); +} + +int Python::PySlice_GetIndices(PyObject* r, Py_ssize_t length, Py_ssize_t* start, Py_ssize_t* stop, Py_ssize_t* step) +{ + return (*PySlice_GetIndices_Ptr)(r, length, start, stop, step); +} + +#ifdef PySlice_GetIndicesEx +int (Python::PySlice_GetIndicesEx)(PyObject* r, Py_ssize_t length, Py_ssize_t* start, Py_ssize_t* stop, Py_ssize_t* step, Py_ssize_t* slicelength) +{ + return (*PySlice_GetIndicesEx_Ptr)(r, length, start, stop, step, slicelength); +} +#endif + +PyObject* Python::PySlice_New(PyObject* start, PyObject* stop, PyObject* step) +{ + return (*PySlice_New_Ptr)(start, stop, step); +} + +int Python::PySlice_Unpack(PyObject* slice, Py_ssize_t* start, Py_ssize_t* stop, Py_ssize_t* step) +{ + return (*PySlice_Unpack_Ptr)(slice, start, stop, step); +} + +int Python::PyState_AddModule(PyObject* module, PyModuleDef* def) +{ + return (*PyState_AddModule_Ptr)(module, def); +} + +PyObject* Python::PyState_FindModule(PyModuleDef* def) +{ + return (*PyState_FindModule_Ptr)(def); +} + +int Python::PyState_RemoveModule(PyModuleDef* def) +{ + return (*PyState_RemoveModule_Ptr)(def); +} + +PyObject* Python::PyStructSequence_GetItem(PyObject* p, Py_ssize_t pos) +{ + return (*PyStructSequence_GetItem_Ptr)(p, pos); +} + +PyObject* Python::PyStructSequence_New(PyTypeObject* type) +{ + return (*PyStructSequence_New_Ptr)(type); +} + +PyTypeObject* Python::PyStructSequence_NewType(PyStructSequence_Desc* desc) +{ + return (*PyStructSequence_NewType_Ptr)(desc); +} + +void Python::PyStructSequence_SetItem(PyObject* p, Py_ssize_t pos, PyObject* o) +{ + return (*PyStructSequence_SetItem_Ptr)(p, pos, o); +} + +void Python::PySys_AddWarnOption(const wchar_t* s) +{ + return (*PySys_AddWarnOption_Ptr)(s); +} + +void Python::PySys_AddWarnOptionUnicode(PyObject* unicode) +{ + return (*PySys_AddWarnOptionUnicode_Ptr)(unicode); +} + +void Python::PySys_AddXOption(const wchar_t* option) +{ + return (*PySys_AddXOption_Ptr)(option); +} + +PyObject* Python::PySys_GetObject(const char* name) +{ + return (*PySys_GetObject_Ptr)(name); +} + +PyObject* Python::PySys_GetXOptions() +{ + return (*PySys_GetXOptions_Ptr)(); +} + +int Python::PySys_HasWarnOptions() +{ + return (*PySys_HasWarnOptions_Ptr)(); +} + +void Python::PySys_ResetWarnOptions() +{ + return (*PySys_ResetWarnOptions_Ptr)(); +} + +void Python::PySys_SetArgv(int argc, wchar_t** argv) +{ + return (*PySys_SetArgv_Ptr)(argc, argv); +} + +void Python::PySys_SetArgvEx(int argc, wchar_t** argv, int updatepath) +{ + return (*PySys_SetArgvEx_Ptr)(argc, argv, updatepath); +} + +int Python::PySys_SetObject(const char* name, PyObject* v) +{ + return (*PySys_SetObject_Ptr)(name, v); +} + +void Python::PySys_SetPath(const wchar_t* path) +{ + return (*PySys_SetPath_Ptr)(path); +} + +void Python::PyThreadState_Clear(PyThreadState* tstate) +{ + return (*PyThreadState_Clear_Ptr)(tstate); +} + +void Python::PyThreadState_Delete(PyThreadState* tstate) +{ + return (*PyThreadState_Delete_Ptr)(tstate); +} + +PyThreadState* Python::PyThreadState_Get() +{ + return (*PyThreadState_Get_Ptr)(); +} + +PyObject* Python::PyThreadState_GetDict() +{ + return (*PyThreadState_GetDict_Ptr)(); +} + +#if HAS_PYTHON_VERSION(0x03090000) +PyFrameObject* Python::PyThreadState_GetFrame(PyThreadState* tstate) +{ + return (*PyThreadState_GetFrame_Ptr)(tstate); +} +#endif + +uint64_t Python::PyThreadState_GetID(PyThreadState* tstate) +{ + return (*PyThreadState_GetID_Ptr)(tstate); +} + +PyInterpreterState* Python::PyThreadState_GetInterpreter(PyThreadState* tstate) +{ + return (*PyThreadState_GetInterpreter_Ptr)(tstate); +} + +PyThreadState* Python::PyThreadState_New(PyInterpreterState* interp) +{ + return (*PyThreadState_New_Ptr)(interp); +} + +int Python::PyThreadState_SetAsyncExc(unsigned long id, PyObject* exc) +{ + return (*PyThreadState_SetAsyncExc_Ptr)(id, exc); +} + +PyThreadState* Python::PyThreadState_Swap(PyThreadState* tstate) +{ + return (*PyThreadState_Swap_Ptr)(tstate); +} + +PyObject* Python::PyThread_GetInfo() +{ + return (*PyThread_GetInfo_Ptr)(); +} + +void Python::PyThread_ReInitTLS() +{ + return (*PyThread_ReInitTLS_Ptr)(); +} + +int Python::PyThread_acquire_lock(PyThread_type_lock lock, int intr_flag) +{ + return (*PyThread_acquire_lock_Ptr)(lock, intr_flag); +} + +PyLockStatus Python::PyThread_acquire_lock_timed(PyThread_type_lock lock, long long microseconds, int intr_flag) +{ + return (*PyThread_acquire_lock_timed_Ptr)(lock, microseconds, intr_flag); +} + +PyThread_type_lock Python::PyThread_allocate_lock() +{ + return (*PyThread_allocate_lock_Ptr)(); +} + +int Python::PyThread_create_key() +{ + return (*PyThread_create_key_Ptr)(); +} + +void Python::PyThread_delete_key(int key) +{ + return (*PyThread_delete_key_Ptr)(key); +} + +void Python::PyThread_delete_key_value(int key) +{ + return (*PyThread_delete_key_value_Ptr)(key); +} + +void Python::PyThread_exit_thread() +{ + return (*PyThread_exit_thread_Ptr)(); +} + +void Python::PyThread_free_lock(PyThread_type_lock lock) +{ + return (*PyThread_free_lock_Ptr)(lock); +} + +void* Python::PyThread_get_key_value(int key) +{ + return (*PyThread_get_key_value_Ptr)(key); +} + +size_t Python::PyThread_get_stacksize() +{ + return (*PyThread_get_stacksize_Ptr)(); +} + +unsigned long Python::PyThread_get_thread_ident() +{ + return (*PyThread_get_thread_ident_Ptr)(); +} + +unsigned long Python::PyThread_get_thread_native_id() +{ + return (*PyThread_get_thread_native_id_Ptr)(); +} + +void Python::PyThread_init_thread() +{ + return (*PyThread_init_thread_Ptr)(); +} + +void Python::PyThread_release_lock(PyThread_type_lock lock) +{ + return (*PyThread_release_lock_Ptr)(lock); +} + +int Python::PyThread_set_key_value(int key, void* value) +{ + return (*PyThread_set_key_value_Ptr)(key, value); +} + +int Python::PyThread_set_stacksize(size_t size) +{ + return (*PyThread_set_stacksize_Ptr)(size); +} + +unsigned long Python::PyThread_start_new_thread(void (*initfunc)(void*) , void* pdata) +{ + return (*PyThread_start_new_thread_Ptr)(initfunc, pdata); +} + +#if HAS_PYTHON_VERSION(0x03070000) +Py_tss_t* Python::PyThread_tss_alloc() +{ + return (*PyThread_tss_alloc_Ptr)(); +} + +int Python::PyThread_tss_create(Py_tss_t* key) +{ + return (*PyThread_tss_create_Ptr)(key); +} + +void Python::PyThread_tss_delete(Py_tss_t* key) +{ + return (*PyThread_tss_delete_Ptr)(key); +} + +void Python::PyThread_tss_free(Py_tss_t* key) +{ + return (*PyThread_tss_free_Ptr)(key); +} + +void* Python::PyThread_tss_get(Py_tss_t* key) +{ + return (*PyThread_tss_get_Ptr)(key); +} + +int Python::PyThread_tss_is_created(Py_tss_t* key) +{ + return (*PyThread_tss_is_created_Ptr)(key); +} + +int Python::PyThread_tss_set(Py_tss_t* key, void* value) +{ + return (*PyThread_tss_set_Ptr)(key, value); +} +#endif + +#if HAS_PYTHON_VERSION(0x03090000) +int Python::PyTraceBack_Here(PyFrameObject* f) +{ + return (*PyTraceBack_Here_Ptr)(f); +} +#endif + +int Python::PyTraceBack_Print(PyObject* v, PyObject* f) +{ + return (*PyTraceBack_Print_Ptr)(v, f); +} + +PyObject* Python::PyTuple_GetItem(PyObject* tuple, Py_ssize_t index) +{ + return (*PyTuple_GetItem_Ptr)(tuple, index); +} + +PyObject* Python::PyTuple_GetSlice(PyObject* tuple, Py_ssize_t low, Py_ssize_t high) +{ + return (*PyTuple_GetSlice_Ptr)(tuple, low, high); +} + +PyObject* Python::PyTuple_New(Py_ssize_t size) +{ + return (*PyTuple_New_Ptr)(size); +} + +int Python::PyTuple_SetItem(PyObject* tuple, Py_ssize_t index, PyObject* item) +{ + return (*PyTuple_SetItem_Ptr)(tuple, index, item); +} + +Py_ssize_t Python::PyTuple_Size(PyObject* tuple) +{ + return (*PyTuple_Size_Ptr)(tuple); +} + +unsigned int Python::PyType_ClearCache() +{ + return (*PyType_ClearCache_Ptr)(); +} + +#if !defined(Py_LIMITED_API) +const char* Python::_PyType_Name(PyTypeObject* type) +{ + return (*PyType_Name_Ptr)(type); +} +#endif + +PyObject* Python::PyType_FromModuleAndSpec(PyObject* module, PyType_Spec* spec, PyObject* bases) +{ + return (*PyType_FromModuleAndSpec_Ptr)(module, spec, bases); +} + +PyObject* Python::PyType_FromSpec(PyType_Spec* spec) +{ + return (*PyType_FromSpec_Ptr)(spec); +} + +PyObject* Python::PyType_FromSpecWithBases(PyType_Spec* spec, PyObject* bases) +{ + return (*PyType_FromSpecWithBases_Ptr)(spec, bases); +} + +PyObject* Python::PyType_GenericAlloc(PyTypeObject* type, Py_ssize_t nitems) +{ + return (*PyType_GenericAlloc_Ptr)(type, nitems); +} + +PyObject* Python::PyType_GenericNew(PyTypeObject* type, PyObject* args, PyObject* kwds) +{ + return (*PyType_GenericNew_Ptr)(type, args, kwds); +} + +unsigned long Python::PyType_GetFlags(PyTypeObject* type) +{ + return (*PyType_GetFlags_Ptr)(type); +} + +PyObject* Python::PyType_GetModule(PyTypeObject* type) +{ + return (*PyType_GetModule_Ptr)(type); +} + +void* Python::PyType_GetModuleState(PyTypeObject* type) +{ + return (*PyType_GetModuleState_Ptr)(type); +} + +PyObject* Python::PyType_GetName(PyTypeObject* type) +{ + return (*PyType_GetName_Ptr)(type); +} + +PyObject* Python::PyType_GetQualName(PyTypeObject* type) +{ + return (*PyType_GetQualName_Ptr)(type); +} + +void* Python::PyType_GetSlot(PyTypeObject* type, int slot) +{ + return (*PyType_GetSlot_Ptr)(type, slot); +} + +int Python::PyType_IsSubtype(PyTypeObject* child, PyTypeObject* parent) +{ + return (*PyType_IsSubtype_Ptr)(child, parent); +} + +void Python::PyType_Modified(PyTypeObject* type) +{ + return (*PyType_Modified_Ptr)(type); +} + +int Python::PyType_Ready(PyTypeObject* type) +{ + return (*PyType_Ready_Ptr)(type); +} + +PyObject* Python::PyUnicodeDecodeError_Create(const char* encoding, const char* object, Py_ssize_t length, Py_ssize_t start, Py_ssize_t end, const char* reason) +{ + return (*PyUnicodeDecodeError_Create_Ptr)(encoding, object, length, start, end, reason); +} + +PyObject* Python::PyUnicodeDecodeError_GetEncoding(PyObject* exc) +{ + return (*PyUnicodeDecodeError_GetEncoding_Ptr)(exc); +} + +int Python::PyUnicodeDecodeError_GetEnd(PyObject* exc, Py_ssize_t* end) +{ + return (*PyUnicodeDecodeError_GetEnd_Ptr)(exc, end); +} + +PyObject* Python::PyUnicodeDecodeError_GetObject(PyObject* exc) +{ + return (*PyUnicodeDecodeError_GetObject_Ptr)(exc); +} + +PyObject* Python::PyUnicodeDecodeError_GetReason(PyObject* exc) +{ + return (*PyUnicodeDecodeError_GetReason_Ptr)(exc); +} + +int Python::PyUnicodeDecodeError_GetStart(PyObject* exc, Py_ssize_t* start) +{ + return (*PyUnicodeDecodeError_GetStart_Ptr)(exc, start); +} + +int Python::PyUnicodeDecodeError_SetEnd(PyObject* exc, Py_ssize_t end) +{ + return (*PyUnicodeDecodeError_SetEnd_Ptr)(exc, end); +} + +int Python::PyUnicodeDecodeError_SetReason(PyObject* exc, const char* reason) +{ + return (*PyUnicodeDecodeError_SetReason_Ptr)(exc, reason); +} + +int Python::PyUnicodeDecodeError_SetStart(PyObject* exc, Py_ssize_t start) +{ + return (*PyUnicodeDecodeError_SetStart_Ptr)(exc, start); +} + +PyObject* Python::PyUnicodeEncodeError_GetEncoding(PyObject* exc) +{ + return (*PyUnicodeEncodeError_GetEncoding_Ptr)(exc); +} + +int Python::PyUnicodeEncodeError_GetEnd(PyObject* exc, Py_ssize_t* end) +{ + return (*PyUnicodeEncodeError_GetEnd_Ptr)(exc, end); +} + +PyObject* Python::PyUnicodeEncodeError_GetObject(PyObject* exc) +{ + return (*PyUnicodeEncodeError_GetObject_Ptr)(exc); +} + +PyObject* Python::PyUnicodeEncodeError_GetReason(PyObject* exc) +{ + return (*PyUnicodeEncodeError_GetReason_Ptr)(exc); +} + +int Python::PyUnicodeEncodeError_GetStart(PyObject* exc, Py_ssize_t* start) +{ + return (*PyUnicodeEncodeError_GetStart_Ptr)(exc, start); +} + +int Python::PyUnicodeEncodeError_SetEnd(PyObject* exc, Py_ssize_t end) +{ + return (*PyUnicodeEncodeError_SetEnd_Ptr)(exc, end); +} + +int Python::PyUnicodeEncodeError_SetReason(PyObject* exc, const char* reason) +{ + return (*PyUnicodeEncodeError_SetReason_Ptr)(exc, reason); +} + +int Python::PyUnicodeEncodeError_SetStart(PyObject* exc, Py_ssize_t start) +{ + return (*PyUnicodeEncodeError_SetStart_Ptr)(exc, start); +} + +int Python::PyUnicodeTranslateError_GetEnd(PyObject* exc, Py_ssize_t* end) +{ + return (*PyUnicodeTranslateError_GetEnd_Ptr)(exc, end); +} + +PyObject* Python::PyUnicodeTranslateError_GetObject(PyObject* exc) +{ + return (*PyUnicodeTranslateError_GetObject_Ptr)(exc); +} + +PyObject* Python::PyUnicodeTranslateError_GetReason(PyObject* exc) +{ + return (*PyUnicodeTranslateError_GetReason_Ptr)(exc); +} + +int Python::PyUnicodeTranslateError_GetStart(PyObject* exc, Py_ssize_t* start) +{ + return (*PyUnicodeTranslateError_GetStart_Ptr)(exc, start); +} + +int Python::PyUnicodeTranslateError_SetEnd(PyObject* exc, Py_ssize_t end) +{ + return (*PyUnicodeTranslateError_SetEnd_Ptr)(exc, end); +} + +int Python::PyUnicodeTranslateError_SetReason(PyObject* exc, const char* reason) +{ + return (*PyUnicodeTranslateError_SetReason_Ptr)(exc, reason); +} + +int Python::PyUnicodeTranslateError_SetStart(PyObject* exc, Py_ssize_t start) +{ + return (*PyUnicodeTranslateError_SetStart_Ptr)(exc, start); +} + +void Python::PyUnicode_Append(PyObject** pleft, PyObject* right) +{ + return (*PyUnicode_Append_Ptr)(pleft, right); +} + +void Python::PyUnicode_AppendAndDel(PyObject** pleft, PyObject* right) +{ + return (*PyUnicode_AppendAndDel_Ptr)(pleft, right); +} + +PyObject* Python::PyUnicode_AsASCIIString(PyObject* unicode) +{ + return (*PyUnicode_AsASCIIString_Ptr)(unicode); +} + +PyObject* Python::PyUnicode_AsCharmapString(PyObject* unicode, PyObject* mapping) +{ + return (*PyUnicode_AsCharmapString_Ptr)(unicode, mapping); +} + +PyObject* Python::PyUnicode_AsDecodedObject(PyObject* unicode, const char* encoding, const char* errors) +{ + return (*PyUnicode_AsDecodedObject_Ptr)(unicode, encoding, errors); +} + +PyObject* Python::PyUnicode_AsDecodedUnicode(PyObject* unicode, const char* encoding, const char* errors) +{ + return (*PyUnicode_AsDecodedUnicode_Ptr)(unicode, encoding, errors); +} + +PyObject* Python::PyUnicode_AsEncodedObject(PyObject* unicode, const char* encoding, const char* errors) +{ + return (*PyUnicode_AsEncodedObject_Ptr)(unicode, encoding, errors); +} + +PyObject* Python::PyUnicode_AsEncodedString(PyObject* unicode, const char* encoding, const char* errors) +{ + return (*PyUnicode_AsEncodedString_Ptr)(unicode, encoding, errors); +} + +PyObject* Python::PyUnicode_AsEncodedUnicode(PyObject* unicode, const char* encoding, const char* errors) +{ + return (*PyUnicode_AsEncodedUnicode_Ptr)(unicode, encoding, errors); +} + +PyObject* Python::PyUnicode_AsLatin1String(PyObject* unicode) +{ + return (*PyUnicode_AsLatin1String_Ptr)(unicode); +} + +PyObject* Python::PyUnicode_AsRawUnicodeEscapeString(PyObject* unicode) +{ + return (*PyUnicode_AsRawUnicodeEscapeString_Ptr)(unicode); +} + +Py_UCS4* Python::PyUnicode_AsUCS4(PyObject* unicode, Py_UCS4* buffer, Py_ssize_t buflen, int copy_null) +{ + return (*PyUnicode_AsUCS4_Ptr)(unicode, buffer, buflen, copy_null); +} + +Py_UCS4* Python::PyUnicode_AsUCS4Copy(PyObject* unicode) +{ + return (*PyUnicode_AsUCS4Copy_Ptr)(unicode); +} + +PyObject* Python::PyUnicode_AsUTF16String(PyObject* unicode) +{ + return (*PyUnicode_AsUTF16String_Ptr)(unicode); +} + +PyObject* Python::PyUnicode_AsUTF32String(PyObject* unicode) +{ + return (*PyUnicode_AsUTF32String_Ptr)(unicode); +} + +const char* Python::PyUnicode_AsUTF8AndSize(PyObject* unicode, Py_ssize_t* size) +{ + return (*PyUnicode_AsUTF8AndSize_Ptr)(unicode, size); +} + +PyObject* Python::PyUnicode_AsUTF8String(PyObject* unicode) +{ + return (*PyUnicode_AsUTF8String_Ptr)(unicode); +} + +PyObject* Python::PyUnicode_AsUnicodeEscapeString(PyObject* unicode) +{ + return (*PyUnicode_AsUnicodeEscapeString_Ptr)(unicode); +} + +Py_ssize_t Python::PyUnicode_AsWideChar(PyObject* unicode, wchar_t* w, Py_ssize_t size) +{ + return (*PyUnicode_AsWideChar_Ptr)(unicode, w, size); +} + +wchar_t* Python::PyUnicode_AsWideCharString(PyObject* unicode, Py_ssize_t* size) +{ + return (*PyUnicode_AsWideCharString_Ptr)(unicode, size); +} + +PyObject* Python::PyUnicode_BuildEncodingMap(PyObject* string) +{ + return (*PyUnicode_BuildEncodingMap_Ptr)(string); +} + +int Python::PyUnicode_Compare(PyObject* left, PyObject* right) +{ + return (*PyUnicode_Compare_Ptr)(left, right); +} + +int Python::PyUnicode_CompareWithASCIIString(PyObject* left, const char* right) +{ + return (*PyUnicode_CompareWithASCIIString_Ptr)(left, right); +} + +PyObject* Python::PyUnicode_Concat(PyObject* left, PyObject* right) +{ + return (*PyUnicode_Concat_Ptr)(left, right); +} + +int Python::PyUnicode_Contains(PyObject* container, PyObject* element) +{ + return (*PyUnicode_Contains_Ptr)(container, element); +} + +Py_ssize_t Python::PyUnicode_Count(PyObject* str, PyObject* substr, Py_ssize_t start, Py_ssize_t end) +{ + return (*PyUnicode_Count_Ptr)(str, substr, start, end); +} + +PyObject* Python::PyUnicode_Decode(const char* s, Py_ssize_t size, const char* encoding, const char* errors) +{ + return (*PyUnicode_Decode_Ptr)(s, size, encoding, errors); +} + +PyObject* Python::PyUnicode_DecodeASCII(const char* string, Py_ssize_t length, const char* errors) +{ + return (*PyUnicode_DecodeASCII_Ptr)(string, length, errors); +} + +PyObject* Python::PyUnicode_DecodeCharmap(const char* string, Py_ssize_t length, PyObject* mapping, const char* errors) +{ + return (*PyUnicode_DecodeCharmap_Ptr)(string, length, mapping, errors); +} + +PyObject* Python::PyUnicode_DecodeFSDefault(const char* s) +{ + return (*PyUnicode_DecodeFSDefault_Ptr)(s); +} + +PyObject* Python::PyUnicode_DecodeFSDefaultAndSize(const char* s, Py_ssize_t size) +{ + return (*PyUnicode_DecodeFSDefaultAndSize_Ptr)(s, size); +} + +PyObject* Python::PyUnicode_DecodeLatin1(const char* string, Py_ssize_t length, const char* errors) +{ + return (*PyUnicode_DecodeLatin1_Ptr)(string, length, errors); +} + +PyObject* Python::PyUnicode_DecodeLocale(const char* str, const char* errors) +{ + return (*PyUnicode_DecodeLocale_Ptr)(str, errors); +} + +PyObject* Python::PyUnicode_DecodeLocaleAndSize(const char* str, Py_ssize_t len, const char* errors) +{ + return (*PyUnicode_DecodeLocaleAndSize_Ptr)(str, len, errors); +} + +PyObject* Python::PyUnicode_DecodeRawUnicodeEscape(const char* string, Py_ssize_t length, const char* errors) +{ + return (*PyUnicode_DecodeRawUnicodeEscape_Ptr)(string, length, errors); +} + +PyObject* Python::PyUnicode_DecodeUTF16(const char* string, Py_ssize_t length, const char* errors, int* byteorder) +{ + return (*PyUnicode_DecodeUTF16_Ptr)(string, length, errors, byteorder); +} + +PyObject* Python::PyUnicode_DecodeUTF16Stateful(const char* string, Py_ssize_t length, const char* errors, int* byteorder, Py_ssize_t* consumed) +{ + return (*PyUnicode_DecodeUTF16Stateful_Ptr)(string, length, errors, byteorder, consumed); +} + +PyObject* Python::PyUnicode_DecodeUTF32(const char* string, Py_ssize_t length, const char* errors, int* byteorder) +{ + return (*PyUnicode_DecodeUTF32_Ptr)(string, length, errors, byteorder); +} + +PyObject* Python::PyUnicode_DecodeUTF32Stateful(const char* string, Py_ssize_t length, const char* errors, int* byteorder, Py_ssize_t* consumed) +{ + return (*PyUnicode_DecodeUTF32Stateful_Ptr)(string, length, errors, byteorder, consumed); +} + +PyObject* Python::PyUnicode_DecodeUTF7(const char* string, Py_ssize_t length, const char* errors) +{ + return (*PyUnicode_DecodeUTF7_Ptr)(string, length, errors); +} + +PyObject* Python::PyUnicode_DecodeUTF7Stateful(const char* string, Py_ssize_t length, const char* errors, Py_ssize_t* consumed) +{ + return (*PyUnicode_DecodeUTF7Stateful_Ptr)(string, length, errors, consumed); +} + +PyObject* Python::PyUnicode_DecodeUTF8(const char* string, Py_ssize_t length, const char* errors) +{ + return (*PyUnicode_DecodeUTF8_Ptr)(string, length, errors); +} + +PyObject* Python::PyUnicode_DecodeUTF8Stateful(const char* string, Py_ssize_t length, const char* errors, Py_ssize_t* consumed) +{ + return (*PyUnicode_DecodeUTF8Stateful_Ptr)(string, length, errors, consumed); +} + +PyObject* Python::PyUnicode_DecodeUnicodeEscape(const char* string, Py_ssize_t length, const char* errors) +{ + return (*PyUnicode_DecodeUnicodeEscape_Ptr)(string, length, errors); +} + +PyObject* Python::PyUnicode_EncodeFSDefault(PyObject* unicode) +{ + return (*PyUnicode_EncodeFSDefault_Ptr)(unicode); +} + +PyObject* Python::PyUnicode_EncodeLocale(PyObject* unicode, const char* errors) +{ + return (*PyUnicode_EncodeLocale_Ptr)(unicode, errors); +} + +int Python::PyUnicode_FSConverter(PyObject* obj, void* result) +{ + return (*PyUnicode_FSConverter_Ptr)(obj, result); +} + +int Python::PyUnicode_FSDecoder(PyObject* obj, void* result) +{ + return (*PyUnicode_FSDecoder_Ptr)(obj, result); +} + +Py_ssize_t Python::PyUnicode_Find(PyObject* str, PyObject* substr, Py_ssize_t start, Py_ssize_t end, int direction) +{ + return (*PyUnicode_Find_Ptr)(str, substr, start, end, direction); +} + +Py_ssize_t Python::PyUnicode_FindChar(PyObject* str, Py_UCS4 ch, Py_ssize_t start, Py_ssize_t end, int direction) +{ + return (*PyUnicode_FindChar_Ptr)(str, ch, start, end, direction); +} + +PyObject* Python::PyUnicode_Format(PyObject* format, PyObject* args) +{ + return (*PyUnicode_Format_Ptr)(format, args); +} + +PyObject* Python::PyUnicode_FromEncodedObject(PyObject* obj, const char* encoding, const char* errors) +{ + return (*PyUnicode_FromEncodedObject_Ptr)(obj, encoding, errors); +} + +PyObject* Python::PyUnicode_FromFormat(const char* format, ...) +{ + va_list arguments; + va_start(arguments, format); + PyObject* result = PyUnicode_FromFormatV(format, arguments); + va_end(arguments); + return result; +} + +PyObject* Python::PyUnicode_FromFormatV(const char* format, va_list vargs) +{ + return (*PyUnicode_FromFormatV_Ptr)(format, vargs); +} + +PyObject* Python::PyUnicode_FromObject(PyObject* obj) +{ + return (*PyUnicode_FromObject_Ptr)(obj); +} + +PyObject* Python::PyUnicode_FromOrdinal(int ordinal) +{ + return (*PyUnicode_FromOrdinal_Ptr)(ordinal); +} + +PyObject* Python::PyUnicode_FromString(const char* u) +{ + return (*PyUnicode_FromString_Ptr)(u); +} + +PyObject* Python::PyUnicode_FromStringAndSize(const char* u, Py_ssize_t size) +{ + return (*PyUnicode_FromStringAndSize_Ptr)(u, size); +} + +PyObject* Python::PyUnicode_FromWideChar(const wchar_t* w, Py_ssize_t size) +{ + return (*PyUnicode_FromWideChar_Ptr)(w, size); +} + +const char* Python::PyUnicode_GetDefaultEncoding() +{ + return (*PyUnicode_GetDefaultEncoding_Ptr)(); +} + +Py_ssize_t Python::PyUnicode_GetLength(PyObject* unicode) +{ + return (*PyUnicode_GetLength_Ptr)(unicode); +} + +Py_ssize_t Python::PyUnicode_GetSize(PyObject* unicode) +{ + return (*PyUnicode_GetSize_Ptr)(unicode); +} + +PyObject* Python::PyUnicode_InternFromString(const char* u) +{ + return (*PyUnicode_InternFromString_Ptr)(u); +} + +void Python::PyUnicode_InternImmortal(PyObject** unicode) +{ + return (*PyUnicode_InternImmortal_Ptr)(unicode); +} + +void Python::PyUnicode_InternInPlace(PyObject** unicode) +{ + return (*PyUnicode_InternInPlace_Ptr)(unicode); +} + +int Python::PyUnicode_IsIdentifier(PyObject* s) +{ + return (*PyUnicode_IsIdentifier_Ptr)(s); +} + +PyObject* Python::PyUnicode_Join(PyObject* separator, PyObject* seq) +{ + return (*PyUnicode_Join_Ptr)(separator, seq); +} + +PyObject* Python::PyUnicode_Partition(PyObject* s, PyObject* sep) +{ + return (*PyUnicode_Partition_Ptr)(s, sep); +} + +PyObject* Python::PyUnicode_RPartition(PyObject* s, PyObject* sep) +{ + return (*PyUnicode_RPartition_Ptr)(s, sep); +} + +PyObject* Python::PyUnicode_RSplit(PyObject* s, PyObject* sep, Py_ssize_t maxsplit) +{ + return (*PyUnicode_RSplit_Ptr)(s, sep, maxsplit); +} + +Py_UCS4 Python::PyUnicode_ReadChar(PyObject* unicode, Py_ssize_t index) +{ + return (*PyUnicode_ReadChar_Ptr)(unicode, index); +} + +PyObject* Python::PyUnicode_Replace(PyObject* str, PyObject* substr, PyObject* replstr, Py_ssize_t maxcount) +{ + return (*PyUnicode_Replace_Ptr)(str, substr, replstr, maxcount); +} + +int Python::PyUnicode_Resize(PyObject** unicode, Py_ssize_t length) +{ + return (*PyUnicode_Resize_Ptr)(unicode, length); +} + +PyObject* Python::PyUnicode_RichCompare(PyObject* left, PyObject* right, int op) +{ + return (*PyUnicode_RichCompare_Ptr)(left, right, op); +} + +PyObject* Python::PyUnicode_Split(PyObject* s, PyObject* sep, Py_ssize_t maxsplit) +{ + return (*PyUnicode_Split_Ptr)(s, sep, maxsplit); +} + +PyObject* Python::PyUnicode_Splitlines(PyObject* s, int keepends) +{ + return (*PyUnicode_Splitlines_Ptr)(s, keepends); +} + +PyObject* Python::PyUnicode_Substring(PyObject* str, Py_ssize_t start, Py_ssize_t end) +{ + return (*PyUnicode_Substring_Ptr)(str, start, end); +} + +Py_ssize_t Python::PyUnicode_Tailmatch(PyObject* str, PyObject* substr, Py_ssize_t start, Py_ssize_t end, int direction) +{ + return (*PyUnicode_Tailmatch_Ptr)(str, substr, start, end, direction); +} + +PyObject* Python::PyUnicode_Translate(PyObject* str, PyObject* table, const char* errors) +{ + return (*PyUnicode_Translate_Ptr)(str, table, errors); +} + +int Python::PyUnicode_WriteChar(PyObject* unicode, Py_ssize_t index, Py_UCS4 character) +{ + return (*PyUnicode_WriteChar_Ptr)(unicode, index, character); +} + +PyObject* Python::PyWeakref_GetObject(PyObject* ref) +{ + return (*PyWeakref_GetObject_Ptr)(ref); +} + +PyObject* Python::PyWeakref_NewProxy(PyObject* ob, PyObject* callback) +{ + return (*PyWeakref_NewProxy_Ptr)(ob, callback); +} + +PyObject* Python::PyWeakref_NewRef(PyObject* ob, PyObject* callback) +{ + return (*PyWeakref_NewRef_Ptr)(ob, callback); +} + +PyObject* Python::PyWrapper_New(PyObject* descr, PyObject* self) +{ + return (*PyWrapper_New_Ptr)(descr, self); +} + +int Python::Py_AddPendingCall(int (*func)(void*), void* arg) +{ + return (*Py_AddPendingCall_Ptr)(func, arg); +} + +int Python::Py_AtExit(void (*func)()) +{ + return (*Py_AtExit_Ptr)(func); +} + +int Python::Py_BytesMain(int argc, char** argv) +{ + return (*Py_BytesMain_Ptr)(argc, argv); +} + +PyObject* (Python::Py_CompileString)(const char* str, const char* filename, int start) +{ + return (*Py_CompileString_Ptr)(str, filename, start); +} + +void Python::Py_DecRef(PyObject* obj) +{ + return (*Py_DecRef_Ptr)(obj); +} + +wchar_t* Python::Py_DecodeLocale(const char* arg, size_t* size) +{ + return (*Py_DecodeLocale_Ptr)(arg, size); +} + +char* Python::Py_EncodeLocale(const wchar_t* text, size_t* error_pos) +{ + return (*Py_EncodeLocale_Ptr)(text, error_pos); +} + +void Python::Py_EndInterpreter(PyThreadState* tstate) +{ + return (*Py_EndInterpreter_Ptr)(tstate); +} + +int (Python::Py_EnterRecursiveCall)(const char* where) +{ + return (*Py_EnterRecursiveCall_Ptr)(where); +} + +void Python::Py_Exit(int exit_code) +{ + return (*Py_Exit_Ptr)(exit_code); +} + +void (Python::Py_FatalError)(const char* message) +{ + return (*Py_FatalError_Ptr)(message); +} + +void Python::Py_Finalize() +{ + return (*Py_Finalize_Ptr)(); +} + +int Python::Py_FinalizeEx() +{ + return (*Py_FinalizeEx_Ptr)(); +} + +PyObject* Python::Py_GenericAlias(PyObject* origin, PyObject* args) +{ + return (*Py_GenericAlias_Ptr)(origin, args); +} + +const char* Python::Py_GetBuildInfo() +{ + return (*Py_GetBuildInfo_Ptr)(); +} + +const char* Python::Py_GetCompiler() +{ + return (*Py_GetCompiler_Ptr)(); +} + +const char* Python::Py_GetCopyright() +{ + return (*Py_GetCopyright_Ptr)(); +} + +wchar_t* Python::Py_GetExecPrefix() +{ + return (*Py_GetExecPrefix_Ptr)(); +} + +wchar_t* Python::Py_GetPath() +{ + return (*Py_GetPath_Ptr)(); +} + +const char* Python::Py_GetPlatform() +{ + return (*Py_GetPlatform_Ptr)(); +} + +wchar_t* Python::Py_GetPrefix() +{ + return (*Py_GetPrefix_Ptr)(); +} + +wchar_t* Python::Py_GetProgramFullPath() +{ + return (*Py_GetProgramFullPath_Ptr)(); +} + +wchar_t* Python::Py_GetProgramName() +{ + return (*Py_GetProgramName_Ptr)(); +} + +wchar_t* Python::Py_GetPythonHome() +{ + return (*Py_GetPythonHome_Ptr)(); +} + +int Python::Py_GetRecursionLimit() +{ + return (*Py_GetRecursionLimit_Ptr)(); +} + +const char* Python::Py_GetVersion() +{ + return (*Py_GetVersion_Ptr)(); +} + +void Python::Py_IncRef(PyObject* obj) +{ + return (*Py_IncRef_Ptr)(obj); +} + +void Python::Py_Initialize() +{ + return (*Py_Initialize_Ptr)(); +} + +void Python::Py_InitializeEx(int initsigs) +{ + return (*Py_InitializeEx_Ptr)(initsigs); +} + +int (Python::Py_Is)(PyObject* x, PyObject* y) +{ + return x == y; //(*Py_Is_Ptr)(x, y); +} + +int (Python::Py_IsFalse)(PyObject* x) +{ + return (Py_Is)((x), static_cast(Py_FalseStruct_Ptr)); +} + +int Python::Py_IsInitialized() +{ + return (*Py_IsInitialized_Ptr)(); +} + +int (Python::Py_IsNone)(PyObject* x) +{ + return (Py_Is)((x), static_cast(Py_NoneStruct_Ptr)); +} + +int (Python::Py_IsTrue)(PyObject* x) +{ + return (Py_Is)((x), static_cast(Py_TrueStruct_Ptr)); +} + +int (Python::Py_IS_TYPE)(PyObject* o, PyTypeObject* type) +{ + return (Py_TYPE)(o) == type; //(*Py_IS_TYPE_Ptr)(o, type); +} + +PyTypeObject* (Python::Py_TYPE)(PyObject *o) +{ + return o->ob_type; //(*Py_TYPE_Ptr)(o); +} + +void (Python::Py_LeaveRecursiveCall)() +{ + return (*Py_LeaveRecursiveCall_Ptr)(); +} + +int Python::Py_Main(int argc, wchar_t** argv) +{ + return (*Py_Main_Ptr)(argc, argv); +} + +int Python::Py_MakePendingCalls() +{ + return (*Py_MakePendingCalls_Ptr)(); +} + +PyThreadState* Python::Py_NewInterpreter() +{ + return (*Py_NewInterpreter_Ptr)(); +} + +#ifdef Py_NewRef +PyObject* (Python::Py_NewRef)(PyObject* obj) +{ + return (*Py_NewRef_Ptr)(obj); +} +#endif + +int Python::Py_ReprEnter(PyObject* object) +{ + return (*Py_ReprEnter_Ptr)(object); +} + +void Python::Py_ReprLeave(PyObject* object) +{ + return (*Py_ReprLeave_Ptr)(object); +} + +void Python::Py_SetPath(const wchar_t* path) +{ + return (*Py_SetPath_Ptr)(path); +} + +void Python::Py_SetProgramName(const wchar_t* name) +{ + return (*Py_SetProgramName_Ptr)(name); +} + +void Python::Py_SetPythonHome(const wchar_t* home) +{ + return (*Py_SetPythonHome_Ptr)(home); +} + +void Python::Py_SetRecursionLimit(int limit) +{ + return (*Py_SetRecursionLimit_Ptr)(limit); +} + +PyObject* Python::Py_VaBuildValue(const char* format, va_list vargs) +{ + return (*Py_VaBuildValue_Ptr)(format, vargs); +} + +#if HAS_PYTHON_VERSION(0x030A0000) +PyObject* (Python::Py_XNewRef)(PyObject* obj) +{ + return (*Py_XNewRef_Ptr)(obj); +} +#endif + +#endif \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Plugin/Python/Python.hxx b/third-party/RemoteInput/RemoteInput/Plugin/Python/Python.hxx new file mode 100644 index 0000000..dd05cfa --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Plugin/Python/Python.hxx @@ -0,0 +1,1631 @@ +// +// Created by Brandon on 2023-02-16. +// + +#ifndef REMOTEINPUT_PYTHON_HXX +#define REMOTEINPUT_PYTHON_HXX + +#if !defined(USE_PYBIND11) +#include "PythonMacros.hxx" + +class Python +{ +private: + #if defined(_WIN32) || defined(_WIN64) + HMODULE module; + #else + void* module; + #endif // defined + + void steal(Python& a, Python& b); + + void* Py_NoneStruct_Ptr; + void* Py_TrueStruct_Ptr; + void* Py_FalseStruct_Ptr; + void* Py_ListType_Ptr; + + int (*PyAIter_Check_Ptr)(PyObject*); + int (*PyArg_Parse_Ptr)(PyObject*, const char*, ...); + int (*PyArg_ParseTuple_Ptr)(PyObject*, const char*, ...); + int (*PyArg_ParseTupleAndKeywords_Ptr)(PyObject*, PyObject*, const char*, char**, ...); + int (*PyArg_UnpackTuple_Ptr)(PyObject*, const char*, Py_ssize_t, Py_ssize_t, ...); + int (*PyArg_VaParse_Ptr)(PyObject*, const char*, va_list); + int (*PyArg_VaParseTupleAndKeywords_Ptr)(PyObject*, PyObject*, const char*, char**, va_list); + int (*PyArg_ValidateKeywordArguments_Ptr)(PyObject*); + PyObject* (*PyBool_FromLong_Ptr)(long); + + #if HAS_PYTHON_VERSION(0x030B1000) + void (*PyBuffer_FillContiguousStrides_Ptr)(int, Py_ssize_t*, Py_ssize_t*, int, char); + int (*PyBuffer_FillInfo_Ptr)(Py_buffer*, PyObject*, void*, Py_ssize_t, int, int); + int (*PyBuffer_FromContiguous_Ptr)(const Py_buffer*, const void*, Py_ssize_t, char); + void* (*PyBuffer_GetPointer_Ptr)(const Py_buffer*, const Py_ssize_t*); + int (*PyBuffer_IsContiguous_Ptr)(const Py_buffer*, char); + void (*PyBuffer_Release_Ptr)(Py_buffer*); + Py_ssize_t (*PyBuffer_SizeFromFormat_Ptr)(const char*); + int (*PyBuffer_ToContiguous_Ptr)(void*, const Py_buffer*, Py_ssize_t, char); + #endif + + char* (*PyByteArray_AsString_Ptr)(PyObject*); + PyObject* (*PyByteArray_Concat_Ptr)(PyObject*, PyObject*); + PyObject* (*PyByteArray_FromObject_Ptr)(PyObject*); + PyObject* (*PyByteArray_FromStringAndSize_Ptr)(const char*, Py_ssize_t); + int (*PyByteArray_Resize_Ptr)(PyObject*, Py_ssize_t); + Py_ssize_t (*PyByteArray_Size_Ptr)(PyObject*); + char* (*PyBytes_AsString_Ptr)(PyObject*); + int (*PyBytes_AsStringAndSize_Ptr)(PyObject*, char**, Py_ssize_t*); + void (*PyBytes_Concat_Ptr)(PyObject**, PyObject*); + void (*PyBytes_ConcatAndDel_Ptr)(PyObject**, PyObject*); + PyObject* (*PyBytes_DecodeEscape_Ptr)(const char*, Py_ssize_t, const char*, Py_ssize_t, const char*); + PyObject* (*PyBytes_FromFormat_Ptr)(const char*, ...); + PyObject* (*PyBytes_FromFormatV_Ptr)(const char*, va_list); + PyObject* (*PyBytes_FromObject_Ptr)(PyObject*); + PyObject* (*PyBytes_FromString_Ptr)(const char*); + PyObject* (*PyBytes_FromStringAndSize_Ptr)(const char*, Py_ssize_t); + PyObject* (*PyBytes_Repr_Ptr)(PyObject*, int); + Py_ssize_t (*PyBytes_Size_Ptr)(PyObject*); + PyObject* (*PyCFunction_Call_Ptr)(PyObject*, PyObject*, PyObject*); + int (*PyCFunction_GetFlags_Ptr)(PyObject*); + PyCFunction (*PyCFunction_GetFunction_Ptr)(PyObject*); + PyObject* (*PyCFunction_GetSelf_Ptr)(PyObject*); + + PyObject* (*PyCFunction_New_Ptr)(PyMethodDef*, PyObject*); + + #if HAS_PYTHON_VERSION(0x03090000) + PyObject* (*PyCFunction_NewEx_Ptr)(PyMethodDef*, PyObject*, PyObject*); + #endif + + PyObject* (*PyCMethod_New_Ptr)(PyMethodDef*, PyObject*, PyObject*, PyTypeObject*); + PyObject* (*PyCallIter_New_Ptr)(PyObject*, PyObject*); + int (*PyCallable_Check_Ptr)(PyObject*); + void* (*PyCapsule_GetContext_Ptr)(PyObject*); + PyCapsule_Destructor (*PyCapsule_GetDestructor_Ptr)(PyObject*); + const char* (*PyCapsule_GetName_Ptr)(PyObject*); + void* (*PyCapsule_GetPointer_Ptr)(PyObject*, const char*); + void* (*PyCapsule_Import_Ptr)(const char*, int); + int (*PyCapsule_IsValid_Ptr)(PyObject*, const char*); + PyObject* (*PyCapsule_New_Ptr)(void*, const char*, PyCapsule_Destructor); + int (*PyCapsule_SetContext_Ptr)(PyObject*, void*); + int (*PyCapsule_SetDestructor_Ptr)(PyObject*, PyCapsule_Destructor); + int (*PyCapsule_SetName_Ptr)(PyObject*, const char*); + int (*PyCapsule_SetPointer_Ptr)(PyObject*, void*); + PyObject* (*PyComplex_FromDoubles_Ptr)(double, double); + double (*PyComplex_ImagAsDouble_Ptr)(PyObject*); + double (*PyComplex_RealAsDouble_Ptr)(PyObject*); + PyObject* (*PyDescr_NewClassMethod_Ptr)(PyTypeObject*, PyMethodDef*); + PyObject* (*PyDescr_NewGetSet_Ptr)(PyTypeObject*, PyGetSetDef*); + PyObject* (*PyDescr_NewMember_Ptr)(PyTypeObject*, PyMemberDef*); + PyObject* (*PyDescr_NewMethod_Ptr)(PyTypeObject*, PyMethodDef*); + PyObject* (*PyDictProxy_New_Ptr)(PyObject*); + void (*PyDict_Clear_Ptr)(PyObject*); + int (*PyDict_Contains_Ptr)(PyObject*, PyObject*); + PyObject* (*PyDict_Copy_Ptr)(PyObject*); + int (*PyDict_DelItem_Ptr)(PyObject*, PyObject*); + int (*PyDict_DelItemString_Ptr)(PyObject*, const char*); + PyObject* (*PyDict_GetItem_Ptr)(PyObject*, PyObject*); + PyObject* (*PyDict_GetItemString_Ptr)(PyObject*, const char*); + PyObject* (*PyDict_GetItemWithError_Ptr)(PyObject*, PyObject*); + PyObject* (*PyDict_Items_Ptr)(PyObject*); + PyObject* (*PyDict_Keys_Ptr)(PyObject*); + int (*PyDict_Merge_Ptr)(PyObject*, PyObject*, int); + int (*PyDict_MergeFromSeq2_Ptr)(PyObject*, PyObject*, int); + PyObject* (*PyDict_New_Ptr)(); + int (*PyDict_Next_Ptr)(PyObject*, Py_ssize_t*, PyObject**, PyObject**); + int (*PyDict_SetItem_Ptr)(PyObject*, PyObject*, PyObject*); + int (*PyDict_SetItemString_Ptr)(PyObject*, const char*, PyObject*); + Py_ssize_t (*PyDict_Size_Ptr)(PyObject*); + int (*PyDict_Update_Ptr)(PyObject*, PyObject*); + PyObject* (*PyDict_Values_Ptr)(PyObject*); + int (*PyErr_BadArgument_Ptr)(); + + #ifdef PyErr_BadInternalCall + void (*_PyErr_BadInternalCall_Ptr)(const char*, int); + #endif + + int (*PyErr_CheckSignals_Ptr)(); + void (*PyErr_Clear_Ptr)(); + void (*PyErr_Display_Ptr)(PyObject*, PyObject*, PyObject*); + int (*PyErr_ExceptionMatches_Ptr)(PyObject*); + void (*PyErr_Fetch_Ptr)(PyObject**, PyObject**, PyObject**); + PyObject* (*PyErr_Format_Ptr)(PyObject*, const char*, ...); + PyObject* (*PyErr_FormatV_Ptr)(PyObject*, const char*, va_list); + void (*PyErr_GetExcInfo_Ptr)(PyObject**, PyObject**, PyObject**); + PyObject* (*PyErr_GetHandledException_Ptr)(); + int (*PyErr_GivenExceptionMatches_Ptr)(PyObject*, PyObject*); + PyObject* (*PyErr_NewException_Ptr)(const char*, PyObject*, PyObject*); + PyObject* (*PyErr_NewExceptionWithDoc_Ptr)(const char*, const char*, PyObject*, PyObject*); + PyObject* (*PyErr_NoMemory_Ptr)(); + void (*PyErr_NormalizeException_Ptr)(PyObject**, PyObject**, PyObject**); + PyObject* (*PyErr_Occurred_Ptr)(); + void (*PyErr_Print_Ptr)(); + void (*PyErr_PrintEx_Ptr)(int); + PyObject* (*PyErr_ProgramText_Ptr)(const char*, int); + int (*PyErr_ResourceWarning_Ptr)(PyObject*, Py_ssize_t, const char*, ...); + void (*PyErr_Restore_Ptr)(PyObject*, PyObject*, PyObject*); + void (*PyErr_SetExcInfo_Ptr)(PyObject*, PyObject*, PyObject*); + PyObject* (*PyErr_SetFromErrno_Ptr)(PyObject*); + PyObject* (*PyErr_SetFromErrnoWithFilename_Ptr)(PyObject*, const char*); + PyObject* (*PyErr_SetFromErrnoWithFilenameObject_Ptr)(PyObject*, PyObject*); + PyObject* (*PyErr_SetFromErrnoWithFilenameObjects_Ptr)(PyObject*, PyObject*, PyObject*); + void (*PyErr_SetHandledException_Ptr)(PyObject*); + PyObject* (*PyErr_SetImportError_Ptr)(PyObject*, PyObject*, PyObject*); + PyObject* (*PyErr_SetImportErrorSubclass_Ptr)(PyObject*, PyObject*, PyObject*, PyObject*); + void (*PyErr_SetInterrupt_Ptr)(); + int (*PyErr_SetInterruptEx_Ptr)(int); + void (*PyErr_SetNone_Ptr)(PyObject*); + void (*PyErr_SetObject_Ptr)(PyObject*, PyObject*); + void (*PyErr_SetString_Ptr)(PyObject*, const char*); + void (*PyErr_SyntaxLocation_Ptr)(const char*, int); + void (*PyErr_SyntaxLocationEx_Ptr)(const char*, int, int); + int (*PyErr_WarnEx_Ptr)(PyObject*, const char*, Py_ssize_t); + int (*PyErr_WarnExplicit_Ptr)(PyObject*, const char*, const char*, int, const char*, PyObject*); + int (*PyErr_WarnFormat_Ptr)(PyObject*, Py_ssize_t, const char*, ...); + void (*PyErr_WriteUnraisable_Ptr)(PyObject*); + void (*PyEval_AcquireLock_Ptr)(); + void (*PyEval_AcquireThread_Ptr)(PyThreadState*); + PyObject* (*PyEval_CallFunction_Ptr)(PyObject*, const char*, ...); + PyObject* (*PyEval_CallMethod_Ptr)(PyObject*, const char*, const char*, ...); + PyObject* (*PyEval_CallObjectWithKeywords_Ptr)(PyObject*, PyObject*, PyObject*); + PyObject* (*PyEval_EvalCode_Ptr)(PyObject*, PyObject*, PyObject*); + PyObject* (*PyEval_EvalCodeEx_Ptr)(PyObject*, PyObject*, PyObject*, PyObject*const*, int, PyObject*const*, int, PyObject*const*, int, PyObject*, PyObject*); + #if HAS_PYTHON_VERSION(0x03090000) + PyObject* (*PyEval_EvalFrame_Ptr)(PyFrameObject*); + PyObject* (*PyEval_EvalFrameEx_Ptr)(PyFrameObject*, int); + #endif + PyObject* (*PyEval_GetBuiltins_Ptr)(); + #if HAS_PYTHON_VERSION(0x03090000) + PyFrameObject* (*PyEval_GetFrame_Ptr)(); + #endif + const char* (*PyEval_GetFuncDesc_Ptr)(PyObject*); + const char* (*PyEval_GetFuncName_Ptr)(PyObject*); + PyObject* (*PyEval_GetGlobals_Ptr)(); + PyObject* (*PyEval_GetLocals_Ptr)(); + void (*PyEval_InitThreads_Ptr)(); + void (*PyEval_ReleaseLock_Ptr)(); + void (*PyEval_ReleaseThread_Ptr)(PyThreadState*); + void (*PyEval_RestoreThread_Ptr)(PyThreadState*); + PyThreadState* (*PyEval_SaveThread_Ptr)(); + int (*PyEval_ThreadsInitialized_Ptr)(); + const char* (*PyExceptionClass_Name_Ptr)(PyObject*); + PyObject* (*PyException_GetCause_Ptr)(PyObject*); + PyObject* (*PyException_GetContext_Ptr)(PyObject*); + PyObject* (*PyException_GetTraceback_Ptr)(PyObject*); + void (*PyException_SetCause_Ptr)(PyObject*, PyObject*); + void (*PyException_SetContext_Ptr)(PyObject*, PyObject*); + int (*PyException_SetTraceback_Ptr)(PyObject*, PyObject*); + PyObject* (*PyFile_FromFd_Ptr)(int, const char*, const char*, int, const char*, const char*, const char*, int); + PyObject* (*PyFile_GetLine_Ptr)(PyObject*, int); + int (*PyFile_WriteObject_Ptr)(PyObject*, PyObject*, int); + int (*PyFile_WriteString_Ptr)(const char*, PyObject*); + double (*PyFloat_AsDouble_Ptr)(PyObject*); + PyObject* (*PyFloat_FromDouble_Ptr)(double); + PyObject* (*PyFloat_FromString_Ptr)(PyObject*); + PyObject* (*PyFloat_GetInfo_Ptr)(); + double (*PyFloat_GetMax_Ptr)(); + double (*PyFloat_GetMin_Ptr)(); + PyObject* (*PyFrozenSet_New_Ptr)(PyObject*); + Py_ssize_t (*PyGC_Collect_Ptr)(); + int (*PyGC_Disable_Ptr)(); + int (*PyGC_Enable_Ptr)(); + int (*PyGC_IsEnabled_Ptr)(); + PyGILState_STATE (*PyGILState_Ensure_Ptr)(); + PyThreadState* (*PyGILState_GetThisThreadState_Ptr)(); + void (*PyGILState_Release_Ptr)(PyGILState_STATE); + PyObject* (*PyImport_AddModule_Ptr)(const char*); + PyObject* (*PyImport_AddModuleObject_Ptr)(PyObject*); + int (*PyImport_AppendInittab_Ptr)(const char*, PyObject*(*)()); + PyObject* (*PyImport_ExecCodeModule_Ptr)(const char*, PyObject*); + PyObject* (*PyImport_ExecCodeModuleEx_Ptr)(const char*, PyObject*, const char*); + PyObject* (*PyImport_ExecCodeModuleObject_Ptr)(PyObject*, PyObject*, PyObject*, PyObject*); + PyObject* (*PyImport_ExecCodeModuleWithPathnames_Ptr)(const char*, PyObject*, const char*, const char*); + PyObject* (*PyImport_GetImporter_Ptr)(PyObject*); + long (*PyImport_GetMagicNumber_Ptr)(); + const char* (*PyImport_GetMagicTag_Ptr)(); + PyObject* (*PyImport_GetModule_Ptr)(PyObject*); + PyObject* (*PyImport_GetModuleDict_Ptr)(); + PyObject* (*PyImport_Import_Ptr)(PyObject*); + int (*PyImport_ImportFrozenModule_Ptr)(const char*); + int (*PyImport_ImportFrozenModuleObject_Ptr)(PyObject*); + PyObject* (*PyImport_ImportModule_Ptr)(const char*); + PyObject* (*PyImport_ImportModuleLevel_Ptr)(const char*, PyObject*, PyObject*, PyObject*, int); + PyObject* (*PyImport_ImportModuleLevelObject_Ptr)(PyObject*, PyObject*, PyObject*, PyObject*, int); + PyObject* (*PyImport_ImportModuleNoBlock_Ptr)(const char*); + PyObject* (*PyImport_ReloadModule_Ptr)(PyObject*); + int (*PyIndex_Check_Ptr)(PyObject*); + void (*PyInterpreterState_Clear_Ptr)(PyInterpreterState*); + void (*PyInterpreterState_Delete_Ptr)(PyInterpreterState*); + PyInterpreterState* (*PyInterpreterState_Get_Ptr)(); + PyObject* (*PyInterpreterState_GetDict_Ptr)(PyInterpreterState*); + int64_t (*PyInterpreterState_GetID_Ptr)(PyInterpreterState*); + PyInterpreterState* (*PyInterpreterState_New_Ptr)(); + int (*PyIter_Check_Ptr)(PyObject*); + PyObject* (*PyIter_Next_Ptr)(PyObject*); + #if HAS_PYTHON_VERSION(0x030A0000) + PySendResult (*PyIter_Send_Ptr)(PyObject*, PyObject*, PyObject**); + #endif + int (*PyList_Append_Ptr)(PyObject*, PyObject*); + PyObject* (*PyList_AsTuple_Ptr)(PyObject*); + PyObject* (*PyList_GetItem_Ptr)(PyObject*, Py_ssize_t); + PyObject* (*PyList_GetSlice_Ptr)(PyObject*, Py_ssize_t, Py_ssize_t); + int (*PyList_Insert_Ptr)(PyObject*, Py_ssize_t, PyObject*); + PyObject* (*PyList_New_Ptr)(Py_ssize_t); + int (*PyList_Reverse_Ptr)(PyObject*); + int (*PyList_SetItem_Ptr)(PyObject*, Py_ssize_t, PyObject*); + int (*PyList_SetSlice_Ptr)(PyObject*, Py_ssize_t, Py_ssize_t, PyObject*); + Py_ssize_t (*PyList_Size_Ptr)(PyObject*); + int (*PyList_Sort_Ptr)(PyObject*); + double (*PyLong_AsDouble_Ptr)(PyObject*); + long (*PyLong_AsLong_Ptr)(PyObject*); + long (*PyLong_AsLongAndOverflow_Ptr)(PyObject*, int*); + long long (*PyLong_AsLongLong_Ptr)(PyObject*); + long long (*PyLong_AsLongLongAndOverflow_Ptr)(PyObject*, int*); + size_t (*PyLong_AsSize_t_Ptr)(PyObject*); + Py_ssize_t (*PyLong_AsSsize_t_Ptr)(PyObject*); + unsigned long (*PyLong_AsUnsignedLong_Ptr)(PyObject*); + unsigned long long (*PyLong_AsUnsignedLongLong_Ptr)(PyObject*); + unsigned long long (*PyLong_AsUnsignedLongLongMask_Ptr)(PyObject*); + unsigned long (*PyLong_AsUnsignedLongMask_Ptr)(PyObject*); + void* (*PyLong_AsVoidPtr_Ptr)(PyObject*); + PyObject* (*PyLong_FromDouble_Ptr)(double); + PyObject* (*PyLong_FromLong_Ptr)(long); + PyObject* (*PyLong_FromLongLong_Ptr)(long long); + PyObject* (*PyLong_FromSize_t_Ptr)(size_t); + PyObject* (*PyLong_FromSsize_t_Ptr)(Py_ssize_t); + PyObject* (*PyLong_FromString_Ptr)(const char*, char**, int); + PyObject* (*PyLong_FromUnsignedLong_Ptr)(unsigned long); + PyObject* (*PyLong_FromUnsignedLongLong_Ptr)(unsigned long long); + PyObject* (*PyLong_FromVoidPtr_Ptr)(void*); + PyObject* (*PyLong_GetInfo_Ptr)(); + int (*PyMapping_Check_Ptr)(PyObject*); + PyObject* (*PyMapping_GetItemString_Ptr)(PyObject*, const char*); + int (*PyMapping_HasKey_Ptr)(PyObject*, PyObject*); + int (*PyMapping_HasKeyString_Ptr)(PyObject*, const char*); + PyObject* (*PyMapping_Items_Ptr)(PyObject*); + PyObject* (*PyMapping_Keys_Ptr)(PyObject*); + Py_ssize_t (*PyMapping_Length_Ptr)(PyObject*); + int (*PyMapping_SetItemString_Ptr)(PyObject*, const char*, PyObject*); + Py_ssize_t (*PyMapping_Size_Ptr)(PyObject*); + PyObject* (*PyMapping_Values_Ptr)(PyObject*); + void* (*PyMem_Calloc_Ptr)(size_t, size_t); + void (*PyMem_Free_Ptr)(void*); + void* (*PyMem_Malloc_Ptr)(size_t); + void* (*PyMem_Realloc_Ptr)(void*, size_t); + #if HAS_PYTHON_VERSION(0x030B1000) + PyObject* (*PyMemoryView_FromBuffer_Ptr)(const Py_buffer*); + #endif + PyObject* (*PyMemoryView_FromMemory_Ptr)(char*, Py_ssize_t, int); + PyObject* (*PyMemoryView_FromObject_Ptr)(PyObject*); + PyObject* (*PyMemoryView_GetContiguous_Ptr)(PyObject*, int, char); + PyObject* (*PyModuleDef_Init_Ptr)(PyModuleDef*); + int (*PyModule_AddFunctions_Ptr)(PyObject*, PyMethodDef*); + int (*PyModule_AddIntConstant_Ptr)(PyObject*, const char*, long); + int (*PyModule_AddObject_Ptr)(PyObject*, const char*, PyObject*); + int (*PyModule_AddObjectRef_Ptr)(PyObject*, const char*, PyObject*); + int (*PyModule_AddStringConstant_Ptr)(PyObject*, const char*, const char*); + int (*PyModule_AddType_Ptr)(PyObject*, PyTypeObject*); + PyObject* (*PyModule_Create2_Ptr)(PyModuleDef*, int); + int (*PyModule_ExecDef_Ptr)(PyObject*, PyModuleDef*); + PyObject* (*PyModule_FromDefAndSpec2_Ptr)(PyModuleDef*, PyObject*, int); + PyModuleDef* (*PyModule_GetDef_Ptr)(PyObject*); + PyObject* (*PyModule_GetDict_Ptr)(PyObject*); + const char* (*PyModule_GetFilename_Ptr)(PyObject*); + PyObject* (*PyModule_GetFilenameObject_Ptr)(PyObject*); + const char* (*PyModule_GetName_Ptr)(PyObject*); + PyObject* (*PyModule_GetNameObject_Ptr)(PyObject*); + void* (*PyModule_GetState_Ptr)(PyObject*); + PyObject* (*PyModule_New_Ptr)(const char*); + PyObject* (*PyModule_NewObject_Ptr)(PyObject*); + int (*PyModule_SetDocString_Ptr)(PyObject*, const char*); + PyObject* (*PyNumber_Absolute_Ptr)(PyObject*); + PyObject* (*PyNumber_Add_Ptr)(PyObject*, PyObject*); + PyObject* (*PyNumber_And_Ptr)(PyObject*, PyObject*); + Py_ssize_t (*PyNumber_AsSsize_t_Ptr)(PyObject*, PyObject*); + int (*PyNumber_Check_Ptr)(PyObject*); + PyObject* (*PyNumber_Divmod_Ptr)(PyObject*, PyObject*); + PyObject* (*PyNumber_Float_Ptr)(PyObject*); + PyObject* (*PyNumber_FloorDivide_Ptr)(PyObject*, PyObject*); + PyObject* (*PyNumber_InPlaceAdd_Ptr)(PyObject*, PyObject*); + PyObject* (*PyNumber_InPlaceAnd_Ptr)(PyObject*, PyObject*); + PyObject* (*PyNumber_InPlaceFloorDivide_Ptr)(PyObject*, PyObject*); + PyObject* (*PyNumber_InPlaceLshift_Ptr)(PyObject*, PyObject*); + PyObject* (*PyNumber_InPlaceMatrixMultiply_Ptr)(PyObject*, PyObject*); + PyObject* (*PyNumber_InPlaceMultiply_Ptr)(PyObject*, PyObject*); + PyObject* (*PyNumber_InPlaceOr_Ptr)(PyObject*, PyObject*); + PyObject* (*PyNumber_InPlacePower_Ptr)(PyObject*, PyObject*, PyObject*); + PyObject* (*PyNumber_InPlaceRemainder_Ptr)(PyObject*, PyObject*); + PyObject* (*PyNumber_InPlaceRshift_Ptr)(PyObject*, PyObject*); + PyObject* (*PyNumber_InPlaceSubtract_Ptr)(PyObject*, PyObject*); + PyObject* (*PyNumber_InPlaceTrueDivide_Ptr)(PyObject*, PyObject*); + PyObject* (*PyNumber_InPlaceXor_Ptr)(PyObject*, PyObject*); + PyObject* (*PyNumber_Index_Ptr)(PyObject*); + PyObject* (*PyNumber_Invert_Ptr)(PyObject*); + PyObject* (*PyNumber_Long_Ptr)(PyObject*); + PyObject* (*PyNumber_Lshift_Ptr)(PyObject*, PyObject*); + PyObject* (*PyNumber_MatrixMultiply_Ptr)(PyObject*, PyObject*); + PyObject* (*PyNumber_Multiply_Ptr)(PyObject*, PyObject*); + PyObject* (*PyNumber_Negative_Ptr)(PyObject*); + PyObject* (*PyNumber_Or_Ptr)(PyObject*, PyObject*); + PyObject* (*PyNumber_Positive_Ptr)(PyObject*); + PyObject* (*PyNumber_Power_Ptr)(PyObject*, PyObject*, PyObject*); + PyObject* (*PyNumber_Remainder_Ptr)(PyObject*, PyObject*); + PyObject* (*PyNumber_Rshift_Ptr)(PyObject*, PyObject*); + PyObject* (*PyNumber_Subtract_Ptr)(PyObject*, PyObject*); + PyObject* (*PyNumber_ToBase_Ptr)(PyObject*, int); + PyObject* (*PyNumber_TrueDivide_Ptr)(PyObject*, PyObject*); + PyObject* (*PyNumber_Xor_Ptr)(PyObject*, PyObject*); + void (*PyOS_AfterFork_Ptr)(); + void (*PyOS_AfterFork_Child_Ptr)(); + void (*PyOS_AfterFork_Parent_Ptr)(); + void (*PyOS_BeforeFork_Ptr)(); + PyObject* (*PyOS_FSPath_Ptr)(PyObject*); + int (*PyOS_InterruptOccurred_Ptr)(); + char* (*PyOS_double_to_string_Ptr)(double, char, int, int, int*); + PyOS_sighandler_t (*PyOS_getsig_Ptr)(int); + int (*PyOS_mystricmp_Ptr)(const char*, const char*); + int (*PyOS_mystrnicmp_Ptr)(const char*, const char*, Py_ssize_t); + PyOS_sighandler_t (*PyOS_setsig_Ptr)(int, PyOS_sighandler_t); + int (*PyOS_snprintf_Ptr)(char*, size_t, const char*, ...); + double (*PyOS_string_to_double_Ptr)(const char*, char**, PyObject*); + long (*PyOS_strtol_Ptr)(const char*, char**, int); + unsigned long (*PyOS_strtoul_Ptr)(const char*, char**, int); + int (*PyOS_vsnprintf_Ptr)(char*, size_t, const char*, va_list); + PyObject* (*PyObject_ASCII_Ptr)(PyObject*); + int (*PyObject_AsCharBuffer_Ptr)(PyObject*, const char**, Py_ssize_t*); + int (*PyObject_AsFileDescriptor_Ptr)(PyObject*); + int (*PyObject_AsReadBuffer_Ptr)(PyObject*, const void**, Py_ssize_t*); + int (*PyObject_AsWriteBuffer_Ptr)(PyObject*, void**, Py_ssize_t*); + PyObject* (*PyObject_Bytes_Ptr)(PyObject*); + PyObject* (*PyObject_Call_Ptr)(PyObject*, PyObject*, PyObject*); + PyObject* (*PyObject_CallFunction_Ptr)(PyObject*, const char*, ...); + PyObject* (*PyObject_CallFunctionObjArgs_Ptr)(PyObject*, ...); + PyObject* (*PyObject_CallMethod_Ptr)(PyObject*, const char*, const char*, ...); + PyObject* (*PyObject_CallMethodObjArgs_Ptr)(PyObject*, PyObject*, ...); + PyObject* (*PyObject_CallNoArgs_Ptr)(PyObject*); + PyObject* (*PyObject_CallObject_Ptr)(PyObject*, PyObject*); + void* (*PyObject_Calloc_Ptr)(size_t, size_t); + int (*PyObject_CheckBuffer_Ptr)(PyObject*); + int (*PyObject_CheckReadBuffer_Ptr)(PyObject*); + void (*PyObject_ClearWeakRefs_Ptr)(PyObject*); + int (*PyObject_CopyData_Ptr)(PyObject*, PyObject*); + int (*PyObject_DelItem_Ptr)(PyObject*, PyObject*); + int (*PyObject_DelItemString_Ptr)(PyObject*, const char*); + PyObject* (*PyObject_Dir_Ptr)(PyObject*); + PyObject* (*PyObject_Format_Ptr)(PyObject*, PyObject*); + void (*PyObject_Free_Ptr)(void*); + void (*PyObject_GC_Del_Ptr)(void*); + int (*PyObject_GC_IsFinalized_Ptr)(PyObject*); + int (*PyObject_GC_IsTracked_Ptr)(PyObject*); + void (*PyObject_GC_Track_Ptr)(void*); + void (*PyObject_GC_UnTrack_Ptr)(void*); + PyObject* (*PyObject_GenericGetAttr_Ptr)(PyObject*, PyObject*); + PyObject* (*PyObject_GenericGetDict_Ptr)(PyObject*, void*); + int (*PyObject_GenericSetAttr_Ptr)(PyObject*, PyObject*, PyObject*); + int (*PyObject_GenericSetDict_Ptr)(PyObject*, PyObject*, void*); + PyObject* (*PyObject_GetAIter_Ptr)(PyObject*); + PyObject* (*PyObject_GetAttr_Ptr)(PyObject*, PyObject*); + PyObject* (*PyObject_GetAttrString_Ptr)(PyObject*, const char*); + #if HAS_PYTHON_VERSION(0x030B1000) + int (*PyObject_GetBuffer_Ptr)(PyObject*, Py_buffer*, int); + #endif + PyObject* (*PyObject_GetItem_Ptr)(PyObject*, PyObject*); + PyObject* (*PyObject_GetIter_Ptr)(PyObject*); + int (*PyObject_HasAttr_Ptr)(PyObject*, PyObject*); + int (*PyObject_HasAttrString_Ptr)(PyObject*, const char*); + Py_hash_t (*PyObject_Hash_Ptr)(PyObject*); + Py_hash_t (*PyObject_HashNotImplemented_Ptr)(PyObject*); + PyObject* (*PyObject_Init_Ptr)(PyObject*, PyTypeObject*); + PyVarObject* (*PyObject_InitVar_Ptr)(PyVarObject*, PyTypeObject*, Py_ssize_t); + int (*PyObject_IsInstance_Ptr)(PyObject*, PyObject*); + int (*PyObject_IsSubclass_Ptr)(PyObject*, PyObject*); + int (*PyObject_IsTrue_Ptr)(PyObject*); + Py_ssize_t (*PyObject_Length_Ptr)(PyObject*); + void* (*PyObject_Malloc_Ptr)(size_t); + int (*PyObject_Not_Ptr)(PyObject*); + void* (*PyObject_Realloc_Ptr)(void*, size_t); + PyObject* (*PyObject_Repr_Ptr)(PyObject*); + PyObject* (*PyObject_RichCompare_Ptr)(PyObject*, PyObject*, int); + int (*PyObject_RichCompareBool_Ptr)(PyObject*, PyObject*, int); + PyObject* (*PyObject_SelfIter_Ptr)(PyObject*); + int (*PyObject_SetAttr_Ptr)(PyObject*, PyObject*, PyObject*); + int (*PyObject_SetAttrString_Ptr)(PyObject*, const char*, PyObject*); + int (*PyObject_SetItem_Ptr)(PyObject*, PyObject*, PyObject*); + Py_ssize_t (*PyObject_Size_Ptr)(PyObject*); + PyObject* (*PyObject_Str_Ptr)(PyObject*); + PyObject* (*PyObject_Type_Ptr)(PyObject*); + #if HAS_PYTHON_VERSION(0x030B0000) + int (*PyObject_TypeCheck_Ptr)(PyObject*, PyTypeObject*); + #endif + PyObject* (*PySeqIter_New_Ptr)(PyObject*); + int (*PySequence_Check_Ptr)(PyObject*); + PyObject* (*PySequence_Concat_Ptr)(PyObject*, PyObject*); + int (*PySequence_Contains_Ptr)(PyObject*, PyObject*); + Py_ssize_t (*PySequence_Count_Ptr)(PyObject*, PyObject*); + int (*PySequence_DelItem_Ptr)(PyObject*, Py_ssize_t); + int (*PySequence_DelSlice_Ptr)(PyObject*, Py_ssize_t, Py_ssize_t); + PyObject* (*PySequence_Fast_Ptr)(PyObject*, const char*); + PyObject* (*PySequence_GetItem_Ptr)(PyObject*, Py_ssize_t); + PyObject* (*PySequence_GetSlice_Ptr)(PyObject*, Py_ssize_t, Py_ssize_t); + int (*PySequence_In_Ptr)(PyObject*, PyObject*); + PyObject* (*PySequence_InPlaceConcat_Ptr)(PyObject*, PyObject*); + PyObject* (*PySequence_InPlaceRepeat_Ptr)(PyObject*, Py_ssize_t); + Py_ssize_t (*PySequence_Index_Ptr)(PyObject*, PyObject*); + Py_ssize_t (*PySequence_Length_Ptr)(PyObject*); + PyObject* (*PySequence_List_Ptr)(PyObject*); + PyObject* (*PySequence_Repeat_Ptr)(PyObject*, Py_ssize_t); + int (*PySequence_SetItem_Ptr)(PyObject*, Py_ssize_t, PyObject*); + int (*PySequence_SetSlice_Ptr)(PyObject*, Py_ssize_t, Py_ssize_t, PyObject*); + Py_ssize_t (*PySequence_Size_Ptr)(PyObject*); + PyObject* (*PySequence_Tuple_Ptr)(PyObject*); + int (*PySet_Add_Ptr)(PyObject*, PyObject*); + int (*PySet_Clear_Ptr)(PyObject*); + int (*PySet_Contains_Ptr)(PyObject*, PyObject*); + int (*PySet_Discard_Ptr)(PyObject*, PyObject*); + PyObject* (*PySet_New_Ptr)(PyObject*); + PyObject* (*PySet_Pop_Ptr)(PyObject*); + Py_ssize_t (*PySet_Size_Ptr)(PyObject*); + Py_ssize_t (*PySlice_AdjustIndices_Ptr)(Py_ssize_t, Py_ssize_t*, Py_ssize_t*, Py_ssize_t); + int (*PySlice_GetIndices_Ptr)(PyObject*, Py_ssize_t, Py_ssize_t*, Py_ssize_t*, Py_ssize_t*); + int (*PySlice_GetIndicesEx_Ptr)(PyObject*, Py_ssize_t, Py_ssize_t*, Py_ssize_t*, Py_ssize_t*, Py_ssize_t*); + PyObject* (*PySlice_New_Ptr)(PyObject*, PyObject*, PyObject*); + int (*PySlice_Unpack_Ptr)(PyObject*, Py_ssize_t*, Py_ssize_t*, Py_ssize_t*); + int (*PyState_AddModule_Ptr)(PyObject*, PyModuleDef*); + PyObject* (*PyState_FindModule_Ptr)(PyModuleDef*); + int (*PyState_RemoveModule_Ptr)(PyModuleDef*); + PyObject* (*PyStructSequence_GetItem_Ptr)(PyObject*, Py_ssize_t); + PyObject* (*PyStructSequence_New_Ptr)(PyTypeObject*); + PyTypeObject* (*PyStructSequence_NewType_Ptr)(PyStructSequence_Desc*); + void (*PyStructSequence_SetItem_Ptr)(PyObject*, Py_ssize_t, PyObject*); + void (*PySys_AddWarnOption_Ptr)(const wchar_t*); + void (*PySys_AddWarnOptionUnicode_Ptr)(PyObject*); + void (*PySys_AddXOption_Ptr)(const wchar_t*); + void (*PySys_FormatStderr_Ptr)(const char*, ...); + void (*PySys_FormatStdout_Ptr)(const char*, ...); + PyObject* (*PySys_GetObject_Ptr)(const char*); + PyObject* (*PySys_GetXOptions_Ptr)(); + int (*PySys_HasWarnOptions_Ptr)(); + void (*PySys_ResetWarnOptions_Ptr)(); + void (*PySys_SetArgv_Ptr)(int, wchar_t**); + void (*PySys_SetArgvEx_Ptr)(int, wchar_t**, int); + int (*PySys_SetObject_Ptr)(const char*, PyObject*); + void (*PySys_SetPath_Ptr)(const wchar_t*); + void (*PySys_WriteStderr_Ptr)(const char*, ...); + void (*PySys_WriteStdout_Ptr)(const char*, ...); + void (*PyThreadState_Clear_Ptr)(PyThreadState*); + void (*PyThreadState_Delete_Ptr)(PyThreadState*); + PyThreadState* (*PyThreadState_Get_Ptr)(); + PyObject* (*PyThreadState_GetDict_Ptr)(); + #if HAS_PYTHON_VERSION(0x03090000) + PyFrameObject* (*PyThreadState_GetFrame_Ptr)(PyThreadState*); + #endif + uint64_t (*PyThreadState_GetID_Ptr)(PyThreadState*); + PyInterpreterState* (*PyThreadState_GetInterpreter_Ptr)(PyThreadState*); + PyThreadState* (*PyThreadState_New_Ptr)(PyInterpreterState*); + int (*PyThreadState_SetAsyncExc_Ptr)(unsigned long, PyObject*); + PyThreadState* (*PyThreadState_Swap_Ptr)(PyThreadState*); + PyObject* (*PyThread_GetInfo_Ptr)(); + void (*PyThread_ReInitTLS_Ptr)(); + int (*PyThread_acquire_lock_Ptr)(PyThread_type_lock, int); + PyLockStatus (*PyThread_acquire_lock_timed_Ptr)(PyThread_type_lock, long long, int); + PyThread_type_lock (*PyThread_allocate_lock_Ptr)(); + int (*PyThread_create_key_Ptr)(); + void (*PyThread_delete_key_Ptr)(int); + void (*PyThread_delete_key_value_Ptr)(int); + void (*PyThread_exit_thread_Ptr)(); + void (*PyThread_free_lock_Ptr)(PyThread_type_lock); + void* (*PyThread_get_key_value_Ptr)(int); + size_t (*PyThread_get_stacksize_Ptr)(); + unsigned long (*PyThread_get_thread_ident_Ptr)(); + unsigned long (*PyThread_get_thread_native_id_Ptr)(); + void (*PyThread_init_thread_Ptr)(); + void (*PyThread_release_lock_Ptr)(PyThread_type_lock); + int (*PyThread_set_key_value_Ptr)(int, void*); + int (*PyThread_set_stacksize_Ptr)(size_t); + unsigned long (*PyThread_start_new_thread_Ptr)(void (*)(void*), void*); + #if HAS_PYTHON_VERSION(0x03070000) + Py_tss_t* (*PyThread_tss_alloc_Ptr)(); + int (*PyThread_tss_create_Ptr)(Py_tss_t*); + void (*PyThread_tss_delete_Ptr)(Py_tss_t*); + void (*PyThread_tss_free_Ptr)(Py_tss_t*); + void* (*PyThread_tss_get_Ptr)(Py_tss_t*); + int (*PyThread_tss_is_created_Ptr)(Py_tss_t*); + int (*PyThread_tss_set_Ptr)(Py_tss_t*, void*); + #endif + #if HAS_PYTHON_VERSION(0x03090000) + int (*PyTraceBack_Here_Ptr)(PyFrameObject*); + #endif + int (*PyTraceBack_Print_Ptr)(PyObject*, PyObject*); + PyObject* (*PyTuple_GetItem_Ptr)(PyObject*, Py_ssize_t); + PyObject* (*PyTuple_GetSlice_Ptr)(PyObject*, Py_ssize_t, Py_ssize_t); + PyObject* (*PyTuple_New_Ptr)(Py_ssize_t); + PyObject* (*PyTuple_Pack_Ptr)(Py_ssize_t, ...); + int (*PyTuple_SetItem_Ptr)(PyObject*, Py_ssize_t, PyObject*); + Py_ssize_t (*PyTuple_Size_Ptr)(PyObject*); + unsigned int (*PyType_ClearCache_Ptr)(); + #if !defined(Py_LIMITED_API) + const char* (*PyType_Name_Ptr)(PyTypeObject*); + #endif + PyObject* (*PyType_FromModuleAndSpec_Ptr)(PyObject*, PyType_Spec*, PyObject*); + PyObject* (*PyType_FromSpec_Ptr)(PyType_Spec*); + PyObject* (*PyType_FromSpecWithBases_Ptr)(PyType_Spec*, PyObject*); + PyObject* (*PyType_GenericAlloc_Ptr)(PyTypeObject*, Py_ssize_t); + PyObject* (*PyType_GenericNew_Ptr)(PyTypeObject*, PyObject*, PyObject*); + unsigned long (*PyType_GetFlags_Ptr)(PyTypeObject*); + PyObject* (*PyType_GetModule_Ptr)(PyTypeObject*); + void* (*PyType_GetModuleState_Ptr)(PyTypeObject*); + PyObject* (*PyType_GetName_Ptr)(PyTypeObject*); + PyObject* (*PyType_GetQualName_Ptr)(PyTypeObject*); + void* (*PyType_GetSlot_Ptr)(PyTypeObject*, int); + int (*PyType_IsSubtype_Ptr)(PyTypeObject*, PyTypeObject*); + void (*PyType_Modified_Ptr)(PyTypeObject*); + int (*PyType_Ready_Ptr)(PyTypeObject*); + PyObject* (*PyUnicodeDecodeError_Create_Ptr)(const char*, const char*, Py_ssize_t, Py_ssize_t, Py_ssize_t, const char*); + PyObject* (*PyUnicodeDecodeError_GetEncoding_Ptr)(PyObject*); + int (*PyUnicodeDecodeError_GetEnd_Ptr)(PyObject*, Py_ssize_t*); + PyObject* (*PyUnicodeDecodeError_GetObject_Ptr)(PyObject*); + PyObject* (*PyUnicodeDecodeError_GetReason_Ptr)(PyObject*); + int (*PyUnicodeDecodeError_GetStart_Ptr)(PyObject*, Py_ssize_t*); + int (*PyUnicodeDecodeError_SetEnd_Ptr)(PyObject*, Py_ssize_t); + int (*PyUnicodeDecodeError_SetReason_Ptr)(PyObject*, const char*); + int (*PyUnicodeDecodeError_SetStart_Ptr)(PyObject*, Py_ssize_t); + PyObject* (*PyUnicodeEncodeError_GetEncoding_Ptr)(PyObject*); + int (*PyUnicodeEncodeError_GetEnd_Ptr)(PyObject*, Py_ssize_t*); + PyObject* (*PyUnicodeEncodeError_GetObject_Ptr)(PyObject*); + PyObject* (*PyUnicodeEncodeError_GetReason_Ptr)(PyObject*); + int (*PyUnicodeEncodeError_GetStart_Ptr)(PyObject*, Py_ssize_t*); + int (*PyUnicodeEncodeError_SetEnd_Ptr)(PyObject*, Py_ssize_t); + int (*PyUnicodeEncodeError_SetReason_Ptr)(PyObject*, const char*); + int (*PyUnicodeEncodeError_SetStart_Ptr)(PyObject*, Py_ssize_t); + int (*PyUnicodeTranslateError_GetEnd_Ptr)(PyObject*, Py_ssize_t*); + PyObject* (*PyUnicodeTranslateError_GetObject_Ptr)(PyObject*); + PyObject* (*PyUnicodeTranslateError_GetReason_Ptr)(PyObject*); + int (*PyUnicodeTranslateError_GetStart_Ptr)(PyObject*, Py_ssize_t*); + int (*PyUnicodeTranslateError_SetEnd_Ptr)(PyObject*, Py_ssize_t); + int (*PyUnicodeTranslateError_SetReason_Ptr)(PyObject*, const char*); + int (*PyUnicodeTranslateError_SetStart_Ptr)(PyObject*, Py_ssize_t); + void (*PyUnicode_Append_Ptr)(PyObject**, PyObject*); + void (*PyUnicode_AppendAndDel_Ptr)(PyObject**, PyObject*); + PyObject* (*PyUnicode_AsASCIIString_Ptr)(PyObject*); + PyObject* (*PyUnicode_AsCharmapString_Ptr)(PyObject*, PyObject*); + PyObject* (*PyUnicode_AsDecodedObject_Ptr)(PyObject*, const char*, const char*); + PyObject* (*PyUnicode_AsDecodedUnicode_Ptr)(PyObject*, const char*, const char*); + PyObject* (*PyUnicode_AsEncodedObject_Ptr)(PyObject*, const char*, const char*); + PyObject* (*PyUnicode_AsEncodedString_Ptr)(PyObject*, const char*, const char*); + PyObject* (*PyUnicode_AsEncodedUnicode_Ptr)(PyObject*, const char*, const char*); + PyObject* (*PyUnicode_AsLatin1String_Ptr)(PyObject*); + PyObject* (*PyUnicode_AsRawUnicodeEscapeString_Ptr)(PyObject*); + Py_UCS4* (*PyUnicode_AsUCS4_Ptr)(PyObject*, Py_UCS4*, Py_ssize_t, int); + Py_UCS4* (*PyUnicode_AsUCS4Copy_Ptr)(PyObject*); + PyObject* (*PyUnicode_AsUTF16String_Ptr)(PyObject*); + PyObject* (*PyUnicode_AsUTF32String_Ptr)(PyObject*); + const char* (*PyUnicode_AsUTF8AndSize_Ptr)(PyObject*, Py_ssize_t*); + PyObject* (*PyUnicode_AsUTF8String_Ptr)(PyObject*); + PyObject* (*PyUnicode_AsUnicodeEscapeString_Ptr)(PyObject*); + Py_ssize_t (*PyUnicode_AsWideChar_Ptr)(PyObject*, wchar_t*, Py_ssize_t); + wchar_t* (*PyUnicode_AsWideCharString_Ptr)(PyObject*, Py_ssize_t*); + PyObject* (*PyUnicode_BuildEncodingMap_Ptr)(PyObject*); + int (*PyUnicode_Compare_Ptr)(PyObject*, PyObject*); + int (*PyUnicode_CompareWithASCIIString_Ptr)(PyObject*, const char*); + PyObject* (*PyUnicode_Concat_Ptr)(PyObject*, PyObject*); + int (*PyUnicode_Contains_Ptr)(PyObject*, PyObject*); + Py_ssize_t (*PyUnicode_Count_Ptr)(PyObject*, PyObject*, Py_ssize_t, Py_ssize_t); + PyObject* (*PyUnicode_Decode_Ptr)(const char*, Py_ssize_t, const char*, const char*); + PyObject* (*PyUnicode_DecodeASCII_Ptr)(const char*, Py_ssize_t, const char*); + PyObject* (*PyUnicode_DecodeCharmap_Ptr)(const char*, Py_ssize_t, PyObject*, const char*); + PyObject* (*PyUnicode_DecodeFSDefault_Ptr)(const char*); + PyObject* (*PyUnicode_DecodeFSDefaultAndSize_Ptr)(const char*, Py_ssize_t); + PyObject* (*PyUnicode_DecodeLatin1_Ptr)(const char*, Py_ssize_t, const char*); + PyObject* (*PyUnicode_DecodeLocale_Ptr)(const char*, const char*); + PyObject* (*PyUnicode_DecodeLocaleAndSize_Ptr)(const char*, Py_ssize_t, const char*); + PyObject* (*PyUnicode_DecodeRawUnicodeEscape_Ptr)(const char*, Py_ssize_t, const char*); + PyObject* (*PyUnicode_DecodeUTF16_Ptr)(const char*, Py_ssize_t, const char*, int*); + PyObject* (*PyUnicode_DecodeUTF16Stateful_Ptr)(const char*, Py_ssize_t, const char*, int*, Py_ssize_t*); + PyObject* (*PyUnicode_DecodeUTF32_Ptr)(const char*, Py_ssize_t, const char*, int*); + PyObject* (*PyUnicode_DecodeUTF32Stateful_Ptr)(const char*, Py_ssize_t, const char*, int*, Py_ssize_t*); + PyObject* (*PyUnicode_DecodeUTF7_Ptr)(const char*, Py_ssize_t, const char*); + PyObject* (*PyUnicode_DecodeUTF7Stateful_Ptr)(const char*, Py_ssize_t, const char*, Py_ssize_t*); + PyObject* (*PyUnicode_DecodeUTF8_Ptr)(const char*, Py_ssize_t, const char*); + PyObject* (*PyUnicode_DecodeUTF8Stateful_Ptr)(const char*, Py_ssize_t, const char*, Py_ssize_t*); + PyObject* (*PyUnicode_DecodeUnicodeEscape_Ptr)(const char*, Py_ssize_t, const char*); + PyObject* (*PyUnicode_EncodeFSDefault_Ptr)(PyObject*); + PyObject* (*PyUnicode_EncodeLocale_Ptr)(PyObject*, const char*); + int (*PyUnicode_FSConverter_Ptr)(PyObject*, void*); + int (*PyUnicode_FSDecoder_Ptr)(PyObject*, void*); + Py_ssize_t (*PyUnicode_Find_Ptr)(PyObject*, PyObject*, Py_ssize_t, Py_ssize_t, int); + Py_ssize_t (*PyUnicode_FindChar_Ptr)(PyObject*, Py_UCS4, Py_ssize_t, Py_ssize_t, int); + PyObject* (*PyUnicode_Format_Ptr)(PyObject*, PyObject*); + PyObject* (*PyUnicode_FromEncodedObject_Ptr)(PyObject*, const char*, const char*); + PyObject* (*PyUnicode_FromFormat_Ptr)(const char*, ...); + PyObject* (*PyUnicode_FromFormatV_Ptr)(const char*, va_list); + PyObject* (*PyUnicode_FromObject_Ptr)(PyObject*); + PyObject* (*PyUnicode_FromOrdinal_Ptr)(int); + PyObject* (*PyUnicode_FromString_Ptr)(const char*); + PyObject* (*PyUnicode_FromStringAndSize_Ptr)(const char*, Py_ssize_t); + PyObject* (*PyUnicode_FromWideChar_Ptr)(const wchar_t*, Py_ssize_t); + const char* (*PyUnicode_GetDefaultEncoding_Ptr)(); + Py_ssize_t (*PyUnicode_GetLength_Ptr)(PyObject*); + Py_ssize_t (*PyUnicode_GetSize_Ptr)(PyObject*); + PyObject* (*PyUnicode_InternFromString_Ptr)(const char*); + void (*PyUnicode_InternImmortal_Ptr)(PyObject**); + void (*PyUnicode_InternInPlace_Ptr)(PyObject**); + int (*PyUnicode_IsIdentifier_Ptr)(PyObject*); + PyObject* (*PyUnicode_Join_Ptr)(PyObject*, PyObject*); + PyObject* (*PyUnicode_Partition_Ptr)(PyObject*, PyObject*); + PyObject* (*PyUnicode_RPartition_Ptr)(PyObject*, PyObject*); + PyObject* (*PyUnicode_RSplit_Ptr)(PyObject*, PyObject*, Py_ssize_t); + Py_UCS4 (*PyUnicode_ReadChar_Ptr)(PyObject*, Py_ssize_t); + PyObject* (*PyUnicode_Replace_Ptr)(PyObject*, PyObject*, PyObject*, Py_ssize_t); + int (*PyUnicode_Resize_Ptr)(PyObject**, Py_ssize_t); + PyObject* (*PyUnicode_RichCompare_Ptr)(PyObject*, PyObject*, int); + PyObject* (*PyUnicode_Split_Ptr)(PyObject*, PyObject*, Py_ssize_t); + PyObject* (*PyUnicode_Splitlines_Ptr)(PyObject*, int); + PyObject* (*PyUnicode_Substring_Ptr)(PyObject*, Py_ssize_t, Py_ssize_t); + Py_ssize_t (*PyUnicode_Tailmatch_Ptr)(PyObject*, PyObject*, Py_ssize_t, Py_ssize_t, int); + PyObject* (*PyUnicode_Translate_Ptr)(PyObject*, PyObject*, const char*); + int (*PyUnicode_WriteChar_Ptr)(PyObject*, Py_ssize_t, Py_UCS4); + PyObject* (*PyWeakref_GetObject_Ptr)(PyObject*); + PyObject* (*PyWeakref_NewProxy_Ptr)(PyObject*, PyObject*); + PyObject* (*PyWeakref_NewRef_Ptr)(PyObject*, PyObject*); + PyObject* (*PyWrapper_New_Ptr)(PyObject*, PyObject*); + int (*Py_AddPendingCall_Ptr)(int (*)(void*), void*); + int (*Py_AtExit_Ptr)(void (*)()); + PyObject* (*Py_BuildValue_Ptr)(const char*, ...); + int (*Py_BytesMain_Ptr)(int, char**); + PyObject* (*Py_CompileString_Ptr)(const char*, const char*, int); + void (*Py_DecRef_Ptr)(PyObject*); + wchar_t* (*Py_DecodeLocale_Ptr)(const char*, size_t*); + char* (*Py_EncodeLocale_Ptr)(const wchar_t*, size_t*); + void (*Py_EndInterpreter_Ptr)(PyThreadState*); + int (*Py_EnterRecursiveCall_Ptr)(const char*); + void (*Py_Exit_Ptr)(int); + void (*Py_FatalError_Ptr)(const char*); + void (*Py_Finalize_Ptr)(); + int (*Py_FinalizeEx_Ptr)(); + PyObject* (*Py_GenericAlias_Ptr)(PyObject*, PyObject*); + const char* (*Py_GetBuildInfo_Ptr)(); + const char* (*Py_GetCompiler_Ptr)(); + const char* (*Py_GetCopyright_Ptr)(); + wchar_t* (*Py_GetExecPrefix_Ptr)(); + wchar_t* (*Py_GetPath_Ptr)(); + const char* (*Py_GetPlatform_Ptr)(); + wchar_t* (*Py_GetPrefix_Ptr)(); + wchar_t* (*Py_GetProgramFullPath_Ptr)(); + wchar_t* (*Py_GetProgramName_Ptr)(); + wchar_t* (*Py_GetPythonHome_Ptr)(); + int (*Py_GetRecursionLimit_Ptr)(); + const char* (*Py_GetVersion_Ptr)(); + void (*Py_IncRef_Ptr)(PyObject*); + void (*Py_Initialize_Ptr)(); + void (*Py_InitializeEx_Ptr)(int); + int (*Py_Is_Ptr)(PyObject*, PyObject*); + int (*Py_IsInitialized_Ptr)(); + void (*Py_LeaveRecursiveCall_Ptr)(); + int (*Py_Main_Ptr)(int, wchar_t**); + int (*Py_MakePendingCalls_Ptr)(); + PyThreadState* (*Py_NewInterpreter_Ptr)(); + PyObject* (*Py_NewRef_Ptr)(PyObject*); + int (*Py_ReprEnter_Ptr)(PyObject*); + void (*Py_ReprLeave_Ptr)(PyObject*); + void (*Py_SetPath_Ptr)(const wchar_t*); + void (*Py_SetProgramName_Ptr)(const wchar_t*); + void (*Py_SetPythonHome_Ptr)(const wchar_t*); + void (*Py_SetRecursionLimit_Ptr)(int); + PyObject* (*Py_VaBuildValue_Ptr)(const char*, va_list); + + #if HAS_PYTHON_VERSION(0x030A0000) + PyObject* (*Py_XNewRef_Ptr)(PyObject*); + #endif + +public: + Python(); + + Python(const Python& other) = delete; + Python(Python &&other); + + Python& operator = (const Python& other) = delete; + Python& operator = (Python&& other); + + bool IsPythonLoaded() const; + + int PyAIter_Check(PyObject* o); + + template + int PyArg_Parse(PyObject* args, const char* format, Args... arguments) + { + return (*PyArg_Parse_Ptr)(args, format, arguments...); + } + + int PyArg_ParseTuple(PyObject* args, const char* format, ...); + int PyArg_ParseTupleAndKeywords(PyObject* args, PyObject* kw, const char* format, char* keywords[], ...); + + template + int PyArg_UnpackTuple(PyObject* args, const char* name, Py_ssize_t min, Py_ssize_t max, Args... arguments) + { + return (*PyArg_UnpackTuple_Ptr)(args, name, min, max, arguments...); + } + + int PyArg_VaParse(PyObject* args, const char* format, va_list vargs); + int PyArg_VaParseTupleAndKeywords(PyObject* args, PyObject* kw, const char* format, char* keywords[], va_list vargs); + int PyArg_ValidateKeywordArguments(PyObject* keywords); + PyObject* PyBool_FromLong(long v); + + #if HAS_PYTHON_VERSION(0x030B1000) + void PyBuffer_FillContiguousStrides(int ndims, Py_ssize_t* shape, Py_ssize_t* strides, int itemsize, char fort); + int PyBuffer_FillInfo(Py_buffer* view, PyObject* o, void* buf, Py_ssize_t len, int readonly, int flags); + int PyBuffer_FromContiguous(const Py_buffer* view, const void* buf, Py_ssize_t len, char order); + void* PyBuffer_GetPointer(const Py_buffer* view, const Py_ssize_t* indices); + int PyBuffer_IsContiguous(const Py_buffer* view, char fort); + void PyBuffer_Release(Py_buffer* view); + Py_ssize_t PyBuffer_SizeFromFormat(const char* format); + int PyBuffer_ToContiguous(void* buf, const Py_buffer* view, Py_ssize_t len, char order); + #endif + + char* PyByteArray_AsString(PyObject* bytearray); + PyObject* PyByteArray_Concat(PyObject* a, PyObject* b); + PyObject* PyByteArray_FromObject(PyObject* o); + PyObject* PyByteArray_FromStringAndSize(const char* string, Py_ssize_t len); + int PyByteArray_Resize(PyObject* bytearray, Py_ssize_t len); + Py_ssize_t PyByteArray_Size(PyObject* bytearray); + char* PyBytes_AsString(PyObject* bytearray); + int PyBytes_AsStringAndSize(PyObject* obj, char** s, Py_ssize_t* len); + void PyBytes_Concat(PyObject** bytes, PyObject* newpart); + void PyBytes_ConcatAndDel(PyObject** bytes, PyObject* newpart); + PyObject* PyBytes_DecodeEscape(const char* arg1, Py_ssize_t arg2, const char* arg3, Py_ssize_t arg4, const char* arg5); + PyObject* PyBytes_FromFormat(const char *format, ...); + PyObject* PyBytes_FromFormatV(const char* format, va_list vargs); + PyObject* PyBytes_FromObject(PyObject* o); + PyObject* PyBytes_FromString(const char* string); + PyObject* PyBytes_FromStringAndSize(const char* string, Py_ssize_t len); + PyObject* PyBytes_Repr(PyObject* o, int smartquotes); + Py_ssize_t PyBytes_Size(PyObject* o); + PyObject* PyCFunction_Call(PyObject* func, PyObject* arg, PyObject* kw); + int PyCFunction_GetFlags(PyObject* op); + PyCFunction PyCFunction_GetFunction(PyObject* op); + PyObject* PyCFunction_GetSelf(PyObject* op); + PyObject* (PyCFunction_New)(PyMethodDef* ml, PyObject* self); + + #if HAS_PYTHON_VERSION(0x03050000) + PyObject* (PyCFunction_NewEx)(PyMethodDef* ml, PyObject* self, PyObject* module); + #endif + + PyObject* PyCMethod_New(PyMethodDef* ml, PyObject* self, PyObject* module, PyTypeObject* type); + PyObject* PyCallIter_New(PyObject* callable, PyObject* sentinel); + int PyCallable_Check(PyObject* o); + void* PyCapsule_GetContext(PyObject* capsule); + PyCapsule_Destructor PyCapsule_GetDestructor(PyObject* capsule); + const char* PyCapsule_GetName(PyObject* capsule); + void* PyCapsule_GetPointer(PyObject* capsule, const char* name); + void* PyCapsule_Import(const char* name, int no_block); + int PyCapsule_IsValid(PyObject* capsule, const char* name); + PyObject* PyCapsule_New(void* pointer, const char* name, PyCapsule_Destructor destructor); + int PyCapsule_SetContext(PyObject* capsule, void* context); + int PyCapsule_SetDestructor(PyObject* capsule, PyCapsule_Destructor destructor); + int PyCapsule_SetName(PyObject* capsule, const char* name); + int PyCapsule_SetPointer(PyObject* capsule, void* pointer); + PyObject* PyComplex_FromDoubles(double real, double imag); + double PyComplex_ImagAsDouble(PyObject* op); + double PyComplex_RealAsDouble(PyObject* op); + PyObject* PyDescr_NewClassMethod(PyTypeObject* type, PyMethodDef* method); + PyObject* PyDescr_NewGetSet(PyTypeObject* type, PyGetSetDef* getset); + PyObject* PyDescr_NewMember(PyTypeObject* type, PyMemberDef* meth); + PyObject* PyDescr_NewMethod(PyTypeObject* type, PyMethodDef* meth); + PyObject* PyDictProxy_New(PyObject* mapping); + void PyDict_Clear(PyObject* mp); + int PyDict_Contains(PyObject* mp, PyObject* key); + PyObject* PyDict_Copy(PyObject* mp); + int PyDict_DelItem(PyObject* mp, PyObject* key); + int PyDict_DelItemString(PyObject* dp, const char* key); + PyObject* PyDict_GetItem(PyObject* mp, PyObject* key); + PyObject* PyDict_GetItemString(PyObject* dp, const char* key); + PyObject* PyDict_GetItemWithError(PyObject* mp, PyObject* key); + PyObject* PyDict_Items(PyObject* mp); + PyObject* PyDict_Keys(PyObject* mp); + int PyDict_Merge(PyObject* mp, PyObject* other, int override); + int PyDict_MergeFromSeq2(PyObject* d, PyObject* seq2, int override); + PyObject* PyDict_New(); + int PyDict_Next(PyObject* mp, Py_ssize_t* pos, PyObject** key, PyObject** value); + int PyDict_SetItem(PyObject* mp, PyObject* key, PyObject* item); + int PyDict_SetItemString(PyObject* dp, const char* key, PyObject* item); + Py_ssize_t PyDict_Size(PyObject* mp); + int PyDict_Update(PyObject* mp, PyObject* other); + PyObject* PyDict_Values(PyObject* mp); + int PyErr_BadArgument(); + + #ifdef PyErr_BadInternalCall + void (PyErr_BadInternalCall)(); + #endif + + int PyErr_CheckSignals(); + void PyErr_Clear(); + void PyErr_Display(PyObject* exc, PyObject* val, PyObject* tb); + int PyErr_ExceptionMatches(PyObject* exc); + void PyErr_Fetch(PyObject** ptype, PyObject** pvalue, PyObject** ptraceback); + PyObject* PyErr_Format(PyObject* exception, const char* format, ...); + PyObject* PyErr_FormatV(PyObject* exception, const char* format, va_list vargs); + void PyErr_GetExcInfo(PyObject** ptype, PyObject** pvalue, PyObject** ptraceback); + PyObject* PyErr_GetHandledException(); + int PyErr_GivenExceptionMatches(PyObject* given, PyObject* exc); + PyObject* PyErr_NewException(const char* name, PyObject* base, PyObject* dict); + PyObject* PyErr_NewExceptionWithDoc(const char* name, const char* doc, PyObject* base, PyObject* dict); + PyObject* PyErr_NoMemory(); + void PyErr_NormalizeException(PyObject** exc, PyObject** val, PyObject** tb); + PyObject* PyErr_Occurred(); + void PyErr_Print(); + void PyErr_PrintEx(int set_sys_last_vars); + PyObject* PyErr_ProgramText(const char* filename, int lineno); + + template + int PyErr_ResourceWarning(PyObject* source, Py_ssize_t stack_level, const char* format, Args... args) + { + return (*PyErr_ResourceWarning_Ptr)(source, stack_level, format, args...); + } + + void PyErr_Restore(PyObject* type, PyObject* value, PyObject* traceback); + void PyErr_SetExcInfo(PyObject* type, PyObject* value, PyObject* traceback); + PyObject* PyErr_SetFromErrno(PyObject* type); + PyObject* PyErr_SetFromErrnoWithFilename(PyObject* type, const char* filename); + PyObject* PyErr_SetFromErrnoWithFilenameObject(PyObject* type, PyObject* filenameObject); + PyObject* PyErr_SetFromErrnoWithFilenameObjects(PyObject* type, PyObject* filenameObject, PyObject* filenameObject2); + PyObject* PyErr_SetImportError(PyObject *msg, PyObject *name, PyObject *path); + void PyErr_SetHandledException(PyObject* exc); + PyObject* PyErr_SetImportErrorSubclass(PyObject* exception, PyObject* msg, PyObject* name, PyObject* path); + void PyErr_SetInterrupt(); + int PyErr_SetInterruptEx(int signum); + void PyErr_SetNone(PyObject* type); + void PyErr_SetObject(PyObject* type, PyObject* value); + void PyErr_SetString(PyObject* exception, const char* string); + void PyErr_SyntaxLocation(const char* filename, int lineno); + void PyErr_SyntaxLocationEx(const char* filename, int lineno, int col_offset); + int PyErr_WarnEx(PyObject* category, const char* message, Py_ssize_t stack_level); + int PyErr_WarnExplicit(PyObject* category, const char* message, const char* filename, int lineno, const char* module, PyObject* registry); + + template + int PyErr_WarnFormat(PyObject* category, Py_ssize_t stack_level, const char* format, Args... args) + { + return (*PyErr_WarnFormat_Ptr)(category, stack_level, format, args...); + } + + void PyErr_WriteUnraisable(PyObject* obj); + void PyEval_AcquireLock(); + void PyEval_AcquireThread(PyThreadState* tstate); + + template + PyObject* PyEval_CallFunction(PyObject* callable, const char* format, Args... args) + { + return (*PyEval_CallFunction_Ptr)(callable, format, args...); + } + + template + PyObject* PyEval_CallMethod(PyObject* obj, const char* name, const char* format, Args... args) + { + return (*PyEval_CallMethod_Ptr)(obj, name, format, args...); + } + + PyObject* PyEval_CallObjectWithKeywords(PyObject* callable, PyObject* args, PyObject* kwargs); + PyObject* PyEval_EvalCode(PyObject* co, PyObject* globals, PyObject* locals); + PyObject* PyEval_EvalCodeEx(PyObject* co, PyObject* globals, PyObject* locals, PyObject*const* args, int argc, PyObject* const* kwds, int kwdc, PyObject* const* defs, int defc, PyObject* kwdefs, PyObject* closure); + #if HAS_PYTHON_VERSION(0x03090000) + PyObject* PyEval_EvalFrame(PyFrameObject* f); + PyObject* PyEval_EvalFrameEx(PyFrameObject* f, int exc); + #endif + PyObject* PyEval_GetBuiltins(); + #if HAS_PYTHON_VERSION(0x03090000) + PyFrameObject* PyEval_GetFrame(); + #endif + const char* PyEval_GetFuncDesc(PyObject* func); + const char* PyEval_GetFuncName(PyObject* func); + PyObject* PyEval_GetGlobals(); + PyObject* PyEval_GetLocals(); + void PyEval_InitThreads(); + void PyEval_ReleaseLock(); + void PyEval_ReleaseThread(PyThreadState* tstate); + void PyEval_RestoreThread(PyThreadState* tstate); + PyThreadState* PyEval_SaveThread(); + int PyEval_ThreadsInitialized(); + const char* (PyExceptionClass_Name)(PyObject* exc); + PyObject* PyException_GetCause(PyObject* exc); + PyObject* PyException_GetContext(PyObject* exc); + PyObject* PyException_GetTraceback(PyObject* exc); + void PyException_SetCause(PyObject* exc, PyObject* cause); + void PyException_SetContext(PyObject* exc, PyObject* context); + int PyException_SetTraceback(PyObject* exc, PyObject* traceback); + PyObject* PyFile_FromFd(int fd, const char* name, const char* mode, int buffering, const char* encoding, const char* errors, const char* newline, int closefd); + PyObject* PyFile_GetLine(PyObject* p, int n); + int PyFile_WriteObject(PyObject* obj, PyObject* p, int flags); + int PyFile_WriteString(const char* s, PyObject* p); + double PyFloat_AsDouble(PyObject* pyfloat); + PyObject* PyFloat_FromDouble(double dbl); + PyObject* PyFloat_FromString(PyObject* str); + PyObject* PyFloat_GetInfo(); + double PyFloat_GetMax(); + double PyFloat_GetMin(); + PyObject* PyFrozenSet_New(PyObject* iterable); + Py_ssize_t PyGC_Collect(); + int PyGC_Disable(); + int PyGC_Enable(); + int PyGC_IsEnabled(); + PyGILState_STATE PyGILState_Ensure(); + PyThreadState* PyGILState_GetThisThreadState(); + void PyGILState_Release(PyGILState_STATE state); + PyObject* PyImport_AddModule(const char* name); + PyObject* PyImport_AddModuleObject(PyObject* name); + int PyImport_AppendInittab(const char* name, PyObject*(*initfunc)()); + PyObject* PyImport_ExecCodeModule(const char* name, PyObject* co); + PyObject* PyImport_ExecCodeModuleEx(const char* name, PyObject* co, const char* pathname); + PyObject* PyImport_ExecCodeModuleObject(PyObject* name, PyObject* co, PyObject* pathname, PyObject* cpathname); + PyObject* PyImport_ExecCodeModuleWithPathnames(const char* name, PyObject* co, const char* pathname, const char* cpathname); + PyObject* PyImport_GetImporter(PyObject* path); + long PyImport_GetMagicNumber(); + const char* PyImport_GetMagicTag(); + PyObject* PyImport_GetModule(PyObject *name); + PyObject* PyImport_GetModuleDict(); + PyObject* PyImport_Import(PyObject* name); + int PyImport_ImportFrozenModule(const char* name); + int PyImport_ImportFrozenModuleObject(PyObject* name); + PyObject* PyImport_ImportModule(const char* name); + PyObject* PyImport_ImportModuleLevel(const char* name, PyObject* globals, PyObject* locals, PyObject* fromlist, int level); + PyObject* PyImport_ImportModuleLevelObject(PyObject* name, PyObject* globals, PyObject* locals, PyObject* fromlist, int level); + PyObject* PyImport_ImportModuleNoBlock(const char* name); + PyObject* PyImport_ReloadModule(PyObject* m); + int (PyIndex_Check)(PyObject* o); + void PyInterpreterState_Clear(PyInterpreterState* state); + void PyInterpreterState_Delete(PyInterpreterState* state); + PyInterpreterState* PyInterpreterState_Get(); + PyObject* PyInterpreterState_GetDict(PyInterpreterState* state); + int64_t PyInterpreterState_GetID(PyInterpreterState* state); + PyInterpreterState* PyInterpreterState_New(); + int (PyIter_Check)(PyObject* o); + PyObject* PyIter_Next(PyObject* o); + #if HAS_PYTHON_VERSION(0x030A0000) + PySendResult PyIter_Send(PyObject* iter, PyObject* arg, PyObject** presult); + #endif + int PyList_Append(PyObject* list, PyObject* item); + PyObject* PyList_AsTuple(PyObject* list); + PyObject* PyList_GetItem(PyObject* list, Py_ssize_t index); + PyObject* PyList_GetSlice(PyObject* list, Py_ssize_t low, Py_ssize_t high); + int PyList_Insert(PyObject* list, Py_ssize_t index, PyObject* item); + PyObject* PyList_New(Py_ssize_t size); + int PyList_Reverse(PyObject* list); + int PyList_SetItem(PyObject* list, Py_ssize_t index, PyObject* item); + int PyList_SetSlice(PyObject* list, Py_ssize_t low, Py_ssize_t high, PyObject* item); + Py_ssize_t PyList_Size(PyObject* list); + int PyList_Sort(PyObject* list); + double PyLong_AsDouble(PyObject* pylong); + long PyLong_AsLong(PyObject* pylong); + long PyLong_AsLongAndOverflow(PyObject* pylong, int* overflow); + long long PyLong_AsLongLong(PyObject* pylong); + long long PyLong_AsLongLongAndOverflow(PyObject* pylong, int* overflow); + size_t PyLong_AsSize_t(PyObject* pylong); + Py_ssize_t PyLong_AsSsize_t(PyObject* pylong); + unsigned long PyLong_AsUnsignedLong(PyObject* pylong); + unsigned long long PyLong_AsUnsignedLongLong(PyObject* pylong); + unsigned long long PyLong_AsUnsignedLongLongMask(PyObject* pylong); + unsigned long PyLong_AsUnsignedLongMask(PyObject* pylong); + void* PyLong_AsVoidPtr(PyObject* pylong); + PyObject* PyLong_FromDouble(double dbl); + PyObject* PyLong_FromLong(long lng); + PyObject* PyLong_FromLongLong(long long longlong); + PyObject* PyLong_FromSize_t(size_t size); + PyObject* PyLong_FromSsize_t(Py_ssize_t size); + PyObject* PyLong_FromString(const char* str, char** pend, int base); + PyObject* PyLong_FromUnsignedLong(unsigned long ulong); + PyObject* PyLong_FromUnsignedLongLong(unsigned long long ulonglong); + PyObject* PyLong_FromVoidPtr(void* ptr); + PyObject* PyLong_GetInfo(); + int PyMapping_Check(PyObject* o); + PyObject* PyMapping_GetItemString(PyObject* o, const char* key); + int PyMapping_HasKey(PyObject* o, PyObject* key); + int PyMapping_HasKeyString(PyObject* o, const char* key); + PyObject* PyMapping_Items(PyObject* o); + PyObject* PyMapping_Keys(PyObject* o); + + #ifdef PyMapping_Length + #undef PyMapping_Length + Py_ssize_t PyMapping_Length(PyObject* o); + #endif + + int PyMapping_SetItemString(PyObject* o, const char* key, PyObject* value); + Py_ssize_t PyMapping_Size(PyObject* o); + PyObject* PyMapping_Values(PyObject* o); + void* PyMem_Calloc(size_t nelem, size_t elsize); + void PyMem_Free(void* ptr); + void* PyMem_Malloc(size_t size); + void* PyMem_Realloc(void* ptr, size_t new_size); + + #if HAS_PYTHON_VERSION(0x030B1000) + PyObject* PyMemoryView_FromBuffer(const Py_buffer* info); + #endif + + PyObject* PyMemoryView_FromMemory(char* mem, Py_ssize_t size, int flags); + PyObject* PyMemoryView_FromObject(PyObject* base); + PyObject* PyMemoryView_GetContiguous(PyObject* base, int buffertype, char order); + PyObject* PyModuleDef_Init(PyModuleDef* def); + int PyModule_AddFunctions(PyObject* module, PyMethodDef* functions); + int PyModule_AddIntConstant(PyObject* module, const char* name, long value); + int PyModule_AddObject(PyObject* module, const char* name, PyObject* value); + int PyModule_AddObjectRef(PyObject* mod, const char* name, PyObject* value); + int PyModule_AddStringConstant(PyObject* module, const char* name, const char* value); + int PyModule_AddType(PyObject* module, PyTypeObject* type); + PyObject* PyModule_Create2(PyModuleDef* def, int apiver); + int PyModule_ExecDef(PyObject* module, PyModuleDef* def); + PyObject* PyModule_FromDefAndSpec2(PyModuleDef* def, PyObject* spec, int module_api_version); + PyModuleDef* PyModule_GetDef(PyObject* module); + PyObject* PyModule_GetDict(PyObject* module); + const char* PyModule_GetFilename(PyObject* module); + PyObject* PyModule_GetFilenameObject(PyObject* module); + const char* PyModule_GetName(PyObject* module); + PyObject* PyModule_GetNameObject(PyObject* module); + void* PyModule_GetState(PyObject* module); + PyObject* PyModule_New(const char* name); + PyObject* PyModule_NewObject(PyObject* name); + int PyModule_SetDocString(PyObject* module, const char* docs); + PyObject* PyNumber_Absolute(PyObject* o); + PyObject* PyNumber_Add(PyObject* o1, PyObject* o2); + PyObject* PyNumber_And(PyObject* o1, PyObject* o2); + Py_ssize_t PyNumber_AsSsize_t(PyObject* o, PyObject* exc); + int PyNumber_Check(PyObject* o); + PyObject* PyNumber_Divmod(PyObject* o1, PyObject* o2); + PyObject* PyNumber_Float(PyObject* o); + PyObject* PyNumber_FloorDivide(PyObject* o1, PyObject* o2); + PyObject* PyNumber_InPlaceAdd(PyObject* o1, PyObject* o2); + PyObject* PyNumber_InPlaceAnd(PyObject* o1, PyObject* o2); + PyObject* PyNumber_InPlaceFloorDivide(PyObject* o1, PyObject* o2); + PyObject* PyNumber_InPlaceLshift(PyObject* o1, PyObject* o2); + PyObject* PyNumber_InPlaceMatrixMultiply(PyObject* o1, PyObject* o2); + PyObject* PyNumber_InPlaceMultiply(PyObject* o1, PyObject* o2); + PyObject* PyNumber_InPlaceOr(PyObject* o1, PyObject* o2); + PyObject* PyNumber_InPlacePower(PyObject* o1, PyObject* o2, PyObject* o3); + PyObject* PyNumber_InPlaceRemainder(PyObject* o1, PyObject* o2); + PyObject* PyNumber_InPlaceRshift(PyObject* o1, PyObject* o2); + PyObject* PyNumber_InPlaceSubtract(PyObject* o1, PyObject* o2); + PyObject* PyNumber_InPlaceTrueDivide(PyObject* o1, PyObject* o2); + PyObject* PyNumber_InPlaceXor(PyObject* o1, PyObject* o2); + PyObject* PyNumber_Index(PyObject* o); + PyObject* PyNumber_Invert(PyObject* o); + PyObject* PyNumber_Long(PyObject* o); + PyObject* PyNumber_Lshift(PyObject* o1, PyObject* o2); + PyObject* PyNumber_MatrixMultiply(PyObject* o1, PyObject* o2); + PyObject* PyNumber_Multiply(PyObject* o1, PyObject* o2); + PyObject* PyNumber_Negative(PyObject* o); + PyObject* PyNumber_Or(PyObject* o1, PyObject* o2); + PyObject* PyNumber_Positive(PyObject* o); + PyObject* PyNumber_Power(PyObject* o1, PyObject* o2, PyObject* o3); + PyObject* PyNumber_Remainder(PyObject* o1, PyObject* o2); + PyObject* PyNumber_Rshift(PyObject* o1, PyObject* o2); + PyObject* PyNumber_Subtract(PyObject* o1, PyObject* o2); + PyObject* PyNumber_ToBase(PyObject* n, int base); + PyObject* PyNumber_TrueDivide(PyObject* o1, PyObject* o2); + PyObject* PyNumber_Xor(PyObject* o1, PyObject* o2); + void PyOS_AfterFork(); + void PyOS_AfterFork_Child(); + void PyOS_AfterFork_Parent(); + void PyOS_BeforeFork(); + PyObject* PyOS_FSPath(PyObject* path); + int PyOS_InterruptOccurred(); + char* PyOS_double_to_string(double val, char format_code, int precision, int flags, int* type); + PyOS_sighandler_t PyOS_getsig(int i); + int PyOS_mystricmp(const char* a, const char* b); + int PyOS_mystrnicmp(const char* a, const char* b, Py_ssize_t max_len); + PyOS_sighandler_t PyOS_setsig(int i, PyOS_sighandler_t handler); + + template + int PyOS_snprintf(char* str, size_t size, const char* format, Args... args) + { + return (*PyOS_snprintf_Ptr)(str, size, format, args...); + } + + double PyOS_string_to_double(const char* str, char** endptr, PyObject* overflow_exception); + long PyOS_strtol(const char* str, char** endptr, int base); + unsigned long PyOS_strtoul(const char* str, char** endptr, int base); + int PyOS_vsnprintf(char* str, size_t size, const char* format, va_list va); + PyObject* PyObject_ASCII(PyObject* o); + int PyObject_AsCharBuffer(PyObject* obj, const char** buffer, Py_ssize_t* buffer_len); + int PyObject_AsFileDescriptor(PyObject* o); + int PyObject_AsReadBuffer(PyObject* obj, const void** buffer, Py_ssize_t* buffer_len); + int PyObject_AsWriteBuffer(PyObject* obj, void** buffer, Py_ssize_t* buffer_len); + PyObject* PyObject_Bytes(PyObject* o); + + PyObject* PyObject_Call(PyObject* callable, PyObject* args, PyObject* kwargs); + + template + PyObject* PyObject_CallFunction(PyObject* callable, const char* format, Args... args) + { + return (*PyObject_CallFunction_Ptr)(callable, format, args...); + } + + template + PyObject* PyObject_CallFunctionObjArgs(PyObject* callable, Args... args) + { + return (*PyObject_CallFunctionObjArgs_Ptr)(callable, args...); + } + + template + PyObject* PyObject_CallMethod(PyObject* obj, const char* name, const char* format, Args... args) + { + return (*PyObject_CallMethod_Ptr)(obj, name, format, args...); + } + + template + PyObject* PyObject_CallMethodObjArgs(PyObject* obj, PyObject* name, Args... args) + { + return (*PyObject_CallMethodObjArgs_Ptr)(obj, name, args...); + } + + PyObject* PyObject_CallNoArgs(PyObject* func); + PyObject* PyObject_CallObject(PyObject* callable, PyObject* args); + void* PyObject_Calloc(size_t nelem, size_t elsize); + int PyObject_CheckBuffer(PyObject* obj); + int PyObject_CheckReadBuffer(PyObject* obj); + void PyObject_ClearWeakRefs(PyObject* obj); + int PyObject_CopyData(PyObject* dest, PyObject* src); + int PyObject_DelItem(PyObject* o, PyObject* key); + int PyObject_DelItemString(PyObject* o, const char* key); + PyObject* PyObject_Dir(PyObject* obj); + PyObject* PyObject_Format(PyObject* obj, PyObject* format_spec); + void PyObject_Free(void* ptr); + void PyObject_GC_Del(void* op); + int PyObject_GC_IsFinalized(PyObject* op); + int PyObject_GC_IsTracked(PyObject* op); + void PyObject_GC_Track(void* op); + void PyObject_GC_UnTrack(void* op); + PyObject* PyObject_GenericGetAttr(PyObject* o, PyObject* name); + PyObject* PyObject_GenericGetDict(PyObject* o, void* context); + int PyObject_GenericSetAttr(PyObject* o, PyObject* name, PyObject* value); + int PyObject_GenericSetDict(PyObject* o, PyObject* value, void* context); + PyObject* PyObject_GetAIter(PyObject* o); + PyObject* PyObject_GetAttr(PyObject* o, PyObject* attr_name); + PyObject* PyObject_GetAttrString(PyObject* o, const char* attr); + + #if HAS_PYTHON_VERSION(0x030B1000) + int PyObject_GetBuffer(PyObject* obj, Py_buffer* view, int flags); + #endif + + PyObject* PyObject_GetItem(PyObject* o, PyObject* key); + PyObject* PyObject_GetIter(PyObject* o); + int PyObject_HasAttr(PyObject* o, PyObject* attr_name); + int PyObject_HasAttrString(PyObject* o, const char* attr); + Py_hash_t PyObject_Hash(PyObject* o); + Py_hash_t PyObject_HashNotImplemented(PyObject* o); + PyObject* PyObject_Init(PyObject* o, PyTypeObject* type); + PyVarObject* PyObject_InitVar(PyVarObject* op, PyTypeObject* type, Py_ssize_t size); + int PyObject_IsInstance(PyObject* object, PyObject* typeorclass); + int PyObject_IsSubclass(PyObject* object, PyObject* typeorclass); + int PyObject_IsTrue(PyObject* o); + + #ifdef PyObject_Length + #undef PyObject_Length + Py_ssize_t PyObject_Length(PyObject* o); + #endif + + void* PyObject_Malloc(size_t size); + int PyObject_Not(PyObject* o); + void* PyObject_Realloc(void* ptr, size_t new_size); + PyObject* PyObject_Repr(PyObject* o); + PyObject* PyObject_RichCompare(PyObject* a, PyObject* b, int opid); + int PyObject_RichCompareBool(PyObject* a, PyObject* b, int opid); + PyObject* PyObject_SelfIter(PyObject* o); + int PyObject_SetAttr(PyObject* o, PyObject* attr_name, PyObject* v); + int PyObject_SetAttrString(PyObject* o, const char* attr_name, PyObject* value); + int PyObject_SetItem(PyObject* o, PyObject* key, PyObject* v); + Py_ssize_t PyObject_Size(PyObject* o); + PyObject* PyObject_Str(PyObject* o); + PyObject* PyObject_Type(PyObject* o); + #if HAS_PYTHON_VERSION(0x030B0000) + int (PyObject_TypeCheck)(PyObject* o, PyTypeObject* type); + #endif + PyObject* PySeqIter_New(PyObject* o); + int PySequence_Check(PyObject* o); + PyObject* PySequence_Concat(PyObject* o1, PyObject* o2); + int PySequence_Contains(PyObject* seq, PyObject* ob); + Py_ssize_t PySequence_Count(PyObject* o, PyObject* value); + int PySequence_DelItem(PyObject* o, Py_ssize_t i); + int PySequence_DelSlice(PyObject* o, Py_ssize_t i1, Py_ssize_t i2); + PyObject* PySequence_Fast(PyObject* o, const char* m); + PyObject* PySequence_GetItem(PyObject* o, Py_ssize_t i); + PyObject* PySequence_GetSlice(PyObject* o, Py_ssize_t i1, Py_ssize_t i2); + + #ifdef PySequence_In + #undef PySequence_In + int PySequence_In(PyObject* o, PyObject* value); + #endif + + PyObject* PySequence_InPlaceConcat(PyObject* o1, PyObject* o2); + PyObject* PySequence_InPlaceRepeat(PyObject* o, Py_ssize_t count); + Py_ssize_t PySequence_Index(PyObject* o, PyObject* value); + + #ifdef PySequence_Length + #undef PySequence_Length + Py_ssize_t PySequence_Length(PyObject* o); + #endif + + PyObject* PySequence_List(PyObject* o); + PyObject* PySequence_Repeat(PyObject* o, Py_ssize_t count); + int PySequence_SetItem(PyObject* o, Py_ssize_t i, PyObject* v); + int PySequence_SetSlice(PyObject* o, Py_ssize_t i1, Py_ssize_t i2, PyObject* v); + Py_ssize_t PySequence_Size(PyObject* o); + PyObject* PySequence_Tuple(PyObject* o); + int PySet_Add(PyObject* set, PyObject* key); + int PySet_Clear(PyObject* set); + int PySet_Contains(PyObject* anyset, PyObject* key); + int PySet_Discard(PyObject* set, PyObject* key); + PyObject* PySet_New(PyObject* iterable); + PyObject* PySet_Pop(PyObject* set); + Py_ssize_t PySet_Size(PyObject* anyset); + Py_ssize_t PySlice_AdjustIndices(Py_ssize_t length, Py_ssize_t* start, Py_ssize_t* stop, Py_ssize_t step); + int PySlice_GetIndices(PyObject* r, Py_ssize_t length, Py_ssize_t* start, Py_ssize_t* stop, Py_ssize_t* step); + + #ifdef PySlice_GetIndicesEx + int (PySlice_GetIndicesEx)(PyObject* r, Py_ssize_t length, Py_ssize_t* start, Py_ssize_t* stop, Py_ssize_t* step, Py_ssize_t* slicelength); + #endif + + PyObject* PySlice_New(PyObject* start, PyObject* stop, PyObject* step); + int PySlice_Unpack(PyObject* slice, Py_ssize_t* start, Py_ssize_t* stop, Py_ssize_t* step); + int PyState_AddModule(PyObject* module, PyModuleDef* def); + PyObject* PyState_FindModule(PyModuleDef* def); + int PyState_RemoveModule(PyModuleDef* def); + PyObject* PyStructSequence_GetItem(PyObject* p, Py_ssize_t pos); + PyObject* PyStructSequence_New(PyTypeObject* type); + PyTypeObject* PyStructSequence_NewType(PyStructSequence_Desc* desc); + void PyStructSequence_SetItem(PyObject* p, Py_ssize_t pos, PyObject* o); + void PySys_AddWarnOption(const wchar_t* s); + void PySys_AddWarnOptionUnicode(PyObject* unicode); + void PySys_AddXOption(const wchar_t* option); + + template + void PySys_FormatStderr(const char* format, Args... args) + { + return (*PySys_FormatStderr_Ptr)(format, args...); + } + + template + void PySys_FormatStdout(const char* format, Args... args) + { + return (*PySys_FormatStdout_Ptr)(format, args...); + } + + PyObject* PySys_GetObject(const char* name); + PyObject* PySys_GetXOptions(); + int PySys_HasWarnOptions(); + void PySys_ResetWarnOptions(); + void PySys_SetArgv(int argc, wchar_t** argv); + void PySys_SetArgvEx(int argc, wchar_t** argv, int updatepath); + int PySys_SetObject(const char* name, PyObject* v); + void PySys_SetPath(const wchar_t* path); + + template + void PySys_WriteStderr(const char* format, Args... args) + { + return (*PySys_WriteStderr_Ptr)(format, args...); + } + + template + void PySys_WriteStdout(const char* format, Args... args) + { + return (*PySys_WriteStdout_Ptr)(format, args...); + } + + void PyThreadState_Clear(PyThreadState* tstate); + void PyThreadState_Delete(PyThreadState* tstate); + PyThreadState* PyThreadState_Get(); + PyObject* PyThreadState_GetDict(); + #if HAS_PYTHON_VERSION(0x03090000) + PyFrameObject* PyThreadState_GetFrame(PyThreadState* tstate); + #endif + uint64_t PyThreadState_GetID(PyThreadState* tstate); + PyInterpreterState* PyThreadState_GetInterpreter(PyThreadState* tstate); + PyThreadState* PyThreadState_New(PyInterpreterState* interp); + int PyThreadState_SetAsyncExc(unsigned long id, PyObject* exc); + PyThreadState* PyThreadState_Swap(PyThreadState* tstate); + PyObject* PyThread_GetInfo(); + void PyThread_ReInitTLS(); + int PyThread_acquire_lock(PyThread_type_lock lock, int intr_flag); + PyLockStatus PyThread_acquire_lock_timed(PyThread_type_lock lock, long long microseconds, int intr_flag); + PyThread_type_lock PyThread_allocate_lock(); + int PyThread_create_key(); + void PyThread_delete_key(int key); + void PyThread_delete_key_value(int key); + void PyThread_exit_thread(); + void PyThread_free_lock(PyThread_type_lock lock); + void* PyThread_get_key_value(int key); + size_t PyThread_get_stacksize(); + unsigned long PyThread_get_thread_ident(); + unsigned long PyThread_get_thread_native_id(); + void PyThread_init_thread(); + void PyThread_release_lock(PyThread_type_lock lock); + int PyThread_set_key_value(int key, void* value); + int PyThread_set_stacksize(size_t size); + unsigned long PyThread_start_new_thread(void (*initfunc)(void*) , void* pdata); + #if HAS_PYTHON_VERSION(0x03070000) + Py_tss_t* PyThread_tss_alloc(); + int PyThread_tss_create(Py_tss_t* key); + void PyThread_tss_delete(Py_tss_t* key); + void PyThread_tss_free(Py_tss_t* key); + void* PyThread_tss_get(Py_tss_t* key); + int PyThread_tss_is_created(Py_tss_t* key); + int PyThread_tss_set(Py_tss_t* key, void* value); + #endif + #if HAS_PYTHON_VERSION(0x03090000) + int PyTraceBack_Here(PyFrameObject* f); + #endif + int PyTraceBack_Print(PyObject* v, PyObject* f); + PyObject* PyTuple_GetItem(PyObject* tuple, Py_ssize_t index); + PyObject* PyTuple_GetSlice(PyObject* tuple, Py_ssize_t low, Py_ssize_t high); + PyObject* PyTuple_New(Py_ssize_t size); + + template + PyObject* PyTuple_Pack(Py_ssize_t n, Args... args) + { + return (*PyTuple_Pack_Ptr)(n, args...); + } + + int PyTuple_SetItem(PyObject* tuple, Py_ssize_t index, PyObject* item); + Py_ssize_t PyTuple_Size(PyObject* tuple); + unsigned int PyType_ClearCache(); + const char* _PyType_Name(PyTypeObject* type); + PyObject* PyType_FromModuleAndSpec(PyObject* module, PyType_Spec* spec, PyObject* bases); + PyObject* PyType_FromSpec(PyType_Spec* spec); + PyObject* PyType_FromSpecWithBases(PyType_Spec* spec, PyObject* bases); + PyObject* PyType_GenericAlloc(PyTypeObject* type, Py_ssize_t nitems); + PyObject* PyType_GenericNew(PyTypeObject* type, PyObject* args, PyObject* kwds); + unsigned long PyType_GetFlags(PyTypeObject* type); + PyObject* PyType_GetModule(PyTypeObject* type); + void* PyType_GetModuleState(PyTypeObject* type); + PyObject* PyType_GetName(PyTypeObject* type); + PyObject* PyType_GetQualName(PyTypeObject* type); + void* PyType_GetSlot(PyTypeObject* type, int slot); + int PyType_IsSubtype(PyTypeObject* child, PyTypeObject* parent); + void PyType_Modified(PyTypeObject* type); + int PyType_Ready(PyTypeObject* type); + PyObject* PyUnicodeDecodeError_Create(const char* encoding, const char* object, Py_ssize_t length, Py_ssize_t start, Py_ssize_t end, const char* reason); + PyObject* PyUnicodeDecodeError_GetEncoding(PyObject* exc); + int PyUnicodeDecodeError_GetEnd(PyObject* exc, Py_ssize_t* end); + PyObject* PyUnicodeDecodeError_GetObject(PyObject* exc); + PyObject* PyUnicodeDecodeError_GetReason(PyObject* exc); + int PyUnicodeDecodeError_GetStart(PyObject* exc, Py_ssize_t* start); + int PyUnicodeDecodeError_SetEnd(PyObject* exc, Py_ssize_t end); + int PyUnicodeDecodeError_SetReason(PyObject* exc, const char* reason); + int PyUnicodeDecodeError_SetStart(PyObject* exc, Py_ssize_t start); + PyObject* PyUnicodeEncodeError_GetEncoding(PyObject* exc); + int PyUnicodeEncodeError_GetEnd(PyObject* exc, Py_ssize_t* end); + PyObject* PyUnicodeEncodeError_GetObject(PyObject* exc); + PyObject* PyUnicodeEncodeError_GetReason(PyObject* exc); + int PyUnicodeEncodeError_GetStart(PyObject* exc, Py_ssize_t* start); + int PyUnicodeEncodeError_SetEnd(PyObject* exc, Py_ssize_t end); + int PyUnicodeEncodeError_SetReason(PyObject* exc, const char* reason); + int PyUnicodeEncodeError_SetStart(PyObject* exc, Py_ssize_t start); + int PyUnicodeTranslateError_GetEnd(PyObject* exc, Py_ssize_t* end); + PyObject* PyUnicodeTranslateError_GetObject(PyObject* exc); + PyObject* PyUnicodeTranslateError_GetReason(PyObject* exc); + int PyUnicodeTranslateError_GetStart(PyObject* exc, Py_ssize_t* start); + int PyUnicodeTranslateError_SetEnd(PyObject* exc, Py_ssize_t end); + int PyUnicodeTranslateError_SetReason(PyObject* exc, const char* reason); + int PyUnicodeTranslateError_SetStart(PyObject* exc, Py_ssize_t start); + void PyUnicode_Append(PyObject** pleft, PyObject* right); + void PyUnicode_AppendAndDel(PyObject** pleft, PyObject* right); + PyObject* PyUnicode_AsASCIIString(PyObject* unicode); + PyObject* PyUnicode_AsCharmapString(PyObject* unicode, PyObject* mapping); + PyObject* PyUnicode_AsDecodedObject(PyObject* unicode, const char* encoding, const char* errors); + PyObject* PyUnicode_AsDecodedUnicode(PyObject* unicode, const char* encoding, const char* errors); + PyObject* PyUnicode_AsEncodedObject(PyObject* unicode, const char* encoding, const char* errors); + PyObject* PyUnicode_AsEncodedString(PyObject* unicode, const char* encoding, const char* errors); + PyObject* PyUnicode_AsEncodedUnicode(PyObject* unicode, const char* encoding, const char* errors); + PyObject* PyUnicode_AsLatin1String(PyObject* unicode); + PyObject* PyUnicode_AsRawUnicodeEscapeString(PyObject* unicode); + Py_UCS4* PyUnicode_AsUCS4(PyObject* unicode, Py_UCS4* buffer, Py_ssize_t buflen, int copy_null); + Py_UCS4* PyUnicode_AsUCS4Copy(PyObject* unicode); + PyObject* PyUnicode_AsUTF16String(PyObject* unicode); + PyObject* PyUnicode_AsUTF32String(PyObject* unicode); + const char* PyUnicode_AsUTF8AndSize(PyObject* unicode, Py_ssize_t* size); + PyObject* PyUnicode_AsUTF8String(PyObject* unicode); + PyObject* PyUnicode_AsUnicodeEscapeString(PyObject* unicode); + Py_ssize_t PyUnicode_AsWideChar(PyObject* unicode, wchar_t* w, Py_ssize_t size); + wchar_t* PyUnicode_AsWideCharString(PyObject* unicode, Py_ssize_t* size); + PyObject* PyUnicode_BuildEncodingMap(PyObject* string); + int PyUnicode_Compare(PyObject* left, PyObject* right); + int PyUnicode_CompareWithASCIIString(PyObject* left, const char* right); + PyObject* PyUnicode_Concat(PyObject* left, PyObject* right); + int PyUnicode_Contains(PyObject* container, PyObject* element); + Py_ssize_t PyUnicode_Count(PyObject* str, PyObject* substr, Py_ssize_t start, Py_ssize_t end); + PyObject* PyUnicode_Decode(const char* s, Py_ssize_t size, const char* encoding, const char* errors); + PyObject* PyUnicode_DecodeASCII(const char* string, Py_ssize_t length, const char* errors); + PyObject* PyUnicode_DecodeCharmap(const char* string, Py_ssize_t length, PyObject* mapping, const char* errors); + PyObject* PyUnicode_DecodeFSDefault(const char* s); + PyObject* PyUnicode_DecodeFSDefaultAndSize(const char* s, Py_ssize_t size); + PyObject* PyUnicode_DecodeLatin1(const char* string, Py_ssize_t length, const char* errors); + PyObject* PyUnicode_DecodeLocale(const char* str, const char* errors); + PyObject* PyUnicode_DecodeLocaleAndSize(const char* str, Py_ssize_t len, const char* errors); + PyObject* PyUnicode_DecodeRawUnicodeEscape(const char* string, Py_ssize_t length, const char* errors); + PyObject* PyUnicode_DecodeUTF16(const char* string, Py_ssize_t length, const char* errors, int* byteorder); + PyObject* PyUnicode_DecodeUTF16Stateful(const char* string, Py_ssize_t length, const char* errors, int* byteorder, Py_ssize_t* consumed); + PyObject* PyUnicode_DecodeUTF32(const char* string, Py_ssize_t length, const char* errors, int* byteorder); + PyObject* PyUnicode_DecodeUTF32Stateful(const char* string, Py_ssize_t length, const char* errors, int* byteorder, Py_ssize_t* consumed); + PyObject* PyUnicode_DecodeUTF7(const char* string, Py_ssize_t length, const char* errors); + PyObject* PyUnicode_DecodeUTF7Stateful(const char* string, Py_ssize_t length, const char* errors, Py_ssize_t* consumed); + PyObject* PyUnicode_DecodeUTF8(const char* string, Py_ssize_t length, const char* errors); + PyObject* PyUnicode_DecodeUTF8Stateful(const char* string, Py_ssize_t length, const char* errors, Py_ssize_t* consumed); + PyObject* PyUnicode_DecodeUnicodeEscape(const char* string, Py_ssize_t length, const char* errors); + PyObject* PyUnicode_EncodeFSDefault(PyObject* unicode); + PyObject* PyUnicode_EncodeLocale(PyObject* unicode, const char* errors); + int PyUnicode_FSConverter(PyObject* obj, void* result); + int PyUnicode_FSDecoder(PyObject* obj, void* result); + Py_ssize_t PyUnicode_Find(PyObject* str, PyObject* substr, Py_ssize_t start, Py_ssize_t end, int direction); + Py_ssize_t PyUnicode_FindChar(PyObject* str, Py_UCS4 ch, Py_ssize_t start, Py_ssize_t end, int direction); + PyObject* PyUnicode_Format(PyObject* format, PyObject* args); + PyObject* PyUnicode_FromEncodedObject(PyObject* obj, const char* encoding, const char* errors); + PyObject* PyUnicode_FromFormat(const char* format, ...); + PyObject* PyUnicode_FromFormatV(const char* format, va_list vargs); + PyObject* PyUnicode_FromObject(PyObject* obj); + PyObject* PyUnicode_FromOrdinal(int ordinal); + PyObject *PyUnicode_FromString(const char* u); + PyObject* PyUnicode_FromStringAndSize(const char* u, Py_ssize_t size); + PyObject* PyUnicode_FromWideChar(const wchar_t* w, Py_ssize_t size); + const char* PyUnicode_GetDefaultEncoding(); + Py_ssize_t PyUnicode_GetLength(PyObject* unicode); + Py_ssize_t PyUnicode_GetSize(PyObject* unicode); + PyObject* PyUnicode_InternFromString(const char* u); + void PyUnicode_InternImmortal(PyObject** unicode); + void PyUnicode_InternInPlace(PyObject** unicode); + int PyUnicode_IsIdentifier(PyObject* s); + PyObject* PyUnicode_Join(PyObject* separator, PyObject* seq); + PyObject* PyUnicode_Partition(PyObject* s, PyObject* sep); + PyObject* PyUnicode_RPartition(PyObject* s, PyObject* sep); + PyObject* PyUnicode_RSplit(PyObject* s, PyObject* sep, Py_ssize_t maxsplit); + Py_UCS4 PyUnicode_ReadChar(PyObject* unicode, Py_ssize_t index); + PyObject* PyUnicode_Replace(PyObject* str, PyObject* substr, PyObject* replstr, Py_ssize_t maxcount); + int PyUnicode_Resize(PyObject** unicode, Py_ssize_t length); + PyObject* PyUnicode_RichCompare(PyObject* left, PyObject* right, int op); + PyObject* PyUnicode_Split(PyObject* s, PyObject* sep, Py_ssize_t maxsplit); + PyObject* PyUnicode_Splitlines(PyObject* s, int keepends); + PyObject* PyUnicode_Substring(PyObject* str, Py_ssize_t start, Py_ssize_t end); + Py_ssize_t PyUnicode_Tailmatch(PyObject* str, PyObject* substr, Py_ssize_t start, Py_ssize_t end, int direction); + PyObject* PyUnicode_Translate(PyObject* str, PyObject* table, const char* errors); + int PyUnicode_WriteChar(PyObject* unicode, Py_ssize_t index, Py_UCS4 character); + PyObject* PyWeakref_GetObject(PyObject* ref); + PyObject* PyWeakref_NewProxy(PyObject* ob, PyObject* callback); + PyObject* PyWeakref_NewRef(PyObject* ob, PyObject* callback); + PyObject* PyWrapper_New(PyObject* descr, PyObject* self); + int Py_AddPendingCall(int (*func)(void*), void* arg); + int Py_AtExit(void (*func)()); + + template + PyObject* Py_BuildValue(const char* format, Args... args) + { + return (*Py_BuildValue_Ptr)(format, args...); + } + + int Py_BytesMain(int argc, char** argv); + PyObject* (Py_CompileString)(const char* str, const char* filename, int start); + void Py_DecRef(PyObject* obj); + wchar_t* Py_DecodeLocale(const char* arg, size_t* size); + char* Py_EncodeLocale(const wchar_t* text, size_t* error_pos); + void Py_EndInterpreter(PyThreadState* tstate); + int (Py_EnterRecursiveCall)(const char* where); + void Py_Exit(int exit_code); + void (Py_FatalError)(const char* message); + void Py_Finalize(); + int Py_FinalizeEx(); + PyObject* Py_GenericAlias(PyObject* origin, PyObject* args); + const char* Py_GetBuildInfo(); + const char* Py_GetCompiler(); + const char* Py_GetCopyright(); + wchar_t* Py_GetExecPrefix(); + wchar_t* Py_GetPath(); + const char* Py_GetPlatform(); + wchar_t* Py_GetPrefix(); + wchar_t* Py_GetProgramFullPath(); + wchar_t* Py_GetProgramName(); + wchar_t* Py_GetPythonHome(); + int Py_GetRecursionLimit(); + const char* Py_GetVersion(); + void Py_IncRef(PyObject* obj); + void Py_Initialize(); + void Py_InitializeEx(int initsigs); + int (Py_Is)(PyObject* x, PyObject* y); + int (Py_IsFalse)(PyObject* x); + int Py_IsInitialized(); + int (Py_IsNone)(PyObject* x); + int (Py_IsTrue)(PyObject* x); + int (Py_IS_TYPE)(PyObject* o, PyTypeObject* type); + PyTypeObject* (Py_TYPE)(PyObject *o); + void (Py_LeaveRecursiveCall)(); + int Py_Main(int argc, wchar_t** argv); + int Py_MakePendingCalls(); + PyThreadState* Py_NewInterpreter(); + + #ifdef Py_NewRef + PyObject* (Py_NewRef)(PyObject* obj); + #endif + + int Py_ReprEnter(PyObject* object); + void Py_ReprLeave(PyObject* object); + void Py_SetPath(const wchar_t* path); + void Py_SetProgramName(const wchar_t* name); + void Py_SetPythonHome(const wchar_t* home); + void Py_SetRecursionLimit(int limit); + PyObject* Py_VaBuildValue(const char* format, va_list vargs); + + #ifdef Py_XNewRef + PyObject* (Py_XNewRef)(PyObject* obj); + #endif + + + void (Py_INCREF)(PyObject* op) + { + //++op->ob_refcnt; + Py_IncRef(op); + } + + void (Py_DECREF)(PyObject* op) + { + Py_DecRef(op); + /*PyObject* _py_decref_tmp = op; + if (--(_py_decref_tmp)->ob_refcnt == 0) + { + _Py_Dealloc(_py_decref_tmp); + }*/ + } + + void (Py_CLEAR)(PyObject* op) + { + PyObject* _py_tmp = op; + if (_py_tmp != NULL) + { + op = NULL; + (Py_DECREF)(_py_tmp); + } + } + + void (Py_XINCREF)(PyObject* op) + { + PyObject* _py_xincref_tmp = op; + if (_py_xincref_tmp != NULL) + { + (Py_INCREF)(_py_xincref_tmp); + } + } + + void (Py_XDECREF)(PyObject* op) + { + PyObject* _py_xdecref_tmp = op; + if (_py_xdecref_tmp != NULL) + { + (Py_DECREF)(_py_xdecref_tmp); + } + } + + bool (PyList_Check)(PyObject* op) + { + return ((PyType_GetFlags((Py_TYPE)(op)) & Py_TPFLAGS_LIST_SUBCLASS) != 0); + } + + template + Type* (PyObject_New)(PyTypeObject* type_object) + { + auto _PyObject_Size = [](PyTypeObject* tp) -> Py_ssize_t { + std::size_t offset = sizeof(PyVarObject) + sizeof(const char*); + return *reinterpret_cast(reinterpret_cast(tp) + offset); + }; + + auto _PyObject_New = [this, &_PyObject_Size](PyTypeObject* tp) -> PyObject* { + PyObject* op = reinterpret_cast(PyObject_Malloc(_PyObject_Size(tp))); + if (op == NULL) { + return PyErr_NoMemory(); + } + PyObject_Init(op, tp); + return op; + }; + + return reinterpret_cast(_PyObject_New(type_object)); + } + + PyObject* Py_GetNone_Object() const + { + return static_cast(Py_NoneStruct_Ptr); + } + + PyTypeObject* PyList_Type() const + { + return static_cast(Py_ListType_Ptr); + } +}; +#endif + +#endif //REMOTEINPUT_PYTHON_HXX diff --git a/third-party/RemoteInput/RemoteInput/Plugin/Python/PythonCommon.cxx b/third-party/RemoteInput/RemoteInput/Plugin/Python/PythonCommon.cxx new file mode 100644 index 0000000..32a680e --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Plugin/Python/PythonCommon.cxx @@ -0,0 +1,309 @@ +// +// Created by Brandon on 2022-09-21. +// + +#include "PythonCommon.hxx" + +#if defined(USE_PYBIND11) +nanobind::object python_create_eios(EIOS* eios) noexcept +{ + if (eios) + { + auto py_eios_object = new PyEIOS(); + py_eios_object->pid = eios->pid; + py_eios_object->native_eios = eios; + + return nanobind::cast(py_eios_object); + } + return nanobind::none(); +} + +nanobind::object python_create_object(PyEIOS* self, jobject object) noexcept +{ + if (object) + { + auto py_java_object = new PyJavaObject(); + py_java_object->eios = self; + py_java_object->object = object; + + return nanobind::cast(py_java_object); + } + return nanobind::none(); +} + +nanobind::object python_create_object(PyJavaObject* self, jobject object) noexcept +{ + if (object) + { + auto py_java_object = new PyJavaObject(); + py_java_object->eios = self->eios; + py_java_object->object = object; + + return nanobind::cast(py_java_object); + } + return nanobind::none(); +} + +nanobind::object python_create_object(PyJavaArray* self, jobject object) noexcept +{ + if (object) + { + auto py_java_object = new PyJavaObject(); + py_java_object->eios = self->eios; + py_java_object->object = object; + + return nanobind::cast(py_java_object); + } + return nanobind::none(); +} + +nanobind::object python_create_array(PyEIOS* self, jarray array, std::size_t array_size) noexcept +{ + if (array) + { + auto py_java_array = new PyJavaArray(); + py_java_array->eios = self; + py_java_array->array = array; + py_java_array->size = array_size; + + return nanobind::cast(py_java_array); + } + return nanobind::none(); +} + +nanobind::object python_create_array(PyJavaObject* self, jarray array, std::size_t array_size) noexcept +{ + if (array) + { + auto py_java_array = new PyJavaArray(); + py_java_array->eios = self->eios; + py_java_array->array = array; + py_java_array->size = array_size; + + return nanobind::cast(py_java_array); + } + return nanobind::none(); +} + +nanobind::object python_create_array(PyJavaArray* self, jarray array, std::size_t array_size) noexcept +{ + if (array) + { + auto py_java_array = new PyJavaArray(); + py_java_array->eios = self->eios; + py_java_array->array = array; + py_java_array->size = array_size; + + return nanobind::cast(py_java_array); + } + return nanobind::none(); +} + +PyEIOS::~PyEIOS() +{ + if (native_eios) + { + EIOS_ReleaseTarget(this->native_eios); + this->native_eios = nullptr; + } +} + +PyJavaObject::~PyJavaObject() +{ + if (this->eios && this->object) + { + this->eios->native_eios->control_center->reflect_release_object(this->object); + this->object = nullptr; + this->eios = nullptr; + this->object = nullptr; + } +} + +PyJavaArray::~PyJavaArray() +{ + if (this->eios && this->array) + { + this->eios->native_eios->control_center->reflect_release_object(this->array); + this->eios = nullptr; + this->array = nullptr; + this->size = 0; + } +} + +template +PyTypeObject* PyTypeFromType() noexcept +{ + static PyTypeObject* type = nullptr; + if (!type) + { + type = Py_TYPE(nanobind::cast(T()).ptr()); + return type; + } + return nullptr; +} + +PyRemoteInputType GetPythonObjectType(PyObject* object) noexcept +{ + if (Py_IS_TYPE(object, PyTypeFromType())) + { + return PyRemoteInputType::EIOS; + } + + if (Py_IS_TYPE(object, PyTypeFromType())) + { + return PyRemoteInputType::JAVA_OBJECT; + } + + if (Py_IS_TYPE(object, PyTypeFromType())) + { + return PyRemoteInputType::JAVA_ARRAY; + } + + return PyRemoteInputType::OTHER; +} + +//PyRemoteInputType GetPythonObjectType(PyObject* object) noexcept +//{ +// if (nanobind::isinstance(object)) +// { +// return PyRemoteInputType::EIOS; +// } +// +// if (nanobind::isinstance(object)) +// { +// return PyRemoteInputType::JAVA_OBJECT; +// } +// +// if (nanobind::isinstance(object)) +// { +// return PyRemoteInputType::JAVA_ARRAY; +// } +// +// return PyRemoteInputType::OTHER; +//} +#else +PyRemoteInputType GetPythonObjectType(PyObject* object) noexcept +{ + if ((python->Py_IS_TYPE)(object, PyEIOS_Type())) + { + return PyRemoteInputType::EIOS; + } + + if ((python->Py_IS_TYPE)(object, PyJavaObject_Type())) + { + return PyRemoteInputType::JAVA_OBJECT; + } + + if ((python->Py_IS_TYPE)(object, PyJavaArray_Type())) + { + return PyRemoteInputType::JAVA_ARRAY; + } + + return PyRemoteInputType::OTHER; +} + +PyObject* PythonWrapEIOS(EIOS* eios) noexcept +{ + if (eios) + { + PyEIOS* py_eios = (python->PyObject_New)(PyEIOS_Type()); + py_eios->pid = eios->pid; + py_eios->native_eios = eios; + return reinterpret_cast(py_eios); + } + + (python->Py_INCREF)(python->Py_GetNone_Object()); + return python->Py_GetNone_Object(); +} + +EIOS* PythonUnwrapEIOS(PyEIOS* eios) noexcept +{ + if (eios) + { + return eios->native_eios; + } + return nullptr; +} + +PyEIOS* PythonGetEIOS(PyObject* object) +{ + PyRemoteInputType type = GetPythonObjectType(object); + switch (type) + { + case PyRemoteInputType::EIOS: + return reinterpret_cast(object); + + case PyRemoteInputType::JAVA_OBJECT: + return reinterpret_cast(object)->eios; + + case PyRemoteInputType::JAVA_ARRAY: + return reinterpret_cast(object)->eios; + + default: + return nullptr; + } +} + +PyJavaObject* PythonWrapJavaObject(PyEIOS* eios, jobject object) noexcept +{ + if (object) + { + PyTypeObject* PyJavaObjectType = PyJavaObject_Type(); + PyJavaObject* py_java_object = (python->PyObject_New)(PyJavaObjectType); + py_java_object->eios = eios; + py_java_object->object = object; + (python->Py_INCREF)(reinterpret_cast(eios)); + return py_java_object; + } + + (python->Py_INCREF)(python->Py_GetNone_Object()); + return reinterpret_cast(python->Py_GetNone_Object()); +} + +jobject PythonUnwrapJavaObject(PyJavaObject* object) noexcept +{ + if (!object) + { + return nullptr; + } + + if (GetPythonObjectType(reinterpret_cast(object)) != PyRemoteInputType::JAVA_OBJECT) + { + return nullptr; + } + + return object->object; +} + +PyJavaArray* PythonWrapJavaArray(PyEIOS* eios, jarray array, std::size_t size) noexcept +{ + if (array) + { + PyTypeObject* PyJavaArrayType = PyJavaArray_Type(); + PyJavaArray* py_java_array = (python->PyObject_New)(PyJavaArrayType); + py_java_array->eios = eios; + py_java_array->array = array; + py_java_array->size = size; + (python->Py_INCREF)(reinterpret_cast(eios)); + return py_java_array; + } + + (python->Py_INCREF)(python->Py_GetNone_Object()); + return reinterpret_cast(python->Py_GetNone_Object()); +} + +jarray PythonUnwrapJavaArray(PyJavaArray* array) noexcept +{ + if (!array) + { + return nullptr; + } + + if (GetPythonObjectType(reinterpret_cast(array)) != PyRemoteInputType::JAVA_ARRAY) + { + return nullptr; + } + + return array->array; +} +#endif \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Plugin/Python/PythonCommon.hxx b/third-party/RemoteInput/RemoteInput/Plugin/Python/PythonCommon.hxx new file mode 100644 index 0000000..5285d00 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Plugin/Python/PythonCommon.hxx @@ -0,0 +1,123 @@ +// +// Created by Brandon on 2022-09-21. +// + +#ifndef REMOTEINPUT_PYTHONCOMMON_HXX +#define REMOTEINPUT_PYTHONCOMMON_HXX + +#if defined(USE_PYBIND11) + #include + #include + #include + #include + #include +#else + #include + #include + + #include "PythonMacros.hxx" + #include "TypeTraits.hxx" +#endif + +#include "JNI_Common.hxx" +#include "EIOS.hxx" + +// STRUCTURES +enum class PyRemoteInputType +{ + EIOS, + JAVA_OBJECT, + JAVA_ARRAY, + OTHER +}; + +struct PyEIOS +{ + #if !defined(USE_PYBIND11) + PyObject_HEAD + #endif + + std::int32_t pid; + EIOS* native_eios; + + #if defined(USE_PYBIND11) + ~PyEIOS(); + #endif +}; + +struct PyJavaObject +{ + #if defined(USE_PYBIND11) + PyEIOS* eios; + jobject object; + #else + PyObject_HEAD + PyEIOS* eios; + jobject object; + #endif + + #if defined(USE_PYBIND11) + ~PyJavaObject(); + #endif +}; + +struct PyJavaArray +{ + #if defined(USE_PYBIND11) + PyEIOS* eios; + jarray array; + std::size_t size; + #else + PyObject_HEAD + PyEIOS* eios; + jarray array; + std::size_t size; + #endif + + #if defined(USE_PYBIND11) + ~PyJavaArray(); + #endif +}; + +#if defined(USE_PYBIND11) +nanobind::object python_create_eios(EIOS* eios) noexcept; +nanobind::object python_create_object(PyEIOS* self, jobject object) noexcept; +nanobind::object python_create_object(PyJavaObject* self, jobject object) noexcept; +nanobind::object python_create_object(PyJavaArray* self, jobject object) noexcept; +nanobind::object python_create_array(PyEIOS* self, jarray array, std::size_t array_size) noexcept; +nanobind::object python_create_array(PyJavaObject* self, jarray array, std::size_t array_size) noexcept; +nanobind::object python_create_array(PyJavaArray* self, jarray array, std::size_t array_size) noexcept; +PyRemoteInputType GetPythonObjectType(PyObject* object) noexcept; +#else +extern PyTypeObject* PyEIOS_Type() noexcept; +extern PyTypeObject* PyJavaObject_Type() noexcept; +extern PyTypeObject* PyJavaArray_Type() noexcept; + +PyRemoteInputType GetPythonObjectType(PyObject* object) noexcept; + +PyObject* PythonWrapEIOS(EIOS* eios) noexcept; +EIOS* PythonUnwrapEIOS(PyEIOS* eios) noexcept; +PyEIOS* PythonGetEIOS(PyObject* object); + +PyJavaObject* PythonWrapJavaObject(PyEIOS* eios, jobject object) noexcept; +jobject PythonUnwrapJavaObject(PyJavaObject* object) noexcept; + +PyJavaArray* PythonWrapJavaArray(PyEIOS* eios, jarray array, std::size_t size) noexcept; +jarray PythonUnwrapJavaArray(PyJavaArray* array) noexcept; + +template +T from_python_object(PyObject* object); + +template +std::vector from_python_array(PyObject* object); + +template +PyObject* to_python_object(const T& value); + +template +PyObject* to_python_array(const std::vector& value); + +#include "PythonCommon_Templates.hxx" +#endif + +#endif //REMOTEINPUT_PYTHONCOMMON_HXX diff --git a/third-party/RemoteInput/RemoteInput/Plugin/Python/PythonCommon_Templates.hxx b/third-party/RemoteInput/RemoteInput/Plugin/Python/PythonCommon_Templates.hxx new file mode 100644 index 0000000..740fb39 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Plugin/Python/PythonCommon_Templates.hxx @@ -0,0 +1,515 @@ +// +// Created by Brandon on 2022-09-21. +// + +#ifndef REMOTEINPUT_PYTHONCOMMON_TEMPLATES_HXX +#define REMOTEINPUT_PYTHONCOMMON_TEMPLATES_HXX + +#include "Python.hxx" + +extern std::unique_ptr python; + +#if !defined(METH_FASTCALL) +// Non-Keywords Args + +template +struct PyFunctionType +{ + typedef typename std::tuple_element<0, typename function_arguments_type::type>::type self_type; + typedef PyObject* (*fn_type)(self_type, PyObject*[], Py_ssize_t) noexcept; + typedef PyObject* (*fn_keyword_type)(self_type, PyObject*[], Py_ssize_t, PyObject*) noexcept; +}; + +template +typename std::enable_if::fn_type + >::value, PyObject*>::type +python_fastcall(PyObject* self, PyObject* args) +{ + #if defined(Py_LIMITED_API) + typedef struct + { + PyObject_VAR_HEAD + PyObject *ob_item[1]; + } PyTupleObject; + #endif + + typedef typename PyFunctionType::self_type self_type; + return fn(reinterpret_cast(self), + reinterpret_cast(args)->ob_item, + python->PyTuple_Size(args)); +} + +// Keyword Args + +template +typename std::enable_if::fn_keyword_type + >::value, PyObject*>::type +python_fastcall(PyObject* self, PyObject* args, PyObject* kwnames) +{ + #if defined(Py_LIMITED_API) + typedef struct + { + PyObject_VAR_HEAD + PyObject *ob_item[1]; + } PyTupleObject; + #endif + + Py_ssize_t args_size = python->PyTuple_Size(args); + Py_ssize_t keywords_size = python->PyDict_Size(kwnames); + PyObject* keys = python->PyList_AsTuple(python->PyDict_Keys(kwnames)); + PyObject* values = python->PyDict_Values(kwnames); + + std::vector arguments; + arguments.reserve(args_size + keywords_size); + for (std::size_t i = 0; i < arguments.size(); ++i) + { + arguments.push_back(reinterpret_cast(args)->ob_item[i]); + } + + for (Py_ssize_t i = 0; i < keywords_size; ++i) + { + arguments.push_back(python->PyList_GetItem(values, i)); + } + + typedef typename PyFunctionType::self_type self_type; + return fn(reinterpret_cast(self), + &arguments[0], + args_size, + keys); +} + +#define PYTHON_FASTCALL(f) (PyCFunction)python_fastcall +#define METH_FASTCALL METH_VARARGS +#else +#define PYTHON_FASTCALL(f) reinterpret_cast(f) +#endif + +template +bool type_python_is_valid(PyObject* object, std::string& error) +{ + auto python_object_name = [](PyObject* object) -> std::string { + #if defined(Py_LIMITED_API) && Py_LIMITED_API >= 0x030B1000 + PyObject* name = python->PyType_GetName(reinterpret_cast(python->PyObject_Type(object))); + Py_ssize_t size = 0; + const char* buffer = python->PyUnicode_AsUTF8AndSize(name, &size); + if (buffer && size > 0) + { + return std::string(buffer, size); + } + return "UNKNOWN TYPE"; + #elif defined(Py_LIMITED_API) && Py_LIMITED_API >= 0x03020000 + PyObject* name = python->PyObject_GetAttrString(object, "__name__"); + if (name) + { + PyObject* utf8_string = python->PyUnicode_AsUTF8String(name); + if (utf8_string) + { + Py_ssize_t size = 0; + char* buffer = nullptr; + python->PyBytes_AsStringAndSize(utf8_string, &buffer, &size); + return std::string(buffer, size);; + } + } + return "UNKNOWN TYPE"; + #elif !defined(Py_LIMITED_API) + return python->_PyType_Name(reinterpret_cast(python->PyObject_Type(object))); + #else + PyObject* name = python->PyObject_GetAttrString(object, "__name__"); + if (name) + { + Py_ssize_t size = 0; + const char* buffer = python->PyUnicode_AsUTF8AndSize(name, &size); + if (buffer && size > 0) + { + return std::string(buffer, size); + } + } + return "UNKNOWN TYPE"; + #endif + }; + + if constexpr(std::is_same::value) + { + error = "Expected Boolean Type. Received: "; + error += python_object_name(object); + return PyBool_Check(object); + } + else if constexpr(std::is_same::value) + { + error = "Expected Char Type. Received: "; + error += python_object_name(object); + return PyUnicode_CheckExact(object); + } + else if constexpr(is_same_of::value) + { + error = "Expected Int/Long Type. Received: "; + error += python_object_name(object); + return PyLong_CheckExact(object); + } + else if constexpr(is_same_of::value) + { + error = "Expected Unsigned Int/Long Type. Received: "; + error += python_object_name(object); + return PyLong_CheckExact(object); + } + else if constexpr(is_same_of::value) + { + error = "Expected Float/Double Type. Received: "; + error += python_object_name(object); + return PyFloat_Check(object); + } + else if constexpr(is_same_of::value) + { + error = "Expected String Type. Received: "; + error += python_object_name(object); + return PyUnicode_CheckExact(object); + } + else if constexpr(std::is_same>::value) + { + error = "Expected List of bytes Type. Received: "; + error += python_object_name(object); + return PyObject_TypeCheck(object, &PyList_Type); + } + else if constexpr(std::is_same::value) + { + error = "Expected EIOS Type. Received: "; + error += python_object_name(object); + #if defined(Py_LIMITED_API) + return python->PyObject_IsInstance(object, reinterpret_cast(PyEIOS_Type())); + #else + return python->PyObject_TypeCheck(object, PyEIOS_Type()); + #endif + } + else if constexpr(std::is_same::value) + { + #if defined(Py_LIMITED_API) && Py_LIMITED_API >= 0x030A0000 + if ((python->Py_IsNone)(object)) + { + return true; + } + #else + if (object == Py_None) + { + return true; + } + #endif + + error = "Expected JavaObject Type. Received: "; + error += python_object_name(object); + #if defined(Py_LIMITED_API) + return python->PyObject_IsInstance(object, reinterpret_cast(PyJavaObject_Type())); + #else + return python->PyObject_TypeCheck(object, PyJavaObject_Type()); + #endif + } + else if constexpr(std::is_same>::value) + { + if ((python->PyList_Check)(object)) + { + PyObject *iter = python->PyObject_GetIter(object); + if (!iter) + { + return false; + } + + while (PyObject* next = python->PyIter_Next(iter)) + { + if (!type_python_is_valid(next, error)) + { + return false; + } + } + return true; + } + + error = "Expected Array of JavaObject. Received: "; + error += python_object_name(object); + return false; + } + else if constexpr(std::is_void::value) + { + error = "Expected Void Type. Received: "; + error += python_object_name(object); + static_assert(!std::is_void::value, "INVALID TYPE: VOID!"); + return false; + } + else if constexpr(std::is_pointer::value) + { + error = "Expected Pointer Type. Received: "; + error += python_object_name(object); + static_assert(!std::is_pointer::value, "INVALID TYPE: POINTER!"); + return false; + } + else if constexpr(std::is_reference::value) + { + error = "Expected Reference Type. Received: "; + error += python_object_name(object); + static_assert(!std::is_reference::value, "INVALID TYPE: REFERENCE!"); + return false; + } + else + { + error = "Expected ??? Type. Received: "; + error += python_object_name(object); + static_assert(!std::is_pointer::value && !std::is_reference::value, "INVALID TYPE!"); + return false; + } +} + +template +T from_python_object(PyObject* object) +{ + if constexpr(std::is_same::value) + { + return python->PyLong_AsLong(object); + } + else if constexpr(std::is_same::value) + { + #if defined(Py_LIMITED_API) && Py_LIMITED_API >= 0x030A0000 + Py_ssize_t size = 0; + const char* buffer = python->PyUnicode_AsUTF8AndSize(object, &size); + if (buffer && size == 1) + { + return *buffer; + } + return -1; + // return *PyUnicode_AsUTF8(object); + #else + PyObject* utf8_string = python->PyUnicode_AsUTF8String(object); + if (utf8_string) + { + Py_ssize_t size = 0; + char* buffer = nullptr; + python->PyBytes_AsStringAndSize(utf8_string, &buffer, &size); + (python->Py_XDECREF)(utf8_string); + return *buffer; + } + return -1; + #endif + } + if constexpr(is_same_of::value) + { + return python->PyLong_AsLong(object); + } + else if constexpr(std::is_same::value) + { + return python->PyLong_AsLongLong(object); + } + else if constexpr(is_same_of::value) + { + return python->PyLong_AsUnsignedLong(object); + } + else if constexpr(std::is_same::value) + { + return python->PyLong_AsUnsignedLongLong(object); + } + else if constexpr(std::is_same::value) + { + return python->PyLong_AsSize_t(object); + } + else if constexpr(is_same_of::value) + { + return static_cast(python->PyFloat_AsDouble(object)); + } + else if constexpr(std::is_same::value) + { + #if defined(Py_LIMITED_API) && Py_LIMITED_API >= 0x030A0000 + Py_ssize_t size = 0; + const char* buffer = python->PyUnicode_AsUTF8AndSize(object, &size); + if (buffer && size > 0) + { + return std::string(buffer, size); + } + return std::string(); + //PyBytes_AsStringAndSize(object, &buffer, &size) != -1; + #else + PyObject* utf8_string = python->PyUnicode_AsUTF8String(object); + if (utf8_string) + { + Py_ssize_t size = 0; + char* buffer = nullptr; + python->PyBytes_AsStringAndSize(utf8_string, &buffer, &size); + (python->Py_XDECREF)(utf8_string); + return std::string(buffer, size); + } + return std::string(); + #endif + } + else if constexpr(std::is_same::value) + { + #if defined(Py_LIMITED_API) && Py_LIMITED_API >= 0x030A0000 + return python->PyUnicode_AsUTF8AndSize(object, nullptr); + #else + PyObject* utf8_string = python->PyUnicode_AsUTF8String(object); + if (utf8_string) + { + Py_ssize_t size = 0; + char* buffer = nullptr; + python->PyBytes_AsStringAndSize(utf8_string, &buffer, &size); + (python->Py_XDECREF)(utf8_string); + return buffer; + } + return nullptr; + #endif + } + else if constexpr(std::is_same>::value) + { + char* buffer = nullptr; + Py_ssize_t size = 0; + if (python->PyBytes_AsStringAndSize(object, &buffer, &size) != -1) + { + return std::vector(buffer, buffer + size); + } + return std::vector(); + } + else if constexpr(std::is_same::value) + { + return PythonUnwrapEIOS(reinterpret_cast(object)); + } + else if constexpr(std::is_same::value) + { + return PythonUnwrapJavaObject(reinterpret_cast(object)); + } + else if constexpr(std::is_same::value) + { + return PythonUnwrapJavaArray(reinterpret_cast(object)); + } + else if constexpr(std::is_enum::value) + { + return static_cast(from_python_object::type>(object)); + } + else if constexpr(std::is_void::value) + { + static_assert(!std::is_void::value, "INVALID TYPE: VOID!"); + return nullptr; + } + else if constexpr(std::is_pointer::value) + { + static_assert(!std::is_pointer::value, "INVALID TYPE: POINTER!"); + return nullptr; + } + else if constexpr(std::is_reference::value) + { + static_assert(!std::is_reference::value, "INVALID TYPE: REFERENCE!"); + return nullptr; + } + else + { + static_assert(!std::is_pointer::value && !std::is_reference::value, "INVALID TYPE!"); + return nullptr; + } +} + +template +std::vector from_python_array(PyObject* object) +{ + std::vector result; + if ((python->PyList_Check)(object)) + { + PyObject *iter = python->PyObject_GetIter(object); + if (!iter) + { + return result; + } + + while (PyObject* next = python->PyIter_Next(iter)) + { + if constexpr(std::is_same::value) + { + result.push_back(reinterpret_cast(next)); + } + else if constexpr(std::is_same::value) + { + result.push_back(reinterpret_cast(next)); + } + else + { + result.push_back(from_python_object(next)); + } + } + + (python->Py_DECREF)(iter); + } + return result; +} + +template +PyObject* to_python_object(const T& value) +{ + if constexpr(is_same_of::value) + { + return python->PyLong_FromLong(value); + } + else if constexpr(std::is_same::value) + { + return python->PyLong_FromLongLong(value); + } + else if constexpr(is_same_of::value) + { + return python->PyLong_FromUnsignedLong(value); + } + else if constexpr(std::is_same::value) + { + return python->PyLong_FromUnsignedLongLong(value); + } + else if constexpr(std::is_same::value) + { + return python->PyLong_FromSize_t(value); + } + else if constexpr(is_same_of::value) + { + return python->PyFloat_FromDouble(value); + } + else if constexpr(std::is_same::value) + { + return python->PyUnicode_FromStringAndSize(value.c_str(), value.size()); + } + else if constexpr(std::is_same::value) + { + if (value) + { + return python->PyUnicode_FromString(value); + } + + (python->Py_INCREF)(python->Py_GetNone_Object()); + return python->Py_GetNone_Object(); + } + else if constexpr(is_same_of::value) + { + return PythonWrapEIOS(value); + } + else if constexpr(std::is_enum::value) + { + typedef typename std::underlying_type::type type; + return to_python_object(static_cast(value)); + } + else + { + static_assert("INVALID TYPE!"); + return nullptr; + } +} + +template +PyObject* to_python_array(const std::vector& values) +{ + PyObject* result = python->PyList_New(values.size()); + for (std::size_t i = 0; i < values.size(); ++i) + { + if constexpr(is_same_of::value) + { + python->PyList_SetItem(result, i, values[i]); + } + else + { + python->PyList_SetItem(result, i, to_python_object(values[i])); + } + } + return result; +} + +#endif //REMOTEINPUT_PYTHONCOMMON_TEMPLATES_HXX \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Plugin/Python/PythonEIOS.cxx b/third-party/RemoteInput/RemoteInput/Plugin/Python/PythonEIOS.cxx new file mode 100644 index 0000000..51ed6e2 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Plugin/Python/PythonEIOS.cxx @@ -0,0 +1,1377 @@ +// +// Created by Brandon on 2022-09-21. +// + +#include "PythonEIOS.hxx" +#include "NativePlugin.hxx" +#include "PythonJavaObject.hxx" + +#include +#include +#include +#include + +#if defined(USE_PYBIND11) +void Python_EIOS_Inject(const std::string& process_name) noexcept +{ + EIOS_Inject(process_name.c_str()); +} + +void Python_EIOS_Inject_PID(std::int32_t pid) noexcept +{ + EIOS_Inject_PID(pid); +} + +nanobind::object Python_EIOS_From_PID(std::int32_t pid) noexcept +{ + return python_create_eios(EIOS_From_PID(pid)); +} + +nanobind::object Python_EIOS_RequestTarget(const std::string& pid) noexcept +{ + return python_create_eios(EIOS_RequestTarget(pid.c_str())); +} + +nanobind::object Python_EIOS_GetTargetDimensions(PyEIOS* self) noexcept +{ + std::int32_t width = 0; + std::int32_t height = 0; + EIOS_GetTargetDimensions(self->native_eios, &width, &height); + return nanobind::make_tuple(width, height); +} + +nanobind::object Python_EIOS_GetImageFormat(PyEIOS* self) noexcept +{ + ImageFormat format = EIOS_GetImageFormat(self->native_eios); + return nanobind::cast(format); +} + +void Python_EIOS_SetImageFormat(PyEIOS* self, ImageFormat format) noexcept +{ + EIOS_SetImageFormat(self->native_eios, format); +} + +nanobind::object Python_EIOS_GetImageBuffer(PyEIOS* self) noexcept +{ + std::int32_t width = 0; + std::int32_t height = 0; + EIOS_GetTargetDimensions(self->native_eios, &width, &height); + std::uint8_t* buffer = EIOS_GetImageBuffer(self->native_eios); + return nanobind::steal(PyMemoryView_FromMemory(reinterpret_cast(buffer), width * height * 4 * sizeof(std::uint8_t), PyBUF_READ)); +} + +nanobind::object Python_EIOS_GetDebugImageBuffer(PyEIOS* self) noexcept +{ + std::int32_t width = 0; + std::int32_t height = 0; + EIOS_GetTargetDimensions(self->native_eios, &width, &height); + std::uint8_t* buffer = EIOS_GetDebugImageBuffer(self->native_eios); + return nanobind::steal(PyMemoryView_FromMemory(reinterpret_cast(buffer), width * height * 4 * sizeof(std::uint8_t), PyBUF_WRITE)); +} + +void Python_EIOS_SetGraphicsDebugging(PyEIOS* self, bool enabled) noexcept +{ + EIOS_SetGraphicsDebugging(self->native_eios, enabled); +} + +nanobind::object Python_EIOS_HasFocus(PyEIOS* self) noexcept +{ + return nanobind::bool_(EIOS_HasFocus(self->native_eios)); +} + +void Python_EIOS_GainFocus(PyEIOS* self) noexcept +{ + EIOS_GainFocus(self->native_eios); +} + +void Python_EIOS_LoseFocus(PyEIOS* self) noexcept +{ + EIOS_LoseFocus(self->native_eios); +} + +nanobind::object Python_EIOS_IsKeyboardInputEnabled(PyEIOS* self) noexcept +{ + return nanobind::bool_(EIOS_IsKeyboardInputEnabled(self->native_eios)); +} + +void Python_EIOS_SetKeyboardInputEnabled(PyEIOS* self, bool enabled) noexcept +{ + EIOS_SetKeyboardInputEnabled(self->native_eios, enabled); +} + +nanobind::object Python_EIOS_IsMouseInputEnabled(PyEIOS* self) noexcept +{ + return nanobind::bool_(EIOS_IsMouseInputEnabled(self->native_eios)); +} + +void Python_EIOS_SetMouseInputEnabled(PyEIOS* self, bool enabled) noexcept +{ + EIOS_SetMouseInputEnabled(self->native_eios, enabled); +} + +nanobind::object Python_EIOS_GetMousePosition(PyEIOS* self) noexcept +{ + std::int32_t width = 0; + std::int32_t height = 0; + EIOS_GetMousePosition(self->native_eios, &width, &height); + return nanobind::make_tuple(width, height); +} + +nanobind::object Python_EIOS_GetRealMousePosition(PyEIOS* self) noexcept +{ + std::int32_t width = 0; + std::int32_t height = 0; + EIOS_GetRealMousePosition(self->native_eios, &width, &height); + return nanobind::make_tuple(width, height); +} + +void Python_EIOS_MoveMouse(PyEIOS* self, std::int32_t x, std::int32_t y) noexcept +{ + EIOS_MoveMouse(self->native_eios, x, y); +} + +void Python_EIOS_HoldMouse(PyEIOS* self, std::int32_t button) noexcept +{ + EIOS_HoldMouse(self->native_eios, 0, 0, button); +} + +void Python_EIOS_ReleaseMouse(PyEIOS* self, std::int32_t button) noexcept +{ + EIOS_ReleaseMouse(self->native_eios, 0, 0, button); +} + +void Python_EIOS_ScrollMouse(PyEIOS* self, std::int32_t lines) noexcept +{ + EIOS_ScrollMouse(self->native_eios, 0, 0, lines); +} + +nanobind::object Python_EIOS_IsMouseButtonHeld(PyEIOS* self, std::int32_t button) noexcept +{ + return nanobind::bool_(EIOS_IsMouseButtonHeld(self->native_eios, button)); +} + +void Python_EIOS_SendString(PyEIOS* self, const std::string& text, std::int32_t key_wait, std::int32_t key_mod_wait) noexcept +{ + EIOS_SendString(self->native_eios, text.c_str(), key_wait, key_mod_wait); +} + +void Python_EIOS_HoldKey(PyEIOS* self, std::int32_t key) noexcept +{ + EIOS_HoldKey(self->native_eios, key); +} + +void Python_EIOS_ReleaseKey(PyEIOS* self, std::int32_t key) noexcept +{ + EIOS_ReleaseKey(self->native_eios, key); +} + +nanobind::object Python_EIOS_IsKeyHeld(PyEIOS* self, std::int32_t key) noexcept +{ + return nanobind::bool_(EIOS_IsKeyHeld(self->native_eios, key)); +} + +nanobind::object Python_EIOS_GetKeyboardSpeed(PyEIOS* self) noexcept +{ + return nanobind::int_(EIOS_GetKeyboardSpeed(self->native_eios)); +} + +void Python_EIOS_SetKeyboardSpeed(PyEIOS* self, std::int32_t speed) noexcept +{ + EIOS_SetKeyboardSpeed(self->native_eios, speed); +} + +nanobind::object Python_EIOS_GetKeyboardRepeatDelay(PyEIOS* self) noexcept +{ + return nanobind::int_(EIOS_GetKeyboardRepeatDelay(self->native_eios)); +} + +void Python_EIOS_SetKeyboardRepeatDelay(PyEIOS* self, std::int32_t delay) noexcept +{ + EIOS_SetKeyboardRepeatDelay(self->native_eios, delay); +} + +nanobind::object Python_EIOS_GetClientsPIDs(bool unpaired_only) noexcept +{ + std::size_t client_count = EIOS_GetClients(unpaired_only); + std::vector client_pids(client_count); + + for (std::size_t i = 0; i < client_count; ++i) + { + client_pids[i] = EIOS_GetClientPID(i); + } + return nanobind::cast(client_pids); +} + +nanobind::object Python_EIOS_PairClient_PID(std::int32_t pid) noexcept +{ + return python_create_eios(EIOS_PairClient(pid)); +} + +void Python_EIOS_KillClientPID(std::int32_t pid) noexcept +{ + EIOS_KillClientPID(pid); +} + +void Python_EIOS_KillClient(PyEIOS* self) noexcept +{ + EIOS_KillClient(self->native_eios); +} + +nanobind::object Python_EIOS_Reflect_Object(PyEIOS* self, const std::string& cls, const std::string& field, const std::string& desc) noexcept +{ + EIOS* eios = self->native_eios; + jobject result = eios->control_center->reflect_object({nullptr, cls, field, desc}); + return python_create_object(self, result); +} + +nanobind::object Python_EIOS_Reflect_Bool(PyEIOS* self, const std::string& cls, const std::string& field) noexcept +{ + EIOS* eios = self->native_eios; + jboolean result = eios->control_center->reflect_boolean({nullptr, cls, field, "Z"}); + return nanobind::bool_(result); +} + +nanobind::object Python_EIOS_Reflect_Char(PyEIOS* self, const std::string& cls, const std::string& field) noexcept +{ + EIOS* eios = self->native_eios; + char result = eios->control_center->reflect_char({nullptr, cls, field, "C"}); + return nanobind::cast(std::string(1, result)); +} + +nanobind::object Python_EIOS_Reflect_Byte(PyEIOS* self, const std::string& cls, const std::string& field) noexcept +{ + EIOS* eios = self->native_eios; + std::uint8_t result = eios->control_center->reflect_byte({nullptr, cls, field, "B"}); + return nanobind::int_(result); +} + +nanobind::object Python_EIOS_Reflect_Short(PyEIOS* self, const std::string& cls, const std::string& field) noexcept +{ + EIOS* eios = self->native_eios; + std::int16_t result = eios->control_center->reflect_short({nullptr, cls, field, "S"}); + return nanobind::int_(result); +} + +nanobind::object Python_EIOS_Reflect_Int(PyEIOS* self, const std::string& cls, const std::string& field) noexcept +{ + EIOS* eios = self->native_eios; + std::int32_t result = eios->control_center->reflect_int({nullptr, cls, field, "I"}); + return nanobind::int_(result); +} + +nanobind::object Python_EIOS_Reflect_Long(PyEIOS* self, const std::string& cls, const std::string& field) noexcept +{ + EIOS* eios = self->native_eios; + std::int64_t result = eios->control_center->reflect_long({nullptr, cls, field, "J"}); + return nanobind::int_(result); +} + +nanobind::object Python_EIOS_Reflect_Float(PyEIOS* self, const std::string& cls, const std::string& field) noexcept +{ + EIOS* eios = self->native_eios; + float result = eios->control_center->reflect_float({nullptr, cls, field, "F"}); + return nanobind::float_(result); +} + +nanobind::object Python_EIOS_Reflect_Double(PyEIOS* self, const std::string& cls, const std::string& field) noexcept +{ + EIOS* eios = self->native_eios; + double result = eios->control_center->reflect_double({nullptr, cls, field, "D"}); + return nanobind::float_(result); +} + +nanobind::object Python_EIOS_Reflect_String(PyEIOS* self, const std::string& cls, const std::string& field) noexcept +{ + EIOS* eios = self->native_eios; + std::string result = eios->control_center->reflect_string({nullptr, cls, field, "Ljava/lang/String;"}); + return nanobind::cast(result); +} + +nanobind::object Python_EIOS_Reflect_Array(PyEIOS* self, const std::string& cls, const std::string& field, const std::string& desc) noexcept +{ + EIOS* eios = self->native_eios; + std::size_t array_size = 0; + jarray array = eios->control_center->reflect_array({nullptr, cls, field, desc}, &array_size); + return python_create_array(self, array, array_size); +} + +void Python_Reflect_Release_Objects(PyEIOS* self, const nanobind::object& object) noexcept +{ + // Flatten the List + std::stack stack; + std::vector objects; + stack.push(object); + + while (!stack.empty()) + { + nanobind::handle current = stack.top(); + stack.pop(); + + if (nanobind::isinstance(current)) + { + for (auto item : nanobind::cast(current)) + { + stack.push(item); + } + } + else + { + if (Py_REFCNT(current.ptr()) == 1) + { + objects.push_back(current); + } + } + } + + // Early exit if no objects to free + if (objects.empty()) + { + return; + } + + // Unwrap each object and clear as we go along to prevent double-free + std::vector result; + result.reserve(objects.size()); + EIOS* eios = self ? self->native_eios : nullptr; + + for (auto& handle : objects) + { + if (nanobind::isinstance(handle)) + { + auto* java_object = nanobind::cast(handle); + if (!eios) + { + eios = java_object->eios->native_eios; + } + + result.push_back(java_object->object); + handle.dec_ref(); + } + else if (nanobind::isinstance(handle)) + { + auto* java_array = nanobind::cast(handle); + if (!eios) + { + eios = java_array->eios->native_eios; + } + + result.push_back(java_array->array); + handle.dec_ref(); + } + } + + // Release all objects at once + Reflect_Release_Objects(eios, &result[0], result.size()); +} + +nanobind::object PyEIOS_Str(PyEIOS* self) +{ + std::ostringstream stream; + + std::ios state(nullptr); + state.copyfmt(stream); + + stream << std::setfill('0') << std::uppercase << std::hex; + stream << "EIOS("; + stream << "0x" << reinterpret_cast(self); + stream << "): "; + stream.copyfmt(state); + + stream << "{"<<"\n"; + stream<< " pid: " << self->pid << "\n"; + stream<< "}"; + + return nanobind::cast(stream.str()); +} + +void declare_python_eios(nanobind::module_ &module) +{ + nanobind::class_(module, "EIOS") + // .def(nanobind::init<>()) // Cannot instantiate from Python + .def_ro("pid", &PyEIOS::pid) + .def_ro("native_eios", &PyEIOS::native_eios) + .def_static("inject", &Python_EIOS_Inject) + .def_static("inject_pid", &Python_EIOS_Inject_PID) + .def_static("from_pid", &Python_EIOS_From_PID) + .def_static("request_target", &Python_EIOS_RequestTarget) + .def("get_target_dimensions", &Python_EIOS_GetTargetDimensions) + .def("get_image_format", &Python_EIOS_GetImageFormat) + .def("set_image_format", &Python_EIOS_SetImageFormat) + .def("get_image_buffer", &Python_EIOS_GetImageBuffer) + .def("get_debug_image_buffer", &Python_EIOS_GetDebugImageBuffer) + .def("set_graphics_debugging", &Python_EIOS_SetGraphicsDebugging) + .def("has_focus", &Python_EIOS_HasFocus) + .def("gain_focus", &Python_EIOS_GainFocus) + .def("lose_focus", &Python_EIOS_LoseFocus) + .def("is_keyboard_input_enabled", &Python_EIOS_IsKeyboardInputEnabled) + .def("set_keyboard_input_enabled", &Python_EIOS_SetKeyboardInputEnabled) + .def("is_mouse_input_enabled", &Python_EIOS_IsMouseInputEnabled) + .def("set_mouse_input_enabled", &Python_EIOS_SetMouseInputEnabled) + .def("get_mouse_position", &Python_EIOS_GetMousePosition) + .def("get_real_mouse_position", &Python_EIOS_GetRealMousePosition) + .def("move_mouse", &Python_EIOS_MoveMouse) + .def("hold_mouse", &Python_EIOS_HoldMouse) + .def("release_mouse", &Python_EIOS_ReleaseMouse) + .def("scroll_mouse", &Python_EIOS_ScrollMouse) + .def("is_mouse_button_held", &Python_EIOS_IsMouseButtonHeld) + .def("send_string", &Python_EIOS_SendString) + .def("hold_key", &Python_EIOS_HoldKey) + .def("release_key", &Python_EIOS_ReleaseKey) + .def("is_key_held", &Python_EIOS_IsKeyHeld) + .def("get_keyboard_speed", &Python_EIOS_GetKeyboardSpeed) + .def("set_keyboard_speed", &Python_EIOS_SetKeyboardSpeed) + .def("get_keyboard_repeat_delay", &Python_EIOS_GetKeyboardRepeatDelay) + .def("set_keyboard_repeat_delay", &Python_EIOS_SetKeyboardRepeatDelay) + .def_static("get_clients_pids", &Python_EIOS_GetClientsPIDs) + .def_static("pair_client_pid", &Python_EIOS_PairClient_PID) + .def_static("kill_client_pid", &Python_EIOS_KillClientPID) + .def("kill_client", &Python_EIOS_KillClient) + .def("reflect_object", &Python_EIOS_Reflect_Object, nanobind::arg("cls"), nanobind::arg("field"), nanobind::arg("desc")) + .def("reflect_bool", &Python_EIOS_Reflect_Bool, nanobind::arg("cls"), nanobind::arg("field")) + .def("reflect_char", &Python_EIOS_Reflect_Char, nanobind::arg("cls"), nanobind::arg("field")) + .def("reflect_byte", &Python_EIOS_Reflect_Byte, nanobind::arg("cls"), nanobind::arg("field")) + .def("reflect_short", &Python_EIOS_Reflect_Short, nanobind::arg("cls"), nanobind::arg("field")) + .def("reflect_int", &Python_EIOS_Reflect_Int, nanobind::arg("cls"), nanobind::arg("field")) + .def("reflect_long", &Python_EIOS_Reflect_Long, nanobind::arg("cls"), nanobind::arg("field")) + .def("reflect_float", &Python_EIOS_Reflect_Float, nanobind::arg("cls"), nanobind::arg("field")) + .def("reflect_double", &Python_EIOS_Reflect_Double, nanobind::arg("cls"), nanobind::arg("field")) + .def("reflect_string", &Python_EIOS_Reflect_String, nanobind::arg("cls"), nanobind::arg("field")) + .def("reflect_array", &Python_EIOS_Reflect_Array, nanobind::arg("cls"), nanobind::arg("field"), nanobind::arg("desc")) + .def("release_objects", &Python_Reflect_Release_Objects) + .def("__str__", &PyEIOS_Str); +} +#else +int PyEIOS_Clear(PyObject* object) +{ + PyEIOS* py_eios = reinterpret_cast(object); + py_eios->pid = -1; + py_eios->native_eios = nullptr; + return 0; +} + +void PyEIOS_Dealloc(PyObject* object) +{ + PyEIOS* py_eios = reinterpret_cast(object); + + EIOS_ReleaseTarget(py_eios->native_eios); + PyEIOS_Clear(object); + python->PyObject_Free(object); +} + +PyObject* PyEIOS_Str(PyObject* object) +{ + PyEIOS* py_eios = reinterpret_cast(object); + std::ostringstream stream; + + std::ios state(nullptr); + state.copyfmt(stream); + + stream << std::setfill('0') << std::uppercase << std::hex; + stream << "EIOS("; + stream << "0x" << reinterpret_cast(object); + stream << "): "; + stream.copyfmt(state); + + stream << "{"<<"\n"; + stream<< " pid: " << py_eios->pid << "\n"; + stream<< "}"; + + std::string result = stream.str(); + return python->PyUnicode_FromStringAndSize(result.c_str(), result.size()); +} + +PyMemberDef PyEIOS_Members[] = { + //{(char*)"pid", T_INT, offsetof(PyEIOS, pid), READONLY, PyDoc_STR("Process ID")}, + //{(char*)"width", T_INT, offsetof(PyEIOS, width), READONLY, PyDoc_STR("Client Width")}, + //{(char*)"height", T_INT, offsetof(PyEIOS, height), READONLY, PyDoc_STR("Client Height")}, + {nullptr} /* Sentinel */ +}; + +PyGetSetDef PyEIOS_PropertyMembers[] = { + {(char*)"pid", [](PyObject* object, void *closure) -> PyObject* { + return object ? python->PyLong_FromLong(reinterpret_cast(object)->pid) : python->PyLong_FromLong(-1); + }, nullptr, PyDoc_STR("Process ID"), nullptr}, + + {(char*)"dimensions", [](PyObject* object, void *closure) -> PyObject* { + if (object) + { + return Python_EIOS_GetTargetDimensions(reinterpret_cast(object), nullptr, 0); + } + return python->PyTuple_Pack(2, python->PyLong_FromLong(0), python->PyLong_FromLong(0)); + }, nullptr, PyDoc_STR("Client Dimensions"), nullptr}, + + {nullptr} /* Sentinel */ +}; + +PyMethodDef PyEIOS_Methods[] = { + {(char*)"inject", PYTHON_FASTCALL(Python_EIOS_Inject), METH_FASTCALL | METH_STATIC, PyDoc_STR("inject('process_name', /)\n--\n\nInjects into a process by name")}, + {(char*)"inject_pid", PYTHON_FASTCALL(Python_EIOS_Inject_PID), METH_FASTCALL | METH_STATIC, PyDoc_STR("inject_pid(process_pid)\n--\n\nInjects into a process by PID")}, + {(char*)"from_pid", PYTHON_FASTCALL(Python_EIOS_From_PID), METH_FASTCALL | METH_STATIC, PyDoc_STR("Gets an existing EIOS by PID")}, + {(char*)"request_target", PYTHON_FASTCALL(Python_EIOS_RequestTarget), METH_FASTCALL | METH_STATIC, PyDoc_STR("Requests an EIOS by PID, and Pairs to it if not already paired")}, + {(char*)"get_target_dimensions", PYTHON_FASTCALL(Python_EIOS_GetTargetDimensions), METH_FASTCALL, PyDoc_STR("Returns an existing EIOS for the specified pid")}, + {(char*)"get_image_format", PYTHON_FASTCALL(Python_EIOS_GetImageFormat), METH_FASTCALL, (char*)""}, + {(char*)"set_image_format", PYTHON_FASTCALL(Python_EIOS_SetImageFormat), METH_FASTCALL, (char*)""}, + {(char*)"get_image_buffer", PYTHON_FASTCALL(Python_EIOS_GetImageBuffer), METH_FASTCALL, (char*)""}, + {(char*)"get_debug_image_buffer", PYTHON_FASTCALL(Python_EIOS_GetDebugImageBuffer), METH_FASTCALL, (char*)""}, + {(char*)"set_graphics_debugging", PYTHON_FASTCALL(Python_EIOS_SetGraphicsDebugging), METH_FASTCALL, (char*)""}, + {(char*)"has_focus", PYTHON_FASTCALL(Python_EIOS_HasFocus), METH_FASTCALL, (char*)""}, + {(char*)"gain_focus", PYTHON_FASTCALL(Python_EIOS_GainFocus), METH_FASTCALL, (char*)""}, + {(char*)"lose_focus", PYTHON_FASTCALL(Python_EIOS_LoseFocus), METH_FASTCALL, (char*)""}, + {(char*)"is_keyboard_input_enabled", PYTHON_FASTCALL(Python_EIOS_IsKeyboardInputEnabled), METH_FASTCALL, (char*)""}, + {(char*)"set_keyboard_input_enabled", PYTHON_FASTCALL(Python_EIOS_SetKeyboardInputEnabled), METH_FASTCALL, (char*)""}, + {(char*)"is_mouse_input_enabled", PYTHON_FASTCALL(Python_EIOS_IsMouseInputEnabled), METH_FASTCALL, (char*)""}, + {(char*)"set_mouse_input_enabled", PYTHON_FASTCALL(Python_EIOS_SetMouseInputEnabled), METH_FASTCALL, (char*)""}, + {(char*)"get_mouse_position", PYTHON_FASTCALL(Python_EIOS_GetMousePosition), METH_FASTCALL, (char*)""}, + {(char*)"get_real_mouse_position", PYTHON_FASTCALL(Python_EIOS_GetRealMousePosition), METH_FASTCALL, (char*)""}, + {(char*)"move_mouse", PYTHON_FASTCALL(Python_EIOS_MoveMouse), METH_FASTCALL, (char*)""}, + {(char*)"hold_mouse", PYTHON_FASTCALL(Python_EIOS_HoldMouse), METH_FASTCALL, (char*)""}, + {(char*)"release_mouse", PYTHON_FASTCALL(Python_EIOS_ReleaseMouse), METH_FASTCALL, (char*)""}, + {(char*)"scroll_mouse", PYTHON_FASTCALL(Python_EIOS_ScrollMouse), METH_FASTCALL, (char*)""}, + {(char*)"is_mouse_button_held", PYTHON_FASTCALL(Python_EIOS_IsMouseButtonHeld), METH_FASTCALL, (char*)""}, + {(char*)"send_string", PYTHON_FASTCALL(Python_EIOS_SendString), METH_FASTCALL, (char*)""}, + {(char*)"hold_key", PYTHON_FASTCALL(Python_EIOS_HoldKey), METH_FASTCALL, (char*)""}, + {(char*)"release_key", PYTHON_FASTCALL(Python_EIOS_ReleaseKey), METH_FASTCALL, (char*)""}, + {(char*)"is_key_held", PYTHON_FASTCALL(Python_EIOS_IsKeyHeld), METH_FASTCALL, (char*)""}, + {(char*)"get_keyboard_speed", PYTHON_FASTCALL(Python_EIOS_GetKeyboardSpeed), METH_FASTCALL, (char*)""}, + {(char*)"set_keyboard_speed", PYTHON_FASTCALL(Python_EIOS_SetKeyboardSpeed), METH_FASTCALL, (char*)""}, + {(char*)"get_keyboard_repeat_delay", PYTHON_FASTCALL(Python_EIOS_GetKeyboardRepeatDelay), METH_FASTCALL, (char*)""}, + {(char*)"set_keyboard_repeat_delay", PYTHON_FASTCALL(Python_EIOS_SetKeyboardRepeatDelay), METH_FASTCALL, (char*)""}, + {(char*)"get_clients_pids", PYTHON_FASTCALL(Python_EIOS_GetClientsPIDs), METH_FASTCALL | METH_STATIC, (char*)""}, + {(char*)"pair_client_pid", PYTHON_FASTCALL(Python_EIOS_PairClient_PID), METH_FASTCALL | METH_STATIC, (char*)""}, + {(char*)"kill_client_pid", PYTHON_FASTCALL(Python_EIOS_KillClientPID), METH_FASTCALL | METH_STATIC, (char*)""}, + {(char*)"kill_client", PYTHON_FASTCALL(Python_EIOS_KillClient), METH_FASTCALL, (char*)""}, + + {(char*)"reflect_object", PYTHON_FASTCALL(Python_Reflect_Object), METH_FASTCALL, (char*)""}, + {(char*)"reflect_bool", PYTHON_FASTCALL(Python_Reflect_Bool), METH_FASTCALL, (char*)""}, + {(char*)"reflect_char", PYTHON_FASTCALL(Python_Reflect_Char), METH_FASTCALL, (char*)""}, + {(char*)"reflect_byte", PYTHON_FASTCALL(Python_Reflect_Byte), METH_FASTCALL, (char*)""}, + {(char*)"reflect_short", PYTHON_FASTCALL(Python_Reflect_Short), METH_FASTCALL, (char*)""}, + {(char*)"reflect_int", PYTHON_FASTCALL(Python_Reflect_Int), METH_FASTCALL, (char*)""}, + {(char*)"reflect_long", PYTHON_FASTCALL(Python_Reflect_Long), METH_FASTCALL, (char*)""}, + {(char*)"reflect_float", PYTHON_FASTCALL(Python_Reflect_Float), METH_FASTCALL, (char*)""}, + {(char*)"reflect_double", PYTHON_FASTCALL(Python_Reflect_Double), METH_FASTCALL, (char*)""}, + {(char*)"reflect_string", PYTHON_FASTCALL(Python_Reflect_String), METH_FASTCALL, (char*)""}, + {(char*)"reflect_array", PYTHON_FASTCALL(Python_Reflect_Array), METH_FASTCALL, (char*)""}, + {(char*)"release_objects", PYTHON_FASTCALL(Python_Reflect_Release_Objects), METH_FASTCALL, (char*)""}, + {nullptr} /* Sentinel */ +}; + +PyType_Slot PyEIOS_Slots[] = { + {Py_tp_init, nullptr}, + {Py_tp_members, PyEIOS_Members}, + {Py_tp_methods, PyEIOS_Methods}, + {Py_tp_getset, PyEIOS_PropertyMembers}, + {Py_tp_dealloc, reinterpret_cast(&PyEIOS_Dealloc)}, + {Py_tp_clear, reinterpret_cast(&PyEIOS_Clear)}, + {Py_tp_str, reinterpret_cast(&PyEIOS_Str)}, + {Py_tp_doc, static_cast(const_cast(PyDoc_STR("EIOS Structure")))}, + {0, NULL}, +}; + +PyType_Spec PyEIOS_Spec { + "remote_input.EIOS", + sizeof(PyEIOS), + 0, + #if HAS_PYTHON_VERSION(0x030A0000) + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_DISALLOW_INSTANTIATION, + #else + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + #endif + PyEIOS_Slots +}; + +// MARK: EXPORTS + +PyTypeObject* PyEIOS_Type() noexcept +{ + static PyTypeObject* type = reinterpret_cast(python->PyType_FromSpec(&PyEIOS_Spec)); + return type; +} + +EIOS* python_get_eios(PyEIOS* eios) +{ + return PythonGetEIOS(reinterpret_cast(eios))->native_eios; +} + +PyObject* Python_EIOS_Inject(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + #ifdef PYTHON_API_VALIDATION + if (args_length != 1) + { + PyErr_Format(PyExc_TypeError, "function takes exactly %zu argument (%zus given)", 1, args_length); + return nullptr; + } + + std::string error; + if (!type_python_is_valid(args[0], error)) + { + PyErr_Format(PyExc_TypeError, "Invalid Argument: %s. Expected 'process_name: string'", error.c_str()); + return nullptr; + } + #endif + + std::string process_name = from_python_object(args[0]); + EIOS_Inject(process_name.c_str()); + + (python->Py_INCREF)(python->Py_GetNone_Object()); + return python->Py_GetNone_Object(); +} + +PyObject* Python_EIOS_Inject_PID(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + #ifdef PYTHON_API_VALIDATION + if (args_length != 1) + { + PyErr_Format(PyExc_TypeError, "function takes exactly %zu argument (%zus given)", 1, args_length); + return nullptr; + } + + std::string error; + if (!type_python_is_valid(args[0], error)) + { + PyErr_Format(PyExc_TypeError, "Invalid Argument: %s. Expected 'process_id: Int32'", error.c_str()); + return nullptr; + } + #endif + + std::int32_t pid = from_python_object(args[0]); + EIOS_Inject_PID(pid); + + (python->Py_INCREF)(python->Py_GetNone_Object()); + return python->Py_GetNone_Object(); +} + +PyObject* Python_EIOS_From_PID(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + #ifdef PYTHON_API_VALIDATION + if (args_length != 1) + { + PyErr_Format(PyExc_TypeError, "function takes exactly %zu argument (%zus given)", 1, args_length); + return nullptr; + } + + std::string error; + if (!type_python_is_valid(args[0], error)) + { + PyErr_Format(PyExc_TypeError, "Invalid Argument: %s. Expected 'process_id: Int32'", error.c_str()); + return nullptr; + } + #endif + + std::int32_t pid = from_python_object(args[0]); + return PythonWrapEIOS(EIOS_From_PID(pid)); +} + +PyObject* Python_EIOS_RequestTarget(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + #ifdef PYTHON_API_VALIDATION + if (args_length != 1) + { + PyErr_Format(PyExc_TypeError, "function takes exactly %zu argument (%zus given)", 1, args_length); + return nullptr; + } + + std::string error; + if (!type_python_is_valid(args[0], error)) + { + PyErr_Format(PyExc_TypeError, "Invalid Argument: %s. Expected 'process_id: String'", error.c_str()); + return nullptr; + } + #endif + + std::string pid = from_python_object(args[0]); + return PythonWrapEIOS(EIOS_RequestTarget(pid.c_str())); +} + +PyObject* Python_EIOS_GetTargetDimensions(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + #ifdef PYTHON_API_VALIDATION + if (args_length != 0) + { + PyErr_Format(PyExc_TypeError, "function takes exactly %zu arguments (%zus given)", 0, args_length); + return nullptr; + } + #endif + + std::int32_t width = 0; + std::int32_t height = 0; + EIOS_GetTargetDimensions(python_get_eios(self), &width, &height); + return python->PyTuple_Pack(2, python->PyLong_FromLong(width), python->PyLong_FromLong(height)); +} + +PyObject* Python_EIOS_GetImageFormat(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + ImageFormat format = EIOS_GetImageFormat(python_get_eios(self)); + return to_python_object(format); +} + +PyObject* Python_EIOS_SetImageFormat(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + EIOS_SetImageFormat(python_get_eios(self), from_python_object(args[0])); + (python->Py_INCREF)(python->Py_GetNone_Object()); + return python->Py_GetNone_Object(); +} + +PyObject* Python_EIOS_GetImageBuffer(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + #ifdef PYTHON_API_VALIDATION + if (args_length != 0) + { + PyErr_Format(PyExc_TypeError, "function takes exactly %zu arguments (%zus given)", 0, args_length); + return nullptr; + } + #endif + + std::int32_t width = 0; + std::int32_t height = 0; + EIOS_GetTargetDimensions(python_get_eios(self), &width, &height); + std::uint8_t* buffer = EIOS_GetImageBuffer(python_get_eios(self)); + return python->PyMemoryView_FromMemory(reinterpret_cast(buffer), width * height * 4 * sizeof(std::uint8_t), 0x100 /*PyBUF_READ*/); +} + +PyObject* Python_EIOS_GetDebugImageBuffer(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + #ifdef PYTHON_API_VALIDATION + if (args_length != 0) + { + PyErr_Format(PyExc_TypeError, "function takes exactly %zu arguments (%zus given)", 0, args_length); + return nullptr; + } + #endif + + std::int32_t width = 0; + std::int32_t height = 0; + EIOS_GetTargetDimensions(python_get_eios(self), &width, &height); + std::uint8_t* buffer = EIOS_GetDebugImageBuffer(python_get_eios(self)); + return python->PyMemoryView_FromMemory(reinterpret_cast(buffer), width * height * 4 * sizeof(std::uint8_t), 0x200 /*PyBUF_WRITE*/); +} + +PyObject* Python_EIOS_SetGraphicsDebugging(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + #ifdef PYTHON_API_VALIDATION + if (args_length != 1) + { + PyErr_Format(PyExc_TypeError, "function takes exactly %zu argument (%zus given)", 1, args_length); + return nullptr; + } + + std::string error; + if (!type_python_is_valid(args[0], error)) + { + PyErr_Format(PyExc_TypeError, "Invalid Argument: %s. Expected 'enabled: Bool'", error.c_str()); + return nullptr; + } + #endif + + bool enabled = from_python_object(args[0]); + EIOS_SetGraphicsDebugging(python_get_eios(self), enabled); + + (python->Py_INCREF)(python->Py_GetNone_Object()); + return python->Py_GetNone_Object(); +} + +PyObject* Python_EIOS_HasFocus(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + #ifdef PYTHON_API_VALIDATION + if (args_length != 0) + { + PyErr_Format(PyExc_TypeError, "function takes exactly %zu arguments (%zus given)", 0, args_length); + return nullptr; + } + #endif + + return to_python_object(EIOS_HasFocus(python_get_eios(self))); +} + +PyObject* Python_EIOS_GainFocus(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + #ifdef PYTHON_API_VALIDATION + if (args_length != 0) + { + PyErr_Format(PyExc_TypeError, "function takes exactly %zu arguments (%zus given)", 0, args_length); + return nullptr; + } + #endif + + EIOS_GainFocus(python_get_eios(self)); + + (python->Py_INCREF)(python->Py_GetNone_Object()); + return python->Py_GetNone_Object(); +} + +PyObject* Python_EIOS_LoseFocus(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + #ifdef PYTHON_API_VALIDATION + if (args_length != 0) + { + PyErr_Format(PyExc_TypeError, "function takes exactly %zu arguments (%zus given)", 0, args_length); + return nullptr; + } + #endif + + EIOS_LoseFocus(python_get_eios(self)); + + (python->Py_INCREF)(python->Py_GetNone_Object()); + return python->Py_GetNone_Object(); +} + +PyObject* Python_EIOS_IsKeyboardInputEnabled(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + #ifdef PYTHON_API_VALIDATION + if (args_length != 0) + { + PyErr_Format(PyExc_TypeError, "function takes exactly %zu arguments (%zus given)", 0, args_length); + return nullptr; + } + #endif + + return to_python_object(EIOS_IsKeyboardInputEnabled(python_get_eios(self))); +} + +PyObject* Python_EIOS_SetKeyboardInputEnabled(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + #ifdef PYTHON_API_VALIDATION + if (args_length != 1) + { + PyErr_Format(PyExc_TypeError, "function takes exactly %zu argument (%zus given)", 1, args_length); + return nullptr; + } + + std::string error; + if (!type_python_is_valid(args[0], error)) + { + PyErr_Format(PyExc_TypeError, "Invalid Argument: %s. Expected 'enabled: Bool'", error.c_str()); + return nullptr; + } + #endif + + bool enabled = from_python_object(args[0]); + EIOS_SetKeyboardInputEnabled(python_get_eios(self), enabled); + + (python->Py_INCREF)(python->Py_GetNone_Object()); + return python->Py_GetNone_Object(); +} + +PyObject* Python_EIOS_IsMouseInputEnabled(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + #ifdef PYTHON_API_VALIDATION + if (args_length != 0) + { + PyErr_Format(PyExc_TypeError, "function takes exactly %zu arguments (%zus given)", 0, args_length); + return nullptr; + } + #endif + + return to_python_object(EIOS_IsMouseInputEnabled(python_get_eios(self))); +} + +PyObject* Python_EIOS_SetMouseInputEnabled(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + #ifdef PYTHON_API_VALIDATION + if (args_length != 1) + { + PyErr_Format(PyExc_TypeError, "function takes exactly %zu argument (%zus given)", 1, args_length); + return nullptr; + } + + std::string error; + if (!type_python_is_valid(args[0], error)) + { + PyErr_Format(PyExc_TypeError, "Invalid Argument: %s. Expected 'enabled: Bool'", error.c_str()); + return nullptr; + } + #endif + + bool enabled = from_python_object(args[0]); + EIOS_SetMouseInputEnabled(python_get_eios(self), enabled); + + (python->Py_INCREF)(python->Py_GetNone_Object()); + return python->Py_GetNone_Object(); +} + +PyObject* Python_EIOS_GetMousePosition(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + #ifdef PYTHON_API_VALIDATION + if (args_length != 0) + { + PyErr_Format(PyExc_TypeError, "function takes exactly %zu arguments (%zus given)", 0, args_length); + return nullptr; + } + #endif + + std::int32_t width = 0; + std::int32_t height = 0; + EIOS_GetMousePosition(python_get_eios(self), &width, &height); + return python->PyTuple_Pack(2, python->PyLong_FromLong(width), python->PyLong_FromLong(height)); +} + +PyObject* Python_EIOS_GetRealMousePosition(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + #ifdef PYTHON_API_VALIDATION + if (args_length != 0) + { + PyErr_Format(PyExc_TypeError, "function takes exactly %zu arguments (%zus given)", 0, args_length); + return nullptr; + } + #endif + + std::int32_t width = 0; + std::int32_t height = 0; + EIOS_GetRealMousePosition(python_get_eios(self), &width, &height); + return python->PyTuple_Pack(2, python->PyLong_FromLong(width), python->PyLong_FromLong(height)); +} + +PyObject* Python_EIOS_MoveMouse(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + #ifdef PYTHON_API_VALIDATION + if (args_length != 2) + { + PyErr_Format(PyExc_TypeError, "function takes exactly %zu arguments (%zus given)", 2, args_length); + return nullptr; + } + + std::string error; + if (!type_python_is_valid(args[0], error)) + { + PyErr_Format(PyExc_TypeError, "Invalid Argument: %s. Expected 'x: Int32'", error.c_str()); + return nullptr; + } + + if (!type_python_is_valid(args[1], error)) + { + PyErr_Format(PyExc_TypeError, "Invalid Argument: %s. Expected 'y: Int32'", error.c_str()); + return nullptr; + } + #endif + + EIOS_MoveMouse(python_get_eios(self), from_python_object(args[0]), from_python_object(args[1])); + + (python->Py_INCREF)(python->Py_GetNone_Object()); + return python->Py_GetNone_Object(); +} + +PyObject* Python_EIOS_HoldMouse(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + #ifdef PYTHON_API_VALIDATION + if (args_length != 1) + { + PyErr_Format(PyExc_TypeError, "function takes exactly %zu argument (%zus given)", 1, args_length); + return nullptr; + } + + std::string error; + + if (!type_python_is_valid(args[0], error)) + { + PyErr_Format(PyExc_TypeError, "Invalid Argument: %s. Expected 'button: Int32'", error.c_str()); + return nullptr; + } + #endif + + EIOS_HoldMouse(python_get_eios(self), 0, 0, from_python_object(args[0])); + + (python->Py_INCREF)(python->Py_GetNone_Object()); + return python->Py_GetNone_Object(); +} + +PyObject* Python_EIOS_ReleaseMouse(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + #ifdef PYTHON_API_VALIDATION + if (args_length != 1) + { + PyErr_Format(PyExc_TypeError, "function takes exactly %zu argument (%zus given)", 1, args_length); + return nullptr; + } + + std::string error; + + if (!type_python_is_valid(args[0], error)) + { + PyErr_Format(PyExc_TypeError, "Invalid Argument: %s. Expected 'button: Int32'", error.c_str()); + return nullptr; + } + #endif + + EIOS_ReleaseMouse(python_get_eios(self), 0, 0, from_python_object(args[0])); + + (python->Py_INCREF)(python->Py_GetNone_Object()); + return python->Py_GetNone_Object(); +} + +PyObject* Python_EIOS_ScrollMouse(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + #ifdef PYTHON_API_VALIDATION + if (args_length != 1) + { + PyErr_Format(PyExc_TypeError, "function takes exactly %zu argument (%zus given)", 1, args_length); + return nullptr; + } + + std::string error; + if (!type_python_is_valid(args[0], error)) + { + PyErr_Format(PyExc_TypeError, "Invalid Argument: %s. Expected 'lines: Int32'", error.c_str()); + return nullptr; + } + #endif + + EIOS_ScrollMouse(python_get_eios(self), 0, 0, from_python_object(args[0])); + + (python->Py_INCREF)(python->Py_GetNone_Object()); + return python->Py_GetNone_Object(); +} + +PyObject* Python_EIOS_IsMouseButtonHeld(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + #ifdef PYTHON_API_VALIDATION + if (args_length != 1) + { + PyErr_Format(PyExc_TypeError, "function takes exactly %zu argument (%zus given)", 1, args_length); + return nullptr; + } + + std::string error; + if (!type_python_is_valid(args[0], error)) + { + PyErr_Format(PyExc_TypeError, "Invalid Argument: %s. Expected 'button: Int32'", error.c_str()); + return nullptr; + } + #endif + + return to_python_object(EIOS_IsMouseButtonHeld(python_get_eios(self), from_python_object(args[0]))); +} + +PyObject* Python_EIOS_SendString(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + #ifdef PYTHON_API_VALIDATION + if (args_length != 3) + { + PyErr_Format(PyExc_TypeError, "function takes exactly %zu arguments (%zus given)", 3, args_length); + return nullptr; + } + + std::string error; + if (!type_python_is_valid(args[0], error)) + { + PyErr_Format(PyExc_TypeError, "Invalid Argument: %s. Expected 'string: String'", error.c_str()); + return nullptr; + } + + if (!type_python_is_valid(args[1], error)) + { + PyErr_Format(PyExc_TypeError, "Invalid Argument: %s. Expected 'keymod: Int32'", error.c_str()); + return nullptr; + } + + if (!type_python_is_valid(args[2], error)) + { + PyErr_Format(PyExc_TypeError, "Invalid Argument: %s. Expected 'keymodwait: Int32'", error.c_str()); + return nullptr; + } + #endif + + std::string text = from_python_object(args[0]); + EIOS_SendString(python_get_eios(self), text.c_str(), from_python_object(args[1]), from_python_object(args[2])); + + (python->Py_INCREF)(python->Py_GetNone_Object()); + return python->Py_GetNone_Object(); +} + +PyObject* Python_EIOS_HoldKey(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + #ifdef PYTHON_API_VALIDATION + if (args_length != 1) + { + PyErr_Format(PyExc_TypeError, "function takes exactly %zu argument (%zus given)", 1, args_length); + return nullptr; + } + + std::string error; + if (!type_python_is_valid(args[0], error)) + { + PyErr_Format(PyExc_TypeError, "Invalid Argument: %s. Expected 'key: Int32'", error.c_str()); + return nullptr; + } + #endif + + EIOS_HoldKey(python_get_eios(self), from_python_object(args[0])); + + (python->Py_INCREF)(python->Py_GetNone_Object()); + return python->Py_GetNone_Object(); +} + +PyObject* Python_EIOS_ReleaseKey(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + #ifdef PYTHON_API_VALIDATION + if (args_length != 1) + { + PyErr_Format(PyExc_TypeError, "function takes exactly %zu argument (%zus given)", 1, args_length); + return nullptr; + } + + std::string error; + if (!type_python_is_valid(args[0], error)) + { + PyErr_Format(PyExc_TypeError, "Invalid Argument: %s. Expected 'key: Int32'", error.c_str()); + return nullptr; + } + #endif + + EIOS_ReleaseKey(python_get_eios(self), from_python_object(args[0])); + + (python->Py_INCREF)(python->Py_GetNone_Object()); + return python->Py_GetNone_Object(); +} + +PyObject* Python_EIOS_IsKeyHeld(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + #ifdef PYTHON_API_VALIDATION + if (args_length != 1) + { + PyErr_Format(PyExc_TypeError, "function takes exactly %zu argument (%zus given)", 1, args_length); + return nullptr; + } + + std::string error; + if (!type_python_is_valid(args[0], error)) + { + PyErr_Format(PyExc_TypeError, "Invalid Argument: %s. Expected 'key: Int32'", error.c_str()); + return nullptr; + } + #endif + + return to_python_object(EIOS_IsKeyHeld(python_get_eios(self), from_python_object(args[0]))); +} + +PyObject* Python_EIOS_GetKeyboardSpeed(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + #ifdef PYTHON_API_VALIDATION + if (args_length != 0) + { + PyErr_Format(PyExc_TypeError, "function takes exactly %zu arguments (%zus given)", 0, args_length); + return nullptr; + } + #endif + + return to_python_object(EIOS_GetKeyboardSpeed(python_get_eios(self))); +} + +PyObject* Python_EIOS_SetKeyboardSpeed(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + #ifdef PYTHON_API_VALIDATION + if (args_length != 1) + { + PyErr_Format(PyExc_TypeError, "function takes exactly %zu argument (%zus given)", 1, args_length); + return nullptr; + } + + std::string error; + if (!type_python_is_valid(args[0], error)) + { + PyErr_Format(PyExc_TypeError, "Invalid Argument: %s. Expected 'speed: Int32'", error.c_str()); + return nullptr; + } + #endif + + EIOS_SetKeyboardSpeed(python_get_eios(self), from_python_object(args[0])); + + (python->Py_INCREF)(python->Py_GetNone_Object()); + return python->Py_GetNone_Object(); +} + +PyObject* Python_EIOS_GetKeyboardRepeatDelay(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + #ifdef PYTHON_API_VALIDATION + if (args_length != 0) + { + PyErr_Format(PyExc_TypeError, "function takes exactly %zu arguments (%zus given)", 0, args_length); + return nullptr; + } + #endif + + return to_python_object(EIOS_GetKeyboardRepeatDelay(python_get_eios(self))); +} + +PyObject* Python_EIOS_SetKeyboardRepeatDelay(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + #ifdef PYTHON_API_VALIDATION + if (args_length != 1) + { + PyErr_Format(PyExc_TypeError, "function takes exactly %zu argument (%zus given)", 1, args_length); + return nullptr; + } + + std::string error; + if (!type_python_is_valid(args[0], error)) + { + PyErr_Format(PyExc_TypeError, "Invalid Argument: %s. Expected 'delay: Int32'", error.c_str()); + return nullptr; + } + #endif + + EIOS_SetKeyboardRepeatDelay(python_get_eios(self), from_python_object(args[0])); + + (python->Py_INCREF)(python->Py_GetNone_Object()); + return python->Py_GetNone_Object(); +} + +PyObject* Python_EIOS_GetClientsPIDs(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + #ifdef PYTHON_API_VALIDATION + if (args_length != 1) + { + PyErr_Format(PyExc_TypeError, "function takes exactly %zu argument (%zus given)", 1, args_length); + return nullptr; + } + + std::string error; + if (!type_python_is_valid(args[0], error)) + { + PyErr_Format(PyExc_TypeError, "Invalid Argument: %s. Expected 'unpaired_only: Bool'", error.c_str()); + return nullptr; + } + #endif + + std::size_t client_count = EIOS_GetClients(from_python_object(args[0])); + std::vector client_pids(client_count); + + for (std::size_t i = 0; i < client_count; ++i) + { + client_pids[i] = EIOS_GetClientPID(i); + } + return to_python_array(client_pids); +} + +PyObject* Python_EIOS_PairClient_PID(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + #ifdef PYTHON_API_VALIDATION + if (args_length != 1) + { + PyErr_Format(PyExc_TypeError, "function takes exactly %zu argument (%zus given)", 1, args_length); + return nullptr; + } + + std::string error; + if (!type_python_is_valid(args[0], error)) + { + PyErr_Format(PyExc_TypeError, "Invalid Argument: %s. Expected 'process_id: Int32'", error.c_str()); + return nullptr; + } + #endif + + return PythonWrapEIOS(EIOS_PairClient(from_python_object(args[0]))); +} + +PyObject* Python_EIOS_KillClientPID(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + #ifdef PYTHON_API_VALIDATION + if (args_length != 1) + { + PyErr_Format(PyExc_TypeError, "function takes exactly %zu argument (%zus given)", 1, args_length); + return nullptr; + } + + std::string error; + if (!type_python_is_valid(args[0], error)) + { + PyErr_Format(PyExc_TypeError, "Invalid Argument: %s. Expected 'process_id: Int32'", error.c_str()); + return nullptr; + } + #endif + + EIOS_KillClientPID(from_python_object(args[0])); + + (python->Py_INCREF)(python->Py_GetNone_Object()); + return python->Py_GetNone_Object(); +} + +PyObject* Python_EIOS_KillClient(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + #ifdef PYTHON_API_VALIDATION + if (args_length != 0) + { + PyErr_Format(PyExc_TypeError, "function takes exactly %zu arguments (%zus given)", 0, args_length); + return nullptr; + } + #endif + + EIOS_KillClient(python_get_eios(self)); + + (python->Py_INCREF)(python->Py_GetNone_Object()); + return python->Py_GetNone_Object(); +} + +PyObject* Python_Reflect_Release_Objects(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + extern int PyJavaObject_Clear(PyObject* object); + extern int PyJavaArray_Clear(PyObject* object); + + // Flatten the List + std::stack stack; + std::vector objects; + stack.push(args[0]); + + while (!stack.empty()) + { + PyObject* object = stack.top(); + stack.pop(); + + if ((python->PyList_Check)(object)) + { + for (std::size_t i = 0; i < python->PyList_Size(object); ++i) + { + stack.push(python->PyList_GetItem(object, i)); + } + } + else + { + if (Py_REFCNT(object) == 1) + { + objects.push_back(object); + } + } + } + + // Early exit if no objects to free + if (objects.empty()) + { + (python->Py_INCREF)(python->Py_GetNone_Object()); + return python->Py_GetNone_Object(); + } + + // Unwrap each object and clear as we go along to prevent double-free + std::vector result; + result.reserve(objects.size()); + + for (PyObject* object : objects) + { + PyRemoteInputType type = GetPythonObjectType(object); + if (type == PyRemoteInputType::JAVA_OBJECT) + { + if (!self) + { + self = reinterpret_cast(object)->eios; + } + + result.push_back(from_python_object(object)); + PyJavaObject_Clear(object); + } + else if (type == PyRemoteInputType::JAVA_ARRAY) + { + if (!self) + { + self = reinterpret_cast(object)->eios; + } + + result.push_back(from_python_object(object)); + PyJavaArray_Clear(object); + } + } + + // Release all objects at once + Reflect_Release_Objects(python_get_eios(self), &result[0], result.size()); + + (python->Py_INCREF)(python->Py_GetNone_Object()); + return python->Py_GetNone_Object(); +} +#endif \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Plugin/Python/PythonEIOS.hxx b/third-party/RemoteInput/RemoteInput/Plugin/Python/PythonEIOS.hxx new file mode 100644 index 0000000..65065ec --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Plugin/Python/PythonEIOS.hxx @@ -0,0 +1,53 @@ +// +// Created by Brandon on 2022-09-21. +// + +#ifndef REMOTEINPUT_PYTHONEIOS_HXX +#define REMOTEINPUT_PYTHONEIOS_HXX + +#include "PythonCommon.hxx" + +#if defined(USE_PYBIND11) +void declare_python_eios(nanobind::module_ &module); +#else +PyObject* Python_EIOS_Inject(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_EIOS_Inject_PID(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_EIOS_From_PID(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_EIOS_RequestTarget(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept; +//PyObject* Python_EIOS_ReleaseTarget(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_EIOS_GetTargetDimensions(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_EIOS_GetImageFormat(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_EIOS_SetImageFormat(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_EIOS_GetImageBuffer(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_EIOS_GetDebugImageBuffer(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_EIOS_SetGraphicsDebugging(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_EIOS_HasFocus(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_EIOS_GainFocus(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_EIOS_LoseFocus(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_EIOS_IsKeyboardInputEnabled(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_EIOS_SetKeyboardInputEnabled(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_EIOS_IsMouseInputEnabled(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_EIOS_SetMouseInputEnabled(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_EIOS_GetMousePosition(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_EIOS_GetRealMousePosition(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_EIOS_MoveMouse(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_EIOS_HoldMouse(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_EIOS_ReleaseMouse(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_EIOS_ScrollMouse(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_EIOS_IsMouseButtonHeld(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_EIOS_SendString(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_EIOS_HoldKey(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_EIOS_ReleaseKey(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_EIOS_IsKeyHeld(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_EIOS_GetKeyboardSpeed(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_EIOS_SetKeyboardSpeed(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_EIOS_GetKeyboardRepeatDelay(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_EIOS_SetKeyboardRepeatDelay(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_EIOS_GetClientsPIDs(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_EIOS_PairClient_PID(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_EIOS_KillClientPID(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_EIOS_KillClient(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_Reflect_Release_Objects(PyEIOS* self, PyObject* args[], Py_ssize_t args_length) noexcept; +#endif + +#endif //REMOTEINPUT_PYTHONEIOS_HXX diff --git a/third-party/RemoteInput/RemoteInput/Plugin/Python/PythonJavaArray.cxx b/third-party/RemoteInput/RemoteInput/Plugin/Python/PythonJavaArray.cxx new file mode 100644 index 0000000..665b4b3 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Plugin/Python/PythonJavaArray.cxx @@ -0,0 +1,789 @@ +// +// Created by Brandon on 2023-12-10. +// + +#include "PythonJavaArray.hxx" +#include "PythonJavaList.hxx" + +#include +#include +#include + +#if defined(USE_PYBIND11) +#include +#endif + +#if defined(USE_PYBIND11) +nanobind::object read_array_type(Stream &stream, PyJavaArray* object, ReflectionType type, std::size_t dimensions); + +nanobind::object Python_JavaArray_GetLength(PyJavaArray* self) noexcept +{ + EIOS* eios = self->eios->native_eios; + jarray array = self->array; + std::size_t length = eios->control_center->reflect_array_size(array); + return nanobind::int_(length); +} + +nanobind::object Python_JavaArray_Get1D(PyJavaArray* self, const nanobind::object& type_object, const nanobind::object& indices_object, const nanobind::object& index_object, const nanobind::object& length_object) noexcept +{ + ReflectionType type = ReflectionType::OBJECT; + if (!type_object.is_none()) + { + type = nanobind::cast(type_object); + } + + EIOS* eios = self->eios->native_eios; + jarray array = self->array; + std::size_t index = 0; + std::size_t length = 0; + + if (indices_object.is_none() && index_object.is_none() && length_object.is_none()) + { + // Read entire array + Stream &stream = eios->control_center->reflect_array_all(array, type, 1)->data_stream(); + return read_array_type(stream, self, type, 1); + } + + if (!indices_object.is_none()) + { + // Read array indexed by indices + auto indices = nanobind::cast>(indices_object); + Stream &stream = eios->control_center->reflect_array_indices(array, type, &indices[0], indices.size())->data_stream(); + return read_array_type(stream, self, type, 1); + } + + if (!index_object.is_none()) + { + index = nanobind::cast(index_object); + } + + if (length_object) + { + length = nanobind::cast(length_object); + } + + if (!index_object.is_none() && length_object.is_none()) + { + // Read array[index] + Stream &stream = eios->control_center->reflect_array(array, type, 1, index)->data_stream(); + return read_array_type(stream, self, type, 0); + } + + length = std::min(std::max(length, 1), self->size); + + // Read array of [index..control_center->reflect_array(array, type, length, index)->data_stream(); + return read_array_type(stream, self, type, 1); +} + +nanobind::object Python_JavaArray_Get2D(PyJavaArray* self, const nanobind::object& type_object, const nanobind::object& x_object, const nanobind::object& y_object) noexcept +{ + ReflectionType type = ReflectionType::OBJECT; + if (!type_object.is_none()) + { + type = nanobind::cast(type_object); + } + + EIOS* eios = self->eios->native_eios; + jarray array = self->array; + + // Array[x][y] + if (!x_object.is_none() && !y_object.is_none()) + { + std::size_t x = nanobind::cast(x_object); + std::size_t y = nanobind::cast(y_object); + + Stream &stream = eios->control_center->reflect_array(array, type, 1, x, y)->data_stream(); + return read_array_type(stream, self, type, 0); + } + + // Array[][] + Stream &stream = eios->control_center->reflect_array_all(array, type, 2)->data_stream(); + return read_array_type(stream, self, type, 2); +} + +nanobind::object Python_JavaArray_Get3D(PyJavaArray* self, const nanobind::object& type_object, const nanobind::object& x_object, const nanobind::object& y_object, const nanobind::object& z_object) noexcept +{ + ReflectionType type = ReflectionType::OBJECT; + if (!type_object.is_none()) + { + type = nanobind::cast(type_object); + } + + EIOS* eios = self->eios->native_eios; + jarray array = self->array; + + // Array[x][y][z] + if (!x_object.is_none() && !y_object.is_none() && !z_object.is_none()) + { + std::size_t x = nanobind::cast(x_object); + std::size_t y = nanobind::cast(y_object); + std::size_t z = nanobind::cast(z_object); + + Stream &stream = eios->control_center->reflect_array(array, type, 1, x, y, z)->data_stream(); + return read_array_type(stream, self, type, 0); + } + + // Array[][][] + Stream &stream = eios->control_center->reflect_array_all(array, type, 3)->data_stream(); + return read_array_type(stream, self, type, 3); +} + +nanobind::object Python_JavaArray_Get4D(PyJavaArray* self, const nanobind::object& type_object, const nanobind::object& x_object, const nanobind::object& y_object, const nanobind::object& z_object, const nanobind::object& w_object) noexcept +{ + ReflectionType type = ReflectionType::OBJECT; + if (!type_object.is_none()) + { + type = nanobind::cast(type_object); + } + + EIOS* eios = self->eios->native_eios; + jarray array = self->array; + + // Array[x][y][z][w] + if (!x_object.is_none() && !y_object.is_none() && !z_object.is_none() && !w_object.is_none()) + { + std::size_t x = nanobind::cast(x_object); + std::size_t y = nanobind::cast(y_object); + std::size_t z = nanobind::cast(z_object); + std::size_t w = nanobind::cast(z_object); + + Stream &stream = eios->control_center->reflect_array(array, type, 1, x, y, z, w)->data_stream(); + return read_array_type(stream, self, type, 0); + } + + // Array[][][][] + Stream &stream = eios->control_center->reflect_array_all(array, type, 4)->data_stream(); + return read_array_type(stream, self, type, 4); +} + +void Python_JavaArray_Release_Object(PyJavaArray* self) noexcept +{ + if (self->eios && self->array) + { + EIOS* eios = self->eios->native_eios; + jarray object = self->array; + + eios->control_center->reflect_release_object(object); + + self->eios = nullptr; + self->array = nullptr; + self->size = 0; + } +} + +nanobind::object PyJavaArray_Str(PyJavaArray* self) +{ + std::ostringstream stream; + + std::ios state(nullptr); + state.copyfmt(stream); + + stream << std::setfill('0') << std::uppercase << std::hex; + stream << "JavaArray("; + stream << "0x" << reinterpret_cast(self); + stream << "): "; + stream.copyfmt(state); + + stream << "{"<<"\n"; + stream<< " eios: " << self->eios << "\n"; + stream<< " array: " << self->array << "\n"; + stream<< " size: " << self->size << "\n"; + stream<< "}"; + + return nanobind::cast(stream.str()); +} + +void declare_python_java_array(nanobind::module_ &module) +{ + nanobind::class_(module, "JavaArray") + // .def(nanobind::init<>()) // Cannot instantiate from Python + .def_ro("eios", &PyJavaArray::eios) + .def_ro("array", &PyJavaArray::array) + .def_ro("size", &PyJavaArray::size) + .def("get_length", &Python_JavaArray_GetLength) + .def("get_1d", &Python_JavaArray_Get1D, + nanobind::arg("type") = nanobind::none(), + nanobind::arg("indices") = nanobind::none(), + nanobind::arg("index") = nanobind::none(), + nanobind::arg("length") = nanobind::none()) + .def("get_2d", &Python_JavaArray_Get2D, + nanobind::arg("type") = nanobind::none(), + nanobind::arg("x") = nanobind::none(), + nanobind::arg("y") = nanobind::none()) + .def("get_3d", &Python_JavaArray_Get3D, + nanobind::arg("type") = nanobind::none(), + nanobind::arg("x") = nanobind::none(), + nanobind::arg("y") = nanobind::none(), + nanobind::arg("z") = nanobind::none()) + .def("get_4d", &Python_JavaArray_Get4D, + nanobind::arg("type") = nanobind::none(), + nanobind::arg("x") = nanobind::none(), + nanobind::arg("y") = nanobind::none(), + nanobind::arg("z") = nanobind::none(), + nanobind::arg("w") = nanobind::none()) + .def("release_object", &Python_JavaArray_Release_Object) + .def("__str__", &PyJavaArray_Str); +} + +template +nanobind::object read_array_type(Stream &stream, PyJavaArray* array) +{ + if constexpr(std::is_same::value) + { + std::size_t length = stream.read(); + PyObject* list = create_java_list(length); + + for (std::size_t i = 0; i < length; ++i) + { + PyList_SetItem(list, i, python_create_object(array, stream.read()).release().ptr()); + } + + return nanobind::steal(list); + } + else if constexpr(std::is_same::value) + { + std::size_t length = stream.read(); + PyObject* list = create_java_list(length); + + for (std::size_t i = 0; i < length; ++i) + { + PyList_SetItem(list, i, python_create_array(array, stream.read(), length).release().ptr()); + } + + return nanobind::steal(list); + } + else + { + return nanobind::cast(stream.read>()); + } +} + +nanobind::object read_array_type(Stream &stream, PyJavaArray* object, ReflectionType type, std::size_t dimensions) +{ + if (dimensions == 0) + { + std::size_t size = stream.read(); + if (size == 0) + { + return nanobind::none(); + } + + switch(type) + { + case ReflectionType::CHAR: return nanobind::cast(std::string(1, stream.read())); + case ReflectionType::BYTE: return nanobind::int_(stream.read()); + case ReflectionType::BOOL: return nanobind::bool_(stream.read()); + case ReflectionType::SHORT: return nanobind::int_(stream.read()); + case ReflectionType::INT: return nanobind::int_(stream.read()); + case ReflectionType::LONG: return nanobind::int_(stream.read()); + case ReflectionType::FLOAT: return nanobind::float_(stream.read()); + case ReflectionType::DOUBLE: return nanobind::float_(stream.read()); + case ReflectionType::STRING: return nanobind::cast(stream.read()); + case ReflectionType::OBJECT: return python_create_object(object, stream.read()); + case ReflectionType::ARRAY: return python_create_array(object, stream.read(), 0); + default: return nanobind::none(); + } + } + + if (dimensions == 1) + { + switch(type) + { + case ReflectionType::CHAR: return read_array_type(stream, object); + case ReflectionType::BYTE: return read_array_type(stream, object); + case ReflectionType::BOOL: return read_array_type(stream, object); + case ReflectionType::SHORT: return read_array_type(stream, object); + case ReflectionType::INT: return read_array_type(stream, object); + case ReflectionType::LONG: return read_array_type(stream, object); + case ReflectionType::FLOAT: return read_array_type(stream, object); + case ReflectionType::DOUBLE: return read_array_type(stream, object); + case ReflectionType::STRING: return read_array_type(stream, object); + case ReflectionType::OBJECT: return read_array_type(stream, object); + case ReflectionType::ARRAY: return read_array_type(stream, object); + default: return nanobind::none(); + } + } + + std::size_t length = stream.read(); + PyObject* list = create_java_list(length); + + for (std::size_t i = 0; i < length; ++i) + { + PyList_SetItem(list, i, read_array_type(stream, object, type, dimensions - 1).release().ptr()); + } + + return nanobind::steal(list); +} +#else +int PyJavaArray_Clear(PyObject* object) +{ + PyJavaArray* py_java_array = reinterpret_cast(object); + if (py_java_array->eios) + { + (python->Py_CLEAR)(reinterpret_cast(py_java_array->eios)); + } + py_java_array->eios = nullptr; + py_java_array->array = nullptr; + return 0; +} + +void PyJavaArray_Dealloc(PyObject* object) +{ + EIOS* eios = PythonUnwrapEIOS(reinterpret_cast(object)->eios); + jarray array = reinterpret_cast(object)->array; + + if (eios && array) + { + eios->control_center->reflect_release_object(array); + } + + PyJavaArray_Clear(object); + python->PyObject_Free(object); +} + +PyObject* PyJavaArray_Str(PyObject* object) +{ + PyJavaArray* py_java_array = reinterpret_cast(object); + std::ostringstream stream; + + std::ios state(nullptr); + state.copyfmt(stream); + + stream << std::setfill('0') << std::uppercase << std::hex; + stream << "JavaArray("; + stream << "0x" << reinterpret_cast(object); + stream << "): "; + stream.copyfmt(state); + + stream << "{"<<"\n"; + stream<< " eios: " << py_java_array->eios << "\n"; + stream<< " array: " << py_java_array->array << "\n"; + stream<< "}"; + + std::string result = stream.str(); + return python->PyUnicode_FromStringAndSize(result.c_str(), result.size()); +} + +PyMemberDef PyJavaArray_Members[] = { + {nullptr} /* Sentinel */ +}; + +PyGetSetDef PyJavaArray_PropertyMembers[] = { + {(char*)"eios", [](PyObject* object, void *closure) -> PyObject* { + if (object) + { + PyObject* eios = reinterpret_cast(PythonGetEIOS(object)); + (python->Py_INCREF)(eios); + return eios; + } + + (python->Py_INCREF)(python->Py_GetNone_Object()); + return python->Py_GetNone_Object(); + }, nullptr, PyDoc_STR("EIOS Object"), nullptr}, + + {(char*)"size", [](PyObject* object, void *closure) -> PyObject* { + return object ? python->PyLong_FromLong(reinterpret_cast(object)->size) : python->PyLong_FromLong(0); + }, nullptr, PyDoc_STR("Array Size"), nullptr}, + + {nullptr} /* Sentinel */ +}; + +PyMethodDef PyJavaArray_Methods[] = { + {(char*)"get_length", PYTHON_FASTCALL(Python_JavaArray_GetLength), METH_FASTCALL, (char*)""}, + {(char*)"get_1d", PYTHON_FASTCALL(Python_JavaArray_Get1D), METH_FASTCALL | METH_KEYWORDS, (char*)""}, + {(char*)"get_2d", PYTHON_FASTCALL(Python_JavaArray_Get2D), METH_FASTCALL | METH_KEYWORDS, (char*)""}, + {(char*)"get_3d", PYTHON_FASTCALL(Python_JavaArray_Get3D), METH_FASTCALL | METH_KEYWORDS, (char*)""}, + {(char*)"get_4d", PYTHON_FASTCALL(Python_JavaArray_Get4D), METH_FASTCALL | METH_KEYWORDS, (char*)""}, + {(char*)"release", PYTHON_FASTCALL(Python_JavaArray_Release_Object), METH_FASTCALL, (char*)""}, + {nullptr} /* Sentinel */ +}; + +PyType_Slot PyJavaArray_Slots[] = { + {Py_tp_init, nullptr}, + {Py_tp_members, PyJavaArray_Members}, + {Py_tp_methods, PyJavaArray_Methods}, + {Py_tp_getset, PyJavaArray_PropertyMembers}, + {Py_tp_dealloc, reinterpret_cast(&PyJavaArray_Dealloc)}, + {Py_tp_clear, reinterpret_cast(&PyJavaArray_Clear)}, + {Py_tp_str, reinterpret_cast(&PyJavaArray_Str)}, + {Py_tp_doc, static_cast(const_cast(PyDoc_STR("JavaArray Structure")))}, + {Py_tp_setattr, nullptr}, + {0, NULL}, +}; + +PyType_Spec PyJavaArray_Spec { + "remote_input.JavaArray", + sizeof(PyJavaArray), + 0, + #if HAS_PYTHON_VERSION(0x030A0000) + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_DISALLOW_INSTANTIATION, + #else + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + #endif + PyJavaArray_Slots +}; + +// MARK: EXPORTS + +PyObject* read_array_type(Stream &stream, PyJavaArray* object, ReflectionType type, std::size_t dimensions); + +PyTypeObject* PyJavaArray_Type() noexcept +{ + static PyTypeObject* type = reinterpret_cast(python->PyType_FromSpec(&PyJavaArray_Spec)); + return type; +} + +template +typename std::enable_if::value, T>::type from_python_object(PyJavaArray* object) +{ + return PythonUnwrapJavaArray(object); +} + +PyObject* python_create_object(PyJavaArray* self, jobject object) noexcept +{ + return reinterpret_cast(PythonWrapJavaObject(self->eios, object)); +} + +PyObject* python_create_array(PyJavaArray* self, jarray object, std::size_t size) noexcept +{ + return reinterpret_cast(PythonWrapJavaArray(self->eios, object, size)); +} + +EIOS* python_get_eios(PyJavaArray* object) +{ + return PythonGetEIOS(reinterpret_cast(object))->native_eios; +} + +template +std::vector python_parse_arguments(const char* (&keywords)[keywords_size], PyObject* args[], Py_ssize_t args_length, PyObject* kwnames) +{ + std::vector result; + result.resize(args_length + keywords_size); + + for (std::size_t i = 0; i < args_length; ++i) + { + if (!(python->Py_IsNone)(args[i])) + { + result[i] = args[i]; + } + } + + if (kwnames) + { + for (std::size_t i = 0; i < python->PyTuple_Size(kwnames); ++i) + { + std::string keyword = from_python_object(python->PyTuple_GetItem(kwnames, i)); + if (auto it = std::find(std::begin(keywords), std::end(keywords), keyword); it != std::end(keywords)) + { + PyObject* argument = args[i + args_length]; + if (!(python->Py_IsNone)(argument)) + { + result[std::distance(std::begin(keywords), it)] = argument; + } + } + } + } + return result; +} + +PyObject* Python_JavaArray_GetLength(PyJavaArray* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + EIOS* eios = python_get_eios(self); + jarray array = from_python_object(self); + std::size_t length = eios->control_center->reflect_array_size(array); + return to_python_object(length); +} + +PyObject* Python_JavaArray_Get1D(PyJavaArray* self, PyObject* args[], Py_ssize_t args_length, PyObject* kwnames) noexcept +{ + static const char* kwlist[] = {"type", "indices", "index", "length", nullptr}; + std::vector arguments = python_parse_arguments(kwlist, args, args_length, kwnames); + + ReflectionType type = ReflectionType::OBJECT; + if (arguments[0]) + { + type = from_python_object(arguments[0]); + } + + PyObject* indices_object = arguments[1]; + PyObject* index_object = arguments[2]; + PyObject* length_object = arguments[3]; + + EIOS* eios = python_get_eios(self); + jarray array = from_python_object(self); + std::size_t index = 0; + std::size_t length = 0; + + if (!index_object && !length_object && !indices_object) + { + // Read entire array + Stream &stream = eios->control_center->reflect_array_all(array, type, 1)->data_stream(); + return read_array_type(stream, self, type, 1); + } + + if (indices_object) + { + // Read array indexed by indices + std::vector indices = from_python_array(indices_object); + Stream &stream = eios->control_center->reflect_array_indices(array, type, &indices[0], indices.size())->data_stream(); + return read_array_type(stream, self, type, 1); + } + + if (index_object) + { + index = from_python_object(index_object); + } + + if (length_object) + { + length = from_python_object(length_object); + } + + if (index_object && !length_object) + { + // Read array[index] + Stream &stream = eios->control_center->reflect_array(array, type, 1, index)->data_stream(); + return read_array_type(stream, self, type, 0); + } + + length = std::min(std::max(length, 1), self->size); + + // Read array of [index..control_center->reflect_array(array, type, length, index)->data_stream(); + return read_array_type(stream, self, type, 1); +} + +PyObject* Python_JavaArray_Get2D(PyJavaArray* self, PyObject* args[], Py_ssize_t args_length, PyObject* kwnames) noexcept +{ + static const char* kwlist[] = {"type", "x", "y", nullptr}; + std::vector arguments = python_parse_arguments(kwlist, args, args_length, kwnames); + + ReflectionType type = ReflectionType::OBJECT; + if (arguments[0]) + { + type = from_python_object(arguments[0]); + } + + EIOS* eios = python_get_eios(self); + jarray array = from_python_object(self); + + PyObject* x_object = arguments[1]; + PyObject* y_object = arguments[2]; + + // Array[x][y] + if (x_object && y_object) + { + std::size_t x = from_python_object(x_object); + std::size_t y = from_python_object(y_object); + + Stream &stream = eios->control_center->reflect_array(array, type, 1, x, y)->data_stream(); + return read_array_type(stream, self, type, 0); + } + + // Array[][] + Stream &stream = eios->control_center->reflect_array_all(array, type, 2)->data_stream(); + return read_array_type(stream, self, type, 2); +} + +PyObject* Python_JavaArray_Get3D(PyJavaArray* self, PyObject* args[], Py_ssize_t args_length, PyObject* kwnames) noexcept +{ + static const char* kwlist[] = {"type", "x", "y", "z", nullptr}; + std::vector arguments = python_parse_arguments(kwlist, args, args_length, kwnames); + + ReflectionType type = ReflectionType::OBJECT; + if (arguments[0]) + { + type = from_python_object(arguments[0]); + } + + PyObject* x_object = arguments[1]; + PyObject* y_object = arguments[2]; + PyObject* z_object = arguments[3]; + + EIOS* eios = python_get_eios(self); + jarray array = from_python_object(self); + + // Array[x][y][z] + if (x_object && y_object && z_object) + { + std::size_t x = from_python_object(x_object); + std::size_t y = from_python_object(y_object); + std::size_t z = from_python_object(z_object); + + Stream &stream = eios->control_center->reflect_array(array, type, 1, x, y, z)->data_stream(); + return read_array_type(stream, self, type, 0); + } + + // Array[][][] + Stream &stream = eios->control_center->reflect_array_all(array, type, 3)->data_stream(); + return read_array_type(stream, self, type, 3); +} + +PyObject* Python_JavaArray_Get4D(PyJavaArray* self, PyObject* args[], Py_ssize_t args_length, PyObject* kwnames) noexcept +{ + static const char* kwlist[] = {"type", "x", "y", "z", "w", nullptr}; + std::vector arguments = python_parse_arguments(kwlist, args, args_length, kwnames); + + ReflectionType type = ReflectionType::OBJECT; + if (arguments[0]) + { + type = from_python_object(arguments[0]); + } + + PyObject* x_object = arguments[1]; + PyObject* y_object = arguments[2]; + PyObject* z_object = arguments[3]; + PyObject* w_object = arguments[4]; + + EIOS* eios = python_get_eios(self); + jarray array = from_python_object(self); + + // Array[x][y][z][w] + if (x_object && y_object && z_object && w_object) + { + std::size_t x = from_python_object(x_object); + std::size_t y = from_python_object(y_object); + std::size_t z = from_python_object(z_object); + std::size_t w = from_python_object(z_object); + + Stream &stream = eios->control_center->reflect_array(array, type, 1, x, y, z, w)->data_stream(); + return read_array_type(stream, self, type, 0); + } + + // Array[][][][] + Stream &stream = eios->control_center->reflect_array_all(array, type, 4)->data_stream(); + return read_array_type(stream, self, type, 4); +} + +PyObject* Python_JavaArray_Release_Object(PyJavaArray* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + if (self->eios && self->array) + { + EIOS* eios = python_get_eios(self); + jarray object = from_python_object(self); + eios->control_center->reflect_release_object(object); + PyJavaArray_Clear(reinterpret_cast(self)); + } + + (python->Py_INCREF)(python->Py_GetNone_Object()); + return python->Py_GetNone_Object(); +} + +template +PyObject* read_array_type(Stream &stream, PyJavaArray* object) +{ + extern PyObject* create_java_list(Py_ssize_t length); + + if constexpr(std::is_same::value) + { + return to_python_array(stream.read>()); + } + else if constexpr(std::is_same::value) + { + return to_python_array(stream.read>()); + } + else if constexpr(std::is_same::value) + { + std::size_t length = stream.read(); + PyObject* list = create_java_list(length); //python->PyList_New(length); + for (std::size_t i = 0; i < length; ++i) + { + python->PyList_SetItem(list, i, python_create_object(object, stream.read())); + } + return list; + } + else if constexpr(std::is_same::value) + { + std::size_t length = stream.read(); + PyObject* list = create_java_list(length); //python->PyList_New(length); + for (std::size_t i = 0; i < length; ++i) + { + python->PyList_SetItem(list, i, python_create_array(object, stream.read(), length)); + } + return list; + } + else + { + return to_python_array(stream.read>()); + +// std::size_t length = stream.read(); +// std::size_t element_size = ControlCenter::reflect_size_for_type(type); +// PyObject* array_module = python->PyImport_ImportModule("array"); +// PyObject* array_type = python->PyObject_GetAttrString(array_module, "array"); +// (python->Py_DECREF)(array_module); +// +// PyObject* result = python->PyObject_CallFunction(array_type, "s[l]", "l", 0); +// (python->Py_DECREF)(array_type); +// +// PyObject *pysize = python->PyLong_FromSsize_t(length); +// PyObject *py_array = python->PyNumber_Multiply(result, pysize); +// (python->Py_DECREF)(pysize); +// +// PyObject *buffer_info = python->PyObject_CallMethod(py_array, "buffer_info", ""); +// PyObject* pyaddr = python->PyTuple_GetItem(buffer_info, 0); +// void *addr = python->PyLong_AsVoidPtr(pyaddr); +// +// stream.read(addr, length * element_size); +// return py_array; + } +} + +PyObject* read_array_type(Stream &stream, PyJavaArray* object, ReflectionType type, std::size_t dimensions) +{ + if (dimensions == 0) + { + std::size_t size = stream.read(); + if (size == 0) + { + (python->Py_INCREF)(python->Py_GetNone_Object()); + return python->Py_GetNone_Object(); + } + + switch(type) + { + case ReflectionType::CHAR: return to_python_object(stream.read()); + case ReflectionType::BYTE: return to_python_object(stream.read()); + case ReflectionType::BOOL: return to_python_object(stream.read()); + case ReflectionType::SHORT: return to_python_object(stream.read()); + case ReflectionType::INT: return to_python_object(stream.read()); + case ReflectionType::LONG: return to_python_object(stream.read()); + case ReflectionType::FLOAT: return to_python_object(stream.read()); + case ReflectionType::DOUBLE: return to_python_object(stream.read()); + case ReflectionType::STRING: return to_python_object(stream.read()); + case ReflectionType::OBJECT: return python_create_object(object, stream.read()); + case ReflectionType::ARRAY: return python_create_array(object, stream.read(), 0); + default: (python->Py_INCREF)(python->Py_GetNone_Object()); return python->Py_GetNone_Object(); + } + } + + if (dimensions == 1) + { + switch(type) + { + case ReflectionType::CHAR: return read_array_type(stream, object); + case ReflectionType::BYTE: return read_array_type(stream, object); + case ReflectionType::BOOL: return read_array_type(stream, object); + case ReflectionType::SHORT: return read_array_type(stream, object); + case ReflectionType::INT: return read_array_type(stream, object); + case ReflectionType::LONG: return read_array_type(stream, object); + case ReflectionType::FLOAT: return read_array_type(stream, object); + case ReflectionType::DOUBLE: return read_array_type(stream, object); + case ReflectionType::STRING: return read_array_type(stream, object); + case ReflectionType::OBJECT: return read_array_type(stream, object); + case ReflectionType::ARRAY: return read_array_type(stream, object); + default: (python->Py_INCREF)(python->Py_GetNone_Object()); return python->Py_GetNone_Object(); + } + } + + std::size_t length = stream.read(); + PyObject* result = python->PyList_New(length); + + for (std::size_t i = 0; i < length; ++i) + { + python->PyList_SetItem(result, i, read_array_type(stream, object, type, dimensions - 1)); + } + + return result; +} +#endif \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Plugin/Python/PythonJavaArray.hxx b/third-party/RemoteInput/RemoteInput/Plugin/Python/PythonJavaArray.hxx new file mode 100644 index 0000000..e4cd39c --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Plugin/Python/PythonJavaArray.hxx @@ -0,0 +1,21 @@ +// +// Created by Brandon on 2023-12-10. +// + +#ifndef REMOTEINPUT_PYTHONJAVAARRAY_HXX +#define REMOTEINPUT_PYTHONJAVAARRAY_HXX + +#include "PythonCommon.hxx" + +#if defined(USE_PYBIND11) +void declare_python_java_array(nanobind::module_ &module); +#else +PyObject* Python_JavaArray_GetLength(PyJavaArray* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_JavaArray_Get1D(PyJavaArray* self, PyObject* args[], Py_ssize_t args_length, PyObject* kwnames) noexcept; +PyObject* Python_JavaArray_Get2D(PyJavaArray* self, PyObject* args[], Py_ssize_t args_length, PyObject* kwnames) noexcept; +PyObject* Python_JavaArray_Get3D(PyJavaArray* self, PyObject* args[], Py_ssize_t args_length, PyObject* kwnames) noexcept; +PyObject* Python_JavaArray_Get4D(PyJavaArray* self, PyObject* args[], Py_ssize_t args_length, PyObject* kwnames) noexcept; +PyObject* Python_JavaArray_Release_Object(PyJavaArray* self, PyObject* args[], Py_ssize_t args_length) noexcept; +#endif + +#endif //REMOTEINPUT_PYTHONJAVAARRAY_HXX diff --git a/third-party/RemoteInput/RemoteInput/Plugin/Python/PythonJavaList.cxx b/third-party/RemoteInput/RemoteInput/Plugin/Python/PythonJavaList.cxx new file mode 100644 index 0000000..5eb4255 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Plugin/Python/PythonJavaList.cxx @@ -0,0 +1,173 @@ +// +// Created by Brandon on 2023-12-18. +// + +#include "PythonJavaList.hxx" +#include "PythonCommon.hxx" + +#if defined(USE_PYBIND11) +void (*PyList_tp_dealloc)(PyObject*) = nullptr; + +void PyList_Dealloc(PyObject* self) +{ + PyListObject* list = reinterpret_cast(self); + Py_ssize_t size = reinterpret_cast(list)->ob_size; //Py_SIZE(py_java_list); + + if (list->ob_item && size > 0) + { + PyEIOS* eios = nullptr; + PyObject** items = list->ob_item; + + std::vector java_objects; + java_objects.reserve(size); + + for (Py_ssize_t i = 0; i < size; ++i) + { + PyObject* object = items[i]; + if (object->ob_refcnt == 1) //Py_REFCNT + { + PyRemoteInputType type = GetPythonObjectType(object); + if (type == PyRemoteInputType::JAVA_OBJECT) + { + PyJavaObject* py_java_object = reinterpret_cast(object); + java_objects.push_back(py_java_object->object); + py_java_object->object = nullptr; + + if (!eios) + { + eios = py_java_object->eios; + } + } + else if (type == PyRemoteInputType::JAVA_ARRAY) + { + PyJavaArray* py_java_array = reinterpret_cast(object); + java_objects.push_back(py_java_array->array); + py_java_array->array = nullptr; + + if (!eios) + { + eios = py_java_array->eios; + } + } + } + } + + if (!java_objects.empty() && eios) + { + eios->native_eios->control_center->reflect_release_objects(&java_objects[0], java_objects.size()); + } + } + + if (PyList_tp_dealloc) + { + PyList_tp_dealloc(self); + } +} + +PyObject* create_java_list(Py_ssize_t length) +{ + PyObject* list = PyList_New(length); + static PyTypeObject* type = reinterpret_cast(Py_TYPE(list)); + static destructor original_des = type->tp_dealloc; + PyList_tp_dealloc = original_des; + if (PyList_tp_dealloc) + { + type->tp_dealloc = PyList_Dealloc; + } + + return list; +} +#else +#include + +extern std::unique_ptr python; + +#if defined(Py_LIMITED_API) +typedef struct { + PyObject_VAR_HEAD + PyObject **ob_item; + Py_ssize_t allocated; +} PyListObject; +#endif + +void (*PyList_tp_dealloc)(PyObject*) = nullptr; + +void PyList_Dealloc(PyObject* self) +{ + PyListObject* list = reinterpret_cast(self); + Py_ssize_t size = reinterpret_cast(list)->ob_size; //Py_SIZE(py_java_list); + + if (list->ob_item && size > 0) + { + PyEIOS* eios = nullptr; + PyObject** items = list->ob_item; + + std::vector java_objects; + java_objects.reserve(size); + + for (Py_ssize_t i = 0; i < size; ++i) + { + PyObject* object = items[i]; + if (object->ob_refcnt == 1) //Py_REFCNT + { + PyRemoteInputType type = GetPythonObjectType(object); + if (type == PyRemoteInputType::JAVA_OBJECT) + { + PyJavaObject* py_java_object = reinterpret_cast(object); + java_objects.push_back(py_java_object->object); + py_java_object->object = nullptr; + + if (!eios) + { + eios = py_java_object->eios; + } + } + else if (type == PyRemoteInputType::JAVA_ARRAY) + { + PyJavaArray* py_java_array = reinterpret_cast(object); + java_objects.push_back(py_java_array->array); + py_java_array->array = nullptr; + + if (!eios) + { + eios = py_java_array->eios; + } + } + } + } + + if (!java_objects.empty() && eios) + { + eios->native_eios->control_center->reflect_release_objects(&java_objects[0], java_objects.size()); + } + } + + if (PyList_tp_dealloc) + { + PyList_tp_dealloc(self); + } +} + +PyObject* create_java_list(Py_ssize_t length) +{ + #if defined(Py_LIMITED_API) + struct PyTypeObject + { + PyObject_VAR_HEAD + const char *tp_name; /* For printing, in format "." */ + Py_ssize_t tp_basicsize, tp_itemsize; /* For allocation */ + destructor tp_dealloc; + }; + #endif + + PyObject* list = python->PyList_New(length); + static PyTypeObject* type = reinterpret_cast((python->Py_TYPE)(list)); + static destructor original_des = type->tp_dealloc; + PyList_tp_dealloc = original_des; + if (PyList_tp_dealloc) + { + type->tp_dealloc = PyList_Dealloc; + } + return list; +} +#endif \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Plugin/Python/PythonJavaList.hxx b/third-party/RemoteInput/RemoteInput/Plugin/Python/PythonJavaList.hxx new file mode 100644 index 0000000..f5eb940 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Plugin/Python/PythonJavaList.hxx @@ -0,0 +1,12 @@ +// +// Created by Brandon on 2023-12-18. +// + +#ifndef REMOTEINPUT_PYTHONJAVALIST_HXX +#define REMOTEINPUT_PYTHONJAVALIST_HXX + +#include "PythonCommon.hxx" + +PyObject* create_java_list(Py_ssize_t length); + +#endif //REMOTEINPUT_PYTHONJAVALIST_HXX diff --git a/third-party/RemoteInput/RemoteInput/Plugin/Python/PythonJavaObject.cxx b/third-party/RemoteInput/RemoteInput/Plugin/Python/PythonJavaObject.cxx new file mode 100644 index 0000000..6333068 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Plugin/Python/PythonJavaObject.cxx @@ -0,0 +1,478 @@ +// +// Created by Brandon on 2022-09-21. +// + +#include "PythonJavaObject.hxx" + +#include +#include +#include + +#if defined(USE_PYBIND11) +nanobind::object Python_Reflect_Object(PyJavaObject* self, const std::string& cls, const std::string& field, const std::string& desc) noexcept +{ + EIOS* eios = self->eios->native_eios; + jobject object = self->object; + jobject result = eios->control_center->reflect_object({object, cls, field, desc}); + return python_create_object(self, result); +} + +nanobind::object Python_Reflect_IsSame_Object(PyJavaObject* self, const std::shared_ptr& other) noexcept +{ + EIOS* eios = self->eios->native_eios; + jboolean result = eios->control_center->reflect_is_objects_equal(self->object, other->object); + return nanobind::bool_(result); +} + +nanobind::object Python_Reflect_InstanceOf(PyJavaObject* self, const std::string& cls) noexcept +{ + EIOS* eios = self->eios->native_eios; + jobject object = self->object; + jboolean result = eios->control_center->reflect_instance_of(object, cls); + return nanobind::bool_(result); +} + +nanobind::object Python_Reflect_Bool(PyJavaObject* self, const std::string& cls, const std::string& field) noexcept +{ + EIOS* eios = self->eios->native_eios; + jobject object = self->object; + jboolean result = eios->control_center->reflect_boolean({object, cls, field, "Z"}); + return nanobind::bool_(result); +} + +nanobind::object Python_Reflect_Char(PyJavaObject* self, const std::string& cls, const std::string& field) noexcept +{ + EIOS* eios = self->eios->native_eios; + jobject object = self->object; + char result = eios->control_center->reflect_char({object, cls, field, "C"}); + return nanobind::cast(std::string(1, result)); +} + +nanobind::object Python_Reflect_Byte(PyJavaObject* self, const std::string& cls, const std::string& field) noexcept +{ + EIOS* eios = self->eios->native_eios; + jobject object = self->object; + std::uint8_t result = eios->control_center->reflect_byte({object, cls, field, "B"}); + return nanobind::int_(result); +} + +nanobind::object Python_Reflect_Short(PyJavaObject* self, const std::string& cls, const std::string& field) noexcept +{ + EIOS* eios = self->eios->native_eios; + jobject object = self->object; + std::int16_t result = eios->control_center->reflect_short({object, cls, field, "S"}); + return nanobind::int_(result); +} + +nanobind::object Python_Reflect_Int(PyJavaObject* self, const std::string& cls, const std::string& field) noexcept +{ + EIOS* eios = self->eios->native_eios; + jobject object = self->object; + std::int32_t result = eios->control_center->reflect_int({object, cls, field, "I"}); + return nanobind::int_(result); +} + +nanobind::object Python_Reflect_Long(PyJavaObject* self, const std::string& cls, const std::string& field) noexcept +{ + EIOS* eios = self->eios->native_eios; + jobject object = self->object; + std::int64_t result = eios->control_center->reflect_long({object, cls, field, "J"}); + return nanobind::int_(result); +} + +nanobind::object Python_Reflect_Float(PyJavaObject* self, const std::string& cls, const std::string& field) noexcept +{ + EIOS* eios = self->eios->native_eios; + jobject object = self->object; + float result = eios->control_center->reflect_float({object, cls, field, "F"}); + return nanobind::float_(result); +} + +nanobind::object Python_Reflect_Double(PyJavaObject* self, const std::string& cls, const std::string& field) noexcept +{ + EIOS* eios = self->eios->native_eios; + jobject object = self->object; + double result = eios->control_center->reflect_double({object, cls, field, "D"}); + return nanobind::float_(result); +} + +nanobind::object Python_Reflect_String(PyJavaObject* self, const std::string& cls, const std::string& field) noexcept +{ + EIOS* eios = self->eios->native_eios; + jobject object = self->object; + std::string result = eios->control_center->reflect_string({object, cls, field, "Ljava/lang/String;"}); + return nanobind::cast(result); +} + +// MARK: - Array Functions + +nanobind::object Python_Reflect_Array(PyJavaObject* self, const std::string& cls, const std::string& field, const std::string& desc) noexcept +{ + EIOS* eios = self->eios->native_eios; + jobject object = self->object; + + std::size_t array_size = 0; + jarray array = eios->control_center->reflect_array({object, cls, field, desc}, &array_size); + return python_create_array(self, array, array_size); +} + +void Python_JavaObject_Release_Object(PyJavaObject* self) noexcept +{ + if (self->eios && self->object) + { + EIOS* eios = self->eios->native_eios; + jobject object = self->object; + + eios->control_center->reflect_release_object(object); + + self->eios = nullptr; + self->object = nullptr; + } +} + +nanobind::object PyJavaObject_Str(PyJavaObject* self) +{ + auto eios = self->eios; + jobject object = self->object; + std::ostringstream stream; + + std::ios state(nullptr); + state.copyfmt(stream); + + stream << std::setfill('0') << std::uppercase << std::hex; + stream << "JavaObject("; + stream << "0x" << reinterpret_cast(object); + stream << "): "; + stream.copyfmt(state); + + stream << "{"<<"\n"; + stream<< " eios: " << eios << "\n"; + stream<< " object: " << object << "\n"; + stream<< "}"; + + return nanobind::cast(stream.str()); +} + +void declare_python_java_object(nanobind::module_ &module) +{ + nanobind::class_(module, "JavaObject") + // .def(pybind11::init<>()) // Cannot instantiate from Python + .def_ro("eios", &PyJavaObject::eios) + .def_ro("object", &PyJavaObject::object) + .def("reflect_object", &Python_Reflect_Object, + nanobind::arg("cls"), nanobind::arg("field"), nanobind::arg("desc")) + .def("is_same_object", &Python_Reflect_IsSame_Object, + nanobind::arg("other")) + .def("instance_of", &Python_Reflect_InstanceOf, + nanobind::arg("cls")) + .def("reflect_bool", &Python_Reflect_Bool, + nanobind::arg("cls"), nanobind::arg("field")) + .def("reflect_char", &Python_Reflect_Char, + nanobind::arg("cls"), nanobind::arg("field")) + .def("reflect_byte", &Python_Reflect_Byte, + nanobind::arg("cls"), nanobind::arg("field")) + .def("reflect_short", &Python_Reflect_Short, + nanobind::arg("cls"), nanobind::arg("field")) + .def("reflect_int", &Python_Reflect_Int, + nanobind::arg("cls"), nanobind::arg("field")) + .def("reflect_long", &Python_Reflect_Long, + nanobind::arg("cls"), nanobind::arg("field")) + .def("reflect_float", &Python_Reflect_Float, + nanobind::arg("cls"), nanobind::arg("field")) + .def("reflect_double", &Python_Reflect_Double, + nanobind::arg("cls"), nanobind::arg("field")) + .def("reflect_string", &Python_Reflect_String, + nanobind::arg("cls"), nanobind::arg("field")) + .def("reflect_array", &Python_Reflect_Array, + nanobind::arg("cls"), nanobind::arg("field"), nanobind::arg("desc")) + .def("__del__", &Python_JavaObject_Release_Object) + .def("__str__", &PyJavaObject_Str); +} +#else +int PyJavaObject_Clear(PyObject* object) +{ + PyJavaObject* py_java_object = reinterpret_cast(object); + if (py_java_object->eios) + { + (python->Py_CLEAR)(reinterpret_cast(py_java_object->eios)); + } + py_java_object->eios = nullptr; + py_java_object->object = nullptr; + return 0; +} + +void PyJavaObject_Dealloc(PyObject* object) +{ + EIOS* eios = PythonUnwrapEIOS(reinterpret_cast(object)->eios); + jobject java_object = reinterpret_cast(object)->object; + + if (eios && java_object) + { + eios->control_center->reflect_release_object(java_object); + } + + PyJavaObject_Clear(object); + python->PyObject_Free(object); +} + +PyObject* PyJavaObject_Str(PyObject* object) +{ + PyJavaObject* py_java_object = reinterpret_cast(object); + std::ostringstream stream; + + std::ios state(nullptr); + state.copyfmt(stream); + + stream << std::setfill('0') << std::uppercase << std::hex; + stream << "JavaObject("; + stream << "0x" << reinterpret_cast(object); + stream << "): "; + stream.copyfmt(state); + + stream << "{"<<"\n"; + stream<< " eios: " << py_java_object->eios << "\n"; + stream<< " object: " << py_java_object->object << "\n"; + stream<< "}"; + + std::string result = stream.str(); + return python->PyUnicode_FromStringAndSize(result.c_str(), result.size()); +} + +PyMemberDef PyJavaObject_Members[] = { + {nullptr} /* Sentinel */ +}; + +PyGetSetDef PyJavaObject_PropertyMembers[] = { + {(char*)"eios", [](PyObject* object, void *closure) -> PyObject* { + if (object) + { + PyObject* eios = reinterpret_cast(PythonGetEIOS(object)); + (python->Py_INCREF)(eios); + return eios; + } + + (python->Py_INCREF)(python->Py_GetNone_Object()); + return python->Py_GetNone_Object(); + }, nullptr, PyDoc_STR("EIOS Object"), nullptr}, + + {nullptr} /* Sentinel */ +}; + +PyMethodDef PyJavaObject_Methods[] = { + {(char*)"reflect_object", PYTHON_FASTCALL(Python_Reflect_Object), METH_FASTCALL, (char*)""}, + {(char*)"is_same_object", PYTHON_FASTCALL(Python_Reflect_IsSame_Object), METH_FASTCALL, (char*)""}, + {(char*)"is_instance_of", PYTHON_FASTCALL(Python_Reflect_InstanceOf), METH_FASTCALL, (char*)""}, + {(char*)"reflect_bool", PYTHON_FASTCALL(Python_Reflect_Bool), METH_FASTCALL, (char*)""}, + {(char*)"reflect_char", PYTHON_FASTCALL(Python_Reflect_Char), METH_FASTCALL, (char*)""}, + {(char*)"reflect_byte", PYTHON_FASTCALL(Python_Reflect_Byte), METH_FASTCALL, (char*)""}, + {(char*)"reflect_short", PYTHON_FASTCALL(Python_Reflect_Short), METH_FASTCALL, (char*)""}, + {(char*)"reflect_int", PYTHON_FASTCALL(Python_Reflect_Int), METH_FASTCALL, (char*)""}, + {(char*)"reflect_long", PYTHON_FASTCALL(Python_Reflect_Long), METH_FASTCALL, (char*)""}, + {(char*)"reflect_float", PYTHON_FASTCALL(Python_Reflect_Float), METH_FASTCALL, (char*)""}, + {(char*)"reflect_double", PYTHON_FASTCALL(Python_Reflect_Double), METH_FASTCALL, (char*)""}, + {(char*)"reflect_string", PYTHON_FASTCALL(Python_Reflect_String), METH_FASTCALL, (char*)""}, + {(char*)"reflect_array", PYTHON_FASTCALL(Python_Reflect_Array), METH_FASTCALL, (char*)""}, + {(char*)"release", PYTHON_FASTCALL(Python_JavaObject_Release_Object), METH_FASTCALL, (char*)""}, + {nullptr} /* Sentinel */ +}; + +PyType_Slot PyJavaObject_Slots[] = { + {Py_tp_init, nullptr}, + {Py_tp_members, PyJavaObject_Members}, + {Py_tp_methods, PyJavaObject_Methods}, + {Py_tp_getset, PyJavaObject_PropertyMembers}, + {Py_tp_dealloc, reinterpret_cast(&PyJavaObject_Dealloc)}, + {Py_tp_clear, reinterpret_cast(&PyJavaObject_Clear)}, + {Py_tp_str, reinterpret_cast(&PyJavaObject_Str)}, + {Py_tp_doc, static_cast(const_cast(PyDoc_STR("JavaObject Structure")))}, + {Py_tp_setattr, nullptr}, + {0, NULL}, +}; + +PyType_Spec PyJavaObject_Spec { + "remote_input.JavaObject", + sizeof(PyJavaObject), + 0, + #if HAS_PYTHON_VERSION(0x030A0000) + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_DISALLOW_INSTANTIATION, + #else + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + #endif + PyJavaObject_Slots +}; + +// MARK: EXPORTS + +PyTypeObject* PyJavaObject_Type() noexcept +{ + static PyTypeObject* type = reinterpret_cast(python->PyType_FromSpec(&PyJavaObject_Spec)); + return type; +} + +// MARK: Functions + +template +typename std::enable_if::value, T>::type from_python_object(PyJavaObject* object) +{ + return PythonUnwrapJavaObject(object); +} + +EIOS* python_get_eios(PyJavaObject* object) +{ + return PythonGetEIOS(reinterpret_cast(object))->native_eios; +} + +PyObject* python_create_object(PyJavaObject* self, jobject object) noexcept +{ + PyEIOS* eios = PythonGetEIOS(reinterpret_cast(self)); + return reinterpret_cast(PythonWrapJavaObject(eios, object)); +} + +PyObject* Python_Reflect_Object(PyJavaObject* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + EIOS* eios = python_get_eios(self); + jobject object = from_python_object(self); + std::string cls = from_python_object(args[0]); + std::string field = from_python_object(args[1]); + std::string desc = from_python_object(args[2]); + jobject result = eios->control_center->reflect_object({object, cls, field, desc}); + return python_create_object(self, result); +} + +PyObject* Python_Reflect_IsSame_Object(PyJavaObject* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + EIOS* eios = python_get_eios(self); + jobject a = from_python_object(self); + jobject b = from_python_object(args[0]); + jboolean result = eios->control_center->reflect_is_objects_equal(a, b); + return to_python_object(result); +} + +PyObject* Python_Reflect_InstanceOf(PyJavaObject* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + EIOS* eios = python_get_eios(self); + jobject object = from_python_object(self); + std::string cls = from_python_object(args[0]); + jboolean result = eios->control_center->reflect_instance_of(object, cls); + return to_python_object(result); +} + +PyObject* Python_Reflect_Bool(PyJavaObject* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + EIOS* eios = python_get_eios(self); + jobject object = from_python_object(self); + std::string cls = from_python_object(args[0]); + std::string field = from_python_object(args[1]); + jboolean result = eios->control_center->reflect_boolean({object, cls, field, "Z"}); + return to_python_object(result); +} + +PyObject* Python_Reflect_Char(PyJavaObject* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + EIOS* eios = python_get_eios(self); + jobject object = from_python_object(self); + std::string cls = from_python_object(args[0]); + std::string field = from_python_object(args[1]); + char result = eios->control_center->reflect_char({object, cls, field, "C"}); + return to_python_object(result); +} + +PyObject* Python_Reflect_Byte(PyJavaObject* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + EIOS* eios = python_get_eios(self); + jobject object = from_python_object(self); + std::string cls = from_python_object(args[0]); + std::string field = from_python_object(args[1]); + std::uint8_t result = eios->control_center->reflect_byte({object, cls, field, "B"}); + return to_python_object(result); +} + +PyObject* Python_Reflect_Short(PyJavaObject* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + EIOS* eios = python_get_eios(self); + jobject object = from_python_object(self); + std::string cls = from_python_object(args[0]); + std::string field = from_python_object(args[1]); + std::int16_t result = eios->control_center->reflect_short({object, cls, field, "S"}); + return to_python_object(result); +} + +PyObject* Python_Reflect_Int(PyJavaObject* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + EIOS* eios = python_get_eios(self); + jobject object = from_python_object(self); + std::string cls = from_python_object(args[0]); + std::string field = from_python_object(args[1]); + std::int32_t result = eios->control_center->reflect_int({object, cls, field, "I"}); + return to_python_object(result); +} + +PyObject* Python_Reflect_Long(PyJavaObject* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + EIOS* eios = python_get_eios(self); + jobject object = from_python_object(self); + std::string cls = from_python_object(args[0]); + std::string field = from_python_object(args[1]); + std::int64_t result = eios->control_center->reflect_long({object, cls, field, "J"}); + return to_python_object(result); +} + +PyObject* Python_Reflect_Float(PyJavaObject* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + EIOS* eios = python_get_eios(self); + jobject object = from_python_object(self); + std::string cls = from_python_object(args[0]); + std::string field = from_python_object(args[1]); + float result = eios->control_center->reflect_float({object, cls, field, "F"}); + return to_python_object(result); +} + +PyObject* Python_Reflect_Double(PyJavaObject* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + EIOS* eios = python_get_eios(self); + jobject object = from_python_object(self); + std::string cls = from_python_object(args[0]); + std::string field = from_python_object(args[1]); + double result = eios->control_center->reflect_double({object, cls, field, "D"}); + return to_python_object(result); +} + +PyObject* Python_Reflect_String(PyJavaObject* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + EIOS* eios = python_get_eios(self); + jobject object = from_python_object(self); + std::string cls = from_python_object(args[0]); + std::string field = from_python_object(args[1]); + std::string result = eios->control_center->reflect_string({object, cls, field, "Ljava/lang/String;"}); + return to_python_object(result); +} + +// MARK: - Array Functions + +PyObject* Python_Reflect_Array(PyJavaObject* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + EIOS* eios = python_get_eios(self); + jobject object = from_python_object(self); + std::string cls = from_python_object(args[0]); + std::string field = from_python_object(args[1]); + std::string desc = from_python_object(args[2]); + + std::size_t array_size = 0; + jarray array = eios->control_center->reflect_array({object, cls, field, desc}, &array_size); + return reinterpret_cast(PythonWrapJavaArray(PythonGetEIOS(reinterpret_cast(self)), array, array_size)); +} + +PyObject* Python_JavaObject_Release_Object(PyJavaObject* self, PyObject* args[], Py_ssize_t args_length) noexcept +{ + if (self->eios && self->object) + { + EIOS* eios = python_get_eios(self); + jobject object = from_python_object(self); + eios->control_center->reflect_release_object(object); + PyJavaObject_Clear(reinterpret_cast(self)); + } + + (python->Py_INCREF)(python->Py_GetNone_Object()); + return python->Py_GetNone_Object(); +} +#endif \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Plugin/Python/PythonJavaObject.hxx b/third-party/RemoteInput/RemoteInput/Plugin/Python/PythonJavaObject.hxx new file mode 100644 index 0000000..1b6c108 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Plugin/Python/PythonJavaObject.hxx @@ -0,0 +1,29 @@ +// +// Created by Brandon on 2022-09-21. +// + +#ifndef REMOTEINPUT_PYTHONJAVAOBJECT_HXX +#define REMOTEINPUT_PYTHONJAVAOBJECT_HXX + +#include "PythonCommon.hxx" + +#if defined(USE_PYBIND11) +void declare_python_java_object(nanobind::module_ &module); +#else +PyObject* Python_Reflect_Object(PyJavaObject* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_Reflect_IsSame_Object(PyJavaObject* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_Reflect_InstanceOf(PyJavaObject* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_Reflect_Bool(PyJavaObject* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_Reflect_Char(PyJavaObject* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_Reflect_Byte(PyJavaObject* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_Reflect_Short(PyJavaObject* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_Reflect_Int(PyJavaObject* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_Reflect_Long(PyJavaObject* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_Reflect_Float(PyJavaObject* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_Reflect_Double(PyJavaObject* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_Reflect_String(PyJavaObject* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_Reflect_Array(PyJavaObject* self, PyObject* args[], Py_ssize_t args_length) noexcept; +PyObject* Python_JavaObject_Release_Object(PyJavaObject* self, PyObject* args[], Py_ssize_t args_length) noexcept; +#endif + +#endif //REMOTEINPUT_PYTHONJAVAOBJECT_HXX diff --git a/third-party/RemoteInput/RemoteInput/Plugin/Python/PythonMacros.hxx b/third-party/RemoteInput/RemoteInput/Plugin/Python/PythonMacros.hxx new file mode 100644 index 0000000..7308532 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Plugin/Python/PythonMacros.hxx @@ -0,0 +1,53 @@ +// +// Created by Brandon on 2024-09-17. +// + +#ifndef REMOTEINPUT_PYTHONMACROS_HXX +#define REMOTEINPUT_PYTHONMACROS_HXX + +#if !defined(USE_PYBIND11) +// On Windows, Python is really badly implemented, +// so we must include `math.h` and not `cmath` to get the _hpyot symbol +#include +#include +#include +#include "object.h" + +#if defined(_WIN32) || defined(_WIN64) + #include "structmember.h" +#elif defined(__linux__) && (defined(__x86_64__) || defined(__i386__)) + #include "structmember.h" +#elif defined(__APPLE__) + #if __has_include() + #include /* Python.framework */ + #else + #include "structmember.h" + #endif +#elif defined(__aarch64__) || defined(__arm__) + #include "structmember.h" +#endif + +#if defined(_WIN32) || defined(_WIN64) + #include +#else + #include +#endif // defined + +#if defined(Py_LIMITED_API) + #ifndef HAS_PYTHON_VERSION + #define HAS_PYTHON_VERSION(MIN_VERSION) ((Py_LIMITED_API >= MIN_VERSION) && (PY_VERSION_HEX >= MIN_VERSION)) + #endif +#else + #ifndef HAS_PYTHON_VERSION + #define HAS_PYTHON_VERSION(MIN_VERSION) (PY_VERSION_HEX >= MIN_VERSION) + #endif +#endif + +#if HAS_PYTHON_VERSION(0x03070000) + #ifndef METH_FASTCALL + #define METH_FASTCALL 0x0080 // Python 3.7 incorrectly labels this as Py_LIMITED_API + #endif +#endif +#endif + +#endif //REMOTEINPUT_PYTHONMACROS_HXX diff --git a/third-party/RemoteInput/RemoteInput/Plugin/Python/PythonPlugin.cxx b/third-party/RemoteInput/RemoteInput/Plugin/Python/PythonPlugin.cxx new file mode 100644 index 0000000..a4f123b --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Plugin/Python/PythonPlugin.cxx @@ -0,0 +1,225 @@ +// +// PythonPlugin.cxx +// RemoteInput +// +// Created by Brandon on 2020-11-09. +// Copyright © 2020 XIO. All rights reserved. +// + +#include "PythonPlugin.hxx" + +#include "PythonCommon.hxx" +#include "PythonEIOS.hxx" +#include "PythonJavaObject.hxx" +#include "PythonJavaArray.hxx" + +#if defined(USE_PYBIND11) +void PrintPythonVersionInfo() +{ + fprintf(stdout, "RUNNING WITH: %s\n", PY_VERSION); + fprintf(stdout, "COMPILED WITH: %d.%d.%d\n", PY_MAJOR_VERSION, PY_MINOR_VERSION, PY_MICRO_VERSION); + fflush(stdout); +} + +NB_MODULE(remote_input, module) { + #if defined(DEBUG) + PrintPythonVersionInfo(); + #endif + + // Register enums + nanobind::enum_(module, "ImageFormat") + .value("BGR_BGRA", ImageFormat::BGR_BGRA) + .value("BGRA", ImageFormat::BGRA) + .value("RGBA", ImageFormat::RGBA) + .value("ARGB", ImageFormat::ARGB) + .value("ABGR", ImageFormat::ABGR); + + nanobind::enum_(module, "ReflectType") + .value("BOOLEAN", ReflectionType::BOOL) + .value("CHAR", ReflectionType::CHAR) + .value("BYTE", ReflectionType::BYTE) + .value("SHORT", ReflectionType::SHORT) + .value("INT", ReflectionType::INT) + .value("LONG", ReflectionType::LONG) + .value("FLOAT", ReflectionType::FLOAT) + .value("DOUBLE", ReflectionType::DOUBLE) + .value("STRING", ReflectionType::STRING) + .value("OBJECT", ReflectionType::OBJECT) + .value("ARRAY", ReflectionType::ARRAY); + + declare_python_eios(module); + declare_python_java_object(module); + declare_python_java_array(module); +} +#else +#include "Python.hxx" + +#include +#include + +std::unique_ptr python; + +// MODULE + +static struct PyMethodDef RemoteInputMethods[] = +{ + //{"load_module", nullptr, METH_VARARGS, "Documentation"}, + {nullptr} /* SENTINEL */ +}; + +static struct PyModuleDef RemoteInputModule = +{ + PyModuleDef_HEAD_INIT, + "remote_input", + "Remote Input Module", + -1, + RemoteInputMethods, + nullptr, + nullptr, + nullptr, + nullptr +}; + +void PrintPythonVersionInfo() +{ + #if HAS_PYTHON_VERSION(0x030B1000) + fprintf(stdout, "RUNNING WITH: %zu.%zu.%zu\n", Py_Version >> 24 & 0xFF, Py_Version >> 16 & 0xFF, Py_Version >> 8 & 0xFF); + fprintf(stdout, "COMPILED WITH: %d.%d.%d\n", PY_MAJOR_VERSION, PY_MINOR_VERSION, PY_MICRO_VERSION); + #else + unsigned long Py_Version = 0; + + // Py_Initialize(); + PyObject* sys_import = python->PyUnicode_FromString("sys"); + if (sys_import) + { + PyObject* sys_module = python->PyImport_Import(sys_import); + if (sys_module) + { + PyObject* sys_module_info = python->PyModule_GetDict(sys_module); + if (sys_module_info) + { + PyObject* hex_version = python->PyDict_GetItemString(sys_module_info, "hexversion"); + if (hex_version) + { + Py_Version = python->PyLong_AsUnsignedLong(hex_version); + } + } + (python->Py_XDECREF)(sys_module); + } + (python->Py_XDECREF)(sys_import); + } + // Py_Finalize(); + + if (Py_Version > 0) + { + fprintf(stdout, "RUNNING WITH: %lu.%lu.%lu\n", Py_Version >> 24 & 0xFF, Py_Version >> 16 & 0xFF, Py_Version >> 8 & 0xFF); + } + else + { + fprintf(stdout, "RUNNING WITH: %s\n", python->Py_GetVersion()); + } + + fprintf(stdout, "COMPILED WITH: %d.%d.%d\n", PY_MAJOR_VERSION, PY_MINOR_VERSION, PY_MICRO_VERSION); + #endif + + fflush(stdout); +} + +bool RegisterEnum(PyObject* module, const std::string &enum_name, const std::unordered_map &enumeration) +{ + PyObject* enum_module = python->PyImport_ImportModule("enum"); + if (!enum_module) + { + return false; + } + + PyObject* constants = python->PyDict_New(); + for (const auto& it : enumeration) + { + python->PyDict_SetItemString(constants, it.first.c_str(), python->PyLong_FromLong(it.second)); + } + + PyObject* enum_class = python->PyObject_CallMethod(enum_module, "IntEnum", "sO", enum_name.c_str(), constants); + (python->Py_CLEAR)(constants); + (python->Py_CLEAR)(enum_module); + + if (python->PyModule_AddObject(module, enum_name.c_str(), enum_class) < 0) + { + (python->Py_CLEAR)(enum_class); + return false; + } + return true; +} + +bool RegisterPythonType(PyObject* module, PyTypeObject* type_object, const char* type_name) +{ + if (python->PyType_Ready(type_object) < 0) + { + return false; + } + + (python->Py_INCREF)(reinterpret_cast(type_object)); + if (python->PyModule_AddObject(module, type_name, reinterpret_cast(type_object)) < 0) + { + (python->Py_DECREF)(reinterpret_cast(type_object)); + return false; + } + return true; +} + +//PyMODINIT_FUNC MODINIT(remote_input)() +PyObject* PyInit_remote_input() +{ + python = std::make_unique(); + if (!python->IsPythonLoaded()) + { + fprintf(stderr, "Cannot Find libpython3\n"); + return nullptr; + } + + #if defined(DEBUG) + PrintPythonVersionInfo(); + #endif + + PyObject* module = (python->PyModule_Create2)(&RemoteInputModule, PYTHON_ABI_VERSION); + + if (!RegisterPythonType(module, PyEIOS_Type(), "EIOS")) + { + return nullptr; + } + + if (!RegisterPythonType(module, PyJavaObject_Type(), "JavaObject")) + { + return nullptr; + } + + if (!RegisterPythonType(module, PyJavaArray_Type(), "JavaArray")) + { + return nullptr; + } + + RegisterEnum(module, "ImageFormat", { + {"BGR_BGRA", static_cast(ImageFormat::BGR_BGRA)}, + {"BGRA", static_cast(ImageFormat::BGRA)}, + {"RGBA", static_cast(ImageFormat::RGBA)}, + {"ARGB", static_cast(ImageFormat::ARGB)}, + {"ABGR", static_cast(ImageFormat::ABGR)}, + }); + + RegisterEnum(module, "ReflectType", { + {"BOOLEAN", static_cast(ReflectionType::BOOL)}, + {"CHAR", static_cast(ReflectionType::CHAR)}, + {"BYTE", static_cast(ReflectionType::BYTE)}, + {"SHORT", static_cast(ReflectionType::SHORT)}, + {"INT", static_cast(ReflectionType::INT)}, + {"LONG", static_cast(ReflectionType::LONG)}, + {"FLOAT", static_cast(ReflectionType::FLOAT)}, + {"DOUBLE", static_cast(ReflectionType::DOUBLE)}, + {"STRING", static_cast(ReflectionType::STRING)}, + {"OBJECT", static_cast(ReflectionType::OBJECT)}, + {"ARRAY", static_cast(ReflectionType::ARRAY)}, + }); + + return module; +} +#endif \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Plugin/Python/PythonPlugin.hxx b/third-party/RemoteInput/RemoteInput/Plugin/Python/PythonPlugin.hxx new file mode 100644 index 0000000..32b814b --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Plugin/Python/PythonPlugin.hxx @@ -0,0 +1,40 @@ +// +// PythonPlugin.hxx +// RemoteInput +// +// Created by Brandon on 2020-11-09. +// Copyright © 2020 XIO. All rights reserved. +// + +#ifndef REMOTEINPUT_PYTHONPLUGIN_HXX +#define REMOTEINPUT_PYTHONPLUGIN_HXX + +#include "Plugin.hxx" + +#ifndef USE_PYBIND11 + +//On Windows, Python is really badly implemented, so we must include `math.h` and not `cmath` to get the _hpyot symbol +#include +#include + +#if defined(_WIN32) || defined (_WIN64) +#define EXPORT __declspec(dllexport) +#else +#define EXPORT [[gnu::visibility("default")]] +#endif + +#if PY_MAJOR_VERSION < 3 +#define MODINIT(name) init##name +#else +#define MODINIT(name) PyInit_##name +#endif + +extern "C" +{ + //PyMODINIT_FUNC MODINIT(remote_input)(); + EXPORT PyObject* PyInit_remote_input(); +} + +#endif + +#endif //REMOTEINPUT_PYTHONPLUGIN_HXX diff --git a/third-party/RemoteInput/RemoteInput/Plugin/Signal.hxx b/third-party/RemoteInput/RemoteInput/Plugin/Signal.hxx new file mode 100644 index 0000000..eb28e37 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Plugin/Signal.hxx @@ -0,0 +1,120 @@ +// +// Signal.hxx +// RemoteInput +// +// Created by Brandon on 2019-12-15. +// Copyright © 2019 XIO. All rights reserved. +// + +#ifndef SIGNAL_HXX_INCLUDED +#define SIGNAL_HXX_INCLUDED + +#include "Synchronization.hxx" + +template +class Signal +{ +private: + T primitive; + +public: + Signal() noexcept requires synchronization::lockable || synchronization::acquirable; + Signal(std::string name) noexcept requires synchronization::lockable || synchronization::acquirable; + ~Signal() = default; + + Signal(const Signal &other) = delete; + Signal& operator = (const Signal &other) = delete; + + void signal() noexcept; + void wait() noexcept; + + bool try_wait() noexcept; + + template + bool try_wait_for(const std::chrono::duration& relative_time) noexcept; + + template + bool try_wait_until(const std::chrono::time_point& absolute_time) noexcept; +}; + +template +Signal::Signal() noexcept requires synchronization::lockable || synchronization::acquirable + : primitive() +{ +} + +template +Signal::Signal(std::string name) noexcept requires synchronization::lockable || synchronization::acquirable + : primitive(name) +{ +} + +template +void Signal::signal() noexcept +{ + if constexpr(synchronization::lockable) + { + primitive.unlock(); + } + else + { + primitive.release(); + } +} + +template +void Signal::wait() noexcept +{ + if constexpr(synchronization::lockable) + { + primitive.lock(); + } + else + { + primitive.acquire(); + } +} + +template +bool Signal::try_wait() noexcept +{ + if constexpr(synchronization::lockable) + { + return primitive.try_lock(); + } + else + { + return primitive.try_acquire(); + } +} + + +template +template +bool Signal::try_wait_for(const std::chrono::duration& relative_time) noexcept +{ + if constexpr(synchronization::lockable) + { + return primitive.try_lock_for(relative_time); + } + else + { + return primitive.try_acquire_for(relative_time); + } +} + +template +template +bool Signal::try_wait_until(const std::chrono::time_point& absolute_time) noexcept +{ + if constexpr(synchronization::lockable) + { + return primitive.try_lock_until(absolute_time); + } + else + { + return primitive.try_acquire_until(absolute_time); + } +} + +#endif /* SIGNAL_HXX_INCLUDED */ diff --git a/third-party/RemoteInput/RemoteInput/Plugin/SimbaPlugin.cxx b/third-party/RemoteInput/RemoteInput/Plugin/SimbaPlugin.cxx new file mode 100644 index 0000000..4b41506 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Plugin/SimbaPlugin.cxx @@ -0,0 +1,2565 @@ +#include "SimbaPlugin.hxx" +#include +#include +#include +#include +#include + +#include "ControlCenter.hxx" +#include "RemoteVM.hxx" +#include "Module.hxx" +#include "NativePlugin.hxx" +#include "TypeTraits.hxx" +#include "TypeTraits_Functional.hxx" + +#if defined(_WIN32) || defined(_WIN64) +extern HMODULE module; +#endif + +#if defined(PASCAL_CALLING_CONVENTION) +TMemoryManager PLUGIN_MEMORY_MANAGER = {0}; +#endif + +// MARK: - Simba v1.4 - Deprecated +TSimbaMethods PLUGIN_SYNC_METHODS = {nullptr}; +TSimbaMemoryAllocators PLUGIN_MEMORY_ALLOCATORS = {nullptr}; + +// MARK: - Simba v1.4+ +TSimbaInfomation PLUGIN_SIMBA_INFO = {0}; +TSimbaMethodsExtended PLUGIN_SIMBA_METHODS = {0}; + +// Internal +extern std::unique_ptr control_center; + + +// MARK: - DECLARATIONS + +template +int PascalHigh(T* Arr) noexcept; + +template +int PascalLength(T* Arr) noexcept; + +template +T* AllocateArray(std::size_t size, std::size_t element_size = sizeof(T)) noexcept; + +template +T* GetArray(void* ptr, std::size_t* size) noexcept; + +template +std::vector GetArrayAsVector(void* ptr) noexcept; + +template +T* AllocateString(std::size_t size, std::size_t element_size = sizeof(T)) noexcept; + +template +T* GetString(void* ptr, std::size_t* size) noexcept; + +template +T PascalRead(void* ptr) noexcept; + +template +void PascalWrite(void* ptr, T result) noexcept; + +void PascalWrite(EIOS* eios, Stream &stream, void* ptr, ReflectionType type) noexcept; + + +// MARK: - EXPORTS + +int GetPluginABIVersion() noexcept +{ + return 2; +} + +int GetFunctionCount() noexcept +{ + return PascalExportCount; +} + +int GetTypeCount() noexcept +{ + return PascalTypeCount; +} + +int GetFunctionInfo(int Index, void** Address, char** Definition) noexcept +{ + if (Index < PascalExportCount) + { + #if defined(_WIN32) || defined(_WIN64) + *Address = (void*)GetProcAddress(module, PascalExports[Index * 2]); + #else + *Address = (void*)dlsym(RTLD_DEFAULT, PascalExports[Index * 2]); + #endif + strcpy(*Definition, PascalExports[Index * 2 + 1]); + return Index; + } + return -1; +} + +int GetTypeInfo(int Index, char** Type, char** Definition) noexcept +{ + if (Index < PascalTypeCount) + { + strcpy(*Type, PascalTypes[Index * 2 + 0]); + strcpy(*Definition, PascalTypes[Index * 2 + 1]); + return Index; + } + return -1; +} + +#if defined(PASCAL_CALLING_CONVENTION) +void SetPluginMemManager(TMemoryManager MemMgr) noexcept +{ + PLUGIN_MEMORY_MANAGER = MemMgr; +} +#endif + +void SetPluginSimbaMethods(TSimbaMethods Methods) noexcept +{ + PLUGIN_SYNC_METHODS = Methods; +} + +void SetPluginSimbaMemoryAllocators(TSimbaMemoryAllocators Allocators) noexcept +{ + PLUGIN_MEMORY_ALLOCATORS = Allocators; +} + +void RegisterSimbaPlugin(TSimbaInfomation* Information, TSimbaMethodsExtended* Methods) noexcept +{ + if (Information->SimbaMajor < 1500) + { + std::memcpy(&PLUGIN_SIMBA_INFO, Information, offsetof(TSimbaInfomation, Compiler)); + std::memcpy(&PLUGIN_SIMBA_METHODS, Methods, offsetof(TSimbaMethodsExtended, RaiseException)); + } + else + { + std::memcpy(&PLUGIN_SIMBA_INFO, Information, sizeof(TSimbaInfomation)); + std::memcpy(&PLUGIN_SIMBA_METHODS, Methods, sizeof(TSimbaMethodsExtended)); + } +} + +void OnAttach(void* info) noexcept +{ + control_center.reset(); + EIOS_KillZombieClients(); +} + +void OnDetach() noexcept +{ + control_center.reset(); + EIOS_KillZombieClients(); +} + + +// MARK: - Reflection + +STRUCT_PACK(typedef struct +{ + std::make_signed_t refCount; + std::make_signed_t length; + char data[]; +}) PascalArray; + +#if defined(FPC2) +STRUCT_PACK(typedef struct +{ + std::make_signed_t refCount; + std::make_signed_t length; + char data[]; +}) PascalString; +#else + #if defined(_MSC_VER) + #pragma pack(push, 1) + typedef struct + { + std::uint16_t codePage; + std::uint16_t elementSize; + #if defined(__x86_64__) + std::uint32_t dummy; + #endif + std::make_signed_t refCount; + std::make_signed_t length; + char data[]; + } PascalString; + #pragma pack(pop) + #else + typedef struct + { + std::uint16_t codePage; + std::uint16_t elementSize; + #if defined(__x86_64__) + std::uint32_t dummy; + #endif + std::make_signed_t refCount; + std::make_signed_t length; + char data[]; + } __attribute__((__packed__)) PascalString; + #endif +#endif // defined + +STRUCT_PACK(typedef struct +{ + char* cls; + char* field; + char* desc; +}) PascalField; + +#if defined(_MSC_VER) +#pragma pack(push, 1) +template +struct StaticPascalArray +{ + const std::make_signed_t refCount = -1; + const std::make_signed_t length = size; + T data[size] = {0}; +}; +#pragma pack(pop) +#else +template +struct __attribute__((__packed__)) StaticPascalArray +{ + const std::make_signed_t refCount = -1; + const std::make_signed_t length = size; + T data[size] = {0}; +}; +#endif + +template +void call_pascal_function(void** Params, void** Result, F&& fn, Extras... extras) +{ + using Args = typename function_arguments_type::type; + using R = typename function_return_type::type; + //using R = typename decltype(std::function{fn})::result_type; + + std::uint32_t ArgumentIndex = 0; + auto arguments = tuple_transform([&Params, &ArgumentIndex](auto arg) { + return PascalRead(Params[ArgumentIndex++]); + }, std::tuple()); + + auto native_arguments = std::tuple_cat(tuple_remove_last_n(arguments), std::make_tuple(extras...)); + + if constexpr(std::is_same::value) + { + return std::apply([fn](auto... args) { + return fn(args...); + }, native_arguments); + } + else + { + R result = std::apply([fn](auto... args) { + return fn(args...); + }, native_arguments); + + PascalWrite(Result, result); + } +} + +// MARK: Single Functions + +void Pascal_Reflect_ClassLoader(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + if (eios) + { + jobject object = PascalRead(Params[1]); + + PascalWrite(Result, eios->control_center->reflect_class_loader(object)); + } +} + +void Pascal_Reflect_Equal(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + if (eios) + { + jobject first = PascalRead(Params[1]); + jobject second = PascalRead(Params[2]); + + PascalWrite(Result, eios->control_center->reflect_is_objects_equal(first, second)); + } +} + +void Pascal_Reflect_InstanceOf(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + if (eios) + { + jobject object = PascalRead(Params[1]); + char* cls = PascalRead(Params[2]); + + PascalWrite(Result, eios->control_center->reflect_instance_of(object, cls)); + } +} + +void Pascal_Reflect_Object(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + if (eios) + { + jobject object = PascalRead(Params[1]); + PascalField* field = PascalRead(Params[2]); + + ReflectionHook hook{object, field->cls, field->field, field->desc}; + PascalWrite(Result, eios->control_center->reflect_object(hook)); + } +} + +void Pascal_Reflect_Release_Object(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + if (eios) + { + jobject object = PascalRead(Params[1]); + if (object) + { + eios->control_center->reflect_release_object(object); + } + } +} + +void Pascal_Reflect_Release_Objects(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + if (eios) + { + std::size_t length = 0; + jobject* objects = GetArray(PascalRead(Params[1]), &length); + + //Make sure we're not freeing null objects.. + if (objects && length > 0) + { + //This is the fastest possible way to remove duplicates.. + //Do NOT use `unordered_set` constructor instead of the for-loop. + //It is slower than emplace/insert. + std::unordered_set set; + for (std::size_t i = 0; i < length; ++i) + { + set.insert(objects[i]); + } + + //Remove null objects + set.erase(nullptr); + + //Create a contiguous array of objects for the client. + std::vector objects; + objects.assign(set.begin(), set.end()); + + //Make sure we're not freeing null objects.. + if (objects.size()) + { + eios->control_center->reflect_release_objects(objects.data(), objects.size()); + } + } + } +} + +void Pascal_Reflect_Boolean(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + if (eios) + { + jobject object = PascalRead(Params[1]); + PascalField* field = PascalRead(Params[2]); + + ReflectionHook hook{object, field->cls, field->field, field->desc}; + PascalWrite(Result, eios->control_center->reflect_boolean(hook)); + } +} + +void Pascal_Reflect_Char(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + if (eios) + { + jobject object = PascalRead(Params[1]); + PascalField* field = PascalRead(Params[2]); + + ReflectionHook hook{object, field->cls, field->field, field->desc}; + PascalWrite(Result, eios->control_center->reflect_char(hook)); + } +} + +void Pascal_Reflect_Byte(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + if (eios) + { + jobject object = PascalRead(Params[1]); + PascalField* field = PascalRead(Params[2]); + + ReflectionHook hook{object, field->cls, field->field, field->desc}; + PascalWrite(Result, eios->control_center->reflect_byte(hook)); + } +} + +void Pascal_Reflect_Short(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + if (eios) + { + jobject object = PascalRead(Params[1]); + PascalField* field = PascalRead(Params[2]); + + ReflectionHook hook{object, field->cls, field->field, field->desc}; + PascalWrite(Result, eios->control_center->reflect_short(hook)); + } +} + +void Pascal_Reflect_Int(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + if (eios) + { + jobject object = PascalRead(Params[1]); + PascalField* field = PascalRead(Params[2]); + + ReflectionHook hook{object, field->cls, field->field, field->desc}; + PascalWrite(Result, eios->control_center->reflect_int(hook)); + } +} + +void Pascal_Reflect_Long(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + if (eios) + { + jobject object = PascalRead(Params[1]); + PascalField* field = PascalRead(Params[2]); + + ReflectionHook hook{object, field->cls, field->field, field->desc}; + PascalWrite(Result, eios->control_center->reflect_long(hook)); + } +} + +void Pascal_Reflect_Float(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + if (eios) + { + jobject object = PascalRead(Params[1]); + PascalField* field = PascalRead(Params[2]); + + ReflectionHook hook{object, field->cls, field->field, field->desc}; + PascalWrite(Result, eios->control_center->reflect_float(hook)); + } +} + +void Pascal_Reflect_Double(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + if (eios) + { + jobject object = PascalRead(Params[1]); + PascalField* field = PascalRead(Params[2]); + + ReflectionHook hook{object, field->cls, field->field, field->desc}; + PascalWrite(Result, eios->control_center->reflect_double(hook)); + } +} + +void Pascal_Reflect_String(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + + if (eios) + { + jobject object = PascalRead(Params[1]); + PascalField* field = PascalRead(Params[2]); + + ReflectionHook hook{object, field->cls, field->field, field->desc}; + std::string result = eios->control_center->reflect_string(hook); + + if (!result.empty()) + { + char* output = AllocateString(result.length()); + std::memcpy(output, &result[0], result.length()); + output[result.length()] = '\0'; + + PascalWrite(Result, output); + } + } +} + + +// MARK: - Array Functions + +void* read_array(Stream &stream, ReflectionType type) +{ + std::size_t length = stream.read(); + + if (type == ReflectionType::STRING) + { + std::size_t element_size = ControlCenter::reflect_size_for_type(type); + void** buffer = AllocateArray(length, element_size); + + for (std::size_t i = 0; i < length; ++i) + { + buffer[i] = nullptr; + std::string string = stream.read(); + if (!string.empty()) + { + char* output = AllocateString(string.length()); + std::memcpy(output, &string[0], string.length()); + output[string.length()] = '\0'; + + buffer[i] = output; + } + } + return buffer; + } + + std::size_t element_size = ControlCenter::reflect_size_for_type(type); + void* buffer = AllocateArray(length, element_size); + stream.read(buffer, length * element_size); + return buffer; +} + +void Pascal_Reflect_Array(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + + jobject object = PascalRead(Params[1]); + PascalField* field = PascalRead(Params[2]); + + ReflectionHook hook{object, field->cls, field->field, field->desc}; + PascalWrite(Result, eios->control_center->reflect_array(hook)); +} + +void Pascal_Reflect_Array_With_Size(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + + jobject object = PascalRead(Params[1]); + std::size_t* output_size = PascalRead(Params[2]); + PascalField* field = PascalRead(Params[3]); + + ReflectionHook hook{object, field->cls, field->field, field->desc}; + PascalWrite(Result, eios->control_center->reflect_array(hook, output_size)); +} + +void Pascal_Reflect_Array_Size(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + + jarray array = PascalRead(Params[1]); + PascalWrite(Result, eios->control_center->reflect_array_size(array)); +} + +void Pascal_Reflect_Array_Indices(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + + std::size_t length = 0; + jarray array = PascalRead(Params[1]); + ReflectionType type = PascalRead(Params[2]); + std::int32_t* indices = GetArray(PascalRead(Params[3]), &length); + + if (!indices || length == 0) + { + return; + } + + ImageData* image_data = eios->control_center->reflect_array_indices(array, type, indices, length); + PascalWrite(Result, read_array(image_data->data_stream(), type)); +} + +void Pascal_Reflect_Array_SingleIndex(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + + jarray array = PascalRead(Params[1]); + ReflectionType type = PascalRead(Params[2]); + std::size_t index = PascalRead(Params[3]); + + Stream &stream = eios->control_center->reflect_array(array, type, 1, index)->data_stream(); + if (stream.read() == 0) + { + PascalWrite(Result, nullptr); + return; + } + PascalWrite(eios, stream, Result, type); +} + +void Pascal_Reflect_Array_SingleIndex2D(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + + jarray array = PascalRead(Params[1]); + ReflectionType type = PascalRead(Params[2]); + std::size_t x = PascalRead(Params[3]); + std::size_t y = PascalRead(Params[4]); + + Stream &stream = eios->control_center->reflect_array(array, type, 1, x, y)->data_stream(); + if (stream.read() == 0) + { + PascalWrite(Result, nullptr); + return; + } + PascalWrite(eios, stream, Result, type); +} + +void Pascal_Reflect_Array_SingleIndex3D(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + + jarray array = PascalRead(Params[1]); + ReflectionType type = PascalRead(Params[2]); + std::size_t x = PascalRead(Params[3]); + std::size_t y = PascalRead(Params[4]); + std::size_t z = PascalRead(Params[5]); + + Stream &stream = eios->control_center->reflect_array(array, type, 1, x, y, z)->data_stream(); + if (stream.read() == 0) + { + PascalWrite(Result, nullptr); + return; + } + PascalWrite(eios, stream, Result, type); +} + +void Pascal_Reflect_Array_SingleIndex4D(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + + jarray array = PascalRead(Params[1]); + ReflectionType type = PascalRead(Params[2]); + std::size_t x = PascalRead(Params[3]); + std::size_t y = PascalRead(Params[4]); + std::size_t z = PascalRead(Params[5]); + std::size_t w = PascalRead(Params[6]); + + Stream &stream = eios->control_center->reflect_array(array, type, 1, x, y, z, w)->data_stream(); + if (stream.read() == 0) + { + PascalWrite(Result, nullptr); + return; + } + PascalWrite(eios, stream, Result, type); +} + +void Pascal_Reflect_Array_Index(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + + jarray array = PascalRead(Params[1]); + ReflectionType type = PascalRead(Params[2]); + std::size_t index = PascalRead(Params[3]); + std::size_t length = PascalRead(Params[4]); + + if (length == 0) + { + return; + } + + ImageData* image_data = eios->control_center->reflect_array(array, type, length, index); + PascalWrite(Result, read_array(image_data->data_stream(), type)); +} + +void Pascal_Reflect_Array_Index2D(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + + jarray array = PascalRead(Params[1]); + ReflectionType type = PascalRead(Params[2]); + std::size_t length = PascalRead(Params[3]); + std::size_t x = PascalRead(Params[4]); + std::size_t y = PascalRead(Params[5]); + + if (length == 0) + { + return; + } + + ImageData* image_data = eios->control_center->reflect_array(array, type, length, x, y); + PascalWrite(Result, read_array(image_data->data_stream(), type)); +} + +void Pascal_Reflect_Array_Index3D(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + + jarray array = PascalRead(Params[1]); + ReflectionType type = PascalRead(Params[2]); + std::size_t length = PascalRead(Params[3]); + std::size_t x = PascalRead(Params[4]); + std::size_t y = PascalRead(Params[5]); + std::size_t z = PascalRead(Params[6]); + + if (length == 0) + { + return; + } + + ImageData* image_data = eios->control_center->reflect_array(array, type, length, x, y, z); + PascalWrite(Result, read_array(image_data->data_stream(), type)); +} + +void Pascal_Reflect_Array_Index4D(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + + jarray array = PascalRead(Params[1]); + ReflectionType type = PascalRead(Params[2]); + std::size_t length = PascalRead(Params[3]); + std::size_t x = PascalRead(Params[4]); + std::size_t y = PascalRead(Params[5]); + std::size_t z = PascalRead(Params[6]); + std::size_t w = PascalRead(Params[7]); + + if (length == 0) + { + return; + } + + ImageData* image_data = eios->control_center->reflect_array(array, type, length, x, y, z, w); + PascalWrite(Result, read_array(image_data->data_stream(), type)); +} + +void Pascal_Reflect_Class_Name(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + + if (eios) + { + jobject object = PascalRead(Params[1]); + std::string result = eios->control_center->reflect_class_name(object); + + if (!result.empty()) + { + char *output = AllocateString(result.length()); + std::memcpy(output, &result[0], result.length()); + output[result.length()] = '\0'; + + PascalWrite(Result, output); + } + } +} + +void Pascal_Reflect_Class_Type(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + + if (eios) + { + jobject object = PascalRead(Params[1]); + std::string result = eios->control_center->reflect_class_type(object); + + if (!result.empty()) + { + char *output = AllocateString(result.length()); + std::memcpy(output, &result[0], result.length()); + output[result.length()] = '\0'; + + PascalWrite(Result, output); + } + } +} + +void Pascal_GetImageFormat(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + PascalWrite(Result, eios->control_center->get_image_format()); +} + +void Pascal_SetImageFormat(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + ImageFormat format = PascalRead(Params[1]); + EIOS_SetImageFormat(eios, format); +} + +void Pascal_GetDebugImageBuffer(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + PascalWrite(Result, eios->control_center->get_debug_image()); +} + +void Pascal_SetGraphicsDebugging(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + bool enabled = PascalRead(Params[1]); + EIOS_SetGraphicsDebugging(eios, enabled); +} + +void Pascal_PairClient(void** Params, void** Result) noexcept +{ + std::int32_t pid = PascalRead(Params[0]); + PascalWrite(Result, EIOS_PairClient(pid)); +} + +void Pascal_KillClientPID(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + EIOS_KillClient(eios); +} + +void Pascal_KillClient(void** Params, void** Result) noexcept +{ + std::int32_t pid = PascalRead(Params[0]); + EIOS_KillClientPID(pid); +} + +void Pascal_ReleaseClient(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + EIOS_ReleaseClient(eios); +} + +void Pascal_GetClients(void** Params, void** Result) noexcept +{ + bool unpaired_only = PascalRead(Params[0]); + PascalWrite(Result, EIOS_GetClients(unpaired_only)); +} + +void Pascal_GetClientPID(void** Params, void** Result) noexcept +{ + std::size_t index = PascalRead(Params[0]); + PascalWrite(Result, EIOS_GetClientPID(index)); +} + +void Pascal_Inject(void** Params, void** Result) noexcept +{ + EIOS_Inject(PascalRead(Params[0])); +} + +void Pascal_Inject_PID(void** Params, void** Result) noexcept +{ + EIOS_Inject_PID(PascalRead(Params[0])); +} + +void Pascal_HasFocus(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + PascalWrite(Result, EIOS_HasFocus(eios)); +} + +void Pascal_GainFocus(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + EIOS_GainFocus(eios); +} + +void Pascal_LoseFocus(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + EIOS_LoseFocus(eios); +} + +void Pascal_IsKeyboardInputEnabled(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + PascalWrite(Result, EIOS_IsKeyboardInputEnabled(eios)); +} + +void Pascal_SetKeyboardInputEnabled(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + bool enabled = PascalRead(Params[1]); + EIOS_SetKeyboardInputEnabled(eios, enabled); +} + +void Pascal_IsMouseInputEnabled(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + PascalWrite(Result, EIOS_IsMouseInputEnabled(eios)); +} + +void Pascal_SetMouseInputEnabled(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + bool enabled = PascalRead(Params[1]); + EIOS_SetMouseInputEnabled(eios, enabled); +} + +void Pascal_GetRealMousePosition(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + std::int32_t* x = static_cast(Params[1]); + std::int32_t* y = static_cast(Params[2]); + if (x && y) + { + EIOS_GetRealMousePosition(eios, x, y); + } +} + +void Pascal_GetKeyboardSpeed(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + PascalWrite(Result, EIOS_GetKeyboardSpeed(eios)); +} + +void Pascal_SetKeyboardSpeed(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + std::int32_t speed = PascalRead(Params[1]); + EIOS_SetKeyboardSpeed(eios, speed); +} + +void Pascal_GetKeyboardRepeatDelay(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + PascalWrite(Result, EIOS_GetKeyboardRepeatDelay(eios)); +} + +void Pascal_SetKeyboardRepeatDelay(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + std::int32_t delay = PascalRead(Params[1]); + EIOS_SetKeyboardRepeatDelay(eios, delay); +} + +void Pascal_GetGraphicsScaling(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + PascalWrite(Result, EIOS_GetGraphicsScaling(eios)); +} + +void Pascal_SetGraphicsScaling(void** Params, void** Result) noexcept +{ + EIOS* eios = PascalRead(Params[0]); + bool enabled = PascalRead(Params[1]); + EIOS_SetGraphicsScaling(eios, enabled); +} + + +//MARK: - RemoteVM +void Pascal_RemoteVM_Init(void** Params, void** Result) +{ + EIOS* eios = PascalRead(Params[1]); + RemoteVM** remote_vm = static_cast(Params[0]); + if (eios && eios->control_center) + { + *remote_vm = eios->control_center->create_remote_vm().release(); + } +} + +void Pascal_RemoteVM_InitEx(void** Params, void** Result) +{ +#if defined(_WIN32) || defined(_WIN64) + static Module module = Module("jvm.dll"); +#elif defined(__APPLE__) + static Module module = Module("libjvm.dylib"); + #else + static Module module = Module("libjvm.so"); +#endif // defined + + jint (JNICALL *JNI_GetCreatedJavaVMs)(JavaVM**, jsize, jsize*) = nullptr; + jint (JNICALL *JNI_GetDefaultJavaVMInitArgs)(void* args) = nullptr; + jint (JNICALL *JNI_CreateJavaVM)(JavaVM** pvm, void** penv, void* args) = nullptr; + + if (!module || + !module.AddressOf(JNI_GetCreatedJavaVMs, "JNI_GetCreatedJavaVMs") || + !module.AddressOf(JNI_GetDefaultJavaVMInitArgs, "JNI_GetDefaultJavaVMInitArgs") || + !module.AddressOf(JNI_CreateJavaVM, "JNI_CreateJavaVM")) + { + return; + } + + RemoteVM** remote_vm = static_cast(Params[0]); + std::vector arguments = GetArrayAsVector(PascalRead(Params[1])); + + JavaVMInitArgs jvm_args; + std::unique_ptr options = !arguments.empty() ? std::make_unique(arguments.size()) : nullptr; + + for (std::size_t i = 0; i < arguments.size(); ++i) + { + options[i].optionString = arguments[i]; + } + + JNI_GetDefaultJavaVMInitArgs(&jvm_args); + jvm_args.version = JNI_VERSION_1_8; + jvm_args.nOptions = arguments.size(); + jvm_args.options = options.get(); + jvm_args.ignoreUnrecognized = false; + + jint num_vms = 0; + const jint max_vms = 5; + JavaVM* vms[max_vms] = {0}; + if (JNI_GetCreatedJavaVMs(vms, max_vms, &num_vms) == JNI_OK) + { + for (int i = 0; i < num_vms; ++i) + { + if (vms[i]) + { + JNIEnv* env = nullptr; + vms[i]->GetEnv(reinterpret_cast(&env), jvm_args.version); + if (!env) + { + vms[i]->AttachCurrentThread(reinterpret_cast(&env), nullptr); + } + + if (env) + { + *remote_vm = new RemoteVM(env, nullptr, nullptr, nullptr); + } + return; + } + } + } + + JavaVM* vm = nullptr; + JNIEnv* env = nullptr; + if (JNI_CreateJavaVM(&vm, reinterpret_cast(&env), &jvm_args) == JNI_OK) + { + if (env) + { + *remote_vm = new RemoteVM(env, nullptr, nullptr, nullptr); + return; + } + + vm->AttachCurrentThread(reinterpret_cast(&env), nullptr); + if (env) + { + *remote_vm = new RemoteVM(env, nullptr, nullptr, nullptr); + return; + } + + vm->DestroyJavaVM(); + } +} + +void Pascal_RemoteVM_Free(void** Params, void** Result) +{ + RemoteVM** remote_vm = static_cast(Params[0]); + if (remote_vm && *remote_vm) + { + if (!(*remote_vm)->is_remote()) + { + JavaVM* vm = (*remote_vm)->GetJavaVM(); + if (vm) + { + JNIEnv* env = nullptr; + vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_8); + if (env) + { + vm->DetachCurrentThread(); + } + vm->DestroyJavaVM(); + } + + } + delete *remote_vm; + *remote_vm = nullptr; + } +} + +void Pascal_RemoteVM_MaxMemoryChunkSize(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + PascalWrite(Result, remote_vm->MaxMemoryChunkSize()); +} + +void Pascal_RemoteVM_AllocateMemory(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + std::size_t size = PascalRead(Params[1]); + PascalWrite(Result, remote_vm->AllocateMemory(size)); +} + +void Pascal_RemoteVM_ReadMemory(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + void* destintation = PascalRead(Params[1]); + void* source = PascalRead(Params[2]); + std::size_t size = PascalRead(Params[3]); + PascalWrite(Result, remote_vm->ReadMemory(destintation, source, size)); +} + +void Pascal_RemoteVM_WriteMemory(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + void* destintation = PascalRead(Params[1]); + void* source = PascalRead(Params[2]); + std::size_t size = PascalRead(Params[3]); + PascalWrite(Result, remote_vm->WriteMemory(destintation, source, size)); +} + +void Pascal_RemoteVM_FreeMemory(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + void* memory = PascalRead(Params[1]); + remote_vm->FreeMemory(memory); +} + + +void Pascal_RemoteVM_GetVersion(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + PascalWrite(Result, remote_vm->GetVersion()); +} + +void Pascal_RemoteVM_DefineClass(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + const char* name = PascalRead(Params[1]); + jobject loader = PascalRead(Params[2]); + jbyte* buf = PascalRead(Params[3]); + jsize len = PascalRead(Params[4]); + PascalWrite(Result, remote_vm->DefineClass(name, loader, buf, len)); +} + +void Pascal_RemoteVM_FindClass(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + const char* name = PascalRead(Params[1]); + PascalWrite(Result, remote_vm->FindClass(name)); +} + +void Pascal_RemoteVM_FromReflectedMethod(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject method = PascalRead(Params[1]); + PascalWrite(Result, remote_vm->FromReflectedMethod(method)); +} + +void Pascal_RemoteVM_FromReflectedField(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject field = PascalRead(Params[1]); + PascalWrite(Result, remote_vm->FromReflectedField(field)); +} + +void Pascal_RemoteVM_ToReflectedMethod(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jclass cls = PascalRead(Params[1]); + jmethodID methodID = PascalRead(Params[2]); + jboolean isStatic = PascalRead(Params[3]); + PascalWrite(Result, remote_vm->ToReflectedMethod(cls, methodID, isStatic)); +} + +void Pascal_RemoteVM_GetSuperclass(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jclass sub = PascalRead(Params[1]); + PascalWrite(Result, remote_vm->GetSuperclass(sub)); +} + +void Pascal_RemoteVM_IsAssignableFrom(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jclass sub = PascalRead(Params[1]); + jclass sup = PascalRead(Params[2]); + PascalWrite(Result, remote_vm->IsAssignableFrom(sub, sup)); +} + +void Pascal_RemoteVM_ToReflectedField(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jclass cls = PascalRead(Params[1]); + jfieldID fieldID = PascalRead(Params[2]); + jboolean isStatic = PascalRead(Params[3]); + PascalWrite(Result, remote_vm->ToReflectedField(cls, fieldID, isStatic)); +} + +void Pascal_RemoteVM_Throw(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jthrowable obj = PascalRead(Params[1]); + PascalWrite(Result, remote_vm->Throw(obj)); +} + +void Pascal_RemoteVM_ThrowNew(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jclass clazz = PascalRead(Params[1]); + const char* msg = PascalRead(Params[2]); + PascalWrite(Result, remote_vm->ThrowNew(clazz, msg)); +} + +void Pascal_RemoteVM_GetExceptionMessage(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + std::string result = remote_vm->GetExceptionMessage(); + + if (!result.empty()) + { + char* output = AllocateString(result.length()); + std::memcpy(output, &result[0], result.length()); + output[result.length()] = '\0'; + + PascalWrite(Result, output); + } +} + +void Pascal_RemoteVM_FatalError(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + const char* msg = PascalRead(Params[1]); + remote_vm->FatalError(msg); +} + +void Pascal_RemoteVM_DeleteGlobalRef(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject gref = PascalRead(Params[1]); + remote_vm->DeleteGlobalRef(gref); +} + +void Pascal_RemoteVM_IsSameObject(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject obj1 = PascalRead(Params[1]); + jobject obj2 = PascalRead(Params[2]); + PascalWrite(Result, remote_vm->IsSameObject(obj1, obj2)); +} + +void Pascal_RemoteVM_AllocObject(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jclass clazz = PascalRead(Params[1]); + PascalWrite(Result, remote_vm->AllocObject(clazz)); +} + +void Pascal_RemoteVM_NewObject(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jclass clazz = PascalRead(Params[1]); + jmethodID methodID = PascalRead(Params[2]); + std::vector args = GetArrayAsVector(PascalRead(Params[3])); + PascalWrite(Result, remote_vm->NewObject(clazz, methodID, args)); +} + +void Pascal_RemoteVM_GetObjectClass(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject obj = PascalRead(Params[1]); + PascalWrite(Result, remote_vm->GetObjectClass(obj)); +} + +void Pascal_RemoteVM_IsInstanceOf(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject obj = PascalRead(Params[1]); + jclass clazz = PascalRead(Params[2]); + PascalWrite(Result, remote_vm->IsInstanceOf(obj, clazz)); +} + +void Pascal_RemoteVM_GetMethodID(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jclass clazz = PascalRead(Params[1]); + const char* name = PascalRead(Params[2]); + const char* sig = PascalRead(Params[3]); + PascalWrite(Result, remote_vm->GetMethodID(clazz, name, sig)); +} + +void Pascal_RemoteVM_CallObjectMethod(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject obj = PascalRead(Params[1]); + jmethodID methodID = PascalRead(Params[2]); + std::vector args = GetArrayAsVector(PascalRead(Params[3])); + PascalWrite(Result, remote_vm->CallObjectMethod(obj, methodID, args)); +} + +void Pascal_RemoteVM_CallBooleanMethod(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject obj = PascalRead(Params[1]); + jmethodID methodID = PascalRead(Params[2]); + std::vector args = GetArrayAsVector(PascalRead(Params[3])); + PascalWrite(Result, remote_vm->CallBooleanMethod(obj, methodID, args)); +} + +void Pascal_RemoteVM_CallByteMethod(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject obj = PascalRead(Params[1]); + jmethodID methodID = PascalRead(Params[2]); + std::vector args = GetArrayAsVector(PascalRead(Params[3])); + PascalWrite(Result, remote_vm->CallByteMethod(obj, methodID, args)); +} + +void Pascal_RemoteVM_CallCharMethod(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject obj = PascalRead(Params[1]); + jmethodID methodID = PascalRead(Params[2]); + std::vector args = GetArrayAsVector(PascalRead(Params[3])); + PascalWrite(Result, remote_vm->CallCharMethod(obj, methodID, args)); +} + +void Pascal_RemoteVM_CallShortMethod(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject obj = PascalRead(Params[1]); + jmethodID methodID = PascalRead(Params[2]); + std::vector args = GetArrayAsVector(PascalRead(Params[3])); + PascalWrite(Result, remote_vm->CallShortMethod(obj, methodID, args)); +} + +void Pascal_RemoteVM_CallIntMethod(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject obj = PascalRead(Params[1]); + jmethodID methodID = PascalRead(Params[2]); + std::vector args = GetArrayAsVector(PascalRead(Params[3])); + PascalWrite(Result, remote_vm->CallIntMethod(obj, methodID, args)); +} + +void Pascal_RemoteVM_CallLongMethod(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject obj = PascalRead(Params[1]); + jmethodID methodID = PascalRead(Params[2]); + + std::vector args = GetArrayAsVector(PascalRead(Params[3])); + PascalWrite(Result, remote_vm->CallLongMethod(obj, methodID, args)); +} + +void Pascal_RemoteVM_CallFloatMethod(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject obj = PascalRead(Params[1]); + jmethodID methodID = PascalRead(Params[2]); + + std::vector args = GetArrayAsVector(PascalRead(Params[3])); + PascalWrite(Result, remote_vm->CallFloatMethod(obj, methodID, args)); +} + +void Pascal_RemoteVM_CallDoubleMethod(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject obj = PascalRead(Params[1]); + jmethodID methodID = PascalRead(Params[2]); + + std::vector args = GetArrayAsVector(PascalRead(Params[3])); + PascalWrite(Result, remote_vm->CallDoubleMethod(obj, methodID, args)); +} + +void Pascal_RemoteVM_CallVoidMethod(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject obj = PascalRead(Params[1]); + jmethodID methodID = PascalRead(Params[2]); + + std::vector args = GetArrayAsVector(PascalRead(Params[3])); + remote_vm->CallVoidMethod(obj, methodID, args); +} + +void Pascal_RemoteVM_CallNonvirtualObjectMethod(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject obj = PascalRead(Params[1]); + jclass clazz = PascalRead(Params[2]); + jmethodID methodID = PascalRead(Params[3]); + + std::vector args = GetArrayAsVector(PascalRead(Params[4])); + PascalWrite(Result, remote_vm->CallNonvirtualObjectMethod(obj, clazz, methodID, args)); +} + +void Pascal_RemoteVM_CallNonvirtualBooleanMethod(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject obj = PascalRead(Params[1]); + jclass clazz = PascalRead(Params[2]); + jmethodID methodID = PascalRead(Params[3]); + + std::vector args = GetArrayAsVector(PascalRead(Params[4])); + PascalWrite(Result, remote_vm->CallNonvirtualBooleanMethod(obj, clazz, methodID, args)); +} + +void Pascal_RemoteVM_CallNonvirtualByteMethod(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject obj = PascalRead(Params[1]); + jclass clazz = PascalRead(Params[2]); + jmethodID methodID = PascalRead(Params[3]); + + std::vector args = GetArrayAsVector(PascalRead(Params[4])); + PascalWrite(Result, remote_vm->CallNonvirtualByteMethod(obj, clazz, methodID, args)); +} + +void Pascal_RemoteVM_CallNonvirtualCharMethod(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject obj = PascalRead(Params[1]); + jclass clazz = PascalRead(Params[2]); + jmethodID methodID = PascalRead(Params[3]); + + std::vector args = GetArrayAsVector(PascalRead(Params[4])); + PascalWrite(Result, remote_vm->CallNonvirtualCharMethod(obj, clazz, methodID, args)); +} + +void Pascal_RemoteVM_CallNonvirtualShortMethod(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject obj = PascalRead(Params[1]); + jclass clazz = PascalRead(Params[2]); + jmethodID methodID = PascalRead(Params[3]); + + std::vector args = GetArrayAsVector(PascalRead(Params[4])); + PascalWrite(Result, remote_vm->CallNonvirtualShortMethod(obj, clazz, methodID, args)); +} + +void Pascal_RemoteVM_CallNonvirtualIntMethod(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject obj = PascalRead(Params[1]); + jclass clazz = PascalRead(Params[2]); + jmethodID methodID = PascalRead(Params[3]); + + std::vector args = GetArrayAsVector(PascalRead(Params[4])); + PascalWrite(Result, remote_vm->CallNonvirtualIntMethod(obj, clazz, methodID, args)); +} + +void Pascal_RemoteVM_CallNonvirtualLongMethod(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject obj = PascalRead(Params[1]); + jclass clazz = PascalRead(Params[2]); + jmethodID methodID = PascalRead(Params[3]); + + std::vector args = GetArrayAsVector(PascalRead(Params[4])); + PascalWrite(Result, remote_vm->CallNonvirtualLongMethod(obj, clazz, methodID, args)); +} + +void Pascal_RemoteVM_CallNonvirtualFloatMethod(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject obj = PascalRead(Params[1]); + jclass clazz = PascalRead(Params[2]); + jmethodID methodID = PascalRead(Params[3]); + + std::vector args = GetArrayAsVector(PascalRead(Params[4])); + PascalWrite(Result, remote_vm->CallNonvirtualFloatMethod(obj, clazz, methodID, args)); +} + +void Pascal_RemoteVM_CallNonvirtualDoubleMethod(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject obj = PascalRead(Params[1]); + jclass clazz = PascalRead(Params[2]); + jmethodID methodID = PascalRead(Params[3]); + + std::vector args = GetArrayAsVector(PascalRead(Params[4])); + PascalWrite(Result, remote_vm->CallNonvirtualDoubleMethod(obj, clazz, methodID, args)); +} + +void Pascal_RemoteVM_CallNonvirtualVoidMethod(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject obj = PascalRead(Params[1]); + jclass clazz = PascalRead(Params[2]); + jmethodID methodID = PascalRead(Params[3]); + + std::vector args = GetArrayAsVector(PascalRead(Params[4])); + remote_vm->CallNonvirtualVoidMethod(obj, clazz, methodID, args); +} + +void Pascal_RemoteVM_GetFieldID(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jclass clazz = PascalRead(Params[1]); + const char* name = PascalRead(Params[2]); + const char* sig = PascalRead(Params[3]); + PascalWrite(Result, remote_vm->GetFieldID(clazz, name, sig)); +} + +void Pascal_RemoteVM_GetObjectField(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject obj = PascalRead(Params[1]); + jfieldID fieldID = PascalRead(Params[2]); + PascalWrite(Result, remote_vm->GetObjectField(obj, fieldID)); +} + +void Pascal_RemoteVM_GetBooleanField(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject obj = PascalRead(Params[1]); + jfieldID fieldID = PascalRead(Params[2]); + PascalWrite(Result, remote_vm->GetBooleanField(obj, fieldID)); +} + +void Pascal_RemoteVM_GetByteField(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject obj = PascalRead(Params[1]); + jfieldID fieldID = PascalRead(Params[2]); + PascalWrite(Result, remote_vm->GetByteField(obj, fieldID)); +} + +void Pascal_RemoteVM_GetCharField(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject obj = PascalRead(Params[1]); + jfieldID fieldID = PascalRead(Params[2]); + PascalWrite(Result, remote_vm->GetCharField(obj, fieldID)); +} + +void Pascal_RemoteVM_GetShortField(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject obj = PascalRead(Params[1]); + jfieldID fieldID = PascalRead(Params[2]); + PascalWrite(Result, remote_vm->GetShortField(obj, fieldID)); +} + +void Pascal_RemoteVM_GetIntField(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject obj = PascalRead(Params[1]); + jfieldID fieldID = PascalRead(Params[2]); + PascalWrite(Result, remote_vm->GetIntField(obj, fieldID)); +} + +void Pascal_RemoteVM_GetLongField(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject obj = PascalRead(Params[1]); + jfieldID fieldID = PascalRead(Params[2]); + PascalWrite(Result, remote_vm->GetLongField(obj, fieldID)); +} + +void Pascal_RemoteVM_GetFloatField(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject obj = PascalRead(Params[1]); + jfieldID fieldID = PascalRead(Params[2]); + PascalWrite(Result, remote_vm->GetFloatField(obj, fieldID)); +} + +void Pascal_RemoteVM_GetDoubleField(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject obj = PascalRead(Params[1]); + jfieldID fieldID = PascalRead(Params[2]); + PascalWrite(Result, remote_vm->GetDoubleField(obj, fieldID)); +} + +void Pascal_RemoteVM_SetObjectField(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject obj = PascalRead(Params[1]); + jfieldID fieldID = PascalRead(Params[2]); + jobject value = PascalRead(Params[3]); + remote_vm->SetObjectField(obj, fieldID, value); +} + +void Pascal_RemoteVM_SetBooleanField(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject obj = PascalRead(Params[1]); + jfieldID fieldID = PascalRead(Params[2]); + jboolean value = PascalRead(Params[3]); + remote_vm->SetBooleanField(obj, fieldID, value); +} + +void Pascal_RemoteVM_SetByteField(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject obj = PascalRead(Params[1]); + jfieldID fieldID = PascalRead(Params[2]); + jbyte value = PascalRead(Params[3]); + remote_vm->SetByteField(obj, fieldID, value); +} + +void Pascal_RemoteVM_SetCharField(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject obj = PascalRead(Params[1]); + jfieldID fieldID = PascalRead(Params[2]); + jchar value = PascalRead(Params[3]); + remote_vm->SetCharField(obj, fieldID, value); +} + +void Pascal_RemoteVM_SetShortField(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject obj = PascalRead(Params[1]); + jfieldID fieldID = PascalRead(Params[2]); + jshort value = PascalRead(Params[3]); + remote_vm->SetShortField(obj, fieldID, value); +} + +void Pascal_RemoteVM_SetIntField(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject obj = PascalRead(Params[1]); + jfieldID fieldID = PascalRead(Params[2]); + jint value = PascalRead(Params[3]); + remote_vm->SetIntField(obj, fieldID, value); +} + +void Pascal_RemoteVM_SetLongField(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject obj = PascalRead(Params[1]); + jfieldID fieldID = PascalRead(Params[2]); + jlong value = PascalRead(Params[3]); + remote_vm->SetLongField(obj, fieldID, value); +} + +void Pascal_RemoteVM_SetFloatField(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject obj = PascalRead(Params[1]); + jfieldID fieldID = PascalRead(Params[2]); + jfloat value = PascalRead(Params[3]); + remote_vm->SetFloatField(obj, fieldID, value); +} + +void Pascal_RemoteVM_SetDoubleField(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject obj = PascalRead(Params[1]); + jfieldID fieldID = PascalRead(Params[2]); + jdouble value = PascalRead(Params[3]); + remote_vm->SetDoubleField(obj, fieldID, value); +} + +void Pascal_RemoteVM_GetStaticMethodID(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jclass clazz = PascalRead(Params[1]); + const char* name = PascalRead(Params[2]); + const char* sig = PascalRead(Params[3]); + PascalWrite(Result, remote_vm->GetStaticMethodID(clazz, name, sig)); +} + +void Pascal_RemoteVM_CallStaticObjectMethod(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jclass clazz = PascalRead(Params[1]); + jmethodID methodID = PascalRead(Params[2]); + + std::vector args = GetArrayAsVector(PascalRead(Params[3])); + PascalWrite(Result, remote_vm->CallStaticObjectMethod(clazz, methodID, args)); +} + +void Pascal_RemoteVM_CallStaticBooleanMethod(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jclass clazz = PascalRead(Params[1]); + jmethodID methodID = PascalRead(Params[2]); + + std::vector args = GetArrayAsVector(PascalRead(Params[3])); + PascalWrite(Result, remote_vm->CallStaticBooleanMethod(clazz, methodID, args)); +} + +void Pascal_RemoteVM_CallStaticByteMethod(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jclass clazz = PascalRead(Params[1]); + jmethodID methodID = PascalRead(Params[2]); + + std::vector args = GetArrayAsVector(PascalRead(Params[3])); + PascalWrite(Result, remote_vm->CallStaticByteMethod(clazz, methodID, args)); +} + +void Pascal_RemoteVM_CallStaticCharMethod(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jclass clazz = PascalRead(Params[1]); + jmethodID methodID = PascalRead(Params[2]); + + std::vector args = GetArrayAsVector(PascalRead(Params[3])); + PascalWrite(Result, remote_vm->CallStaticCharMethod(clazz, methodID, args)); +} + +void Pascal_RemoteVM_CallStaticShortMethod(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jclass clazz = PascalRead(Params[1]); + jmethodID methodID = PascalRead(Params[2]); + + std::vector args = GetArrayAsVector(PascalRead(Params[3])); + PascalWrite(Result, remote_vm->CallStaticShortMethod(clazz, methodID, args)); +} + +void Pascal_RemoteVM_CallStaticIntMethod(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jclass clazz = PascalRead(Params[1]); + jmethodID methodID = PascalRead(Params[2]); + + std::vector args = GetArrayAsVector(PascalRead(Params[3])); + PascalWrite(Result, remote_vm->CallStaticIntMethod(clazz, methodID, args)); +} + +void Pascal_RemoteVM_CallStaticLongMethod(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jclass clazz = PascalRead(Params[1]); + jmethodID methodID = PascalRead(Params[2]); + + std::vector args = GetArrayAsVector(PascalRead(Params[3])); + PascalWrite(Result, remote_vm->CallStaticLongMethod(clazz, methodID, args)); +} + +void Pascal_RemoteVM_CallStaticFloatMethod(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jclass clazz = PascalRead(Params[1]); + jmethodID methodID = PascalRead(Params[2]); + + std::vector args = GetArrayAsVector(PascalRead(Params[3])); + PascalWrite(Result, remote_vm->CallStaticFloatMethod(clazz, methodID, args)); +} + +void Pascal_RemoteVM_CallStaticDoubleMethod(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jclass clazz = PascalRead(Params[1]); + jmethodID methodID = PascalRead(Params[2]); + + std::vector args = GetArrayAsVector(PascalRead(Params[3])); + PascalWrite(Result, remote_vm->CallStaticDoubleMethod(clazz, methodID, args)); +} + +void Pascal_RemoteVM_CallStaticVoidMethod(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jclass clazz = PascalRead(Params[1]); + jmethodID methodID = PascalRead(Params[2]); + + std::vector args = GetArrayAsVector(PascalRead(Params[3])); + remote_vm->CallStaticVoidMethod(clazz, methodID, args); +} + +void Pascal_RemoteVM_GetStaticFieldID(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jclass clazz = PascalRead(Params[1]); + const char* name = PascalRead(Params[2]); + const char* sig = PascalRead(Params[3]); + PascalWrite(Result, remote_vm->GetStaticFieldID(clazz, name, sig)); +} + +void Pascal_RemoteVM_GetStaticObjectField(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jclass clazz = PascalRead(Params[1]); + jfieldID fieldID = PascalRead(Params[2]); + PascalWrite(Result, remote_vm->GetStaticObjectField(clazz, fieldID)); +} + +void Pascal_RemoteVM_GetStaticBooleanField(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jclass clazz = PascalRead(Params[1]); + jfieldID fieldID = PascalRead(Params[2]); + PascalWrite(Result, remote_vm->GetStaticBooleanField(clazz, fieldID)); +} + +void Pascal_RemoteVM_GetStaticByteField(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jclass clazz = PascalRead(Params[1]); + jfieldID fieldID = PascalRead(Params[2]); + PascalWrite(Result, remote_vm->GetStaticByteField(clazz, fieldID)); +} + +void Pascal_RemoteVM_GetStaticCharField(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jclass clazz = PascalRead(Params[1]); + jfieldID fieldID = PascalRead(Params[2]); + PascalWrite(Result, remote_vm->GetStaticCharField(clazz, fieldID)); +} + +void Pascal_RemoteVM_GetStaticShortField(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jclass clazz = PascalRead(Params[1]); + jfieldID fieldID = PascalRead(Params[2]); + PascalWrite(Result, remote_vm->GetStaticShortField(clazz, fieldID)); +} + +void Pascal_RemoteVM_GetStaticIntField(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jclass clazz = PascalRead(Params[1]); + jfieldID fieldID = PascalRead(Params[2]); + PascalWrite(Result, remote_vm->GetStaticIntField(clazz, fieldID)); +} + +void Pascal_RemoteVM_GetStaticLongField(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jclass clazz = PascalRead(Params[1]); + jfieldID fieldID = PascalRead(Params[2]); + PascalWrite(Result, remote_vm->GetStaticLongField(clazz, fieldID)); +} + +void Pascal_RemoteVM_GetStaticFloatField(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jclass clazz = PascalRead(Params[1]); + jfieldID fieldID = PascalRead(Params[2]); + PascalWrite(Result, remote_vm->GetStaticFloatField(clazz, fieldID)); +} + +void Pascal_RemoteVM_GetStaticDoubleField(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jclass clazz = PascalRead(Params[1]); + jfieldID fieldID = PascalRead(Params[2]); + PascalWrite(Result, remote_vm->GetStaticDoubleField(clazz, fieldID)); +} + +void Pascal_RemoteVM_SetStaticObjectField(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jclass clazz = PascalRead(Params[1]); + jfieldID fieldID = PascalRead(Params[2]); + jobject value = PascalRead(Params[3]); + remote_vm->SetStaticObjectField(clazz, fieldID, value); +} + +void Pascal_RemoteVM_SetStaticBooleanField(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jclass clazz = PascalRead(Params[1]); + jfieldID fieldID = PascalRead(Params[2]); + jboolean value = PascalRead(Params[3]); + remote_vm->SetStaticBooleanField(clazz, fieldID, value); +} + +void Pascal_RemoteVM_SetStaticByteField(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jclass clazz = PascalRead(Params[1]); + jfieldID fieldID = PascalRead(Params[2]); + jbyte value = PascalRead(Params[3]); + remote_vm->SetStaticByteField(clazz, fieldID, value); +} + +void Pascal_RemoteVM_SetStaticCharField(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jclass clazz = PascalRead(Params[1]); + jfieldID fieldID = PascalRead(Params[2]); + jchar value = PascalRead(Params[3]); + remote_vm->SetStaticCharField(clazz, fieldID, value); +} + +void Pascal_RemoteVM_SetStaticShortField(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jclass clazz = PascalRead(Params[1]); + jfieldID fieldID = PascalRead(Params[2]); + jshort value = PascalRead(Params[3]); + remote_vm->SetStaticShortField(clazz, fieldID, value); +} + +void Pascal_RemoteVM_SetStaticIntField(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jclass clazz = PascalRead(Params[1]); + jfieldID fieldID = PascalRead(Params[2]); + jint value = PascalRead(Params[3]); + remote_vm->SetStaticIntField(clazz, fieldID, value); +} + +void Pascal_RemoteVM_SetStaticLongField(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jclass clazz = PascalRead(Params[1]); + jfieldID fieldID = PascalRead(Params[2]); + jlong value = PascalRead(Params[3]); + remote_vm->SetStaticLongField(clazz, fieldID, value); +} + +void Pascal_RemoteVM_SetStaticFloatField(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jclass clazz = PascalRead(Params[1]); + jfieldID fieldID = PascalRead(Params[2]); + jfloat value = PascalRead(Params[3]); + remote_vm->SetStaticFloatField(clazz, fieldID, value); +} + +void Pascal_RemoteVM_SetStaticDoubleField(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jclass clazz = PascalRead(Params[1]); + jfieldID fieldID = PascalRead(Params[2]); + jdouble value = PascalRead(Params[3]); + remote_vm->SetStaticDoubleField(clazz, fieldID, value); +} + +void Pascal_RemoteVM_NewString(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + const wchar_t* unicode = PascalRead(Params[1]); + PascalWrite(Result, remote_vm->NewString(unicode)); +} + +void Pascal_RemoteVM_GetStringLength(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jstring str = PascalRead(Params[1]); + PascalWrite(Result, remote_vm->GetStringLength(str)); +} + +void Pascal_RemoteVM_GetStringChars(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jstring str = PascalRead(Params[1]); + + std::wstring result = remote_vm->GetStringChars(str); + + if (!result.empty()) + { + wchar_t* output = AllocateString(result.length()); + std::memcpy(output, &result[0], result.length()); + output[result.length()] = '\0'; + PascalWrite(Result, output); + } +} + +void Pascal_RemoteVM_NewStringUTF(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + const char* utf = PascalRead(Params[1]); + PascalWrite(Result, remote_vm->NewStringUTF(utf)); +} + +void Pascal_RemoteVM_GetStringUTFLength(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jstring str = PascalRead(Params[1]); + PascalWrite(Result, remote_vm->GetStringUTFLength(str)); +} + +void Pascal_RemoteVM_GetStringUTFChars(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jstring str = PascalRead(Params[1]); + + std::string result = remote_vm->GetStringUTFChars(str); + + if (!result.empty()) + { + char* output = AllocateString(result.length()); + std::memcpy(output, &result[0], result.length()); + output[result.length()] = '\0'; + PascalWrite(Result, output); + } +} + +void Pascal_RemoteVM_GetArrayLength(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jarray array = PascalRead(Params[1]); + PascalWrite(Result, remote_vm->GetArrayLength(array)); +} + +void Pascal_RemoteVM_NewObjectArray(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jsize len = PascalRead(Params[1]); + jclass clazz = PascalRead(Params[2]); + jobject init = PascalRead(Params[3]); + PascalWrite(Result, remote_vm->NewObjectArray(len, clazz, init)); +} + +void Pascal_RemoteVM_NewBooleanArray(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jsize len = PascalRead(Params[1]); + PascalWrite(Result, remote_vm->NewBooleanArray(len)); +} + +void Pascal_RemoteVM_NewByteArray(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jsize len = PascalRead(Params[1]); + PascalWrite(Result, remote_vm->NewByteArray(len)); +} + +void Pascal_RemoteVM_NewCharArray(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jsize len = PascalRead(Params[1]); + PascalWrite(Result, remote_vm->NewCharArray(len)); +} + +void Pascal_RemoteVM_NewShortArray(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jsize len = PascalRead(Params[1]); + PascalWrite(Result, remote_vm->NewShortArray(len)); +} + +void Pascal_RemoteVM_NewIntArray(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jsize len = PascalRead(Params[1]); + PascalWrite(Result, remote_vm->NewIntArray(len)); +} + +void Pascal_RemoteVM_NewLongArray(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jsize len = PascalRead(Params[1]); + PascalWrite(Result, remote_vm->NewLongArray(len)); +} + +void Pascal_RemoteVM_NewFloatArray(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jsize len = PascalRead(Params[1]); + PascalWrite(Result, remote_vm->NewFloatArray(len)); +} + +void Pascal_RemoteVM_NewDoubleArray(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jsize len = PascalRead(Params[1]); + PascalWrite(Result, remote_vm->NewDoubleArray(len)); +} + +void Pascal_RemoteVM_GetObjectArrayElements(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobjectArray array = PascalRead(Params[1]); + std::vector elements = remote_vm->GetObjectArrayElements(array); + + jobject* buffer = AllocateArray(elements.size()); + std::memcpy(buffer, &elements[0], sizeof(elements[0]) * elements.size()); + PascalWrite(Result, buffer); +} + +void Pascal_RemoteVM_SetObjectArrayElements(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobjectArray array = PascalRead(Params[1]); + jsize index = PascalRead(Params[2]); + + std::size_t length = 0; + jobject* objects = GetArray(PascalRead(Params[3]), &length); + remote_vm->SetObjectArrayElements(array, index, std::vector(objects, objects + length)); +} + +void Pascal_RemoteVM_GetBooleanArrayElements(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jbooleanArray array = PascalRead(Params[1]); + std::vector elements = remote_vm->GetBooleanArrayElements(array); + + jboolean* buffer = AllocateArray(elements.size()); + std::memcpy(&buffer[0], &elements[0], sizeof(elements[0]) * elements.size()); + PascalWrite(Result, buffer); +} + +void Pascal_RemoteVM_GetByteArrayElements(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jbyteArray array = PascalRead(Params[1]); + std::vector elements = remote_vm->GetByteArrayElements(array); + + jbyte* buffer = AllocateArray(elements.size()); + std::memcpy(&buffer[0], &elements[0], sizeof(elements[0]) * elements.size()); + PascalWrite(Result, buffer); +} + +void Pascal_RemoteVM_GetCharArrayElements(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jcharArray array = PascalRead(Params[1]); + std::vector elements = remote_vm->GetCharArrayElements(array); + + jchar* buffer = AllocateArray(elements.size()); + std::memcpy(&buffer[0], &elements[0], sizeof(elements[0]) * elements.size()); + PascalWrite(Result, buffer); +} + +void Pascal_RemoteVM_GetShortArrayElements(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jshortArray array = PascalRead(Params[1]); + std::vector elements = remote_vm->GetShortArrayElements(array); + + jshort* buffer = AllocateArray(elements.size()); + std::memcpy(&buffer[0], &elements[0], sizeof(elements[0]) * elements.size()); + PascalWrite(Result, buffer); +} + +void Pascal_RemoteVM_GetIntArrayElements(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jintArray array = PascalRead(Params[1]); + std::vector elements = remote_vm->GetIntArrayElements(array); + + jint* buffer = AllocateArray(elements.size()); + std::memcpy(&buffer[0], &elements[0], sizeof(elements[0]) * elements.size()); + PascalWrite(Result, buffer); +} + +void Pascal_RemoteVM_GetLongArrayElements(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jlongArray array = PascalRead(Params[1]); + std::vector elements = remote_vm->GetLongArrayElements(array); + + jlong* buffer = AllocateArray(elements.size()); + std::memcpy(&buffer[0], &elements[0], sizeof(elements[0]) * elements.size()); + PascalWrite(Result, buffer); +} + +void Pascal_RemoteVM_GetFloatArrayElements(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jfloatArray array = PascalRead(Params[1]); + std::vector elements = remote_vm->GetFloatArrayElements(array); + + jfloat* buffer = AllocateArray(elements.size()); + std::memcpy(&buffer[0], &elements[0], sizeof(elements[0]) * elements.size()); + PascalWrite(Result, buffer); +} + +void Pascal_RemoteVM_GetDoubleArrayElements(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jdoubleArray array = PascalRead(Params[1]); + std::vector elements = remote_vm->GetDoubleArrayElements(array); + + jdouble* buffer = AllocateArray(elements.size()); + std::memcpy(&buffer[0], &elements[0], sizeof(elements[0]) * elements.size()); + PascalWrite(Result, buffer); +} + +void Pascal_RemoteVM_GetBooleanArrayRegion(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jbooleanArray array = PascalRead(Params[1]); + jsize start = PascalRead(Params[2]); + jsize len = PascalRead(Params[3]); + std::vector elements = remote_vm->GetBooleanArrayRegion(array, start, len); + + jboolean* buffer = AllocateArray(elements.size()); + std::memcpy(&buffer[0], &elements[0], sizeof(elements[0]) * elements.size()); + PascalWrite(Result, buffer); +} + +void Pascal_RemoteVM_GetByteArrayRegion(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jbyteArray array = PascalRead(Params[1]); + jsize start = PascalRead(Params[2]); + jsize len = PascalRead(Params[3]); + std::vector elements = remote_vm->GetByteArrayRegion(array, start, len); + + jbyte* buffer = AllocateArray(elements.size()); + std::memcpy(&buffer[0], &elements[0], sizeof(elements[0]) * elements.size()); + PascalWrite(Result, buffer); +} + +void Pascal_RemoteVM_GetCharArrayRegion(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jcharArray array = PascalRead(Params[1]); + jsize start = PascalRead(Params[2]); + jsize len = PascalRead(Params[3]); + std::vector elements = remote_vm->GetCharArrayRegion(array, start, len); + + jchar* buffer = AllocateArray(elements.size()); + std::memcpy(&buffer[0], &elements[0], sizeof(elements[0]) * elements.size()); + PascalWrite(Result, buffer); +} + +void Pascal_RemoteVM_GetShortArrayRegion(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jshortArray array = PascalRead(Params[1]); + jsize start = PascalRead(Params[2]); + jsize len = PascalRead(Params[3]); + std::vector elements = remote_vm->GetShortArrayRegion(array, start, len); + + jshort* buffer = AllocateArray(elements.size()); + std::memcpy(&buffer[0], &elements[0], sizeof(elements[0]) * elements.size()); + PascalWrite(Result, buffer); +} + +void Pascal_RemoteVM_GetIntArrayRegion(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jintArray array = PascalRead(Params[1]); + jsize start = PascalRead(Params[2]); + jsize len = PascalRead(Params[3]); + std::vector elements = remote_vm->GetIntArrayRegion(array, start, len); + + jint* buffer = AllocateArray(elements.size()); + std::memcpy(&buffer[0], &elements[0], sizeof(elements[0]) * elements.size()); + PascalWrite(Result, buffer); +} + +void Pascal_RemoteVM_GetLongArrayRegion(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jlongArray array = PascalRead(Params[1]); + jsize start = PascalRead(Params[2]); + jsize len = PascalRead(Params[3]); + std::vector elements = remote_vm->GetLongArrayRegion(array, start, len); + + jlong* buffer = AllocateArray(elements.size()); + std::memcpy(&buffer[0], &elements[0], sizeof(elements[0]) * elements.size()); + PascalWrite(Result, buffer); +} + +void Pascal_RemoteVM_GetFloatArrayRegion(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jfloatArray array = PascalRead(Params[1]); + jsize start = PascalRead(Params[2]); + jsize len = PascalRead(Params[3]); + std::vector elements = remote_vm->GetFloatArrayRegion(array, start, len); + + jfloat* buffer = AllocateArray(elements.size()); + std::memcpy(&buffer[0], &elements[0], sizeof(elements[0]) * elements.size()); + PascalWrite(Result, buffer); +} + +void Pascal_RemoteVM_GetDoubleArrayRegion(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jdoubleArray array = PascalRead(Params[1]); + jsize start = PascalRead(Params[2]); + jsize len = PascalRead(Params[3]); + std::vector elements = remote_vm->GetDoubleArrayRegion(array, start, len); + + jdouble * buffer = AllocateArray(elements.size()); + std::memcpy(&buffer[0], &elements[0], sizeof(elements[0]) * elements.size()); + PascalWrite(Result, buffer); +} + +void Pascal_RemoteVM_SetBooleanArrayRegion(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jbooleanArray array = PascalRead(Params[1]); + jsize start = PascalRead(Params[2]); + + std::size_t length = 0; + jboolean* elements = GetArray(PascalRead(Params[3]), &length); + remote_vm->SetBooleanArrayRegion(array, start, std::vector(elements, elements + length)); +} + +void Pascal_RemoteVM_SetByteArrayRegion(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jbyteArray array = PascalRead(Params[1]); + jsize start = PascalRead(Params[2]); + + std::size_t length = 0; + jbyte * elements = GetArray(PascalRead(Params[3]), &length); + remote_vm->SetByteArrayRegion(array, start, std::vector(elements, elements + length)); +} + +void Pascal_RemoteVM_SetCharArrayRegion(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jcharArray array = PascalRead(Params[1]); + jsize start = PascalRead(Params[2]); + + std::size_t length = 0; + jchar* elements = GetArray(PascalRead(Params[3]), &length); + remote_vm->SetCharArrayRegion(array, start, std::vector(elements, elements + length)); +} + +void Pascal_RemoteVM_SetShortArrayRegion(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jshortArray array = PascalRead(Params[1]); + jsize start = PascalRead(Params[2]); + + std::size_t length = 0; + jshort* elements = GetArray(PascalRead(Params[3]), &length); + remote_vm->SetShortArrayRegion(array, start, std::vector(elements, elements + length)); +} + +void Pascal_RemoteVM_SetIntArrayRegion(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jintArray array = PascalRead(Params[1]); + jsize start = PascalRead(Params[2]); + + std::size_t length = 0; + jint* elements = GetArray(PascalRead(Params[3]), &length); + remote_vm->SetIntArrayRegion(array, start, std::vector(elements, elements + length)); +} + +void Pascal_RemoteVM_SetLongArrayRegion(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jlongArray array = PascalRead(Params[1]); + jsize start = PascalRead(Params[2]); + + std::size_t length = 0; + jlong* elements = GetArray(PascalRead(Params[3]), &length); + remote_vm->SetLongArrayRegion(array, start, std::vector(elements, elements + length)); +} + +void Pascal_RemoteVM_SetFloatArrayRegion(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jfloatArray array = PascalRead(Params[1]); + jsize start = PascalRead(Params[2]); + + std::size_t length = 0; + jfloat* elements = GetArray(PascalRead(Params[3]), &length); + remote_vm->SetFloatArrayRegion(array, start, std::vector(elements, elements + length)); +} + +void Pascal_RemoteVM_SetDoubleArrayRegion(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jdoubleArray array = PascalRead(Params[1]); + jsize start = PascalRead(Params[2]); + + std::size_t length = 0; + jdouble* elements = GetArray(PascalRead(Params[3]), &length); + remote_vm->SetDoubleArrayRegion(array, start, std::vector(elements, elements + length)); +} + +void Pascal_RemoteVM_MonitorEnter(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject obj = PascalRead(Params[1]); + PascalWrite(Result, remote_vm->MonitorEnter(obj)); +} + +void Pascal_RemoteVM_MonitorExit(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject obj = PascalRead(Params[1]); + PascalWrite(Result, remote_vm->MonitorExit(obj)); +} + +void Pascal_RemoteVM_GetJavaVM(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + PascalWrite(Result, remote_vm->GetJavaVM()); +} + +void Pascal_RemoteVM_NewDirectByteBuffer(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + void* address = PascalRead(Params[1]); + jlong capacity = PascalRead(Params[2]); + PascalWrite(Result, remote_vm->NewDirectByteBuffer(address, capacity)); +} + +void Pascal_RemoteVM_GetDirectBufferAddress(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject buf = PascalRead(Params[1]); + PascalWrite(Result, remote_vm->GetDirectBufferAddress(buf)); +} + +void Pascal_RemoteVM_GetDirectBufferCapacity(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject buf = PascalRead(Params[1]); + PascalWrite(Result, remote_vm->GetDirectBufferCapacity(buf)); +} + +void Pascal_RemoteVM_GetObjectRefType(void** Params, void** Result) +{ + RemoteVM* remote_vm = PascalRead(Params[0]); + jobject obj = PascalRead(Params[1]); + PascalWrite(Result, remote_vm->GetObjectRefType(obj)); +} + + + +template +int PascalHigh(T* Arr) noexcept +{ + return reinterpret_cast(Arr)[-1]; +} + +template +int PascalLength(T* Arr) noexcept +{ + return PascalHigh(Arr) + 1; +} + +template +T* AllocateArray(std::size_t size, std::size_t element_size) noexcept +{ + std::size_t new_size = (size * element_size) + sizeof(PascalArray); + #if defined(PASCAL_CALLING_CONVENTION) + PascalArray* ptr = static_cast(PLUGIN_MEMORY_MANAGER.AllocMem(new_size)); + #else + PascalArray* ptr = static_cast( + PLUGIN_MEMORY_ALLOCATORS.GetMem ? PLUGIN_MEMORY_ALLOCATORS.GetMem(new_size) : + PLUGIN_SIMBA_METHODS.GetMem ? PLUGIN_SIMBA_METHODS.GetMem(new_size) : + nullptr + ); + #endif + ptr->refCount = 0; + ptr->length = size - 1; + return reinterpret_cast(++ptr); +} + +template +T* AllocateString(std::size_t size, std::size_t element_size) noexcept +{ + std::size_t new_size = (size * element_size) + sizeof(PascalString) + sizeof(T); +#if defined(PASCAL_CALLING_CONVENTION) + PascalString* ptr = static_cast(PLUGIN_MEMORY_MANAGER.AllocMem(new_size)); +#else + PascalString* ptr = static_cast( + PLUGIN_MEMORY_ALLOCATORS.GetMem ? PLUGIN_MEMORY_ALLOCATORS.GetMem(new_size) : + PLUGIN_SIMBA_METHODS.GetMem ? PLUGIN_SIMBA_METHODS.GetMem(new_size) : + nullptr + ); +#endif + + ptr->refCount = 0; + ptr->length = size; + ptr->codePage = 65001; //CP_UTF8 + ptr->elementSize = sizeof(T); + *reinterpret_cast(ptr + 1) = '\0'; + return reinterpret_cast(++ptr); +} + +template +T* GetArray(void* ptr, std::size_t* size) noexcept +{ + if (!ptr) + { + return nullptr; + } + + PascalArray* mem = static_cast(ptr) - 1; + *size = mem->length + 1; + return reinterpret_cast(mem->data); +} + +template +std::vector GetArrayAsVector(void* ptr) noexcept +{ + if (!ptr) + { + return {}; + } + + PascalArray* mem = static_cast(ptr) - 1; + if (mem->length + 1 > 0) + { + T* values = reinterpret_cast(mem->data); + return std::vector(values, values + mem->length + 1); + } + return {}; +} + +template +T* GetString(void* ptr, std::size_t* size) noexcept +{ + PascalString* mem = static_cast(ptr) - 1; + *size = mem->length; + return reinterpret_cast(mem->data); +} + +template +T PascalRead(void* ptr) noexcept +{ + T result; + std::memcpy(&result, ptr, sizeof(T)); + return result; +} + +template +void PascalWrite(void* ptr, T result) noexcept +{ + std::memcpy(ptr, &result, sizeof(T)); +} + +template +void PascalProcess(EIOS* eios, void* ptr, Stream &result) noexcept +{ + T* buffer = reinterpret_cast(&eios->local_storage); + result.read(*buffer); + PascalWrite(ptr, buffer); +} + +void PascalWrite(EIOS* eios, Stream &stream, void* ptr, ReflectionType type) noexcept +{ + switch (type) + { + case ReflectionType::CHAR: + { + PascalProcess(eios, ptr, stream); + } + break; + + case ReflectionType::BYTE: + { + PascalProcess(eios, ptr, stream); + } + break; + + case ReflectionType::BOOL: + { + PascalProcess(eios, ptr, stream); + } + break; + + case ReflectionType::SHORT: + { + PascalProcess(eios, ptr, stream); + } + break; + + case ReflectionType::INT: + { + PascalProcess(eios, ptr, stream); + } + break; + + case ReflectionType::LONG: + { + PascalProcess(eios, ptr, stream); + } + break; + + case ReflectionType::FLOAT: + { + PascalProcess(eios, ptr, stream); + } + break; + + case ReflectionType::DOUBLE: + { + PascalProcess(eios, ptr, stream); + } + break; + + case ReflectionType::STRING: + { + std::string string = stream.read(); + if (!string.empty()) + { + char* output = AllocateString(string.length()); + std::memcpy(output, &string[0], string.length()); + output[string.length()] = '\0'; + + PascalWrite(ptr, output); + } + } + break; + + case ReflectionType::OBJECT: + { + PascalWrite(ptr, stream.read()); + } + break; + + case ReflectionType::ARRAY: + { + PascalWrite(ptr, stream.read()); + } + break; + + default: + { + std::terminate(); + } + break; + } +} \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Plugin/SimbaPlugin.hxx b/third-party/RemoteInput/RemoteInput/Plugin/SimbaPlugin.hxx new file mode 100644 index 0000000..49a32a0 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Plugin/SimbaPlugin.hxx @@ -0,0 +1,527 @@ +#ifndef SIMBAPLUGIN_HXX_INCLUDED +#define SIMBAPLUGIN_HXX_INCLUDED + +#include +#include + +#include "EIOS.hxx" +#include "TMemoryManager.hxx" + + +static const char* PascalExports[] = +{ + //"Pascal_Reflect_ClassLoader", "Function RIGetClassLoader(eios: Pointer; instance: Pointer): jobject; native;", + "Pascal_Reflect_Equal", "Function RIObjectsEqual(eios: Pointer; A, B: Pointer): Boolean; native;", + "Pascal_Reflect_InstanceOf", "Function RIObjectIsInstanceOf(eios: Pointer; instance: Pointer; cls: String): Boolean; native;", + "Pascal_Reflect_Object", "Function RIGetObject(eios: Pointer; instance: Pointer; constref field: ^const RIField): Pointer; native;", + "Pascal_Reflect_Release_Object", "Procedure RIReleaseObject(eios: Pointer; instance: Pointer); native;", + "Pascal_Reflect_Release_Objects", "Procedure RIReleaseObjects(eios: Pointer; objects: Array of Pointer); native;", + "Pascal_Reflect_Boolean", "Function RIGetBool(eios: Pointer; instance: Pointer; constref field: ^const RIField): Boolean; native;", + "Pascal_Reflect_Char", "Function RIGetChar(eios: Pointer; instance: Pointer; constref field: ^const RIField): Char; native;", + "Pascal_Reflect_Byte", "Function RIGetByte(eios: Pointer; instance: Pointer; constref field: ^const RIField): Byte; native;", + "Pascal_Reflect_Short", "Function RIGetShort(eios: Pointer; instance: Pointer; constref field: ^const RIField): Int16; native;", + "Pascal_Reflect_Int", "Function RIGetInt(eios: Pointer; instance: Pointer; constref field: ^const RIField): Int32; native;", + "Pascal_Reflect_Long", "Function RIGetLong(eios: Pointer; instance: Pointer; constref field: ^const RIField): Int64; native;", + "Pascal_Reflect_Float", "Function RIGetFloat(eios: Pointer; instance: Pointer; constref field: ^const RIField): Single; native;", + "Pascal_Reflect_Double", "Function RIGetDouble(eios: Pointer; instance: Pointer; constref field: ^const RIField): Double; native;", + "Pascal_Reflect_String", "Function RIGetString(eios: Pointer; instance: Pointer; constref field: ^const RIField): String; native;", + "Pascal_Reflect_Array", "Function RIGetArray(eios: Pointer; instance: Pointer; constref field: ^const RIField): Pointer; native;", + + //Array size + "Pascal_Reflect_Array_With_Size", "Function RIGetArray(eios: Pointer; instance: Pointer; output_size: ^SizeUInt; constref field: ^const RIField): Pointer; overload; native;", + "Pascal_Reflect_Array_Size", "Function RIGetArraySize(eios: Pointer; arr: Pointer): SizeUInt; native;", + "Pascal_Reflect_Array_Indices", "Function RIGetArrayElements(eios: Pointer; arr: Pointer; elementType: ReflectionType; indices: Array of Int32): Pointer; overload; native;", + + //Array 1-D + "Pascal_Reflect_Array_SingleIndex", "Function RIGetArraySingleElement(eios: Pointer; arr: Pointer; elementType: ReflectionType; index: SizeUInt): Pointer; native;", + "Pascal_Reflect_Array_Index", "Function RIGetArrayElement(eios: Pointer; arr: Pointer; elementType: ReflectionType; index, length: SizeUInt): Pointer; native;", + + //Array 2-D + "Pascal_Reflect_Array_SingleIndex2D", "Function RIGetArraySingleElement(eios: Pointer; arr: Pointer; elementType: ReflectionType; x, y: Int32): Pointer; overload; native;", + "Pascal_Reflect_Array_Index2D", "Function RIGetArrayElement(eios: Pointer; arr: Pointer; elementType: ReflectionType; length: SizeUInt; x, y: Int32): Pointer; overload; native;", + + //Array 3-D + "Pascal_Reflect_Array_SingleIndex3D", "Function RIGetArraySingleElement(eios: Pointer; arr: Pointer; elementType: ReflectionType; x, y, z: Int32): Pointer; overload; native;", + "Pascal_Reflect_Array_Index3D", "Function RIGetArrayElement(eios: Pointer; arr: Pointer; elementType: ReflectionType; length: SizeUInt; x, y, z: Int32): Pointer; overload; native;", + + //Array 4-D + "Pascal_Reflect_Array_SingleIndex4D", "Function RIGetArraySingleElement(eios: Pointer; arr: Pointer; elementType: ReflectionType; x, y, z, w: Int32): Pointer; overload; native;", + "Pascal_Reflect_Array_Index4D", "Function RIGetArrayElement(eios: Pointer; arr: Pointer; elementType: ReflectionType; length: SizeUInt; x, y, z, w: Int32): Pointer; overload; native;", + + //Type Info + "Pascal_Reflect_Class_Name", "Function RIGetClassName(eios: Pointer; instance: Pointer): String native;", + "Pascal_Reflect_Class_Type", "Function RIGetClassType(eios: Pointer; instance: Pointer): String native;", + + //Graphics + "Pascal_GetImageFormat", "Function EIOS_GetImageFormat(eios: Pointer): RIImageFormat; native;", + "Pascal_SetImageFormat", "Procedure EIOS_SetImageFormat(eios: Pointer; format: RIImageFormat); native;", + "Pascal_GetDebugImageBuffer", "Function EIOS_GetDebugImageBuffer(eios: Pointer): ^UInt8; native;", + "Pascal_SetGraphicsDebugging", "Procedure EIOS_SetGraphicsDebugging(eios: Pointer; enabled: Boolean); native;", + + //Pairing + "Pascal_PairClient", "Function EIOS_PairClient(pid: Int32): Pointer; native;", + "Pascal_KillClientPID", "Procedure EIOS_KillClient(pid: Int32); native;", + "Pascal_KillClient", "Procedure EIOS_KillClient(eios: Pointer); overload; native;", + "Pascal_ReleaseClient", "Procedure EIOS_ReleaseClient(eios: Pointer); overload; native;", + "Pascal_GetClients", "Function EIOS_GetClients(unpaired_only: Boolean): SizeUInt; native;", + "Pascal_GetClientPID", "Function EIOS_GetClientPID(index: SizeUInt): Int32; native;", + + //Injecting + "Pascal_Inject", "Procedure RIInject(process_name: String); native;", + "Pascal_Inject_PID", "Procedure RIInject(pid: Int32); overload; native;", + + //Other + "Pascal_HasFocus", "Function EIOS_HasFocus(eios: Pointer): Boolean; native;", + "Pascal_GainFocus", "Procedure EIOS_GainFocus(eios: Pointer); native;", + "Pascal_LoseFocus", "Procedure EIOS_LoseFocus(eios: Pointer); native;", + "Pascal_IsKeyboardInputEnabled", "Function EIOS_IsKeyboardInputEnabled(eios: Pointer): Boolean; native;", + "Pascal_SetKeyboardInputEnabled", "Function EIOS_SetKeyboardInputEnabled(eios: Pointer; enabled: Boolean): Boolean; native;", + "Pascal_IsMouseInputEnabled", "Function EIOS_IsMouseInputEnabled(eios: Pointer): Boolean; native;", + "Pascal_SetMouseInputEnabled", "Function EIOS_SetMouseInputEnabled(eios: Pointer; enabled: Boolean): Boolean; native;", + "Pascal_GetRealMousePosition", "Procedure EIOS_GetRealMousePosition(eios: Pointer; var x, y: Int32); native;", + "Pascal_GetKeyboardSpeed", "Function EIOS_GetKeyboardSpeed(eios: Pointer): Int32; native;", + "Pascal_SetKeyboardSpeed", "Procedure EIOS_SetKeyboardSpeed(eios: Pointer; speed: Int32); native;", + "Pascal_GetKeyboardRepeatDelay", "Function EIOS_GetKeyboardRepeatDelay(eios: Pointer): Int32; native;", + "Pascal_SetKeyboardRepeatDelay", "Procedure EIOS_SetKeyboardRepeatDelay(eios: Pointer; delay: Int32); native;", + //"Pascal_GetGraphicsScaling", "Function EIOS_GetGraphicsScaling(eios: Pointer): Boolean; native;", + //"Pascal_SetGraphicsScaling", "Procedure EIOS_SetGraphicsScaling(eios: Pointer; enabled: Boolean); native;", + + //RemoteVM + "Pascal_RemoteVM_Init", "Procedure RemoteVM.Init(eios: Pointer); native;", + "Pascal_RemoteVM_InitEx", "Procedure RemoteVM.Init(args: array of String); overload; native;", + "Pascal_RemoteVM_Free", "Procedure RemoteVM.Free(); native;", + + "Pascal_RemoteVM_MaxMemoryChunkSize", "Function RemoteVM.MaxMemoryChunkSize(): SizeUInt; native;", + "Pascal_RemoteVM_AllocateMemory", "Function RemoteVM.AllocateMemory(size: SizeUInt): Pointer; native;", + "Pascal_RemoteVM_ReadMemory", "Function RemoteVM.ReadMemory(destintation, source: Pointer; size: SizeUInt): Boolean; native;", + "Pascal_RemoteVM_WriteMemory", "Function RemoteVM.WriteMemory(destintation, source: Pointer; size: SizeUInt): Boolean; native;", + "Pascal_RemoteVM_FreeMemory", "Procedure RemoteVM.FreeMemory(memory: Pointer); native;", + "Pascal_RemoteVM_GetVersion", "Function RemoteVM.GetVersion: jint; native", + "Pascal_RemoteVM_DefineClass", "Function RemoteVM.DefineClass(name: String; loader: jobject; buf: Pointer; len: jsize): jclass; native;", + "Pascal_RemoteVM_FindClass", "Function RemoteVM.FindClass(name: String): jclass; native;", + "Pascal_RemoteVM_FromReflectedMethod", "Function RemoteVM.FromReflectedMethod(method: jobject): jmethodID; native;", + "Pascal_RemoteVM_FromReflectedField", "Function RemoteVM.FromReflectedField(field: jobject): jfieldID; native;", + "Pascal_RemoteVM_ToReflectedMethod", "Function RemoteVM.ToReflectedMethod(cls: jclass; methodID: jmethodID; isStatic: jboolean): jobject; native;", + "Pascal_RemoteVM_GetSuperclass", "Function RemoteVM.GetSuperclass(sub: jclass): jclass; native;", + "Pascal_RemoteVM_IsAssignableFrom", "Function RemoteVM.IsAssignableFrom(sub, sup: jclass): jboolean; native;", + "Pascal_RemoteVM_ToReflectedField", "Function RemoteVM.ToReflectedField(cls: jclass; fieldID: jfieldID; isStatic: jboolean): jobject; native;", + "Pascal_RemoteVM_Throw", "Function RemoteVM.Throw(obj: jthrowable): jint; native;", + "Pascal_RemoteVM_ThrowNew", "Function RemoteVM.ThrowNew(clazz: jclass; const msg: string): jint; native;", + "Pascal_RemoteVM_GetExceptionMessage", "Function RemoteVM.GetExceptionMessage(): String; native;", + "Pascal_RemoteVM_FatalError", "Procedure RemoteVM.FatalError(msg: String); native;", + "Pascal_RemoteVM_DeleteGlobalRef", "Procedure RemoteVM.DeleteGlobalRef(gref: jobject); native;", + "Pascal_RemoteVM_IsSameObject", "Function RemoteVM.IsSameObject(obj1, obj2: jobject): jboolean; native;", + "Pascal_RemoteVM_AllocObject", "Function RemoteVM.AllocObject(clazz: jclass): jobject; native;", + "Pascal_RemoteVM_NewObject", "Function RemoteVM.NewObject(clazz: jclass; methodID: jmethodID; args: array of jvalue): jobject; native;", + "Pascal_RemoteVM_GetObjectClass", "Function RemoteVM.GetObjectClass(obj: jobject): jclass; native;", + "Pascal_RemoteVM_IsInstanceOf", "Function RemoteVM.IsInstanceOf(obj: jobject; clazz: jclass): jboolean; native;", + "Pascal_RemoteVM_GetMethodID", "Function RemoteVM.GetMethodID(clazz: jclass; name, sig: String): jmethodID; native;", + "Pascal_RemoteVM_CallObjectMethod", "Function RemoteVM.CallObjectMethod(obj: jobject; methodID: jmethodID; args: array of jvalue): jobject; native;", + "Pascal_RemoteVM_CallBooleanMethod", "Function RemoteVM.CallBooleanMethod(obj: jobject; methodID: jmethodID; args: array of jvalue): jboolean; native;", + "Pascal_RemoteVM_CallByteMethod", "Function RemoteVM.CallByteMethod(obj: jobject; methodID: jmethodID; args: array of jvalue): jbyte; native;", + "Pascal_RemoteVM_CallCharMethod", "Function RemoteVM.CallCharMethod(obj: jobject; methodID: jmethodID; args: array of jvalue): jchar; native;", + "Pascal_RemoteVM_CallShortMethod", "Function RemoteVM.CallShortMethod(obj: jobject; methodID: jmethodID; args: array of jvalue): jshort; native;", + "Pascal_RemoteVM_CallIntMethod", "Function RemoteVM.CallIntMethod(obj: jobject; methodID: jmethodID; args: array of jvalue): jint; native;", + "Pascal_RemoteVM_CallLongMethod", "Function RemoteVM.CallLongMethod(obj: jobject; methodID: jmethodID; args: array of jvalue): jlong; native;", + "Pascal_RemoteVM_CallFloatMethod", "Function RemoteVM.CallFloatMethod(obj: jobject; methodID: jmethodID; args: array of jvalue): jfloat; native;", + "Pascal_RemoteVM_CallDoubleMethod", "Function RemoteVM.CallDoubleMethod(obj: jobject; methodID: jmethodID; args: array of jvalue): jdouble; native;", + "Pascal_RemoteVM_CallVoidMethod", "Procedure RemoteVM.CallVoidMethod(obj: jobject; methodID: jmethodID; args: array of jvalue); native;", + "Pascal_RemoteVM_CallNonVirtualObjectMethod", "Function RemoteVM.CallNonVirtualObjectMethod(obj: jobject; clazz: jclass; methodID: jmethodID; args: array of jvalue): jobject; native;", + "Pascal_RemoteVM_CallNonVirtualBooleanMethod", "Function RemoteVM.CallNonVirtualBooleanMethod(obj: jobject; clazz: jclass; methodID: jmethodID; args: array of jvalue): jboolean; native;", + "Pascal_RemoteVM_CallNonVirtualByteMethod", "Function RemoteVM.CallNonVirtualByteMethod(obj: jobject; clazz: jclass; methodID: jmethodID; args: array of jvalue): jbyte; native;", + "Pascal_RemoteVM_CallNonVirtualCharMethod", "Function RemoteVM.CallNonVirtualCharMethod(obj: jobject; clazz: jclass; methodID: jmethodID; args: array of jvalue): jchar; native;", + "Pascal_RemoteVM_CallNonVirtualShortMethod", "Function RemoteVM.CallNonVirtualShortMethod(obj: jobject; clazz: jclass; methodID: jmethodID; args: array of jvalue): jshort; native;", + "Pascal_RemoteVM_CallNonVirtualIntMethod", "Function RemoteVM.CallNonVirtualIntMethod(obj: jobject; clazz: jclass; methodID: jmethodID; args: array of jvalue): jint; native;", + "Pascal_RemoteVM_CallNonVirtualLongMethod", "Function RemoteVM.CallNonVirtualLongMethod(obj: jobject; clazz: jclass; methodID: jmethodID; args: array of jvalue): jlong; native;", + "Pascal_RemoteVM_CallNonVirtualFloatMethod", "Function RemoteVM.CallNonVirtualFloatMethod(obj: jobject; clazz: jclass; methodID: jmethodID; args: array of jvalue): jfloat; native;", + "Pascal_RemoteVM_CallNonVirtualDoubleMethod", "Function RemoteVM.CallNonVirtualDoubleMethod(obj: jobject; clazz: jclass; methodID: jmethodID; args: array of jvalue): jdouble; native;", + "Pascal_RemoteVM_CallNonVirtualVoidMethod", "Procedure RemoteVM.CallNonVirtualVoidMethod(obj: jobject; clazz: jclass; methodID: jmethodID; args: array of jvalue); native;", + "Pascal_RemoteVM_GetFieldID", "Function RemoteVM.GetFieldID(clazz: jclass; name, sig: string): jfieldID; native;", + "Pascal_RemoteVM_GetObjectField", "Function RemoteVM.GetObjectField(obj: jobject; fieldID: jfieldID): jobject; native;", + "Pascal_RemoteVM_GetBooleanField", "Function RemoteVM.GetBooleanField(obj: jobject; fieldID: jfieldID): jboolean; native;", + "Pascal_RemoteVM_GetByteField", "Function RemoteVM.GetByteField(obj: jobject; fieldID: jfieldID): jbyte; native;", + "Pascal_RemoteVM_GetCharField", "Function RemoteVM.GetCharField(obj: jobject; fieldID: jfieldID): jchar; native;", + "Pascal_RemoteVM_GetShortField", "Function RemoteVM.GetShortField(obj: jobject; fieldID: jfieldID): jshort; native;", + "Pascal_RemoteVM_GetIntField", "Function RemoteVM.GetIntField(obj: jobject; fieldID: jfieldID): jint; native;", + "Pascal_RemoteVM_GetFloatField", "Function RemoteVM.GetFloatField(obj: jobject; fieldID: jfieldID): jfloat; native;", + "Pascal_RemoteVM_GetDoubleField", "Function RemoteVM.GetDoubleField(obj: jobject; fieldID: jfieldID): jdouble; native;", + "Pascal_RemoteVM_SetObjectField", "Procedure RemoteVM.SetObjectField(obj: jobject; fieldID: jfieldID; value: jobject); native;", + "Pascal_RemoteVM_SetBooleanField", "Procedure RemoteVM.SetBooleanField(obj: jobject; fieldID: jfieldID; value: jboolean); native;", + "Pascal_RemoteVM_SetByteField", "Procedure RemoteVM.SetByteField(obj: jobject; fieldID: jfieldID; value: jbyte); native;", + "Pascal_RemoteVM_SetCharField", "Procedure RemoteVM.SetCharField(obj: jobject; fieldID: jfieldID; value: jchar); native;", + "Pascal_RemoteVM_SetShortField", "Procedure RemoteVM.SetShortField(obj: jobject; fieldID: jfieldID; value: jshort); native;", + "Pascal_RemoteVM_SetIntField", "Procedure RemoteVM.SetIntField(obj: jobject; fieldID: jfieldID; value: jint); native;", + "Pascal_RemoteVM_SetFloatField", "Procedure RemoteVM.SetFloatField(obj: jobject; fieldID: jfieldID; value: jfloat); native;", + "Pascal_RemoteVM_SetDoubleField", "Procedure RemoteVM.SetDoubleField(obj: jobject; fieldID: jfieldID; value: jdouble); native;", + "Pascal_RemoteVM_GetStaticMethodID", "Function RemoteVM.GetStaticMethodID(clazz: jclass; name, sig: string): jmethodID; native;", + "Pascal_RemoteVM_CallStaticObjectMethod", "Function RemoteVM.CallStaticObjectMethod(clazz: jclass; methodID: jmethodID; args: array of jvalue): jobject; native;", + "Pascal_RemoteVM_CallStaticBooleanMethod", "Function RemoteVM.CallStaticBooleanMethod(clazz: jclass; methodID: jmethodID; args: array of jvalue): jboolean; native;", + "Pascal_RemoteVM_CallStaticByteMethod", "Function RemoteVM.CallStaticByteMethod(clazz: jclass; methodID: jmethodID; args: array of jvalue): jbyte; native;", + "Pascal_RemoteVM_CallStaticCharMethod", "Function RemoteVM.CallStaticCharMethod(clazz: jclass; methodID: jmethodID; args: array of jvalue): jchar; native;", + "Pascal_RemoteVM_CallStaticShortMethod", "Function RemoteVM.CallStaticShortMethod(clazz: jclass; methodID: jmethodID; args: array of jvalue): jshort; native;", + "Pascal_RemoteVM_CallStaticIntMethod", "Function RemoteVM.CallStaticIntMethod(clazz: jclass; methodID: jmethodID; args: array of jvalue): jint; native;", + "Pascal_RemoteVM_CallStaticLongMethod", "Function RemoteVM.CallStaticLongMethod(clazz: jclass; methodID: jmethodID; args: array of jvalue): jlong; native;", + "Pascal_RemoteVM_CallStaticFloatMethod", "Function RemoteVM.CallStaticFloatMethod(clazz: jclass; methodID: jmethodID; args: array of jvalue): jfloat; native;", + "Pascal_RemoteVM_CallStaticDoubleMethod", "Function RemoteVM.CallStaticDoubleMethod(clazz: jclass; methodID: jmethodID; args: array of jvalue): jdouble; native;", + "Pascal_RemoteVM_CallStaticVoidMethod", "Procedure RemoteVM.CallStaticVoidMethod(cls: jclass; methodID: jmethodID; args: array of jvalue); native;", + "Pascal_RemoteVM_GetStaticFieldID", "Function RemoteVM.GetStaticFieldID(clazz: jclass; name: string; sign: string): jfieldID; native;", + "Pascal_RemoteVM_GetStaticObjectField", "Function RemoteVM.GetStaticObjectField(clazz: jclass; fieldID: jfieldID): jobject; native;", + "Pascal_RemoteVM_GetStaticBooleanField", "Function RemoteVM.GetStaticBooleanField(clazz: jclass; fieldID: jfieldID): jboolean; native;", + "Pascal_RemoteVM_GetStaticByteField", "Function RemoteVM.GetStaticByteField(clazz: jclass; fieldID: jfieldID): jbyte; native;", + "Pascal_RemoteVM_GetStaticCharField", "Function RemoteVM.GetStaticCharField(clazz: jclass; fieldID: jfieldID): jchar; native;", + "Pascal_RemoteVM_GetStaticShortField", "Function RemoteVM.GetStaticShortField(clazz: jclass; fieldID: jfieldID): jshort; native;", + "Pascal_RemoteVM_GetStaticIntField", "Function RemoteVM.GetStaticIntField(clazz: jclass; fieldID: jfieldID): jint; native;", + "Pascal_RemoteVM_GetStaticFloatField", "Function RemoteVM.GetStaticFloatField(clazz: jclass; fieldID: jfieldID): jfloat; native;", + "Pascal_RemoteVM_GetStaticDoubleField", "Function RemoteVM.GetStaticDoubleField(clazz: jclass; fieldID: jfieldID): jdouble; native;", + "Pascal_RemoteVM_SetStaticObjectField", "Procedure RemoteVM.SetStaticObjectField(clazz: jclass; fieldID: jfieldID; value: jobject); native;", + "Pascal_RemoteVM_SetStaticBooleanField", "Procedure RemoteVM.SetStaticBooleanField(clazz: jclass; fieldID: jfieldID; value: jboolean); native;", + "Pascal_RemoteVM_SetStaticByteField", "Procedure RemoteVM.SetStaticByteField(clazz: jclass; fieldID: jfieldID; value: jbyte); native;", + "Pascal_RemoteVM_SetStaticCharField", "Procedure RemoteVM.SetStaticCharField(clazz: jclass; fieldID: jfieldID; value: jchar); native;", + "Pascal_RemoteVM_SetStaticShortField", "Procedure RemoteVM.SetStaticShortField(clazz: jclass; fieldID: jfieldID; value: jshort); native;", + "Pascal_RemoteVM_SetStaticIntField", "Procedure RemoteVM.SetStaticIntField(clazz: jclass; fieldID: jfieldID; value: jint); native;", + "Pascal_RemoteVM_SetStaticFloatField", "Procedure RemoteVM.SetStaticFloatField(clazz: jclass; fieldID: jfieldID; value: jfloat); native;", + "Pascal_RemoteVM_SetStaticDoubleField", "Procedure RemoteVM.SetStaticDoubleField(clazz: jclass; fieldID: jfieldID; value: jdouble); native;", + "Pascal_RemoteVM_NewString", "Function RemoteVM.NewString(unicode_string: widestring): jstring; native;", + "Pascal_RemoteVM_GetStringLength", "Function RemoteVM.GetStringLength(str: jstring): jsize; native;", + "Pascal_RemoteVM_GetStringChars", "Function RemoteVM.GetStringChars(str: jstring): widestring; native;", + "Pascal_RemoteVM_NewStringUTF", "Function RemoteVM.NewStringUTF(utf_string: string): jstring; native;", + "Pascal_RemoteVM_GetStringUTFLength", "Function RemoteVM.GetStringUTFLength(str: jstring): jsize; native;", + "Pascal_RemoteVM_GetStringUTFChars", "Function RemoteVM.GetStringUTFChars(str: jstring): string; native;", + "Pascal_RemoteVM_GetArrayLength", "Function RemoteVM.GetArrayLength(arr: jarray): jsize; native;", + "Pascal_RemoteVM_NewObjectArray", "Function RemoteVM.NewObjectArray(len: jsize; clazz: jclass; init: jobject): jobjectArray; native;", + "Pascal_RemoteVM_NewBooleanArray", "Function RemoteVM.NewBooleanArray(len: jsize): jbooleanArray; native;", + "Pascal_RemoteVM_NewByteArray", "Function RemoteVM.NewByteArray(len: jsize): jbyteArray; native;", + "Pascal_RemoteVM_NewCharArray", "Function RemoteVM.NewCharArray(len: jsize): jcharArray; native;", + "Pascal_RemoteVM_NewShortArray", "Function RemoteVM.NewShortArray(len: jsize): jshortArray; native;", + "Pascal_RemoteVM_NewIntArray", "Function RemoteVM.NewIntArray(len: jsize): jintArray; native;", + "Pascal_RemoteVM_NewLongArray", "Function RemoteVM.NewLongArray(len: jsize): jlongArray; native;", + "Pascal_RemoteVM_NewFloatArray", "Function RemoteVM.NewFloatArray(len: jsize): jfloatArray; native;", + "Pascal_RemoteVM_NewDoubleArray", "Function RemoteVM.NewDoubleArray(len: jsize): jdoubleArray; native;", + "Pascal_RemoteVM_GetObjectArrayElements", "Function RemoteVM.GetObjectArrayElements(arr: jobjectArray): array of jobject; native;", + "Pascal_RemoteVM_SetObjectArrayElements", "Procedure RemoteVM.SetObjectArrayElements(arr: jobjectArray; index: jsize; elements: array of jobject); native;", + "Pascal_RemoteVM_GetBooleanArrayElements", "Function RemoteVM.GetBooleanArrayElements(arr: jbooleanArray): array of jboolean; native;", + "Pascal_RemoteVM_GetByteArrayElements", "Function RemoteVM.GetByteArrayElements(arr: jbyteArray): array of jbyte; native;", + "Pascal_RemoteVM_GetCharArrayElements", "Function RemoteVM.GetCharArrayElements(arr: jcharArray): array of jchar; native;", + "Pascal_RemoteVM_GetShortArrayElements", "Function RemoteVM.GetShortArrayElements(arr: jshortArray): array of jshort; native;", + "Pascal_RemoteVM_GetIntArrayElements", "Function RemoteVM.GetIntArrayElements(arr: jintArray): array of jint; native;", + "Pascal_RemoteVM_GetLongArrayElements", "Function RemoteVM.GetLongArrayElements(arr: jlongArray): array of jlong; native;", + "Pascal_RemoteVM_GetFloatArrayElements", "Function RemoteVM.GetFloatArrayElements(arr: jfloatArray): array of jfloat; native;", + "Pascal_RemoteVM_GetDoubleArrayElements", "Function RemoteVM.GetDoubleArrayElements(arr: jdoubleArray): array of jdouble; native;", + "Pascal_RemoteVM_GetBooleanArrayRegion", "Function RemoteVM.GetBooleanArrayRegion(arr: jbooleanArray; start, len: jsize): array of jboolean; native;", + "Pascal_RemoteVM_GetByteArrayRegion", "Function RemoteVM.GetByteArrayRegion(arr: jbyteArray; start, len: jsize): array of jbyte; native;", + "Pascal_RemoteVM_GetCharArrayRegion", "Function RemoteVM.GetCharArrayRegion(arr: jcharArray; start, len: jsize): array of jchar; native;", + "Pascal_RemoteVM_GetShortArrayRegion", "Function RemoteVM.GetShortArrayRegion(arr: jshortArray; start, len: jsize): array of jshort; native;", + "Pascal_RemoteVM_GetIntArrayRegion", "Function RemoteVM.GetIntArrayRegion(arr: jintArray; start, len: jsize): array of jint; native;", + "Pascal_RemoteVM_GetLongArrayRegion", "Function RemoteVM.GetLongArrayRegion(arr: jlongArray; start, len: jsize): array of jlong; native;", + "Pascal_RemoteVM_GetFloatArrayRegion", "Function RemoteVM.GetFloatArrayRegion(arr: jfloatArray; start, len: jsize): array of jfloat; native;", + "Pascal_RemoteVM_GetDoubleArrayRegion", "Function RemoteVM.GetDoubleArrayRegion(arr: jdoubleArray; start, len: jsize): array of jboolean; native;", + "Pascal_RemoteVM_SetBooleanArrayRegion", "Procedure RemoteVM.SetBooleanArrayRegion(arr: jbooleanArray; start, len: jsize; buf: array of jboolean); native;", + "Pascal_RemoteVM_SetByteArrayRegion", "Procedure RemoteVM.SetByteArrayRegion(arr: jbyteArray; start, len: jsize; buf: array of jbyte); native;", + "Pascal_RemoteVM_SetCharArrayRegion", "Procedure RemoteVM.SetCharArrayRegion(arr: jcharArray; start, len: jsize; buf: array of jchar); native;", + "Pascal_RemoteVM_SetShortArrayRegion", "Procedure RemoteVM.SetShortArrayRegion(arr: jshortArray; start, len: jsize; buf: array of jshort); native;", + "Pascal_RemoteVM_SetIntArrayRegion", "Procedure RemoteVM.SetIntArrayRegion(arr: jintArray; start, len: jsize; buf: array of jint); native;", + "Pascal_RemoteVM_SetLongArrayRegion", "Procedure RemoteVM.SetLongArrayRegion(arr: jlongArray; start, len: jsize; buf: array of jlong); native;", + "Pascal_RemoteVM_SetFloatArrayRegion", "Procedure RemoteVM.SetFloatArrayRegion(arr: jfloatArray; start, len: jsize; buf: array of jfloat); native;", + "Pascal_RemoteVM_SetDoubleArrayRegion", "Procedure RemoteVM.SetDoubleArrayRegion(arr: jdoubleArray; start, len: jsize; buf: array of jboolean); native;", + "Pascal_RemoteVM_MonitorEnter", "Function RemoteVM.MonitorEnter(obj: jobject): jint; native;", + "Pascal_RemoteVM_MonitorExit", "Function RemoteVM.MonitorExit(obj: jobject): jint; native;", + //"Pascal_RemoteVM_GetJavaVM", "Function RemoteVM.GetJavaVM(): JavaVM; native;", + "Pascal_RemoteVM_NewDirectByteBuffer", "Function RemoteVM.NewDirectByteBuffer(address: Pointer; capacity: jlong): jobject; native;", + "Pascal_RemoteVM_GetDirectBufferAddress", "Function RemoteVM.GetDirectBufferAddress(buf: jobject): Pointer; native;", + "Pascal_RemoteVM_GetDirectBufferCapacity", "Function RemoteVM.GetDirectBufferCapacity(buf: jobject): jlong; native;", + "Pascal_RemoteVM_GetObjectRefType", "Function RemoteVM.GetObjectRefType(obj: jobject): jobjectRefType; native;" +}; + +static const char* PascalTypes[] = +{ + "ReflectionType", "(RI_CHAR = 0, RI_BYTE = 1, RI_BOOLEAN = 2, RI_SHORT = 3, RI_INT = 4, RI_LONG = 5, RI_FLOAT = 6, RI_DOUBLE = 7, RI_STRING = 8, RI_OBJECT = 9)", + "RIImageFormat", "(RI_BGR_BGRA = 0, RI_BGRA = 1, RI_RGBA = 2, RI_ARGB = 3, RI_ABGR = 4)", + "RIField", "packed record cls, field, desc: String; end;", + + //JVM Types + "jint", "Int32", + "jlong", "Int64", + "jbyte", "UInt8", + "jboolean", "ByteBool", + "jchar", "UInt16", + "jshort", "Int16", + "jfloat", "Single", + "jdouble", "Double", + "jsize", "jint", + + "jobject", "type Pointer", + "jclass", "type jobject", + "jthrowable", "type jobject", + "jstring", "type jobject", + "jarray", "type jobject", + + "jbooleanArray", "type jarray", + "jbyteArray", "type jarray", + "jcharArray", "type jarray", + "jshortArray", "type jarray", + "jintArray", "type jarray", + "jlongArray", "type jarray", + "jfloatArray", "type jarray", + "jdoubleArray", "type jarray", + "jobjectArray", "type jarray", + + "jweak", "type jobject", + "jfieldID", "type Pointer", + "jmethodID", "type Pointer", + + "jvalue", "union z: jboolean; b: jbyte; c: jchar; s: jshort; i: jint; j: jlong; f: jfloat; d: jdouble; l: jobject; end;", + "jobjectRefType", "(JNIInvalidRefType = 0, JNILocalRefType = 1, JNIGlobalRefType = 2, JNIWeakGlobalRefType = 3)", + + "RemoteVM", "type Pointer" +}; + +static const long int PascalExportCount = sizeof(PascalExports) / (sizeof(PascalExports[0]) * 2); +static const long int PascalTypeCount = sizeof(PascalTypes) / (sizeof(PascalTypes[0]) * 2); + + +#ifdef __cplusplus +extern "C" +{ +#endif + +EXPORT int GetPluginABIVersion() noexcept; +EXPORT int GetFunctionCount() noexcept; +EXPORT int GetTypeCount() noexcept; +EXPORT int GetFunctionInfo(int Index, void** Address, char** Definition) noexcept; +EXPORT int GetTypeInfo(int Index, char** Type, char** Definition) noexcept; + +#if defined(PASCAL_CALLING_CONVENTION) +EXPORT void SetPluginMemManager(TMemoryManager MemMgr) noexcept; +#endif + +EXPORT void SetPluginSimbaMethods(TSimbaMethods Methods) noexcept; +EXPORT void SetPluginSimbaMemoryAllocators(TSimbaMemoryAllocators Allocators) noexcept; +EXPORT void RegisterSimbaPlugin(TSimbaInfomation* Information, TSimbaMethodsExtended* Methods) noexcept; +EXPORT void OnAttach(void* info) noexcept; +EXPORT void OnDetach() noexcept; + +#ifdef __cplusplus +} +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +EXPORT void Pascal_Reflect_ClassLoader(void** Params, void** Result) noexcept; +EXPORT void Pascal_Reflect_Equal(void** Params, void** Result) noexcept; +EXPORT void Pascal_Reflect_InstanceOf(void** Params, void** Result) noexcept; +EXPORT void Pascal_Reflect_Object(void** Params, void** Result) noexcept; +EXPORT void Pascal_Reflect_Release_Object(void** Params, void** Result) noexcept; +EXPORT void Pascal_Reflect_Release_Objects(void** Params, void** Result) noexcept; +EXPORT void Pascal_Reflect_Boolean(void** Params, void** Result) noexcept; +EXPORT void Pascal_Reflect_Char(void** Params, void** Result) noexcept; +EXPORT void Pascal_Reflect_Byte(void** Params, void** Result) noexcept; +EXPORT void Pascal_Reflect_Short(void** Params, void** Result) noexcept; +EXPORT void Pascal_Reflect_Int(void** Params, void** Result) noexcept; +EXPORT void Pascal_Reflect_Long(void** Params, void** Result) noexcept; +EXPORT void Pascal_Reflect_Float(void** Params, void** Result) noexcept; +EXPORT void Pascal_Reflect_Double(void** Params, void** Result) noexcept; +EXPORT void Pascal_Reflect_String(void** Params, void** Result) noexcept; +EXPORT void Pascal_Reflect_Array(void** Params, void** Result) noexcept; +EXPORT void Pascal_Reflect_Array_With_Size(void** Params, void** Result) noexcept; +EXPORT void Pascal_Reflect_Array_Size(void** Params, void** Result) noexcept; +EXPORT void Pascal_Reflect_Array_Indices(void** Params, void** Result) noexcept; + +EXPORT void Pascal_Reflect_Array_SingleIndex(void** Params, void** Result) noexcept; +EXPORT void Pascal_Reflect_Array_SingleIndex2D(void** Params, void** Result) noexcept; +EXPORT void Pascal_Reflect_Array_SingleIndex3D(void** Params, void** Result) noexcept; +EXPORT void Pascal_Reflect_Array_SingleIndex4D(void** Params, void** Result) noexcept; + +EXPORT void Pascal_Reflect_Array_Index(void** Params, void** Result) noexcept; +EXPORT void Pascal_Reflect_Array_Index2D(void** Params, void** Result) noexcept; +EXPORT void Pascal_Reflect_Array_Index3D(void** Params, void** Result) noexcept; +EXPORT void Pascal_Reflect_Array_Index4D(void** Params, void** Result) noexcept; + +EXPORT void Pascal_Reflect_Class_Name(void** Params, void** Result) noexcept; +EXPORT void Pascal_Reflect_Class_Type(void** Params, void** Result) noexcept; + +EXPORT void Pascal_GetImageFormat(void** Params, void** Result) noexcept; +EXPORT void Pascal_SetImageFormat(void** Params, void** Result) noexcept; +EXPORT void Pascal_GetDebugImageBuffer(void** Params, void** Result) noexcept; +EXPORT void Pascal_SetGraphicsDebugging(void** Params, void** Result) noexcept; +EXPORT void Pascal_PairClient(void** Params, void** Result) noexcept; +EXPORT void Pascal_KillClientPID(void** Params, void** Result) noexcept; +EXPORT void Pascal_KillClient(void** Params, void** Result) noexcept; +EXPORT void Pascal_ReleaseClient(void** Params, void** Result) noexcept; +EXPORT void Pascal_GetClients(void** Params, void** Result) noexcept; +EXPORT void Pascal_GetClientPID(void** Params, void** Result) noexcept; + +EXPORT void Pascal_Inject(void** Params, void** Result) noexcept; +EXPORT void Pascal_Inject_PID(void** Params, void** Result) noexcept; + +EXPORT void Pascal_HasFocus(void** Params, void** Result) noexcept; +EXPORT void Pascal_GainFocus(void** Params, void** Result) noexcept; +EXPORT void Pascal_LoseFocus(void** Params, void** Result) noexcept; +EXPORT void Pascal_IsKeyboardInputEnabled(void** Params, void** Result) noexcept; +EXPORT void Pascal_SetKeyboardInputEnabled(void** Params, void** Result) noexcept; +EXPORT void Pascal_IsMouseInputEnabled(void** Params, void** Result) noexcept; +EXPORT void Pascal_SetMouseInputEnabled(void** Params, void** Result) noexcept; +EXPORT void Pascal_GetRealMousePosition(void** Params, void** Result) noexcept; + +EXPORT void Pascal_GetKeyboardSpeed(void** Params, void** Result) noexcept; +EXPORT void Pascal_SetKeyboardSpeed(void** Params, void** Result) noexcept; +EXPORT void Pascal_GetKeyboardRepeatDelay(void** Params, void** Result) noexcept; +EXPORT void Pascal_SetKeyboardRepeatDelay(void** Params, void** Result) noexcept; +EXPORT void Pascal_GetGraphicsScaling(void** Params, void** Result) noexcept; +EXPORT void Pascal_SetGraphicsScaling(void** Params, void** Result) noexcept; + +// MARK: - RemoteVM +EXPORT void Pascal_RemoteVM_Init(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_InitEx(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_Free(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_MaxMemoryChunkSize(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_AllocateMemory(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_ReadMemory(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_WriteMemory(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_FreeMemory(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetVersion(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_DefineClass(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_FindClass(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_FromReflectedMethod(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_FromReflectedField(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_ToReflectedMethod(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetSuperclass(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_IsAssignableFrom(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_ToReflectedField(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_Throw(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_ThrowNew(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetExceptionMessage(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_FatalError(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_DeleteGlobalRef(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_IsSameObject(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_AllocObject(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_NewObject(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetObjectClass(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_IsInstanceOf(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetMethodID(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_CallObjectMethod(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_CallBooleanMethod(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_CallByteMethod(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_CallCharMethod(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_CallShortMethod(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_CallIntMethod(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_CallLongMethod(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_CallFloatMethod(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_CallDoubleMethod(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_CallVoidMethod(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_CallNonvirtualObjectMethod(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_CallNonvirtualBooleanMethod(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_CallNonvirtualByteMethod(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_CallNonvirtualCharMethod(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_CallNonvirtualShortMethod(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_CallNonvirtualIntMethod(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_CallNonvirtualLongMethod(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_CallNonvirtualFloatMethod(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_CallNonvirtualDoubleMethod(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_CallNonvirtualVoidMethod(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetFieldID(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetObjectField(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetBooleanField(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetByteField(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetCharField(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetShortField(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetIntField(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetLongField(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetFloatField(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetDoubleField(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_SetObjectField(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_SetBooleanField(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_SetByteField(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_SetCharField(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_SetShortField(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_SetIntField(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_SetLongField(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_SetFloatField(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_SetDoubleField(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetStaticMethodID(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_CallStaticObjectMethod(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_CallStaticBooleanMethod(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_CallStaticByteMethod(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_CallStaticCharMethod(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_CallStaticShortMethod(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_CallStaticIntMethod(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_CallStaticLongMethod(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_CallStaticFloatMethod(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_CallStaticDoubleMethod(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_CallStaticVoidMethod(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetStaticFieldID(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetStaticObjectField(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetStaticBooleanField(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetStaticByteField(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetStaticCharField(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetStaticShortField(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetStaticIntField(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetStaticLongField(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetStaticFloatField(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetStaticDoubleField(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_SetStaticObjectField(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_SetStaticBooleanField(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_SetStaticByteField(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_SetStaticCharField(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_SetStaticShortField(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_SetStaticIntField(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_SetStaticLongField(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_SetStaticFloatField(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_SetStaticDoubleField(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_NewString(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetStringLength(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetStringChars(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_NewStringUTF(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetStringUTFLength(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetStringUTFChars(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetArrayLength(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_NewObjectArray(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_NewBooleanArray(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_NewByteArray(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_NewCharArray(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_NewShortArray(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_NewIntArray(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_NewLongArray(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_NewFloatArray(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_NewDoubleArray(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetObjectArrayElements(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_SetObjectArrayElements(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetBooleanArrayElements(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetByteArrayElements(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetCharArrayElements(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetShortArrayElements(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetIntArrayElements(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetLongArrayElements(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetFloatArrayElements(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetDoubleArrayElements(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetBooleanArrayRegion(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetByteArrayRegion(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetCharArrayRegion(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetShortArrayRegion(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetIntArrayRegion(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetLongArrayRegion(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetFloatArrayRegion(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetDoubleArrayRegion(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_SetBooleanArrayRegion(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_SetByteArrayRegion(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_SetCharArrayRegion(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_SetShortArrayRegion(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_SetIntArrayRegion(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_SetLongArrayRegion(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_SetFloatArrayRegion(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_SetDoubleArrayRegion(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_MonitorEnter(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_MonitorExit(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetJavaVM(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_NewDirectByteBuffer(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetDirectBufferAddress(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetDirectBufferCapacity(void** Params, void** Result); +EXPORT void Pascal_RemoteVM_GetObjectRefType(void** Params, void** Result); + +#ifdef __cplusplus +} +#endif + +#endif // SIMBAPLUGIN_HXX_INCLUDED diff --git a/third-party/RemoteInput/RemoteInput/Plugin/TMemoryManager.hxx b/third-party/RemoteInput/RemoteInput/Plugin/TMemoryManager.hxx new file mode 100644 index 0000000..ef1d442 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Plugin/TMemoryManager.hxx @@ -0,0 +1,126 @@ +#ifndef TMEMORYMANAGER_HXX_INCLUDED +#define TMEMORYMANAGER_HXX_INCLUDED + +#if !defined(_MSC_VER) && !defined(__APPLE__) +//#if __has_attribute(regparm) +#define PASCAL_CALLING_CONVENTION __attribute__((regparm(3))) +//#endif +#endif + +#if defined(PASCAL_CALLING_CONVENTION) +typedef struct +{ + std::uint32_t TotalAddressSpace; + std::uint32_t TotalUncommited; + std::uint32_t TotalCommited; + std::uint32_t TotalAllocated; + std::uint32_t TotalFree; + std::uint32_t FreeSmall; + std::uint32_t FreeBig; + std::uint32_t Unused; + std::uint32_t Overhead; + std::uint32_t HeapErrorCode; +} THeapStatus; + +typedef struct +{ + std::uintptr_t MaxHeapSize; + std::uintptr_t MaxHeapUsed; + std::uintptr_t CurrHeapSize; + std::uintptr_t CurrHeapUsed; + std::uintptr_t CurrHeapFree; +} TFPCHeapStatus; + +typedef struct +{ + bool NeedLock; + void* (*GetMem)(std::intptr_t size) PASCAL_CALLING_CONVENTION; + std::intptr_t (*FreeMem)(void* &ptr) PASCAL_CALLING_CONVENTION; + std::intptr_t (*FreeMemSize)(void* &ptr, std::intptr_t size) PASCAL_CALLING_CONVENTION; + void* (*AllocMem)(std::intptr_t size) PASCAL_CALLING_CONVENTION; + void* (*ReAllocMem)(void* &ptr, std::intptr_t size) PASCAL_CALLING_CONVENTION; + std::intptr_t (*MemSize)(void* ptr) PASCAL_CALLING_CONVENTION; + + void (*InitThread)() PASCAL_CALLING_CONVENTION; + void (*DoneThread)() PASCAL_CALLING_CONVENTION; + void (*RelocateHeap)() PASCAL_CALLING_CONVENTION; + THeapStatus (*GetHeapStatus)() PASCAL_CALLING_CONVENTION; + TFPCHeapStatus (*GetFPCHeapStatus)() PASCAL_CALLING_CONVENTION; +} TMemoryManager; + +typedef struct +{ + bool NeedLock; + void* (*GetMem)(std::intptr_t size) PASCAL_CALLING_CONVENTION; + std::intptr_t (*FreeMem)(void* &ptr) PASCAL_CALLING_CONVENTION; + std::intptr_t (*FreeMemSize)(void* &ptr, std::intptr_t size) PASCAL_CALLING_CONVENTION; + void* (*AllocMem)(std::intptr_t size) PASCAL_CALLING_CONVENTION; + void* (*ReAllocMem)(void* &ptr, std::intptr_t size) PASCAL_CALLING_CONVENTION; + std::intptr_t (*MemSize)(void* ptr) PASCAL_CALLING_CONVENTION; + + void (*InitThread)() PASCAL_CALLING_CONVENTION; + void (*DoneThread)() PASCAL_CALLING_CONVENTION; + void (*RelocateHeap)() PASCAL_CALLING_CONVENTION; + THeapStatus (*GetHeapStatus)() PASCAL_CALLING_CONVENTION; + TFPCHeapStatus (*GetFPCHeapStatus)() PASCAL_CALLING_CONVENTION; +} __attribute__((__packed__)) TCMemoryManager; +#endif + +#if defined(_MSC_VER) +#define STRUCT_PACK( __Declaration__ ) __pragma( pack(push, 1) ) __Declaration__ __pragma( pack(pop)) +#else +#define STRUCT_PACK( __Declaration__ ) __Declaration__ __attribute__((__packed__)) +#endif + +STRUCT_PACK(typedef struct +{ + void (*Sync)(void(*synchronize_method)(void*), void* data); +}) TSimbaMethods; + +STRUCT_PACK(typedef struct +{ + void* (*GetMem)(std::size_t size); + void (*FreeMem)(void* ptr); +}) TSimbaMemoryAllocators; + +STRUCT_PACK(typedef struct +{ + std::int32_t SimbaMinor; + std::int32_t SimbaMajor; + const char* FileName; + void* Compiler; +}) TSimbaInfomation; + +STRUCT_PACK(typedef struct +{ + void (*RunOnMainThread)(void(*TMainThreadMethod)(void*), void* data); + void* (*GetMem)(std::size_t size); + void (*FreeMem)(void* ptr); + void* (*AllocMem)(std::size_t size); + void* (*ReAllocMem)(void** ptr, std::size_t size); + std::size_t (*MemSize)(void* ptr); + + void(*RaiseException)(const char* message); + + void* (*GetTypeInfo)(void* Compiler, const char* Type); + void* (*GetTypeInfoSize)(void* TypeInfo); + std::size_t (*GetTypeInfoFieldOffset)(void* TypeInfo, const char* FieldName); + + void* (*AllocateRawArray)(std::size_t element_size, std::size_t length); + void (*ReAllocateRawArray)(void** array, std::size_t element_size, std::size_t new_length); + + void* (*AllocateArray)(void* TypeInfo, std::size_t length); + void* (*AllocateString)(const char* data); + void* (*AllocateUnicodeString)(const wchar_t* data); + + void (*SetArrayLength)(void* TypeInfo, void** var, std::size_t new_len); + std::size_t (*GetArrayLength)(void* array); + + void* (*ExternalImage_Create)(bool AutoResize); + void (*ExternalImage_SetMemory)(void* img, void* bgra_data, std::int32_t width, std::int32_t height); + void (*ExternalImage_Resize)(void* img, std::int32_t new_width, std::int32_t new_height); + void (*ExternalImage_SetUserData)(void* img, void* userdata); + void* (*ExternalImage_GetUserData)(void* img); +}) TSimbaMethodsExtended; + +#endif // TMEMORYMANAGER_HXX_INCLUDED diff --git a/third-party/RemoteInput/RemoteInput/Random.cxx b/third-party/RemoteInput/RemoteInput/Random.cxx new file mode 100644 index 0000000..5065f03 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Random.cxx @@ -0,0 +1,33 @@ +// +// Random.cxx +// RemoteInput +// +// Created by Brandon on 2019-12-29. +// Copyright © 2019 XIO. All rights reserved. +// + +#include "Random.hxx" + +std::shared_ptr Random::instance() noexcept +{ + thread_local std::shared_ptr instance{new Random()}; + return instance; +} + +std::int64_t Random::generate_random_int(std::int64_t min, std::int64_t max) noexcept +{ + thread_local std::uniform_int_distribution dist{min, max}; + return dist(rng, decltype(dist)::param_type(min, max)); +} + +float Random::generate_random_float(float min, float max) noexcept +{ + thread_local std::uniform_real_distribution<> dist{min, std::nextafter(max, std::numeric_limits::max())}; + return dist(rng, decltype(dist)::param_type(min, std::nextafter(max, std::numeric_limits::max()))); +} + +double Random::generate_random_double(double min, double max) noexcept +{ + thread_local std::uniform_real_distribution<> dist{min, std::nextafter(max, std::numeric_limits::max())}; + return dist(rng, decltype(dist)::param_type(min, std::nextafter(max, std::numeric_limits::max()))); +} diff --git a/third-party/RemoteInput/RemoteInput/Random.hxx b/third-party/RemoteInput/RemoteInput/Random.hxx new file mode 100644 index 0000000..ec43ef1 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Random.hxx @@ -0,0 +1,38 @@ +// +// Random.hxx +// RemoteInput +// +// Created by Brandon on 2019-12-29. +// Copyright © 2019 XIO. All rights reserved. +// + +#ifndef RANDOM_HXX_INCLUDED +#define RANDOM_HXX_INCLUDED + +#include +#include +#include + +class Random +{ +private: + Random() {} + + #if defined(__x86_64__) + std::mt19937_64 rng{std::random_device()()}; + #else + std::mt19937 rng{std::random_device()()}; + #endif + +public: + Random(Random const&) = delete; + Random& operator = (Random const&) = delete; + + static std::shared_ptr instance() noexcept; + + std::int64_t generate_random_int(std::int64_t min, std::int64_t max) noexcept; + float generate_random_float(float min, float max) noexcept; + double generate_random_double(double min, double max) noexcept; +}; + +#endif /* RANDOM_HXX_INCLUDED */ diff --git a/third-party/RemoteInput/RemoteInput/Reflection.cxx b/third-party/RemoteInput/RemoteInput/Reflection.cxx new file mode 100644 index 0000000..2736ce2 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Reflection.cxx @@ -0,0 +1,370 @@ +#include "Reflection.hxx" +#include + +Reflection::Reflection() noexcept : env(nullptr), frame(nullptr), applet(nullptr), classLoader(nullptr), cache(nullptr) +{ +} + +Reflection::Reflection(Reflection&& other) noexcept : env(other.env), frame(other.frame), applet(other.applet), classLoader(other.classLoader), cache(other.cache) +{ + other.env = nullptr; + other.frame = nullptr; + other.applet = nullptr; + other.classLoader = nullptr; + other.cache = nullptr; +} + +Reflection::~Reflection() noexcept +{ + ClearCache(); + + /*if (env) + { + env->DeleteGlobalRef(classLoader); + env->DeleteGlobalRef(applet); + env->DeleteGlobalRef(frame); + this->Detach(); + }*/ +} + +Reflection& Reflection::operator = (Reflection&& other) noexcept +{ + std::swap(env, other.env); + std::swap(frame, other.frame); + std::swap(applet, other.applet); + std::swap(classLoader, other.classLoader); + return *this; +} + +std::unique_ptr Reflection::Create(jobject awtFrame) noexcept +{ + using Result = std::unique_ptr::type, std::function>; + auto IsDecendentOf = [](JNIEnv* env, jobject object, const char* className) -> bool { + auto cls = make_safe_local(env, env->FindClass(className)); + return env->IsInstanceOf(object, cls.get()); + }; + + std::function findApplet = [&](JNIEnv* env, jobject component) -> Result { + if (component && IsDecendentOf(env, component, "java/awt/Container")) + { + auto cls = make_safe_local(env, env->GetObjectClass(component)); + jmethodID mid = env->GetMethodID(cls.get(), "getComponents", "()[Ljava/awt/Component;"); + + if (mid) + { + auto components = make_safe_local(env, env->CallObjectMethod(component, mid)); + jint len = env->GetArrayLength(components.get()); + + for (jint i = 0; i < len; ++i) + { + //Some java.awt.Panel. + auto component = make_safe_local(env, env->GetObjectArrayElement(components.get(), i)); + auto applet_class = make_safe_local(env, env->FindClass("java/applet/Applet")); + if (env->IsInstanceOf(component.get(), applet_class.get())) + { + return component; + } + + auto result = findApplet(env, component.get()); + if (result) + { + return result; + } + } + } + } + + return {}; + }; + + std::function findCanvas = [&](JNIEnv* env, jobject component) -> Result { + if (component && IsDecendentOf(env, component, "java/awt/Container")) + { + auto cls = make_safe_local(env, env->GetObjectClass(component)); + jmethodID mid = env->GetMethodID(cls.get(), "getComponents", "()[Ljava/awt/Component;"); + + if (mid) + { + auto components = make_safe_local(env, env->CallObjectMethod(component, mid)); + jint len = env->GetArrayLength(components.get()); + + for (jint i = 0; i < len; ++i) + { + //Some java.awt.Panel. + auto component = make_safe_local(env, env->GetObjectArrayElement(components.get(), i)); + auto canvas_class = make_safe_local(env, env->FindClass("java/awt/Canvas")); + if (env->IsInstanceOf(component.get(), canvas_class.get())) + { + return component; + } + + auto result = findCanvas(env, component.get()); + if (result) + { + return result; + } + } + } + } + + return {}; + }; + + auto getParent = [&](JNIEnv* env, jobject component) -> Result { + if (component) + { + auto cls = make_safe_local(env, env->GetObjectClass(component)); + if (cls) + { + jmethodID mid = env->GetMethodID(cls.get(), "getParent", "()Ljava/awt/Container;"); + if (mid) + { + return make_safe_local(env, env->CallObjectMethod(component, mid)); + } + } + } + return {}; + }; + + //Attach Current Thread. + auto reflection = std::unique_ptr(new Reflection()); + if (!reflection->Attach()) + { + return nullptr; + } + + JNIEnv* env = reflection->getEnv(); + if (!IsDecendentOf(env, awtFrame, "java/awt/Frame")) + { + return nullptr; + } + + //Find Canvas Class. + auto applet = findApplet(env, awtFrame); + if (!applet) + { + applet = getParent(env, findCanvas(env, awtFrame).get()); + } + + if (applet) + { + reflection->frame = env->NewGlobalRef(awtFrame); + env->DeleteLocalRef(std::exchange(reflection->applet, env->NewGlobalRef(applet.release()))); + + auto cls = make_safe_local(env, env->GetObjectClass(reflection->applet)); + jmethodID mid = env->GetMethodID(cls.get(), "getClass", "()Ljava/lang/Class;"); + auto clsObj = make_safe_local(env, env->CallObjectMethod(reflection->applet, mid)); + cls.reset(env->GetObjectClass(clsObj.get())); + + //Get Canvas's ClassLoader. + mid = env->GetMethodID(cls.get(), "getClassLoader", "()Ljava/lang/ClassLoader;"); + reflection->classLoader = env->NewGlobalRef(make_safe_local(env, env->CallObjectMethod(clsObj.get(), mid)).get()); + reflection->cache = std::make_shared(env, reflection->classLoader); + return reflection; + } + return nullptr; +} + +std::unique_ptr Reflection::Clone() noexcept +{ + JNIEnv* new_env = nullptr; + if (JVM(env).AttachCurrentThread(&new_env) == JNI_OK) + { + auto reflection = std::unique_ptr(new Reflection()); + reflection->env = new_env; + reflection->frame = new_env->NewGlobalRef(frame); + reflection->applet = new_env->NewGlobalRef(applet); + reflection->classLoader = new_env->NewGlobalRef(classLoader); + reflection->cache = cache; + return reflection; + } + return {}; +} + +bool Reflection::Attach() noexcept +{ + return JVM().AttachCurrentThread(&env) == JNI_OK; +} + +bool Reflection::Detach() noexcept +{ + return JVM(env).DetachCurrentThread() == JNI_OK; +} + +void Reflection::PrintClasses() const noexcept +{ + if (this->classLoader) + { + auto clsLoaderClass = make_safe_local(env, env->GetObjectClass(classLoader)); + jfieldID field = env->GetFieldID(clsLoaderClass.get(), "classes", "Ljava/util/Vector;"); + auto classes = make_safe_local(env, env->GetObjectField(classLoader, field)); + jmethodID toArray = env->GetMethodID(make_safe_local(env, env->GetObjectClass(classes.get())).get(), "toArray", "()[Ljava/lang/Object;"); + auto clses = make_safe_local(env, env->CallObjectMethod(classes.get(), toArray)); + + printf("LOADED CLASSES:\n"); + for (int i = 0; i < env->GetArrayLength(clses.get()); ++i) + { + auto clsObj = make_safe_local(env, env->GetObjectArrayElement(clses.get(), i)); + std::string name = this->GetClassName(clsObj.get()); + printf("%s\n", name.c_str()); + } + + printf("FINISHED\n"); + fflush(stdout); + } +} + +std::string Reflection::GetClassName(jobject object) const noexcept +{ + auto getClassName = [&](jobject object) -> std::string { + auto cls = make_safe_local(env, env->GetObjectClass(object)); + jmethodID mid = env->GetMethodID(cls.get(), "getName", "()Ljava/lang/String;"); + auto strObj = make_safe_local(env, env->CallObjectMethod(object, mid)); + + if (strObj) + { + const char *str = env->GetStringUTFChars(strObj.get(), nullptr); + std::string class_name = str; + env->ReleaseStringUTFChars(strObj.get(), str); + return class_name; + } + return std::string(); + }; + return getClassName(object); +} + +std::string Reflection::GetClassType(jobject object) const noexcept +{ + auto getClassType = [&](jobject component) -> std::string { + auto cls = make_safe_local(env, env->GetObjectClass(component)); + jmethodID mid = env->GetMethodID(cls.get(), "getClass", "()Ljava/lang/Class;"); + auto clsObj = make_safe_local(env, env->CallObjectMethod(component, mid)); + + cls.reset(env->GetObjectClass(clsObj.get())); + mid = env->GetMethodID(cls.get(), "getName", "()Ljava/lang/String;"); + auto strObj = make_safe_local(env, env->CallObjectMethod(clsObj.get(), mid)); + + if (strObj) + { + const char *str = env->GetStringUTFChars(strObj.get(), nullptr); + std::string class_name = str; + env->ReleaseStringUTFChars(strObj.get(), str); + return class_name; + } + return std::string(); + }; + + return getClassType(object); +} + +bool Reflection::IsDecendentOf(jobject object, const char* className) const noexcept +{ + auto cls = make_safe_local(env, env->FindClass(className)); + return env->IsInstanceOf(object, cls.get()); +} + +jclass Reflection::LoadClass(std::string_view name) noexcept +{ + return cache->GetClass(env, name); +} + +void Reflection::ClearCache() noexcept +{ + if (cache.use_count() == 1) + { + cache->clear(); + } +} + +jfieldID Reflection::GetFieldID(jclass cls, std::string_view name, std::string_view desc, bool is_static) noexcept +{ + return cache->GetFieldID(env, cls, name, desc, is_static); +} + +jlong Reflection::getFrameHandle() const noexcept +{ + return getNativeHandle(frame); +} + +jobject Reflection::getApplet() const noexcept +{ + return this->applet; +} + +JNIEnv* Reflection::getEnv() const noexcept +{ + return this->env; +} + +jlong Reflection::getNativeHandle(jobject component) const noexcept +{ + auto get_peer = [](JNIEnv* env, jclass frame_cls, jobject frame) -> jobject { + jmethodID getPeerMethodID = env->GetMethodID(frame_cls, "getPeer", "()Ljava/awt/peer/ComponentPeer;"); + if (getPeerMethodID) + { + // Java 8 + return env->CallObjectMethod(frame, getPeerMethodID); + } + else + { + // Java 9+ + auto component_cls = make_safe_local(env, env->FindClass("java/awt/Component")); + if (!component_cls) + { + return nullptr; + } + + jfieldID peer_field = env->GetFieldID(component_cls.get(), "peer", "Ljava/awt/peer/ComponentPeer;"); + if (!peer_field) + { + return nullptr; + } + + return env->GetObjectField(frame, peer_field); + } + }; + + auto component_cls = make_safe_local(env, env->GetObjectClass(component)); + if (!component_cls) + { + return 0; + } + + auto peer = make_safe_local(env, get_peer(env, component_cls.get(), component)); + if (!peer) + { + return 0; + } + + auto component_peer_cls = make_safe_local(env, env->GetObjectClass(peer.get())); + if (!component_peer_cls) + { + return 0; + } + + #if defined(_WIN32) || defined(_WIN64) + jmethodID methodID = env->GetMethodID(component_peer_cls.get(), "getHWnd", "()J"); + if (!methodID) + { + return 0; + } + return env->CallLongMethod(peer.get(), methodID); + #elif defined(__linux__) + jmethodID methodID = env->GetMethodID(component_peer_cls.get(), "getWindow", "()J"); + if (!methodID) + { + return 0; + } + return env->CallLongMethod(peer.get(), methodID); + #elif defined(__APPLE__) + jmethodID methodID = env->GetMethodID(component_peer_cls.get(), "getAWTHandle", "()J"); + if (!methodID) + { + return 0; + } + return env->CallLongMethod(peer.get(), methodID); + #else + #error "Unsupported platform: Cannot retrieve native window handle" + return 0; + #endif +} \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Reflection.hxx b/third-party/RemoteInput/RemoteInput/Reflection.hxx new file mode 100644 index 0000000..3ac8158 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Reflection.hxx @@ -0,0 +1,285 @@ +#ifndef REFLECTION_HXX_INCLUDED +#define REFLECTION_HXX_INCLUDED + +#include +#include +#include +#include +#include +#include +#include "JNI_Common.hxx" +#include "JVM.hxx" +#include "ReflectionHook.hxx" +#include "JVMCache.hxx" + +enum class ReflectionType: std::uint8_t +{ + CHAR, + BYTE, + BOOL, + SHORT, + INT, + LONG, + FLOAT, + DOUBLE, + STRING, + OBJECT, + ARRAY +}; + +class Reflection final +{ +private: +#define HANDLE_JAVA_CLASS_NOT_FOUND(RESULT) \ + if (!cls)\ + {\ + fprintf(stderr, "No Such Class %s Field: %s.%s -> %s\n", object ? "Instance" : "Static", hook.cls.c_str(), hook.field.c_str(), hook.desc.c_str());\ + env->ExceptionClear();\ + return RESULT;\ + } + +#define HANDLE_JAVA_FIELD_NOT_FOUND(RESULT) \ + if (!field)\ + {\ + fprintf(stderr, "No Such %s Field: %s.%s -> %s\n", object ? "Instance" : "Static", hook.cls.c_str(), hook.field.c_str(), hook.desc.c_str());\ + env->ExceptionClear();\ + return RESULT;\ + } + + template + static auto make_safe_local(JNIEnv* env, U object) noexcept + { + auto deleter = [env](T ptr) { + if (env && ptr) + { + env->DeleteLocalRef(static_cast(ptr)); + } + }; + + return std::unique_ptr::type, decltype(deleter)>{static_cast(object), deleter}; + } + + template + T promote_to_global(U object) const noexcept + { + env->DeleteLocalRef(std::exchange(object, env->NewGlobalRef(static_cast(object)))); + return static_cast(object); + } + + JNIEnv* env; + jobject frame; + jobject applet; + jobject classLoader; + std::shared_ptr cache; + + void PrintClasses() const noexcept; + jfieldID GetFieldID(jclass cls, std::string_view name, std::string_view desc, bool is_static) noexcept; + + Reflection() noexcept; + + bool Detach() noexcept; + +public: + Reflection(Reflection&& other) noexcept; + Reflection(const Reflection& other) = delete; + ~Reflection() noexcept; + + Reflection& operator = (const Reflection& other) = delete; + Reflection& operator = (Reflection&& other) noexcept; + + static std::unique_ptr Create(jobject awtFrame) noexcept; + std::unique_ptr Clone() noexcept; + bool Attach() noexcept; + + std::string GetClassName(jobject object) const noexcept; + std::string GetClassType(jobject object) const noexcept; + bool IsDecendentOf(jobject object, const char* className) const noexcept; + + jclass LoadClass(std::string_view name) noexcept; + void ClearCache() noexcept; + + + jlong getFrameHandle() const noexcept; + jobject getApplet() const noexcept; + JNIEnv* getEnv() const noexcept; + + jlong getNativeHandle(jobject component) const noexcept; + + template + typename std::enable_if::type>::value, T>::type + getField(jstring object) noexcept; + + template + typename std::enable_if::type>::value, T>::type + getField(jobject object, const ReflectionHook &hook) noexcept; + + template + typename std::enable_if::type>::value, T>::type + getField(jobject object, const ReflectionHook &hook) noexcept; + + template + typename std::enable_if::type>::value, T>::type + getPrimitive(jobject object, const ReflectionHook &hook) noexcept; + + template + typename std::enable_if::type>::value, T>::type + getPrimitive(jobject object, const ReflectionHook &hook) noexcept; + + template + typename std::enable_if::type>::value, T>::type + getPrimitive(jobject object, const ReflectionHook &hook) noexcept; + + template + typename std::enable_if::type>::value, T>::type + getPrimitive(jobject object, const ReflectionHook &hook) noexcept; + + template + typename std::enable_if::type>::value, T>::type + getPrimitive(jobject object, const ReflectionHook &hook) noexcept; + + template + typename std::enable_if::type>::value, T>::type + getPrimitive(jobject object, const ReflectionHook &hook) noexcept; + + template + typename std::enable_if::type>::value, T>::type + getPrimitive(jobject object, const ReflectionHook &hook) noexcept; + + template + typename std::enable_if::type>::value, T>::type + getPrimitive(jobject object, const ReflectionHook &hook) noexcept; +}; + + +template +typename std::enable_if::type>::value, T>::type +Reflection::getField(jstring string) noexcept +{ + if (string) + { + if (jsize length = env->GetStringUTFLength(string); length > 0) + { + if (const char* chars = env->GetStringUTFChars(string, nullptr)) + { + std::string result = std::string(chars, length); + env->ReleaseStringUTFChars(string, chars); + return result; + } + } + } + return std::string(); +} + +template +typename std::enable_if::type>::value, T>::type +Reflection::getField(jobject object, const ReflectionHook &hook) noexcept +{ + jclass cls = LoadClass(hook.cls); + HANDLE_JAVA_CLASS_NOT_FOUND(std::string()); + jfieldID field = GetFieldID(cls, hook.field, hook.desc, !object); + HANDLE_JAVA_FIELD_NOT_FOUND(std::string()); + auto string = make_safe_local(env, object ? env->GetObjectField(object, field) : env->GetStaticObjectField(cls, field)); + return getField(string.get()); +} + + +template +typename std::enable_if::type>::value, T>::type +Reflection::getField(jobject object, const ReflectionHook &hook) noexcept +{ + jclass cls = LoadClass(hook.cls); + HANDLE_JAVA_CLASS_NOT_FOUND(nullptr); + jfieldID field = GetFieldID(cls, hook.field, hook.desc, !object); + HANDLE_JAVA_FIELD_NOT_FOUND(nullptr); + return promote_to_global(object ? env->GetObjectField(object, field) : env->GetStaticObjectField(cls, field)); +} + +template +typename std::enable_if::type>::value, T>::type +Reflection::getPrimitive(jobject object, const ReflectionHook &hook) noexcept +{ + jclass cls = LoadClass(hook.cls); + HANDLE_JAVA_CLASS_NOT_FOUND(-1); + jfieldID field = GetFieldID(cls, hook.field, hook.desc, !object); + HANDLE_JAVA_FIELD_NOT_FOUND(-1); + return object ? env->GetIntField(object, field) : env->GetStaticIntField(cls, field); +} + +template +typename std::enable_if::type>::value, T>::type +Reflection::getPrimitive(jobject object, const ReflectionHook &hook) noexcept +{ + jclass cls = LoadClass(hook.cls); + HANDLE_JAVA_CLASS_NOT_FOUND(-1); + jfieldID field = GetFieldID(cls, hook.field, hook.desc, !object); + HANDLE_JAVA_FIELD_NOT_FOUND(-1); + return object ? env->GetLongField(object, field) : env->GetStaticLongField(cls, field); +} + +template +typename std::enable_if::type>::value, T>::type +Reflection::getPrimitive(jobject object, const ReflectionHook &hook) noexcept +{ + jclass cls = this->LoadClass(hook.cls); + HANDLE_JAVA_CLASS_NOT_FOUND(false); + jfieldID field = GetFieldID(cls, hook.field, hook.desc, !object); + HANDLE_JAVA_FIELD_NOT_FOUND(false); + return object ? env->GetBooleanField(object, field) : env->GetStaticBooleanField(cls, field); +} + +template +typename std::enable_if::type>::value, T>::type +Reflection::getPrimitive(jobject object, const ReflectionHook &hook) noexcept +{ + jclass cls = LoadClass(hook.cls); + HANDLE_JAVA_CLASS_NOT_FOUND(0); + jfieldID field = GetFieldID(cls, hook.field, hook.desc, !object); + HANDLE_JAVA_FIELD_NOT_FOUND(0); + return object ? env->GetByteField(object, field) : env->GetStaticByteField(cls, field); +} + +template +typename std::enable_if::type>::value, T>::type +Reflection::getPrimitive(jobject object, const ReflectionHook &hook) noexcept +{ + jclass cls = LoadClass(hook.cls); + HANDLE_JAVA_CLASS_NOT_FOUND('\0'); + jfieldID field = GetFieldID(cls, hook.field, hook.desc, !object); + HANDLE_JAVA_FIELD_NOT_FOUND('\0'); + return object ? env->GetCharField(object, field) : env->GetStaticCharField(cls, field); +} + +template +typename std::enable_if::type>::value, T>::type +Reflection::getPrimitive(jobject object, const ReflectionHook &hook) noexcept +{ + jclass cls = LoadClass(hook.cls); + HANDLE_JAVA_CLASS_NOT_FOUND(-1); + jfieldID field = GetFieldID(cls, hook.field, hook.desc, !object); + HANDLE_JAVA_FIELD_NOT_FOUND(-1); + return object ? env->GetShortField(object, field) : env->GetStaticShortField(cls, field); +} + +template +typename std::enable_if::type>::value, T>::type +Reflection::getPrimitive(jobject object, const ReflectionHook &hook) noexcept +{ + jclass cls = LoadClass(hook.cls); + HANDLE_JAVA_CLASS_NOT_FOUND(-1.0); + jfieldID field = GetFieldID(cls, hook.field, hook.desc, !object); + HANDLE_JAVA_FIELD_NOT_FOUND(-1.0); + return object ? env->GetFloatField(object, field) : env->GetStaticFloatField(cls, field); +} + +template +typename std::enable_if::type>::value, T>::type +Reflection::getPrimitive(jobject object, const ReflectionHook &hook) noexcept +{ + jclass cls = LoadClass(hook.cls); + HANDLE_JAVA_CLASS_NOT_FOUND(-1.0); + jfieldID field = GetFieldID(cls, hook.field, hook.desc, !object); + HANDLE_JAVA_FIELD_NOT_FOUND(-1.0); + return object ? env->GetDoubleField(object, field) : env->GetStaticDoubleField(cls, field); +} + +#endif // REFLECTION_HXX_INCLUDED \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/ReflectionHook.cxx b/third-party/RemoteInput/RemoteInput/ReflectionHook.cxx new file mode 100644 index 0000000..ec6e396 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/ReflectionHook.cxx @@ -0,0 +1,22 @@ +// +// ReflectionHook.cxx +// RemoteInput +// +// Created by Brandon on 2019-12-01. +// Copyright © 2019 XIO. All rights reserved. +// + +#include "ReflectionHook.hxx" + +ReflectionHook::ReflectionHook(jobject object, std::string cls, std::string field, std::string desc) noexcept : object(object), cls(cls), field(field), desc(desc) +{} + +Stream& operator << (Stream& stream, const ReflectionHook &hook) +{ + return stream << hook.object << hook.cls << hook.field << hook.desc; +} + +Stream& operator >> (Stream& stream, ReflectionHook &hook) +{ + return stream >> hook.object >> hook.cls >> hook.field >> hook.desc; +} \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/ReflectionHook.hxx b/third-party/RemoteInput/RemoteInput/ReflectionHook.hxx new file mode 100644 index 0000000..68a2fb1 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/ReflectionHook.hxx @@ -0,0 +1,45 @@ +// +// ReflectionHook.hxx +// RemoteInput +// +// Created by Brandon on 2019-12-01. +// Copyright © 2019 XIO. All rights reserved. +// + +#ifndef ReflectionHook_HXX_INCLUDED +#define ReflectionHook_HXX_INCLUDED + +#include +#include + +#include "JNI_Common.hxx" +#include "Stream.hxx" +#include "TypeTraits.hxx" + +struct ReflectionHook +{ + jobject object; + std::string cls; + std::string field; + std::string desc; + + ReflectionHook(jobject object = nullptr, std::string cls = "", std::string field = "", std::string desc = "") noexcept; +}; + +Stream& operator << (Stream& stream, const ReflectionHook &hook); +Stream& operator >> (Stream& stream, ReflectionHook &hook); + +template<> +struct std::hash +{ + std::size_t operator()(const ReflectionHook& hook) const noexcept + { + std::size_t hash = 0; + hash_combine(hash, hook.cls); + hash_combine(hash, hook.field); + hash_combine(hash, hook.desc); + return hash; + } +}; + +#endif /* ReflectionHook_HXX_INCLUDED */ diff --git a/third-party/RemoteInput/RemoteInput/RemoteInput.def b/third-party/RemoteInput/RemoteInput/RemoteInput.def new file mode 100644 index 0000000..36c3d29 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/RemoteInput.def @@ -0,0 +1,28 @@ +EXPORTS + EIOS_GetImageBuffer=EIOS_GetImageBuffer@4 + EIOS_GetDebugImageBuffer=EIOS_GetDebugImageBuffer@4 + EIOS_GetMousePosition=EIOS_GetMousePosition@12 + EIOS_GetTargetDimensions=EIOS_GetTargetDimensions@12 + EIOS_HoldKey=EIOS_HoldKey@8 + EIOS_HoldMouse=EIOS_HoldMouse@16 + EIOS_IsKeyHeld=EIOS_IsKeyHeld@8 + EIOS_IsMouseHeld=EIOS_IsMouseHeld@8 + EIOS_MoveMouse=EIOS_MoveMouse@12 + EIOS_ReleaseKey=EIOS_ReleaseKey@8 + EIOS_ReleaseMouse=EIOS_ReleaseMouse@16 + EIOS_ReleaseTarget=EIOS_ReleaseTarget@4 + EIOS_RequestTarget=EIOS_RequestTarget@4 + EIOS_ScrollMouse=EIOS_ScrollMouse@16 + EIOS_SendString=EIOS_SendString@16 + EIOS_UpdateImageBuffer=EIOS_UpdateImageBuffer@4 + GetFunctionCount + GetFunctionInfo + GetPluginABIVersion + GetTypeCount + GetTypeInfo + OnAttach + OnDetach + SetPluginMemManager + SetPluginSimbaMethods + SetPluginSimbaMemoryAllocators + RegisterSimbaPlugin diff --git a/third-party/RemoteInput/RemoteInput/RemoteInput.h b/third-party/RemoteInput/RemoteInput/RemoteInput.h new file mode 100644 index 0000000..ba0670a --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/RemoteInput.h @@ -0,0 +1,21 @@ +// +// RemoteInput.h +// RemoteInput +// +// Created by Brandon on 2019-09-14. +// Copyright © 2019 XIO. All rights reserved. +// + +#if defined(__APPLE__) +#import + +//! Project version number for RemoteInput. +FOUNDATION_EXPORT double RemoteInputVersionNumber; + +//! Project version string for RemoteInput. +FOUNDATION_EXPORT const unsigned char RemoteInputVersionString[]; +#endif + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/CMakeLists.txt b/third-party/RemoteInput/RemoteInput/Thirdparty/CMakeLists.txt new file mode 100644 index 0000000..9bc92d3 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/CMakeLists.txt @@ -0,0 +1,175 @@ +cmake_minimum_required(VERSION 3.15) +project(THIRD_PARTY_LIBRARIES VERSION 1.0.0 DESCRIPTION "Third Party") + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) + +# ----------------------- ENABLE ASSEMBLY CODE ----------------------- +enable_language(ASM) + + + +# ----------------------- INCLUDE_DIRECTORIES ----------------------- +IF(WIN32) + set(INCLUDE_DIRECTORIES + min_hook/include + min_hook/src + min_hook/src/hde + kubo_injector/include + kubo_injector/src/windows) +ELSEIF(APPLE) + set(INCLUDE_DIRECTORIES + rd_route/include + mach_inject/include + kubo_injector/include + kubo_injector/src/macos) +ELSE() + set(INCLUDE_DIRECTORIES + linux_detours/include + kubo_injector/include + kubo_injector/src/linux) +ENDIF() + + +# ----------------------------- LINKER ----------------------------- +IF(WIN32) + set(LIBRARIES_LIST + kernel32 + dbghelp) +ELSEIF(APPLE) + set(LIBRARIES_LIST + dl + pthread) +ELSE() + set(LIBRARIES_LIST + dl + pthread) +ENDIF() + + +# ---------------------------- SOURCES ---------------------------- +IF(WIN32) + set(LIB_SRC_LIST + min_hook/include/MinHook.h + min_hook/src/hde/hde32.c + min_hook/src/hde/hde32.h + min_hook/src/hde/hde64.c + min_hook/src/hde/hde64.h + min_hook/src/hde/pstdint.h + min_hook/src/hde/table32.h + min_hook/src/hde/table64.h + min_hook/src/buffer.c + min_hook/src/buffer.h + min_hook/src/hook.c + min_hook/src/trampoline.c + min_hook/src/trampoline.h + kubo_injector/include/injector.h + kubo_injector/src/windows/injector.c) +ELSEIF(APPLE) + set(LIB_SRC_LIST + rd_route/include/rd_route.h + rd_route/src/rd_route.c + kubo_injector/include/injector.h + kubo_injector/src/macos/exc_handler.c + kubo_injector/src/macos/injector.c + kubo_injector/src/macos/injector_internal.h + kubo_injector/src/macos/mach.c + kubo_injector/src/macos/mach_exc.h + kubo_injector/src/macos/mach_excServer.c + kubo_injector/src/macos/ptrace.c + kubo_injector/src/macos/remote_call.c + kubo_injector/src/macos/util.c) +ELSE() + set(LIB_SRC_LIST + linux_detours/include/detours.h + #linux_detours/include/limits.h + linux_detours/include/plthook.h + linux_detours/include/types.h + linux_detours/src/barrier.cpp + linux_detours/src/detours.cpp + linux_detours/src/disasm.cpp + linux_detours/src/plthook_elf.cpp + linux_detours/src/trampoline_x86.cpp + linux_detours/src/trampoline_arm.cpp + kubo_injector/src/linux/shellcode.S + kubo_injector/src/linux/elf.c + kubo_injector/src/linux/injector.c + kubo_injector/src/linux/injector_internal.h + kubo_injector/src/linux/ptrace.c + kubo_injector/src/linux/remote_call.c + kubo_injector/src/linux/util.c) +ENDIF() + +set(SRC_LIST + ${LIB_SRC_LIST} + Hook.hxx + Hook.cxx + Injector.cxx + Injector.hxx) + + +# ---------------------------- COMPILE ---------------------------- +add_library(${PROJECT_NAME} OBJECT ${SRC_LIST}) +set_property(TARGET ${PROJECT_NAME} PROPERTY POSITION_INDEPENDENT_CODE ON) +target_include_directories(${PROJECT_NAME} PRIVATE ${INCLUDE_DIRECTORIES}) + +IF(WIN32) + #target_compile_definitions(${PROJECT_NAME} PRIVATE _AMD64_=1 _UNIX64=1) + + IF(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + target_compile_options(${PROJECT_NAME} PRIVATE + $<$:-Wno-multichar -Wno-null-conversion -g -O0 -fno-omit-frame-pointer> + $<$:-Wno-multichar -Wno-null-conversion -s -O0 -fno-omit-frame-pointer>) + target_link_options(${PROJECT_NAME} PRIVATE + $<$:-static -static-libgcc -static-libstdc++> + $<$:-s -static -static-libgcc -static-libstdc++>) + ELSEIF(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_compile_options(${PROJECT_NAME} PRIVATE + $<$:-Wno-multichar -Wno-null-conversion -g -O0 -fno-omit-frame-pointer> + $<$:-Wno-multichar -Wno-null-conversion -s -O0 -fno-omit-frame-pointer>) + target_link_options(${PROJECT_NAME} PRIVATE + $<$:-static -static-libgcc -static-libstdc++> + $<$:-s -static -static-libgcc -static-libstdc++>) + ELSEIF(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + set_property(TARGET ${PROJECT_NAME} PROPERTY + MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + + target_compile_options(${PROJECT_NAME} PRIVATE + $<$:/Zo -DWIN32_LEAN_AND_MEAN /MTd> + $<$:/Zo -DWIN32_LEAN_AND_MEAN /MT>) + target_link_options(${PROJECT_NAME} PRIVATE + $<$:> + $<$:>) + ENDIF() +ELSEIF(APPLE) + #target_compile_definitions(${PROJECT_NAME} PRIVATE _AMD64_=1 _UNIX64=1) + + target_compile_options(${PROJECT_NAME} PRIVATE + $<$:-Wno-multichar -Wno-null-conversion -g -O0 -fno-omit-frame-pointer -fvisibility=hidden> + $<$:-Wno-multichar -Wno-null-conversion -O0 -fno-omit-frame-pointer -fvisibility=hidden>) + target_link_options(${PROJECT_NAME} PRIVATE + $<$:-static> + $<$:-s -static>) +ELSE() + IF(CMAKE_SYSTEM_PROCESSOR MATCHES "amd64.*|x86_64.*|AMD64.*") + target_compile_definitions(${PROJECT_NAME} PRIVATE _AMD64_=1 _UNIX64=1) + ELSEIF(CMAKE_SYSTEM_PROCESSOR MATCHES "i686.*|i386.*|x86.*") + target_compile_definitions(${PROJECT_NAME} PRIVATE _X86_=1) + ELSEIF(CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64.*|AARCH64.*|arm64.*|ARM64.*)") + target_compile_definitions(${PROJECT_NAME} PRIVATE _ARM64_=1 _UNIX64=1) + ELSEIF(CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm.*|ARM.*)") + target_compile_definitions(${PROJECT_NAME} PRIVATE _ARM_=1 _ARM32_=1) + ELSE() + target_compile_definitions(${PROJECT_NAME} PRIVATE _AMD64_=1 _UNIX64=1) + ENDIF() + + target_compile_options(${PROJECT_NAME} PRIVATE + $<$:-Wno-multichar -Wno-null-conversion -g -O0 -fno-omit-frame-pointer -fvisibility=hidden> + $<$:-Wno-multichar -Wno-null-conversion -s -O0 -fno-omit-frame-pointer -fvisibility=hidden>) + target_link_options(${PROJECT_NAME} PRIVATE + $<$:-static-libgcc -static-libstdc++> + $<$:-s -static-libgcc -static-libstdc++>) +ENDIF() + +target_link_libraries(${PROJECT_NAME} ${LIBRARIES_LIST}) diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/Hook.cxx b/third-party/RemoteInput/RemoteInput/Thirdparty/Hook.cxx new file mode 100644 index 0000000..2a81118 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/Hook.cxx @@ -0,0 +1,207 @@ +// +// Created by brandon on 2020-04-06. +// + +#include "Hook.hxx" +#include +#include + +#if defined(_WIN32) || defined(_WIN64) +#include +#include +#elif defined(__APPLE__) +#include +#include +#include +#import +#include +#else +#include +#include +#endif + + +#if defined(_WIN32) || defined(_WIN64) +Hook::Hook(void* original, void* detour) : original(original), detour(detour), trampoline(nullptr), data(), valid(false) +{ + std::memset(&data[0], 0, sizeof(data)); + MH_STATUS status = MH_CreateHook(original, detour, &trampoline); + valid = status == MH_OK; +} + +Hook::Hook(Hook&& other) noexcept : original(other.original), detour(other.detour), trampoline(other.trampoline), data(), valid(other.valid) +{ + other.original = nullptr; + other.detour = nullptr; + other.trampoline = nullptr; + other.valid = false; + std::memcpy(&data[0], &other.data[0], sizeof(data)); + std::memset(&other.data[0], 0, sizeof(data)); +} + +Hook::~Hook() +{ + remove(); + MH_RemoveHook(original); +} + +void Hook::apply() +{ + if (valid) + { + MH_STATUS status = MH_EnableHook(original); + valid = status == MH_OK; + } +} + +void Hook::remove() +{ + if (valid) + { + MH_DisableHook(original); + } +} + +bool Hook::is_enabled() const +{ + return valid; +} + +#elif defined(__APPLE__) +Hook::Hook(void* original, void* detour) : original(original), detour(detour), trampoline(nullptr), data(), valid(false) +{ + std::memset(&data[0], 0, sizeof(data)); + + #if defined (__x86_64__) || defined(__arm64__) + mach_vm_size_t size_of_jump = (sizeof(uintptr_t) * 2); + #else + mach_vm_size_t size_of_jump = (sizeof(int) + 1); + #endif + + mach_msg_type_number_t amount_read = 0; + kern_return_t kr = mach_vm_protect(mach_task_self(), reinterpret_cast(original), size_of_jump, FALSE, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE | VM_PROT_COPY); + + valid = kr == KERN_SUCCESS; + if (valid) + { + kr = mach_vm_read(mach_task_self(), reinterpret_cast(original), size_of_jump, reinterpret_cast(&data[0]), &amount_read); + kr = mach_vm_protect(mach_task_self(), reinterpret_cast(original), size_of_jump, FALSE, VM_PROT_READ | VM_PROT_EXECUTE); + } +} + +Hook::Hook(Hook&& other) noexcept : original(other.original), detour(other.detour), trampoline(other.trampoline), data(), valid(other.valid) +{ + other.original = nullptr; + other.detour = nullptr; + other.trampoline = nullptr; + other.valid = false; + std::memcpy(&data[0], &other.data[0], sizeof(data)); + std::memset(&other.data[0], 0, sizeof(data)); +} + +Hook::~Hook() +{ + remove(); + trampoline = nullptr; +} + +void Hook::apply() +{ + if (valid) + { + rd_route(original, detour, &trampoline); + } +} + +void Hook::remove() +{ + if (valid) + { + #if defined (__x86_64__) + mach_msg_type_number_t size_of_jump = (sizeof(uintptr_t) * 2); + #else + mach_msg_type_number_t size_of_jump = (sizeof(int) + 1); + #endif + + kern_return_t kr = mach_vm_protect(mach_task_self(), reinterpret_cast(original), size_of_jump, FALSE, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE | VM_PROT_COPY); + if (kr == KERN_SUCCESS) + { + kr = mach_vm_write(mach_task_self(), reinterpret_cast(original), reinterpret_cast(&data[0]), size_of_jump); + kr = mach_vm_protect(mach_task_self(), reinterpret_cast(original), size_of_jump, FALSE, VM_PROT_READ | VM_PROT_EXECUTE); + } + + std::memset(&data[0], 0, sizeof(data)); + } +} + +bool Hook::is_enabled() const +{ + return valid; +} + +#else + +Hook::Hook(void *original, void *detour) : original(original), detour(detour), trampoline(new HOOK_TRACE_INFO()), data(), valid(false) +{ + std::memset(&data[0], 0, sizeof(data)); +} + +Hook::Hook(Hook&& other) noexcept : original(other.original), detour(other.detour), trampoline(other.trampoline), data(), valid(other.valid) +{ + other.original = nullptr; + other.detour = nullptr; + other.trampoline = nullptr; + other.valid = false; + std::memcpy(&data[0], &other.data[0], sizeof(data)); + std::memset(&other.data[0], 0, sizeof(data)); +} + +Hook::~Hook() +{ + remove(); + delete static_cast(trampoline); + trampoline = nullptr; +} + +void Hook::apply() +{ + LONG callback = 0; + ULONG acl_list = 0; + LONG result = DetourInstallHook(original, detour, &callback, reinterpret_cast(&data[0])); + + if (result == STATUS_SUCCESS) + { + result = DetourSetExclusiveACL(&acl_list, 1, reinterpret_cast(&data[0])); + } + + valid = result == STATUS_SUCCESS; +} + +void Hook::remove() +{ + if (valid) + { + LONG result = DetourUninstallHook(reinterpret_cast(trampoline)); + valid = result == STATUS_SUCCESS; + } +} + +bool Hook::is_enabled() const +{ + return valid; +} +#endif + +void Hook::setup() +{ + #if defined(_WIN32) || defined(_WIN64) + MH_Initialize(); + #endif +} + +void Hook::teardown() +{ + #if defined(_WIN32) || defined(_WIN64) + MH_Uninitialize(); + #endif +} \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/Hook.hxx b/third-party/RemoteInput/RemoteInput/Thirdparty/Hook.hxx new file mode 100644 index 0000000..be34933 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/Hook.hxx @@ -0,0 +1,54 @@ +// +// Created by brandon on 2020-04-06. +// + +#ifndef HOOK_HXX +#define HOOK_HXX + +#include +#include +#include + +class Hook +{ +private: + void* original; + void* detour; + void* trampoline; + bool valid; + std::uint8_t data[32]; + +public: + Hook(void* original, void* detour); + Hook(const Hook& other) = delete; + Hook(Hook&& other) noexcept; + ~Hook(); + + Hook& operator = (const Hook& other) = delete; + Hook& operator = (Hook&& other) = delete; + + void apply(); + void remove(); + + bool is_enabled() const; + + template + R call(Args... args); + + static void setup(); + static void teardown(); +}; + +template +R Hook::call( Args... args) +{ +#if defined(_WIN32) || defined(_WIN64) + return reinterpret_cast(trampoline ? trampoline : original)(std::forward(args)...); +#elif defined(__APPLE__) + return reinterpret_cast(trampoline ? trampoline : original)(std::forward(args)...); +#else + return reinterpret_cast(original)(std::forward(args)...); +#endif +} + +#endif //HOOK_HXX diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/Injector.cxx b/third-party/RemoteInput/RemoteInput/Thirdparty/Injector.cxx new file mode 100644 index 0000000..fc585ad --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/Injector.cxx @@ -0,0 +1,57 @@ +// +// Created by brandon on 04/11/24. +// + +#include "Injector.hxx" +#include "kubo_injector/include/injector.h" + +Injector::Injector(std::int32_t pid) : pid(pid), injector(nullptr), handle(nullptr) +{ + if (injector_attach(reinterpret_cast(&injector), pid) != 0) + { + injector = nullptr; + } +} + +Injector::~Injector() +{ + if (injector) + { + injector_detach(reinterpret_cast(injector)); + } + + handle = nullptr; + injector = nullptr; +} + +bool Injector::Inject(std::string module_path) noexcept +{ + if (injector && !handle) + { + return injector_inject(static_cast(injector), module_path.c_str(), &handle) == 0; + } + return false; +} + +bool Injector::UnInject() noexcept +{ + if (injector && handle) + { + if (injector_uninject(static_cast(injector), handle) == 0) + { + handle = nullptr; + return true; + } + } + return false; +} + +std::int32_t Injector::get_pid() const noexcept +{ + return pid; +} + +bool Injector::is_injected() const noexcept +{ + return injector && handle; +} \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/Injector.hxx b/third-party/RemoteInput/RemoteInput/Thirdparty/Injector.hxx new file mode 100644 index 0000000..b85282e --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/Injector.hxx @@ -0,0 +1,31 @@ +// +// Created by brandon on 04/11/24. +// + +#ifndef INJECTOR_HXX +#define INJECTOR_HXX + +#include +#include + +class Injector +{ +private: + std::int32_t pid; + void* injector; + void* handle; + +public: + Injector(std::int32_t pid); + ~Injector(); + + bool Inject(std::string module_path) noexcept; + bool UnInject() noexcept; + + std::int32_t get_pid() const noexcept; + bool is_injected() const noexcept; +}; + + + +#endif //INJECTOR_HXX diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/LICENSE_GPL.txt b/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/LICENSE_GPL.txt new file mode 100644 index 0000000..d8cf7d4 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/LICENSE_GPL.txt @@ -0,0 +1,280 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/LICENSE_LGPL.txt b/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/LICENSE_LGPL.txt new file mode 100644 index 0000000..e9ab0b3 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/LICENSE_LGPL.txt @@ -0,0 +1,458 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/README.md b/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/README.md new file mode 100644 index 0000000..3fd59c7 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/README.md @@ -0,0 +1,298 @@ +# Injector + +[![tests](https://github.com/kubo/injector/actions/workflows/test.yml/badge.svg)](https://github.com/kubo/injector/actions/workflows/test.yml) +[![Static Badge](https://img.shields.io/badge/docs-API_reference-blue)](http://www.jiubao.org/injector/injector_8h.html) + +**Library for injecting a shared library into a Linux, Windows and MacOS process** + +## Linux + +> **Warning** +> Don't use this in production environments. It may stop target processes forever. See [Caveats](#caveats). + +I was inspired by [`linux-inject`][] and the basic idea came from it. +However the way to call `__libc_dlopen_mode` in `libc.so.6` is +thoroughly different. + +* `linux-inject` writes about 80 bytes of code to the target process + on x86_64. This writes only 4 ~ 16 bytes. +* `linux-inject` writes code at the firstly found executable region + of memory, which may be referred by other threads. This writes it + at [the entry point of `libc.so.6`][libc_main], which will be referred by + nobody unless the libc itself is executed as a program. + +[libc_main]: https://sourceware.org/git/?p=glibc.git;a=blob;f=csu/version.c;h=8c0ed79c01223e1f12b54d19f90b5e5b7dd78d27;hb=c804cd1c00adde061ca51711f63068c103e94eef#l67 + +## Windows + +Windows version is also here. It uses well-known [`CreateRemoteThread+LoadLibrary`] +technique to load a DLL into another process with some improvements. + +1. It gets Win32 error messages when `LoadLibrary` fails by copying assembly + code into the target process. +2. It can inject a 32-bit dll into a 32-bit process from x64 processes + by checking the export entries in 32-bit kernel32.dll. + +**Note:** It may work on Windows on ARM though I have not tested it because +I have no ARM machines. Let me know if it really works. + +## MacOS +The injector connects to the target process using task_for_pid and creates a mach-thread. If dlopen is called in this thread, the target process will fail with an error, however, it is possible to create another thread using pthread_create_from_mach_thread function for Mac >= 10.12 or pthread_create otherwise. In the created thread, the code for loading the library is executed. The second thread is created when injector_inject is called and terminated when injector_detach is called. +# Compilation + +## Linux + +```shell +$ git clone https://github.com/kubo/injector.git +$ cd injector +$ make +``` + +The `make` command creates: + +| filename | - | +|---|---| +|`src/linux/libinjector.a` |a static library| +|`src/linux/libinjector.so` |a shared library| +|`cmd/injector` |a command line program linked with the static library| + +## Windows (MSVC) + +Open a Visual Studio command prompt and run the following commands: + +```shell +$ git clone https://github.com/kubo/injector.git # Or use any other tool +$ cd injector +$ nmake -f Makefile.win32 +``` + +The `nmake` command creates: + +| filename | - | +|---|---| +|`src/windows/injector-static.lib` |a static library (release build) +|`src/windows/injector.dll` |a shared library (release build) +|`src/windows/injector.lib` |an import library for `injector.dll` +|`src/windows/injectord-static.lib` |a static library (debug build) +|`src/windows/injectord.dll` |a shared library (debug build) +|`src/windows/injectord.lib` |an import library for `injectord.dll` +|`cmd/injector.exe` |a command line program linked the static library (release build)| + +## Windows (mingw-w64) + +On MSYS2: + +```shell +$ git clone https://github.com/kubo/injector.git +$ cd injector +$ CC=gcc make +``` + +Cross-compilation on Linux: + +```shell +$ git clone https://github.com/kubo/injector.git +$ cd injector +$ CC=x86_64-w64-mingw32-gcc OS=Windows_NT make +``` + +The environment variable `OS=Windows_NT` must be set on Linux. + +## MacOS + +```shell +$ git clone https://github.com/TheOiseth/injector.git +$ cd injector +$ make +``` + +The `make` command creates: + +| filename | - | +|---|---| +|`src/macos/libinjector.a` |a static library| +|`src/macos/libinjector.dylib` |a shared library| +|`cmd/injector` |a command line program linked with the static library| + +**Important:** in order for the injector process to connect to another process using task_for_pid, it is necessary to [`disable SIP`][] or sign the injector with a self-signed certificate with debugging permission, for this: +```shell +$ cd cmd/macos-sign +$ chmod +x genkey.sh +$ ./genkey.sh +$ chmod +x sign.sh +$ ./sign.sh +``` +If injector still does not work after signing, reboot the system. + +# Usage + +## C API + +```c +#include + +... + + injector_t *injector; + void *handle; + + /* attach to a process whose process id is 1234. */ + if (injector_attach(&injector, 1234) != 0) { + printf("ATTACH ERROR: %s\n", injector_error()); + return; + } + /* inject a shared library into the process. */ + if (injector_inject(injector, "/path/to/shared/library", NULL) != 0) { + printf("INJECT ERROR: %s\n", injector_error()); + } + /* inject another shared library. */ + if (injector_inject(injector, "/path/to/another/shared/library", &handle) != 0) { + printf("INJECT ERROR: %s\n", injector_error()); + } + +... + + /* uninject the second shared library. */ + if (injector_uninject(injector, handle) != 0) { + printf("UNINJECT ERROR: %s\n", injector_error()); + } + + /* cleanup */ + injector_detach(injector); +``` + +## Command line program + +See [`Usage` section and `Sample` section in linux-inject][`inject`] and substitute +`inject` with `injector` in the page. + +# Tested Architectures + +## Linux + +x86_64: + +injector process \ target process | x86_64 | i386 | x32(*1) +---|---|---|--- +**x86_64** | :smiley: success(*2) | :smiley: success(*3) | :smiley: success(*6) +**i386** | :skull: failure(*4) | :smiley: success(*3) | :skull: failure(*5) +**x32**(*1) | :skull: failure(*4) | :smiley: success(*6) | :skull: failure(*5) + +*1: [x32 ABI](https://en.wikipedia.org/wiki/X32_ABI) +*2: tested on github actions with both glibc and musl. +*3: tested on github actions with glibc. +*4: failure with `64-bit target process isn't supported by 32-bit process`. +*5: failure with `x32-ABI target process is supported only by x86_64`. +*6: tested on a local machine. `CONFIG_X86_X32` isn't enabled in github actions. + +ARM: + +injector process \ target process | arm64 | armhf | armel +---|---|---|--- +**arm64** | :smiley: success | :smiley: success | :smiley: success +**armhf** | :skull: failure(*1) | :smiley: success | :smiley: success +**armel** | :skull: failure(*1) | :smiley: success | :smiley: success + +*1: failure with `64-bit target process isn't supported by 32-bit process`. + +MIPS: + +injector process \ target process | mips64el | mipsel (n32) | mipsel (o32) +---|---|---|--- +**mips64el** | :smiley: success (*1) | :smiley: success (*1) | :smiley: success (*1) +**mipsel (n32)** | :skull: failure(*2) | :smiley: success (*1) | :smiley: success (*1) +**mipsel (o32)** | :skull: failure(*2) | :smiley: success (*1) | :smiley: success (*1) + +*1: tested on [debian 11 mips64el](https://www.debian.org/releases/bullseye/mips64el/ch02s01.en.html#idm271) on [QEMU](https://www.qemu.org/). +*2: failure with `64-bit target process isn't supported by 32-bit process`. + +PowerPC: + +* **ppc64le** (tested on [alpine 3.16.2 ppc64le](https://dl-cdn.alpinelinux.org/alpine/v3.16/releases/ppc64le/) on [QEMU](https://www.qemu.org/)) +* **powerpc (big endian)** (tested on [ubuntu 16.04 powerpc](https://old-releases.ubuntu.com/releases/xenial/) on [QEMU](https://www.qemu.org/) + +RISC-V: + +* **riscv64** (tested on [Ubuntu 22.04.1 riscv64 on QEMU](https://wiki.ubuntu.com/RISC-V#Booting_with_QEMU)) + +## Windows + +Windows x64: + +injector process \ target process | x64 | x86 +---|---|--- +**x64** | :smiley: success(*2) | :smiley: success(*2) +**x86** | :skull: failure(*1) | :smiley: success(*2) + +*1: failure with `x64 target process isn't supported by x86 process`. +*2: tested on github actions + +Windows 11 on Arm: + +injector process \ target process | arm64 | arm64ec | x64 | x86 | arm32 +---|---|---|---|---|--- +**arm64** | :smiley: success | :skull: failure | :skull: failure | :skull: failure | :smiley: success +**arm64ec** | :skull: failure | :smiley: success | :smiley: success | :skull: failure | :skull: failure +**x64** | :skull: failure | :smiley: success | :smiley: success | :skull: failure | :skull: failure +**x86** | :skull: failure | :skull: failure | :skull: failure | :smiley: success | :skull: failure +**arm32** | :skull: failure | :skull: failure | :skull: failure | :skull: failure | :smiley: success + +[Wine](https://www.winehq.org/) (on Linux x86_64): + +injector process \ target process | x64 | x86 +---|---|--- +**x64** | :smiley: success | :skull: failure +**x86** | :skull: failure | :smiley: success + +## MacOS + +injector process \ target process | x64 | arm64 +---|---|--- +**x64** | :smiley: success(*1) | :skull: failure(*2) +**arm64** | :skull: failure(*3) | :smiley: success + +*1: failure with `x86_64 target process isn't supported by x86_64 process on ARM64 machine`. Tested on github actions. +*2: failure with `arm64 target process isn't supported by x86_64 process.` +*3: failure with `x86_64 target process isn't supported by arm64 process.` + +# Caveats + +**The following restrictions are only on Linux.** + +Injector doesn't work where `ptrace()` is disallowed. + +* Non-children processes (See [Caveat about `ptrace()`][]) +* Docker containers on docker version < 19.03 or linux kernel version < 4.8. You need to pass [`--cap-add=SYS_PTRACE`](https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities) +to `docker run` to allow it in the environments. +* Linux inside of UserLAnd (Android App) (See [here](https://github.com/kubo/injector/issues/17#issuecomment-1113990177)) + +Injector calls functions inside of a target process interrupted by `ptrace()`. +If the target process is interrupted while holding a non-reentrant lock and +injector calls a function requiring the same lock, the process stops forever. +If the lock type is reentrant, the status guarded by the lock may become inconsistent. +As far as I checked, `dlopen()` internally calls `malloc()` requiring non-reentrant +locks. `dlopen()` also uses a reentrant lock to guard information about loaded files. + +On Linux x86_64 `injector_inject_in_cloned_thread` in place of `injector_inject` +may be a solution of the locking issue. It calls `dlopen()` in a thread created by +[`clone()`]. Note that no wonder there are unexpected pitfalls because some resources +allocated in [`pthread_create()`] lack in the `clone()`-ed thread. Use it at +your own risk. + +# License + +Files under [`include`][] and [`src`][] are licensed under LGPL 2.1 or later. +Files under [`cmd`][] are licensed under GPL 2 or later. +Files under [`util`][] are licensed under 2-clause BSD. + +[`linux-inject`]: https://github.com/gaffe23/linux-inject +[Caveat about `ptrace()`]: https://github.com/gaffe23/linux-inject#caveat-about-ptrace +[`inject`]: https://github.com/gaffe23/linux-inject#usage +[`clone()`]: https://man7.org/linux/man-pages/man2/clone.2.html +[`cmd`]: cmd +[`include`]: include +[`pthread_create()`]: https://man7.org/linux/man-pages/man3/pthread_create.3.html +[`src`]: src +[`util`]: util +[`CreateRemoteThread+LoadLibrary`]: https://www.google.com/search?&q=CreateRemoteThread+LoadLIbrary +[`disable SIP`]: https://developer.apple.com/documentation/security/disabling_and_enabling_system_integrity_protection diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/include/injector.h b/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/include/injector.h new file mode 100644 index 0000000..55158d6 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/include/injector.h @@ -0,0 +1,245 @@ +/* -*- indent-tabs-mode: nil -*- + * + * injector - Library for injecting a shared library into a Linux process + * + * URL: https://github.com/kubo/injector + * + * ------------------------------------------------------ + * + * Copyright (C) 2018-2023 Kubo Takehiro + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/*! + * \file injector.h + * \brief Library for injecting a shared library into a Linux, Windows and macOS process + */ +#ifndef INJECTOR_H +#define INJECTOR_H + +#if defined(_WIN32) +#include +typedef DWORD injector_pid_t; +#else +#include + +/*! + * \brief Platform-dependent process id type (\c pid_t on Unix. \c DWORD on Windows) + */ +typedef pid_t injector_pid_t; +#endif + +#ifdef __cplusplus +extern "C" { +#endif +#if 0 +} +#endif + +#define INJERR_SUCCESS 0 /* linux, windows, macos */ +#define INJERR_OTHER -1 /* linux, windows, macos */ +#define INJERR_NO_MEMORY -2 /* linux, windows, macos */ +#define INJERR_NO_PROCESS -3 /* linux, windows, macos */ +#define INJERR_NO_LIBRARY -4 /* linux */ +#define INJERR_NO_FUNCTION -4 /* linux */ +#define INJERR_ERROR_IN_TARGET -5 /* linux, windows, macos */ +#define INJERR_FILE_NOT_FOUND -6 /* linux, windows, macos */ +#define INJERR_INVALID_MEMORY_AREA -7 /* linux, macos */ +#define INJERR_PERMISSION -8 /* linux, windows, macos */ +#define INJERR_UNSUPPORTED_TARGET -9 /* linux, windows, macos */ +#define INJERR_INVALID_ELF_FORMAT -10 /* linux */ +#define INJERR_WAIT_TRACEE -11 /* linux */ +#define INJERR_FUNCTION_MISSING -12 /* linux, windows, macos */ + +typedef struct injector injector_t; + +/*! + * \brief Attach to the specified process. + * \param[out] injector the address where the newly created injector handle will be stored + * \param[in] pid the process id to be attached + * \return zero on success. Otherwise, error code + */ +int injector_attach(injector_t **injector, injector_pid_t pid); + +/*! + * \brief Detach from the attached process and destroy the specified handle. + * \param[in] injector the injector handle to destroy + * \return zero on success. Otherwise, error code + */ +int injector_detach(injector_t *injector); + +/*! + * \brief Inject the specified shared library into the target process. + * \param[in] injector the injector handle specifying the target process + * \param[in] path the path name of the shared library + * \param[out] handle the address where the newly created module handle will be stored + * \return zero on success. Otherwise, error code + * + * Note on Linux: + * This calls functions inside of the target process interrupted by \c ptrace(). + * If the target process is interrupted while holding a non-reentrant lock and + * injector calls a function requiring the same lock, the process stops forever. + * If the lock type is reentrant, the status guarded by the lock may become inconsistent. + * As far as I checked, \c dlopen() internally calls \c malloc() requiring non-reentrant + * locks. \c dlopen() also uses a reentrant lock to guard information about loaded files. + */ +int injector_inject(injector_t *injector, const char *path, void **handle); + +/*! + * \brief Uninject the shared library specified by \c handle. + * \param[in] injector the injector handle specifying the target process + * \param[in] handle the module handle created by \c injector_inject + * \return zero on success. Otherwise, error code + * \remarks This fearute isn't supported for musl-libc processes. + * See [Functional differences from glibc](https://wiki.musl-libc.org/functional-differences-from-glibc.html#Unloading_libraries). + */ +int injector_uninject(injector_t *injector, void *handle); + +#if defined(INJECTOR_DOC) || defined(__linux__) || defined(__APPLE__) +/*! + * \brief Call the specified function taking no arguments in the target process (Linux and macOS only) + * \param[in] injector the injector handle specifying the target process + * \param[in] handle the module handle created by \c injector_inject or special-handles such as \c RTLD_DEFAULT + * \param[in] name the function name + * + * The \c handle and \c name arguments are passed to \c dlsym ([Linux](https://man7.org/linux/man-pages/man3/dlvsym.3.html), [macOS](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dlsym.3.html)) and then the return value of \c dlsym is called without arguments in the target process. + * + * This is same with the combination of injector_remote_func_addr() and injector_remote_call() without extra arguments. + * + * \note + * (Linux only) + * If the function in the target process internally calls non-[async-signal-safe]((https://man7.org/linux/man-pages/man7/signal-safety.7.html)) + * functions, it may stop the target process or cause unexpected behaviour. + * \sa injector_remote_func_addr(), injector_remote_call(), injector_remote_vcall() + */ +int injector_call(injector_t *injector, void *handle, const char* name); +#endif + +/*! + * \brief Get the message of the last error. + * \remarks The message is updated only when \c injector functions return non-zero. + */ +const char *injector_error(void); + +#if defined(INJECTOR_DOC) || defined(__linux__) || defined(_WIN32) +#define INJECTOR_HAS_REMOTE_CALL_FUNCS 1 +#include +#include + +/*! + * \brief Get the function address in the target process (Linux and Windows only) + * \param[in] injector the injector handle specifying the target process + * \param[in] handle the module handle created by \c injector_inject or special-handles such as \c RTLD_DEFAULT + * \param[in] name the function name + * \param[out] func_addr_out the address where the function address in the target process will be stored + * \return zero on success. Otherwise, error code + * + * \b Example + * + * Inject libfoo.so and then call foo_func(1, 2, 3) in it. + * \code + * void *handle; + * // inject libfoo.so and get the handle + * if (injector_inject(injector, "libfoo.so", &handle) != 0) { + * return; + * } + * size_t func_addr; + * // get the address of foo_func in the handle + * if (injector_remote_func_addr(injector, handle, "foo_func", &func_addr) != 0) { + * return; + * } + * intptr_t retval; + * // call foo_func + * if (injector_remote_call(injector, &retval, func_addr, 1, 2, 3) != 0) { + * return; + * } + * printf("The return value of foo_func(1, 2, 3) is %ld.\n", retval); + * \endcode + */ +int injector_remote_func_addr(injector_t *injector, void *handle, const char* name, size_t *func_addr_out); + +/*! + * \brief Call the function in the target process (Linux and Windows only) + * \param[in] injector the injector handle specifying the target process + * \param[out] retval \c NULL or the address where the return value of the function call will be stored + * \param[in] func_addr the function address in the target process + * \param[in] ... arguments passed to the function + * \return zero on success. Otherwise, error code + * \remarks + * The types of the arguments must be integer or pointer. + * If it is a pointer, it must point to a valid address in the target process. + * The number of arguments must be less than or equal to six. + * \note + * If the function in the target process internally calls non-[async-signal-safe]((https://man7.org/linux/man-pages/man7/signal-safety.7.html)) + * functions, it may stop the target process or cause unexpected behaviour. + * \sa injector_remote_func_addr(), injector_remote_vcall() + */ +int injector_remote_call(injector_t *injector, intptr_t *retval, size_t func_addr, ...); + +/*! + * \brief Call the function in the target process (Linux and Windows only) + * \param[in] injector the injector handle specifying the target process + * \param[out] retval \c NULL or the address where the return value of the function call will be stored + * \param[in] func_addr the function address in the target process + * \param[in] ap arguments passed to the function + * \return zero on success. Otherwise, error code + * \remarks + * The types of the arguments must be integer or pointer. + * If it is a pointer, it must point to a valid address in the target process. + * The number of arguments must be less than or equal to six. + * \note + * If the function in the target process internally calls non-[async-signal-safe]((https://man7.org/linux/man-pages/man7/signal-safety.7.html)) + * functions, it may stop the target process or cause unexpected behaviour. + * \sa injector_remote_func_addr(), injector_remote_call() + */ +int injector_remote_vcall(injector_t *injector, intptr_t *retval, size_t func_addr, va_list ap); +#endif + +#if defined(INJECTOR_DOC) || defined(_WIN32) +/*! + * \brief Same with \c injector_inject except the type of the \c path argument. (Windows only) + * \param[in] injector the injector handle specifying the target process + * \param[in] path the path name of the shared library + * \param[out] handle the address where the newly created module handle will be stored + * \return zero on success. Otherwise, error code + */ +int injector_inject_w(injector_t *injector, const wchar_t *path, void **handle); +#endif + +#if defined(INJECTOR_DOC) || (defined(__linux__) && defined(__x86_64__)) +#define INJECTOR_HAS_INJECT_IN_CLONED_THREAD 1 // feature test macro +/*! + * \brief Inject the specified shared library into the target process by the \c clone system call. (Linux x86_64 only) + * \param[in] injector the injector handle specifying the target process + * \param[in] path the path name of the shared library + * \param[out] handle the address where the newly created module handle will be stored + * \return zero on success. Otherwise, error code + * + * This calls `dlopen()` in a thread created by \c [clone()](https://man7.org/linux/man-pages/man2/clone.2.html). Note that no wonder there are unexpected + * pitfalls because some resources allocated in \c [pthread_create()](https://man7.org/linux/man-pages/man3/pthread_create.3.html) lack in the \c clone()-ed thread. + * Use it at your own risk. + */ +int injector_inject_in_cloned_thread(injector_t *injector, const char *path, void **handle); +#endif + +#if 0 +{ +#endif +#ifdef __cplusplus +}; /* extern "C" */ +#endif + +#endif diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/linux/elf.c b/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/linux/elf.c new file mode 100644 index 0000000..237eb77 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/linux/elf.c @@ -0,0 +1,624 @@ +/* -*- indent-tabs-mode: nil -*- + * + * injector - Library for injecting a shared library into a Linux process + * + * URL: https://github.com/kubo/injector + * + * ------------------------------------------------------ + * + * Copyright (C) 2018 Kubo Takehiro + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "injector_internal.h" + +#ifdef __LP64__ +#define Elf_Ehdr Elf64_Ehdr +#define Elf_Shdr Elf64_Shdr +#define Elf_Sym Elf64_Sym +#else +#define Elf_Ehdr Elf32_Ehdr +#define Elf_Shdr Elf32_Shdr +#define Elf_Sym Elf32_Sym +#endif + +// #define INJECTOR_DEBUG_ELF_C 1 + +#ifdef INJECTOR_DEBUG_ELF_C +#undef DEBUG +#define DEBUG(...) fprintf(stderr, __VA_ARGS__) +#else +#undef DEBUG +#define DEBUG(...) do {} while(0) +#endif + +typedef struct { + int dlfunc_type; /* -1, DLFUNC_POSIX or DLFUNC_INTERNAL */ + FILE *fp; + size_t libc_addr; + size_t str_offset; + size_t str_size; + size_t sym_offset; + size_t sym_num; + size_t sym_entsize; +} param_t; + +static int search_and_open_libc(FILE **fp_out, pid_t pid, size_t *addr, libc_type_t *libc_type); +static int open_libc(FILE **fp_out, const char *path, pid_t pid, dev_t dev, ino_t ino); +static FILE *fopen_with_ino(const char *path, dev_t dev, ino_t ino); +static int read_elf_ehdr(FILE *fp, Elf_Ehdr *ehdr); +static int read_elf_shdr(FILE *fp, Elf_Shdr *shdr, size_t shdr_size); +static int read_elf_sym(FILE *fp, Elf_Sym *sym, size_t sym_size); +static int find_symbol_addr(size_t *addr, param_t *prm, const char *posix_name, const char *internal_name); +static size_t find_strtab_offset(const param_t *prm, const char *name); + +int injector__collect_libc_information(injector_t *injector) +{ + pid_t pid = injector->pid; + FILE *fp; + Elf_Ehdr ehdr; + Elf_Shdr shdr; + param_t prm = {-1, }; + size_t shstrtab_offset; + int idx; + int rv; + + rv = search_and_open_libc(&fp, pid, &prm.libc_addr, &injector->libc_type); + if (rv != 0) { + return rv; + } + rv = read_elf_ehdr(fp, &ehdr); + if (rv != 0) { + goto cleanup; + } + fseek(fp, ehdr.e_shoff + ehdr.e_shstrndx * ehdr.e_shentsize, SEEK_SET); + rv = read_elf_shdr(fp, &shdr, ehdr.e_shentsize); + if (rv != 0) { + goto cleanup; + } + shstrtab_offset = shdr.sh_offset; + + fseek(fp, ehdr.e_shoff, SEEK_SET); + for (idx = 0; idx < ehdr.e_shnum; idx++) { + fpos_t pos; + char buf[8]; + + rv = read_elf_shdr(fp, &shdr, ehdr.e_shentsize); + if (rv != 0) { + goto cleanup; + } + switch (shdr.sh_type) { + case SHT_STRTAB: + fgetpos(fp, &pos); + fseek(fp, shstrtab_offset + shdr.sh_name, SEEK_SET); + fgets(buf, sizeof(buf), fp); + fsetpos(fp, &pos); + if (strcmp(buf, ".dynstr") == 0) { + prm.str_offset = shdr.sh_offset; + prm.str_size = shdr.sh_size; + } + break; + case SHT_DYNSYM: + fgetpos(fp, &pos); + fseek(fp, shstrtab_offset + shdr.sh_name, SEEK_SET); + fgets(buf, sizeof(buf), fp); + fsetpos(fp, &pos); + if (strcmp(buf, ".dynsym") == 0) { + prm.sym_offset = shdr.sh_offset; + prm.sym_entsize = shdr.sh_entsize; + prm.sym_num = shdr.sh_size / shdr.sh_entsize; + } + break; + } + if (prm.sym_offset != 0 && prm.str_offset != 0) { + break; + } + } + if (idx == ehdr.e_shnum) { + injector__set_errmsg("failed to find the .dynstr and .dynsym sections."); + rv = INJERR_INVALID_ELF_FORMAT; + goto cleanup; + } + + prm.fp = fp; + + rv = find_symbol_addr(&injector->dlopen_addr, &prm, "dlopen", "__libc_dlopen_mode"); + if (rv != 0) { + goto cleanup; + } + + rv = find_symbol_addr(&injector->dlclose_addr, &prm, "dlclose", "__libc_dlclose"); + if (rv != 0) { + goto cleanup; + } + + rv = find_symbol_addr(&injector->dlsym_addr, &prm, "dlsym", "__libc_dlsym"); + if (rv != 0) { + goto cleanup; + } + + if (prm.dlfunc_type != DLFUNC_INTERNAL) { + rv = find_symbol_addr(&injector->dlerror_addr, &prm, "dlerror", NULL); + if (rv != 0) { + goto cleanup; + } + } else { + injector->dlerror_addr = 0; + } + +#ifdef INJECTOR_HAS_INJECT_IN_CLONED_THREAD + rv = find_symbol_addr(&injector->clone_addr, &prm, "clone", "clone"); + if (rv != 0) { + goto cleanup; + } +#endif + + rv = find_symbol_addr(NULL, &prm, "gnu_get_libc_release", "gnu_get_libc_release"); + if (rv == 0) { + /* GNU libc */ + injector->libc_type = LIBC_TYPE_GNU; + } + + injector->dlfunc_type = prm.dlfunc_type; + injector->code_addr = prm.libc_addr + ehdr.e_entry; + + switch (ehdr.e_machine) { + case EM_X86_64: + if (ehdr.e_ident[EI_CLASS] == ELFCLASS64) { + /* LP64 */ + injector->arch = ARCH_X86_64; + injector->sys_mmap = 9; + injector->sys_mprotect = 10; + injector->sys_munmap = 11; + } else { + /* ILP32 */ + injector->arch = ARCH_X86_64_X32; + injector->sys_mmap = 0x40000000 + 9; + injector->sys_mprotect = 0x40000000 + 10; + injector->sys_munmap = 0x40000000 + 11; + } + break; + case EM_386: + injector->arch = ARCH_I386; + injector->sys_mmap = 192; + injector->sys_mprotect = 125; + injector->sys_munmap = 91; + break; + case EM_AARCH64: + injector->arch = ARCH_ARM64; + injector->sys_mmap = 222; + injector->sys_mprotect = 226; + injector->sys_munmap = 215; + break; + case EM_ARM: + if (EF_ARM_EABI_VERSION(ehdr.e_flags) == 0) { + injector__set_errmsg("ARM OABI target process isn't supported."); + rv = INJERR_UNSUPPORTED_TARGET; + goto cleanup; + } + if (injector->code_addr & 1u) { + injector->code_addr &= ~1u; + injector->arch = ARCH_ARM_EABI_THUMB; + } else { + injector->arch = ARCH_ARM_EABI; + } + injector->sys_mmap = 192; + injector->sys_mprotect = 125; + injector->sys_munmap = 91; + break; + case EM_MIPS: + if (ehdr.e_ident[EI_CLASS] == ELFCLASS64) { + /* MIPS 64 */ + injector->arch = ARCH_MIPS_64; + injector->sys_mmap = 5000 + 9; + injector->sys_mprotect = 5000 + 10; + injector->sys_munmap = 5000 + 11; + } else if (ehdr.e_flags & EF_MIPS_ABI2) { + /* MIPS N32 */ + injector->arch = ARCH_MIPS_N32; + injector->sys_mmap = 6000 + 9; + injector->sys_mprotect = 6000 + 10; + injector->sys_munmap = 6000 + 11; + } else { + /* MIPS O32 */ + injector->arch = ARCH_MIPS_O32; + injector->sys_mmap = 4000 + 90; + injector->sys_mprotect = 4000 + 125; + injector->sys_munmap = 4000 + 91; + } + break; + case EM_PPC64: + injector->arch = ARCH_POWERPC_64; + injector->sys_mmap = 90; + injector->sys_mprotect = 125; + injector->sys_munmap = 91; + break; + case EM_PPC: + injector->arch = ARCH_POWERPC; + injector->sys_mmap = 90; + injector->sys_mprotect = 125; + injector->sys_munmap = 91; + break; +#ifdef EM_RISCV + case EM_RISCV: + if (ehdr.e_ident[EI_CLASS] == ELFCLASS64) { + injector->arch = ARCH_RISCV_64; + } else { + injector->arch = ARCH_RISCV_32; + } + injector->sys_mmap = 222; + injector->sys_mprotect = 226; + injector->sys_munmap = 215; + break; +#endif + default: + injector__set_errmsg("Unknown target process architecture: 0x%04x", ehdr.e_machine); + rv = INJERR_UNSUPPORTED_TARGET; + goto cleanup; + } + rv = 0; +cleanup: + fclose(fp); + return rv; +} + +static int search_and_open_libc(FILE **fp_out, pid_t pid, size_t *addr, libc_type_t *libc_type) +{ + char buf[512]; + FILE *fp = NULL; + regex_t reg; + regmatch_t match; + + sprintf(buf, "/proc/%d/maps", pid); + fp = fopen(buf, "r"); + if (fp == NULL) { + injector__set_errmsg("failed to open %s. (error: %s)", buf, strerror(errno)); + return INJERR_OTHER; + } + DEBUG("Open %s\n", buf); + /* /libc.so.6 or /libc-2.{DIGITS}.so or /ld-musl-{arch}.so.1 */ + if (regcomp(®, "/libc(\\.so\\.6|-2\\.[0-9]+\\.so)|/ld-musl-.+?\\.so\\.1", REG_EXTENDED) != 0) { + injector__set_errmsg("regcomp failed!"); + return INJERR_OTHER; + } + while (fgets(buf, sizeof(buf), fp) != NULL) { + unsigned long saddr, eaddr; + unsigned long long offset, inode; + unsigned int dev_major, dev_minor; + DEBUG(" %s", buf); + if (sscanf(buf, "%lx-%lx %*s %llx %x:%x %llu", &saddr, &eaddr, &offset, &dev_major, &dev_minor, &inode) != 6) { + continue; + } + if (offset != 0) { + continue; + } + if (regexec(®, buf, 1, &match, 0) != 0) { + continue; + } + char *p = buf + match.rm_eo; + if (strcmp(p, " (deleted)\n") == 0) { + injector__set_errmsg("The C library when the process started was removed"); + fclose(fp); + regfree(®); + return INJERR_NO_LIBRARY; + } + if (strcmp(p, "\n") != 0) { + continue; + } + fclose(fp); + *addr = saddr; + if (strstr(buf, "/ld-musl-") != NULL) { + *libc_type = LIBC_TYPE_MUSL; + } else { + *libc_type = LIBC_TYPE_GNU; + } + regfree(®); + *p = '\0'; + p = strchr(buf, '/'); + DEBUG(" libc in /proc/PID/maps: '%s'\n", p); + return open_libc(fp_out, p, pid, makedev(dev_major, dev_minor), inode); + } + fclose(fp); + injector__set_errmsg("Could not find libc"); + regfree(®); + return INJERR_NO_LIBRARY; +} + +static int open_libc(FILE **fp_out, const char *path, pid_t pid, dev_t dev, ino_t ino) +{ + FILE *fp = fopen_with_ino(path, dev, ino); + + if (fp != NULL) { + goto found; + } + + /* workaround for LXD */ + const char *p = strstr(path, "/rootfs/"); + if (p != NULL) { + fp = fopen_with_ino(p + 7, dev, ino); + if (fp != NULL) { + goto found; + } + } + + // workaround for Flatpak (https://flatpak.org/) + // + // libc is under /proc//root. + // The idea came from https://github.com/kubo/injector/pull/36. + char buf[PATH_MAX]; + snprintf(buf, sizeof(buf), "/proc/%d/root%s", pid, path); + buf[sizeof(buf) - 1] = '\0'; + fp = fopen_with_ino(buf, dev, ino); + if (fp != NULL) { + goto found; + } + + // workaround for Snap + // + // libc is in a base snap (https://snapcraft.io/docs/base-snaps), + glob_t globbuf; + if (glob("/snap/core*/*", GLOB_NOSORT, NULL, &globbuf) == 0) { + size_t idx; + for (idx = 0; idx < globbuf.gl_pathc; idx++) { + char buf[512]; + snprintf(buf, sizeof(buf), "%s%s", globbuf.gl_pathv[idx], path); + buf[sizeof(buf) - 1] = '\0'; + fp = fopen_with_ino(buf, dev, ino); + if (fp != NULL) { + globfree(&globbuf); + goto found; + } + } + globfree(&globbuf); + } + injector__set_errmsg("failed to open %s. (dev:0x%" PRIx64 ", ino:%lu)", path, dev, ino); + return INJERR_NO_LIBRARY; +found: + *fp_out = fp; + return 0; +} + +static inline int is_on_overlay_fs(int fd) +{ + struct statfs sbuf; + if (fstatfs(fd, &sbuf) != 0) { + DEBUG(" fstatfs() error %s\n", strerror(errno)); + return -1; + } +#ifndef OVERLAYFS_SUPER_MAGIC +#define OVERLAYFS_SUPER_MAGIC 0x794c7630 +#endif + + //return (sbuf.f_type == OVERLAYFS_SUPER_MAGIC) ? 1 : 0; + + // Always return true. Fedora uses BTRFS_SUPER_MAGIC instead of OVERLAYFS_SUPER_MAGIC. + // Injection works fine. Returning false here breaks it. + return 1; +} + +static FILE *fopen_with_ino(const char *path, dev_t dev, ino_t ino) +{ + DEBUG(" checking: '%s' ...", path); + struct stat sbuf; + FILE *fp = fopen(path, "r"); + + if (fp == NULL) { + DEBUG(" fopen() error %s\n", strerror(errno)); + return NULL; + } + + if (fstat(fileno(fp), &sbuf) != 0) { + DEBUG(" fstat() error %s\n", strerror(errno)); + goto cleanup; + } + if (sbuf.st_ino != ino) { + DEBUG(" unexpected inode number: expected %llu but %llu\n", + (unsigned long long)ino, (unsigned long long)sbuf.st_ino); + goto cleanup; + } + if (sbuf.st_dev != dev) { + int rv = is_on_overlay_fs(fileno(fp)); + if (rv < 0) { + goto cleanup; + } + if (rv != 1) { + DEBUG(" unexpected device number: expected %llu but %llu\n", + (unsigned long long)dev, (unsigned long long)sbuf.st_dev); + goto cleanup; + } + DEBUG(" ignore device number mismatch (expected %llu but %llu) on overlay file system ... ", + (unsigned long long)dev, (unsigned long long)sbuf.st_dev); + } + + DEBUG(" OK\n"); + return fp; +cleanup: + fclose(fp); + return NULL; +} + +static int read_elf_ehdr(FILE *fp, Elf_Ehdr *ehdr) +{ + if (fread(ehdr, sizeof(*ehdr), 1, fp) != 1) { + injector__set_errmsg("failed to read ELF header. (error: %s)", strerror(errno)); + return INJERR_INVALID_ELF_FORMAT; + } + if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) { + injector__set_errmsg("Invalid ELF header: 0x%02x,0x%02x,0x%02x,0x%02x", + ehdr->e_ident[0], ehdr->e_ident[1], ehdr->e_ident[2], ehdr->e_ident[3]); + return INJERR_INVALID_ELF_FORMAT; + } + switch (ehdr->e_ident[EI_CLASS]) { + case ELFCLASS32: +#ifdef __LP64__ + { + Elf32_Ehdr *ehdr32 = (Elf32_Ehdr *)ehdr; + /* copy from last */ + ehdr->e_shstrndx = ehdr32->e_shstrndx; + ehdr->e_shnum = ehdr32->e_shnum; + ehdr->e_shentsize = ehdr32->e_shentsize; + ehdr->e_phnum = ehdr32->e_phnum; + ehdr->e_phentsize = ehdr32->e_phentsize; + ehdr->e_ehsize = ehdr32->e_ehsize; + ehdr->e_flags = ehdr32->e_flags; + ehdr->e_shoff = ehdr32->e_shoff; + ehdr->e_phoff = ehdr32->e_phoff; + ehdr->e_entry = ehdr32->e_entry; + ehdr->e_version = ehdr32->e_version; + ehdr->e_machine = ehdr32->e_machine; + ehdr->e_type = ehdr32->e_type; + } +#endif + break; + case ELFCLASS64: +#ifndef __LP64__ + injector__set_errmsg("64-bit target process isn't supported by 32-bit process."); + return INJERR_UNSUPPORTED_TARGET; +#endif + break; + default: + injector__set_errmsg("Invalid ELF class: 0x%x", ehdr->e_ident[EI_CLASS]); + return INJERR_UNSUPPORTED_TARGET; + } + return 0; +} + +static int read_elf_shdr(FILE *fp, Elf_Shdr *shdr, size_t shdr_size) +{ + if (fread(shdr, shdr_size, 1, fp) != 1) { + injector__set_errmsg("failed to read a section header. (error: %s)", strerror(errno)); + return INJERR_INVALID_ELF_FORMAT; + } +#ifdef __LP64__ + if (shdr_size == sizeof(Elf32_Shdr)) { + Elf32_Shdr shdr32 = *(Elf32_Shdr *)shdr; + shdr->sh_name = shdr32.sh_name; + shdr->sh_type = shdr32.sh_type; + shdr->sh_flags = shdr32.sh_flags; + shdr->sh_addr = shdr32.sh_addr; + shdr->sh_offset = shdr32.sh_offset; + shdr->sh_size = shdr32.sh_size; + shdr->sh_link = shdr32.sh_link; + shdr->sh_info = shdr32.sh_info; + shdr->sh_addralign = shdr32.sh_addralign; + shdr->sh_entsize = shdr32.sh_entsize; + } +#endif + return 0; +} + +static int read_elf_sym(FILE *fp, Elf_Sym *sym, size_t sym_size) +{ + if (fread(sym, sym_size, 1, fp) != 1) { + injector__set_errmsg("failed to read a symbol table entry. (error: %s)", strerror(errno)); + return INJERR_INVALID_ELF_FORMAT; + } +#ifdef __LP64__ + if (sym_size == sizeof(Elf32_Sym)) { + Elf32_Sym sym32 = *(Elf32_Sym *)sym; + sym->st_name = sym32.st_name; + sym->st_value = sym32.st_value; + sym->st_size = sym32.st_size; + sym->st_info = sym32.st_info; + sym->st_other = sym32.st_other; + sym->st_shndx = sym32.st_shndx; + } +#endif + return 0; +} + +static int find_symbol_addr(size_t *addr, param_t *prm, const char *posix_name, const char *internal_name) +{ + size_t st_name; + + switch (prm->dlfunc_type) { + case -1: + st_name = find_strtab_offset(prm, posix_name); + if (st_name != 0) { + prm->dlfunc_type = DLFUNC_POSIX; + } else { + prm->dlfunc_type = DLFUNC_INTERNAL; + st_name = find_strtab_offset(prm, internal_name); + } + break; + case DLFUNC_POSIX: + st_name = find_strtab_offset(prm, posix_name); + break; + case DLFUNC_INTERNAL: + st_name = find_strtab_offset(prm, internal_name); + break; + } + + if (addr == NULL) { + return st_name != 0 ? 0 : INJERR_NO_FUNCTION; + } + + if (st_name != 0) { + Elf_Sym sym; + int idx; + int rv; + + fseek(prm->fp, prm->sym_offset, SEEK_SET); + for (idx = 0; idx < prm->sym_num; idx++) { + rv = read_elf_sym(prm->fp, &sym, prm->sym_entsize); + if (rv != 0) { + return rv; + } + if (sym.st_name == st_name) { + *addr = prm->libc_addr + sym.st_value; + return 0; + } + } + } + injector__set_errmsg("failed to find %s%s%s in the .dynstr section.", + posix_name, internal_name ? "/" : "", + internal_name ? internal_name : ""); + return INJERR_NO_FUNCTION; +} + +static size_t find_strtab_offset(const param_t *prm, const char *name) +{ + size_t off; + size_t idx = 0; + + fseek(prm->fp, prm->str_offset, SEEK_SET); + for (off = 0; off < prm->str_size; off++) { + int c = fgetc(prm->fp); + if (c == EOF) { + return 0; + } + if (c == name[idx]) { + if (c == 0) { + return off - idx; + } + idx++; + } else { + idx = 0; + } + } + return 0; +} diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/linux/injector.c b/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/linux/injector.c new file mode 100644 index 0000000..3b7d887 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/linux/injector.c @@ -0,0 +1,389 @@ +/* -*- indent-tabs-mode: nil -*- + * + * injector - Library for injecting a shared library into a Linux process + * + * URL: https://github.com/kubo/injector + * + * ------------------------------------------------------ + * + * Copyright (C) 2018-2023 Kubo Takehiro + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "injector_internal.h" + +static inline size_t remote_mem_size(injector_t *injector) { + return 2 * injector->data_size + injector->stack_size; +} + +int injector_attach(injector_t **injector_out, pid_t pid) +{ + injector_t *injector; + int status; + intptr_t retval; + int prot; + int rv = 0; + + injector__errmsg_is_set = 0; + + injector = calloc(1, sizeof(injector_t)); + if (injector == NULL) { + injector__set_errmsg("malloc error: %s", strerror(errno)); + return INJERR_NO_MEMORY; + } + injector->pid = pid; + rv = injector__attach_process(injector); + if (rv != 0) { + goto error_exit; + } + injector->attached = 1; + + do { + rv = waitpid(pid, &status, 0); + } while (rv == -1 && errno == EINTR); + if (rv == -1) { + injector__set_errmsg("waitpid error while attaching: %s", strerror(errno)); + rv = INJERR_WAIT_TRACEE; + goto error_exit; + } + + rv = injector__collect_libc_information(injector); + if (rv != 0) { + goto error_exit; + } + rv = injector__get_regs(injector, &injector->regs); + if (rv != 0) { + goto error_exit; + } + rv = injector__read(injector, injector->code_addr, &injector->backup_code, sizeof(injector->backup_code)); + if (rv != 0) { + goto error_exit; + } + + injector->data_size = sysconf(_SC_PAGESIZE); + injector->stack_size = 2 * 1024 * 1024; + + rv = injector__call_syscall(injector, &retval, injector->sys_mmap, 0, + remote_mem_size(injector), PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0); + if (rv != 0) { + goto error_exit; + } + if (retval == -1) { + injector__set_errmsg("mmap error: %s", strerror(errno)); + rv = INJERR_ERROR_IN_TARGET; + goto error_exit; + } + injector->mmapped = 1; + injector->data = (size_t)retval; + injector->stack = (size_t)retval + 2 * injector->data_size; +#ifdef INJECTOR_HAS_INJECT_IN_CLONED_THREAD + injector->shellcode = (size_t)retval + 1 * injector->data_size; + prot = PROT_READ | PROT_EXEC; +#else + prot = PROT_NONE; +#endif + rv = injector__call_syscall(injector, &retval, injector->sys_mprotect, + injector->data + injector->data_size, injector->data_size, + prot); + if (rv != 0) { + goto error_exit; + } + if (retval != 0) { + injector__set_errmsg("mprotect error: %s", strerror(errno)); + rv = INJERR_ERROR_IN_TARGET; + goto error_exit; + } +#ifdef INJECTOR_HAS_INJECT_IN_CLONED_THREAD + rv = injector__write(injector, injector->shellcode, &injector_shellcode, injector_shellcode_size); + if (rv != 0) { + return rv; + } +#endif + + *injector_out = injector; + return 0; +error_exit: + injector_detach(injector); + return rv; +} + +int injector_inject(injector_t *injector, const char *path, void **handle) +{ + char abspath[PATH_MAX]; + int dlflags = RTLD_LAZY; + size_t len; + int rv; + intptr_t retval; + + injector__errmsg_is_set = 0; + + if (path[0] == '/') { + len = strlen(path) + 1; + } else if (realpath(path, abspath) != NULL) { + path = abspath; + len = strlen(abspath) + 1; + } else { + injector__set_errmsg("failed to get the full path of '%s': %s", + path, strerror(errno)); + return INJERR_FILE_NOT_FOUND; + } + + if (len > injector->data_size) { + injector__set_errmsg("too long file path: %s", path); + return INJERR_FILE_NOT_FOUND; + } + + rv = injector__write(injector, injector->data, path, len); + if (rv != 0) { + return rv; + } + if (injector->dlfunc_type == DLFUNC_INTERNAL) { +#define __RTLD_DLOPEN 0x80000000 // glibc internal flag + dlflags |= __RTLD_DLOPEN; + } + rv = injector__call_function(injector, &retval, injector->dlopen_addr, injector->data, dlflags); + if (rv != 0) { + return rv; + } + if (retval == 0) { + char buf[256 + 1] = {0,}; + if (injector->dlerror_addr != 0) { + rv = injector__call_function(injector, &retval, injector->dlerror_addr); + if (rv == 0 && retval != 0) { + injector__read(injector, retval, buf, sizeof(buf) - 1); + } + } + if (buf[0] != '\0') { + injector__set_errmsg("dlopen failed: %s", buf); + } else { + injector__set_errmsg("dlopen failed"); + } + return INJERR_ERROR_IN_TARGET; + } + if (handle != NULL) { + *handle = (void*)retval; + } + return 0; +} + +#ifdef INJECTOR_HAS_INJECT_IN_CLONED_THREAD +int injector_inject_in_cloned_thread(injector_t *injector, const char *path, void **handle_out) +{ + void *data; + injector_shellcode_arg_t *arg; + const size_t file_path_offset = offsetof(injector_shellcode_arg_t, file_path); + void * const invalid_handle = (void*)-3; + char abspath[PATH_MAX]; + size_t pathlen; + int rv; + intptr_t retval; + + injector__errmsg_is_set = 0; + + if (injector->arch != ARCH_X86_64) { + injector__set_errmsg("injector_inject_in_cloned_thread doesn't support %s.", + injector__arch2name(injector->arch)); + return INJERR_UNSUPPORTED_TARGET; + } + + if (realpath(path, abspath) == NULL) { + injector__set_errmsg("failed to get the full path of '%s': %s", + path, strerror(errno)); + return INJERR_FILE_NOT_FOUND; + } + pathlen = strlen(abspath) + 1; + + if (file_path_offset + pathlen > injector->data_size) { + injector__set_errmsg("too long path name: %s", path); + return INJERR_FILE_NOT_FOUND; + } + + data = alloca(injector->data_size); + memset(data, 0, injector->data_size); + arg = (injector_shellcode_arg_t *)data; + + arg->handle = invalid_handle; + arg->dlopen_addr = injector->dlopen_addr; + arg->dlerror_addr = injector->dlerror_addr; + arg->dlflags = RTLD_LAZY; + if (injector->dlfunc_type == DLFUNC_INTERNAL) { + arg->dlflags |= __RTLD_DLOPEN; + } + memcpy(arg->file_path, abspath, pathlen); + + rv = injector__write(injector, injector->data, data, injector->data_size); + if (rv != 0) { + return rv; + } + rv = injector__call_function(injector, &retval, injector->clone_addr, + injector->shellcode, injector->stack + injector->stack_size - 4096, + //CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, + CLONE_VM, + injector->data); + if (rv != 0) { + return rv; + } + if (retval == -1) { + injector__set_errmsg("clone error: %s", strerror(errno)); + return INJERR_ERROR_IN_TARGET; + } + const struct timespec ts = {0, 100000000}; /* 0.1 second */ + void *handle; + int cnt = 0; + +retry: + nanosleep(&ts, NULL); + rv = injector__read(injector, injector->data, &handle, sizeof(handle)); + if (rv != 0) { + return rv; + } + if (handle == invalid_handle) { + int max_retyr_cnt = 50; + if (++cnt <= max_retyr_cnt) { + goto retry; + } + injector__set_errmsg("dlopen doesn't return in %d seconds.", max_retyr_cnt / 10); + return INJERR_ERROR_IN_TARGET; + } + if (handle_out != NULL) { + *handle_out = handle; + } + if (handle == NULL) { + arg->file_path[0] = '\0'; + injector__read(injector, injector->data, data, injector->data_size); + if (arg->file_path[0] != '\0') { + injector__set_errmsg("%s", arg->file_path); + } else { + injector__set_errmsg("dlopen error"); + } + return INJERR_ERROR_IN_TARGET; + } + return 0; +} +#endif + +int injector_remote_func_addr(injector_t *injector, void *handle, const char* name, size_t *func_addr_out) +{ + int rv; + intptr_t retval; + size_t len = strlen(name) + 1; + + injector__errmsg_is_set = 0; + + if (len > injector->data_size) { + injector__set_errmsg("too long function name: %s", name); + return INJERR_FUNCTION_MISSING; + } + rv = injector__write(injector, injector->data, name, len); + if (rv != 0) { + return rv; + } + rv = injector__call_function(injector, &retval, injector->dlsym_addr, handle, injector->data); + if (rv != 0) { + return rv; + } + if (retval == 0) { + injector__set_errmsg("function not found: %s", name); + return INJERR_FUNCTION_MISSING; + } + *func_addr_out = (size_t)retval; + return 0; +} + +int injector_remote_call(injector_t *injector, intptr_t *retval, size_t func_addr, ...) +{ + va_list ap; + int rv; + injector__errmsg_is_set = 0; + va_start(ap, func_addr); + rv = injector__call_function_va_list(injector, retval, func_addr, ap); + va_end(ap); + return rv; +} + +int injector_remote_vcall(injector_t *injector, intptr_t *retval, size_t func_addr, va_list ap) +{ + injector__errmsg_is_set = 0; + return injector__call_function_va_list(injector, retval, func_addr, ap); +} + +int injector_call(injector_t *injector, void *handle, const char* name) +{ + size_t func_addr; + int rv = injector_remote_func_addr(injector, handle, name, &func_addr); + if (rv != 0) { + return rv; + } + return injector__call_function(injector, NULL, func_addr); +} + +int injector_uninject(injector_t *injector, void *handle) +{ + int rv; + intptr_t retval; + + injector__errmsg_is_set = 0; + if (injector->libc_type == LIBC_TYPE_MUSL) { + /* Assume that libc is musl. */ + injector__set_errmsg("Cannot uninject libraries under musl libc. See: https://wiki.musl-libc.org/functional-differences-from-glibc.html#Unloading_libraries"); + return INJERR_UNSUPPORTED_TARGET; + } + + rv = injector__call_function(injector, &retval, injector->dlclose_addr, handle); + if (rv != 0) { + return rv; + } + if (retval != 0) { + injector__set_errmsg("dlclose failed"); + return INJERR_ERROR_IN_TARGET; + } + return 0; +} + +int injector_detach(injector_t *injector) +{ + injector__errmsg_is_set = 0; + + if (injector->mmapped) { + injector__call_syscall(injector, NULL, injector->sys_munmap, injector->data, remote_mem_size(injector)); + } + if (injector->attached) { + injector__detach_process(injector); + } + free(injector); + return 0; +} + +const char *injector_error(void) +{ + return injector__errmsg; +} diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/linux/injector_internal.h b/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/linux/injector_internal.h new file mode 100644 index 0000000..993cc51 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/linux/injector_internal.h @@ -0,0 +1,193 @@ +/* -*- indent-tabs-mode: nil -*- + * + * injector - Library for injecting a shared library into a Linux process + * + * URL: https://github.com/kubo/injector + * + * ------------------------------------------------------ + * + * Copyright (C) 2018-2023 Kubo Takehiro + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef INJECTOR_INTERNAL_H +#define INJECTOR_INTERNAL_H 1 +#include +#include +#include +#include +#include +#include +#include +#include "injector.h" + +#ifdef __LP64__ +#define SIZE_T_FMT "l" +#else +#define SIZE_T_FMT "" +#endif + +#ifdef __arm__ +#define user_regs_struct user_regs +#endif + +#ifdef __mips__ +#include +#define user_regs_struct pt_regs +#endif + +#ifdef __powerpc__ +#include +#define user_regs_struct pt_regs +#endif + +#ifdef __riscv +#include +#endif + +#define PTRACE_OR_RETURN(request, injector, addr, data) do { \ + int rv = injector__ptrace(request, injector->pid, addr, data, #request); \ + if (rv != 0) { \ + return rv; \ + } \ +} while (0) + +typedef enum { + /* use dlopen/dlsym/dlclose (glibc 2.34 or later) */ + DLFUNC_POSIX, + /* use __libc_dlopen_mode/__libc_dlsym/__libc_dlclose" (glibc 2.33 or earlier) */ + DLFUNC_INTERNAL, +} dlfunc_type_t; + +typedef enum { + LIBC_TYPE_UNKNOWN = 0, + LIBC_TYPE_GNU, + LIBC_TYPE_MUSL, +} libc_type_t; + +typedef enum { + ARCH_X86_64, + ARCH_X86_64_X32, + ARCH_I386, + ARCH_ARM64, + ARCH_ARM_EABI_THUMB, + ARCH_ARM_EABI, + ARCH_MIPS_64, + ARCH_MIPS_N32, + ARCH_MIPS_O32, + ARCH_POWERPC_64, + ARCH_POWERPC, + ARCH_RISCV_64, + ARCH_RISCV_32, +} arch_t; + +typedef union { +#if defined(__x86_64__) || defined(__i386__) + uint8_t u8[sizeof(long)]; +#elif defined(__aarch64__) || defined(__arm__) + uint16_t u16[4]; + uint32_t u32[2]; +#elif defined(__mips__) + uint32_t u32[4]; +#elif defined(__powerpc__) + uint32_t u32[2]; +#elif defined(__riscv) + uint32_t u32[2]; +#endif + long dummy; +} code_t; + +struct injector { + pid_t pid; + uint8_t attached; + uint8_t mmapped; + arch_t arch; + libc_type_t libc_type; + struct user_regs_struct regs; + dlfunc_type_t dlfunc_type; + size_t dlopen_addr; + size_t dlclose_addr; + size_t dlsym_addr; + size_t dlerror_addr; +#ifdef INJECTOR_HAS_INJECT_IN_CLONED_THREAD + size_t clone_addr; +#endif + size_t code_addr; /* address where instructions are written */ + code_t backup_code; + long sys_mmap; + long sys_mprotect; + long sys_munmap; + + /* memory layout allocated in the target process + * + * high +----------------------+ + * | stack area | + * | size: 2MB | + * |----------------------| + * | inaccessible area | + * | size: 4096 | + * |----------------------| + * | data area | + * | size: 4096 | + * low +----------------------+ + */ + size_t data; /* read-write region */ + size_t data_size; /* page size */ + size_t stack; /* stack area */ + size_t stack_size; /* 2MB */ +#ifdef INJECTOR_HAS_INJECT_IN_CLONED_THREAD + size_t shellcode; +#endif +}; + +/* elf.c */ +int injector__collect_libc_information(injector_t *injector); + +/* ptrace.c */ +int injector__ptrace(int request, pid_t pid, long addr, long data, const char *request_name); +int injector__attach_process(const injector_t *injector); +int injector__detach_process(const injector_t *injector); +int injector__get_regs(const injector_t *injector, struct user_regs_struct *regs); +int injector__set_regs(const injector_t *injector, const struct user_regs_struct *regs); +int injector__read(const injector_t *injector, size_t addr, void *buf, size_t len); +int injector__write(const injector_t *injector, size_t addr, const void *buf, size_t len); +int injector__continue(const injector_t *injector); + +/* remote_call.c - call functions and syscalls in the target process */ +int injector__call_syscall(const injector_t *injector, intptr_t *retval, long syscall_number, ...); +int injector__call_function(const injector_t *injector, intptr_t *retval, long function_addr, ...); +int injector__call_function_va_list(const injector_t *injector, intptr_t *retval, long function_addr, va_list ap); + +/* util.c */ +extern char injector__errmsg[]; +extern char injector__errmsg_is_set; +void injector__set_errmsg(const char *format, ...) __attribute__((format (printf, 1, 2))); +const char *injector__arch2name(arch_t arch); + +/* shellcode.S */ +#ifdef INJECTOR_HAS_INJECT_IN_CLONED_THREAD +typedef struct { + void *handle; + size_t dlopen_addr; + size_t dlerror_addr; + int dlflags; + char file_path[0]; // dummy size. +} injector_shellcode_arg_t; + +void *injector_shellcode(injector_shellcode_arg_t *arg); +extern int injector_shellcode_size; +#endif + +#endif diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/linux/ptrace.c b/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/linux/ptrace.c new file mode 100644 index 0000000..43b5083 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/linux/ptrace.c @@ -0,0 +1,149 @@ +/* -*- indent-tabs-mode: nil -*- + * + * injector - Library for injecting a shared library into a Linux process + * + * URL: https://github.com/kubo/injector + * + * ------------------------------------------------------ + * + * Copyright (C) 2018 Kubo Takehiro + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "injector_internal.h" + +#if defined(__aarch64__) || defined(__riscv) +#define USE_REGSET +#include /* for NT_PRSTATUS */ +#include /* for struct iovec */ +#endif + +static int set_ptrace_error(const char *request_name) +{ + int err = errno; + injector__set_errmsg("%s error : %s", request_name, strerror(errno)); + switch (err) { + case EFAULT: + return INJERR_INVALID_MEMORY_AREA; + case EPERM: + return INJERR_PERMISSION; + case ESRCH: + return INJERR_NO_PROCESS; + } + return INJERR_OTHER; +} + +int injector__ptrace(int request, pid_t pid, long addr, long data, const char *request_name) +{ + if (ptrace(request, pid, addr, data) != 0) { + return set_ptrace_error(request_name); + } + return 0; +} + +int injector__attach_process(const injector_t *injector) +{ + PTRACE_OR_RETURN(PTRACE_ATTACH, injector, 0, 0); + return 0; +} + +int injector__detach_process(const injector_t *injector) +{ + PTRACE_OR_RETURN(PTRACE_DETACH, injector, 0, 0); + return 0; +} + +int injector__get_regs(const injector_t *injector, struct user_regs_struct *regs) +{ +#ifdef USE_REGSET + struct iovec iovec = { regs, sizeof(*regs) }; + PTRACE_OR_RETURN(PTRACE_GETREGSET, injector, NT_PRSTATUS, (long)&iovec); +#else + PTRACE_OR_RETURN(PTRACE_GETREGS, injector, 0, (long)regs); +#endif + return 0; +} + +int injector__set_regs(const injector_t *injector, const struct user_regs_struct *regs) +{ +#ifdef USE_REGSET + struct iovec iovec = { (void*)regs, sizeof(*regs) }; + PTRACE_OR_RETURN(PTRACE_SETREGSET, injector, NT_PRSTATUS, (long)&iovec); +#else + PTRACE_OR_RETURN(PTRACE_SETREGS, injector, 0, (long)regs); +#endif + return 0; +} + +int injector__read(const injector_t *injector, size_t addr, void *buf, size_t len) +{ + pid_t pid = injector->pid; + long word; + char *dest = (char *)buf; + + errno = 0; + while (len >= sizeof(long)) { + word = ptrace(PTRACE_PEEKTEXT, pid, addr, 0); + if (word == -1 && errno != 0) { + return set_ptrace_error("PTRACE_PEEKTEXT"); + } + *(long*)dest = word; + addr += sizeof(long); + dest += sizeof(long); + len -= sizeof(long); + } + if (len != 0) { + char *src = (char *)&word; + word = ptrace(PTRACE_PEEKTEXT, pid, addr, 0); + if (word == -1 && errno != 0) { + return set_ptrace_error("PTRACE_PEEKTEXT"); + } + while (len--) { + *(dest++) = *(src++); + } + } + return 0; +} + +int injector__write(const injector_t *injector, size_t addr, const void *buf, size_t len) +{ + pid_t pid = injector->pid; + const char *src = (const char *)buf; + + while (len >= sizeof(long)) { + PTRACE_OR_RETURN(PTRACE_POKETEXT, injector, addr, *(long*)src); + addr += sizeof(long); + src += sizeof(long); + len -= sizeof(long); + } + if (len != 0) { + long word = ptrace(PTRACE_PEEKTEXT, pid, addr, 0); + char *dest = (char*)&word; + if (word == -1 && errno != 0) { + return set_ptrace_error("PTRACE_PEEKTEXT"); + } + while (len--) { + *(dest++) = *(src++); + } + PTRACE_OR_RETURN(PTRACE_POKETEXT, injector, addr, word); + } + return 0; +} + +int injector__continue(const injector_t *injector) +{ + PTRACE_OR_RETURN(PTRACE_CONT, injector, 0, 0); + return 0; +} diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/linux/remote_call.c b/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/linux/remote_call.c new file mode 100644 index 0000000..3afeb83 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/linux/remote_call.c @@ -0,0 +1,821 @@ +/* -*- indent-tabs-mode: nil -*- + * + * injector - Library for injecting a shared library into a Linux process + * + * URL: https://github.com/kubo/injector + * + * ------------------------------------------------------ + * + * Copyright (C) 2018-2023 Kubo Takehiro + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#if defined __linux__ +/* Detect musl libc. See https://stackoverflow.com/a/70211227/985524 */ +#define _GNU_SOURCE +#include +#ifndef __USE_GNU +#define MUSL_LIBC +#endif // __USE_GNU +#endif // __linux__ + +#include +#include +#include +#include +#include "injector_internal.h" + +// #define INJECTOR_DEBUG_REMOTE_CALL 1 + +#ifdef INJECTOR_DEBUG_REMOTE_CALL +#undef DEBUG +#define DEBUG(...) fprintf(stderr, __VA_ARGS__) +#else +#undef DEBUG +#define DEBUG(...) do {} while(0) +#endif + +#ifdef __x86_64__ +#define eip rip +#define ebp rbp +#define esp rsp +#define eax rax +#define ebx rbx +#define ecx rcx +#define edx rdx +#define esi rsi +#define edi rdi +#define ebp rbp +#endif + +#if defined(__arm__) +#define reg32_return reg_return +#define uregs regs.uregs +#endif + +#define THUMB_MODE_BIT (1u << 5) +#define BREAKINST_THUMB 0xde01 /* in linux-source-tree/arch/arm/kernel/ptrace.c */ +#define BREAKINST_ARM 0xe7f001f0 /* in linux-source-tree/arch/arm/kernel/ptrace.c */ +#define BREAKINST_ARM64 0xd4200000 /* asm("brk #0") */ + +#ifdef __mips__ +#define REG_V0 2 +#define REG_A0 4 +#define REG_A1 5 +#define REG_A2 6 +#define REG_A3 7 +#define REG_A4 8 +#define REG_A5 9 +#define REG_T4 12 +#define REG_T9 25 +#define REG_SP 29 +#define REG_FP 30 +#define REG_RA 31 + +static void print_regs(const injector_t *injector, const struct pt_regs *regs) +{ + DEBUG(" Registers:\n"); + DEBUG(" -- at v0 v1: %016"PRIx64" %016"PRIx64" %016"PRIx64" %016"PRIx64"\n", + regs->regs[0], regs->regs[1], regs->regs[2], regs->regs[3]); + DEBUG(" a0 a1 a2 a3: %016"PRIx64" %016"PRIx64" %016"PRIx64" %016"PRIx64"\n", + regs->regs[4], regs->regs[5], regs->regs[6], regs->regs[7]); + DEBUG(" %s: %016"PRIx64" %016"PRIx64" %016"PRIx64" %016"PRIx64"\n", + (injector->arch != ARCH_MIPS_O32) ? "a4 a5 a6 a7" : "t0 t1 t2 t3", + regs->regs[8], regs->regs[9], regs->regs[10], regs->regs[11]); + DEBUG(" t4 t5 t6 t7: %016"PRIx64" %016"PRIx64" %016"PRIx64" %016"PRIx64"\n", + regs->regs[12], regs->regs[13], regs->regs[14], regs->regs[15]); + DEBUG(" s0 s1 s2 s3: %016"PRIx64" %016"PRIx64" %016"PRIx64" %016"PRIx64"\n", + regs->regs[16], regs->regs[17], regs->regs[18], regs->regs[19]); + DEBUG(" s4 s5 s6 s7: %016"PRIx64" %016"PRIx64" %016"PRIx64" %016"PRIx64"\n", + regs->regs[20], regs->regs[21], regs->regs[22], regs->regs[23]); + DEBUG(" t8 t9 k0 k1: %016"PRIx64" %016"PRIx64" %016"PRIx64" %016"PRIx64"\n", + regs->regs[24], regs->regs[25], regs->regs[26], regs->regs[27]); + DEBUG(" gp sp s8 ra: %016"PRIx64" %016"PRIx64" %016"PRIx64" %016"PRIx64"\n", + regs->regs[28], regs->regs[29], regs->regs[30], regs->regs[31]); + DEBUG(" lo hi epc: %016"PRIx64" %016"PRIx64" %016"PRIx64"\n", + regs->lo, regs->hi, regs->cp0_epc); + DEBUG(" badvaddr status cause: %016"PRIx64" %016"PRIx64" %016"PRIx64"\n", + regs->cp0_badvaddr, regs->cp0_status, regs->cp0_cause); +} +#define PRINT_REGS(injector, regs) print_regs((injector), (regs)) +#endif /* __mips__ */ + + +#ifdef __powerpc__ +static void print_regs(const injector_t *injector, const struct pt_regs *regs) +{ +#undef WIDTH +#ifdef __LP64__ +#define WIDTH "016" +#define softe_or_mq_str "softe" +#define softe_or_mq softe +#else +#define WIDTH "08" +#define softe_or_mq_str "mq " +#define softe_or_mq mq +#endif + DEBUG(" Registers:\n"); + DEBUG(" gpr0 gpr1 gpr2 gpr3 : %"WIDTH"lx %"WIDTH"lx %"WIDTH"lx %"WIDTH"lx\n", + regs->gpr[0], regs->gpr[1], regs->gpr[2], regs->gpr[3]); + DEBUG(" gpr4 gpr5 gpr6 gpr7 : %"WIDTH"lx %"WIDTH"lx %"WIDTH"lx %"WIDTH"lx\n", + regs->gpr[4], regs->gpr[5], regs->gpr[6], regs->gpr[7]); + DEBUG(" gpr8 gpr9 gpr10 gpr11 : %"WIDTH"lx %"WIDTH"lx %"WIDTH"lx %"WIDTH"lx\n", + regs->gpr[8], regs->gpr[9], regs->gpr[10], regs->gpr[11]); + DEBUG(" gpr12 gpr13 gpr14 gpr15 : %"WIDTH"lx %"WIDTH"lx %"WIDTH"lx %"WIDTH"lx\n", + regs->gpr[12], regs->gpr[13], regs->gpr[14], regs->gpr[15]); + DEBUG(" gpr16 gpr17 gpr18 gpr19 : %"WIDTH"lx %"WIDTH"lx %"WIDTH"lx %"WIDTH"lx\n", + regs->gpr[16], regs->gpr[17], regs->gpr[18], regs->gpr[19]); + DEBUG(" gpr20 gpr21 gpr22 gpr23 : %"WIDTH"lx %"WIDTH"lx %"WIDTH"lx %"WIDTH"lx\n", + regs->gpr[20], regs->gpr[21], regs->gpr[22], regs->gpr[23]); + DEBUG(" gpr24 gpr25 gpr26 gpr27 : %"WIDTH"lx %"WIDTH"lx %"WIDTH"lx %"WIDTH"lx\n", + regs->gpr[24], regs->gpr[25], regs->gpr[26], regs->gpr[27]); + DEBUG(" gpr28 gpr29 gpr30 gpr31 : %"WIDTH"lx %"WIDTH"lx %"WIDTH"lx %"WIDTH"lx\n", + regs->gpr[28], regs->gpr[29], regs->gpr[30], regs->gpr[31]); + DEBUG(" nip msr orig_gpr3 ctr : %"WIDTH"lx %"WIDTH"lx %"WIDTH"lx %"WIDTH"lx\n", + regs->nip, regs->msr, regs->orig_gpr3, regs->ctr); + DEBUG(" link xer ccr "softe_or_mq_str" : %"WIDTH"lx %"WIDTH"lx %"WIDTH"lx %"WIDTH"lx\n", + regs->link, regs->xer, regs->ccr, regs->softe_or_mq); + DEBUG(" trap dar dsisr result : %"WIDTH"lx %"WIDTH"lx %"WIDTH"lx %"WIDTH"lx\n", + regs->trap, regs->dar, regs->dsisr, regs->result); +#undef WIDTH +} +#define PRINT_REGS(injector, regs) print_regs((injector), (regs)) +#endif + +#ifdef __riscv +#define REG_RA 1 +#define REG_T1 6 +#ifdef __LP64__ +#define WIDTH "016" +#else +#define WIDTH "08" +#endif +static void print_regs(const injector_t *injector, const struct user_regs_struct *regs) +{ + DEBUG(" Registers:\n"); + DEBUG(" pc ra sp gp : %"WIDTH"lx %"WIDTH"lx %"WIDTH"lx %"WIDTH"lx\n", + regs->pc, regs->ra, regs->sp, regs->gp); + DEBUG(" tp t0 t1 t2 : %"WIDTH"lx %"WIDTH"lx %"WIDTH"lx %"WIDTH"lx\n", + regs->tp, regs->t0, regs->t1, regs->t2); + DEBUG(" s0 s1 a0 a1 : %"WIDTH"lx %"WIDTH"lx %"WIDTH"lx %"WIDTH"lx\n", + regs->s0, regs->s1, regs->a0, regs->a1); + DEBUG(" a2 a3 a4 a5 : %"WIDTH"lx %"WIDTH"lx %"WIDTH"lx %"WIDTH"lx\n", + regs->a2, regs->a3, regs->a4, regs->a5); + DEBUG(" a6 a7 s2 s3 : %"WIDTH"lx %"WIDTH"lx %"WIDTH"lx %"WIDTH"lx\n", + regs->a6, regs->a7, regs->s2, regs->s3); + DEBUG(" s4 s5 s6 s7 : %"WIDTH"lx %"WIDTH"lx %"WIDTH"lx %"WIDTH"lx\n", + regs->s4, regs->s5, regs->s6, regs->s7); + DEBUG(" s8 s9 s10 s11 : %"WIDTH"lx %"WIDTH"lx %"WIDTH"lx %"WIDTH"lx\n", + regs->s8, regs->s9, regs->s10, regs->s11); + DEBUG(" t3 t4 t5 t6 : %"WIDTH"lx %"WIDTH"lx %"WIDTH"lx %"WIDTH"lx\n", + regs->t3, regs->t4, regs->t5, regs->t6); +} +#define PRINT_REGS(injector, regs) print_regs((injector), (regs)) +#endif + +/* register type used in struct user_regs_struct */ +#if defined(__mips__) +typedef uint64_t user_reg_t; +#elif defined(__riscv) +typedef unsigned long user_reg_t; +#elif defined(__LP64__) && !defined(MUSL_LIBC) +typedef unsigned long long user_reg_t; +#elif defined(__i386__) +typedef long user_reg_t; +#else +typedef unsigned long user_reg_t; +#endif + +static int kick_then_wait_sigtrap(const injector_t *injector, struct user_regs_struct *regs, code_t *code, size_t code_size); + +#ifndef PRINT_REGS +#define PRINT_REGS(injector, regs) do {} while (0) +#endif + +/* + * Call the specified system call in the target process. + * + * The arguments after syscall_number must be integer types and + * the size must not be greater than the size of long. + */ +int injector__call_syscall(const injector_t *injector, intptr_t *retval, long syscall_number, ...) +{ + struct user_regs_struct regs = injector->regs; + code_t code; + size_t code_size; + long arg1, arg2, arg3, arg4, arg5, arg6; + va_list ap; + int rv; +#if !defined(__mips__) && !defined(__powerpc__) + user_reg_t *reg_return = NULL; +#if defined(__aarch64__) + uint32_t *reg32_return = NULL; + uint32_t *uregs = (uint32_t *)®s; +#endif +#endif + + va_start(ap, syscall_number); + arg1 = va_arg(ap, long); + arg2 = va_arg(ap, long); + arg3 = va_arg(ap, long); + arg4 = va_arg(ap, long); + arg5 = va_arg(ap, long); + arg6 = va_arg(ap, long); + va_end(ap); + + DEBUG("injector__call_syscall:\n"); + DEBUG(" args: %ld, %lx, %lx, %lx, %lx, %lx, %lx\n", syscall_number, arg1, arg2, arg3, arg4, arg5, arg6); + +#if !(defined(__x86_64__) && defined(__LP64__)) + if (injector->arch == ARCH_X86_64_X32) { + injector__set_errmsg("x32-ABI target process is supported only by x86_64."); + return INJERR_UNSUPPORTED_TARGET; + } +#endif + + switch (injector->arch) { +#if defined(__x86_64__) && defined(__LP64__) + case ARCH_X86_64: + case ARCH_X86_64_X32: + /* setup instructions */ + code.u8[0] = 0x0f; + code.u8[1] = 0x05; /* 0f 05 : syscall */ + code.u8[2] = 0xcc; /* cc : int3 */ + memset(&code.u8[3], 0x90, sizeof(long) - 3); /* fill the rests with `nop` */ + code_size = sizeof(long); + /* setup registers */ + regs.rip = injector->code_addr; + regs.rax = syscall_number; + regs.rdi = arg1; + regs.rsi = arg2; + regs.rdx = arg3; + regs.r10 = arg4; + regs.r8 = arg5; + regs.r9 = arg6; + reg_return = ®s.rax; + break; +#endif +#if defined(__x86_64__) || defined(__i386__) + case ARCH_I386: + /* setup instructions */ + code.u8[0] = 0xcd; + code.u8[1] = 0x80; /* cd 80 : int $80 */ + code.u8[2] = 0xcc; /* cc : int3 */ + memset(&code.u8[3], 0x90, sizeof(long) - 3); /* fill the rests with `nop` */ + code_size = sizeof(long); + /* setup registers */ + regs.eip = injector->code_addr; + regs.eax = syscall_number; + regs.ebx = arg1; + regs.ecx = arg2; + regs.edx = arg3; + regs.esi = arg4; + regs.edi = arg5; + regs.ebp = arg6; + reg_return = ®s.eax; + break; +#endif +#if defined(__aarch64__) + case ARCH_ARM64: + /* setup instructions */ + code.u32[0] = 0xd4000001; /* svc #0 */ + code.u32[1] = BREAKINST_ARM64; + code_size = 2 * 4; + /* setup registers */ + regs.pc = injector->code_addr; + regs.regs[8] = syscall_number; + regs.regs[0] = arg1; + regs.regs[1] = arg2; + regs.regs[2] = arg3; + regs.regs[3] = arg4; + regs.regs[4] = arg5; + regs.regs[5] = arg6; + reg_return = ®s.regs[0]; + break; +#endif +#if defined(__aarch64__) || defined(__arm__) + case ARCH_ARM_EABI_THUMB: + /* setup instructions */ + code.u16[0] = 0xdf00; /* svc #0 */ + code.u16[1] = BREAKINST_THUMB; +#ifdef __LP64__ + code.u16[2] = 0x46c0; /* nop (mov r8, r8) */ + code.u16[3] = 0x46c0; /* nop (mov r8, r8) */ +#endif + code_size = sizeof(long); + /* setup registers */ + uregs[16] |= THUMB_MODE_BIT; + uregs[15] = injector->code_addr; + uregs[7] = syscall_number; + uregs[0] = arg1; + uregs[1] = arg2; + uregs[2] = arg3; + uregs[3] = arg4; + uregs[4] = arg5; + uregs[5] = arg6; + reg32_return = &uregs[0]; + break; + case ARCH_ARM_EABI: + /* setup instructions */ + code.u32[0] = 0xef000000; /* svc #0 */ + code.u32[1] = BREAKINST_ARM; + code_size = 2 * 4; + /* setup registers */ + uregs[16] &= ~THUMB_MODE_BIT; + uregs[15] = injector->code_addr; + uregs[7] = syscall_number; + uregs[0] = arg1; + uregs[1] = arg2; + uregs[2] = arg3; + uregs[3] = arg4; + uregs[4] = arg5; + uregs[5] = arg6; + reg32_return = &uregs[0]; + break; +#endif +#if defined(__mips__) + case ARCH_MIPS_64: + case ARCH_MIPS_N32: + case ARCH_MIPS_O32: + /* setup instructions */ + if (syscall_number > 0xffff) { + injector__set_errmsg("too large system call number: %d", syscall_number); + return INJERR_OTHER; + } + code.u32[0] = 0x00000025 | (REG_A3 << 11) | (REG_T4 << 21); /* or $a3, $t4, $zero; move $a3, $t4 */ + code.u32[1] = 0x24000000 | (REG_V0 << 16) | syscall_number; /* addiu $v0, $zero, syscall_number */ + code.u32[2] = 0x0000000c; /* syscall */ + code.u32[3] = 0x0000000d; /* break */ + code_size = 4 * 4; + DEBUG(" Code: %08"PRIx32" %08"PRIx32" %08"PRIx32" %08"PRIx32"\n", + code.u32[0], code.u32[1], code.u32[2], code.u32[3]); + /* setup registers */ + regs.cp0_epc = injector->code_addr; + regs.regs[REG_A0] = arg1; + regs.regs[REG_A1] = arg2; + regs.regs[REG_A2] = arg3; + // Use the combination of "regs.regs[REG_T4] = arg4" and "move $a3, $t4" + // instead of "regs.regs[REG_A3] = arg4". I don't know why the latter + // doesn't work. + regs.regs[REG_T4] = arg4; + if (injector->arch != ARCH_MIPS_O32) { + /* ARCH_MIPS_64 or ARCH_MIPS_N32 */ + regs.regs[REG_A4] = arg5; + regs.regs[REG_A5] = arg6; + } else { + /* ARCH_MIPS_O32 */ + regs.regs[REG_SP] -= 32; + PTRACE_OR_RETURN(PTRACE_POKETEXT, injector, regs.regs[REG_SP] + 16, arg5); + PTRACE_OR_RETURN(PTRACE_POKETEXT, injector, regs.regs[REG_SP] + 20, arg6); + } + break; +#endif +#if defined(__powerpc__) +#ifdef __LP64__ + case ARCH_POWERPC_64: +#endif + case ARCH_POWERPC: + /* setup instructions */ + code.u32[0] = 0x44000002; /* sc */ + code.u32[1] = 0x7fe00008; /* trap */ + code_size = 2 * 4; + /* setup registers */ + regs.nip = injector->code_addr; + regs.gpr[PT_R0] = syscall_number; + regs.gpr[PT_R3] = arg1; + regs.gpr[PT_R4] = arg2; + regs.gpr[PT_R5] = arg3; + regs.gpr[PT_R6] = arg4; + regs.gpr[PT_R7] = arg5; + regs.gpr[PT_R8] = arg6; + break; +#endif +#if defined(__riscv) +#ifdef __LP64__ + case ARCH_RISCV_64: +#endif + case ARCH_RISCV_32: + /* setup instructions */ + code.u32[0] = 0x00000073; /* ecall */ + code.u32[1] = 0x00100073; /* ebreak */ + code_size = 2 * 4; + DEBUG(" Code: %08"PRIx32" %08"PRIx32"\n", + code.u32[0], code.u32[1]); + /* setup registers */ + regs.pc = injector->code_addr; + regs.a0 = arg1; + regs.a1 = arg2; + regs.a2 = arg3; + regs.a3 = arg4; + regs.a4 = arg5; + regs.a5 = arg6; + regs.a7 = syscall_number; + reg_return = ®s.a0; + break; +#endif + default: + injector__set_errmsg("Unexpected architecture: %s", injector__arch2name(injector->arch)); + return INJERR_UNSUPPORTED_TARGET; + } + + PRINT_REGS(injector, ®s); + rv = kick_then_wait_sigtrap(injector, ®s, &code, code_size); + if (rv != 0) { + return rv; + } + PRINT_REGS(injector, ®s); + + if (retval != NULL) { +#if defined(__mips__) + if (regs.regs[REG_A3] == 0) { + *retval = (intptr_t)regs.regs[REG_V0]; + } else { + errno = (int)regs.regs[REG_V0]; + *retval = -1; + } +#elif defined(__powerpc__) + /* https://github.com/strace/strace/blob/v5.19/src/linux/powerpc/get_error.c#L21-L26 */ + if (regs.ccr & 0x10000000) { + errno = (int)regs.gpr[PT_R3]; + *retval = -1; + } else { + *retval = (intptr_t)regs.gpr[PT_R3]; + } +#else +#if defined(__aarch64__) + if (reg32_return != NULL) { + if (*reg32_return <= -4096u) { + *retval = (intptr_t)*reg32_return; + } else { + errno = -((int)*reg32_return); + *retval = -1; + } + } else { +#endif + if ((unsigned long)*reg_return <= -4096ul) { + *retval = (intptr_t)*reg_return; + } else { + errno = -((int)*reg_return); + *retval = -1; + } +#if defined(__aarch64__) + } +#endif +#endif /* defined(__mips__) */ + } + return 0; +} + +/* + * Call the function at the specified address in the target process. + * + * The arguments after function_addr must be integer types and + * the size must not be greater than the size of long. + */ +int injector__call_function(const injector_t *injector, intptr_t *retval, long function_addr, ...) +{ + va_list ap; + int rv; + va_start(ap, function_addr); + rv = injector__call_function_va_list(injector, retval, function_addr, ap); + va_end(ap); + return rv; +} + +/* + * Call the function at the specified address in the target process. + * + * The arguments after function_addr must be integer types and + * the size must not be greater than the size of long. + */ +int injector__call_function_va_list(const injector_t *injector, intptr_t *retval, long function_addr, va_list ap) +{ + struct user_regs_struct regs = injector->regs; + code_t code; + size_t code_size; + long arg1, arg2, arg3, arg4, arg5, arg6; + int rv; + user_reg_t *reg_return = NULL; +#if defined(__aarch64__) + uint32_t *reg32_return = NULL; + uint32_t *uregs = (uint32_t *)®s; +#endif + + arg1 = va_arg(ap, long); + arg2 = va_arg(ap, long); + arg3 = va_arg(ap, long); + arg4 = va_arg(ap, long); + arg5 = va_arg(ap, long); + arg6 = va_arg(ap, long); + + DEBUG("injector__call_function:\n"); + DEBUG(" args: %lx, %lx, %lx, %lx, %lx, %lx, %lx\n", function_addr, arg1, arg2, arg3, arg4, arg5, arg6); + + switch (injector->arch) { +#if defined(__x86_64__) && defined(__LP64__) + case ARCH_X86_64: + case ARCH_X86_64_X32: + /* setup instructions */ + code.u8[0] = 0xff; + code.u8[1] = 0xd0; /* ff d0 : callq *%rax */ + code.u8[2] = 0xcc; /* cc : int3 */ + memset(&code.u8[3], 0x90, sizeof(long) - 3); /* fill the rests with `nop` */ + code_size = sizeof(long); + /* setup registers */ + regs.rip = injector->code_addr; + regs.rbp = injector->stack + injector->stack_size - 16; + /* rsp must be aligned to a 16-byte boundary. */ + regs.rsp = injector->stack + injector->stack_size - (2 * 16); + regs.rax = function_addr; + regs.rdi = arg1; + regs.rsi = arg2; + regs.rdx = arg3; + regs.rcx = arg4; + regs.r8 = arg5; + regs.r9 = arg6; + reg_return = ®s.rax; + break; +#endif +#if defined(__x86_64__) || defined(__i386__) + case ARCH_I386: + /* setup instructions */ + code.u8[0] = 0xff; + code.u8[1] = 0xd0; /* ff d0 : call *%eax */ + code.u8[2] = 0xcc; /* cc : int3 */ + memset(&code.u8[3], 0x90, sizeof(long) - 3); /* fill the rests with `nop` */ + code_size = sizeof(long); + /* setup registers */ + regs.eip = injector->code_addr; + regs.ebp = injector->stack + injector->stack_size - 16; + /* esp should be at 16-byte boundary after call instruction.*/ + regs.esp = injector->stack + injector->stack_size - (3 * 16) + 4; + regs.eax = function_addr; + PTRACE_OR_RETURN(PTRACE_POKETEXT, injector, regs.esp + 0, arg1); + PTRACE_OR_RETURN(PTRACE_POKETEXT, injector, regs.esp + 4, arg2); + PTRACE_OR_RETURN(PTRACE_POKETEXT, injector, regs.esp + 8, arg3); + PTRACE_OR_RETURN(PTRACE_POKETEXT, injector, regs.esp + 12, arg4); + PTRACE_OR_RETURN(PTRACE_POKETEXT, injector, regs.esp + 16, arg5); + PTRACE_OR_RETURN(PTRACE_POKETEXT, injector, regs.esp + 20, arg6); + reg_return = ®s.eax; + break; +#endif +#if defined(__aarch64__) + case ARCH_ARM64: + /* setup instructions */ + code.u32[0] = 0xd63f00c0; /* blr x6 */ + code.u32[1] = BREAKINST_ARM64; + code_size = 2 * 4; + /* setup registers */ + regs.pc = injector->code_addr; + regs.sp = injector->stack + injector->stack_size - 16; + regs.regs[6] = function_addr; + regs.regs[0] = arg1; + regs.regs[1] = arg2; + regs.regs[2] = arg3; + regs.regs[3] = arg4; + regs.regs[4] = arg5; + regs.regs[5] = arg6; + reg_return = ®s.regs[0]; + break; +#endif +#if defined(__aarch64__) || defined(__arm__) + case ARCH_ARM_EABI_THUMB: + /* setup instructions */ + code.u16[0] = 0x47a0; /* blx r4 */ + code.u16[1] = BREAKINST_THUMB; +#ifdef __LP64__ + code.u16[2] = 0x46c0; /* nop (mov r8, r8) */ + code.u16[3] = 0x46c0; /* nop (mov r8, r8) */ +#endif + code_size = sizeof(long); + /* setup registers */ + uregs[16] |= THUMB_MODE_BIT; + uregs[15] = injector->code_addr; + uregs[13] = injector->stack + injector->stack_size - 16; + uregs[4] = function_addr; + uregs[0] = arg1; + uregs[1] = arg2; + uregs[2] = arg3; + uregs[3] = arg4; + PTRACE_OR_RETURN(PTRACE_POKETEXT, injector, uregs[13] + 0, arg5); + PTRACE_OR_RETURN(PTRACE_POKETEXT, injector, uregs[13] + 4, arg6); + reg32_return = &uregs[0]; + break; + case ARCH_ARM_EABI: + /* setup instructions */ + code.u32[0] = 0xe12fff34; /* blx r4 */ + code.u32[1] = BREAKINST_ARM; + code_size = 2 * 4; + /* setup registers */ + uregs[16] &= ~THUMB_MODE_BIT; + uregs[15] = injector->code_addr; + uregs[13] = injector->stack + injector->stack_size - 16; + uregs[4] = function_addr; + uregs[0] = arg1; + uregs[1] = arg2; + uregs[2] = arg3; + uregs[3] = arg4; + PTRACE_OR_RETURN(PTRACE_POKETEXT, injector, uregs[13] + 0, arg5); + PTRACE_OR_RETURN(PTRACE_POKETEXT, injector, uregs[13] + 4, arg6); + reg32_return = &uregs[0]; + break; +#endif +#if defined(__mips__) + case ARCH_MIPS_64: + case ARCH_MIPS_N32: + case ARCH_MIPS_O32: + /* setup instructions */ + code.u32[0] = 0x00000009 | (REG_RA << 11) | (REG_T9 << 21); /* jalr $t9; */ + code.u32[1] = 0x00000025 | (REG_A3 << 11) | (REG_T4 << 21); /* or $a3, $t4, $zero; in a delay slot */ + code.u32[2] = 0x0000000d; /* break */ + code.u32[3] = 0x00000000; /* nop */ + code_size = 4 * 4; + DEBUG(" Code: %08"PRIx32" %08"PRIx32" %08"PRIx32" %08"PRIx32"\n", + code.u32[0], code.u32[1], code.u32[2], code.u32[3]); + /* setup registers */ + regs.cp0_epc = injector->code_addr; + regs.regs[REG_FP] = injector->stack + injector->stack_size - 32; + regs.regs[REG_SP] = injector->stack + injector->stack_size - 64; + regs.regs[REG_T9] = function_addr; + regs.regs[REG_A0] = arg1; + regs.regs[REG_A1] = arg2; + regs.regs[REG_A2] = arg3; + regs.regs[REG_T4] = arg4; + if (injector->arch != ARCH_MIPS_O32) { + /* ARCH_MIPS_64 or ARCH_MIPS_N32 */ + regs.regs[REG_A4] = arg5; + regs.regs[REG_A5] = arg6; + } else { + /* ARCH_MIPS_O32 */ + PTRACE_OR_RETURN(PTRACE_POKETEXT, injector, regs.regs[REG_SP] + 16, arg5); + PTRACE_OR_RETURN(PTRACE_POKETEXT, injector, regs.regs[REG_SP] + 20, arg6); + } + reg_return = ®s.regs[REG_V0]; + break; +#endif +#if defined(__powerpc__) +#ifdef __LP64__ + case ARCH_POWERPC_64: +#endif + case ARCH_POWERPC: + /* setup instructions */ + code.u32[0] = 0x4e800421; /* bctrl */ + code.u32[1] = 0x7fe00008; /* trap */ + code_size = 2 * 4; + /* setup registers */ + regs.nip = injector->code_addr; + regs.gpr[PT_R1] = injector->stack + injector->stack_size - 256; + regs.ctr = function_addr; + regs.gpr[PT_R3] = arg1; + regs.gpr[PT_R4] = arg2; + regs.gpr[PT_R5] = arg3; + regs.gpr[PT_R6] = arg4; + regs.gpr[PT_R7] = arg5; + regs.gpr[PT_R8] = arg6; + regs.gpr[PT_R12] = function_addr; + reg_return = ®s.gpr[PT_R3]; + break; +#endif +#if defined(__riscv) +#ifdef __LP64__ + case ARCH_RISCV_64: +#endif + case ARCH_RISCV_32: + /* setup instructions */ + code.u32[0] = 0x00000067 | (REG_RA << 7) | (REG_T1 << 15) ; /* jalr t1 */ + code.u32[1] = 0x00100073; /* ebreak */ + code_size = 2 * 4; + DEBUG(" Code: %08"PRIx32" %08"PRIx32"\n", + code.u32[0], code.u32[1]); + /* setup registers */ + regs.pc = injector->code_addr; + regs.sp = injector->stack + injector->stack_size - 16; + regs.t1 = function_addr; + regs.a0 = arg1; + regs.a1 = arg2; + regs.a2 = arg3; + regs.a3 = arg4; + regs.a4 = arg5; + regs.a5 = arg6; + reg_return = ®s.a0; + break; +#endif + default: + injector__set_errmsg("Unexpected architecture: %s", injector__arch2name(injector->arch)); + return -1; + } + + PRINT_REGS(injector, ®s); + rv = kick_then_wait_sigtrap(injector, ®s, &code, code_size); + if (rv != 0) { + return rv; + } + PRINT_REGS(injector, ®s); + + if (retval != NULL) { +#if defined(__aarch64__) + if (reg32_return != NULL) { + *retval = (long)*reg32_return; + } else { + *retval = (long)*reg_return; + } +#else + *retval = (long)*reg_return; +#endif + } + return 0; +} + +static int kick_then_wait_sigtrap(const injector_t *injector, struct user_regs_struct *regs, code_t *code, size_t code_size) +{ + int status; + int rv; + + rv = injector__set_regs(injector, regs); + if (rv != 0) { + return rv; + } + rv = injector__write(injector, injector->code_addr, code, code_size); + if (rv != 0) { + injector__set_regs(injector, &injector->regs); + return rv; + } + + rv = injector__continue(injector); + if (rv != 0) { + goto cleanup; + } + while (1) { + pid_t pid = waitpid(injector->pid, &status, 0); + if (pid == -1) { + if (errno == EINTR) { + continue; + } + injector__set_errmsg("waitpid error: %s", strerror(errno)); + rv = INJERR_WAIT_TRACEE; + goto cleanup; + } + if (WIFSTOPPED(status)) { +#if defined(PT_GETSIGINFO) + siginfo_t si = {0,}; +#endif + switch (WSTOPSIG(status)) { + case SIGTRAP: + goto got_sigtrap; + case SIGSTOP: + rv = injector__continue(injector); + if (rv != 0) { + goto cleanup; + } + break; +#if defined(PT_GETSIGINFO) + case SIGSYS: + PTRACE_OR_RETURN(PT_GETSIGINFO, injector, 0, (long)&si); + if (si.si_signo == SIGSYS && si.si_code == 1) { + injector__set_errmsg("Got SIGSYS. System call %d at address %p might be blocked by seccomp.", + si.si_syscall, (void*)si.si_call_addr); + rv = INJERR_OTHER; + goto cleanup; + } + // FALL THROUGH */ +#endif + default: + injector__set_errmsg("The target process unexpectedly stopped by signal %d.", WSTOPSIG(status)); + rv = INJERR_OTHER; + goto cleanup; + } + } else if (WIFEXITED(status)) { + injector__set_errmsg("The target process unexpectedly terminated with exit code %d.", WEXITSTATUS(status)); + rv = INJERR_OTHER; + goto cleanup; + } else if (WIFSIGNALED(status)) { + injector__set_errmsg("The target process unexpectedly terminated by signal %d.", WTERMSIG(status)); + rv = INJERR_OTHER; + goto cleanup; + } else { + /* never reach here */ + injector__set_errmsg("Unexpected waitpid status: 0x%x", status); + rv = INJERR_OTHER; + goto cleanup; + } + } +got_sigtrap: + /* success */ + rv = injector__get_regs(injector, regs); +cleanup: + injector__set_regs(injector, &injector->regs); + injector__write(injector, injector->code_addr, &injector->backup_code, code_size); + return rv; +} diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/linux/shellcode.S b/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/linux/shellcode.S new file mode 100644 index 0000000..6704606 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/linux/shellcode.S @@ -0,0 +1,70 @@ +#if defined(__x86_64__) +#define handle_offset 0 +#define dlopen_addr_offset 8 +#define dlerror_addr_offset 16 +#define dlflags_offset 24 +#define file_path_offset 28 +#define page_size 4096 + .text + .global injector_shellcode + .hidden injector_shellcode + .type injector_shellcode, @function + // void *injector_shellcode(injector_shellcode_arg_t *arg) { +injector_shellcode: + // // prolog + pushq %rbx + movq %rdi, %rbx + // int dlflags = arg->dlflags; + movl dlflags_offset(%rbx), %esi + // const char *file_path = arg->file_path; + leaq file_path_offset(%rbx), %rdi + // void *handle = dlopen(file_path, dlflags); + call *dlopen_addr_offset(%rbx) + // arg->handle = handle; + movq %rax, handle_offset(%rbx) + // arg->file_path[0] = '\0'; + movb $0, file_path_offset(%rbx) + // if (handle != NULL) return; + test %rax, %rax + jnz .exit + // if (arg->dlerror_addr == 0) return; + cmpq $0, dlerror_addr_offset(%rbx) + je .exit + // char *errmsg = dlerror(); + call *dlerror_addr_offset(%rbx) + // if (errmsg == NULL) return; + test %rax, %rax + jz .exit + // char *dest = arg->file_path + leaq file_path_offset(%rbx), %rdi + // char *end = (char*)arg + page_size; + leaq page_size(%rbx), %rcx +.loop: + // char c = *(errmsg++); + movb (%rax), %dl + addq $1, %rax + // *(dest++) = c; + movb %dl, (%rdi) + addq $1, %rdi + // if (c == 0) return; + testb %dl, %dl + jz .exit + // if (dest < end) goto loop; + cmpq %rdi, %rcx + ja .loop +.exit: + // // epilog + popq %rbx + ret + // } + .size injector_shellcode, . - injector_shellcode + + .balign 4 + .global injector_shellcode_size + .hidden injector_shellcode_size + .type injector_shellcode_size, @object + .size injector_shellcode_size, 4 +injector_shellcode_size: + // distance from injector_shellcode to current. + .int . - injector_shellcode +#endif diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/linux/util.c b/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/linux/util.c new file mode 100644 index 0000000..3146743 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/linux/util.c @@ -0,0 +1,82 @@ +/* -*- indent-tabs-mode: nil -*- + * + * injector - Library for injecting a shared library into a Linux process + * + * URL: https://github.com/kubo/injector + * + * ------------------------------------------------------ + * + * Copyright (C) 2018 Kubo Takehiro + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include +#include +#include "injector_internal.h" + +char injector__errmsg[512]; +char injector__errmsg_is_set; + +void injector__set_errmsg(const char *format, ...) +{ + va_list ap; + int rv; + + /* prevent the error message from being overwritten. */ + if (injector__errmsg_is_set) { + return; + } + injector__errmsg_is_set = 1; + + va_start(ap, format); + rv = vsnprintf(injector__errmsg, sizeof(injector__errmsg), format, ap); + va_end(ap); + if (rv == -1 || rv >= sizeof(injector__errmsg)) { + injector__errmsg[sizeof(injector__errmsg) - 1] = '\0'; + } +} + +const char *injector__arch2name(arch_t arch) +{ + switch (arch) { + case ARCH_X86_64: + return "x86_64"; + case ARCH_X86_64_X32: + return "x86_64 x32-ABI"; + case ARCH_I386: + return "i386"; + case ARCH_ARM64: + return "ARM64"; + case ARCH_ARM_EABI_THUMB: + return "ARM EABI thumb"; + case ARCH_ARM_EABI: + return "ARM EABI"; + case ARCH_MIPS_64: + return "MIPS 64"; + case ARCH_MIPS_N32: + return "MIPS N32 ABI"; + case ARCH_MIPS_O32: + return "MIPS O32 ABI"; + case ARCH_POWERPC_64: + return "PowerPC 64-bit"; + case ARCH_POWERPC: + return "PowerPC"; + case ARCH_RISCV_64: + return "RISC-V 64"; + case ARCH_RISCV_32: + return "RISC-V 32"; + } + return "?"; +} diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/macos/exc_handler.c b/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/macos/exc_handler.c new file mode 100644 index 0000000..1dd10d7 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/macos/exc_handler.c @@ -0,0 +1,314 @@ +/* -*- indent-tabs-mode: nil -*- + * + * injector - Library for injecting a shared library into a Linux process + * + * URL: https://github.com/kubo/injector + * + * ------------------------------------------------------ + * + * Copyright (C) 2022 TheOiseth + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "injector_internal.h" +#include +#include +#include +#include +#include +#define HANDLE_EXC EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | EXC_MASK_SOFTWARE | EXC_MASK_BREAKPOINT | EXC_MASK_CRASH | EXC_MASK_CORPSE_NOTIFY +boolean_t mach_exc_server(mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP); +int injector__create_exc_handler(injector_t *injector) { + mach_port_name_t exc_port = 0; + mach_msg_type_number_t exception_types_count; + int rv; + injector->exc_port = 0; + injector->saved_exception_types_count = 0; + rv = task_get_exception_ports(injector->remote_task, + EXC_MASK_ALL, + injector->saved_masks, + &exception_types_count, + injector->saved_ports, + injector->saved_behaviors, + injector->saved_flavors); + + injector->saved_exception_types_count = exception_types_count; + if(rv != 0){ + injector__set_errmsg("%s error : %s", "EXC_GET_PORTS", mach_error_string(rv)); + return INJERR_OTHER; + } + + rv = mach_port_allocate(mach_task_self(), + MACH_PORT_RIGHT_RECEIVE, + &exc_port); + injector->exc_port = exc_port; + if(rv != 0){ + injector__set_errmsg("%s error : %s", "EXC_PORT_ALLOCATE", mach_error_string(rv)); + rv = INJERR_OTHER; + goto cleanup; + } + rv = mach_port_insert_right(mach_task_self(), + exc_port, + exc_port, + MACH_MSG_TYPE_MAKE_SEND); + if(rv != 0){ + injector__set_errmsg("%s error : %s", "EXC_INSERT_RIGHTS", mach_error_string(rv)); + rv = INJERR_OTHER; + goto cleanup; + } + + rv = task_set_exception_ports(injector->remote_task, + HANDLE_EXC, + exc_port, + EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, + THREAD_STATE_NONE); + if(rv != 0){ + injector__set_errmsg("%s error : %s", "EXC_SET_PORTS", mach_error_string(rv)); + rv = INJERR_OTHER; + goto cleanup; + } + + return 0; +cleanup: + injector__release_exc_handler(injector); +return rv; +} +int injector__handle_exc(injector_t *injector) { + char req[128], rpl[128]; + mach_msg_header_with_injector *mmhwi; + int rv; + + mmhwi = (mach_msg_header_with_injector*)req; + mmhwi->injector = injector; + rv = mach_msg((mach_msg_header_t *)req, MACH_RCV_MSG, 0, sizeof(req), injector->exc_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); + if (rv != KERN_SUCCESS) { + injector__set_errmsg("%s error : %s", "EXC_RECV_MACH_MSG", mach_error_string(rv)); + return INJERR_OTHER; + } +/* suspend all threads in the process after an exception was received */ + + task_suspend(injector->remote_task); + + boolean_t message_parsed_correctly = mach_exc_server((mach_msg_header_t *)req, (mach_msg_header_t *)rpl); + if (! message_parsed_correctly) { + + size_t parse_exc = ((mig_reply_error_t *)rpl)->RetCode; + if(parse_exc != 0 ){ + injector__set_errmsg("%s error : %s", "mach_exc_server", mach_error_string(parse_exc)); + } + } + task_resume(injector->remote_task); + mach_msg_size_t send_sz = ((mach_msg_header_t *)rpl)->msgh_size; + + rv = mach_msg((mach_msg_header_t *)rpl, MACH_SEND_MSG, send_sz, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); + if (rv != KERN_SUCCESS) { + injector__set_errmsg("%s error : %s", "EXC_SEND_MACH_MSG", mach_error_string(rv)); + return INJERR_OTHER; + } + return 0; +} + +static bool isSIGSTOP(exception_type_t exception_type, mach_exception_data_t codes){ + return exception_type == EXC_SOFTWARE && codes[0] == EXC_SOFT_SIGNAL && codes[1] == SIGSTOP; +} + +kern_return_t catch_mach_exception_raise( + mach_port_t exception_port, + mach_port_t thread_port, + mach_port_t task_port, + exception_type_t exception_type, + mach_exception_data_t codes, + mach_msg_type_number_t num_codes, + injector_t *injector) +{ + injector->handle_err = 0; + bool bad_exc = true; + int rv; + switch (injector->handle_action) + { + case STOP_CONTINUE: + if(isSIGSTOP(exception_type, codes)){ + bad_exc = false; + injector__ptrace_update(injector, thread_port); + } + break; + case STOP_DETACH: + if(isSIGSTOP(exception_type, codes)){ + bad_exc = false; + injector__ptrace_detach(injector); + } + break; + case TRAP_GETREGS: + if(exception_type == EXC_BREAKPOINT){ + bad_exc = false; +#if defined(__arm64__) || defined(__aarch64__) + mach_msg_type_number_t thread_state_count = ARM_THREAD_STATE64_COUNT; + arm_thread_state64_t state; + rv = thread_get_state(thread_port, ARM_THREAD_STATE64, (thread_state_t)&state, &thread_state_count); + if (rv == KERN_SUCCESS) { + injector->retval = state.__x[0]; + } else { + injector__set_errmsg("%s error : %s", "GET_THREAD_STATE", mach_error_string(rv)); + injector->handle_err = INJERR_ERROR_IN_TARGET; + } +#else + mach_msg_type_number_t thread_state_count = x86_THREAD_STATE64_COUNT; + x86_thread_state64_t state; + rv = thread_get_state(thread_port, x86_THREAD_STATE64, (thread_state_t)&state, &thread_state_count); + if (rv == KERN_SUCCESS) { + injector->retval = state.__rax; + } else { + injector__set_errmsg("%s error : %s", "GET_THREAD_STATE", mach_error_string(rv)); + injector->handle_err = INJERR_ERROR_IN_TARGET; + } +#endif + if (injector->mach_thread != 0){ + rv = thread_terminate(injector->mach_thread); + injector->mach_thread = 0; + } + rv = thread_suspend(thread_port); + if(rv != KERN_SUCCESS){ + injector__set_errmsg("%s error : %s", "THREAD_SUSPEND", mach_error_string(rv)); + injector->handle_err = INJERR_ERROR_IN_TARGET; + } + //we don't need to continue since we already called task_resume and mach_msg later + //rv = injector__ptrace_continue(injector); + + } + break; + case TRAP_SETREGS: + if(exception_type == EXC_BREAKPOINT){ + bad_exc = false; + bool thread_init = false; + if(thread_port != injector->mach_thread){ + thread_init = injector->remote_thread == 0; + injector->remote_thread = thread_port; + } +#if defined(__arm64__) || defined(__aarch64__) + mach_msg_type_number_t thread_state_count = ARM_THREAD_STATE64_COUNT; + arm_thread_state64_t state; + rv = thread_get_state(thread_port, ARM_THREAD_STATE64, (thread_state_t)&state, &thread_state_count); + if (rv != KERN_SUCCESS) { + injector__set_errmsg("%s error : %s", "GET_THREAD_STATE", mach_error_string(rv)); + injector->handle_err = INJERR_ERROR_IN_TARGET; + goto exit; + } + if(thread_init){ + memcpy(&injector->remote_thread_saved_state, &state, sizeof(state)); + injector->state_saved = 1; + } + state.__x[0] = injector->arg1; + state.__x[1] = injector->arg2; + state.__x[2] = injector->arg3; + state.__x[3] = injector->arg4; + state.__x[4] = injector->arg5; + state.__x[5] = injector->arg6; + state.__x[8] = injector->func_addr; + state.__sp = injector->stack; + state.__pc = injector->code2_addr + 4; + rv = thread_set_state(thread_port, ARM_THREAD_STATE64, (thread_state_t)&state, ARM_THREAD_STATE64_COUNT); + if (rv != KERN_SUCCESS) { + injector__set_errmsg("%s error : %s", "SET_THREAD_STATE", mach_error_string(rv)); + injector->handle_err = INJERR_ERROR_IN_TARGET; + } +#else + mach_msg_type_number_t thread_state_count = x86_THREAD_STATE64_COUNT; + x86_thread_state64_t state; + + rv = thread_get_state(thread_port, x86_THREAD_STATE64, (thread_state_t)&state, &thread_state_count); + if (rv != KERN_SUCCESS) { + injector__set_errmsg("%s error : %s", "GET_THREAD_STATE", mach_error_string(rv)); + injector->handle_err = INJERR_ERROR_IN_TARGET; + goto exit; + } + if(thread_init){ + memcpy(&injector->remote_thread_saved_state, &state, sizeof(state)); + injector->state_saved = 1; + } + state.__rax = injector->func_addr; + state.__rdi = injector->arg1; + state.__rsi = injector->arg2; + state.__rdx = injector->arg3; + state.__rcx = injector->arg4; + state.__r8 = injector->arg5; + state.__r9 = injector->arg6; + state.__rsp = injector->stack; + state.__rbp = injector->stack; + rv = thread_set_state(thread_port, x86_THREAD_STATE64, (thread_state_t)&state, x86_THREAD_STATE64_COUNT); + if (rv != KERN_SUCCESS) { + injector__set_errmsg("%s error : %s", "SET_THREAD_STATE", mach_error_string(rv)); + injector->handle_err = INJERR_ERROR_IN_TARGET; + } +#endif + //we don't need to continue since we already called task_resume and mach_msg later + //rv = injector__ptrace_continue(injector); + + } + break; + } + + if(bad_exc){ + if(exception_type == EXC_SOFTWARE){ + injector__set_errmsg("The target process got an unexpected signal %i.", codes[1]); + } else { + injector__set_errmsg("Got unhandled exception %i.", exception_type); + } + injector->handle_err = INJERR_OTHER; + } +exit: + return KERN_SUCCESS; +} + +kern_return_t catch_mach_exception_raise_state( + mach_port_t exception_port, + exception_type_t exception, + const mach_exception_data_t code, + mach_msg_type_number_t codeCnt, + int *flavor, + const thread_state_t old_state, + mach_msg_type_number_t old_stateCnt, + thread_state_t new_state, + mach_msg_type_number_t *new_stateCnt) +{ + return MACH_RCV_INVALID_TYPE; +} + +kern_return_t catch_mach_exception_raise_state_identity( + mach_port_t exception_port, + mach_port_t thread, + mach_port_t task, + exception_type_t exception, + mach_exception_data_t code, + mach_msg_type_number_t codeCnt, + int *flavor, + thread_state_t old_state, + mach_msg_type_number_t old_stateCnt, + thread_state_t new_state, + mach_msg_type_number_t *new_stateCnt) +{ + return MACH_RCV_INVALID_TYPE; +} + +int injector__release_exc_handler(injector_t *injector) { + for (int i = 0; i < injector->saved_exception_types_count; i++) { + task_set_exception_ports(injector->remote_task, injector->saved_masks[i], injector->saved_ports[i], injector->saved_behaviors[i], injector->saved_flavors[i]); + } + injector->saved_exception_types_count = 0; + if (injector->exc_port != 0){ + mach_port_deallocate(mach_task_self(), injector->exc_port); + injector->exc_port = 0; + } + return 0; +} \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/macos/injector.c b/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/macos/injector.c new file mode 100644 index 0000000..3711022 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/macos/injector.c @@ -0,0 +1,295 @@ +/* -*- indent-tabs-mode: nil -*- + * + * injector - Library for injecting a shared library into a Linux process + * + * URL: https://github.com/kubo/injector + * + * ------------------------------------------------------ + * + * Copyright (C) 2022 TheOiseth + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "injector_internal.h" +#include +#include +#include +#include +#include + +#define STACK_SIZE 2 * 1024 * 1024 +#define CODE_SIZE 512 +int injector_attach(injector_t **injector_out, pid_t pid) +{ + injector_t *injector; + arch_t self_arch, target_arch; + int rv = 0; + + injector__errmsg_is_set = 0; + + injector = calloc(1, sizeof(injector_t)); + if (injector == NULL) { + injector__set_errmsg("malloc error: %s", strerror(errno)); + return INJERR_NO_MEMORY; + } + injector->pid = pid; + rv = injector__get_process_arch(getpid(), &self_arch); + if (rv != 0) { + goto error_exit; + } + rv = injector__get_process_arch(pid, &target_arch); + if (rv != 0) { + goto error_exit; + } + arch_t sys_arch = injector__get_system_arch(); + + if(self_arch != ARCH_UNKNOWN && target_arch != ARCH_UNKNOWN){ + if(self_arch != target_arch){ + injector__set_errmsg("%s target process isn't supported by %s process.", injector__arch2name(target_arch), injector__arch2name(self_arch)); + rv = INJERR_UNSUPPORTED_TARGET; + goto error_exit; + } + if(sys_arch == ARCH_ARM64 && self_arch != ARCH_ARM64){ + injector__set_errmsg("%s target process isn't supported by %s process on ARM64 machine.", injector__arch2name(target_arch), injector__arch2name(self_arch)); + rv = INJERR_UNSUPPORTED_TARGET; + goto error_exit; + } + } + + rv = injector__task_pid(injector); + if (rv != 0) { + goto error_exit; + } + injector->attached = 1; + + rv = injector__create_exc_handler(injector); + + if (rv != 0) { + goto error_exit; + } + rv = injector__ptrace_attach(injector); + if(rv != 0){ + return rv; + } + injector->handle_action = STOP_CONTINUE; + injector->handle_err = 0; + do{ + injector__handle_exc(injector); + } while(injector->handle_err != 0); + + injector->ptrace_attached = 1; + + injector->text_size = sysconf(_SC_PAGESIZE); + injector->stack_size = STACK_SIZE; + injector->code_size = CODE_SIZE; + + size_t alloc_size = injector->text_size + injector->stack_size; + + mach_vm_address_t addr = (vm_address_t)NULL; + rv = injector__allocate(injector, &addr, alloc_size, VM_FLAGS_ANYWHERE); + if (rv != 0) { + goto error_exit; + } + + mach_vm_address_t code_addr = (vm_address_t)NULL; + rv = injector__allocate(injector, &code_addr, CODE_SIZE, VM_FLAGS_ANYWHERE); + if (rv != 0) { + goto error_exit; + } + + injector->allocated = 1; + injector->text = (size_t)addr; + injector->stack = injector->text + injector->text_size + injector->stack_size / 2; + injector->stack &= 0xFFFFFFFFFFFFFFF0; //alignment + injector->code_addr = (size_t)code_addr; + + rv = injector__protect(injector, addr, alloc_size, FALSE, VM_PROT_READ | VM_PROT_WRITE); + if (rv != 0) { + goto error_exit; + } + + rv = injector__protect(injector, code_addr, CODE_SIZE, FALSE, VM_PROT_READ | VM_PROT_WRITE); + if (rv != 0) { + goto error_exit; + } + *injector_out = injector; + return 0; +error_exit: + injector_detach(injector); + return rv; +} + +int injector_inject(injector_t *injector, const char *path, void **handle) +{ + char abspath[PATH_MAX]; + int dlflags = RTLD_LAZY; + size_t len; + int rv; + long retval; + injector__errmsg_is_set = 0; + if (realpath(path, abspath) == NULL) { + injector__set_errmsg("failed to get the full path of '%s': %s", + path, strerror(errno)); + return INJERR_FILE_NOT_FOUND; + } + len = strlen(abspath) + 1; + if (len > injector->text_size) { + injector__set_errmsg("too long file path: %s", path); + return INJERR_FILE_NOT_FOUND; + } + + rv = injector__write(injector, injector->text, abspath, len); + if (rv != 0) { + return rv; + } + + rv = injector__call_function(injector, &retval, (long)dlopen, injector->text, dlflags); + if (rv != 0) { + return rv; + } + if (retval == 0) { + char buf[256 + 1] = {0,}; + rv = injector__call_function(injector, &retval, (long)dlerror); + if (rv == 0 && retval != 0) { + injector__read(injector, retval, buf, sizeof(buf) - 1); + } + + if (buf[0] != '\0') { + injector__set_errmsg("dlopen failed: %s", buf); + } else { + injector__set_errmsg("dlopen failed"); + } + + return INJERR_ERROR_IN_TARGET; + } + if (handle != NULL) { + *handle = (void*)retval; + } + return 0; +} + +int injector_call(injector_t *injector, void *handle, const char* name) +{ + int rv; + long retval; + size_t len = strlen(name) + 1; + + injector__errmsg_is_set = 0; + + if (len > injector->text_size) { + injector__set_errmsg("too long function name: %s", name); + return INJERR_FUNCTION_MISSING; + } + rv = injector__write(injector, injector->text, name, len); + if (rv != 0) { + return rv; + } + rv = injector__call_function(injector, &retval, (long)dlsym, handle, injector->text); + if (retval == 0) { + injector__set_errmsg("function not found: %s", name); + return INJERR_FUNCTION_MISSING; + } + return injector__call_function(injector, &retval, retval); +} + +int injector_uninject(injector_t *injector, void *handle) +{ + int rv; + long retval; + + injector__errmsg_is_set = 0; + + rv = injector__call_function(injector, &retval, (long)dlclose, handle); + if (rv != 0) { + return rv; + } + if (retval != 0) { + injector__set_errmsg("dlclose failed"); + return INJERR_ERROR_IN_TARGET; + } + return 0; +} + +int injector_detach(injector_t *injector) +{ + int rv = 0; + injector__errmsg_is_set = 0; + if (injector->remote_thread != 0) { + //For some reasons on MacOS ARM64 (tested on 12.0.1) thread_terminate() returns unknown error, so let it end by itslef + if(injector->state_saved){ +#if defined(__arm64__) || defined(__aarch64__) + injector->remote_thread_saved_state.__pc = injector->code2_addr + 12; + rv = thread_set_state(injector->remote_thread, ARM_THREAD_STATE64, (thread_state_t)&injector->remote_thread_saved_state, ARM_THREAD_STATE64_COUNT); + if (rv != KERN_SUCCESS) { + injector__set_errmsg("%s error : %s", "GET_THREAD_STATE", mach_error_string(rv)); + rv = INJERR_ERROR_IN_TARGET; + } +#else + injector->remote_thread_saved_state.__rip = injector->code2_addr + 4; + rv = thread_set_state(injector->remote_thread, x86_THREAD_STATE64, (thread_state_t)&injector->remote_thread_saved_state, x86_THREAD_STATE64_COUNT); + if (rv != KERN_SUCCESS) { + injector__set_errmsg("%s error : %s", "GET_THREAD_STATE", mach_error_string(rv)); + rv = INJERR_ERROR_IN_TARGET; + } +#endif + rv = thread_resume(injector->remote_thread); + if(rv != 0){ + injector__set_errmsg("Remote thread resume error: %s\n", mach_error_string(rv)); + rv = INJERR_ERROR_IN_TARGET; + } + + //wait thread for end +#if defined(__arm64__) || defined(__aarch64__) + thread_state_flavor_t flavor = ARM_THREAD_STATE64; +#else + thread_state_flavor_t flavor = x86_THREAD_STATE64; +#endif + mach_msg_type_number_t state_count; + int counter = 0; + while(thread_get_state(injector->remote_thread, flavor, (thread_state_t)&injector->remote_thread_saved_state, &state_count) == 0){ + counter++; + usleep(10); + if(counter > 1000){ + break; + } + } + } + } + + if (injector->ptrace_attached) { + injector->handle_action = STOP_DETACH; + kill(injector->pid, SIGSTOP); + injector->handle_err = 0; + do{ + injector__handle_exc(injector); + } while(injector->handle_err != 0); + injector__release_exc_handler(injector); + } + + if (injector->allocated) { + injector__deallocate(injector, injector->text, injector->text_size + injector->stack_size); + injector__deallocate(injector, injector->code_addr, injector->code_size); + } + if (injector->attached) { + mach_port_deallocate(mach_task_self(), injector->remote_task); + } + + free(injector); + return rv; +} +const char *injector_error(void) +{ + return injector__errmsg; +} \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/macos/injector_internal.h b/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/macos/injector_internal.h new file mode 100644 index 0000000..5129db6 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/macos/injector_internal.h @@ -0,0 +1,121 @@ +/* -*- indent-tabs-mode: nil -*- + * + * injector - Library for injecting a shared library into a Linux process + * + * URL: https://github.com/kubo/injector + * + * ------------------------------------------------------ + * + * Copyright (C) 2022 TheOiseth + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "injector.h" +#include +#include +#include +#include +#include + +typedef enum { + STOP_CONTINUE, + STOP_DETACH, + TRAP_SETREGS, + TRAP_GETREGS +} handle_action_t; + +struct injector { + pid_t pid; + uint8_t attached; + uint8_t allocated; + uint8_t ptrace_attached; + uint8_t shellcode_writed; + task_t remote_task; + size_t code_addr; + size_t code2_addr; + size_t code_size; + size_t text; + size_t text_size; + size_t stack; + size_t stack_size; + + thread_act_t mach_thread; + thread_act_t remote_thread; +#if defined(__arm64__) || defined(__aarch64__) + arm_thread_state64_t remote_thread_saved_state; +#else + x86_thread_state64_t remote_thread_saved_state; +#endif + uint8_t state_saved; + long func_addr; + long arg1; + long arg2; + long arg3; + long arg4; + long arg5; + long arg6; + mach_port_name_t exc_port; + exception_mask_t saved_masks[EXC_TYPES_COUNT]; + mach_port_t saved_ports[EXC_TYPES_COUNT]; + exception_behavior_t saved_behaviors[EXC_TYPES_COUNT]; + thread_state_flavor_t saved_flavors[EXC_TYPES_COUNT]; + mach_msg_type_number_t saved_exception_types_count; + handle_action_t handle_action; + long retval; + int handle_err; + +}; + +typedef struct{ + char stub[120]; + injector_t *injector; +} mach_msg_header_with_injector; + +typedef enum { + ARCH_X86_64, + ARCH_I386, + ARCH_ARM64, + ARCH_POWERPC_64, + ARCH_POWERPC, + ARCH_UNKNOWN +} arch_t; + + + +typedef int (*pcfmt_t)(pthread_t* ,pthread_attr_t* ,void *, void*); + +int injector__task_pid(injector_t *injector); +int injector__allocate(const injector_t *injector, mach_vm_address_t *address, mach_vm_size_t size, int flags); +int injector__deallocate(const injector_t *injector, mach_vm_address_t address, mach_vm_size_t size); +int injector__protect(const injector_t *injector, mach_vm_address_t address, mach_vm_size_t size, boolean_t set_maximum, vm_prot_t new_protection); +int injector__write(const injector_t *injector, size_t addr, const void *buf, size_t len); +int injector__read(const injector_t *injector, size_t addr, void *buf, size_t len); +int injector__ptrace_attach(const injector_t *injector); +int injector__ptrace_detach(const injector_t *injector); +int injector__ptrace_continue(const injector_t *injector); +int injector__ptrace_update(const injector_t *injector, long thread_port); + +int injector__create_exc_handler(injector_t *injector); +int injector__release_exc_handler(injector_t *injector); +int injector__handle_exc(injector_t *injector); + +int injector__call_function(injector_t *injector, long *retval, long function_addr, ...); +/* util.c */ +extern char injector__errmsg[]; +extern char injector__errmsg_is_set; +void injector__set_errmsg(const char *format, ...); +const char *injector__arch2name(arch_t arch); +int injector__get_process_arch(pid_t pid, arch_t *arch); +arch_t injector__get_system_arch(); \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/macos/mach.c b/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/macos/mach.c new file mode 100644 index 0000000..3d5f43f --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/macos/mach.c @@ -0,0 +1,112 @@ +/* -*- indent-tabs-mode: nil -*- + * + * injector - Library for injecting a shared library into a Linux process + * + * URL: https://github.com/kubo/injector + * + * ------------------------------------------------------ + * + * Copyright (C) 2022 TheOiseth + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "injector_internal.h" +#include +#include +static int set_mach_error(const char *request_name, int err) +{ + injector__set_errmsg("%s error : %s", request_name, mach_error_string(err)); + switch (err) { + case KERN_INVALID_ADDRESS: + return INJERR_INVALID_MEMORY_AREA; + case KERN_NO_ACCESS: + return INJERR_PERMISSION; + } + return INJERR_OTHER; +} + +static int set_error(const char *request_name) +{ + int err = errno; + injector__set_errmsg("%s error : %s", request_name, strerror(errno)); + switch (err) { + case EFAULT: + return INJERR_INVALID_MEMORY_AREA; + case EPERM: + return INJERR_PERMISSION; + case ESRCH: + return INJERR_NO_PROCESS; + } + return INJERR_OTHER; +} + +int injector__task_pid(injector_t *injector) +{ + int rv = kill(injector->pid, 0); + if(rv != 0){ + return set_error("TASK_FOR_PID"); + } + task_t remote_task; + rv = task_for_pid(mach_task_self(), injector->pid, &remote_task); + + if (rv != KERN_SUCCESS) { + return set_mach_error("TASK_FOR_PID", rv); + } + injector->remote_task = remote_task; + return 0; +} + +int injector__allocate(const injector_t *injector, mach_vm_address_t *address, mach_vm_size_t size, int flags) +{ + int rv = mach_vm_allocate(injector->remote_task, address, size, flags); + if (rv != KERN_SUCCESS) { + return set_mach_error("ALLOCATE", rv); + } + return 0; +} + +int injector__deallocate(const injector_t *injector, mach_vm_address_t address, mach_vm_size_t size){ + int rv = mach_vm_deallocate(injector->remote_task, address, size); + if (rv != KERN_SUCCESS) { + return set_mach_error("DEALLOCATE", rv); + } + return 0; +} + +int injector__protect(const injector_t *injector, mach_vm_address_t address, mach_vm_size_t size, boolean_t set_maximum, vm_prot_t new_protection) +{ + int rv = mach_vm_protect(injector->remote_task, address, size, set_maximum, new_protection); + if (rv != KERN_SUCCESS) { + return set_mach_error("PROTECT", rv); + } + return 0; +} + +int injector__write(const injector_t *injector, size_t addr, const void *buf, size_t len) { + int rv = mach_vm_write(injector->remote_task, addr, (vm_offset_t)buf, len); + if (rv != KERN_SUCCESS) { + return set_mach_error("WRITE", rv); + } + return 0; +} +int injector__read(const injector_t *injector, size_t addr, void *buf, size_t len){ + mach_vm_size_t readed; + int rv = mach_vm_read_overwrite(injector->remote_task, addr, len, (mach_vm_address_t)buf, &readed); + if (rv != KERN_SUCCESS) { + return set_mach_error("READ", rv); + } + return 0; +} + diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/macos/mach_exc.h b/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/macos/mach_exc.h new file mode 100644 index 0000000..b4123f9 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/macos/mach_exc.h @@ -0,0 +1,330 @@ +/* -*- indent-tabs-mode: nil -*- + * + * injector - Library for injecting a shared library into a Linux process + * + * URL: https://github.com/kubo/injector + * + * ------------------------------------------------------ + * + * this code is generated automatically by mig + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef _mach_exc_user_ +#define _mach_exc_user_ + +/* Module mach_exc */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* BEGIN VOUCHER CODE */ + +#ifndef KERNEL +#if defined(__has_include) +#if __has_include() +#ifndef USING_VOUCHERS +#define USING_VOUCHERS +#endif +#ifndef __VOUCHER_FORWARD_TYPE_DECLS__ +#define __VOUCHER_FORWARD_TYPE_DECLS__ +#ifdef __cplusplus +extern "C" { +#endif + extern boolean_t voucher_mach_msg_set(mach_msg_header_t *msg) __attribute__((weak_import)); +#ifdef __cplusplus +} +#endif +#endif // __VOUCHER_FORWARD_TYPE_DECLS__ +#endif // __has_include() +#endif // __has_include +#endif // !KERNEL + +/* END VOUCHER CODE */ + + +/* BEGIN MIG_STRNCPY_ZEROFILL CODE */ + +#if defined(__has_include) +#if __has_include() +#ifndef USING_MIG_STRNCPY_ZEROFILL +#define USING_MIG_STRNCPY_ZEROFILL +#endif +#ifndef __MIG_STRNCPY_ZEROFILL_FORWARD_TYPE_DECLS__ +#define __MIG_STRNCPY_ZEROFILL_FORWARD_TYPE_DECLS__ +#ifdef __cplusplus +extern "C" { +#endif + extern int mig_strncpy_zerofill(char *dest, const char *src, int len) __attribute__((weak_import)); +#ifdef __cplusplus +} +#endif +#endif /* __MIG_STRNCPY_ZEROFILL_FORWARD_TYPE_DECLS__ */ +#endif /* __has_include() */ +#endif /* __has_include */ + +/* END MIG_STRNCPY_ZEROFILL CODE */ + + +#ifdef AUTOTEST +#ifndef FUNCTION_PTR_T +#define FUNCTION_PTR_T +typedef void (*function_ptr_t)(mach_port_t, char *, mach_msg_type_number_t); +typedef struct { + char *name; + function_ptr_t function; +} function_table_entry; +typedef function_table_entry *function_table_t; +#endif /* FUNCTION_PTR_T */ +#endif /* AUTOTEST */ + +#ifndef mach_exc_MSG_COUNT +#define mach_exc_MSG_COUNT 3 +#endif /* mach_exc_MSG_COUNT */ + +#include +#include +#include +#include + +#ifdef __BeforeMigUserHeader +__BeforeMigUserHeader +#endif /* __BeforeMigUserHeader */ + +#include +__BEGIN_DECLS + + +/* Routine mach_exception_raise */ +#ifdef mig_external +mig_external +#else +extern +#endif /* mig_external */ +kern_return_t mach_exception_raise +( + mach_port_t exception_port, + mach_port_t thread, + mach_port_t task, + exception_type_t exception, + mach_exception_data_t code, + mach_msg_type_number_t codeCnt +); + +/* Routine mach_exception_raise_state */ +#ifdef mig_external +mig_external +#else +extern +#endif /* mig_external */ +kern_return_t mach_exception_raise_state +( + mach_port_t exception_port, + exception_type_t exception, + const mach_exception_data_t code, + mach_msg_type_number_t codeCnt, + int *flavor, + const thread_state_t old_state, + mach_msg_type_number_t old_stateCnt, + thread_state_t new_state, + mach_msg_type_number_t *new_stateCnt +); + +/* Routine mach_exception_raise_state_identity */ +#ifdef mig_external +mig_external +#else +extern +#endif /* mig_external */ +kern_return_t mach_exception_raise_state_identity +( + mach_port_t exception_port, + mach_port_t thread, + mach_port_t task, + exception_type_t exception, + mach_exception_data_t code, + mach_msg_type_number_t codeCnt, + int *flavor, + thread_state_t old_state, + mach_msg_type_number_t old_stateCnt, + thread_state_t new_state, + mach_msg_type_number_t *new_stateCnt +); + +__END_DECLS + +/********************** Caution **************************/ +/* The following data types should be used to calculate */ +/* maximum message sizes only. The actual message may be */ +/* smaller, and the position of the arguments within the */ +/* message layout may vary from what is presented here. */ +/* For example, if any of the arguments are variable- */ +/* sized, and less than the maximum is sent, the data */ +/* will be packed tight in the actual message to reduce */ +/* the presence of holes. */ +/********************** Caution **************************/ + +/* typedefs for all requests */ + +#ifndef __Request__mach_exc_subsystem__defined +#define __Request__mach_exc_subsystem__defined + +#ifdef __MigPackStructs +#pragma pack(push, 4) +#endif + typedef struct { + mach_msg_header_t Head; + /* start of the kernel processed data */ + mach_msg_body_t msgh_body; + mach_msg_port_descriptor_t thread; + mach_msg_port_descriptor_t task; + /* end of the kernel processed data */ + NDR_record_t NDR; + exception_type_t exception; + mach_msg_type_number_t codeCnt; + int64_t code[2]; + } __Request__mach_exception_raise_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack(pop) +#endif + +#ifdef __MigPackStructs +#pragma pack(push, 4) +#endif + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + exception_type_t exception; + mach_msg_type_number_t codeCnt; + int64_t code[2]; + int flavor; + mach_msg_type_number_t old_stateCnt; + natural_t old_state[614]; + } __Request__mach_exception_raise_state_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack(pop) +#endif + +#ifdef __MigPackStructs +#pragma pack(push, 4) +#endif + typedef struct { + mach_msg_header_t Head; + /* start of the kernel processed data */ + mach_msg_body_t msgh_body; + mach_msg_port_descriptor_t thread; + mach_msg_port_descriptor_t task; + /* end of the kernel processed data */ + NDR_record_t NDR; + exception_type_t exception; + mach_msg_type_number_t codeCnt; + int64_t code[2]; + int flavor; + mach_msg_type_number_t old_stateCnt; + natural_t old_state[614]; + } __Request__mach_exception_raise_state_identity_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack(pop) +#endif +#endif /* !__Request__mach_exc_subsystem__defined */ + +/* union of all requests */ + +#ifndef __RequestUnion__mach_exc_subsystem__defined +#define __RequestUnion__mach_exc_subsystem__defined +union __RequestUnion__mach_exc_subsystem { + __Request__mach_exception_raise_t Request_mach_exception_raise; + __Request__mach_exception_raise_state_t Request_mach_exception_raise_state; + __Request__mach_exception_raise_state_identity_t Request_mach_exception_raise_state_identity; +}; +#endif /* !__RequestUnion__mach_exc_subsystem__defined */ +/* typedefs for all replies */ + +#ifndef __Reply__mach_exc_subsystem__defined +#define __Reply__mach_exc_subsystem__defined + +#ifdef __MigPackStructs +#pragma pack(push, 4) +#endif + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + } __Reply__mach_exception_raise_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack(pop) +#endif + +#ifdef __MigPackStructs +#pragma pack(push, 4) +#endif + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + int flavor; + mach_msg_type_number_t new_stateCnt; + natural_t new_state[614]; + } __Reply__mach_exception_raise_state_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack(pop) +#endif + +#ifdef __MigPackStructs +#pragma pack(push, 4) +#endif + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + int flavor; + mach_msg_type_number_t new_stateCnt; + natural_t new_state[614]; + } __Reply__mach_exception_raise_state_identity_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack(pop) +#endif +#endif /* !__Reply__mach_exc_subsystem__defined */ + +/* union of all replies */ + +#ifndef __ReplyUnion__mach_exc_subsystem__defined +#define __ReplyUnion__mach_exc_subsystem__defined +union __ReplyUnion__mach_exc_subsystem { + __Reply__mach_exception_raise_t Reply_mach_exception_raise; + __Reply__mach_exception_raise_state_t Reply_mach_exception_raise_state; + __Reply__mach_exception_raise_state_identity_t Reply_mach_exception_raise_state_identity; +}; +#endif /* !__RequestUnion__mach_exc_subsystem__defined */ + +#ifndef subsystem_to_name_map_mach_exc +#define subsystem_to_name_map_mach_exc \ + { "mach_exception_raise", 2405 },\ + { "mach_exception_raise_state", 2406 },\ + { "mach_exception_raise_state_identity", 2407 } +#endif + +#ifdef __AfterMigUserHeader +__AfterMigUserHeader +#endif /* __AfterMigUserHeader */ + +#endif /* _mach_exc_user_ */ diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/macos/mach_excServer.c b/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/macos/mach_excServer.c new file mode 100644 index 0000000..b1a20e8 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/macos/mach_excServer.c @@ -0,0 +1,828 @@ +/* -*- indent-tabs-mode: nil -*- + * + * injector - Library for injecting a shared library into a Linux process + * + * URL: https://github.com/kubo/injector + * + * ------------------------------------------------------ + * + * this code is generated automatically by mig + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +/* + * IDENTIFICATION: + * stub generated Mon Nov 7 09:48:39 2022 + * with a MiG generated by bootstrap_cmds-117 + * OPTIONS: + */ + +/* Module mach_exc */ + +#define __MIG_check__Request__mach_exc_subsystem__ 1 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "injector_internal.h" +/* BEGIN VOUCHER CODE */ + +#ifndef KERNEL +#if defined(__has_include) +#if __has_include() +#ifndef USING_VOUCHERS +#define USING_VOUCHERS +#endif +#ifndef __VOUCHER_FORWARD_TYPE_DECLS__ +#define __VOUCHER_FORWARD_TYPE_DECLS__ +#ifdef __cplusplus +extern "C" { +#endif + extern boolean_t voucher_mach_msg_set(mach_msg_header_t *msg) __attribute__((weak_import)); +#ifdef __cplusplus +} +#endif +#endif // __VOUCHER_FORWARD_TYPE_DECLS__ +#endif // __has_include() +#endif // __has_include +#endif // !KERNEL + +/* END VOUCHER CODE */ + + +/* BEGIN MIG_STRNCPY_ZEROFILL CODE */ + +#if defined(__has_include) +#if __has_include() +#ifndef USING_MIG_STRNCPY_ZEROFILL +#define USING_MIG_STRNCPY_ZEROFILL +#endif +#ifndef __MIG_STRNCPY_ZEROFILL_FORWARD_TYPE_DECLS__ +#define __MIG_STRNCPY_ZEROFILL_FORWARD_TYPE_DECLS__ +#ifdef __cplusplus +extern "C" { +#endif + extern int mig_strncpy_zerofill(char *dest, const char *src, int len) __attribute__((weak_import)); +#ifdef __cplusplus +} +#endif +#endif /* __MIG_STRNCPY_ZEROFILL_FORWARD_TYPE_DECLS__ */ +#endif /* __has_include() */ +#endif /* __has_include */ + +/* END MIG_STRNCPY_ZEROFILL CODE */ + + +#include +#include +#include +#include + +#ifndef mig_internal +#define mig_internal static __inline__ +#endif /* mig_internal */ + +#ifndef mig_external +#define mig_external +#endif /* mig_external */ + +#if !defined(__MigTypeCheck) && defined(TypeCheck) +#define __MigTypeCheck TypeCheck /* Legacy setting */ +#endif /* !defined(__MigTypeCheck) */ + +#if !defined(__MigKernelSpecificCode) && defined(_MIG_KERNEL_SPECIFIC_CODE_) +#define __MigKernelSpecificCode _MIG_KERNEL_SPECIFIC_CODE_ /* Legacy setting */ +#endif /* !defined(__MigKernelSpecificCode) */ + +#ifndef LimitCheck +#define LimitCheck 0 +#endif /* LimitCheck */ + +#ifndef min +#define min(a,b) ( ((a) < (b))? (a): (b) ) +#endif /* min */ + +#if !defined(_WALIGN_) +#define _WALIGN_(x) (((x) + 3) & ~3) +#endif /* !defined(_WALIGN_) */ + +#if !defined(_WALIGNSZ_) +#define _WALIGNSZ_(x) _WALIGN_(sizeof(x)) +#endif /* !defined(_WALIGNSZ_) */ + +#ifndef UseStaticTemplates +#define UseStaticTemplates 0 +#endif /* UseStaticTemplates */ + +#ifndef MIG_SERVER_ROUTINE +#define MIG_SERVER_ROUTINE +#endif + +#ifndef __DeclareRcvRpc +#define __DeclareRcvRpc(_NUM_, _NAME_) +#endif /* __DeclareRcvRpc */ + +#ifndef __BeforeRcvRpc +#define __BeforeRcvRpc(_NUM_, _NAME_) +#endif /* __BeforeRcvRpc */ + +#ifndef __AfterRcvRpc +#define __AfterRcvRpc(_NUM_, _NAME_) +#endif /* __AfterRcvRpc */ + +#ifndef __DeclareRcvSimple +#define __DeclareRcvSimple(_NUM_, _NAME_) +#endif /* __DeclareRcvSimple */ + +#ifndef __BeforeRcvSimple +#define __BeforeRcvSimple(_NUM_, _NAME_) +#endif /* __BeforeRcvSimple */ + +#ifndef __AfterRcvSimple +#define __AfterRcvSimple(_NUM_, _NAME_) +#endif /* __AfterRcvSimple */ + +#define novalue void + +#define msgh_request_port msgh_local_port +#define MACH_MSGH_BITS_REQUEST(bits) MACH_MSGH_BITS_LOCAL(bits) +#define msgh_reply_port msgh_remote_port +#define MACH_MSGH_BITS_REPLY(bits) MACH_MSGH_BITS_REMOTE(bits) + +#define MIG_RETURN_ERROR(X, code) {\ + ((mig_reply_error_t *)X)->RetCode = code;\ + ((mig_reply_error_t *)X)->NDR = NDR_record;\ + return;\ + } + +/* typedefs for all requests */ + +#ifndef __Request__mach_exc_subsystem__defined +#define __Request__mach_exc_subsystem__defined + +#ifdef __MigPackStructs +#pragma pack(push, 4) +#endif + typedef struct { + mach_msg_header_t Head; + /* start of the kernel processed data */ + mach_msg_body_t msgh_body; + mach_msg_port_descriptor_t thread; + mach_msg_port_descriptor_t task; + /* end of the kernel processed data */ + NDR_record_t NDR; + exception_type_t exception; + mach_msg_type_number_t codeCnt; + int64_t code[2]; + } __Request__mach_exception_raise_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack(pop) +#endif + +#ifdef __MigPackStructs +#pragma pack(push, 4) +#endif + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + exception_type_t exception; + mach_msg_type_number_t codeCnt; + int64_t code[2]; + int flavor; + mach_msg_type_number_t old_stateCnt; + natural_t old_state[614]; + } __Request__mach_exception_raise_state_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack(pop) +#endif + +#ifdef __MigPackStructs +#pragma pack(push, 4) +#endif + typedef struct { + mach_msg_header_t Head; + /* start of the kernel processed data */ + mach_msg_body_t msgh_body; + mach_msg_port_descriptor_t thread; + mach_msg_port_descriptor_t task; + /* end of the kernel processed data */ + NDR_record_t NDR; + exception_type_t exception; + mach_msg_type_number_t codeCnt; + int64_t code[2]; + int flavor; + mach_msg_type_number_t old_stateCnt; + natural_t old_state[614]; + } __Request__mach_exception_raise_state_identity_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack(pop) +#endif +#endif /* !__Request__mach_exc_subsystem__defined */ + +/* typedefs for all replies */ + +#ifndef __Reply__mach_exc_subsystem__defined +#define __Reply__mach_exc_subsystem__defined + +#ifdef __MigPackStructs +#pragma pack(push, 4) +#endif + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + } __Reply__mach_exception_raise_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack(pop) +#endif + +#ifdef __MigPackStructs +#pragma pack(push, 4) +#endif + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + int flavor; + mach_msg_type_number_t new_stateCnt; + natural_t new_state[614]; + } __Reply__mach_exception_raise_state_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack(pop) +#endif + +#ifdef __MigPackStructs +#pragma pack(push, 4) +#endif + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + int flavor; + mach_msg_type_number_t new_stateCnt; + natural_t new_state[614]; + } __Reply__mach_exception_raise_state_identity_t __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack(pop) +#endif +#endif /* !__Reply__mach_exc_subsystem__defined */ + + +/* union of all replies */ + +#ifndef __ReplyUnion__catch_mach_exc_subsystem__defined +#define __ReplyUnion__catch_mach_exc_subsystem__defined +union __ReplyUnion__catch_mach_exc_subsystem { + __Reply__mach_exception_raise_t Reply_mach_exception_raise; + __Reply__mach_exception_raise_state_t Reply_mach_exception_raise_state; + __Reply__mach_exception_raise_state_identity_t Reply_mach_exception_raise_state_identity; +}; +#endif /* __ReplyUnion__catch_mach_exc_subsystem__defined */ +/* Forward Declarations */ + + +mig_internal novalue _Xmach_exception_raise + (mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP); + +mig_internal novalue _Xmach_exception_raise_state + (mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP); + +mig_internal novalue _Xmach_exception_raise_state_identity + (mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP); + + +#if ( __MigTypeCheck ) +#if __MIG_check__Request__mach_exc_subsystem__ +#if !defined(__MIG_check__Request__mach_exception_raise_t__defined) +#define __MIG_check__Request__mach_exception_raise_t__defined + +mig_internal kern_return_t __MIG_check__Request__mach_exception_raise_t(__attribute__((__unused__)) __Request__mach_exception_raise_t *In0P) +{ + + typedef __Request__mach_exception_raise_t __Request; +#if __MigTypeCheck + unsigned int msgh_size; +#endif /* __MigTypeCheck */ + +#if __MigTypeCheck + msgh_size = In0P->Head.msgh_size; + if (!(In0P->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) || + (In0P->msgh_body.msgh_descriptor_count != 2) || + (msgh_size < (mach_msg_size_t)(sizeof(__Request) - 16)) || (msgh_size > (mach_msg_size_t)sizeof(__Request))) + return MIG_BAD_ARGUMENTS; +#endif /* __MigTypeCheck */ + +#if __MigTypeCheck + if (In0P->thread.type != MACH_MSG_PORT_DESCRIPTOR || + In0P->thread.disposition != 17) + return MIG_TYPE_ERROR; +#endif /* __MigTypeCheck */ + +#if __MigTypeCheck + if (In0P->task.type != MACH_MSG_PORT_DESCRIPTOR || + In0P->task.disposition != 17) + return MIG_TYPE_ERROR; +#endif /* __MigTypeCheck */ + +#if defined(__NDR_convert__int_rep__Request__mach_exception_raise_t__codeCnt__defined) + if (In0P->NDR.int_rep != NDR_record.int_rep) + __NDR_convert__int_rep__Request__mach_exception_raise_t__codeCnt(&In0P->codeCnt, In0P->NDR.int_rep); +#endif /* __NDR_convert__int_rep__Request__mach_exception_raise_t__codeCnt__defined */ +#if __MigTypeCheck + if ( In0P->codeCnt > 2 ) + return MIG_BAD_ARGUMENTS; + if (((msgh_size - (mach_msg_size_t)(sizeof(__Request) - 16)) / 8 < In0P->codeCnt) || + (msgh_size != (mach_msg_size_t)(sizeof(__Request) - 16) + (8 * In0P->codeCnt))) + return MIG_BAD_ARGUMENTS; +#endif /* __MigTypeCheck */ + + return MACH_MSG_SUCCESS; +} +#endif /* !defined(__MIG_check__Request__mach_exception_raise_t__defined) */ +#endif /* __MIG_check__Request__mach_exc_subsystem__ */ +#endif /* ( __MigTypeCheck ) */ + + +/* Routine mach_exception_raise */ +#ifdef mig_external +mig_external +#else +extern +#endif /* mig_external */ +MIG_SERVER_ROUTINE +kern_return_t catch_mach_exception_raise +( + mach_port_t exception_port, + mach_port_t thread, + mach_port_t task, + exception_type_t exception, + mach_exception_data_t code, + mach_msg_type_number_t codeCnt, + injector_t *injector +); + +/* Routine mach_exception_raise */ +mig_internal novalue _Xmach_exception_raise + (mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP) +{ + +#ifdef __MigPackStructs +#pragma pack(push, 4) +#endif + typedef struct { + mach_msg_header_t Head; + /* start of the kernel processed data */ + mach_msg_body_t msgh_body; + mach_msg_port_descriptor_t thread; + mach_msg_port_descriptor_t task; + /* end of the kernel processed data */ + NDR_record_t NDR; + exception_type_t exception; + mach_msg_type_number_t codeCnt; + int64_t code[2]; + mach_msg_trailer_t trailer; + } Request __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack(pop) +#endif + typedef __Request__mach_exception_raise_t __Request; + typedef __Reply__mach_exception_raise_t Reply __attribute__((unused)); + + /* + * typedef struct { + * mach_msg_header_t Head; + * NDR_record_t NDR; + * kern_return_t RetCode; + * } mig_reply_error_t; + */ + Request *In0P = (Request *) InHeadP; + Reply *OutP = (Reply *) OutHeadP; +#ifdef __MIG_check__Request__mach_exception_raise_t__defined + kern_return_t check_result; +#endif /* __MIG_check__Request__mach_exception_raise_t__defined */ + + __DeclareRcvRpc(2405, "mach_exception_raise") + __BeforeRcvRpc(2405, "mach_exception_raise") + +#if defined(__MIG_check__Request__mach_exception_raise_t__defined) + check_result = __MIG_check__Request__mach_exception_raise_t((__Request *)In0P); + if (check_result != MACH_MSG_SUCCESS) + { MIG_RETURN_ERROR(OutP, check_result); } +#endif /* defined(__MIG_check__Request__mach_exception_raise_t__defined) */ + mach_msg_header_with_injector *mmhwi = (mach_msg_header_with_injector*)In0P; + OutP->RetCode = catch_mach_exception_raise(In0P->Head.msgh_request_port, In0P->thread.name, In0P->task.name, In0P->exception, In0P->code, In0P->codeCnt, mmhwi->injector); + + OutP->NDR = NDR_record; + + + __AfterRcvRpc(2405, "mach_exception_raise") +} + +#if ( __MigTypeCheck ) +#if __MIG_check__Request__mach_exc_subsystem__ +#if !defined(__MIG_check__Request__mach_exception_raise_state_t__defined) +#define __MIG_check__Request__mach_exception_raise_state_t__defined + +mig_internal kern_return_t __MIG_check__Request__mach_exception_raise_state_t(__attribute__((__unused__)) __Request__mach_exception_raise_state_t *In0P, __attribute__((__unused__)) __Request__mach_exception_raise_state_t **In1PP) +{ + + typedef __Request__mach_exception_raise_state_t __Request; + __Request *In1P; +#if __MigTypeCheck + unsigned int msgh_size; +#endif /* __MigTypeCheck */ + unsigned int msgh_size_delta; + +#if __MigTypeCheck + msgh_size = In0P->Head.msgh_size; + if ((In0P->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) || + (msgh_size < (mach_msg_size_t)(sizeof(__Request) - 2472)) || (msgh_size > (mach_msg_size_t)sizeof(__Request))) + return MIG_BAD_ARGUMENTS; +#endif /* __MigTypeCheck */ + +#if defined(__NDR_convert__int_rep__Request__mach_exception_raise_state_t__codeCnt__defined) + if (In0P->NDR.int_rep != NDR_record.int_rep) + __NDR_convert__int_rep__Request__mach_exception_raise_state_t__codeCnt(&In0P->codeCnt, In0P->NDR.int_rep); +#endif /* __NDR_convert__int_rep__Request__mach_exception_raise_state_t__codeCnt__defined */ + msgh_size_delta = (8 * In0P->codeCnt); +#if __MigTypeCheck + if ( In0P->codeCnt > 2 ) + return MIG_BAD_ARGUMENTS; + if (((msgh_size - (mach_msg_size_t)(sizeof(__Request) - 2472)) / 8 < In0P->codeCnt) || + (msgh_size < (mach_msg_size_t)(sizeof(__Request) - 2472) + (8 * In0P->codeCnt))) + return MIG_BAD_ARGUMENTS; + msgh_size -= msgh_size_delta; +#endif /* __MigTypeCheck */ + + *In1PP = In1P = (__Request *) ((pointer_t) In0P + msgh_size_delta - 16); + +#if defined(__NDR_convert__int_rep__Request__mach_exception_raise_state_t__old_stateCnt__defined) + if (In0P->NDR.int_rep != NDR_record.int_rep) + __NDR_convert__int_rep__Request__mach_exception_raise_state_t__old_stateCnt(&In1P->old_stateCnt, In1P->NDR.int_rep); +#endif /* __NDR_convert__int_rep__Request__mach_exception_raise_state_t__old_stateCnt__defined */ +#if __MigTypeCheck + if ( In1P->old_stateCnt > 614 ) + return MIG_BAD_ARGUMENTS; + if (((msgh_size - (mach_msg_size_t)(sizeof(__Request) - 2472)) / 4 < In1P->old_stateCnt) || + (msgh_size != (mach_msg_size_t)(sizeof(__Request) - 2472) + (4 * In1P->old_stateCnt))) + return MIG_BAD_ARGUMENTS; +#endif /* __MigTypeCheck */ + + return MACH_MSG_SUCCESS; +} +#endif /* !defined(__MIG_check__Request__mach_exception_raise_state_t__defined) */ +#endif /* __MIG_check__Request__mach_exc_subsystem__ */ +#endif /* ( __MigTypeCheck ) */ + + +/* Routine mach_exception_raise_state */ +#ifdef mig_external +mig_external +#else +extern +#endif /* mig_external */ +MIG_SERVER_ROUTINE +kern_return_t catch_mach_exception_raise_state +( + mach_port_t exception_port, + exception_type_t exception, + const mach_exception_data_t code, + mach_msg_type_number_t codeCnt, + int *flavor, + const thread_state_t old_state, + mach_msg_type_number_t old_stateCnt, + thread_state_t new_state, + mach_msg_type_number_t *new_stateCnt +); + +/* Routine mach_exception_raise_state */ +mig_internal novalue _Xmach_exception_raise_state + (mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP) +{ + +#ifdef __MigPackStructs +#pragma pack(push, 4) +#endif + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + exception_type_t exception; + mach_msg_type_number_t codeCnt; + int64_t code[2]; + int flavor; + mach_msg_type_number_t old_stateCnt; + natural_t old_state[614]; + mach_msg_trailer_t trailer; + } Request __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack(pop) +#endif + typedef __Request__mach_exception_raise_state_t __Request; + typedef __Reply__mach_exception_raise_state_t Reply __attribute__((unused)); + + /* + * typedef struct { + * mach_msg_header_t Head; + * NDR_record_t NDR; + * kern_return_t RetCode; + * } mig_reply_error_t; + */ + + Request *In0P = (Request *) InHeadP; + Request *In1P; + Reply *OutP = (Reply *) OutHeadP; +#ifdef __MIG_check__Request__mach_exception_raise_state_t__defined + kern_return_t check_result; +#endif /* __MIG_check__Request__mach_exception_raise_state_t__defined */ + + __DeclareRcvRpc(2406, "mach_exception_raise_state") + __BeforeRcvRpc(2406, "mach_exception_raise_state") + +#if defined(__MIG_check__Request__mach_exception_raise_state_t__defined) + check_result = __MIG_check__Request__mach_exception_raise_state_t((__Request *)In0P, (__Request **)&In1P); + if (check_result != MACH_MSG_SUCCESS) + { MIG_RETURN_ERROR(OutP, check_result); } +#endif /* defined(__MIG_check__Request__mach_exception_raise_state_t__defined) */ + + OutP->new_stateCnt = 614; + + OutP->RetCode = catch_mach_exception_raise_state(In0P->Head.msgh_request_port, In0P->exception, In0P->code, In0P->codeCnt, &In1P->flavor, In1P->old_state, In1P->old_stateCnt, OutP->new_state, &OutP->new_stateCnt); + if (OutP->RetCode != KERN_SUCCESS) { + MIG_RETURN_ERROR(OutP, OutP->RetCode); + } + + OutP->NDR = NDR_record; + + + OutP->flavor = In1P->flavor; + OutP->Head.msgh_size = (mach_msg_size_t)(sizeof(Reply) - 2456) + (((4 * OutP->new_stateCnt))); + + __AfterRcvRpc(2406, "mach_exception_raise_state") +} + +#if ( __MigTypeCheck ) +#if __MIG_check__Request__mach_exc_subsystem__ +#if !defined(__MIG_check__Request__mach_exception_raise_state_identity_t__defined) +#define __MIG_check__Request__mach_exception_raise_state_identity_t__defined + +mig_internal kern_return_t __MIG_check__Request__mach_exception_raise_state_identity_t(__attribute__((__unused__)) __Request__mach_exception_raise_state_identity_t *In0P, __attribute__((__unused__)) __Request__mach_exception_raise_state_identity_t **In1PP) +{ + + typedef __Request__mach_exception_raise_state_identity_t __Request; + __Request *In1P; +#if __MigTypeCheck + unsigned int msgh_size; +#endif /* __MigTypeCheck */ + unsigned int msgh_size_delta; + +#if __MigTypeCheck + msgh_size = In0P->Head.msgh_size; + if (!(In0P->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) || + (In0P->msgh_body.msgh_descriptor_count != 2) || + (msgh_size < (mach_msg_size_t)(sizeof(__Request) - 2472)) || (msgh_size > (mach_msg_size_t)sizeof(__Request))) + return MIG_BAD_ARGUMENTS; +#endif /* __MigTypeCheck */ + +#if __MigTypeCheck + if (In0P->thread.type != MACH_MSG_PORT_DESCRIPTOR || + In0P->thread.disposition != 17) + return MIG_TYPE_ERROR; +#endif /* __MigTypeCheck */ + +#if __MigTypeCheck + if (In0P->task.type != MACH_MSG_PORT_DESCRIPTOR || + In0P->task.disposition != 17) + return MIG_TYPE_ERROR; +#endif /* __MigTypeCheck */ + +#if defined(__NDR_convert__int_rep__Request__mach_exception_raise_state_identity_t__codeCnt__defined) + if (In0P->NDR.int_rep != NDR_record.int_rep) + __NDR_convert__int_rep__Request__mach_exception_raise_state_identity_t__codeCnt(&In0P->codeCnt, In0P->NDR.int_rep); +#endif /* __NDR_convert__int_rep__Request__mach_exception_raise_state_identity_t__codeCnt__defined */ + msgh_size_delta = (8 * In0P->codeCnt); +#if __MigTypeCheck + if ( In0P->codeCnt > 2 ) + return MIG_BAD_ARGUMENTS; + if (((msgh_size - (mach_msg_size_t)(sizeof(__Request) - 2472)) / 8 < In0P->codeCnt) || + (msgh_size < (mach_msg_size_t)(sizeof(__Request) - 2472) + (8 * In0P->codeCnt))) + return MIG_BAD_ARGUMENTS; + msgh_size -= msgh_size_delta; +#endif /* __MigTypeCheck */ + + *In1PP = In1P = (__Request *) ((pointer_t) In0P + msgh_size_delta - 16); + +#if defined(__NDR_convert__int_rep__Request__mach_exception_raise_state_identity_t__old_stateCnt__defined) + if (In0P->NDR.int_rep != NDR_record.int_rep) + __NDR_convert__int_rep__Request__mach_exception_raise_state_identity_t__old_stateCnt(&In1P->old_stateCnt, In1P->NDR.int_rep); +#endif /* __NDR_convert__int_rep__Request__mach_exception_raise_state_identity_t__old_stateCnt__defined */ +#if __MigTypeCheck + if ( In1P->old_stateCnt > 614 ) + return MIG_BAD_ARGUMENTS; + if (((msgh_size - (mach_msg_size_t)(sizeof(__Request) - 2472)) / 4 < In1P->old_stateCnt) || + (msgh_size != (mach_msg_size_t)(sizeof(__Request) - 2472) + (4 * In1P->old_stateCnt))) + return MIG_BAD_ARGUMENTS; +#endif /* __MigTypeCheck */ + + return MACH_MSG_SUCCESS; +} +#endif /* !defined(__MIG_check__Request__mach_exception_raise_state_identity_t__defined) */ +#endif /* __MIG_check__Request__mach_exc_subsystem__ */ +#endif /* ( __MigTypeCheck ) */ + + +/* Routine mach_exception_raise_state_identity */ +#ifdef mig_external +mig_external +#else +extern +#endif /* mig_external */ +MIG_SERVER_ROUTINE +kern_return_t catch_mach_exception_raise_state_identity +( + mach_port_t exception_port, + mach_port_t thread, + mach_port_t task, + exception_type_t exception, + mach_exception_data_t code, + mach_msg_type_number_t codeCnt, + int *flavor, + thread_state_t old_state, + mach_msg_type_number_t old_stateCnt, + thread_state_t new_state, + mach_msg_type_number_t *new_stateCnt +); + +/* Routine mach_exception_raise_state_identity */ +mig_internal novalue _Xmach_exception_raise_state_identity + (mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP) +{ + +#ifdef __MigPackStructs +#pragma pack(push, 4) +#endif + typedef struct { + mach_msg_header_t Head; + /* start of the kernel processed data */ + mach_msg_body_t msgh_body; + mach_msg_port_descriptor_t thread; + mach_msg_port_descriptor_t task; + /* end of the kernel processed data */ + NDR_record_t NDR; + exception_type_t exception; + mach_msg_type_number_t codeCnt; + int64_t code[2]; + int flavor; + mach_msg_type_number_t old_stateCnt; + natural_t old_state[614]; + mach_msg_trailer_t trailer; + } Request __attribute__((unused)); +#ifdef __MigPackStructs +#pragma pack(pop) +#endif + typedef __Request__mach_exception_raise_state_identity_t __Request; + typedef __Reply__mach_exception_raise_state_identity_t Reply __attribute__((unused)); + + /* + * typedef struct { + * mach_msg_header_t Head; + * NDR_record_t NDR; + * kern_return_t RetCode; + * } mig_reply_error_t; + */ + + Request *In0P = (Request *) InHeadP; + Request *In1P; + Reply *OutP = (Reply *) OutHeadP; +#ifdef __MIG_check__Request__mach_exception_raise_state_identity_t__defined + kern_return_t check_result; +#endif /* __MIG_check__Request__mach_exception_raise_state_identity_t__defined */ + + __DeclareRcvRpc(2407, "mach_exception_raise_state_identity") + __BeforeRcvRpc(2407, "mach_exception_raise_state_identity") + +#if defined(__MIG_check__Request__mach_exception_raise_state_identity_t__defined) + check_result = __MIG_check__Request__mach_exception_raise_state_identity_t((__Request *)In0P, (__Request **)&In1P); + if (check_result != MACH_MSG_SUCCESS) + { MIG_RETURN_ERROR(OutP, check_result); } +#endif /* defined(__MIG_check__Request__mach_exception_raise_state_identity_t__defined) */ + + OutP->new_stateCnt = 614; + + OutP->RetCode = catch_mach_exception_raise_state_identity(In0P->Head.msgh_request_port, In0P->thread.name, In0P->task.name, In0P->exception, In0P->code, In0P->codeCnt, &In1P->flavor, In1P->old_state, In1P->old_stateCnt, OutP->new_state, &OutP->new_stateCnt); + if (OutP->RetCode != KERN_SUCCESS) { + MIG_RETURN_ERROR(OutP, OutP->RetCode); + } + + OutP->NDR = NDR_record; + + + OutP->flavor = In1P->flavor; + OutP->Head.msgh_size = (mach_msg_size_t)(sizeof(Reply) - 2456) + (((4 * OutP->new_stateCnt))); + + __AfterRcvRpc(2407, "mach_exception_raise_state_identity") +} + + +#ifdef mig_external +mig_external +#else +extern +#endif /* mig_external */ +boolean_t mach_exc_server( + mach_msg_header_t *InHeadP, + mach_msg_header_t *OutHeadP); + +#ifdef mig_external +mig_external +#else +extern +#endif /* mig_external */ +mig_routine_t mach_exc_server_routine( + mach_msg_header_t *InHeadP); + + +/* Description of this subsystem, for use in direct RPC */ +const struct catch_mach_exc_subsystem { + mig_server_routine_t server; /* Server routine */ + mach_msg_id_t start; /* Min routine number */ + mach_msg_id_t end; /* Max routine number + 1 */ + unsigned int maxsize; /* Max msg size */ + vm_address_t reserved; /* Reserved */ + struct routine_descriptor /*Array of routine descriptors */ + routine[3]; +} catch_mach_exc_subsystem = { + mach_exc_server_routine, + 2405, + 2408, + (mach_msg_size_t)sizeof(union __ReplyUnion__catch_mach_exc_subsystem), + (vm_address_t)0, + { + { (mig_impl_routine_t) 0, + (mig_stub_routine_t) _Xmach_exception_raise, 6, 0, (routine_arg_descriptor_t)0, (mach_msg_size_t)sizeof(__Reply__mach_exception_raise_t)}, + { (mig_impl_routine_t) 0, + (mig_stub_routine_t) _Xmach_exception_raise_state, 9, 0, (routine_arg_descriptor_t)0, (mach_msg_size_t)sizeof(__Reply__mach_exception_raise_state_t)}, + { (mig_impl_routine_t) 0, + (mig_stub_routine_t) _Xmach_exception_raise_state_identity, 11, 0, (routine_arg_descriptor_t)0, (mach_msg_size_t)sizeof(__Reply__mach_exception_raise_state_identity_t)}, + } +}; + +mig_external boolean_t mach_exc_server + (mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP) +{ + /* + * typedef struct { + * mach_msg_header_t Head; + * NDR_record_t NDR; + * kern_return_t RetCode; + * } mig_reply_error_t; + */ + + mig_routine_t routine; + + OutHeadP->msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REPLY(InHeadP->msgh_bits), 0); + OutHeadP->msgh_remote_port = InHeadP->msgh_reply_port; + /* Minimal size: routine() will update it if different */ + OutHeadP->msgh_size = (mach_msg_size_t)sizeof(mig_reply_error_t); + OutHeadP->msgh_local_port = MACH_PORT_NULL; + OutHeadP->msgh_id = InHeadP->msgh_id + 100; + OutHeadP->msgh_reserved = 0; + + if ((InHeadP->msgh_id > 2407) || (InHeadP->msgh_id < 2405) || + ((routine = catch_mach_exc_subsystem.routine[InHeadP->msgh_id - 2405].stub_routine) == 0)) { + ((mig_reply_error_t *)OutHeadP)->NDR = NDR_record; + ((mig_reply_error_t *)OutHeadP)->RetCode = MIG_BAD_ID; + return FALSE; + } + (*routine) (InHeadP, OutHeadP); + return TRUE; +} + +mig_external mig_routine_t mach_exc_server_routine + (mach_msg_header_t *InHeadP) +{ + int msgh_id; + + msgh_id = InHeadP->msgh_id - 2405; + + if ((msgh_id > 2) || (msgh_id < 0)) + return 0; + + return catch_mach_exc_subsystem.routine[msgh_id].stub_routine; +} diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/macos/ptrace.c b/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/macos/ptrace.c new file mode 100644 index 0000000..52e4888 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/macos/ptrace.c @@ -0,0 +1,81 @@ +/* -*- indent-tabs-mode: nil -*- + * + * injector - Library for injecting a shared library into a Linux process + * + * URL: https://github.com/kubo/injector + * + * ------------------------------------------------------ + * + * Copyright (C) 2022 TheOiseth + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "injector_internal.h" +#include +#include +#include +#include +#define PTRACE_OR_RETURN(request, injector, addr, data) do { \ + int rv = injector__ptrace(request, injector->pid, addr, data, #request); \ + if (rv != 0) { \ + return rv; \ + } \ +} while (0) +static int set_ptrace_error(const char *request_name) +{ + int err = errno; + injector__set_errmsg("%s error : %s", request_name, strerror(errno)); + switch (err) { + case EFAULT: + return INJERR_INVALID_MEMORY_AREA; + case EPERM: + return INJERR_PERMISSION; + case ESRCH: + return INJERR_NO_PROCESS; + } + return INJERR_OTHER; +} + +int injector__ptrace(int request, pid_t pid, long addr, long data, const char *request_name) +{ + if (ptrace(request, pid, (caddr_t)addr, data) != 0) { + return set_ptrace_error(request_name); + } + return 0; +} + +int injector__ptrace_attach(const injector_t *injector) +{ + PTRACE_OR_RETURN(PT_ATTACHEXC, injector, 0, 0); + return 0; +} + +int injector__ptrace_detach(const injector_t *injector) +{ + PTRACE_OR_RETURN(PT_DETACH, injector, 0, 0); + return 0; +} + +int injector__ptrace_continue(const injector_t *injector) +{ + PTRACE_OR_RETURN(PT_CONTINUE, injector, 1, 0); + return 0; +} + +int injector__ptrace_update(const injector_t *injector, long thread_port) +{ + PTRACE_OR_RETURN(PT_THUPDATE, injector, thread_port, 0); + return 0; +} \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/macos/remote_call.c b/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/macos/remote_call.c new file mode 100644 index 0000000..f37072e --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/macos/remote_call.c @@ -0,0 +1,228 @@ +/* -*- indent-tabs-mode: nil -*- + * + * injector - Library for injecting a shared library into a Linux process + * + * URL: https://github.com/kubo/injector + * + * ------------------------------------------------------ + * + * Copyright (C) 2022 TheOiseth + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "injector_internal.h" +#include "mach_exc.h" +#include +#include +#include +#include +#include +#include + +//Before change shellcode, see: +//injector.c -> injector_detach +//exc_handler.c -> catch_mach_exception_raise +#if defined(__arm64__) || defined(__aarch64__) +char* shellcode = + "\x00\x01\x3f\xd6" //blr x8 + "\x00\x00\x00\x14" //b //infinity loop, we will terminate this thread later + + //second thread + "\x00\x00\x20\xd4" //brk 0 + "\x00\x01\x3f\xd6" //blr x8 + "\x00\x00\x20\xd4" //brk 0 + "\xc0\x03\x5f\xd6" //ret +; +int shellcode1_len = 8; +int shellcode_length = 24; +#else + +char* shellcode = + //"\x55" //push rbp + //"\x48\x89\xE5" //mov rbp, rsp + //"\x48\x83\xEC\x10" //sub rsp, 0x10 + //"\x48\x8D\x7D\xF8" //lea rdi, [rbp - 8] + "\x90" //nop + "\x90" //nop + "\x90" //nop + "\xFF\xD0" //call rax + //"\x48\x83\xC4\x10" //add rsp, 0x10 + //"\x5D" //pop rbp + "\xeb\xfe" //jmp 0 //infinity loop, we will terminate this thread later + + //second thread + "\xcc" //int3 + //"\x55" //push rbp + //"\x48\x89\xe5" //mov rbp, rsp + "\xff\xd0" //call rax + //"\x5D" //pop rbp + "\xcc" //int3 + "\xc3" //ret +; +int shellcode1_len = 7; +int shellcode_length = 12; +#endif +int injector__call_function(injector_t *injector, long *retval, long function_addr, ...) +{ + va_list ap; + va_start(ap, function_addr); + long arg1, arg2, arg3, arg4, arg5, arg6; + arg1 = va_arg(ap, long); + arg2 = va_arg(ap, long); + arg3 = va_arg(ap, long); + arg4 = va_arg(ap, long); + arg5 = va_arg(ap, long); + arg6 = va_arg(ap, long); + va_end(ap); + int rv; + + if(injector->shellcode_writed == 0){ + pcfmt_t pcfmt = (pcfmt_t)dlsym(RTLD_DEFAULT, "pthread_create_from_mach_thread"); + + + rv = injector__write(injector, injector->code_addr, shellcode, shellcode_length); + if(rv != 0){ + return rv; + } + if(pcfmt == 0){ + //char* legacy_append = + //"\xFF\xD0" //call rax + //"\xcc" //int3 + //; + //rv = injector__write(injector, injector->code_addr, legacy_append, 3); + + //It turns out that we can call pthread_create in mach thread without _pthread_set_self on MacOS < 10.12 + pcfmt = (pcfmt_t)dlsym(RTLD_DEFAULT, "pthread_create"); + } + rv = injector__protect(injector, injector->code_addr, injector->code_size, FALSE, VM_PROT_READ | VM_PROT_EXECUTE); + if (rv != 0) { + return rv; + } + injector->shellcode_writed = 1; + injector->code2_addr = injector->code_addr + shellcode1_len; + + thread_act_t mach_thread; +#if defined(__arm64__) || defined(__aarch64__) + arm_thread_state64_t state; + memset(&state, '\0', sizeof(state)); + state.__pc = injector->code_addr; + state.__sp = injector->stack; + state.__x[8] = (uint64_t)pcfmt; + state.__x[0] = injector->stack - 32; + + state.__x[1] = 0; + state.__x[2] = injector->code2_addr; + state.__x[3] = 0; + rv = thread_create_running(injector->remote_task, ARM_THREAD_STATE64, (thread_state_t)&state, ARM_THREAD_STATE64_COUNT , &mach_thread); +#else + x86_thread_state64_t state; + memset(&state, '\0', sizeof(state)); + state.__rip = injector->code_addr; + state.__rsp = injector->stack - 0x10; + state.__rbp = injector->stack; + if(pcfmt == NULL){ + state.__rax = (uint64_t)dlsym(RTLD_DEFAULT, "_pthread_set_self"); + state.__rdi = 0; + injector->func_addr = (uint64_t)dlsym(RTLD_DEFAULT, "pthread_create"); + injector->arg1 = injector->stack - 0x8; + injector->arg2 = 0; + injector->arg3 = injector->code2_addr; + injector->arg4 = 0; + } else { + state.__rax = (uint64_t)pcfmt; + state.__rdi = injector->stack - 0x8;//&thread + } + + state.__rsi = 0; + state.__rdx = injector->code2_addr; + state.__rcx = 0; + + rv = thread_create_running(injector->remote_task, x86_THREAD_STATE64, (thread_state_t)&state, x86_THREAD_STATE64_COUNT, &mach_thread); +#endif + if(rv != 0){ + injector__set_errmsg("%s error : %s", "CREATE_THREAD", mach_error_string(rv)); + return INJERR_ERROR_IN_TARGET; + } + injector->mach_thread = mach_thread; + if(pcfmt == NULL){ + injector->handle_action = TRAP_SETREGS; + injector__handle_exc(injector); + } + injector->func_addr = function_addr; + injector->arg1 = arg1; + injector->arg2 = arg2; + injector->arg3 = arg3; + injector->arg4 = arg4; + injector->arg5 = arg5; + injector->arg6 = arg6; + + injector->handle_action = TRAP_SETREGS; + injector__handle_exc(injector); + } else { +#if defined(__arm64__) || defined(__aarch64__) + + mach_msg_type_number_t state_count = ARM_THREAD_STATE64_COUNT; + arm_thread_state64_t state; + rv = thread_get_state(injector->remote_thread, ARM_THREAD_STATE64, (thread_state_t)&state, &state_count); + if(rv != 0){ + injector__set_errmsg("%s error : %s", "THREAD_GET_STATE", mach_error_string(rv)); + return INJERR_ERROR_IN_TARGET; + } + state.__pc = injector->code2_addr + 4; + state.__x[0] = arg1; + state.__x[1] = arg2; + state.__x[2] = arg3; + state.__x[3] = arg4; + state.__x[4] = arg5; + state.__x[5] = arg6; + state.__x[8] = function_addr; + rv = thread_set_state(injector->remote_thread, ARM_THREAD_STATE64, (thread_state_t)&state, ARM_THREAD_STATE64_COUNT); +#else + mach_msg_type_number_t state_count = x86_THREAD_STATE64_COUNT; + x86_thread_state64_t state; + rv = thread_get_state(injector->remote_thread, x86_THREAD_STATE64, (thread_state_t)&state, &state_count); + if(rv != 0){ + injector__set_errmsg("%s error : %s", "THREAD_GET_STATE", mach_error_string(rv)); + return INJERR_ERROR_IN_TARGET; + } + state.__rip = injector->code2_addr + 1; + state.__rax = function_addr; + state.__rdi = arg1; + state.__rsi = arg2; + state.__rdx = arg3; + state.__rcx = arg4; + state.__r8 = arg5; + state.__r9 = arg6; + rv = thread_set_state(injector->remote_thread, x86_THREAD_STATE64, (thread_state_t)&state, x86_THREAD_STATE64_COUNT); +#endif + if(rv != 0){ + injector__set_errmsg("%s error : %s", "THREAD_SET_STATE", mach_error_string(rv)); + return INJERR_ERROR_IN_TARGET; + } + rv = thread_resume(injector->remote_thread); + if(rv != 0){ + injector__set_errmsg("%s error : %s", "THREAD_RESUME", mach_error_string(rv)); + return INJERR_ERROR_IN_TARGET; + } + } + + injector->handle_action = TRAP_GETREGS; + injector__handle_exc(injector); + if(injector->handle_err != 0){ + return injector->handle_err; + } + *retval = injector->retval; + return 0; +} diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/macos/util.c b/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/macos/util.c new file mode 100644 index 0000000..afa3cea --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/macos/util.c @@ -0,0 +1,155 @@ +/* -*- indent-tabs-mode: nil -*- + * + * injector - Library for injecting a shared library into a Linux process + * + * URL: https://github.com/kubo/injector + * + * ------------------------------------------------------ + * + * Copyright (C) 2022 TheOiseth + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "injector_internal.h" +#include +#include +#include +#include +#include +#include + + +char injector__errmsg[512]; +char injector__errmsg_is_set; + +void injector__set_errmsg(const char *format, ...) +{ + va_list ap; + int rv; + + /* prevent the error message from being overwritten. */ + if (injector__errmsg_is_set) { + return; + } + injector__errmsg_is_set = 1; + + va_start(ap, format); + rv = vsnprintf(injector__errmsg, sizeof(injector__errmsg), format, ap); + va_end(ap); + if (rv == -1 || rv >= sizeof(injector__errmsg)) { + injector__errmsg[sizeof(injector__errmsg) - 1] = '\0'; + } +} +#ifndef P_TRANSLATED +#define P_TRANSLATED 0x00020000 +#endif +int injector__get_process_arch(pid_t pid, arch_t *arch){ + int mib[CTL_MAXNAME] = {0}; + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = pid; + size_t length = 4; + struct kinfo_proc proc_info = {0}; + size_t size = sizeof(proc_info); + + if(sysctl(mib, (u_int)length, &proc_info, &size, NULL, 0) != 0) { + *arch = ARCH_UNKNOWN; + return INJERR_SUCCESS; + } + if (size == 0) { + injector__set_errmsg("Process %d not found", pid); + return INJERR_NO_PROCESS; + } + + if(P_TRANSLATED == (P_TRANSLATED & proc_info.kp_proc.p_flag)){ + if(P_LP64 == (P_LP64 & proc_info.kp_proc.p_flag)){ + *arch = ARCH_X86_64; + return INJERR_SUCCESS; + } else { + *arch = ARCH_I386; + return INJERR_SUCCESS; + } + } else { + arch_t sys_arch = injector__get_system_arch(); + if(sys_arch == ARCH_ARM64){ + *arch = ARCH_ARM64; + return INJERR_SUCCESS; + } +#if defined(__arm64__) || defined(__aarch64__) + if(sys_arch == ARCH_UNKNOWN){ + *arch = ARCH_ARM64; + return INJERR_SUCCESS; + } +#endif + } + + if(P_LP64 == (P_LP64 & proc_info.kp_proc.p_flag)){ + *arch = ARCH_X86_64; + return INJERR_SUCCESS; + } + *arch = ARCH_I386; + return INJERR_SUCCESS; +} + +#ifndef CPU_TYPE_ARM64 +#define CPU_TYPE_ARM ((cpu_type_t) 12) +#define CPU_ARCH_ABI64 0x01000000 +#define CPU_TYPE_ARM64 (CPU_TYPE_ARM | CPU_ARCH_ABI64) +#endif +arch_t injector__get_system_arch(){ + size_t size; + cpu_type_t type = -1; + int mib[CTL_MAXNAME] = {0}; + size_t length = CTL_MAXNAME; + + if (sysctlnametomib("sysctl.proc_cputype", mib, &length) != 0){ + return ARCH_UNKNOWN; + } + + mib[length] = getpid(); + length++; + size = sizeof(cpu_type_t); + + if (sysctl(mib, (u_int)length, &type, &size, 0, 0) != 0){ + return ARCH_UNKNOWN; + } + if (CPU_TYPE_X86_64 == type) { + return ARCH_X86_64; + } + + if (CPU_TYPE_ARM64 == type) { + return ARCH_ARM64; + } + return ARCH_UNKNOWN; +} +const char *injector__arch2name(arch_t arch) +{ + switch (arch) { + case ARCH_X86_64: + return "x86_64"; + case ARCH_I386: + return "i386"; + case ARCH_ARM64: + return "ARM64"; + case ARCH_POWERPC_64: + return "PowerPC 64-bit"; + case ARCH_POWERPC: + return "PowerPC"; + case ARCH_UNKNOWN: + return "Unknown"; + } + return "?"; +} \ No newline at end of file diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/windows/injector.c b/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/windows/injector.c new file mode 100644 index 0000000..6854107 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/windows/injector.c @@ -0,0 +1,859 @@ +/* -*- indent-tabs-mode: nil -*- + * + * injector - Library for injecting a shared library into a Linux process + * + * URL: https://github.com/kubo/injector + * + * ------------------------------------------------------ + * + * Copyright (C) 2018-2023 Kubo Takehiro + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0602 +#endif +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include "injector.h" + +#if !defined(WDK_NTDDI_VERSION) || WDK_NTDDI_VERSION < 0x0A00000B +// Windows SDK version < 10.0.22000.0 +#define ProcessMachineTypeInfo 9 +typedef enum _MACHINE_ATTRIBUTES { + UserEnabled = 0x00000001, + KernelEnabled = 0x00000002, + Wow64Container = 0x00000004 +} MACHINE_ATTRIBUTES; +typedef struct _PROCESS_MACHINE_INFORMATION { + USHORT ProcessMachine; + USHORT Res0; + MACHINE_ATTRIBUTES MachineAttributes; +} PROCESS_MACHINE_INFORMATION; +#endif + +#pragma comment(lib, "advapi32.lib") +#pragma comment(lib, "dbghelp.lib") +#if !defined(PSAPI_VERSION) || PSAPI_VERSION == 1 +#pragma comment(lib, "psapi.lib") +#endif + +typedef BOOL (WINAPI *IsWow64Process2_t)(HANDLE hProcess, USHORT *pProcessMachine, USHORT *pNativeMachine); +typedef BOOL (WINAPI *GetProcessInformation_t)(HANDLE hProcess, PROCESS_INFORMATION_CLASS ProcessInformationClass, LPVOID ProcessInformation, DWORD ProcessInformationSize); + +static DWORD page_size = 0; +static size_t func_LoadLibraryW; +static size_t func_FreeLibrary; +static size_t func_GetLastError; +static size_t func_GetProcAddress; +static char errmsg[512]; + +typedef struct { + uint64_t func; + uint64_t arg1; + uint64_t arg2; + uint64_t arg3; + uint64_t arg4; + uint64_t arg5; + uint64_t arg6; +} remote_call_args_t; + +// size_t remote_call(remote_call_args_t *args) +// { +// size_t rv = args->func(args->arg1, args->arg2, args->arg3, args->arg4, args->arg5, args->arg6); +// *(DWORD*)args = GetLastError(); +// return rv; +// } +static const char x64_code_template[] = + + /* 0000: */ "\x40\x53" // push rbx + /* 0002: */ "\x48\x8B\xD9" // mov rbx,rcx ; preserve the first argument + /* 0005: */ "\x48\x83\xEC\x30" // sub rsp,30h ; align the stack pointer + /* 0009: */ "\x48\x8B\x41\x30" // mov rax,qword ptr [rcx+30h] ; set the 6th argument + /* 000D: */ "\x48\x89\x44\x24\x28" // mov qword ptr [rsp+28h],rax ; ditto + /* 0012: */ "\x48\x8B\x41\x28" // mov rax,qword ptr [rcx+28h] ; set the 5th argument + /* 0016: */ "\x48\x89\x44\x24\x20" // mov qword ptr [rsp+20h],rax ; ditto + /* 001B: */ "\x4C\x8B\x49\x20" // mov r9,qword ptr [rcx+20h] ; set the 4th argument + /* 001F: */ "\x4C\x8B\x41\x18" // mov r8,qword ptr [rcx+18h] ; set the 3rd argument + /* 0023: */ "\x48\x8B\x51\x10" // mov rdx,qword ptr [rcx+10h] ; set the 2nd argument + /* 0027: */ "\x48\x8B\x49\x08" // mov rcx,qword ptr [rcx+8] ; set the 1st argument + /* 002B: */ "\xFF\x13" // call qword ptr [rbx] ; call func + /* 002D: */ "\x48\x89\x03" // mov qword ptr [rbx],rax ; store the return value + /* 0030: */ "\xFF\x15\x0A\x00\x00\x00" // call GetLastError + /* 0036: */ "\x48\x83\xC4\x30" // add rsp,30h + /* 003A: */ "\x5B" // pop rbx + /* 003B: */ "\xC3" // ret + /* 003C: */ "\x90\x90\x90\x90" // nop; nop; nop; nop +#define X64_ADDR_GetLastError 0x0040 + /* 0040: */ "\x90\x90\x90\x90\x90\x90\x90\x90" + ; +#define X64_CODE_SIZE 0x0048 + +static const uint32_t arm64_code_template[] = { + /* 0000: */ 0xa9be7bfd, // stp x29, x30, [sp, #-32]! ; prolog + /* 0004: */ 0x910003fd, // mov x29, sp ; ditto + /* 0008: */ 0xf9000bf3, // str x19, [sp, #16] ; reserve x19 + /* 000c: */ 0xaa0003f3, // mov x19, x0 ; set args to x19 + /* 0010: */ 0xa9429664, // ldp x4, x5, [x19, #40] ; set 5th and 6th arguments + /* 0014: */ 0xa9418e62, // ldp x2, x3, [x19, #24] ; set 3rd and 4th arguments + /* 0018: */ 0xa9408660, // ldp x0, x1, [x19, #8] ; set 1st and 2nd arguments + /* 001c: */ 0xf9400269, // ldr x9, [x19] ; get args->func + /* 0020: */ 0xd63f0120, // blr x9 ; call args->func + /* 0024: */ 0xf9000260, // str x0, [x19] ; set the return value + /* 0028: */ 0xf9400bf3, // ldr x19, [sp, #16] ; restore x19 + /* 002c: */ 0xa8c27bfd, // ldp x29, x30, [sp], #32 ; epilog + /* 0030: */ 0x58000049, // ldr x9, ARM64_ADDR_GetLastError + /* 0034: */ 0xd61f0120, // br x9 +#define ARM64_ADDR_GetLastError 0x0038 + /* 0038: */ 0, + /* 003c: */ 0, +}; +#define ARM64_CODE_SIZE 0x0040 + +static const uint16_t armt_code_template[] = { + /* 0000: */ 0xb530, // push {r4, r5, lr} ; prolog + /* 0002: */ 0xb083, // sub sp, #12 ; reserve stack for arguments + /* 0004: */ 0x0004, // movs r4, r0 + /* 0006: */ 0x6b25, // ldr r5, [r4, #48] ; set the 6th argument + /* 0008: */ 0x9501, // str r5, [sp, #4] ; ditto + /* 000a: */ 0x6aa5, // ldr r5, [r4, #40] ; set the 5th argument + /* 000c: */ 0x9500, // str r5, [sp, #0] ; ditto + /* 000e: */ 0x6a23, // ldr r3, [r4, #32] ; set the 4th argument + /* 0010: */ 0x69a2, // ldr r2, [r4, #24] ; set the 3rd argument + /* 0012: */ 0x6921, // ldr r1, [r4, #16] ; set the 2nd argument + /* 0014: */ 0x68a0, // ldr r0, [r4, #8] ; set the 1st argument + /* 0016: */ 0x6825, // ldr r5, [r4, #0] ; get args->func + /* 0018: */ 0x47a8, // blx r5 ; call args->func + /* 001a: */ 0x6020, // str r0, [r4, #0] ; set the return value + /* 001c: */ 0x4d01, // ldr r5, [pc, #4] ; get the address of GetLastError (0x0024) + /* 001e: */ 0x47a8, // blx r5 ; call GetLastError + /* 0020: */ 0xb003, // add sp, #12 ; restore stack + /* 0022: */ 0xbd30, // pop {r4, r5, pc} ; epilog +#define ARMT_ADDR_GetLastError 0x0024 + /* 0024: */ 0, // .word + /* 0026: */ 0, // .word +}; +#define ARMT_CODE_SIZE 0x0028 + +static const char x86_code_template[] = + /* 0000: */ "\x55" // push ebp + /* 0001: */ "\x8B\xEC" // mov ebp,esp + /* 0003: */ "\x53" // push ebx + /* 0004: */ "\x8B\x5D\x08" // mov ebx,dword ptr [ebp+8] ; get args + /* 0007: */ "\xFF\x73\x30" // push dword ptr [ebx+30h] ; set the 6th argument + /* 000A: */ "\xFF\x73\x28" // push dword ptr [ebx+28h] ; set the 5th argument + /* 000D: */ "\xFF\x73\x20" // push dword ptr [ebx+20h] ; set the 4th argument + /* 0010: */ "\xFF\x73\x18" // push dword ptr [ebx+18h] ; set the 3rd argument + /* 0013: */ "\xFF\x73\x10" // push dword ptr [ebx+10h] ; set the 2nd argument + /* 0016: */ "\xFF\x73\x08" // push dword ptr [ebx+8] ; set the 1st argument + /* 0019: */ "\xFF\x13" // call dword ptr [ebx] ; call args->func + /* 001B: */ "\x89\x03" // mov dword ptr [ebx],eax ; store the return value +#define X86_CALL_GetLastError 0x001D + /* 001D: */ "\xE8\x00\x00\x00\x00" // call GetLastError + /* 0022: */ "\x8B\x5D\xF8" // mov ebx,dword ptr [ebp-8] + /* 0025: */ "\xC9" // leave + /* 0026: */ "\xC2\x04\x00" // ret 4 + ; + +#define X86_CODE_SIZE 0x0029 + +#ifdef _M_AMD64 +#define CURRENT_ARCH "x64" +#define CURRENT_IMAGE_FILE_MACHINE IMAGE_FILE_MACHINE_AMD64 +#endif +#ifdef _M_ARM64 +#define CURRENT_ARCH "arm64" +#define CURRENT_IMAGE_FILE_MACHINE IMAGE_FILE_MACHINE_ARM64 +#endif +#ifdef _M_ARMT +#define CURRENT_ARCH "arm" +#define CURRENT_IMAGE_FILE_MACHINE IMAGE_FILE_MACHINE_ARMNT +#endif +#ifdef _M_IX86 +#define CURRENT_ARCH "x86" +#define CURRENT_IMAGE_FILE_MACHINE IMAGE_FILE_MACHINE_I386 +#endif + +#define CODE_SIZE __max(__max(X64_CODE_SIZE, ARM64_CODE_SIZE), __max(X86_CODE_SIZE, ARMT_CODE_SIZE)) + +static BOOL CallIsWow64Process2(HANDLE hProcess, USHORT *pProcessMachine, USHORT *pNativeMachine) +{ + static IsWow64Process2_t IsWow64Process2_func = (IsWow64Process2_t)-1; + if (IsWow64Process2_func == (IsWow64Process2_t)-1) { + IsWow64Process2_func = (IsWow64Process2_t)GetProcAddress(GetModuleHandleA("kernel32"), "IsWow64Process2"); + } + if (IsWow64Process2_func == NULL) { + return FALSE; + } + return IsWow64Process2_func(hProcess, pProcessMachine, pNativeMachine); +} +#define IsWow64Process2 CallIsWow64Process2 + +static BOOL CallGetProcessInformation(HANDLE hProcess, PROCESS_INFORMATION_CLASS ProcessInformationClass, LPVOID ProcessInformation, DWORD ProcessInformationSize) +{ + static GetProcessInformation_t GetProcessInformation_func = (GetProcessInformation_t)-1; + if (GetProcessInformation_func == (GetProcessInformation_t)-1) { + GetProcessInformation_func = (GetProcessInformation_t)GetProcAddress(GetModuleHandleA("kernel32"), "GetProcessInformation"); + } + if (GetProcessInformation_func == NULL) { + return FALSE; + } + return GetProcessInformation_func(hProcess, ProcessInformationClass, ProcessInformation, ProcessInformationSize); +} +#define GetProcessInformation CallGetProcessInformation + +static void set_errmsg(const char *format, ...); +static const char *w32strerr(DWORD err); +static USHORT process_arch(HANDLE hProcess); +static const char *arch_name(USHORT arch); + +struct injector { + HANDLE hProcess; + USHORT arch; + char *code; + char *data; + size_t load_library; + size_t free_library; + size_t get_last_error; + size_t get_proc_address; +}; + +static BOOL init(void) +{ + SYSTEM_INFO si; + HANDLE hToken; + LUID luid; + TOKEN_PRIVILEGES tp; + HMODULE kernel32 = GetModuleHandleA("kernel32"); + + GetSystemInfo(&si); + page_size = si.dwPageSize; + func_LoadLibraryW = (size_t)GetProcAddress(kernel32, "LoadLibraryW"); + func_FreeLibrary = (size_t)GetProcAddress(kernel32, "FreeLibrary"); + func_GetProcAddress = (size_t)GetProcAddress(kernel32, "GetProcAddress"); + func_GetLastError = (size_t)GetProcAddress(kernel32, "GetLastError"); + + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) { + return FALSE; + } + if (!LookupPrivilegeValue(0, SE_DEBUG_NAME, &luid)) { + CloseHandle(hToken); + return FALSE; + } + tp.PrivilegeCount = 1; + tp.Privileges[0].Luid = luid; + tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + if (!AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL)) { + CloseHandle(hToken); + return FALSE; + } + CloseHandle(hToken); + return TRUE; +} + +static DWORD name_index(IMAGE_NT_HEADERS *nt_hdrs, void *base, const DWORD *names, DWORD num_names, const char *name) +{ + DWORD idx; + for (idx = 0; idx < num_names; idx++) { + if (strcmp((const char*)ImageRvaToVa(nt_hdrs, base, names[idx], NULL), name) == 0) { + return idx; + } + } + set_errmsg("Could not find the address of %s", name); + return (DWORD)-1; +} + +static int funcaddr(DWORD pid, injector_t *injector) +{ + HANDLE hSnapshot; + MODULEENTRY32W me; + BOOL ok; + HANDLE hFile = INVALID_HANDLE_VALUE; + HANDLE hFileMapping = NULL; + void *base = NULL; + IMAGE_NT_HEADERS *nt_hdrs; + ULONG exp_size; + const IMAGE_EXPORT_DIRECTORY *exp; + const DWORD *names, *funcs; + const WORD *ordinals; + DWORD idx; + int rv = INJERR_OTHER; + + /* Get the full path of kernel32.dll. */ +retry: + hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, pid); + if (hSnapshot == INVALID_HANDLE_VALUE) { + DWORD err = GetLastError(); + switch (err) { + case ERROR_BAD_LENGTH: + goto retry; + case ERROR_ACCESS_DENIED: + rv = INJERR_PERMISSION; + break; + case ERROR_INVALID_PARAMETER: + rv = INJERR_NO_PROCESS; + break; + default: + rv = INJERR_OTHER; + } + set_errmsg("CreateToolhelp32Snapshot error: %s", w32strerr(err)); + return rv; + } + me.dwSize = sizeof(me); + for (ok = Module32FirstW(hSnapshot, &me); ok; ok = Module32NextW(hSnapshot, &me)) { + if (wcsicmp(me.szModule, L"kernel32.dll") == 0) { + break; + } + } + CloseHandle(hSnapshot); + if (!ok) { + set_errmsg("kernel32.dll could not be found."); + return INJERR_OTHER; + } + + /* Get the export directory in the kernel32.dll. */ + hFile = CreateFileW(me.szExePath, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) { + set_errmsg("failed to open file %s: %s", me.szExePath, w32strerr(GetLastError())); + goto exit; + } + hFileMapping = CreateFileMappingA(hFile, NULL, PAGE_READONLY, 0, 0, NULL); + if (hFileMapping == NULL) { + set_errmsg("failed to create file mapping of %s: %s", me.szExePath, w32strerr(GetLastError())); + goto exit; + } + base = MapViewOfFile(hFileMapping, FILE_MAP_READ, 0, 0, 0); + if (base == NULL) { + set_errmsg("failed to map file %s to memory: %s", me.szExePath, w32strerr(GetLastError())); + goto exit; + } + nt_hdrs = ImageNtHeader(base); + if (nt_hdrs == NULL) { + set_errmsg("ImageNtHeader error: %s", w32strerr(GetLastError())); + goto exit; + } + exp = (const IMAGE_EXPORT_DIRECTORY *)ImageDirectoryEntryToDataEx(base, FALSE, IMAGE_DIRECTORY_ENTRY_EXPORT, &exp_size, NULL); + if (exp == NULL) { + set_errmsg("ImageDirectoryEntryToDataEx error: %s", w32strerr(GetLastError())); + goto exit; + } + if (exp->NumberOfNames == 0) { + set_errmsg("No export entires are not found."); + goto exit; + } + names = (const DWORD*)ImageRvaToVa(nt_hdrs, base, exp->AddressOfNames, NULL); + if (names == NULL) { + set_errmsg("ImageRvaToVa error: %s", w32strerr(GetLastError())); + goto exit; + } + ordinals = (const WORD*)ImageRvaToVa(nt_hdrs, base, exp->AddressOfNameOrdinals, NULL); + if (ordinals == NULL) { + set_errmsg("ImageRvaToVa error: %s", w32strerr(GetLastError())); + goto exit; + } + funcs = (const DWORD*)ImageRvaToVa(nt_hdrs, base, exp->AddressOfFunctions, NULL); + if (funcs == NULL) { + set_errmsg("ImageRvaToVa error: %s", w32strerr(GetLastError())); + goto exit; + } + + /* Find the address of LoadLibraryW */ + idx = name_index(nt_hdrs, base, names, exp->NumberOfNames, "LoadLibraryW"); + if (idx == (DWORD)-1) { + goto exit; + } + injector->load_library = (size_t)me.modBaseAddr + funcs[ordinals[idx]]; + + /* Find the address of FreeLibrary */ + idx = name_index(nt_hdrs, base, names, exp->NumberOfNames, "FreeLibrary"); + if (idx == (DWORD)-1) { + goto exit; + } + injector->free_library = (size_t)me.modBaseAddr + funcs[ordinals[idx]]; + + /* Find the address of GetProcAddress */ + idx = name_index(nt_hdrs, base, names, exp->NumberOfNames, "GetProcAddress"); + if (idx == (DWORD)-1) { + goto exit; + } + injector->get_proc_address = (size_t)me.modBaseAddr + funcs[ordinals[idx]]; + + /* Find the address of GetLastError */ + idx = name_index(nt_hdrs, base, names, exp->NumberOfNames, "GetLastError"); + if (idx == (DWORD)-1) { + goto exit; + } + injector->get_last_error = (size_t)me.modBaseAddr + funcs[ordinals[idx]]; + rv = 0; +exit: + if (base != NULL) { + UnmapViewOfFile(base); + } + if (hFileMapping != NULL) { + CloseHandle(hFileMapping); + } + if (hFile != INVALID_HANDLE_VALUE) { + CloseHandle(hFile); + } + return rv; +} + +static int remote_call(injector_t *injector, remote_call_args_t *args, size_t size, intptr_t *retval, DWORD *last_error) +{ + char *code = injector->code; + HANDLE hThread; + SIZE_T sz; + + if (injector->arch == IMAGE_FILE_MACHINE_ARMNT) { + ++code; + } + if (!WriteProcessMemory(injector->hProcess, injector->data, args, size, &sz)) { + set_errmsg("WriteProcessMemory error: %s", w32strerr(GetLastError())); + return INJERR_OTHER; + } + hThread = CreateRemoteThread(injector->hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)code, injector->data, 0, NULL); + if (hThread == NULL) { + set_errmsg("CreateRemoteThread error: %s", w32strerr(GetLastError())); + return INJERR_OTHER; + } + WaitForSingleObject(hThread, INFINITE); + if (last_error) { + GetExitCodeThread(hThread, last_error); + } + CloseHandle(hThread); + if (retval) { + union { + size_t s; + uint32_t u32; + } val; + size_t valsize = sizeof(size_t); + switch (injector->arch) { + case IMAGE_FILE_MACHINE_ARMNT: + case IMAGE_FILE_MACHINE_I386: + valsize = 4; + break; + } + if (!ReadProcessMemory(injector->hProcess, injector->data, &val, valsize, &sz)) { + set_errmsg("ReadProcessMemory error: %s", w32strerr(GetLastError())); + return INJERR_OTHER; + } + if (valsize == 4) { + *retval = val.u32; + } else { + *retval = val.s; + } + } + return 0; +} + +int injector_attach(injector_t **injector_out, DWORD pid) +{ + injector_t *injector; + DWORD dwDesiredAccess = + PROCESS_QUERY_LIMITED_INFORMATION | /* for IsWow64Process() */ + PROCESS_CREATE_THREAD | /* for CreateRemoteThread() */ + PROCESS_VM_OPERATION | /* for VirtualAllocEx() */ + PROCESS_VM_READ | /* for ReadProcessMemory() */ + PROCESS_VM_WRITE; /* for WriteProcessMemory() */ + DWORD old_protect; + SIZE_T written; + int rv; + char code[CODE_SIZE]; + size_t code_size; + + if (page_size == 0) { + init(); + } + + injector = calloc(1, sizeof(injector_t)); + if (injector == NULL) { + set_errmsg("malloc error: %s", strerror(errno)); + return INJERR_NO_MEMORY; + } + injector->hProcess = OpenProcess(dwDesiredAccess, FALSE, pid); + if (injector->hProcess == NULL) { + DWORD err = GetLastError(); + set_errmsg("OpenProcess error: %s", w32strerr(err)); + switch (err) { + case ERROR_ACCESS_DENIED: + rv = INJERR_PERMISSION; + break; + case ERROR_INVALID_PARAMETER: + rv = INJERR_NO_PROCESS; + break; + default: + rv = INJERR_OTHER; + } + goto error_exit; + } + injector->load_library = func_LoadLibraryW; + injector->free_library = func_FreeLibrary; + injector->get_last_error = func_GetLastError; + injector->get_proc_address = func_GetProcAddress; + + injector->arch = process_arch(injector->hProcess); + switch (injector->arch) { +#if defined(_M_ARM64) // arm64 + case IMAGE_FILE_MACHINE_ARM64: + case IMAGE_FILE_MACHINE_ARMNT: + break; +#endif +#if defined(_M_AMD64) // x64 + static USHORT native_machine = IMAGE_FILE_MACHINE_UNKNOWN; + case IMAGE_FILE_MACHINE_AMD64: + break; + case IMAGE_FILE_MACHINE_I386: + if (native_machine == IMAGE_FILE_MACHINE_UNKNOWN) { + USHORT dummy; + if (!IsWow64Process2(GetCurrentProcess(), &dummy, &native_machine)) { + native_machine = IMAGE_FILE_MACHINE_AMD64; + } + } + if (native_machine == IMAGE_FILE_MACHINE_AMD64) { + // x86 process on Windows x64 + break; + } + // x86 process on Windows arm64 + // FALL THROUGH +#endif +#if defined(_M_IX86) // x86 + case IMAGE_FILE_MACHINE_I386: + break; +#endif +#if defined(_M_ARMT) // arm32 + case IMAGE_FILE_MACHINE_ARMNT: + break; +#endif + default: + set_errmsg("%s target process isn't supported by %s process.", + arch_name(injector->arch), CURRENT_ARCH); + rv = INJERR_UNSUPPORTED_TARGET; + goto error_exit; + } + + if (injector->arch != CURRENT_IMAGE_FILE_MACHINE) { + rv = funcaddr(pid, injector); + if (rv != 0) { + goto error_exit; + } + } + + injector->code = VirtualAllocEx(injector->hProcess, NULL, 2 * page_size, + MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READ); + if (injector->code == NULL) { + set_errmsg("VirtualAllocEx error: %s", w32strerr(GetLastError())); + rv = INJERR_OTHER; + goto error_exit; + } + injector->data = injector->code + page_size; + switch (injector->arch) { + case IMAGE_FILE_MACHINE_AMD64: /* x64 */ + memcpy(code, x64_code_template, X64_CODE_SIZE); + code_size = X64_CODE_SIZE; + *(size_t*)(code + X64_ADDR_GetLastError) = injector->get_last_error; + break; + case IMAGE_FILE_MACHINE_ARM64: /* arm64 */ + memcpy(code, arm64_code_template, ARM64_CODE_SIZE); + code_size = ARM64_CODE_SIZE; + *(size_t*)(code + ARM64_ADDR_GetLastError) = injector->get_last_error; + break; + case IMAGE_FILE_MACHINE_ARMNT: /* arm (thumb mode) */ + memcpy(code, armt_code_template, ARMT_CODE_SIZE); + code_size = ARMT_CODE_SIZE; + *(uint32_t*)(code + ARMT_ADDR_GetLastError) = (uint32_t)injector->get_last_error; + break; + case IMAGE_FILE_MACHINE_I386: /* x86 */ + memcpy(code, x86_code_template, X86_CODE_SIZE); + code_size = X86_CODE_SIZE; +#define FIX_CALL_RELATIVE(addr, offset) *(uint32_t*)(code + offset + 1) = addr - ((uint32_t)(size_t)injector->code + offset + 5) + FIX_CALL_RELATIVE(injector->get_last_error, X86_CALL_GetLastError); + break; + default: + set_errmsg("Never reach here: arch=0x%x", injector->arch); + rv = INJERR_OTHER; + goto error_exit; + } + + if (!WriteProcessMemory(injector->hProcess, injector->code, code, code_size, &written)) { + set_errmsg("WriteProcessMemory error: %s", w32strerr(GetLastError())); + rv = INJERR_OTHER; + goto error_exit; + } + + if (!VirtualProtectEx(injector->hProcess, injector->data, page_size, PAGE_READWRITE, &old_protect)) { + set_errmsg("VirtualProtectEx error: %s", w32strerr(GetLastError())); + rv = INJERR_OTHER; + goto error_exit; + } + + *injector_out = injector; + return 0; +error_exit: + injector_detach(injector); + return rv; +} + +int injector_inject(injector_t *injector, const char *path, void **handle) +{ + DWORD pathlen = (DWORD)strlen(path); + wchar_t *wpath; + DWORD wpathlen; + + if (pathlen == 0) { + set_errmsg("The specified path is empty."); + return INJERR_FILE_NOT_FOUND; + } + if (pathlen > MAX_PATH) { + set_errmsg("too long file path: %s", path); + return INJERR_FILE_NOT_FOUND; + } + + wpath = _alloca((pathlen + 1) * sizeof(wchar_t)); + wpathlen = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, path, pathlen, wpath, pathlen + 1); + wpath[wpathlen] = L'\0'; + return injector_inject_w(injector, wpath, handle); +} + +int injector_inject_w(injector_t *injector, const wchar_t *path, void **handle) +{ + struct { + remote_call_args_t args; + wchar_t fullpath[MAX_PATH]; + } data = {0, }; + DWORD pathlen; + intptr_t retval; + DWORD last_error; + int rv; + + pathlen = GetFullPathNameW(path, MAX_PATH, data.fullpath, NULL); + if (pathlen > MAX_PATH) { + set_errmsg("too long file path: %S", path); + return INJERR_FILE_NOT_FOUND; + } + if (pathlen == 0) { + set_errmsg("failed to get the full path: %S", path); + return INJERR_FILE_NOT_FOUND; + } + data.args.func = injector->load_library; + data.args.arg1 = (size_t)injector->data + sizeof(remote_call_args_t); + + rv = remote_call(injector, &data.args, sizeof(data), &retval, &last_error); + if (rv != 0) { + return rv; + } + if (retval == 0) { + set_errmsg("LoadLibrary in the target process failed: %s", w32strerr(last_error)); + return INJERR_ERROR_IN_TARGET; + } + if (handle != NULL) { + *handle = (void*)retval; + } + return 0; +} + +int injector_uninject(injector_t *injector, void *handle) +{ + remote_call_args_t args; + DWORD pathlen; + size_t retval; + DWORD last_error; + int rv; + + args.func = injector->free_library; + args.arg1 = (size_t)handle; + + rv = remote_call(injector, &args, sizeof(args), &retval, &last_error); + if (rv != 0) { + return rv; + } + if ((BOOL)retval) { + return 0; + } else { + set_errmsg("FreeLibrary in the target process failed: %s", w32strerr(last_error)); + return INJERR_ERROR_IN_TARGET; + } +} + +int injector_detach(injector_t *injector) +{ + if (injector->code != NULL) { + VirtualFreeEx(injector->hProcess, injector->code, 0, MEM_RELEASE); + } + if (injector->hProcess != NULL) { + CloseHandle(injector->hProcess); + } + free(injector); + return 0; +} + +const char *injector_error(void) +{ + return errmsg; +} + +int injector_remote_func_addr(injector_t *injector, void *handle, const char* name, size_t *func_addr_out) +{ + struct { + remote_call_args_t args; + char name[512]; + } data = {0, }; + intptr_t retval; + DWORD last_error; + int rv; + + if (strlen(name) >= sizeof(data.name)) { + set_errmsg("too long function name: %s", name); + return INJERR_FUNCTION_MISSING; + } + + data.args.func = injector->get_proc_address; + data.args.arg1 = (size_t)handle; + data.args.arg2 = (size_t)injector->data + sizeof(remote_call_args_t); + strncpy(data.name, name, sizeof(data.name)); + rv = remote_call(injector, &data.args, sizeof(data), &retval, &last_error); + if (rv != 0) { + return rv; + } + if (retval == 0) { + set_errmsg("GetProcAddress in the target process failed: %s", w32strerr(last_error)); + return INJERR_ERROR_IN_TARGET; + } + *func_addr_out = (size_t)retval; + return 0; +} + +int injector_remote_call(injector_t *injector, intptr_t *retval, size_t func_addr, ...) +{ + va_list ap; + int rv; + + va_start(ap, func_addr); + rv = injector_remote_vcall(injector, retval, func_addr, ap); + va_end(ap); + return rv; +} + +int injector_remote_vcall(injector_t *injector, intptr_t *retval, size_t func_addr, va_list ap) +{ + remote_call_args_t args = {0,}; + + args.func = func_addr; + args.arg1 = va_arg(ap, size_t); + args.arg2 = va_arg(ap, size_t); + args.arg3 = va_arg(ap, size_t); + args.arg4 = va_arg(ap, size_t); + args.arg5 = va_arg(ap, size_t); + args.arg6 = va_arg(ap, size_t); + return remote_call(injector, &args, sizeof(args), retval, NULL); +} + +static void set_errmsg(const char *format, ...) +{ + va_list ap; + int rv; + + va_start(ap, format); + rv = vsnprintf(errmsg, sizeof(errmsg), format, ap); + va_end(ap); + if (rv == -1 || rv >= sizeof(errmsg)) { + errmsg[sizeof(errmsg) - 1] = '\0'; + } +} + +static const char *w32strerr(DWORD err) +{ + static char errmsg[512]; + DWORD len; + + len = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, err, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), + errmsg, sizeof(errmsg), NULL); + if (len > 0) { + while (len > 0) { + char c = errmsg[len - 1]; + if (c == ' ' || c == '\n' || c == '\r') { + len--; + } else { + break; + } + } + errmsg[len] = '\0'; + } else if ((int)err >= 0) { + sprintf(errmsg, "win32 error code %d", err); + } else { + sprintf(errmsg, "win32 error code 0x%x", err); + } + return errmsg; +} + +static USHORT process_arch(HANDLE hProcess) +{ + PROCESS_MACHINE_INFORMATION pmi; + if (GetProcessInformation(hProcess, ProcessMachineTypeInfo, &pmi, sizeof(pmi))) { + // Windows 11 + return pmi.ProcessMachine; + } + USHORT process_machine; + USHORT native_machine; + if (IsWow64Process2(hProcess, &process_machine, &native_machine)) { + // Windows 10 + if (process_machine != IMAGE_FILE_MACHINE_UNKNOWN) { + return process_machine; + } else { + return native_machine; + } + } + /* Windows 8.1 or earlier */ + /* arch will be either x86 or x64. */ +#if defined(_M_AMD64) || defined(_M_IX86) + BOOL is_wow64_proc; +#if defined(_M_IX86) + if (IsWow64Process(GetCurrentProcess(), &is_wow64_proc) && !is_wow64_proc) { + // Run on 32-bit Windows + return IMAGE_FILE_MACHINE_I386; + } +#endif + if (IsWow64Process(hProcess, &is_wow64_proc)) { + return is_wow64_proc ? IMAGE_FILE_MACHINE_I386 : IMAGE_FILE_MACHINE_AMD64; + } +#endif + return IMAGE_FILE_MACHINE_UNKNOWN; +} + +static const char *arch_name(USHORT arch) +{ + switch (arch) { + case IMAGE_FILE_MACHINE_AMD64: + return "x64"; + case IMAGE_FILE_MACHINE_ARM64: + return "arm64"; + case IMAGE_FILE_MACHINE_ARMNT: + return "arm"; + case IMAGE_FILE_MACHINE_I386: + return "x86"; + default: + return "unknown"; + } +} diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/windows/injector.def b/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/windows/injector.def new file mode 100644 index 0000000..53b997d --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/kubo_injector/src/windows/injector.def @@ -0,0 +1,9 @@ +EXPORTS + injector_attach + injector_inject + injector_detach + injector_error + injector_inject_w + injector_remote_func_addr + injector_remote_call + injector_remote_vcall diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/linux_detours/include/detours.h b/third-party/RemoteInput/RemoteInput/Thirdparty/linux_detours/include/detours.h new file mode 100644 index 0000000..ee8318e --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/linux_detours/include/detours.h @@ -0,0 +1,818 @@ +#pragma once +///////////////////////////////////////////////////////////////////////////// +// +// Core Detours Functionality +// +// + +#ifndef _DETOURS_H_ +#define _DETOURS_H_ + +#define DETOURS_VERSION 0x4c0c1 // 0xMAJORcMINORcPATCH + +////////////////////////////////////////////////////////////////////////////// +// +#include "types.h" + +#undef DETOURS_X64 +#undef DETOURS_X86 +#undef DETOURS_IA64 +#undef DETOURS_ARM +#undef DETOURS_ARM64 +#undef DETOURS_BITS +#undef DETOURS_32BIT +#undef DETOURS_64BIT + +#if defined(_X86_) +#define DETOURS_X86 +#define DETOURS_OPTION_BITS 64 + +#elif defined(_AMD64_) +#define DETOURS_X64 +#define DETOURS_OPTION_BITS 32 + +#elif defined(_IA64_) +#define DETOURS_IA64 +#define DETOURS_OPTION_BITS 32 + +#elif defined(_ARM_) +#define DETOURS_ARM +#if defined(_ARM32_) +#define DETOURS_ARM32 +#endif +#elif defined(_ARM64_) +#define DETOURS_ARM64 + +#else +#error Unknown architecture (x86, amd64, ia64, arm, arm64) +#endif + +#ifdef _UNIX64 +#undef DETOURS_32BIT +#define DETOURS_64BIT 1 +#define DETOURS_BITS 64 +// If all 64bit kernels can run one and only one 32bit architecture. +//#define DETOURS_OPTION_BITS 32 +#else +#define DETOURS_32BIT 1 +#undef DETOURS_64BIT +#define DETOURS_BITS 32 +// If all 64bit kernels can run one and only one 32bit architecture. +//#define DETOURS_OPTION_BITS 32 +#endif + +#define VER_DETOURS_BITS DETOUR_STRINGIFY(DETOURS_BITS) +#define DetourExport __attribute__((visibility("default"))) +////////////////////////////////////////////////////////////////////////////// +// + +#if (_MSC_VER < 1299) +typedef UINT64 LONG_PTR; +typedef UINT64 ULONG_PTR; +#endif +#define ERROR_INVALID_OPERATION 4317L +#define ERROR_DS_DRA_INVALID_PARAMETER 8437L +#define ERROR_NOT_ENOUGH_MEMORY 8L // dderror +#define ERROR_INVALID_BLOCK 9L +#define ERROR_INVALID_HANDLE 6L +#define ERROR_INVALID_PARAMETER 87L // dderror +#define __debugbreak() +///////////////////////////////////////////////// SAL 2.0 Annotations w/o SAL. +// +// These definitions are include so that Detours will build even if the +// compiler doesn't have full SAL 2.0 support. +// +#ifndef DETOURS_DONT_REMOVE_SAL_20 + +#ifdef DETOURS_TEST_REMOVE_SAL_20 +#undef _Analysis_assume_ +#undef _Benign_race_begin_ +#undef _Benign_race_end_ +#undef _Field_range_ +#undef _Field_size_ +#undef _In_ +#undef _In_bytecount_ +#undef _In_count_ +#undef _In_opt_ +#undef _In_opt_bytecount_ +#undef _In_opt_count_ +#undef _In_opt_z_ +#undef _In_range_ +#undef _In_reads_ +#undef _In_reads_bytes_ +#undef _In_reads_opt_ +#undef _In_reads_opt_bytes_ +#undef _In_reads_or_z_ +#undef _In_z_ +#undef _Inout_ +#undef _Inout_opt_ +#undef _Inout_z_count_ +#undef _Out_ +#undef _Out_opt_ +#undef _Out_writes_ +#undef _Outptr_result_maybenull_ +#undef _Readable_bytes_ +#undef _Success_ +#undef _Writable_bytes_ +#undef _Pre_notnull_ +#endif + +#if defined(_Deref_out_opt_z_) && !defined(_Outptr_result_maybenull_) +#define _Outptr_result_maybenull_ _Deref_out_opt_z_ +#endif + +#if defined(_In_count_) && !defined(_In_reads_) +#define _In_reads_(x) _In_count_(x) +#endif + +#if defined(_In_opt_count_) && !defined(_In_reads_opt_) +#define _In_reads_opt_(x) _In_opt_count_(x) +#endif + +#if defined(_In_opt_bytecount_) && !defined(_In_reads_opt_bytes_) +#define _In_reads_opt_bytes_(x) _In_opt_bytecount_(x) +#endif + +#if defined(_In_bytecount_) && !defined(_In_reads_bytes_) +#define _In_reads_bytes_(x) _In_bytecount_(x) +#endif + +#ifndef _In_ +#define _In_ +#endif + +#ifndef _In_bytecount_ +#define _In_bytecount_(x) +#endif + +#ifndef _In_count_ +#define _In_count_(x) +#endif + +#ifndef _In_opt_ +#define _In_opt_ +#endif + +#ifndef _In_opt_bytecount_ +#define _In_opt_bytecount_(x) +#endif + +#ifndef _In_opt_count_ +#define _In_opt_count_(x) +#endif + +#ifndef _In_opt_z_ +#define _In_opt_z_ +#endif + +#ifndef _In_range_ +#define _In_range_(x,y) +#endif + +#ifndef _In_reads_ +#define _In_reads_(x) +#endif + +#ifndef _In_reads_bytes_ +#define _In_reads_bytes_(x) +#endif + +#ifndef _In_reads_opt_ +#define _In_reads_opt_(x) +#endif + +#ifndef _In_reads_opt_bytes_ +#define _In_reads_opt_bytes_(x) +#endif + +#ifndef _In_reads_or_z_ +#define _In_reads_or_z_ +#endif + +#ifndef _In_z_ +#define _In_z_ +#endif + +#ifndef _Inout_ +#define _Inout_ +#endif + +#ifndef _Inout_opt_ +#define _Inout_opt_ +#endif + +#ifndef _Inout_z_count_ +#define _Inout_z_count_(x) +#endif + +#ifndef _Out_ +#define _Out_ +#endif + +#ifndef _Out_opt_ +#define _Out_opt_ +#endif + +#ifndef _Out_writes_ +#define _Out_writes_(x) +#endif + +#ifndef _Outptr_result_maybenull_ +#define _Outptr_result_maybenull_ +#endif + +#ifndef _Writable_bytes_ +#define _Writable_bytes_(x) +#endif + +#ifndef _Readable_bytes_ +#define _Readable_bytes_(x) +#endif + +#ifndef _Success_ +#define _Success_(x) +#endif + +#ifndef _Pre_notnull_ +#define _Pre_notnull_ +#endif + +#ifdef DETOURS_INTERNAL + +#ifndef _Benign_race_begin_ +#define _Benign_race_begin_ +#endif + +#ifndef _Benign_race_end_ +#define _Benign_race_end_ +#endif + +#ifndef _Field_size_ +#define _Field_size_(x) +#endif + +#ifndef _Field_range_ +#define _Field_range_(x,y) +#endif + +#ifndef _Analysis_assume_ +#define _Analysis_assume_(x) +#endif + +#endif // DETOURS_INTERNAL +#endif // DETOURS_DONT_REMOVE_SAL_20 + +////////////////////////////////////////////////////////////////////////////// +// +#ifndef GUID_DEFINED +#define GUID_DEFINED +typedef struct _GUID +{ + DWORD Data1; + WORD Data2; + WORD Data3; + BYTE Data4[8]; +} GUID; + +#ifdef INITGUID +#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + const GUID name \ + = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } +#else +#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + const GUID name +#endif // INITGUID +#endif // !GUID_DEFINED + +#if defined(__cplusplus) +#ifndef _REFGUID_DEFINED +#define _REFGUID_DEFINED +#define REFGUID const GUID & +#endif // !_REFGUID_DEFINED +#else // !__cplusplus +#ifndef _REFGUID_DEFINED +#define _REFGUID_DEFINED +#define REFGUID const GUID * const +#endif // !_REFGUID_DEFINED +#endif // !__cplusplus + +#ifndef ARRAYSIZE +#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0])) +#endif + +// +////////////////////////////////////////////////////////////////////////////// + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + + /////////////////////////////////////////////////// Instruction Target Macros. + // +#define DETOUR_INSTRUCTION_TARGET_NONE ((PVOID)0) +#define DETOUR_INSTRUCTION_TARGET_DYNAMIC ((PVOID)(LONG_PTR)-1) +#define DETOUR_SECTION_HEADER_SIGNATURE 0x00727444 // "Dtr\0" + + extern const GUID DETOUR_EXE_RESTORE_GUID; + extern const GUID DETOUR_EXE_HELPER_GUID; + +#define DETOUR_TRAMPOLINE_SIGNATURE 0x21727444 // Dtr! + typedef struct _DETOUR_TRAMPOLINE DETOUR_TRAMPOLINE, *PDETOUR_TRAMPOLINE; + + /////////////////////////////////////////////////////////// Binary Structures. + // + + + +#define DETOUR_SECTION_HEADER_DECLARE(cbSectionSize) \ +{ \ + sizeof(DETOUR_SECTION_HEADER),\ + DETOUR_SECTION_HEADER_SIGNATURE,\ + sizeof(DETOUR_SECTION_HEADER),\ + (cbSectionSize),\ + \ + 0,\ + 0,\ + 0,\ + 0,\ + \ + 0,\ + 0,\ + 0,\ + 0,\ +} + + /////////////////////////////////////////////////////////////// Helper Macros. + // +#define DETOURS_STRINGIFY(x) DETOURS_STRINGIFY_(x) +#define DETOURS_STRINGIFY_(x) #x + + ///////////////////////////////////////////////////////////// Binary Typedefs. + // + /* + typedef BOOL(CALLBACK *PF_DETOUR_BINARY_BYWAY_CALLBACK)( + _In_opt_ PVOID pContext, + _In_opt_ LPCSTR pszFile, + _Outptr_result_maybenull_ LPCSTR *ppszOutFile); + + typedef BOOL(CALLBACK *PF_DETOUR_BINARY_FILE_CALLBACK)( + _In_opt_ PVOID pContext, + _In_ LPCSTR pszOrigFile, + _In_ LPCSTR pszFile, + _Outptr_result_maybenull_ LPCSTR *ppszOutFile); + + typedef BOOL(CALLBACK *PF_DETOUR_BINARY_SYMBOL_CALLBACK)( + _In_opt_ PVOID pContext, + _In_ ULONG nOrigOrdinal, + _In_ ULONG nOrdinal, + _Out_ ULONG *pnOutOrdinal, + _In_opt_ LPCSTR pszOrigSymbol, + _In_opt_ LPCSTR pszSymbol, + _Outptr_result_maybenull_ LPCSTR *ppszOutSymbol); + + typedef BOOL(CALLBACK *PF_DETOUR_BINARY_COMMIT_CALLBACK)( + _In_opt_ PVOID pContext); + + typedef BOOL(CALLBACK *PF_DETOUR_ENUMERATE_EXPORT_CALLBACK)(_In_opt_ PVOID pContext, + _In_ ULONG nOrdinal, + _In_opt_ LPCSTR pszName, + _In_opt_ PVOID pCode); + + typedef BOOL(CALLBACK *PF_DETOUR_IMPORT_FILE_CALLBACK)(_In_opt_ PVOID pContext, + _In_opt_ HMODULE hModule, + _In_opt_ LPCSTR pszFile); + + typedef BOOL(CALLBACK *PF_DETOUR_IMPORT_FUNC_CALLBACK)(_In_opt_ PVOID pContext, + _In_ DWORD nOrdinal, + _In_opt_ LPCSTR pszFunc, + _In_opt_ PVOID pvFunc); + + // Same as PF_DETOUR_IMPORT_FUNC_CALLBACK but extra indirection on last parameter. + typedef BOOL(CALLBACK *PF_DETOUR_IMPORT_FUNC_CALLBACK_EX)(_In_opt_ PVOID pContext, + _In_ DWORD nOrdinal, + _In_opt_ LPCSTR pszFunc, + _In_opt_ PVOID* ppvFunc);*/ + + typedef VOID * PDETOUR_BINARY; + typedef VOID * PDETOUR_LOADED_BINARY; + + //////////////////////////////////////////////////////////// Transaction APIs. + // + LONG DetourTransactionBegin(VOID); + LONG DetourTransactionAbort(VOID); + LONG DetourTransactionCommit(VOID); + + + ////////////////////////////////////////////////////////////////////////////// + // + // dotnet trampoline barrier definitions + // +#define MAX_HOOK_COUNT 1024 +#define MAX_ACE_COUNT 128 +#define MAX_THREAD_COUNT 128 +#define MAX_PASSTHRU_SIZE 1024 * 64 + +#define DETOUR_ASSERT(expr, Msg) RtlAssert((BOOL)(expr),(LPCWSTR) Msg); +#define THROW(code, Msg) { NtStatus = (code); RtlSetLastError(0, NtStatus, Msg); goto THROW_OUTRO; } + +#define RTL_SUCCESS(ntstatus) SUCCEEDED(ntstatus) + +#define STATUS_SUCCESS 0 +#define RETURN { RtlSetLastError(STATUS_SUCCESS, STATUS_SUCCESS, (PWCHAR)""); NtStatus = STATUS_SUCCESS; goto FINALLY_OUTRO; } +#define FORCE(expr) { if(!RTL_SUCCESS(NtStatus = (expr))) goto THROW_OUTRO; } +#define IsValidPointer RtlIsValidPointer + +#define PtrToUlong( p ) ((ULONG)(ULONG_PTR) (p) ) + BOOL RtlIsValidPointer(PVOID InPtr, ULONG InSize); + + typedef struct _DETOUR_TRAMPOLINE * PLOCAL_HOOK_INFO; + + typedef struct _HOOK_ACL_ + { + ULONG Count; + BOOL IsExclusive; + ULONG Entries[MAX_ACE_COUNT]; + }HOOK_ACL; + + typedef struct _HOOK_TRACE_INFO_ + { + PLOCAL_HOOK_INFO Link; + }HOOK_TRACE_INFO, *TRACED_HOOK_HANDLE; + + /* + Setup the ACLs after hook installation. Please note that every + hook starts suspended. You will have to set a proper ACL to + make it active! + */ + + LONG DetourSetInclusiveACL( + ULONG* InThreadIdList, + ULONG InThreadCount, + TRACED_HOOK_HANDLE InHandle); + + LONG DetourSetExclusiveACL( + ULONG* InThreadIdList, + ULONG InThreadCount, + TRACED_HOOK_HANDLE InHandle); + + LONG DetourSetGlobalInclusiveACL( + ULONG* InThreadIdList, + ULONG InThreadCount); + + LONG DetourSetGlobalExclusiveACL( + ULONG* InThreadIdList, + ULONG InThreadCount); + + LONG DetourIsThreadIntercepted( + TRACED_HOOK_HANDLE InHook, + ULONG InThreadID, + BOOL* OutResult); + + LONG DetourSetACL( + HOOK_ACL* InAcl, + BOOL InIsExclusive, + ULONG* InThreadIdList, + ULONG InThreadCount); + + HOOK_ACL* DetourBarrierGetAcl(); + /* + The following barrier methods are meant to be used in hook handlers only! + + They will all fail with STATUS_NOT_SUPPORTED if called outside a + valid hook handler... + */ + LONG DetourBarrierGetCallback(PVOID* OutValue); + + LONG DetourBarrierGetReturnAddress(PVOID* OutValue); + + LONG DetourBarrierGetAddressOfReturnAddress(PVOID** OutValue); + + LONG DetourTransactionCommitEx(_Out_opt_ PVOID **pppFailedPointer); + + LONG DetourUpdateThread(_In_ pthread_t hThread); + + LONG DetourAttach(_Inout_ PVOID *ppPointer, + _In_ PVOID pDetour); + + LONG DetourAttachEx(_Inout_ PVOID *ppPointer, + _In_ PVOID pDetour, + _Out_opt_ PDETOUR_TRAMPOLINE *ppRealTrampoline, + _Out_opt_ PVOID *ppRealTarget, + _Out_opt_ PVOID *ppRealDetour); + + LONG DetourDetach(_Inout_ PVOID *ppPointer, + _In_ PVOID pDetour); + + BOOL DetourSetIgnoreTooSmall(_In_ BOOL fIgnore); + BOOL DetourSetRetainRegions(_In_ BOOL fRetain); + PVOID DetourSetSystemRegionLowerBound(_In_ PVOID pSystemRegionLowerBound); + PVOID DetourSetSystemRegionUpperBound(_In_ PVOID pSystemRegionUpperBound); + + void DetourBarrierThreadDetach(); + + LONG DetourBarrierProcessAttach(); + void DetourBarrierProcessDetach(); + + void DetourCriticalInitialize(); + void DetourCriticalFinalize(); + + LONG DetourInstallHook( + void* InEntryPoint, + void* InHookProc, + void* InCallback, + TRACED_HOOK_HANDLE OutHandle); + + LONG DetourUninstallHook(TRACED_HOOK_HANDLE InHandle); + + + ////////////////////////////////////////////////////////////// Code Functions. + // + PVOID DetourFindFunction(_In_ LPCSTR pszModule, + _In_ LPCSTR pszFunction); + PVOID DetourCodeFromPointer(_In_ PVOID pPointer, + _Out_opt_ PVOID *ppGlobals); + PVOID DetourCopyInstruction(_In_opt_ PVOID pDst, + _Inout_opt_ PVOID *ppDstPool, + _In_ PVOID pSrc, + _Out_opt_ PVOID *ppTarget, + _Out_opt_ LONG *plExtra); + + + ///////////////////////////////////////////////////// Loaded Binary Functions. + // + HMODULE DetourGetContainingModule(_In_ PVOID pvAddr); + HMODULE DetourEnumerateModules(_In_opt_ HMODULE hModuleLast); + PVOID DetourGetEntryPoint(_In_opt_ HMODULE hModule); + ULONG DetourGetModuleSize(_In_opt_ HMODULE hModule); + + + _Writable_bytes_(*pcbData) + _Readable_bytes_(*pcbData) + _Success_(return != NULL) + PVOID DetourFindPayload(_In_opt_ HMODULE hModule, + _In_ REFGUID rguid, + _Out_ DWORD *pcbData); + + _Writable_bytes_(*pcbData) + _Readable_bytes_(*pcbData) + _Success_(return != NULL) + PVOID DetourFindPayloadEx(_In_ REFGUID rguid, + _Out_ DWORD * pcbData); + + DWORD DetourGetSizeOfPayloads(_In_opt_ HMODULE hModule); + + ///////////////////////////////////////////////// Persistent Binary Functions. + // + + PDETOUR_BINARY DetourBinaryOpen(_In_ HANDLE hFile); + + _Writable_bytes_(*pcbData) + _Readable_bytes_(*pcbData) + _Success_(return != NULL) + PVOID DetourBinaryEnumeratePayloads(_In_ PDETOUR_BINARY pBinary, + _Out_opt_ GUID *pGuid, + _Out_ DWORD *pcbData, + _Inout_ DWORD *pnIterator); + + _Writable_bytes_(*pcbData) + _Readable_bytes_(*pcbData) + _Success_(return != NULL) + PVOID DetourBinaryFindPayload(_In_ PDETOUR_BINARY pBinary, + _In_ REFGUID rguid, + _Out_ DWORD *pcbData); + + PVOID DetourBinarySetPayload(_In_ PDETOUR_BINARY pBinary, + _In_ REFGUID rguid, + _In_reads_opt_(cbData) PVOID pData, + _In_ DWORD cbData); + + + /////////////////////////////////////////////////// Create Process & Load Dll. + // + + + + // + ////////////////////////////////////////////////////////////////////////////// +#ifdef __cplusplus +} +#endif // __cplusplus + +//////////////////////////////////////////////// Detours Internal Definitions. +// +#ifdef __cplusplus +#ifdef DETOURS_INTERNAL + +#define NOTHROW +// #define NOTHROW (nothrow) + +////////////////////////////////////////////////////////////////////////////// +// +#if (_MSC_VER < 1299) + + +static inline +LONG InterlockedCompareExchange(_Inout_ LONG *ptr, _In_ LONG nval, _In_ LONG oval) +{ + return (LONG)__sync_val_compare_and_swap(ptr, oval, nval); +} +#else +#include +#endif + +#if defined(_INC_STDIO) && !defined(_CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS) +#error detours.h must be included before stdio.h (or at least define _CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS earlier) +#endif +#define _CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS 1 + +#ifndef DETOUR_TRACE +#if DETOUR_DEBUG +#define DETOUR_TRACE(x) printf x +#define DETOUR_BREAK() __debugbreak() +#include +#else +#ifdef DETOUR_LOGGING +#include +#endif +#include +const char * ___DETOUR_TRACE(const char *format, ...); +#ifdef DETOUR_LOGGING +#define DETOUR_TRACE(x) LOG(INFO) << ___DETOUR_TRACE x +#else +#define DETOUR_TRACE(x) +#endif +#define DETOUR_BREAK() +#endif +#endif + +#if 1 || defined(DETOURS_IA64) + + +#endif // DETOURS_IA64 + +#ifdef DETOURS_ARM + + +#define DETOURS_PFUNC_TO_PBYTE(p) ((PBYTE)(((ULONG_PTR)(p)) & ~(ULONG_PTR)1)) +#define DETOURS_PBYTE_TO_PFUNC(p) ((PBYTE)(((ULONG_PTR)(p)) | (ULONG_PTR)1)) + +#endif // DETOURS_ARM + +////////////////////////////////////////////////////////////////////////////// + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +#define DETOUR_OFFLINE_LIBRARY(x) \ +PVOID DetourCopyInstruction##x(_In_opt_ PVOID pDst, \ + _Inout_opt_ PVOID *ppDstPool, \ + _In_ PVOID pSrc, \ + _Out_opt_ PVOID *ppTarget, \ + _Out_opt_ LONG *plExtra); \ + \ +BOOL DetourSetCodeModule##x(_In_ HMODULE hModule, \ + _In_ BOOL fLimitReferencesToModule); \ + + DETOUR_OFFLINE_LIBRARY(X86) + DETOUR_OFFLINE_LIBRARY(X64) + DETOUR_OFFLINE_LIBRARY(ARM) + DETOUR_OFFLINE_LIBRARY(ARM64) + DETOUR_OFFLINE_LIBRARY(IA64) + +#undef DETOUR_OFFLINE_LIBRARY + + ////////////////////////////////////////////////////////////////////////////// + // + // Helpers for manipulating page protection. + // + + _Success_(return != FALSE) + BOOL DetourVirtualProtectSameExecuteEx(_In_ pid_t hProcess, + _In_ PVOID pAddress, + _In_ SIZE_T nSize, + _In_ DWORD dwNewProtect, + _Out_ PDWORD pdwOldProtect); + + _Success_(return != FALSE) + BOOL DetourVirtualProtectSameExecute(_In_ PVOID pAddress, + _In_ SIZE_T nSize, + _In_ DWORD dwNewProtect, + _Out_ PDWORD pdwOldProtect); +#ifdef __cplusplus +} +#endif // __cplusplus + +////////////////////////////////////////////////////////////////////////////// + +#define MM_ALLOCATION_GRANULARITY 0x10000 + +////////////////////////////////////////////////////////////////////////////// + + +//LONG DetourBarrierBeginStackTrace(PVOID* OutBackup); + +//LONG DetourBarrierEndStackTrace(PVOID InBackup); + +BOOL DetourIsValidHandle( + TRACED_HOOK_HANDLE InTracedHandle, + PLOCAL_HOOK_INFO* OutHandle); + +BOOL IsLoaderLock(); +BOOL AcquireSelfProtection(); + +void RtlAssert(BOOL InAssert, LPCWSTR lpMessageText); +void RtlSetLastError(LONG InCode, LONG InNtStatus, WCHAR* InMessage); + +typedef struct _RTL_SPIN_LOCK_ +{ + pthread_mutex_t Lock; + BOOL IsOwned; +}RTL_SPIN_LOCK; + +void RtlInitializeLock(RTL_SPIN_LOCK* InLock); + +void RtlAcquireLock(RTL_SPIN_LOCK* InLock); + +void RtlReleaseLock(RTL_SPIN_LOCK* InLock); + +void RtlDeleteLock(RTL_SPIN_LOCK* InLock); + +void RtlSleep(ULONG InTimeout); + + +PVOID detour_get_page(PVOID addr); +int detour_get_page_size(); + +typedef struct _RUNTIME_INFO_ +{ + // "true" if the current thread is within the related hook handler + BOOL IsExecuting; + // the hook this information entry belongs to... This allows a per thread and hook storage! + DWORD HLSIdent; + // the return address of the current thread's hook handler... + void* RetAddress; + // the address of the return address of the current thread's hook handler... + void** AddrOfRetAddr; +}RUNTIME_INFO; + +typedef struct _THREAD_RUNTIME_INFO_ +{ + RUNTIME_INFO* Entries; + RUNTIME_INFO* Current; + void* Callback; + BOOL IsProtected; +}THREAD_RUNTIME_INFO, *LPTHREAD_RUNTIME_INFO; + +typedef struct _THREAD_LOCAL_STORAGE_ +{ + THREAD_RUNTIME_INFO Entries[MAX_THREAD_COUNT]; + DWORD IdList[MAX_THREAD_COUNT]; + RTL_SPIN_LOCK ThreadSafe; +}THREAD_LOCAL_STORAGE; + +typedef struct _BARRIER_UNIT_ +{ + HOOK_ACL GlobalACL; + BOOL IsInitialized; + THREAD_LOCAL_STORAGE TLS; +}BARRIER_UNIT; + + +BOOL TlsGetCurrentValue( + THREAD_LOCAL_STORAGE* InTls, + THREAD_RUNTIME_INFO** OutValue); +BOOL TlsAddCurrentThread(THREAD_LOCAL_STORAGE* InTls); + +void RtlFreeMemory(void* InPointer); + +void* RtlAllocateMemory( + BOOL InZeroMemory, + ULONG InSize); + +#undef RtlCopyMemory +void RtlCopyMemory( + PVOID InDest, + PVOID InSource, + ULONG InByteCount); + +#undef RtlZeroMemory +void RtlZeroMemory( + PVOID InTarget, + ULONG InByteCount); + +BOOL IsThreadIntercepted( + HOOK_ACL* LocalACL, + ULONG InThreadID); +void ReleaseSelfProtection(); + +extern BARRIER_UNIT Unit; +extern RTL_SPIN_LOCK GlobalHookLock; + + +#endif // DETOURS_INTERNAL +#endif // __cplusplus + +#endif // _DETOURS_H_ +// +//////////////////////////////////////////////////////////////// End of File. diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/linux_detours/include/plthook.h b/third-party/RemoteInput/RemoteInput/Thirdparty/linux_detours/include/plthook.h new file mode 100644 index 0000000..2ef827e --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/linux_detours/include/plthook.h @@ -0,0 +1,66 @@ +/* -*- indent-tabs-mode: nil -*- + * + * plthook.h -- the header file of plthook + * + * URL: https://github.com/kubo/plthook + * + * ------------------------------------------------------ + * + * Copyright 2013-2014 Kubo Takehiro + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of the authors. + * + */ +#ifndef PLTHOOK_H +#define PLTHOOK_H 1 + +#define PLTHOOK_SUCCESS 0 +#define PLTHOOK_FILE_NOT_FOUND 1 +#define PLTHOOK_INVALID_FILE_FORMAT 2 +#define PLTHOOK_FUNCTION_NOT_FOUND 3 +#define PLTHOOK_INVALID_ARGUMENT 4 +#define PLTHOOK_OUT_OF_MEMORY 5 +#define PLTHOOK_INTERNAL_ERROR 6 +#define PLTHOOK_NOT_IMPLEMENTED 7 + +typedef struct plthook plthook_t; + +#ifdef __cplusplus +extern "C" { +#endif + +int plthook_open(plthook_t **plthook_out, const char *filename); +int plthook_open_by_handle(plthook_t **plthook_out, void *handle); +int plthook_open_by_address(plthook_t **plthook_out, void *address); +int plthook_enum(plthook_t *plthook, unsigned int *pos, const char **name_out, void ***addr_out); +int plthook_replace(plthook_t *plthook, const char *funcname, void *funcaddr, void **oldfunc); +void plthook_close(plthook_t *plthook); +const char *plthook_error(void); + +#ifdef __cplusplus +}; /* extern "C" */ +#endif + +#endif diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/linux_detours/include/types.h b/third-party/RemoteInput/RemoteInput/Thirdparty/linux_detours/include/types.h new file mode 100644 index 0000000..847f92a --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/linux_detours/include/types.h @@ -0,0 +1,337 @@ +#pragma once +/** +* types.h - These definitions are for linux/windows cross compiler compatibility +* +* author: Eric Young 01/22/2004 +*/ + + +#ifndef TYPES_H_ +#define TYPES_H_ + +#define MIN(a,b) ((a < b) ? a : b) +#define MAX(a,b) ((a > b) ? a : b) +#define CLAMP(val,lower,upper) MAX(MIN(val,upper),lower) + +// These definitions are for linux +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#if defined(__linux) +#include +#endif +#include +#include + +// these are needed for Win32/Linux string comparisons +#define genericStrICmp strcasecmp +#define genericStrNICmp strncasecmp + +typedef char WCHAR; // wc, 16-bit UNICODE character + +#define CONST const +typedef int BOOL; +typedef long int LONG; +typedef short int SHORT; +typedef char CHAR; +typedef int INT; +typedef unsigned int DWORD; +typedef unsigned short WORD; +typedef unsigned char BYTE; +typedef unsigned int UINT; + +typedef long long INT64; +typedef unsigned long long UINT64; + +typedef int PT_FILEHANDLE; +typedef void* DLL_HANDLE; + +typedef unsigned long* LPDWORD; +typedef void* LPOVERLAPPED; +typedef void* OVERLAPPED; +typedef void* LPVOID; +typedef void* PVOID; +typedef void VOID; +typedef int HANDLE; // note that handle here is assumed to be + // a pointer to a file decriptor +typedef int* PHANDLE; +typedef int BOOL; + +typedef unsigned int UINT32; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef unsigned long long ULONGLONG; +typedef long long LONGLONG; + +typedef unsigned short USHORT; +typedef unsigned char UCHAR; +typedef long long INT64; +typedef long long LARGE_INTEGER; +typedef unsigned char BYTE; +typedef BYTE* PBYTE; +typedef int __int32; +typedef short __int16; +typedef unsigned char __int8; + +typedef int INT32; +typedef LONG NTSTATUS; +typedef BYTE BOOLEAN; +typedef BOOLEAN *PBOOLEAN; +typedef WCHAR *LPCWSTR, *PCWSTR; +typedef WCHAR *PCHAR; +typedef WCHAR *LPSTR, *PSTR; +typedef WCHAR * PWCHAR, *UNICODE_STRING; +typedef CHAR *PCHAR, *LPCH, *PCH; +typedef CONST CHAR *LPCCH, *PCCH; + +typedef void * HMODULE; + +typedef CHAR *NPSTR, *LPSTR, *PSTR; +typedef PSTR *PZPSTR; +typedef CONST PSTR *PCZPSTR; +typedef CONST CHAR *LPCSTR, *PCSTR; +typedef PCSTR *PZPCSTR; +typedef CONST PCSTR *PCZPCSTR; + +typedef CHAR *PZZSTR; +typedef CONST CHAR *PCZZSTR; + +typedef CHAR *PNZCH; +typedef CONST CHAR *PCNZCH; +typedef DWORD *PDWORD; + +typedef size_t SIZE_T; + +//typedef unsigned long ULONG; +typedef ULONG *PULONG; +//typedef unsigned short USHORT; +typedef USHORT *PUSHORT; +//typedef unsigned char UCHAR; +typedef UCHAR *PUCHAR; +//typedef _Null_terminated_ char *PSZ; + +typedef signed char INT8, *PINT8; +typedef signed short INT16, *PINT16; +typedef signed int INT32, *PINT32; +typedef unsigned char UINT8, *PUINT8; +typedef unsigned short UINT16, *PUINT16; +typedef unsigned int UINT32, *PUINT32; +#if defined(_ARM64_) +// +// The following types are guaranteed to be signed and 64 bits wide. +// + +typedef long long LONG64, *PLONG64; + +// +// The following types are guaranteed to be unsigned and 64 bits wide. +// + +typedef unsigned long long ULONG64, *PULONG64; +typedef unsigned long long DWORD64, *PDWORD64; + + +#define RtlZeroMemory(Destination,Length) memset((Destination),0,(Length)) +#define ZeroMemory RtlZeroMemory +#define NO_ERROR 0 +#define ERROR_INVALID_DATA 13L +#if !defined(UNALIGNED) +#define UNALIGNED +#endif + +#define UNREFERENCED_PARAMETER(P) \ + /*lint -save -e527 -e530 */ \ + { \ + (P) = (P); \ + } \ + +#define CopyMemory memcpy + +#define _In_ + +#define CALLBACK __stdcall +#define WINAPI +#define WINAPIV __cdecl +#define APIENTRY WINAPI +#define APIPRIVATE __stdcall +#define PASCAL __stdcall + +#define STATUS_SUCCESS 0 +#define STATUS_NOT_SUPPORTED 1 +#define STATUS_INVALID_PARAMETER_1 1 +#define STATUS_INVALID_PARAMETER_2 2 +#define STATUS_INVALID_PARAMETER_3 3 +#define STATUS_INVALID_PARAMETER_4 4 +#define STATUS_INTERNAL_ERROR 0 +#define STATUS_NO_MEMORY 0 + +#define STATUS_TIMEOUT 1 +#define STATUS_INSUFFICIENT_RESOURCES 1 +#define STATUS_INVALID_PARAMETER ((DWORD)0xC000000DL) +#define PAGE_EXECUTE_READWRITE PROT_EXEC | PROT_READ | PROT_WRITE +#define PAGE_READONLY PROT_READ +#define PAGE_READWRITE PROT_READ | PROT_WRITE +#define PAGE_EXECUTE_READ PROT_EXEC | PROT_READ + + +/* common constants */ +#define SUCCESS 0 +#define FAILURE -1 + +#define IOCTL_FAIL(status) (status < 0) + +/** unusual return codes */ +#define UNIMPLEMENTED - 1001 + +// create some equivalent constants in linux that windows have +#define STATIC static + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef INVALID_HANDLE_VALUE +#define INVALID_HANDLE_VALUE -1 +#endif + +/** sleep for x milliseconds */ +//inline void nap(unsigned long msec) { usleep(msec*1000); } + +#define Sleep sleep +#else + +#define RtlZeroMemory(Destination,Length) memset((Destination),0,(Length)) +#define ZeroMemory RtlZeroMemory +#define NO_ERROR 0 +#define ERROR_INVALID_DATA 13L +#if !defined(UNALIGNED) +#define UNALIGNED +#endif + +#define UNREFERENCED_PARAMETER(P) \ + /*lint -save -e527 -e530 */ \ + { \ + (P) = (P); \ + } \ + +#define CopyMemory memcpy + +#define _In_ + +#define CALLBACK __stdcall +#define WINAPI //__stdcall +#define WINAPIV __cdecl +#define APIENTRY WINAPI +#define APIPRIVATE __stdcall +#define PASCAL __stdcall + +#define STATUS_SUCCESS 0 +#define STATUS_NOT_SUPPORTED 1 +#define STATUS_INVALID_PARAMETER_1 1 +#define STATUS_INVALID_PARAMETER_2 2 +#define STATUS_INVALID_PARAMETER_3 3 +#define STATUS_INVALID_PARAMETER_4 4 +#define STATUS_INTERNAL_ERROR 0 +#define STATUS_NO_MEMORY 0 + +#define STATUS_TIMEOUT 1 +#define STATUS_INSUFFICIENT_RESOURCES 1 +#define STATUS_INVALID_PARAMETER ((DWORD)0xC000000DL) +#define PAGE_EXECUTE_READWRITE PROT_EXEC | PROT_READ | PROT_WRITE +#define PAGE_READONLY PROT_READ +#define PAGE_READWRITE PROT_READ | PROT_WRITE +#define PAGE_EXECUTE_READ PROT_EXEC | PROT_READ +/* These are defined so we can use TCHAR compatible string calls */ +#define _TINT int +#define _T(arg) arg +#define TCHAR char +#define tstrcpy strcpy +#define tstrncpy strncpy +#define _tcscat strcat +#define _tcscpy(str1, str2) strcpy(str1, str2) +#define _tcslen(str1) strlen(str1) +#define _tfopen(filename, access) fopen(filename, access) +#define _gettc getc +#define _puttc putc +#define _stscanf sscanf +#define _stprintf sprintf +#define _sntprintf snprintf +#define _tprintf printf + + + +/* common constants */ +#define SUCCESS 0 +#define FAILURE -1 + +#define IOCTL_FAIL(status) (status < 0) + +/** unusual return codes */ +#define UNIMPLEMENTED -1001 + +// create some equivalent constants in linux that windows have +#define STATIC static + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef INVALID_HANDLE_VALUE +#define INVALID_HANDLE_VALUE -1 +#endif + +/** sleep for x milliseconds */ +//inline void nap(unsigned long msec) { usleep(msec*1000); } + +#define Sleep sleep + +//typedef double VWTIME; +/** returns the amount of time in seconds since some arbitrary moment. */ +//inline VWTIME VWGetTime() { return 0.0; } + + +#endif // end of the WIN32/Linux definitions + + +// These are common declared types + +typedef unsigned char* PU8; +typedef unsigned char U8; +typedef unsigned short U16; +typedef unsigned long U32; +typedef signed char S8; +typedef signed short S16; +typedef signed long S32; + + +#if defined(_DEBUG) +#define dbgprint(string) { printf string; fflush(stdout); } +#else +#define dbgprint(string) +#endif + +#include + +#endif // end of TYPES_H_ diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/linux_detours/src/barrier.cpp b/third-party/RemoteInput/RemoteInput/Thirdparty/linux_detours/src/barrier.cpp new file mode 100644 index 0000000..197bc22 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/linux_detours/src/barrier.cpp @@ -0,0 +1,714 @@ +#define _CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS 1 + + +#include "../include/types.h" +// #define DETOUR_DEBUG 1 +#define DETOURS_INTERNAL +#include "../include/detours.h" + +#if DETOURS_VERSION != 0x4c0c1 // 0xMAJORcMINORcPATCH +#error detours.h version mismatch +#endif + +// allocate at DLL Entry + +BARRIER_UNIT Unit; + +void RtlZeroMemory( + PVOID InTarget, + ULONG InByteCount) +{ + ULONG Index; + UCHAR* Target = (UCHAR*)InTarget; + + for (Index = 0; Index < InByteCount; Index++) + { + *Target = 0; + + Target++; + } +} +void RtlInitializeLock(RTL_SPIN_LOCK* OutLock) +{ + if (OutLock != NULL) { + RtlZeroMemory(OutLock, sizeof(RTL_SPIN_LOCK)); + + //create mutex attribute variable + pthread_mutexattr_t mAttr; + + // setup recursive mutex for mutex attribute + pthread_mutexattr_settype(&mAttr, PTHREAD_MUTEX_RECURSIVE); + + // Use the mutex attribute to create the mutex + pthread_mutex_init(&OutLock->Lock, &mAttr); + + // Mutex attribute can be destroy after initializing the mutex variable + pthread_mutexattr_destroy(&mAttr); + } +} + +void RtlAcquireLock(RTL_SPIN_LOCK* InLock) +{ + if (InLock != NULL) { + pthread_mutex_lock(&InLock->Lock); + + DETOUR_ASSERT(!InLock->IsOwned, L"barrier.cpp - !InLock->IsOwned"); + + InLock->IsOwned = TRUE; + } +} + +void RtlReleaseLock(RTL_SPIN_LOCK* InLock) +{ + if (InLock != NULL) { + DETOUR_ASSERT(InLock->IsOwned, L"barrier.cpp - InLock->IsOwned"); + + InLock->IsOwned = FALSE; + + pthread_mutex_unlock(&InLock->Lock); + } +} + +void RtlDeleteLock(RTL_SPIN_LOCK* InLock) +{ + if (InLock != NULL) { + DETOUR_ASSERT(!InLock->IsOwned, L"barrier.cpp - InLock->IsOwned"); + + pthread_mutex_destroy(&InLock->Lock); + } +} + +void RtlSleep(ULONG InTimeout) +{ + sleep(InTimeout); +} + + +void RtlCopyMemory( + PVOID InDest, + PVOID InSource, + ULONG InByteCount) +{ + ULONG Index; + UCHAR* Dest = (UCHAR*)InDest; + UCHAR* Src = (UCHAR*)InSource; + + for (Index = 0; Index < InByteCount; Index++) + { + *Dest = *Src; + + Dest++; + Src++; + } +} + +void* RtlAllocateMemory(BOOL InZeroMemory, ULONG InSize) +{ + void* Result = malloc(InSize); + + if (InZeroMemory && (Result != NULL)) + RtlZeroMemory(Result, InSize); + + return Result; +} + +LONG RtlProtectMemory(void* InPointer, ULONG InSize, ULONG InNewProtection) +{ + NTSTATUS NtStatus; + + if (mprotect(InPointer, (size_t)InSize, (int)InNewProtection)) + THROW(-1, (PWCHAR)"Unable to make memory executable.") + else + RETURN; + +THROW_OUTRO: +FINALLY_OUTRO: + return NtStatus; +} + +void RtlFreeMemory(void* InPointer) +{ + DETOUR_ASSERT(InPointer != NULL, "barrier.cpp - InPointer != NULL"); + +#ifdef _DEBUG + free(InPointer); +#else + free(InPointer); +#endif +} + +LONG RtlInterlockedIncrement(LONG* RefValue) +{ + return __sync_fetch_and_add(RefValue, 1); +} + +BOOL RtlIsValidPointer(PVOID InPtr, ULONG InSize) +{ + if ((InPtr == NULL) || (InPtr == (PVOID)~0)) + return FALSE; + + //DETOUR_ASSERT(!IsBadReadPtr(InPtr, InSize), "barrier.cpp - !IsBadReadPtr(InPtr, InSize)"); + + return TRUE; +} + +static PWCHAR LastError = (PWCHAR)""; +static ULONG LastErrorCode = 0; + +void RtlSetLastError(LONG InCode, LONG InNtStatus, WCHAR* InMessage) +{ + LastErrorCode = InCode; + + if (InMessage == NULL) + LastError = (PWCHAR)""; + else + { + if (InNtStatus == 0) { + + } +#if _DEBUG + LastErrorCode = InNtStatus; +#endif + LastError = (PWCHAR)InMessage; + } +} +void RtlAssert(BOOL InAssert, LPCWSTR lpMessageText) +{ + if (InAssert) { + return; + } + + #ifdef DETOUR_LOGGING + LOG(FATAL) << lpMessageText; + #else + perror(lpMessageText); + #endif +} + + +LONG DetourExport DetourSetGlobalInclusiveACL( + ULONG* InThreadIdList, + ULONG InThreadCount) +{ + /* + Description: + + Sets an inclusive global ACL based on the given thread ID list. + + Parameters: + - InThreadIdList + An array of thread IDs. If you specific zero for an entry in this array, + it will be automatically replaced with the calling thread ID. + + - InThreadCount + The count of entries listed in the thread ID list. This value must not exceed + MAX_ACE_COUNT! + */ + return DetourSetACL(DetourBarrierGetAcl(), FALSE, InThreadIdList, InThreadCount); +} + +LONG DetourExport DetourSetGlobalExclusiveACL( + ULONG* InThreadIdList, + ULONG InThreadCount) +{ + /* + Description: + + Sets an exclusive global ACL based on the given thread ID list. + + Parameters: + - InThreadIdList + An array of thread IDs. If you specific zero for an entry in this array, + it will be automatically replaced with the calling thread ID. + + - InThreadCount + The count of entries listed in the thread ID list. This value must not exceed + MAX_ACE_COUNT! + */ + return DetourSetACL(DetourBarrierGetAcl(), TRUE, InThreadIdList, InThreadCount); +} + +BOOL DetourIsValidHandle( + TRACED_HOOK_HANDLE InTracedHandle, + PLOCAL_HOOK_INFO* OutHandle) +{ + /* + Description: + + A handle is considered to be valid, if the whole structure + points to valid memory AND the signature is valid AND the + hook is installed! + + */ + if (!IsValidPointer(InTracedHandle, sizeof(HOOK_TRACE_INFO))) + return FALSE; + + if (OutHandle != NULL) + *OutHandle = InTracedHandle->Link; + + return TRUE; +} +LONG DetourSetACL( + HOOK_ACL* InAcl, + BOOL InIsExclusive, + ULONG* InThreadIdList, + ULONG InThreadCount) +{ + /* + Description: + + This method is used internally to provide a generic interface to + either the global or local hook ACLs. + + Parameters: + - InAcl + NULL if you want to set the global ACL. + Any LOCAL_HOOK_INFO::LocalACL to set the hook specific ACL. + + - InIsExclusive + TRUE if all listed thread shall be excluded from interception, + FALSE otherwise + + - InThreadIdList + An array of thread IDs. If you specific zero for an entry in this array, + it will be automatically replaced with the calling thread ID. + + - InThreadCount + The count of entries listed in the thread ID list. This value must not exceed + MAX_ACE_COUNT! + */ + + ULONG Index; + + DETOUR_ASSERT(IsValidPointer(InAcl, sizeof(HOOK_ACL)), "barrier.cpp - IsValidPointer(InAcl, sizeof(HOOK_ACL))"); + + if (InThreadCount > MAX_ACE_COUNT) + return -2; + + if (!IsValidPointer(InThreadIdList, InThreadCount * sizeof(ULONG))) + return -1; + + for (Index = 0; Index < InThreadCount; Index++) + { + if (InThreadIdList[Index] == 0) + InThreadIdList[Index] = (ULONG)pthread_self(); + } + + // set ACL... + if (!mprotect(detour_get_page(InAcl), detour_get_page_size(), PAGE_READWRITE)) { + InAcl->IsExclusive = InIsExclusive; + InAcl->Count = InThreadCount; + + RtlCopyMemory(InAcl->Entries, InThreadIdList, InThreadCount * sizeof(ULONG)); + if (mprotect(detour_get_page(InAcl), detour_get_page_size(), PAGE_EXECUTE_READ)) { + + return -3; + } + } + return 0; +} + +HOOK_ACL* DetourBarrierGetAcl() +{ + return &Unit.GlobalACL; +} + +LONG DetourExport DetourBarrierProcessAttach() +{ + /* + Description: + + Will be called on DLL load and initializes all barrier structures. + */ + RtlZeroMemory(&Unit, sizeof(Unit)); + + // globally accept all threads... + Unit.GlobalACL.IsExclusive = TRUE; + + // allocate private heap + RtlInitializeLock(&Unit.TLS.ThreadSafe); + + //Unit.IsInitialized = AuxUlibInitialize()?TRUE:FALSE; + Unit.IsInitialized = TRUE; + + return STATUS_SUCCESS; +} + + +BOOL TlsGetCurrentValue( + THREAD_LOCAL_STORAGE* InTls, + THREAD_RUNTIME_INFO** OutValue) +{ + /* + Description: + + Queries the THREAD_RUNTIME_INFO for the calling thread. + The caller shall previously be added to the storage by + using TlsAddCurrentThread(). + + Parameters: + + - InTls + + The storage where the caller is registered. + + - OutValue + + Is filled with a pointer to the caller's private storage entry. + + Returns: + + FALSE if the caller was not registered in the storage, TRUE otherwise. + */ + ULONG CurrentId = (ULONG)pthread_self(); + + LONG Index; + + for (Index = 0; Index < MAX_THREAD_COUNT; Index++) + { + if (InTls->IdList[Index] == CurrentId) + { + *OutValue = &InTls->Entries[Index]; + + return TRUE; + } + } + + return FALSE; +} +BOOL TlsAddCurrentThread(THREAD_LOCAL_STORAGE* InTls) +{ + /* + Description: + + Tries to reserve a THREAD_RUNTIME_INFO entry for the calling thread. + On success it may call TlsGetCurrentValue() to query a pointer to + its private entry. + + This is a replacement for the Windows Thread Local Storage which seems + to cause trouble when using it in Explorer.EXE for example. + + No parameter validation (for performance reasons). + + This method will raise an assertion if the thread was already added + to the storage! + + Parameters: + - InTls + + The thread local storage to allocate from. + + Returns: + + TRUE on success, FALSE otherwise. + */ + ULONG CurrentId = (ULONG)pthread_self(); + + LONG Index = -1; + LONG i; + + RtlAcquireLock(&InTls->ThreadSafe); + + // select Index AND check whether thread is already registered. + for (i = 0; i < MAX_THREAD_COUNT; i++) + { + if ((InTls->IdList[i] == 0) && (Index == -1)) + Index = i; + + DETOUR_ASSERT(InTls->IdList[i] != CurrentId, "barrier.cpp - InTls->IdList[i] != CurrentId"); + } + + if (Index == -1) + { + RtlReleaseLock(&InTls->ThreadSafe); + + return FALSE; + } + + InTls->IdList[Index] = CurrentId; + RtlZeroMemory(&InTls->Entries[Index], sizeof(THREAD_RUNTIME_INFO)); + + RtlReleaseLock(&InTls->ThreadSafe); + + return TRUE; +} + +void TlsRemoveCurrentThread(THREAD_LOCAL_STORAGE* InTls) +{ + /* + Description: + + Removes the caller from the local storage. If the caller + is already removed, the method will do nothing. + + Parameters: + + - InTls + + The storage from which the caller should be removed. + */ + ULONG CurrentId = (ULONG)pthread_self(); + ULONG Index; + + RtlAcquireLock(&InTls->ThreadSafe); + + for (Index = 0; Index < MAX_THREAD_COUNT; Index++) + { + if (InTls->IdList[Index] == CurrentId) + { + InTls->IdList[Index] = 0; + + RtlZeroMemory(&InTls->Entries[Index], sizeof(THREAD_RUNTIME_INFO)); + } + } + + RtlReleaseLock(&InTls->ThreadSafe); +} + +void DetourBarrierProcessDetach() +{ + /* + Description: + + Will be called on DLL unload. + */ + ULONG Index; + + RtlDeleteLock(&Unit.TLS.ThreadSafe); + + // release thread specific resources + for (Index = 0; Index < MAX_THREAD_COUNT; Index++) + { + if (Unit.TLS.Entries[Index].Entries != NULL) + RtlFreeMemory(Unit.TLS.Entries[Index].Entries); + } + + RtlZeroMemory(&Unit, sizeof(Unit)); +} + +void DetourExport DetourBarrierThreadDetach() +{ + /* + Description: + + Will be called on thread termination and cleans up the TLS. + */ + LPTHREAD_RUNTIME_INFO Info; + + if (TlsGetCurrentValue(&Unit.TLS, &Info)) + { + if (Info->Entries != NULL) + RtlFreeMemory(Info->Entries); + + Info->Entries = NULL; + } + + TlsRemoveCurrentThread(&Unit.TLS); +} + +RTL_SPIN_LOCK GlobalHookLock; + +void DetourCriticalInitialize() +{ + /* + Description: + + Fail safe initialization of global hooking structures... + */ + + RtlInitializeLock(&GlobalHookLock); +} + +void DetourCriticalFinalize() +{ + /* + Description: + + Will be called in the DLL_PROCESS_DETACH event and just uninstalls + all hooks. If it is possible also their memory is released. + */ + RtlDeleteLock(&GlobalHookLock); +} + +BOOL IsLoaderLock() +{ + /* + Returns: + + TRUE if the current thread hols the OS loader lock, or the library was not initialized + properly. In both cases a hook handler should not be executed! + + FALSE if it is safe to execute the hook handler. + + */ + return FALSE; +} + + +BOOL AcquireSelfProtection() +{ + /* + Description: + + To provide more convenience for writing the TDB, this self protection + will disable ALL hooks for the current thread until ReleaseSelfProtection() + is called. This allows one to call any API during TDB initialization + without being intercepted... + + Returns: + + TRUE if the caller's runtime info has been locked down. + + FALSE if the caller's runtime info already has been locked down + or is not available. The hook handler should not be executed in + this case! + + */ + LPTHREAD_RUNTIME_INFO Runtime = NULL; + + if (!TlsGetCurrentValue(&Unit.TLS, &Runtime) || Runtime->IsProtected) + return FALSE; + + Runtime->IsProtected = TRUE; + + return TRUE; +} + +void DetourExport ReleaseSelfProtection() +{ + /* + Description: + + Exists the TDB self protection. Refer to AcquireSelfProtection() for more + information. + + An assertion is raised if the caller has not owned the self protection. + */ + LPTHREAD_RUNTIME_INFO Runtime = NULL; + + DETOUR_ASSERT(TlsGetCurrentValue(&Unit.TLS, &Runtime) && Runtime->IsProtected, "barrier.cpp - TlsGetCurrentValue(&Unit.TLS, &Runtime) && Runtime->IsProtected"); + + Runtime->IsProtected = FALSE; +} + + + +BOOL ACLContains( + HOOK_ACL* InACL, + ULONG InCheckID) +{ + /* + Returns: + + TRUE if the given ACL contains the given ID, FALSE otherwise. + */ + ULONG Index; + + for (Index = 0; Index < InACL->Count; Index++) + { + if (InACL->Entries[Index] == InCheckID) + return TRUE; + } + + return FALSE; +} + + +BOOL DetourExport IsThreadIntercepted( + HOOK_ACL* LocalACL, + ULONG InThreadID) +{ + /* + Description: + + Please refer to DetourIsThreadIntercepted() for more information. + + Returns: + + TRUE if the given thread is intercepted by the global AND local ACL, + FALSE otherwise. + */ + ULONG CheckID; + + if (InThreadID == 0) + CheckID = (ULONG)pthread_self(); + else + CheckID = InThreadID; + + if (ACLContains(&Unit.GlobalACL, CheckID)) + { + if (ACLContains(LocalACL, CheckID)) + { + if (LocalACL->IsExclusive) + return FALSE; + } + else + { + if (!LocalACL->IsExclusive) + return FALSE; + } + + return !Unit.GlobalACL.IsExclusive; + } + else + { + if (ACLContains(LocalACL, CheckID)) + { + if (LocalACL->IsExclusive) + return FALSE; + } + else + { + if (!LocalACL->IsExclusive) + return FALSE; + } + + return Unit.GlobalACL.IsExclusive; + } +} + +LONG DetourExport DetourBarrierGetCallback(PVOID* OutValue) +{ + /* + Description: + + Is expected to be called inside a hook handler. Otherwise it + will fail with STATUS_NOT_SUPPORTED. The method retrieves + the callback initially passed to the related DetourInstallHook() + call. + + */ + LONG NtStatus; + LPTHREAD_RUNTIME_INFO Runtime; + + if (!IsValidPointer(OutValue, sizeof(PVOID))) + THROW(STATUS_INVALID_PARAMETER, (PWCHAR)"Invalid result storage specified."); + + if (!TlsGetCurrentValue(&Unit.TLS, &Runtime)) + THROW(-1, (PWCHAR)("The caller is not inside a hook handler.")); + + if (Runtime->Current != NULL) + *OutValue = Runtime->Callback; + else + THROW(-1, (PWCHAR)"The caller is not inside a hook handler."); + + RETURN; + +THROW_OUTRO: +FINALLY_OUTRO: + return NtStatus; +} + +PWCHAR DetourExport RtlGetLastErrorStringCopy() +{ + ULONG len = (ULONG)(strlen(LastError) + 1) * sizeof(PWCHAR); + PWCHAR pBuffer = (PWCHAR)malloc(len); + CopyMemory(pBuffer, LastError, len); + + return pBuffer; +} + +PWCHAR DetourExport RtlGetLastErrorString() +{ + return LastError; +} diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/linux_detours/src/detours.cpp b/third-party/RemoteInput/RemoteInput/Thirdparty/linux_detours/src/detours.cpp new file mode 100644 index 0000000..b373076 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/linux_detours/src/detours.cpp @@ -0,0 +1,3328 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Core Detours Functionality +// +// + +#define _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE 1 +#include "../include/types.h" +#include "../include/plthook.h" + +//#define DETOUR_DEBUG 1 +#define DETOURS_INTERNAL + +#include "../include/detours.h" + +#define NOTHROW + +////////////////////////////////////////////////////////////////////////////// +// +struct _DETOUR_ALIGN +{ + BYTE obTarget : 3; + BYTE obTrampoline : 5; +}; + +//C_ASSERT(sizeof(_DETOUR_ALIGN) == 1); + +////////////////////////////////////////////////////////////////////////////// +// +// Region reserved for system DLLs, which cannot be used for trampolines. +// +static PVOID s_pSystemRegionLowerBound = (PVOID)(ULONG_PTR)0x70000000; +static PVOID s_pSystemRegionUpperBound = (PVOID)(ULONG_PTR)0x80000000; + + +////////////////////////////////////////////////////////////////////////////// +// +// Hook Handle Slot List +// +ULONG GlobalSlotList[MAX_HOOK_COUNT]; +static LONG UniqueIDCounter = 0x10000000; + +////////////////////////////////////////////////////////////////////////////// +// +int detour_get_page_size() +{ + return getpagesize(); +} +PVOID detour_get_page(PVOID addr) +{ + ULONG_PTR ptr = (ULONG_PTR)addr; + return (PVOID)(ptr - (ptr % getpagesize())); +} + +static bool detour_is_imported(PBYTE pbCode, PBYTE pbAddress) +{ + plthook_t *plthook; + plthook_open(&plthook, NULL); + unsigned int pos = 0; /* This must be initialized with zero. */ + const char *name; + void **addr; + + while (plthook_enum(plthook, &pos, &name, &addr) == 0) { + if (addr != NULL && (PBYTE)addr == pbAddress) { + plthook_close(plthook); + return true; + } + } + plthook_close(plthook); + printf("IMPORTED\n"); + return false; +} + +inline ULONG_PTR detour_2gb_below(ULONG_PTR address) +{ + return (address > (ULONG_PTR)0x7ff80000) ? address - 0x7ff80000 : 0x80000; +} + +inline ULONG_PTR detour_2gb_above(ULONG_PTR address) +{ +#if defined(DETOURS_64BIT) + return (address < (ULONG_PTR)0xffffffff80000000) ? address + 0x7ff80000 : (ULONG_PTR)0xfffffffffff80000; +#else + return (address < (ULONG_PTR)0x80000000) ? address + 0x7ff80000 : (ULONG_PTR)0xfff80000; +#endif +} + +///////////////////////////////////////////////////////////////////////// X86. +// +#ifdef DETOURS_X86 + +const ULONG DETOUR_TRAMPOLINE_CODE_SIZE = 128; + +struct _DETOUR_TRAMPOLINE +{ + BYTE rbCode[30]; // target code + jmp to pbRemain + BYTE cbCode; // size of moved target code. + BYTE cbCodeBreak; // padding to make debugging easier. + BYTE rbRestore[22]; // original target code. + BYTE cbRestore; // size of original target code. + BYTE cbRestoreBreak; // padding to make debugging easier. + _DETOUR_ALIGN rAlign[8]; // instruction alignment array. + PBYTE pbRemain; // first instruction after moved code. [free list] + PBYTE pbDetour; // first instruction of detour function. + HOOK_ACL LocalACL; + void* Callback; + ULONG HLSIndex; + ULONG HLSIdent; + TRACED_HOOK_HANDLE OutHandle; // handle returned to user + void* Trampoline; + INT IsExecuted; + void* HookIntro; // . NET Intro function + UCHAR* OldProc; // old target function + void* HookProc; // function we detour to + void* HookOutro; // .NET Outro function + int* IsExecutedPtr; + BYTE rbTrampolineCode[DETOUR_TRAMPOLINE_CODE_SIZE]; +}; + +C_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 764); + +enum { + SIZE_OF_JMP = 5 +}; + +inline PBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE pbJmpVal) +{ + PBYTE pbJmpSrc = pbCode + 5; + *pbCode++ = 0xE9; // jmp +imm32 + *((INT32*&)pbCode)++ = (INT32)(pbJmpVal - pbJmpSrc); + return pbCode; +} + +inline PBYTE detour_gen_jmp_indirect(PBYTE pbCode, PBYTE *ppbJmpVal) +{ + *pbCode++ = 0xff; // jmp [+imm32] + *pbCode++ = 0x25; + *((INT32*&)pbCode)++ = (INT32)((PBYTE)ppbJmpVal); + return pbCode; +} + +inline PBYTE detour_gen_brk(PBYTE pbCode, PBYTE pbLimit) +{ + while (pbCode < pbLimit) { + *pbCode++ = 0xcc; // brk; + } + return pbCode; +} + +inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals) +{ + if (pbCode == NULL) { + return NULL; + } + if (ppGlobals != NULL) { + *ppGlobals = NULL; + } + + // First, skip over the import vector if there is one. + if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [imm32] + // Looks like an import alias jump, then get the code it points to. + PBYTE pbTarget = *(UNALIGNED PBYTE *)&pbCode[2]; + if (detour_is_imported(pbCode, pbTarget)) { + PBYTE pbNew = *(UNALIGNED PBYTE *)pbTarget; + DETOUR_TRACE(("%p->%p: skipped over import table.\n", pbCode, pbNew)); + pbCode = pbNew; + } + } + + // Then, skip over a patch jump + if (pbCode[0] == 0xeb) { // jmp +imm8 + PBYTE pbNew = pbCode + 2 + *(CHAR *)&pbCode[1]; + DETOUR_TRACE(("%p->%p: skipped over short jump.\n", pbCode, pbNew)); + pbCode = pbNew; + + // First, skip over the import vector if there is one. + if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [imm32] + // Looks like an import alias jump, then get the code it points to. + PBYTE pbTarget = *(UNALIGNED PBYTE *)&pbCode[2]; + if (detour_is_imported(pbCode, pbTarget)) { + pbNew = *(UNALIGNED PBYTE *)pbTarget; + DETOUR_TRACE(("%p->%p: skipped over import table.\n", pbCode, pbNew)); + pbCode = pbNew; + } + } + // Finally, skip over a long jump if it is the target of the patch jump. + else if (pbCode[0] == 0xe9) { // jmp +imm32 + pbNew = pbCode + 5 + *(UNALIGNED INT32 *)&pbCode[1]; + DETOUR_TRACE(("%p->%p: skipped over long jump.\n", pbCode, pbNew)); + pbCode = pbNew; + } + } + return pbCode; +} + +inline void detour_find_jmp_bounds(PBYTE pbCode, + PDETOUR_TRAMPOLINE *ppLower, + PDETOUR_TRAMPOLINE *ppUpper) +{ + // We have to place trampolines within +/- 2GB of code. + ULONG_PTR lo = detour_2gb_below((ULONG_PTR)pbCode); + ULONG_PTR hi = detour_2gb_above((ULONG_PTR)pbCode); + DETOUR_TRACE(("[%p..%p..%p]\n", lo, pbCode, hi)); + + // And, within +/- 2GB of relative jmp targets. + if (pbCode[0] == 0xe9) { // jmp +imm32 + PBYTE pbNew = pbCode + 5 + *(UNALIGNED INT32 *)&pbCode[1]; + + if (pbNew < pbCode) { + hi = detour_2gb_above((ULONG_PTR)pbNew); + } + else { + lo = detour_2gb_below((ULONG_PTR)pbNew); + } + DETOUR_TRACE(("[%p..%p..%p] +imm32\n", lo, pbCode, hi)); + } + + *ppLower = (PDETOUR_TRAMPOLINE)lo; + *ppUpper = (PDETOUR_TRAMPOLINE)hi; +} + +inline BOOL detour_does_code_end_function(PBYTE pbCode) +{ + if (pbCode[0] == 0xeb || // jmp +imm8 + pbCode[0] == 0xe9 || // jmp +imm32 + pbCode[0] == 0xe0 || // jmp eax + pbCode[0] == 0xc2 || // ret +imm8 + pbCode[0] == 0xc3 || // ret + pbCode[0] == 0xcc) { // brk + return TRUE; + } + else if (pbCode[0] == 0xf3 && pbCode[1] == 0xc3) { // rep ret + return TRUE; + } + else if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [+imm32] + return TRUE; + } + else if ((pbCode[0] == 0x26 || // jmp es: + pbCode[0] == 0x2e || // jmp cs: + pbCode[0] == 0x36 || // jmp ss: + pbCode[0] == 0x3e || // jmp ds: + pbCode[0] == 0x64 || // jmp fs: + pbCode[0] == 0x65) && // jmp gs: + pbCode[1] == 0xff && // jmp [+imm32] + pbCode[2] == 0x25) { + return TRUE; + } + return FALSE; +} + +inline ULONG detour_is_code_filler(PBYTE pbCode) +{ + // 1-byte through 11-byte NOPs. + if (pbCode[0] == 0x90) { + return 1; + } + if (pbCode[0] == 0x66 && pbCode[1] == 0x90) { + return 2; + } + if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x00) { + return 3; + } + if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x40 && + pbCode[3] == 0x00) { + return 4; + } + if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x44 && + pbCode[3] == 0x00 && pbCode[4] == 0x00) { + return 5; + } + if (pbCode[0] == 0x66 && pbCode[1] == 0x0F && pbCode[2] == 0x1F && + pbCode[3] == 0x44 && pbCode[4] == 0x00 && pbCode[5] == 0x00) { + return 6; + } + if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x80 && + pbCode[3] == 0x00 && pbCode[4] == 0x00 && pbCode[5] == 0x00 && + pbCode[6] == 0x00) { + return 7; + } + if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x84 && + pbCode[3] == 0x00 && pbCode[4] == 0x00 && pbCode[5] == 0x00 && + pbCode[6] == 0x00 && pbCode[7] == 0x00) { + return 8; + } + if (pbCode[0] == 0x66 && pbCode[1] == 0x0F && pbCode[2] == 0x1F && + pbCode[3] == 0x84 && pbCode[4] == 0x00 && pbCode[5] == 0x00 && + pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00) { + return 9; + } + if (pbCode[0] == 0x66 && pbCode[1] == 0x66 && pbCode[2] == 0x0F && + pbCode[3] == 0x1F && pbCode[4] == 0x84 && pbCode[5] == 0x00 && + pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00 && + pbCode[9] == 0x00) { + return 10; + } + if (pbCode[0] == 0x66 && pbCode[1] == 0x66 && pbCode[2] == 0x66 && + pbCode[3] == 0x0F && pbCode[4] == 0x1F && pbCode[5] == 0x84 && + pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00 && + pbCode[9] == 0x00 && pbCode[10] == 0x00) { + return 11; + } + + // int 3. + if (pbCode[0] == 0xcc) { + return 1; + } + return 0; +} + +#endif // DETOURS_X86 + +///////////////////////////////////////////////////////////////////////// X64. +// +#ifdef DETOURS_X64 + +const ULONG DETOUR_TRAMPOLINE_CODE_SIZE = 0x150; + +struct _DETOUR_TRAMPOLINE +{ + // An X64 instuction can be 15 bytes long. + // In practice 11 seems to be the limit. + BYTE rbCode[30]; // target code + jmp to pbRemain. + BYTE cbCode; // size of moved target code. + BYTE cbCodeBreak; // padding to make debugging easier. + BYTE rbRestore[30]; // original target code. + BYTE cbRestore; // size of original target code. + BYTE cbRestoreBreak; // padding to make debugging easier. + _DETOUR_ALIGN rAlign[8]; // instruction alignment array. + PBYTE pbRemain; // first instruction after moved code. [free list] + PBYTE pbDetour; // first instruction of detour function. + BYTE rbCodeIn[8]; // jmp [pbDetour] + HOOK_ACL LocalACL; + void* Callback; + ULONG HLSIndex; + ULONG HLSIdent; + TRACED_HOOK_HANDLE OutHandle; // handle returned to user + void* Trampoline; + INT IsExecuted; + void* HookIntro; // . NET Intro function + UCHAR* OldProc; // old target function + void* HookProc; // function we detour to + void* HookOutro; // .NET Outro function + int* IsExecutedPtr; + BYTE rbTrampolineCode[DETOUR_TRAMPOLINE_CODE_SIZE]; +}; + +//C_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 968); + +enum { + SIZE_OF_JMP = 5 +}; + +inline PBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE pbJmpVal) +{ + PBYTE pbJmpSrc = pbCode + 5; + *pbCode++ = 0xE9; // jmp +imm32 + *((INT32*&)pbCode)++ = (INT32)(pbJmpVal - pbJmpSrc); + return pbCode; +} + +inline PBYTE detour_gen_jmp_indirect(PBYTE pbCode, PBYTE *ppbJmpVal) +{ + PBYTE pbJmpSrc = pbCode + 6; + *pbCode++ = 0xff; // jmp [+imm32] + *pbCode++ = 0x25; + *((INT32*&)pbCode)++ = (INT32)((PBYTE)ppbJmpVal - pbJmpSrc); + return pbCode; +} + +inline PBYTE detour_gen_brk(PBYTE pbCode, PBYTE pbLimit) +{ + while (pbCode < pbLimit) { + *pbCode++ = 0xcc; // brk; + } + return pbCode; +} + +inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals) +{ + if (pbCode == NULL) { + return NULL; + } + if (ppGlobals != NULL) { + *ppGlobals = NULL; + } + + // First, skip over the import vector if there is one. + if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [+imm32] + // Looks like an import alias jump, then get the code it points to. + PBYTE pbTarget = pbCode + 6 + *(UNALIGNED INT32 *)&pbCode[2]; + if (detour_is_imported(pbCode, pbTarget)) { + PBYTE pbNew = *(UNALIGNED PBYTE *)pbTarget; + DETOUR_TRACE(("%p->%p: skipped over import table.\n", pbCode, pbNew)); + pbCode = pbNew; + } + } + + // Then, skip over a patch jump + if (pbCode[0] == 0xeb) { // jmp +imm8 + PBYTE pbNew = pbCode + 2 + *(CHAR *)&pbCode[1]; + DETOUR_TRACE(("%p->%p: skipped over short jump.\n", pbCode, pbNew)); + pbCode = pbNew; + + // First, skip over the import vector if there is one. + if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [+imm32] + // Looks like an import alias jump, then get the code it points to. + PBYTE pbTarget = pbCode + 6 + *(UNALIGNED INT32 *)&pbCode[2]; + if (detour_is_imported(pbCode, pbTarget)) { + pbNew = *(UNALIGNED PBYTE *)pbTarget; + DETOUR_TRACE(("%p->%p: skipped over import table.\n", pbCode, pbNew)); + pbCode = pbNew; + } + } + // Finally, skip over a long jump if it is the target of the patch jump. + else if (pbCode[0] == 0xe9) { // jmp +imm32 + pbNew = pbCode + 5 + *(UNALIGNED INT32 *)&pbCode[1]; + DETOUR_TRACE(("%p->%p: skipped over long jump.\n", pbCode, pbNew)); + pbCode = pbNew; + } + } + return pbCode; +} + +inline void detour_find_jmp_bounds(PBYTE pbCode, + PDETOUR_TRAMPOLINE *ppLower, + PDETOUR_TRAMPOLINE *ppUpper) +{ + // We have to place trampolines within +/- 2GB of code. + ULONG_PTR lo = detour_2gb_below((ULONG_PTR)pbCode); + ULONG_PTR hi = detour_2gb_above((ULONG_PTR)pbCode); + DETOUR_TRACE(("[%p..%p..%p]\n", lo, pbCode, hi)); + + // And, within +/- 2GB of relative jmp vectors. + if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [+imm32] + PBYTE pbNew = pbCode + 6 + *(UNALIGNED INT32 *)&pbCode[2]; + + if (pbNew < pbCode) { + hi = detour_2gb_above((ULONG_PTR)pbNew); + } + else { + lo = detour_2gb_below((ULONG_PTR)pbNew); + } + DETOUR_TRACE(("[%p..%p..%p] [+imm32]\n", lo, pbCode, hi)); + } + // And, within +/- 2GB of relative jmp targets. + else if (pbCode[0] == 0xe9) { // jmp +imm32 + PBYTE pbNew = pbCode + 5 + *(UNALIGNED INT32 *)&pbCode[1]; + + if (pbNew < pbCode) { + hi = detour_2gb_above((ULONG_PTR)pbNew); + } + else { + lo = detour_2gb_below((ULONG_PTR)pbNew); + } + DETOUR_TRACE(("[%p..%p..%p] +imm32\n", lo, pbCode, hi)); + } + + *ppLower = (PDETOUR_TRAMPOLINE)lo; + *ppUpper = (PDETOUR_TRAMPOLINE)hi; +} + +inline BOOL detour_does_code_end_function(PBYTE pbCode) +{ + if (pbCode[0] == 0xeb || // jmp +imm8 + pbCode[0] == 0xe9 || // jmp +imm32 + pbCode[0] == 0xe0 || // jmp eax + pbCode[0] == 0xc2 || // ret +imm8 + pbCode[0] == 0xc3 || // ret + pbCode[0] == 0xcc) { // brk + return TRUE; + } + else if (pbCode[0] == 0xf3 && pbCode[1] == 0xc3) { // rep ret + return TRUE; + } + else if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [+imm32] + return TRUE; + } + else if ((pbCode[0] == 0x26 || // jmp es: + pbCode[0] == 0x2e || // jmp cs: + pbCode[0] == 0x36 || // jmp ss: + pbCode[0] == 0x3e || // jmp ds: + pbCode[0] == 0x64 || // jmp fs: + pbCode[0] == 0x65) && // jmp gs: + pbCode[1] == 0xff && // jmp [+imm32] + pbCode[2] == 0x25) { + return TRUE; + } + return FALSE; +} + +inline ULONG detour_is_code_filler(PBYTE pbCode) +{ + // 1-byte through 11-byte NOPs. + if (pbCode[0] == 0x90) { + return 1; + } + if (pbCode[0] == 0x66 && pbCode[1] == 0x90) { + return 2; + } + if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x00) { + return 3; + } + if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x40 && + pbCode[3] == 0x00) { + return 4; + } + if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x44 && + pbCode[3] == 0x00 && pbCode[4] == 0x00) { + return 5; + } + if (pbCode[0] == 0x66 && pbCode[1] == 0x0F && pbCode[2] == 0x1F && + pbCode[3] == 0x44 && pbCode[4] == 0x00 && pbCode[5] == 0x00) { + return 6; + } + if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x80 && + pbCode[3] == 0x00 && pbCode[4] == 0x00 && pbCode[5] == 0x00 && + pbCode[6] == 0x00) { + return 7; + } + if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x84 && + pbCode[3] == 0x00 && pbCode[4] == 0x00 && pbCode[5] == 0x00 && + pbCode[6] == 0x00 && pbCode[7] == 0x00) { + return 8; + } + if (pbCode[0] == 0x66 && pbCode[1] == 0x0F && pbCode[2] == 0x1F && + pbCode[3] == 0x84 && pbCode[4] == 0x00 && pbCode[5] == 0x00 && + pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00) { + return 9; + } + if (pbCode[0] == 0x66 && pbCode[1] == 0x66 && pbCode[2] == 0x0F && + pbCode[3] == 0x1F && pbCode[4] == 0x84 && pbCode[5] == 0x00 && + pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00 && + pbCode[9] == 0x00) { + return 10; + } + if (pbCode[0] == 0x66 && pbCode[1] == 0x66 && pbCode[2] == 0x66 && + pbCode[3] == 0x0F && pbCode[4] == 0x1F && pbCode[5] == 0x84 && + pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00 && + pbCode[9] == 0x00 && pbCode[10] == 0x00) { + return 11; + } + + // int 3. + if (pbCode[0] == 0xcc) { + return 1; + } + return 0; +} + +#endif // DETOURS_X64 + +//////////////////////////////////////////////////////////////////////// IA64. +// +#ifdef DETOURS_IA64 + + +#endif // DETOURS_IA64 + +#ifdef DETOURS_ARM + +const ULONG DETOUR_TRAMPOLINE_CODE_SIZE = 0x110; + + +struct _DETOUR_TRAMPOLINE +{ + // A Thumb-2 instruction can be 2 or 4 bytes long. + BYTE rbCode[62]; // target code + jmp to pbRemain + BYTE cbCode; // size of moved target code. + BYTE cbCodeBreak; // padding to make debugging easier. + BYTE rbRestore[22]; // original target code. + BYTE cbRestore; // size of original target code. + BYTE cbRestoreBreak; // padding to make debugging easier. + _DETOUR_ALIGN rAlign[8]; // instruction alignment array. + PBYTE pbRemain; // first instruction after moved code. [free list] + PBYTE pbDetour; // first instruction of detour function. + INT IsThumbTarget; + HOOK_ACL LocalACL; + void* Callback; + ULONG HLSIndex; + ULONG HLSIdent; + TRACED_HOOK_HANDLE OutHandle; // handle returned to user + void* Trampoline; + INT IsExecuted; + void* HookIntro; // . NET Intro function + UCHAR* OldProc; // old target function + void* HookProc; // function we detour to + void* HookOutro; // .NET Outro function + int* IsExecutedPtr; + BYTE rbTrampolineCode[DETOUR_TRAMPOLINE_CODE_SIZE]; +}; + +//C_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 900); + +enum { + SIZE_OF_JMP = 8 +}; + +inline PBYTE align4(PBYTE pValue) +{ + return (PBYTE)(((ULONG)pValue) & ~(ULONG)3u); +} +inline ULONG fetch_opcode(PBYTE pbCode) +{ + ULONG Opcode = *(UINT32 *)&pbCode[0]; + return Opcode; +} +inline ULONG fetch_thumb_opcode(PBYTE pbCode) +{ + ULONG Opcode = *(UINT16 *)&pbCode[0]; + if (Opcode >= 0xe800) { + Opcode = (Opcode << 16) | *(UINT16 *)&pbCode[2]; + } + return Opcode; +} + +inline void write_thumb_opcode(PBYTE &pbCode, ULONG Opcode) +{ + if (Opcode >= 0x10000) { + *((UINT16*&)pbCode)++ = Opcode >> 16; + } + *((UINT16*&)pbCode)++ = (UINT16)Opcode; +} +inline void write_arm_opcode(PBYTE &pbCode, ULONG Opcode) +{ + *((UINT32*&)pbCode)++ = (UINT32)Opcode; +} +#define A$ldr_rd_$rn_im$(rd, rn, im) /* ldr rd, [rn, #im] */ \ + (0xe5100000 | ((im) < 0 ? 0 : 1 << 23) | ((rn) << 16) | ((rd) << 12) | abs(im)) + +PBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE *ppPool, PBYTE pbJmpVal) +{ +#if defined(DETOURS_ARM32) + PBYTE pbLiteral; + if (ppPool != NULL) { + *ppPool = *ppPool - 4; + pbLiteral = *ppPool; + } + else { + pbLiteral = align4(pbCode + 4); + } + *((PBYTE*&)pbLiteral) = pbJmpVal; + LONG delta = pbLiteral - align4(pbCode + 4); + // stored as: F0 04 1F E5 + *((UINT32*&)pbCode)++ = A$ldr_rd_$rn_im$(15, 15, delta - 4); + if (ppPool == NULL) { + if (((ULONG)pbCode & 2) != 0) { + write_arm_opcode(pbCode, 0xe320f000); + } + pbCode += 4; + } + //*((UINT32*&)pbCode)++ = (UINT32)0xF004E51F | (delta); +#elif defined(DETOURS_ARM) + if (reinterpret_cast(pbCode) & 0x1) { + // reset is_thumb_flag + pbCode = DETOURS_PFUNC_TO_PBYTE(pbCode); + PBYTE pbLiteral; + if (ppPool != NULL) { + *ppPool = *ppPool - 4; + pbLiteral = *ppPool; + } + else { + pbLiteral = align4(pbCode + 6); + } + *((PBYTE*&)pbLiteral) = DETOURS_PBYTE_TO_PFUNC(pbJmpVal); + LONG delta = pbLiteral - align4(pbCode + 4); + + // stored as: DF F8 00 F0 + write_thumb_opcode(pbCode, 0xf8dff000 | delta); // LDR PC,[PC+n] + //write_thumb_opcode(pbCode, 0x9FE504f0 | delta); + + //write_thumb_opcode(pbCode, 0xF000DFF8 | delta); + + if (ppPool == NULL) { + if (((ULONG)pbCode & 2) != 0) { + write_thumb_opcode(pbCode, 0xdefe); // BREAK + } + pbCode += 4; + } + } + else { + PBYTE pbLiteral; + if (ppPool != NULL) { + *ppPool = *ppPool - 4; + pbLiteral = *ppPool; + } + else { + pbLiteral = align4(pbCode + 4); + } + *((PBYTE*&)pbLiteral) = pbJmpVal; + LONG delta = pbLiteral - align4(pbCode + 4); + // stored as: F0 04 1F E5 + *((UINT32*&)pbCode)++ = A$ldr_rd_$rn_im$(15, 15, delta - 4); + if (ppPool == NULL) { + if (((ULONG)pbCode & 2) != 0) { + write_arm_opcode(pbCode, 0xe320f000); + } + pbCode += 4; + } + } +#endif + return pbCode; +} + +inline PBYTE detour_gen_brk(PBYTE pbCode, PBYTE pbLimit) +{ + while (pbCode < pbLimit) { +#if defined(DETOURS_ARM32) + write_arm_opcode(pbCode, 0xe320f000); +#elif defined(DETOURS_ARM) + write_thumb_opcode(pbCode, 0xdefe); +#endif + } + return pbCode; +} + +inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals) +{ + if (pbCode == NULL) { + return NULL; + } + ULONG * isThumb = NULL; + if (ppGlobals != NULL) { + isThumb = (ULONG *)*ppGlobals; + *ppGlobals = NULL; + } + + if (isThumb != nullptr && *isThumb == 1) { + // read Thumb instruction set + + // Skip over the import jump if there is one. + pbCode = (PBYTE)DETOURS_PFUNC_TO_PBYTE(pbCode); + ULONG Opcode = fetch_thumb_opcode(pbCode); + + if ((Opcode & 0xfbf08f00) == 0xf2400c00) { // movw r12,#xxxx + ULONG Opcode2 = fetch_thumb_opcode(pbCode + 4); + + if ((Opcode2 & 0xfbf08f00) == 0xf2c00c00) { // movt r12,#xxxx + ULONG Opcode3 = fetch_thumb_opcode(pbCode + 8); + if (Opcode3 == 0xf8dcf000) { // ldr pc,[r12] + PBYTE pbTarget = (PBYTE)(((Opcode2 << 12) & 0xf7000000) | + ((Opcode2 << 1) & 0x08000000) | + ((Opcode2 << 16) & 0x00ff0000) | + ((Opcode >> 4) & 0x0000f700) | + ((Opcode >> 15) & 0x00000800) | + ((Opcode >> 0) & 0x000000ff)); + + if (detour_is_imported(pbCode, pbTarget)) { + PBYTE pbNew = *(PBYTE *)pbTarget; + pbNew = DETOURS_PFUNC_TO_PBYTE(pbNew); + DETOUR_TRACE(("%p->%p: skipped over import table.\n", pbCode, pbNew)); + return pbNew; + } + } + } + } + } + else { + // read ARM instruction set + // Skip over the import jump if there is one. + + ULONG Opcode = fetch_opcode(pbCode); + + if ((Opcode & 0xe28f0000) == 0xe28f0000) { // adr r12, #xxxx + ULONG Opcode2 = fetch_opcode(pbCode + 4); + if ((Opcode2 & 0xe28c0000) == 0xe28c0000) { // add r12, r12, #xxxx + ULONG Opcode3 = fetch_opcode(pbCode + 8); + if ((Opcode3 & 0xe5bcf000) == 0xe5bcf000) { // ldr pc,[r12] + ULONG target = (Opcode2 << 12) & 0x000FFFFF; + PBYTE pbTarget = /*(PBYTE)(((Opcode2 << 12) & 0xf7000000) | + ((Opcode2 << 1) & 0x08000000) | + ((Opcode2 << 16) & 0x00ff0000) | + ((Opcode >> 4) & 0x0000f700) | + ((Opcode >> 15) & 0x00000800) | + ((Opcode >> 0) & 0x000000ff)); */ + + //pbTarget = (PBYTE)(*(ULONG*)((pbCode + 8 + tgt + (Opcode3 & 0xFFF)))); + ((pbCode + 8 + target + (Opcode3 & 0xFFF))); + if (detour_is_imported(pbCode, pbTarget)) { + PBYTE pbNew = *(PBYTE *)pbTarget; + pbNew = DETOURS_PFUNC_TO_PBYTE(pbNew); + DETOUR_TRACE(("%p->%p: skipped over import table.\n", pbCode, pbNew)); + return pbNew; + } + } + } + } + } + + return pbCode; +} + +inline void detour_find_jmp_bounds(PBYTE pbCode, + PDETOUR_TRAMPOLINE *ppLower, + PDETOUR_TRAMPOLINE *ppUpper) +{ + // We have to place trampolines within +/- 2GB of code. + ULONG_PTR lo = detour_2gb_below((ULONG_PTR)pbCode); + ULONG_PTR hi = detour_2gb_above((ULONG_PTR)pbCode); + DETOUR_TRACE(("[%p..%p..%p]\n", lo, pbCode, hi)); + + *ppLower = (PDETOUR_TRAMPOLINE)lo; + *ppUpper = (PDETOUR_TRAMPOLINE)hi; +} + + +inline BOOL detour_does_code_end_function(PBYTE pbCode) +{ + ULONG Opcode = fetch_thumb_opcode(pbCode); + if ((Opcode & 0xffffff87) == 0x4700 || // bx + (Opcode & 0xf800d000) == 0xf0009000) { // b + return TRUE; + } + if ((Opcode & 0xffff8000) == 0xe8bd8000) { // pop {...,pc} + __debugbreak(); + return TRUE; + } + if ((Opcode & 0xffffff00) == 0x0000bd00) { // pop {...,pc} + __debugbreak(); + return TRUE; + } + return FALSE; +} + +inline ULONG detour_is_code_filler(PBYTE pbCode) +{ + if (pbCode[0] == 0x00 && pbCode[1] == 0xbf) { // nop. + return 2; + } + if (pbCode[0] == 0x00 && pbCode[1] == 0x00) { // zero-filled padding. + return 2; + } + return 0; +} + +#endif // DETOURS_ARM + +#ifdef DETOURS_ARM64 + +// must be aligned by 8 +const ULONG DETOUR_TRAMPOLINE_CODE_SIZE = 0x158; + +struct _DETOUR_TRAMPOLINE +{ + // Src: https://github.com/Microsoft/Detours/commit/c5cb6c3af5a6871df47131d6cc29d4262a412623 + // An ARM64 instruction is 4 bytes long. + // The overwrite is always 2 instructions plus a literal, so 16 bytes, 4 instructions. + // + // Copied instructions can expand. + // + // The scheme using MovImmediate can cause an instruction + // to grow as much as 6 times. + // That would be Bcc or Tbz with a large address space: + // 4 instructions to form immediate + // inverted tbz/bcc + // br + // + // An expansion of 4 is not uncommon -- bl/blr and small address space: + // 3 instructions to form immediate + // br or brl + // + // A theoretical maximum for rbCode is thefore 4*4*6 + 16 = 112 (another 16 for jmp to pbRemain). + // + // With literals, the maximum expansion is 5, including the literals: 4*4*5 + 16 = 96. + // + // The number is rounded up to 128. m_rbScratchDst should match this. + // + BYTE rbCode[128]; // target code + jmp to pbRemain + BYTE cbCode; // size of moved target code. + BYTE cbCodeBreak[3]; // padding to make debugging easier. + BYTE rbRestore[24]; // original target code. + BYTE cbRestore; // size of original target code. + BYTE cbRestoreBreak[3]; // padding to make debugging easier. + _DETOUR_ALIGN rAlign[8]; // instruction alignment array. + PBYTE pbRemain; // first instruction after moved code. [free list] + PBYTE pbDetour; // first instruction of detour function. + HOOK_ACL LocalACL; + void* Callback; + ULONG HLSIndex; + ULONG HLSIdent; + TRACED_HOOK_HANDLE OutHandle; // handle returned to user + void* Trampoline; + void* HookIntro; // . NET Intro function + UCHAR* OldProc; // old target function + void* HookProc; // function we detour to + void* HookOutro; // .NET Outro function + int* IsExecutedPtr; + BYTE rbTrampolineCode[DETOUR_TRAMPOLINE_CODE_SIZE]; +}; + +//C_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 1128); + +enum { + SIZE_OF_JMP = 16 +}; + +inline ULONG fetch_opcode(PBYTE pbCode) +{ + return *(ULONG *)pbCode; +} + +inline void write_opcode(PBYTE &pbCode, ULONG Opcode) +{ + *(ULONG *)pbCode = Opcode; + pbCode += 4; +} + +PBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE *ppPool, PBYTE pbJmpVal) +{ + PBYTE pbLiteral; + if (ppPool != NULL) { + *ppPool = *ppPool - 8; + pbLiteral = *ppPool; + } + else { + pbLiteral = pbCode + 2 * 4; + } + + *((PBYTE*&)pbLiteral) = pbJmpVal; + LONG delta = (LONG)(pbLiteral - pbCode); + + write_opcode(pbCode, 0x58000011 | ((delta / 4) << 5)); // LDR X17,[PC+n] + write_opcode(pbCode, 0xd61f0000 | (17 << 5)); // BR X17 + + if (ppPool == NULL) { + pbCode += 8; + } + return pbCode; +} + +inline PBYTE detour_gen_brk(PBYTE pbCode, PBYTE pbLimit) +{ + while (pbCode < pbLimit) { + write_opcode(pbCode, 0xd4100000 | (0xf000 << 5)); + } + return pbCode; +} +inline INT64 detour_sign_extend(UINT64 value, UINT bits) +{ + const UINT left = 64 - bits; + const INT64 m1 = -1; + const INT64 wide = (INT64)(value << left); + const INT64 sign = (wide < 0) ? (m1 << left) : 0; + return value | sign; +} +inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals) +{ + if (pbCode == NULL) { + return NULL; + } + if (ppGlobals != NULL) { + *ppGlobals = NULL; + } + // From here: https://github.com/Microsoft/Detours/pull/8 + // Skip over the import jump if there is one. + pbCode = (PBYTE)pbCode; + ULONG Opcode = fetch_opcode(pbCode); + if ((Opcode & 0x9f00001f) == 0x90000010) { // adrp x16, IAT + ULONG Opcode2 = fetch_opcode(pbCode + 4); + + if ((Opcode2 & 0xffe003fe) == 0xf9400210) { // ldr x16, [x16, IAT] | ldr x17, [x16, IAT] + ULONG Opcode3 = fetch_opcode(pbCode + 8); + + if ((Opcode3 & 0x91020210) == 0x91020210) { // ADD X16, X16, IAT + ULONG Opcode4 = fetch_opcode(pbCode + 0xC); + if ((Opcode4 & 0xd61f0200) == 0xd61f0200) { // br x16 | br x17 + /* https://static.docs.arm.com/ddi0487/bb/DDI0487B_b_armv8_arm.pdf + The ADRP instruction shifts a signed, 21-bit immediate left by 12 bits, adds it to the value of the program counter with + the bottom 12 bits cleared to zero, and then writes the result to a general-purpose register. This permits the + calculation of the address at a 4KB aligned memory region. In conjunction with an ADD (immediate) instruction, or + a Load/Store instruction with a 12-bit immediate offset, this allows for the calculation of, or access to, any address + within 4GB of the current PC. + PC-rel. addressing + This section describes the encoding of the PC-rel. addressing instruction class. The encodings in this section are + decoded from Data Processing -- Immediate on page C4-226. + Add/subtract (immediate) + This section describes the encoding of the Add/subtract (immediate) instruction class. The encodings in this section + are decoded from Data Processing -- Immediate on page C4-226. + Decode fields + Instruction page + op + 0 ADR + 1 ADRP + C6.2.10 ADRP + Form PC-relative address to 4KB page adds an immediate value that is shifted left by 12 bits, to the PC value to + form a PC-relative address, with the bottom 12 bits masked out, and writes the result to the destination register. + ADRP ,

+ + + + nanobind logo + +

+ +_nanobind_ is a small binding library that exposes C++ types in Python and vice +versa. It is reminiscent of +[Boost.Python](https://www.boost.org/doc/libs/1_64_0/libs/python/doc/html) and +[pybind11](https://github.com/pybind/pybind11) and uses near-identical syntax. +In contrast to these existing tools, nanobind is more efficient: bindings +compile in a shorter amount of time, produce smaller binaries, and have better +runtime performance. + +More concretely, +[benchmarks](https://nanobind.readthedocs.io/en/latest/benchmark.html) show up +to **~4× faster** compile time, **~5× smaller** binaries, and **~10× lower** +runtime overheads compared to pybind11. nanobind also outperforms Cython in +important metrics (**3-12×** binary size reduction, **1.6-4×** compilation time +reduction, similar runtime performance). + +## Documentation + +Please see the following links for tutorial and reference documentation in +[HTML](https://nanobind.readthedocs.io/en/latest/) and +[PDF](https://nanobind.readthedocs.io/_/downloads/en/latest/pdf/) formats. + +## License and attribution + +All material in this repository is licensed under a three-clause [BSD +license](LICENSE). + +Please use the following BibTeX template to cite nanobind in scientific +discourse: + +```bibtex +@misc{nanobind, + author = {Wenzel Jakob}, + year = {2022}, + note = {https://github.com/wjakob/nanobind}, + title = {nanobind: tiny and efficient C++/Python bindings} +} +``` + +The nanobind logo was designed by [AndoTwin Studio](https://andotwinstudio.com) +(high-resolution download: +[light](https://rgl.s3.eu-central-1.amazonaws.com/media/uploads/wjakob/2023/03/27/nanobind_logo.jpg), +[dark](https://rgl.s3.eu-central-1.amazonaws.com/media/uploads/wjakob/2023/03/28/nanobind_logo_dark_1.png)). diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/cmake/collect-symbols-pypy.py b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/cmake/collect-symbols-pypy.py new file mode 100644 index 0000000..805490d --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/cmake/collect-symbols-pypy.py @@ -0,0 +1,28 @@ +from urllib.request import urlopen +import tarfile +import subprocess + +funcs: "set[str]" = set() + +files = [ + ('https://downloads.python.org/pypy/pypy3.9-v7.3.11-macos_arm64.tar.bz2', 'pypy3.9-v7.3.11-macos_arm64/bin/libpypy3.9-c.dylib') +] + +for f in files: + fs = urlopen(f[0]) + ft = tarfile.open(fileobj=fs, mode="r|bz2") + success = False + for member in ft: # move to the next file each loop + if member.name == f[1]: + ft.extract(member, path='tmp') + success = True + assert success + + out = subprocess.check_output(['nm', '-gjU', 'tmp/' + f[1]]) + for line in out.decode().split('\n'): + if line.startswith('_Py') or line.startswith('__Py'): + funcs.add(line) + +with open("darwin-ld-pypy.sym", "w") as f: + for func in sorted(list(funcs)): + f.write(f'-U _{func}\n') diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/cmake/collect-symbols.py b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/cmake/collect-symbols.py new file mode 100644 index 0000000..ad92da4 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/cmake/collect-symbols.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# +# This script collects a list of symbols that are considered to be part of the +# CPython API. The result is used to inform the macOS linker that it's fine for +# those symbols to be undefined when an extension module is linked, as they +# will be provided when the extension module is loaded into the interpreter. + +from urllib.request import urlopen +import re + +funcs: "set[str]" = set() + +for ver in ['3.7', '3.8', '3.9']: + url = f'https://raw.githubusercontent.com/python/cpython/{ver}/PC/python3.def' + output = urlopen(url).read().decode('utf-8') + for match in re.findall(r" (.*)=.*", output): + funcs.add(match) + +for ver in ['3.10', '3.11', 'main']: + url = f'https://raw.githubusercontent.com/python/cpython/{ver}/PC/python3dll.c' + output = urlopen(url).read().decode('utf-8') + for match in re.findall(r"EXPORT_FUNC\((.*)\)", output): + funcs.add(match) + +funcs.remove('name') + +# Add a few more functions that nanobind uses and which aren't in the above list +funcs |= { + 'PyFrame_GetBack', + 'PyGILState_Check', + 'PyObject_LengthHint', + 'Py_CompileStringExFlags', + '_PyInterpreterState_Get', + '_PyObject_MakeTpCall', + '_PyObject_NextNotImplemented', + '_Py_CheckFunctionResult', + '_Py_RefTotal' +} + +with open("darwin-ld-cpython.sym", "w") as f: + for func in sorted(list(funcs)): + f.write(f'-U _{func}\n') diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/cmake/darwin-ld-cpython.sym b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/cmake/darwin-ld-cpython.sym new file mode 100644 index 0000000..850dd38 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/cmake/darwin-ld-cpython.sym @@ -0,0 +1,925 @@ +-U _PyAIter_Check +-U _PyArg_Parse +-U _PyArg_ParseTuple +-U _PyArg_ParseTupleAndKeywords +-U _PyArg_UnpackTuple +-U _PyArg_VaParse +-U _PyArg_VaParseTupleAndKeywords +-U _PyArg_ValidateKeywordArguments +-U _PyBaseObject_Type +-U _PyBool_FromLong +-U _PyBool_Type +-U _PyBuffer_FillContiguousStrides +-U _PyBuffer_FillInfo +-U _PyBuffer_FromContiguous +-U _PyBuffer_GetPointer +-U _PyBuffer_IsContiguous +-U _PyBuffer_Release +-U _PyBuffer_SizeFromFormat +-U _PyBuffer_ToContiguous +-U _PyByteArrayIter_Type +-U _PyByteArray_AsString +-U _PyByteArray_Concat +-U _PyByteArray_FromObject +-U _PyByteArray_FromStringAndSize +-U _PyByteArray_Resize +-U _PyByteArray_Size +-U _PyByteArray_Type +-U _PyBytesIter_Type +-U _PyBytes_AsString +-U _PyBytes_AsStringAndSize +-U _PyBytes_Concat +-U _PyBytes_ConcatAndDel +-U _PyBytes_DecodeEscape +-U _PyBytes_FromFormat +-U _PyBytes_FromFormatV +-U _PyBytes_FromObject +-U _PyBytes_FromString +-U _PyBytes_FromStringAndSize +-U _PyBytes_Repr +-U _PyBytes_Size +-U _PyBytes_Type +-U _PyCFunction_Call +-U _PyCFunction_ClearFreeList +-U _PyCFunction_GetFlags +-U _PyCFunction_GetFunction +-U _PyCFunction_GetSelf +-U _PyCFunction_New +-U _PyCFunction_NewEx +-U _PyCFunction_Type +-U _PyCMethod_New +-U _PyCallIter_New +-U _PyCallIter_Type +-U _PyCallable_Check +-U _PyCapsule_GetContext +-U _PyCapsule_GetDestructor +-U _PyCapsule_GetName +-U _PyCapsule_GetPointer +-U _PyCapsule_Import +-U _PyCapsule_IsValid +-U _PyCapsule_New +-U _PyCapsule_SetContext +-U _PyCapsule_SetDestructor +-U _PyCapsule_SetName +-U _PyCapsule_SetPointer +-U _PyCapsule_Type +-U _PyClassMethodDescr_Type +-U _PyCodec_BackslashReplaceErrors +-U _PyCodec_Decode +-U _PyCodec_Decoder +-U _PyCodec_Encode +-U _PyCodec_Encoder +-U _PyCodec_IgnoreErrors +-U _PyCodec_IncrementalDecoder +-U _PyCodec_IncrementalEncoder +-U _PyCodec_KnownEncoding +-U _PyCodec_LookupError +-U _PyCodec_NameReplaceErrors +-U _PyCodec_Register +-U _PyCodec_RegisterError +-U _PyCodec_ReplaceErrors +-U _PyCodec_StreamReader +-U _PyCodec_StreamWriter +-U _PyCodec_StrictErrors +-U _PyCodec_Unregister +-U _PyCodec_XMLCharRefReplaceErrors +-U _PyComplex_AsCComplex +-U _PyComplex_FromCComplex +-U _PyComplex_FromDoubles +-U _PyComplex_ImagAsDouble +-U _PyComplex_RealAsDouble +-U _PyComplex_Type +-U _PyDescr_NewClassMethod +-U _PyDescr_NewGetSet +-U _PyDescr_NewMember +-U _PyDescr_NewMethod +-U _PyDictItems_Type +-U _PyDictIterItem_Type +-U _PyDictIterKey_Type +-U _PyDictIterValue_Type +-U _PyDictKeys_Type +-U _PyDictProxy_New +-U _PyDictProxy_Type +-U _PyDictValues_Type +-U _PyDict_Clear +-U _PyDict_Contains +-U _PyDict_Copy +-U _PyDict_DelItem +-U _PyDict_DelItemString +-U _PyDict_GetItem +-U _PyDict_GetItemString +-U _PyDict_GetItemWithError +-U _PyDict_Items +-U _PyDict_Keys +-U _PyDict_Merge +-U _PyDict_MergeFromSeq2 +-U _PyDict_New +-U _PyDict_Next +-U _PyDict_SetItem +-U _PyDict_SetItemString +-U _PyDict_Size +-U _PyDict_Type +-U _PyDict_Update +-U _PyDict_Values +-U _PyEllipsis_Type +-U _PyEnum_Type +-U _PyErr_BadArgument +-U _PyErr_BadInternalCall +-U _PyErr_CheckSignals +-U _PyErr_Clear +-U _PyErr_Display +-U _PyErr_ExceptionMatches +-U _PyErr_Fetch +-U _PyErr_Format +-U _PyErr_FormatV +-U _PyErr_GetExcInfo +-U _PyErr_GetHandledException +-U _PyErr_GetRaisedException +-U _PyErr_GivenExceptionMatches +-U _PyErr_NewException +-U _PyErr_NewExceptionWithDoc +-U _PyErr_NoMemory +-U _PyErr_NormalizeException +-U _PyErr_Occurred +-U _PyErr_Print +-U _PyErr_PrintEx +-U _PyErr_ProgramText +-U _PyErr_ResourceWarning +-U _PyErr_Restore +-U _PyErr_SetExcFromWindowsErr +-U _PyErr_SetExcFromWindowsErrWithFilename +-U _PyErr_SetExcFromWindowsErrWithFilenameObject +-U _PyErr_SetExcFromWindowsErrWithFilenameObjects +-U _PyErr_SetExcInfo +-U _PyErr_SetFromErrno +-U _PyErr_SetFromErrnoWithFilename +-U _PyErr_SetFromErrnoWithFilenameObject +-U _PyErr_SetFromErrnoWithFilenameObjects +-U _PyErr_SetFromWindowsErr +-U _PyErr_SetFromWindowsErrWithFilename +-U _PyErr_SetHandledException +-U _PyErr_SetImportError +-U _PyErr_SetImportErrorSubclass +-U _PyErr_SetInterrupt +-U _PyErr_SetInterruptEx +-U _PyErr_SetNone +-U _PyErr_SetObject +-U _PyErr_SetRaisedException +-U _PyErr_SetString +-U _PyErr_SyntaxLocation +-U _PyErr_SyntaxLocationEx +-U _PyErr_WarnEx +-U _PyErr_WarnExplicit +-U _PyErr_WarnFormat +-U _PyErr_WriteUnraisable +-U _PyEval_AcquireLock +-U _PyEval_AcquireThread +-U _PyEval_CallFunction +-U _PyEval_CallMethod +-U _PyEval_CallObjectWithKeywords +-U _PyEval_EvalCode +-U _PyEval_EvalCodeEx +-U _PyEval_EvalFrame +-U _PyEval_EvalFrameEx +-U _PyEval_GetBuiltins +-U _PyEval_GetCallStats +-U _PyEval_GetFrame +-U _PyEval_GetFuncDesc +-U _PyEval_GetFuncName +-U _PyEval_GetGlobals +-U _PyEval_GetLocals +-U _PyEval_InitThreads +-U _PyEval_ReInitThreads +-U _PyEval_ReleaseLock +-U _PyEval_ReleaseThread +-U _PyEval_RestoreThread +-U _PyEval_SaveThread +-U _PyEval_ThreadsInitialized +-U _PyExc_ArithmeticError +-U _PyExc_AssertionError +-U _PyExc_AttributeError +-U _PyExc_BaseException +-U _PyExc_BlockingIOError +-U _PyExc_BrokenPipeError +-U _PyExc_BufferError +-U _PyExc_BytesWarning +-U _PyExc_ChildProcessError +-U _PyExc_ConnectionAbortedError +-U _PyExc_ConnectionError +-U _PyExc_ConnectionRefusedError +-U _PyExc_ConnectionResetError +-U _PyExc_DeprecationWarning +-U _PyExc_EOFError +-U _PyExc_EnvironmentError +-U _PyExc_Exception +-U _PyExc_FileExistsError +-U _PyExc_FileNotFoundError +-U _PyExc_FloatingPointError +-U _PyExc_FutureWarning +-U _PyExc_GeneratorExit +-U _PyExc_IOError +-U _PyExc_ImportError +-U _PyExc_ImportWarning +-U _PyExc_IndentationError +-U _PyExc_IndexError +-U _PyExc_InterruptedError +-U _PyExc_IsADirectoryError +-U _PyExc_KeyError +-U _PyExc_KeyboardInterrupt +-U _PyExc_LookupError +-U _PyExc_MemoryError +-U _PyExc_ModuleNotFoundError +-U _PyExc_NameError +-U _PyExc_NotADirectoryError +-U _PyExc_NotImplementedError +-U _PyExc_OSError +-U _PyExc_OverflowError +-U _PyExc_PendingDeprecationWarning +-U _PyExc_PermissionError +-U _PyExc_ProcessLookupError +-U _PyExc_RecursionError +-U _PyExc_ReferenceError +-U _PyExc_ResourceWarning +-U _PyExc_RuntimeError +-U _PyExc_RuntimeWarning +-U _PyExc_StopAsyncIteration +-U _PyExc_StopIteration +-U _PyExc_SyntaxError +-U _PyExc_SyntaxWarning +-U _PyExc_SystemError +-U _PyExc_SystemExit +-U _PyExc_TabError +-U _PyExc_TimeoutError +-U _PyExc_TypeError +-U _PyExc_UnboundLocalError +-U _PyExc_UnicodeDecodeError +-U _PyExc_UnicodeEncodeError +-U _PyExc_UnicodeError +-U _PyExc_UnicodeTranslateError +-U _PyExc_UnicodeWarning +-U _PyExc_UserWarning +-U _PyExc_ValueError +-U _PyExc_Warning +-U _PyExc_WindowsError +-U _PyExc_ZeroDivisionError +-U _PyExceptionClass_Name +-U _PyException_GetCause +-U _PyException_GetContext +-U _PyException_GetTraceback +-U _PyException_SetCause +-U _PyException_SetContext +-U _PyException_SetTraceback +-U _PyFile_FromFd +-U _PyFile_GetLine +-U _PyFile_WriteObject +-U _PyFile_WriteString +-U _PyFilter_Type +-U _PyFloat_AsDouble +-U _PyFloat_FromDouble +-U _PyFloat_FromString +-U _PyFloat_GetInfo +-U _PyFloat_GetMax +-U _PyFloat_GetMin +-U _PyFloat_Type +-U _PyFrame_GetBack +-U _PyFrame_GetCode +-U _PyFrame_GetLineNumber +-U _PyFrozenSet_New +-U _PyFrozenSet_Type +-U _PyGC_Collect +-U _PyGC_Disable +-U _PyGC_Enable +-U _PyGC_IsEnabled +-U _PyGILState_Check +-U _PyGILState_Ensure +-U _PyGILState_GetThisThreadState +-U _PyGILState_Release +-U _PyGetSetDescr_Type +-U _PyImport_AddModule +-U _PyImport_AddModuleObject +-U _PyImport_AppendInittab +-U _PyImport_Cleanup +-U _PyImport_ExecCodeModule +-U _PyImport_ExecCodeModuleEx +-U _PyImport_ExecCodeModuleObject +-U _PyImport_ExecCodeModuleWithPathnames +-U _PyImport_GetImporter +-U _PyImport_GetMagicNumber +-U _PyImport_GetMagicTag +-U _PyImport_GetModule +-U _PyImport_GetModuleDict +-U _PyImport_Import +-U _PyImport_ImportFrozenModule +-U _PyImport_ImportFrozenModuleObject +-U _PyImport_ImportModule +-U _PyImport_ImportModuleLevel +-U _PyImport_ImportModuleLevelObject +-U _PyImport_ImportModuleNoBlock +-U _PyImport_ReloadModule +-U _PyIndex_Check +-U _PyInterpreterState_Clear +-U _PyInterpreterState_Delete +-U _PyInterpreterState_Get +-U _PyInterpreterState_GetDict +-U _PyInterpreterState_GetID +-U _PyInterpreterState_New +-U _PyIter_Check +-U _PyIter_Next +-U _PyIter_Send +-U _PyListIter_Type +-U _PyListRevIter_Type +-U _PyList_Append +-U _PyList_AsTuple +-U _PyList_GetItem +-U _PyList_GetSlice +-U _PyList_Insert +-U _PyList_New +-U _PyList_Reverse +-U _PyList_SetItem +-U _PyList_SetSlice +-U _PyList_Size +-U _PyList_Sort +-U _PyList_Type +-U _PyLongRangeIter_Type +-U _PyLong_AsDouble +-U _PyLong_AsLong +-U _PyLong_AsLongAndOverflow +-U _PyLong_AsLongLong +-U _PyLong_AsLongLongAndOverflow +-U _PyLong_AsSize_t +-U _PyLong_AsSsize_t +-U _PyLong_AsUnsignedLong +-U _PyLong_AsUnsignedLongLong +-U _PyLong_AsUnsignedLongLongMask +-U _PyLong_AsUnsignedLongMask +-U _PyLong_AsVoidPtr +-U _PyLong_FromDouble +-U _PyLong_FromLong +-U _PyLong_FromLongLong +-U _PyLong_FromSize_t +-U _PyLong_FromSsize_t +-U _PyLong_FromString +-U _PyLong_FromUnsignedLong +-U _PyLong_FromUnsignedLongLong +-U _PyLong_FromVoidPtr +-U _PyLong_GetInfo +-U _PyLong_Type +-U _PyMap_Type +-U _PyMapping_Check +-U _PyMapping_GetItemString +-U _PyMapping_HasKey +-U _PyMapping_HasKeyString +-U _PyMapping_Items +-U _PyMapping_Keys +-U _PyMapping_Length +-U _PyMapping_SetItemString +-U _PyMapping_Size +-U _PyMapping_Values +-U _PyMarshal_ReadObjectFromString +-U _PyMarshal_WriteObjectToString +-U _PyMem_Calloc +-U _PyMem_Free +-U _PyMem_Malloc +-U _PyMem_Realloc +-U _PyMemberDescr_Type +-U _PyMember_GetOne +-U _PyMember_SetOne +-U _PyMemoryView_FromBuffer +-U _PyMemoryView_FromMemory +-U _PyMemoryView_FromObject +-U _PyMemoryView_GetContiguous +-U _PyMemoryView_Type +-U _PyMethodDescr_Type +-U _PyModuleDef_Init +-U _PyModuleDef_Type +-U _PyModule_AddFunctions +-U _PyModule_AddIntConstant +-U _PyModule_AddObject +-U _PyModule_AddObjectRef +-U _PyModule_AddStringConstant +-U _PyModule_AddType +-U _PyModule_Create2 +-U _PyModule_ExecDef +-U _PyModule_FromDefAndSpec2 +-U _PyModule_GetDef +-U _PyModule_GetDict +-U _PyModule_GetFilename +-U _PyModule_GetFilenameObject +-U _PyModule_GetName +-U _PyModule_GetNameObject +-U _PyModule_GetState +-U _PyModule_New +-U _PyModule_NewObject +-U _PyModule_SetDocString +-U _PyModule_Type +-U _PyNullImporter_Type +-U _PyNumber_Absolute +-U _PyNumber_Add +-U _PyNumber_And +-U _PyNumber_AsSsize_t +-U _PyNumber_Check +-U _PyNumber_Divmod +-U _PyNumber_Float +-U _PyNumber_FloorDivide +-U _PyNumber_InPlaceAdd +-U _PyNumber_InPlaceAnd +-U _PyNumber_InPlaceFloorDivide +-U _PyNumber_InPlaceLshift +-U _PyNumber_InPlaceMatrixMultiply +-U _PyNumber_InPlaceMultiply +-U _PyNumber_InPlaceOr +-U _PyNumber_InPlacePower +-U _PyNumber_InPlaceRemainder +-U _PyNumber_InPlaceRshift +-U _PyNumber_InPlaceSubtract +-U _PyNumber_InPlaceTrueDivide +-U _PyNumber_InPlaceXor +-U _PyNumber_Index +-U _PyNumber_Invert +-U _PyNumber_Long +-U _PyNumber_Lshift +-U _PyNumber_MatrixMultiply +-U _PyNumber_Multiply +-U _PyNumber_Negative +-U _PyNumber_Or +-U _PyNumber_Positive +-U _PyNumber_Power +-U _PyNumber_Remainder +-U _PyNumber_Rshift +-U _PyNumber_Subtract +-U _PyNumber_ToBase +-U _PyNumber_TrueDivide +-U _PyNumber_Xor +-U _PyODictItems_Type +-U _PyODictIter_Type +-U _PyODictKeys_Type +-U _PyODictValues_Type +-U _PyODict_DelItem +-U _PyODict_New +-U _PyODict_SetItem +-U _PyODict_Type +-U _PyOS_AfterFork +-U _PyOS_CheckStack +-U _PyOS_FSPath +-U _PyOS_InitInterrupts +-U _PyOS_InputHook +-U _PyOS_InterruptOccurred +-U _PyOS_ReadlineFunctionPointer +-U _PyOS_double_to_string +-U _PyOS_getsig +-U _PyOS_mystricmp +-U _PyOS_mystrnicmp +-U _PyOS_setsig +-U _PyOS_snprintf +-U _PyOS_string_to_double +-U _PyOS_strtol +-U _PyOS_strtoul +-U _PyOS_vsnprintf +-U _PyObject_ASCII +-U _PyObject_AsCharBuffer +-U _PyObject_AsFileDescriptor +-U _PyObject_AsReadBuffer +-U _PyObject_AsWriteBuffer +-U _PyObject_Bytes +-U _PyObject_Call +-U _PyObject_CallFunction +-U _PyObject_CallFunctionObjArgs +-U _PyObject_CallMethod +-U _PyObject_CallMethodObjArgs +-U _PyObject_CallNoArgs +-U _PyObject_CallObject +-U _PyObject_Calloc +-U _PyObject_CheckBuffer +-U _PyObject_CheckReadBuffer +-U _PyObject_ClearWeakRefs +-U _PyObject_CopyData +-U _PyObject_DelAttr +-U _PyObject_DelAttrString +-U _PyObject_DelItem +-U _PyObject_DelItemString +-U _PyObject_Dir +-U _PyObject_Format +-U _PyObject_Free +-U _PyObject_GetTypeData +-U _PyObject_GC_Del +-U _PyObject_GC_IsFinalized +-U _PyObject_GC_IsTracked +-U _PyObject_GC_Track +-U _PyObject_GC_UnTrack +-U _PyObject_GenericGetAttr +-U _PyObject_GenericGetDict +-U _PyObject_GenericSetAttr +-U _PyObject_GenericSetDict +-U _PyObject_GetAIter +-U _PyObject_GetAttr +-U _PyObject_GetAttrString +-U _PyObject_GetBuffer +-U _PyObject_GetItem +-U _PyObject_GetIter +-U _PyObject_HasAttr +-U _PyObject_HasAttrString +-U _PyObject_Hash +-U _PyObject_HashNotImplemented +-U _PyObject_Init +-U _PyObject_InitVar +-U _PyObject_IsInstance +-U _PyObject_IsSubclass +-U _PyObject_IsTrue +-U _PyObject_Length +-U _PyObject_LengthHint +-U _PyObject_Malloc +-U _PyObject_Not +-U _PyObject_Realloc +-U _PyObject_Repr +-U _PyObject_RichCompare +-U _PyObject_RichCompareBool +-U _PyObject_SelfIter +-U _PyObject_SetAttr +-U _PyObject_SetAttrString +-U _PyObject_SetItem +-U _PyObject_Size +-U _PyObject_Str +-U _PyObject_Type +-U _PyObject_Vectorcall +-U _PyObject_VectorcallMethod +-U _PyParser_SimpleParseFileFlags +-U _PyParser_SimpleParseStringFlags +-U _PyParser_SimpleParseStringFlagsFilename +-U _PyProperty_Type +-U _PyRangeIter_Type +-U _PyRange_Type +-U _PyReversed_Type +-U _PySeqIter_New +-U _PySeqIter_Type +-U _PySequence_Check +-U _PySequence_Concat +-U _PySequence_Contains +-U _PySequence_Count +-U _PySequence_DelItem +-U _PySequence_DelSlice +-U _PySequence_Fast +-U _PySequence_GetItem +-U _PySequence_GetSlice +-U _PySequence_In +-U _PySequence_InPlaceConcat +-U _PySequence_InPlaceRepeat +-U _PySequence_Index +-U _PySequence_Length +-U _PySequence_List +-U _PySequence_Repeat +-U _PySequence_SetItem +-U _PySequence_SetSlice +-U _PySequence_Size +-U _PySequence_Tuple +-U _PySetIter_Type +-U _PySet_Add +-U _PySet_Clear +-U _PySet_Contains +-U _PySet_Discard +-U _PySet_New +-U _PySet_Pop +-U _PySet_Size +-U _PySet_Type +-U _PySlice_AdjustIndices +-U _PySlice_GetIndices +-U _PySlice_GetIndicesEx +-U _PySlice_New +-U _PySlice_Type +-U _PySlice_Unpack +-U _PySortWrapper_Type +-U _PyState_AddModule +-U _PyState_FindModule +-U _PyState_RemoveModule +-U _PyStructSequence_GetItem +-U _PyStructSequence_New +-U _PyStructSequence_NewType +-U _PyStructSequence_SetItem +-U _PySuper_Type +-U _PySys_AddWarnOption +-U _PySys_AddWarnOptionUnicode +-U _PySys_AddXOption +-U _PySys_FormatStderr +-U _PySys_FormatStdout +-U _PySys_GetObject +-U _PySys_GetXOptions +-U _PySys_HasWarnOptions +-U _PySys_ResetWarnOptions +-U _PySys_SetArgv +-U _PySys_SetArgvEx +-U _PySys_SetObject +-U _PySys_SetPath +-U _PySys_WriteStderr +-U _PySys_WriteStdout +-U _PyThreadState_Clear +-U _PyThreadState_Delete +-U _PyThreadState_DeleteCurrent +-U _PyThreadState_Get +-U _PyThreadState_GetDict +-U _PyThreadState_GetFrame +-U _PyThreadState_GetID +-U _PyThreadState_GetInterpreter +-U _PyThreadState_New +-U _PyThreadState_SetAsyncExc +-U _PyThreadState_Swap +-U _PyThread_GetInfo +-U _PyThread_ReInitTLS +-U _PyThread_acquire_lock +-U _PyThread_acquire_lock_timed +-U _PyThread_allocate_lock +-U _PyThread_create_key +-U _PyThread_delete_key +-U _PyThread_delete_key_value +-U _PyThread_exit_thread +-U _PyThread_free_lock +-U _PyThread_get_key_value +-U _PyThread_get_stacksize +-U _PyThread_get_thread_ident +-U _PyThread_get_thread_native_id +-U _PyThread_init_thread +-U _PyThread_release_lock +-U _PyThread_set_key_value +-U _PyThread_set_stacksize +-U _PyThread_start_new_thread +-U _PyThread_tss_alloc +-U _PyThread_tss_create +-U _PyThread_tss_delete +-U _PyThread_tss_free +-U _PyThread_tss_get +-U _PyThread_tss_is_created +-U _PyThread_tss_set +-U _PyTraceBack_Here +-U _PyTraceBack_Print +-U _PyTraceBack_Type +-U _PyTupleIter_Type +-U _PyTuple_ClearFreeList +-U _PyTuple_GetItem +-U _PyTuple_GetSlice +-U _PyTuple_New +-U _PyTuple_Pack +-U _PyTuple_SetItem +-U _PyTuple_Size +-U _PyTuple_Type +-U _PyType_ClearCache +-U _PyType_FromMetaclass +-U _PyType_FromModuleAndSpec +-U _PyType_FromSpec +-U _PyType_FromSpecWithBases +-U _PyType_GenericAlloc +-U _PyType_GenericNew +-U _PyType_GetFlags +-U _PyType_GetModule +-U _PyType_GetModuleState +-U _PyType_GetName +-U _PyType_GetQualName +-U _PyType_GetSlot +-U _PyType_IsSubtype +-U _PyType_Modified +-U _PyType_Ready +-U _PyType_Type +-U _PyType_GetTypeDataSize +-U _PyUnicodeDecodeError_Create +-U _PyUnicodeDecodeError_GetEncoding +-U _PyUnicodeDecodeError_GetEnd +-U _PyUnicodeDecodeError_GetObject +-U _PyUnicodeDecodeError_GetReason +-U _PyUnicodeDecodeError_GetStart +-U _PyUnicodeDecodeError_SetEnd +-U _PyUnicodeDecodeError_SetReason +-U _PyUnicodeDecodeError_SetStart +-U _PyUnicodeEncodeError_GetEncoding +-U _PyUnicodeEncodeError_GetEnd +-U _PyUnicodeEncodeError_GetObject +-U _PyUnicodeEncodeError_GetReason +-U _PyUnicodeEncodeError_GetStart +-U _PyUnicodeEncodeError_SetEnd +-U _PyUnicodeEncodeError_SetReason +-U _PyUnicodeEncodeError_SetStart +-U _PyUnicodeIter_Type +-U _PyUnicodeTranslateError_GetEnd +-U _PyUnicodeTranslateError_GetObject +-U _PyUnicodeTranslateError_GetReason +-U _PyUnicodeTranslateError_GetStart +-U _PyUnicodeTranslateError_SetEnd +-U _PyUnicodeTranslateError_SetReason +-U _PyUnicodeTranslateError_SetStart +-U _PyUnicode_Append +-U _PyUnicode_AppendAndDel +-U _PyUnicode_AsASCIIString +-U _PyUnicode_AsCharmapString +-U _PyUnicode_AsDecodedObject +-U _PyUnicode_AsDecodedUnicode +-U _PyUnicode_AsEncodedObject +-U _PyUnicode_AsEncodedString +-U _PyUnicode_AsEncodedUnicode +-U _PyUnicode_AsLatin1String +-U _PyUnicode_AsMBCSString +-U _PyUnicode_AsRawUnicodeEscapeString +-U _PyUnicode_AsUCS4 +-U _PyUnicode_AsUCS4Copy +-U _PyUnicode_AsUTF16String +-U _PyUnicode_AsUTF32String +-U _PyUnicode_AsUTF8AndSize +-U _PyUnicode_AsUTF8String +-U _PyUnicode_AsUnicodeEscapeString +-U _PyUnicode_AsWideChar +-U _PyUnicode_AsWideCharString +-U _PyUnicode_BuildEncodingMap +-U _PyUnicode_ClearFreeList +-U _PyUnicode_Compare +-U _PyUnicode_CompareWithASCIIString +-U _PyUnicode_Concat +-U _PyUnicode_Contains +-U _PyUnicode_Count +-U _PyUnicode_Decode +-U _PyUnicode_DecodeASCII +-U _PyUnicode_DecodeCharmap +-U _PyUnicode_DecodeCodePageStateful +-U _PyUnicode_DecodeFSDefault +-U _PyUnicode_DecodeFSDefaultAndSize +-U _PyUnicode_DecodeLatin1 +-U _PyUnicode_DecodeLocale +-U _PyUnicode_DecodeLocaleAndSize +-U _PyUnicode_DecodeMBCS +-U _PyUnicode_DecodeMBCSStateful +-U _PyUnicode_DecodeRawUnicodeEscape +-U _PyUnicode_DecodeUTF16 +-U _PyUnicode_DecodeUTF16Stateful +-U _PyUnicode_DecodeUTF32 +-U _PyUnicode_DecodeUTF32Stateful +-U _PyUnicode_DecodeUTF7 +-U _PyUnicode_DecodeUTF7Stateful +-U _PyUnicode_DecodeUTF8 +-U _PyUnicode_DecodeUTF8Stateful +-U _PyUnicode_DecodeUnicodeEscape +-U _PyUnicode_EncodeCodePage +-U _PyUnicode_EncodeFSDefault +-U _PyUnicode_EncodeLocale +-U _PyUnicode_FSConverter +-U _PyUnicode_FSDecoder +-U _PyUnicode_Find +-U _PyUnicode_FindChar +-U _PyUnicode_Format +-U _PyUnicode_FromEncodedObject +-U _PyUnicode_FromFormat +-U _PyUnicode_FromFormatV +-U _PyUnicode_FromObject +-U _PyUnicode_FromOrdinal +-U _PyUnicode_FromString +-U _PyUnicode_FromStringAndSize +-U _PyUnicode_FromWideChar +-U _PyUnicode_GetDefaultEncoding +-U _PyUnicode_GetLength +-U _PyUnicode_GetSize +-U _PyUnicode_InternFromString +-U _PyUnicode_InternImmortal +-U _PyUnicode_InternInPlace +-U _PyUnicode_IsIdentifier +-U _PyUnicode_Join +-U _PyUnicode_Partition +-U _PyUnicode_RPartition +-U _PyUnicode_RSplit +-U _PyUnicode_ReadChar +-U _PyUnicode_Replace +-U _PyUnicode_Resize +-U _PyUnicode_RichCompare +-U _PyUnicode_Split +-U _PyUnicode_Splitlines +-U _PyUnicode_Substring +-U _PyUnicode_Tailmatch +-U _PyUnicode_Translate +-U _PyUnicode_Type +-U _PyUnicode_WriteChar +-U _PyVectorcall_Call +-U _PyVectorcall_NARGS +-U _PyWeakref_GetObject +-U _PyWeakref_NewProxy +-U _PyWeakref_NewRef +-U _PyWrapperDescr_Type +-U _PyWrapper_New +-U _PyZip_Type +-U _Py_AddPendingCall +-U _Py_AtExit +-U _Py_BuildValue +-U _Py_BytesMain +-U _Py_CompileString +-U _Py_CompileStringExFlags +-U _Py_DecRef +-U _Py_DecodeLocale +-U _Py_EncodeLocale +-U _Py_EndInterpreter +-U _Py_EnterRecursiveCall +-U _Py_Exit +-U _Py_FatalError +-U _Py_FileSystemDefaultEncodeErrors +-U _Py_FileSystemDefaultEncoding +-U _Py_Finalize +-U _Py_FinalizeEx +-U _Py_GenericAlias +-U _Py_GenericAliasType +-U _Py_GetArgcArgv +-U _Py_GetBuildInfo +-U _Py_GetCompiler +-U _Py_GetCopyright +-U _Py_GetExecPrefix +-U _Py_GetPath +-U _Py_GetPlatform +-U _Py_GetPrefix +-U _Py_GetProgramFullPath +-U _Py_GetProgramName +-U _Py_GetPythonHome +-U _Py_GetRecursionLimit +-U _Py_GetVersion +-U _Py_HasFileSystemDefaultEncoding +-U _Py_IncRef +-U _Py_Initialize +-U _Py_InitializeEx +-U _Py_Is +-U _Py_IsFalse +-U _Py_IsFinalizing +-U _Py_IsInitialized +-U _Py_IsNone +-U _Py_IsTrue +-U _Py_LeaveRecursiveCall +-U _Py_Main +-U _Py_MakePendingCalls +-U _Py_NewInterpreter +-U _Py_NewRef +-U _Py_ReprEnter +-U _Py_ReprLeave +-U _Py_SetPath +-U _Py_SetProgramName +-U _Py_SetPythonHome +-U _Py_SetRecursionLimit +-U _Py_SymtableString +-U _Py_UTF8Mode +-U _Py_VaBuildValue +-U _Py_XNewRef +-U __PyArg_ParseTupleAndKeywords_SizeT +-U __PyArg_ParseTuple_SizeT +-U __PyArg_Parse_SizeT +-U __PyArg_VaParseTupleAndKeywords_SizeT +-U __PyArg_VaParse_SizeT +-U __PyErr_BadInternalCall +-U __PyInterpreterState_Get +-U __PyObject_CallFunction_SizeT +-U __PyObject_CallMethod_SizeT +-U __PyObject_GC_Malloc +-U __PyObject_GC_New +-U __PyObject_GC_NewVar +-U __PyObject_GC_Resize +-U __PyObject_MakeTpCall +-U __PyObject_New +-U __PyObject_NewVar +-U __PyObject_NextNotImplemented +-U __PyState_AddModule +-U __PyThreadState_Init +-U __PyThreadState_Prealloc +-U __PyTrash_delete_later +-U __PyTrash_delete_nesting +-U __PyTrash_deposit_object +-U __PyTrash_destroy_chain +-U __PyTrash_thread_deposit_object +-U __PyTrash_thread_destroy_chain +-U __PyWeakref_CallableProxyType +-U __PyWeakref_ProxyType +-U __PyWeakref_RefType +-U __Py_IsFinalizing +-U __Py_BuildValue_SizeT +-U __Py_CheckFunctionResult +-U __Py_CheckRecursionLimit +-U __Py_CheckRecursiveCall +-U __Py_Dealloc +-U __Py_DecRef +-U __Py_EllipsisObject +-U __Py_FalseStruct +-U __Py_IncRef +-U __Py_NegativeRefcount +-U __Py_NoneStruct +-U __Py_NotImplementedStruct +-U __Py_RefTotal +-U __Py_SwappedOp +-U __Py_TrueStruct +-U __Py_VaBuildValue_SizeT +-U _Py_Version +-U __Py_MergeZeroLocalRefcount +-U __Py_DecRefShared +-U __Py_DecRefSharedDebug +-U __Py_DECREF_DecRefTotal +-U __Py_INCREF_IncRefTotal +-U __PyObject_GetDictPtr +-U _PyList_GetItemRef +-U _PyDict_GetItemRef +-U _PyDict_GetItemStringRef +-U _PyDict_SetDefault +-U _PyDict_SetDefaultRef +-U _PyWeakref_GetRef +-U _PyImport_AddModuleRef +-U _PyUnstable_Module_SetGIL +-U _PyMutex_Unlock +-U _PyMutex_Lock +-U _PyObject_IS_GC +-U _PyCriticalSection_Begin +-U _PyCriticalSection_End +-U _PyCriticalSection2_Begin +-U _PyCriticalSection2_End +-U _PyUnicode_AsUTF8 diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/cmake/darwin-ld-pypy.sym b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/cmake/darwin-ld-pypy.sym new file mode 100644 index 0000000..878b560 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/cmake/darwin-ld-pypy.sym @@ -0,0 +1,964 @@ +-U _PyArg_ValidateKeywordArguments +-U _PyModule_AddType +-U _PyPyAnySet_Check +-U _PyPyAnySet_CheckExact +-U _PyPyArg_Parse +-U _PyPyArg_ParseTuple +-U _PyPyArg_ParseTupleAndKeywords +-U _PyPyArg_UnpackTuple +-U _PyPyArg_VaParse +-U _PyPyArg_VaParseTupleAndKeywords +-U _PyPyBaseObject_Type +-U _PyPyBool_FromLong +-U _PyPyBool_Type +-U _PyPyBuffer_FillInfo +-U _PyPyBuffer_FromContiguous +-U _PyPyBuffer_GetPointer +-U _PyPyBuffer_IsContiguous +-U _PyPyBuffer_Release +-U _PyPyBuffer_ToContiguous +-U _PyPyBufferable_Type +-U _PyPyByteArray_AsString +-U _PyPyByteArray_Check +-U _PyPyByteArray_CheckExact +-U _PyPyByteArray_Concat +-U _PyPyByteArray_FromObject +-U _PyPyByteArray_FromStringAndSize +-U _PyPyByteArray_Resize +-U _PyPyByteArray_Size +-U _PyPyByteArray_Type +-U _PyPyBytes_AS_STRING +-U _PyPyBytes_AsString +-U _PyPyBytes_AsStringAndSize +-U _PyPyBytes_Concat +-U _PyPyBytes_ConcatAndDel +-U _PyPyBytes_FromFormat +-U _PyPyBytes_FromFormatV +-U _PyPyBytes_FromObject +-U _PyPyBytes_FromString +-U _PyPyBytes_FromStringAndSize +-U _PyPyBytes_Size +-U _PyPyBytes_Type +-U _PyPyCFunction_Call +-U _PyPyCFunction_Check +-U _PyPyCFunction_GetFunction +-U _PyPyCFunction_Type +-U _PyPyCFunction_NewEx +-U _PyPyCMethod_New +-U _PyPyCallIter_New +-U _PyPyCallable_Check +-U _PyPyCapsule_GetContext +-U _PyPyCapsule_GetDestructor +-U _PyPyCapsule_GetName +-U _PyPyCapsule_GetPointer +-U _PyPyCapsule_Import +-U _PyPyCapsule_IsValid +-U _PyPyCapsule_New +-U _PyPyCapsule_SetContext +-U _PyPyCapsule_SetDestructor +-U _PyPyCapsule_SetName +-U _PyPyCapsule_SetPointer +-U _PyPyCapsule_Type +-U _PyPyCell_Type +-U _PyPyClassMethodDescr_Type +-U _PyPyClassMethod_New +-U _PyPyClassMethod_Type +-U _PyPyCode_Addr2Line +-U _PyPyCode_Check +-U _PyPyCode_CheckExact +-U _PyPyCode_GetNumFree +-U _PyPyCode_New +-U _PyPyCode_NewEmpty +-U _PyPyCodec_Decode +-U _PyPyCodec_Decoder +-U _PyPyCodec_Encode +-U _PyPyCodec_Encoder +-U _PyPyCodec_IncrementalDecoder +-U _PyPyCodec_IncrementalEncoder +-U _PyPyComplex_AsCComplex +-U _PyPyComplex_Check +-U _PyPyComplex_CheckExact +-U _PyPyComplex_FromCComplex +-U _PyPyComplex_FromDoubles +-U _PyPyComplex_ImagAsDouble +-U _PyPyComplex_RealAsDouble +-U _PyPyComplex_Type +-U _PyPyContextVar_Get +-U _PyPyContextVar_New +-U _PyPyContextVar_Set +-U _PyPyCoro_Check +-U _PyPyCoro_CheckExact +-U _PyPyDateTimeAPI +-U _PyPyDateTime_Check +-U _PyPyDateTime_CheckExact +-U _PyPyDateTime_DATE_GET_HOUR +-U _PyPyDateTime_DATE_GET_MICROSECOND +-U _PyPyDateTime_DATE_GET_MINUTE +-U _PyPyDateTime_DATE_GET_SECOND +-U _PyPyDateTime_DELTA_GET_DAYS +-U _PyPyDateTime_DELTA_GET_MICROSECONDS +-U _PyPyDateTime_DELTA_GET_SECONDS +-U _PyPyDateTime_FromTimestamp +-U _PyPyDateTime_GET_DAY +-U _PyPyDateTime_GET_FOLD +-U _PyPyDateTime_GET_MONTH +-U _PyPyDateTime_GET_YEAR +-U _PyPyDateTime_TIME_GET_FOLD +-U _PyPyDateTime_TIME_GET_HOUR +-U _PyPyDateTime_TIME_GET_MICROSECOND +-U _PyPyDateTime_TIME_GET_MINUTE +-U _PyPyDateTime_TIME_GET_SECOND +-U _PyPyDate_Check +-U _PyPyDate_CheckExact +-U _PyPyDate_FromTimestamp +-U _PyPyDelta_Check +-U _PyPyDelta_CheckExact +-U _PyPyDescr_NewClassMethod +-U _PyPyDescr_NewGetSet +-U _PyPyDescr_NewMethod +-U _PyPyDictKeys_Type +-U _PyPyDictProxy_Check +-U _PyPyDictProxy_CheckExact +-U _PyPyDictProxy_New +-U _PyPyDictProxy_Type +-U _PyPyDictValues_Type +-U _PyPyDict_Clear +-U _PyPyDict_Contains +-U _PyPyDict_Copy +-U _PyPyDict_DelItem +-U _PyPyDict_DelItemString +-U _PyPyDict_GetItem +-U _PyPyDict_GetItemString +-U _PyPyDict_GetItemWithError +-U _PyPyDict_Items +-U _PyPyDict_Keys +-U _PyPyDict_Merge +-U _PyPyDict_New +-U _PyPyDict_Next +-U _PyPyDict_SetDefault +-U _PyPyDict_SetItem +-U _PyPyDict_SetItemString +-U _PyPyDict_Size +-U _PyPyDict_Type +-U _PyPyDict_Update +-U _PyPyDict_Values +-U _PyPyErr_BadArgument +-U _PyPyErr_BadInternalCall +-U _PyPyErr_CheckSignals +-U _PyPyErr_Clear +-U _PyPyErr_Display +-U _PyPyErr_ExceptionMatches +-U _PyPyErr_Fetch +-U _PyPyErr_Format +-U _PyPyErr_GetExcInfo +-U _PyPyErr_GivenExceptionMatches +-U _PyPyErr_NewException +-U _PyPyErr_NewExceptionWithDoc +-U _PyPyErr_NoMemory +-U _PyPyErr_NormalizeException +-U _PyPyErr_Occurred +-U _PyPyErr_Print +-U _PyPyErr_PrintEx +-U _PyPyErr_Restore +-U _PyPyErr_SetExcInfo +-U _PyPyErr_SetFromErrno +-U _PyPyErr_SetFromErrnoWithFilename +-U _PyPyErr_SetFromErrnoWithFilenameObject +-U _PyPyErr_SetFromErrnoWithFilenameObjects +-U _PyPyErr_SetInterrupt +-U _PyPyErr_SetNone +-U _PyPyErr_SetObject +-U _PyPyErr_SetString +-U _PyPyErr_Warn +-U _PyPyErr_WarnEx +-U _PyPyErr_WarnExplicit +-U _PyPyErr_WarnFormat +-U _PyPyErr_WriteUnraisable +-U _PyPyEval_AcquireThread +-U _PyPyEval_CallFunction +-U _PyPyEval_CallMethod +-U _PyPyEval_CallObjectWithKeywords +-U _PyPyEval_EvalCode +-U _PyPyEval_GetBuiltins +-U _PyPyEval_GetFrame +-U _PyPyEval_GetGlobals +-U _PyPyEval_GetLocals +-U _PyPyEval_InitThreads +-U _PyPyEval_MergeCompilerFlags +-U _PyPyEval_ReleaseThread +-U _PyPyEval_RestoreThread +-U _PyPyEval_SaveThread +-U _PyPyEval_ThreadsInitialized +-U _PyPyExc_ArithmeticError +-U _PyPyExc_AssertionError +-U _PyPyExc_AttributeError +-U _PyPyExc_BaseException +-U _PyPyExc_BlockingIOError +-U _PyPyExc_BrokenPipeError +-U _PyPyExc_BufferError +-U _PyPyExc_BytesWarning +-U _PyPyExc_ChildProcessError +-U _PyPyExc_ConnectionAbortedError +-U _PyPyExc_ConnectionError +-U _PyPyExc_ConnectionRefusedError +-U _PyPyExc_ConnectionResetError +-U _PyPyExc_DeprecationWarning +-U _PyPyExc_EOFError +-U _PyPyExc_Exception +-U _PyPyExc_FileExistsError +-U _PyPyExc_FileNotFoundError +-U _PyPyExc_FloatingPointError +-U _PyPyExc_FutureWarning +-U _PyPyExc_GeneratorExit +-U _PyPyExc_ImportError +-U _PyPyExc_ImportWarning +-U _PyPyExc_IndentationError +-U _PyPyExc_IndexError +-U _PyPyExc_InterruptedError +-U _PyPyExc_IsADirectoryError +-U _PyPyExc_KeyError +-U _PyPyExc_KeyboardInterrupt +-U _PyPyExc_LookupError +-U _PyPyExc_MemoryError +-U _PyPyExc_ModuleNotFoundError +-U _PyPyExc_NameError +-U _PyPyExc_NotADirectoryError +-U _PyPyExc_NotImplementedError +-U _PyPyExc_OSError +-U _PyPyExc_OverflowError +-U _PyPyExc_PendingDeprecationWarning +-U _PyPyExc_PermissionError +-U _PyPyExc_ProcessLookupError +-U _PyPyExc_RecursionError +-U _PyPyExc_ReferenceError +-U _PyPyExc_ResourceWarning +-U _PyPyExc_RuntimeError +-U _PyPyExc_RuntimeWarning +-U _PyPyExc_StopAsyncIteration +-U _PyPyExc_StopIteration +-U _PyPyExc_SyntaxError +-U _PyPyExc_SyntaxWarning +-U _PyPyExc_SystemError +-U _PyPyExc_SystemExit +-U _PyPyExc_TabError +-U _PyPyExc_TimeoutError +-U _PyPyExc_TypeError +-U _PyPyExc_UnboundLocalError +-U _PyPyExc_UnicodeDecodeError +-U _PyPyExc_UnicodeEncodeError +-U _PyPyExc_UnicodeError +-U _PyPyExc_UnicodeTranslateError +-U _PyPyExc_UnicodeWarning +-U _PyPyExc_UserWarning +-U _PyPyExc_ValueError +-U _PyPyExc_Warning +-U _PyPyExc_ZeroDivisionError +-U _PyPyExceptionInstance_Class +-U _PyPyException_GetCause +-U _PyPyException_GetContext +-U _PyPyException_GetTraceback +-U _PyPyException_SetCause +-U _PyPyException_SetContext +-U _PyPyException_SetTraceback +-U _PyPyFile_FromFd +-U _PyPyFile_FromString +-U _PyPyFile_GetLine +-U _PyPyFile_WriteObject +-U _PyPyFile_WriteString +-U _PyPyFloat_AS_DOUBLE +-U _PyPyFloat_AsDouble +-U _PyPyFloat_Check +-U _PyPyFloat_CheckExact +-U _PyPyFloat_FromDouble +-U _PyPyFloat_FromString +-U _PyPyFloat_Type +-U _PyPyFrame_New +-U _PyPyFrozenSet_Check +-U _PyPyFrozenSet_CheckExact +-U _PyPyFrozenSet_New +-U _PyPyFrozenSet_Type +-U _PyPyFunction_Check +-U _PyPyFunction_CheckExact +-U _PyPyFunction_GetCode +-U _PyPyFunction_Type +-U _PyPyGILState_Check +-U _PyPyGILState_Ensure +-U _PyPyGILState_Release +-U _PyPyGen_Check +-U _PyPyGen_CheckExact +-U _PyPyGetSetDescr_Type +-U _PyPyImport_AddModule +-U _PyPyImport_ExecCodeModule +-U _PyPyImport_ExecCodeModuleEx +-U _PyPyImport_GetModule +-U _PyPyImport_GetModuleDict +-U _PyPyImport_Import +-U _PyPyImport_ImportModule +-U _PyPyImport_ImportModuleLevelObject +-U _PyPyImport_ImportModuleNoBlock +-U _PyPyImport_ReloadModule +-U _PyPyIndex_Check +-U _PyPyInstanceMethod_Check +-U _PyPyInstanceMethod_Function +-U _PyPyInstanceMethod_GET_FUNCTION +-U _PyPyInstanceMethod_New +-U _PyPyInstanceMethod_Type +-U _PyPyInterpreterState_GetID +-U _PyPyInterpreterState_Head +-U _PyPyInterpreterState_Next +-U _PyPyIter_Check +-U _PyPyIter_Next +-U _PyPyList_Append +-U _PyPyList_AsTuple +-U _PyPyList_GET_ITEM +-U _PyPyList_GET_SIZE +-U _PyPyList_GetItem +-U _PyPyList_GetSlice +-U _PyPyList_Insert +-U _PyPyList_New +-U _PyPyList_Reverse +-U _PyPyList_SET_ITEM +-U _PyPyList_SetItem +-U _PyPyList_SetSlice +-U _PyPyList_Size +-U _PyPyList_Sort +-U _PyPyList_Type +-U _PyPyLong_AsDouble +-U _PyPyLong_AsLong +-U _PyPyLong_AsLongAndOverflow +-U _PyPyLong_AsLongLong +-U _PyPyLong_AsLongLongAndOverflow +-U _PyPyLong_AsSize_t +-U _PyPyLong_AsSsize_t +-U _PyPyLong_AsUnsignedLong +-U _PyPyLong_AsUnsignedLongLong +-U _PyPyLong_AsUnsignedLongLongMask +-U _PyPyLong_AsUnsignedLongMask +-U _PyPyLong_AsVoidPtr +-U _PyPyLong_FromDouble +-U _PyPyLong_FromLong +-U _PyPyLong_FromLongLong +-U _PyPyLong_FromSize_t +-U _PyPyLong_FromSsize_t +-U _PyPyLong_FromString +-U _PyPyLong_FromUnicode +-U _PyPyLong_FromUnicodeObject +-U _PyPyLong_FromUnsignedLong +-U _PyPyLong_FromUnsignedLongLong +-U _PyPyLong_FromVoidPtr +-U _PyPyLong_Type +-U _PyPyMapping_Check +-U _PyPyMapping_GetItemString +-U _PyPyMapping_HasKey +-U _PyPyMapping_HasKeyString +-U _PyPyMapping_Items +-U _PyPyMapping_Keys +-U _PyPyMapping_Length +-U _PyPyMapping_SetItemString +-U _PyPyMapping_Size +-U _PyPyMapping_Values +-U _PyPyMarshal_ReadObjectFromString +-U _PyPyMarshal_WriteObjectToString +-U _PyPyMem_Calloc +-U _PyPyMem_Free +-U _PyPyMem_Malloc +-U _PyPyMem_RawCalloc +-U _PyPyMem_RawFree +-U _PyPyMem_RawMalloc +-U _PyPyMem_RawRealloc +-U _PyPyMem_Realloc +-U _PyPyMemberDescr_Type +-U _PyPyMember_GetOne +-U _PyPyMember_SetOne +-U _PyPyMemoryView_Check +-U _PyPyMemoryView_CheckExact +-U _PyPyMemoryView_FromBuffer +-U _PyPyMemoryView_FromMemory +-U _PyPyMemoryView_FromObject +-U _PyPyMemoryView_GetContiguous +-U _PyPyMemoryView_Type +-U _PyPyMethodDescr_Check +-U _PyPyMethodDescr_CheckExact +-U _PyPyMethodDescr_Type +-U _PyPyMethod_Check +-U _PyPyMethod_CheckExact +-U _PyPyMethod_Function +-U _PyPyMethod_New +-U _PyPyMethod_Self +-U _PyPyMethod_Type +-U _PyPyModuleDef_Init +-U _PyPyModule_AddFunctions +-U _PyPyModule_AddIntConstant +-U _PyPyModule_AddObject +-U _PyPyModule_AddStringConstant +-U _PyPyModule_Check +-U _PyPyModule_CheckExact +-U _PyPyModule_Create2 +-U _PyPyModule_ExecDef +-U _PyPyModule_GetDef +-U _PyPyModule_GetDict +-U _PyPyModule_GetName +-U _PyPyModule_GetState +-U _PyPyModule_New +-U _PyPyModule_NewObject +-U _PyPyModule_Type +-U _PyPyNumber_Absolute +-U _PyPyNumber_Add +-U _PyPyNumber_And +-U _PyPyNumber_AsSsize_t +-U _PyPyNumber_Check +-U _PyPyNumber_Divide +-U _PyPyNumber_Divmod +-U _PyPyNumber_Float +-U _PyPyNumber_FloorDivide +-U _PyPyNumber_InPlaceAdd +-U _PyPyNumber_InPlaceAnd +-U _PyPyNumber_InPlaceDivide +-U _PyPyNumber_InPlaceFloorDivide +-U _PyPyNumber_InPlaceLshift +-U _PyPyNumber_InPlaceMatrixMultiply +-U _PyPyNumber_InPlaceMultiply +-U _PyPyNumber_InPlaceOr +-U _PyPyNumber_InPlacePower +-U _PyPyNumber_InPlaceRemainder +-U _PyPyNumber_InPlaceRshift +-U _PyPyNumber_InPlaceSubtract +-U _PyPyNumber_InPlaceTrueDivide +-U _PyPyNumber_InPlaceXor +-U _PyPyNumber_Index +-U _PyPyNumber_Invert +-U _PyPyNumber_Long +-U _PyPyNumber_Lshift +-U _PyPyNumber_MatrixMultiply +-U _PyPyNumber_Multiply +-U _PyPyNumber_Negative +-U _PyPyNumber_Or +-U _PyPyNumber_Positive +-U _PyPyNumber_Power +-U _PyPyNumber_Remainder +-U _PyPyNumber_Rshift +-U _PyPyNumber_Subtract +-U _PyPyNumber_ToBase +-U _PyPyNumber_TrueDivide +-U _PyPyNumber_Xor +-U _PyPyOS_AfterFork +-U _PyPyOS_FSPath +-U _PyPyOS_InputHook +-U _PyPyOS_InterruptOccurred +-U _PyPyOS_double_to_string +-U _PyPyOS_getsig +-U _PyPyOS_setsig +-U _PyPyOS_snprintf +-U _PyPyOS_string_to_double +-U _PyPyOS_vsnprintf +-U _PyPyObject_ASCII +-U _PyPyObject_AsCharBuffer +-U _PyPyObject_AsFileDescriptor +-U _PyPyObject_AsReadBuffer +-U _PyPyObject_AsWriteBuffer +-U _PyPyObject_Bytes +-U _PyPyObject_Call +-U _PyPyObject_CallFinalizerFromDealloc +-U _PyPyObject_CallFunction +-U _PyPyObject_CallFunctionObjArgs +-U _PyPyObject_CallMethod +-U _PyPyObject_CallMethodNoArgs +-U _PyPyObject_CallMethodObjArgs +-U _PyPyObject_CallMethodOneArg +-U _PyPyObject_CallNoArgs +-U _PyPyObject_CallObject +-U _PyPyObject_CallOneArg +-U _PyPyObject_Calloc +-U _PyPyObject_CheckReadBuffer +-U _PyPyObject_ClearWeakRefs +-U _PyPyObject_Del +-U _PyPyObject_DelAttr +-U _PyPyObject_DelAttrString +-U _PyPyObject_DelItem +-U _PyPyObject_DelItemString +-U _PyPyObject_Dir +-U _PyPyObject_Format +-U _PyPyObject_Free +-U _PyPyObject_GC_Del +-U _PyPyObject_GenericGetAttr +-U _PyPyObject_GenericGetDict +-U _PyPyObject_GenericSetAttr +-U _PyPyObject_GenericSetDict +-U _PyPyObject_GetAttr +-U _PyPyObject_GetAttrString +-U _PyPyObject_GetBuffer +-U _PyPyObject_GetItem +-U _PyPyObject_GetIter +-U _PyPyObject_HasAttr +-U _PyPyObject_HasAttrString +-U _PyPyObject_Hash +-U _PyPyObject_HashNotImplemented +-U _PyPyObject_Init +-U _PyPyObject_InitVar +-U _PyPyObject_IsInstance +-U _PyPyObject_IsSubclass +-U _PyPyObject_IsTrue +-U _PyPyObject_LengthHint +-U _PyPyObject_Malloc +-U _PyPyObject_Not +-U _PyPyObject_Print +-U _PyPyObject_Realloc +-U _PyPyObject_Repr +-U _PyPyObject_RichCompare +-U _PyPyObject_RichCompareBool +-U _PyPyObject_SelfIter +-U _PyPyObject_SetAttr +-U _PyPyObject_SetAttrString +-U _PyPyObject_SetItem +-U _PyPyObject_Size +-U _PyPyObject_Str +-U _PyPyObject_Type +-U _PyPyObject_Unicode +-U _PyPyObject_Vectorcall +-U _PyPyObject_VectorcallDict +-U _PyPyObject_VectorcallMethod +-U _PyPyProperty_Type +-U _PyPyRange_Type +-U _PyPyReversed_Type +-U _PyPyRun_File +-U _PyPyRun_SimpleString +-U _PyPyRun_String +-U _PyPyRun_StringFlags +-U _PyPySeqIter_New +-U _PyPySequence_Check +-U _PyPySequence_Concat +-U _PyPySequence_Contains +-U _PyPySequence_DelItem +-U _PyPySequence_DelSlice +-U _PyPySequence_Fast +-U _PyPySequence_Fast_GET_ITEM +-U _PyPySequence_Fast_GET_SIZE +-U _PyPySequence_Fast_ITEMS +-U _PyPySequence_GetItem +-U _PyPySequence_GetSlice +-U _PyPySequence_ITEM +-U _PyPySequence_InPlaceConcat +-U _PyPySequence_InPlaceRepeat +-U _PyPySequence_Index +-U _PyPySequence_Length +-U _PyPySequence_List +-U _PyPySequence_Repeat +-U _PyPySequence_SetItem +-U _PyPySequence_SetSlice +-U _PyPySequence_Size +-U _PyPySequence_Tuple +-U _PyPySet_Add +-U _PyPySet_Check +-U _PyPySet_CheckExact +-U _PyPySet_Clear +-U _PyPySet_Contains +-U _PyPySet_Discard +-U _PyPySet_GET_SIZE +-U _PyPySet_New +-U _PyPySet_Pop +-U _PyPySet_Size +-U _PyPySet_Type +-U _PyPySlice_AdjustIndices +-U _PyPySlice_GetIndices +-U _PyPySlice_GetIndicesEx +-U _PyPySlice_New +-U _PyPySlice_Type +-U _PyPySlice_Unpack +-U _PyPyState_AddModule +-U _PyPyState_RemoveModule +-U _PyPyStaticMethod_New +-U _PyPyStaticMethod_Type +-U _PyPyStructSequence_GetItem +-U _PyPyStructSequence_InitType +-U _PyPyStructSequence_InitType2 +-U _PyPyStructSequence_New +-U _PyPyStructSequence_NewType +-U _PyPyStructSequence_SetItem +-U _PyPyStructSequence_UnnamedField +-U _PyPySys_GetObject +-U _PyPySys_SetObject +-U _PyPySys_WriteStderr +-U _PyPySys_WriteStdout +-U _PyPyTZInfo_Check +-U _PyPyTZInfo_CheckExact +-U _PyPyThreadState_Clear +-U _PyPyThreadState_Delete +-U _PyPyThreadState_DeleteCurrent +-U _PyPyThreadState_Get +-U _PyPyThreadState_GetDict +-U _PyPyThreadState_New +-U _PyPyThreadState_SetAsyncExc +-U _PyPyThreadState_Swap +-U _PyPyThread_ReInitTLS +-U _PyPyThread_acquire_lock +-U _PyPyThread_allocate_lock +-U _PyPyThread_create_key +-U _PyPyThread_delete_key +-U _PyPyThread_delete_key_value +-U _PyPyThread_exit_thread +-U _PyPyThread_free_lock +-U _PyPyThread_get_key_value +-U _PyPyThread_get_thread_ident +-U _PyPyThread_init_thread +-U _PyPyThread_release_lock +-U _PyPyThread_set_key_value +-U _PyPyThread_start_new_thread +-U _PyPyTime_Check +-U _PyPyTime_CheckExact +-U _PyPyTraceBack_Check +-U _PyPyTraceBack_Here +-U _PyPyTraceBack_Print +-U _PyPyTraceBack_Type +-U _PyPyTraceMalloc_Track +-U _PyPyTraceMalloc_Untrack +-U _PyPyTuple_GetItem +-U _PyPyTuple_GetSlice +-U _PyPyTuple_New +-U _PyPyTuple_Pack +-U _PyPyTuple_SetItem +-U _PyPyTuple_Size +-U _PyPyTuple_Type +-U _PyPyType_FromModuleAndSpec +-U _PyPyType_FromSpec +-U _PyPyType_FromSpecWithBases +-U _PyPyType_GenericAlloc +-U _PyPyType_GenericNew +-U _PyPyType_GetModule +-U _PyPyType_GetModuleState +-U _PyPyType_GetSlot +-U _PyPyType_IsSubtype +-U _PyPyType_Modified +-U _PyPyType_Ready +-U _PyPyType_Type +-U _PyPyUnicode_Append +-U _PyPyUnicode_AppendAndDel +-U _PyPyUnicode_AsASCIIString +-U _PyPyUnicode_AsEncodedObject +-U _PyPyUnicode_AsEncodedString +-U _PyPyUnicode_AsLatin1String +-U _PyPyUnicode_AsUCS4 +-U _PyPyUnicode_AsUCS4Copy +-U _PyPyUnicode_AsUTF16String +-U _PyPyUnicode_AsUTF32String +-U _PyPyUnicode_AsUTF8 +-U _PyPyUnicode_AsUTF8AndSize +-U _PyPyUnicode_AsUTF8String +-U _PyPyUnicode_AsUnicode +-U _PyPyUnicode_AsUnicodeAndSize +-U _PyPyUnicode_AsUnicodeEscapeString +-U _PyPyUnicode_AsWideChar +-U _PyPyUnicode_AsWideCharString +-U _PyPyUnicode_Check +-U _PyPyUnicode_CheckExact +-U _PyPyUnicode_Compare +-U _PyPyUnicode_CompareWithASCIIString +-U _PyPyUnicode_Concat +-U _PyPyUnicode_Contains +-U _PyPyUnicode_Count +-U _PyPyUnicode_Decode +-U _PyPyUnicode_DecodeASCII +-U _PyPyUnicode_DecodeFSDefault +-U _PyPyUnicode_DecodeFSDefaultAndSize +-U _PyPyUnicode_DecodeLatin1 +-U _PyPyUnicode_DecodeLocale +-U _PyPyUnicode_DecodeLocaleAndSize +-U _PyPyUnicode_DecodeUTF16 +-U _PyPyUnicode_DecodeUTF32 +-U _PyPyUnicode_DecodeUTF8 +-U _PyPyUnicode_EncodeASCII +-U _PyPyUnicode_EncodeDecimal +-U _PyPyUnicode_EncodeFSDefault +-U _PyPyUnicode_EncodeLatin1 +-U _PyPyUnicode_EncodeLocale +-U _PyPyUnicode_EncodeUTF8 +-U _PyPyUnicode_FSConverter +-U _PyPyUnicode_FSDecoder +-U _PyPyUnicode_Find +-U _PyPyUnicode_FindChar +-U _PyPyUnicode_Format +-U _PyPyUnicode_FromEncodedObject +-U _PyPyUnicode_FromFormat +-U _PyPyUnicode_FromFormatV +-U _PyPyUnicode_FromKindAndData +-U _PyPyUnicode_FromObject +-U _PyPyUnicode_FromOrdinal +-U _PyPyUnicode_FromString +-U _PyPyUnicode_FromStringAndSize +-U _PyPyUnicode_FromUnicode +-U _PyPyUnicode_FromWideChar +-U _PyPyUnicode_GetDefaultEncoding +-U _PyPyUnicode_GetLength +-U _PyPyUnicode_GetMax +-U _PyPyUnicode_GetSize +-U _PyPyUnicode_InternFromString +-U _PyPyUnicode_InternInPlace +-U _PyPyUnicode_Join +-U _PyPyUnicode_New +-U _PyPyUnicode_ReadChar +-U _PyPyUnicode_Replace +-U _PyPyUnicode_Resize +-U _PyPyUnicode_Split +-U _PyPyUnicode_Splitlines +-U _PyPyUnicode_Substring +-U _PyPyUnicode_Tailmatch +-U _PyPyUnicode_TransformDecimalToASCII +-U _PyPyUnicode_Type +-U _PyPyUnicode_WriteChar +-U _PyPyVectorcall_Call +-U _PyPyWeakref_Check +-U _PyPyWeakref_CheckProxy +-U _PyPyWeakref_CheckRef +-U _PyPyWeakref_CheckRefExact +-U _PyPyWeakref_GET_OBJECT +-U _PyPyWeakref_GetObject +-U _PyPyWeakref_LockObject +-U _PyPyWeakref_NewProxy +-U _PyPyWeakref_NewRef +-U _PyPyWrapperDescr_Type +-U _PyPy_AddPendingCall +-U _PyPy_AtExit +-U _PyPy_BuildValue +-U _PyPy_BytesWarningFlag +-U _PyPy_CompileStringFlags +-U _PyPy_DebugFlag +-U _PyPy_DecRef +-U _PyPy_DontWriteBytecodeFlag +-U _PyPy_EnterRecursiveCall +-U _PyPy_FatalError +-U _PyPy_FindMethod +-U _PyPy_FrozenFlag +-U _PyPy_GenericAlias +-U _PyPy_GetProgramName +-U _PyPy_GetRecursionLimit +-U _PyPy_GetVersion +-U _PyPy_HashRandomizationFlag +-U _PyPy_IgnoreEnvironmentFlag +-U _PyPy_IncRef +-U _PyPy_InspectFlag +-U _PyPy_InteractiveFlag +-U _PyPy_IsInitialized +-U _PyPy_IsolatedFlag +-U _PyPy_LeaveRecursiveCall +-U _PyPy_MakePendingCalls +-U _PyPy_NoSiteFlag +-U _PyPy_NoUserSiteDirectory +-U _PyPy_OptimizeFlag +-U _PyPy_QuietFlag +-U _PyPy_ReprEnter +-U _PyPy_ReprLeave +-U _PyPy_SetRecursionLimit +-U _PyPy_UNICODE_COPY +-U _PyPy_UNICODE_ISALNUM +-U _PyPy_UNICODE_ISALPHA +-U _PyPy_UNICODE_ISDECIMAL +-U _PyPy_UNICODE_ISDIGIT +-U _PyPy_UNICODE_ISLINEBREAK +-U _PyPy_UNICODE_ISLOWER +-U _PyPy_UNICODE_ISNUMERIC +-U _PyPy_UNICODE_ISSPACE +-U _PyPy_UNICODE_ISTITLE +-U _PyPy_UNICODE_ISUPPER +-U _PyPy_UNICODE_TODECIMAL +-U _PyPy_UNICODE_TODIGIT +-U _PyPy_UNICODE_TOLOWER +-U _PyPy_UNICODE_TONUMERIC +-U _PyPy_UNICODE_TOTITLE +-U _PyPy_UNICODE_TOUPPER +-U _PyPy_UnbufferedStdioFlag +-U _PyPy_VaBuildValue +-U _PyPy_VerboseFlag +-U _PySlice_AdjustIndices +-U _PyState_FindModule +-U _PyThread_tss_alloc +-U _PyThread_tss_create +-U _PyThread_tss_delete +-U _PyThread_tss_free +-U _PyThread_tss_get +-U _PyThread_tss_is_created +-U _PyThread_tss_set +-U _PyType_GetFlags +-U _Py_FileSystemDefaultEncoding +-U __PyArg_BadArgument +-U __PyArg_CheckPositional +-U __PyArg_NoKeywords +-U __PyArg_NoKwnames +-U __PyArg_NoPositional +-U __PyArg_ParseStack +-U __PyArg_ParseStackAndKeywords +-U __PyArg_ParseStackAndKeywords_SizeT +-U __PyArg_ParseStack_SizeT +-U __PyArg_ParseTupleAndKeywordsFast +-U __PyArg_ParseTupleAndKeywordsFast_SizeT +-U __PyArg_UnpackKeywords +-U __PyArg_UnpackStack +-U __PyArg_VaParseTupleAndKeywordsFast +-U __PyArg_VaParseTupleAndKeywordsFast_SizeT +-U __PyExc_ArithmeticError +-U __PyExc_AssertionError +-U __PyExc_AttributeError +-U __PyExc_BaseException +-U __PyExc_BlockingIOError +-U __PyExc_BrokenPipeError +-U __PyExc_BufferError +-U __PyExc_BytesWarning +-U __PyExc_ChildProcessError +-U __PyExc_ConnectionAbortedError +-U __PyExc_ConnectionError +-U __PyExc_ConnectionRefusedError +-U __PyExc_ConnectionResetError +-U __PyExc_DeprecationWarning +-U __PyExc_EOFError +-U __PyExc_Exception +-U __PyExc_FileExistsError +-U __PyExc_FileNotFoundError +-U __PyExc_FloatingPointError +-U __PyExc_FutureWarning +-U __PyExc_GeneratorExit +-U __PyExc_ImportError +-U __PyExc_ImportWarning +-U __PyExc_IndentationError +-U __PyExc_IndexError +-U __PyExc_InterruptedError +-U __PyExc_IsADirectoryError +-U __PyExc_KeyError +-U __PyExc_KeyboardInterrupt +-U __PyExc_LookupError +-U __PyExc_MemoryError +-U __PyExc_ModuleNotFoundError +-U __PyExc_NameError +-U __PyExc_NotADirectoryError +-U __PyExc_NotImplementedError +-U __PyExc_OSError +-U __PyExc_OverflowError +-U __PyExc_PendingDeprecationWarning +-U __PyExc_PermissionError +-U __PyExc_ProcessLookupError +-U __PyExc_RecursionError +-U __PyExc_ReferenceError +-U __PyExc_ResourceWarning +-U __PyExc_RuntimeError +-U __PyExc_RuntimeWarning +-U __PyExc_StopAsyncIteration +-U __PyExc_StopIteration +-U __PyExc_SyntaxError +-U __PyExc_SyntaxWarning +-U __PyExc_SystemError +-U __PyExc_SystemExit +-U __PyExc_TabError +-U __PyExc_TimeoutError +-U __PyExc_TypeError +-U __PyExc_UnboundLocalError +-U __PyExc_UnicodeDecodeError +-U __PyExc_UnicodeEncodeError +-U __PyExc_UnicodeError +-U __PyExc_UnicodeTranslateError +-U __PyExc_UnicodeWarning +-U __PyExc_UserWarning +-U __PyExc_ValueError +-U __PyExc_Warning +-U __PyExc_ZeroDivisionError +-U __PyLong_AsTime_t +-U __PyLong_FromTime_t +-U __PyPyArg_ParseTupleAndKeywords_SizeT +-U __PyPyArg_ParseTuple_SizeT +-U __PyPyArg_Parse_SizeT +-U __PyPyArg_VaParseTupleAndKeywords_SizeT +-U __PyPyArg_VaParse_SizeT +-U __PyPyBytes_Eq +-U __PyPyBytes_Join +-U __PyPyBytes_Resize +-U __PyPyComplex_AsCComplex +-U __PyPyComplex_FromCComplex +-U __PyPyDateTime_FromDateAndTime +-U __PyPyDateTime_FromDateAndTimeAndFold +-U __PyPyDateTime_FromTimestamp +-U __PyPyDateTime_Import +-U __PyPyDate_FromDate +-U __PyPyDate_FromTimestamp +-U __PyPyDelta_FromDelta +-U __PyPyDict_GetItemStringWithError +-U __PyPyDict_HasOnlyStringKeys +-U __PyPyErr_FormatFromCause +-U __PyPyErr_WriteUnraisableMsg +-U __PyPyEval_SliceIndex +-U __PyPyFloat_Unpack4 +-U __PyPyFloat_Unpack8 +-U __PyPyImport_AcquireLock +-U __PyPyImport_ReleaseLock +-U __PyPyList_Extend +-U __PyPyLong_AsByteArrayO +-U __PyPyLong_FromByteArray +-U __PyPyLong_NumBits +-U __PyPyLong_Sign +-U __PyPyNamespace_New +-U __PyPyNone_Type +-U __PyPyNotImplemented_Type +-U __PyPyObject_CallFunction_SizeT +-U __PyPyObject_CallMethod_SizeT +-U __PyPyObject_FastCall +-U __PyPyObject_GC_Malloc +-U __PyPyObject_GC_New +-U __PyPyObject_GC_NewVar +-U __PyPyObject_GetDictPtr +-U __PyPyObject_New +-U __PyPyObject_NewVar +-U __PyPyObject_Vectorcall +-U __PyPyPyGC_AddMemoryPressure +-U __PyPyPy_Free +-U __PyPyPy_Malloc +-U __PyPySet_Next +-U __PyPySet_NextEntry +-U __PyPyThreadState_UncheckedGet +-U __PyPyTimeZone_FromTimeZone +-U __PyPyTime_FromTime +-U __PyPyTime_FromTimeAndFold +-U __PyPyTuple_Resize +-U __PyPyType_Lookup +-U __PyPyUnicode_EQ +-U __PyPyUnicode_EqualToASCIIString +-U __PyPyUnicode_Ready +-U __PyPy_BuildValue_SizeT +-U __PyPy_Dealloc +-U __PyPy_EllipsisObject +-U __PyPy_FalseStruct +-U __PyPy_HashDouble +-U __PyPy_HashPointer +-U __PyPy_IsFinalizing +-U __PyPy_NoneStruct +-U __PyPy_NotImplementedStruct +-U __PyPy_PackageContext +-U __PyPy_RestoreSignals +-U __PyPy_TrueStruct +-U __PyPy_VaBuildValue_SizeT +-U __PyPy_get_PyOS_InputHook +-U __PyPy_get_capsule_type +-U __PyPy_object_dealloc +-U __PyPy_setfilesystemdefaultencoding +-U __PyPy_strhex +-U __PyPy_strhex_bytes +-U __PyPy_subtype_dealloc +-U __PyPy_tuple_dealloc +-U __PyPy_tuple_new +-U __PyTime_AsMicroseconds +-U __PyTime_AsMilliseconds +-U __PyTime_AsNanosecondsObject +-U __PyTime_AsSecondsDouble +-U __PyTime_AsTimeval +-U __PyTime_AsTimevalTime_t +-U __PyTime_AsTimeval_noraise +-U __PyTime_FromMillisecondsObject +-U __PyTime_FromNanoseconds +-U __PyTime_FromNanosecondsObject +-U __PyTime_FromSeconds +-U __PyTime_FromSecondsObject +-U __PyTime_GetMonotonicClock +-U __PyTime_GetMonotonicClockWithInfo +-U __PyTime_GetSystemClock +-U __PyTime_GetSystemClockWithInfo +-U __PyTime_Init +-U __PyTime_ObjectToTime_t +-U __PyTime_ObjectToTimespec +-U __PyTime_ObjectToTimeval +-U __PyTime_gmtime +-U __PyTime_localtime +-U __PyType_Name diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/cmake/nanobind-config.cmake b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/cmake/nanobind-config.cmake new file mode 100644 index 0000000..3ef847e --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/cmake/nanobind-config.cmake @@ -0,0 +1,472 @@ +include_guard(GLOBAL) + +if (NOT TARGET Python::Module) + message(FATAL_ERROR "You must invoke 'find_package(Python COMPONENTS Interpreter Development REQUIRED)' prior to including nanobind.") +endif() + +# Determine the right suffix for ordinary and stable ABI extensions. + +# We always need to know the extension +if(WIN32) + set(NB_SUFFIX_EXT ".pyd") +else() + set(NB_SUFFIX_EXT "${CMAKE_SHARED_MODULE_SUFFIX}") +endif() + +# Check if FindPython/scikit-build-core defined a SOABI/SOSABI variable +if(DEFINED SKBUILD_SOABI) + set(NB_SOABI "${SKBUILD_SOABI}") +elseif(DEFINED Python_SOABI) + set(NB_SOABI "${Python_SOABI}") +endif() + +if(DEFINED SKBUILD_SOSABI) + set(NB_SOSABI "${SKBUILD_SOSABI}") +elseif(DEFINED Python_SOSABI) + set(NB_SOSABI "${Python_SOSABI}") +endif() + +# PyPy sets an invalid SOABI (platform missing), causing older FindPythons to +# report an incorrect value. Only use it if it looks correct (X-X-X form). +if(DEFINED NB_SOABI AND "${NB_SOABI}" MATCHES ".+-.+-.+") + set(NB_SUFFIX ".${NB_SOABI}${NB_SUFFIX_EXT}") +endif() + +if(DEFINED NB_SOSABI) + if(NB_SOSABI STREQUAL "") + set(NB_SUFFIX_S "${NB_SUFFIX_EXT}") + else() + set(NB_SUFFIX_S ".${NB_SOSABI}${NB_SUFFIX_EXT}") + endif() +endif() + +# Extract Python version and extensions (e.g. free-threaded build) +string(REGEX REPLACE "[^-]*-([^-]*)-.*" "\\1" NB_ABI "${NB_SOABI}") + +# If either suffix is missing, call Python to compute it +if(NOT DEFINED NB_SUFFIX OR NOT DEFINED NB_SUFFIX_S) + # Query Python directly to get the right suffix. + execute_process( + COMMAND "${Python_EXECUTABLE}" "-c" + "import sysconfig; print(sysconfig.get_config_var('EXT_SUFFIX'))" + RESULT_VARIABLE NB_SUFFIX_RET + OUTPUT_VARIABLE EXT_SUFFIX + OUTPUT_STRIP_TRAILING_WHITESPACE) + + if(NB_SUFFIX_RET AND NOT NB_SUFFIX_RET EQUAL 0) + message(FATAL_ERROR "nanobind: Python sysconfig query to " + "find 'EXT_SUFFIX' property failed!") + endif() + + if(NOT DEFINED NB_SUFFIX) + set(NB_SUFFIX "${EXT_SUFFIX}") + endif() + + if(NOT DEFINED NB_SUFFIX_S) + get_filename_component(NB_SUFFIX_EXT "${EXT_SUFFIX}" LAST_EXT) + if(WIN32) + set(NB_SUFFIX_S "${NB_SUFFIX_EXT}") + else() + set(NB_SUFFIX_S ".abi3${NB_SUFFIX_EXT}") + endif() + endif() +endif() + +# Stash these for later use +set(NB_SUFFIX ${NB_SUFFIX} CACHE INTERNAL "") +set(NB_SUFFIX_S ${NB_SUFFIX_S} CACHE INTERNAL "") +set(NB_ABI ${NB_ABI} CACHE INTERNAL "") + +get_filename_component(NB_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) +get_filename_component(NB_DIR "${NB_DIR}" PATH) + +set(NB_DIR ${NB_DIR} CACHE INTERNAL "") +set(NB_OPT $,$> CACHE INTERNAL "") +set(NB_OPT_SIZE $,$,$> CACHE INTERNAL "") + +# --------------------------------------------------------------------------- +# Helper function to handle undefined CPython API symbols on macOS +# --------------------------------------------------------------------------- + +function (nanobind_link_options name) + if (APPLE) + if (Python_INTERPRETER_ID STREQUAL "PyPy") + set(NB_LINKER_RESPONSE_FILE darwin-ld-pypy.sym) + else() + set(NB_LINKER_RESPONSE_FILE darwin-ld-cpython.sym) + endif() + target_link_options(${name} PRIVATE "-Wl,@${NB_DIR}/cmake/${NB_LINKER_RESPONSE_FILE}") + endif() +endfunction() + +# --------------------------------------------------------------------------- +# Create shared/static library targets for nanobind's non-templated core +# --------------------------------------------------------------------------- + +function (nanobind_build_library TARGET_NAME) + if (TARGET ${TARGET_NAME}) + return() + endif() + + if (TARGET_NAME MATCHES "-static") + set (TARGET_TYPE STATIC) + else() + set (TARGET_TYPE SHARED) + endif() + + add_library(${TARGET_NAME} ${TARGET_TYPE} + EXCLUDE_FROM_ALL + ${NB_DIR}/include/nanobind/make_iterator.h + ${NB_DIR}/include/nanobind/nanobind.h + ${NB_DIR}/include/nanobind/nb_accessor.h + ${NB_DIR}/include/nanobind/nb_attr.h + ${NB_DIR}/include/nanobind/nb_call.h + ${NB_DIR}/include/nanobind/nb_cast.h + ${NB_DIR}/include/nanobind/nb_class.h + ${NB_DIR}/include/nanobind/nb_defs.h + ${NB_DIR}/include/nanobind/nb_descr.h + ${NB_DIR}/include/nanobind/nb_enums.h + ${NB_DIR}/include/nanobind/nb_error.h + ${NB_DIR}/include/nanobind/nb_func.h + ${NB_DIR}/include/nanobind/nb_lib.h + ${NB_DIR}/include/nanobind/nb_misc.h + ${NB_DIR}/include/nanobind/nb_python.h + ${NB_DIR}/include/nanobind/nb_traits.h + ${NB_DIR}/include/nanobind/nb_tuple.h + ${NB_DIR}/include/nanobind/nb_types.h + ${NB_DIR}/include/nanobind/ndarray.h + ${NB_DIR}/include/nanobind/trampoline.h + ${NB_DIR}/include/nanobind/typing.h + ${NB_DIR}/include/nanobind/operators.h + ${NB_DIR}/include/nanobind/stl/array.h + ${NB_DIR}/include/nanobind/stl/bind_map.h + ${NB_DIR}/include/nanobind/stl/bind_vector.h + ${NB_DIR}/include/nanobind/stl/detail + ${NB_DIR}/include/nanobind/stl/detail/nb_array.h + ${NB_DIR}/include/nanobind/stl/detail/nb_dict.h + ${NB_DIR}/include/nanobind/stl/detail/nb_list.h + ${NB_DIR}/include/nanobind/stl/detail/nb_set.h + ${NB_DIR}/include/nanobind/stl/detail/traits.h + ${NB_DIR}/include/nanobind/stl/filesystem.h + ${NB_DIR}/include/nanobind/stl/function.h + ${NB_DIR}/include/nanobind/stl/list.h + ${NB_DIR}/include/nanobind/stl/map.h + ${NB_DIR}/include/nanobind/stl/optional.h + ${NB_DIR}/include/nanobind/stl/pair.h + ${NB_DIR}/include/nanobind/stl/set.h + ${NB_DIR}/include/nanobind/stl/shared_ptr.h + ${NB_DIR}/include/nanobind/stl/string.h + ${NB_DIR}/include/nanobind/stl/string_view.h + ${NB_DIR}/include/nanobind/stl/tuple.h + ${NB_DIR}/include/nanobind/stl/unique_ptr.h + ${NB_DIR}/include/nanobind/stl/unordered_map.h + ${NB_DIR}/include/nanobind/stl/unordered_set.h + ${NB_DIR}/include/nanobind/stl/variant.h + ${NB_DIR}/include/nanobind/stl/vector.h + ${NB_DIR}/include/nanobind/eigen/dense.h + ${NB_DIR}/include/nanobind/eigen/sparse.h + + ${NB_DIR}/src/buffer.h + ${NB_DIR}/src/hash.h + ${NB_DIR}/src/nb_internals.h + ${NB_DIR}/src/nb_internals.cpp + ${NB_DIR}/src/nb_func.cpp + ${NB_DIR}/src/nb_type.cpp + ${NB_DIR}/src/nb_enum.cpp + ${NB_DIR}/src/nb_ndarray.cpp + ${NB_DIR}/src/nb_static_property.cpp + ${NB_DIR}/src/common.cpp + ${NB_DIR}/src/error.cpp + ${NB_DIR}/src/trampoline.cpp + ${NB_DIR}/src/implicit.cpp + ) + + if (TARGET_TYPE STREQUAL "SHARED") + nanobind_link_options(${TARGET_NAME}) + target_compile_definitions(${TARGET_NAME} PRIVATE -DNB_BUILD) + target_compile_definitions(${TARGET_NAME} PUBLIC -DNB_SHARED) + nanobind_lto(${TARGET_NAME}) + + nanobind_strip(${TARGET_NAME}) + elseif(NOT WIN32 AND NOT APPLE) + target_compile_options(${TARGET_NAME} PUBLIC $<${NB_OPT_SIZE}:-ffunction-sections -fdata-sections>) + target_link_options(${TARGET_NAME} PUBLIC $<${NB_OPT_SIZE}:-Wl,--gc-sections>) + endif() + + set_target_properties(${TARGET_NAME} PROPERTIES + POSITION_INDEPENDENT_CODE ON) + + if (MSVC) + # Do not complain about vsnprintf + target_compile_definitions(${TARGET_NAME} PRIVATE -D_CRT_SECURE_NO_WARNINGS) + else() + # Generally needed to handle type punning in Python code + target_compile_options(${TARGET_NAME} PRIVATE -fno-strict-aliasing) + endif() + + if (WIN32) + if (${TARGET_NAME} MATCHES "-abi3") + target_link_libraries(${TARGET_NAME} PUBLIC Python::SABIModule) + else() + target_link_libraries(${TARGET_NAME} PUBLIC Python::Module) + endif() + endif() + + if (TARGET_NAME MATCHES "-ft") + target_compile_definitions(${TARGET_NAME} PUBLIC NB_FREE_THREADED) + endif() + + # Nanobind performs many assertion checks -- detailed error messages aren't + # included in Release/MinSizeRel modes + target_compile_definitions(${TARGET_NAME} PRIVATE + $<${NB_OPT_SIZE}:NB_COMPACT_ASSERTIONS>) + + # If nanobind was installed without submodule dependencies, then the + # dependencies directory won't exist and we need to find them. + # However, if the directory _does_ exist, then the user is free to choose + # whether nanobind uses them (based on `NB_USE_SUBMODULE_DEPS`), with a + # preference to choose them if `NB_USE_SUBMODULE_DEPS` is not defined + if (NOT IS_DIRECTORY ${NB_DIR}/ext/robin_map/include OR + (DEFINED NB_USE_SUBMODULE_DEPS AND NOT NB_USE_SUBMODULE_DEPS)) + include(CMakeFindDependencyMacro) + find_dependency(tsl-robin-map) + target_link_libraries(${TARGET_NAME} PRIVATE tsl::robin_map) + else() + target_include_directories(${TARGET_NAME} PRIVATE + ${NB_DIR}/ext/robin_map/include) + endif() + + target_include_directories(${TARGET_NAME} PUBLIC + ${Python_INCLUDE_DIRS} + ${NB_DIR}/include) + + target_compile_features(${TARGET_NAME} PUBLIC cxx_std_17) + nanobind_set_visibility(${TARGET_NAME}) +endfunction() + +# --------------------------------------------------------------------------- +# Define a convenience function for creating nanobind targets +# --------------------------------------------------------------------------- + +function(nanobind_opt_size name) + if (MSVC) + target_compile_options(${name} PRIVATE $<${NB_OPT_SIZE}:$<$:/Os>>) + else() + target_compile_options(${name} PRIVATE $<${NB_OPT_SIZE}:$<$:-Os>>) + endif() +endfunction() + +function(nanobind_disable_stack_protector name) + if (NOT MSVC) + # The stack protector affects binding size negatively (+8% on Linux in my + # benchmarks). Protecting from stack smashing in a Python VM seems in any + # case futile, so let's get rid of it by default in optimized modes. + target_compile_options(${name} PRIVATE $<${NB_OPT}:-fno-stack-protector>) + endif() +endfunction() + +function(nanobind_extension name) + set_target_properties(${name} PROPERTIES PREFIX "" SUFFIX "${NB_SUFFIX}") +endfunction() + +function(nanobind_extension_abi3 name) + set_target_properties(${name} PROPERTIES PREFIX "" SUFFIX "${NB_SUFFIX_S}") +endfunction() + +function (nanobind_lto name) + set_target_properties(${name} PROPERTIES + INTERPROCEDURAL_OPTIMIZATION_RELEASE ON + INTERPROCEDURAL_OPTIMIZATION_MINSIZEREL ON) +endfunction() + +function (nanobind_compile_options name) + if (MSVC) + target_compile_options(${name} PRIVATE $<$:/bigobj /MP>) + endif() +endfunction() + +function (nanobind_strip name) + if (APPLE) + target_link_options(${name} PRIVATE $<${NB_OPT}:-Wl,-dead_strip -Wl,-x -Wl,-S>) + elseif (NOT WIN32) + target_link_options(${name} PRIVATE $<${NB_OPT}:-Wl,-s>) + endif() +endfunction() + +function (nanobind_set_visibility name) + set_target_properties(${name} PROPERTIES CXX_VISIBILITY_PRESET hidden) +endfunction() + +function (nanobind_musl_static_libcpp name) + if ("$ENV{AUDITWHEEL_PLAT}" MATCHES "musllinux") + target_link_options(${name} PRIVATE -static-libstdc++ -static-libgcc) + endif() +endfunction() + +function(nanobind_add_module name) + cmake_parse_arguments(PARSE_ARGV 1 ARG + "STABLE_ABI;FREE_THREADED;NB_STATIC;NB_SHARED;PROTECT_STACK;LTO;NOMINSIZE;NOSTRIP;MUSL_DYNAMIC_LIBCPP" + "NB_DOMAIN" "") + + add_library(${name} MODULE ${ARG_UNPARSED_ARGUMENTS}) + + nanobind_compile_options(${name}) + nanobind_link_options(${name}) + set_target_properties(${name} PROPERTIES LINKER_LANGUAGE CXX) + + if (ARG_NB_SHARED AND ARG_NB_STATIC) + message(FATAL_ERROR "NB_SHARED and NB_STATIC cannot be specified at the same time!") + elseif (NOT ARG_NB_SHARED) + set(ARG_NB_STATIC TRUE) + endif() + + # Stable ABI builds require CPython >= 3.12 and Python::SABIModule + if ((Python_VERSION VERSION_LESS 3.12) OR + (NOT Python_INTERPRETER_ID STREQUAL "Python") OR + (NOT TARGET Python::SABIModule)) + set(ARG_STABLE_ABI FALSE) + endif() + + if (NB_ABI MATCHES "t") + set(ARG_STABLE_ABI FALSE) + else(ARG_STABLE_ABI) + set(ARG_FREE_THREADED FALSE) + endif() + + set(libname "nanobind") + if (ARG_NB_STATIC) + set(libname "${libname}-static") + endif() + + if (ARG_STABLE_ABI) + set(libname "${libname}-abi3") + endif() + + if (ARG_FREE_THREADED) + set(libname "${libname}-ft") + endif() + + if (ARG_NB_DOMAIN AND ARG_NB_SHARED) + set(libname ${libname}-${ARG_NB_DOMAIN}) + endif() + + nanobind_build_library(${libname}) + + if (ARG_NB_DOMAIN) + target_compile_definitions(${name} PRIVATE NB_DOMAIN=${ARG_NB_DOMAIN}) + endif() + + if (ARG_STABLE_ABI) + target_compile_definitions(${libname} PUBLIC -DPy_LIMITED_API=0x030C0000) + nanobind_extension_abi3(${name}) + else() + nanobind_extension(${name}) + endif() + + if (ARG_FREE_THREADED) + target_compile_definitions(${name} PRIVATE NB_FREE_THREADED) + endif() + + target_link_libraries(${name} PRIVATE ${libname}) + + if (NOT ARG_PROTECT_STACK) + nanobind_disable_stack_protector(${name}) + endif() + + if (NOT ARG_NOMINSIZE) + nanobind_opt_size(${name}) + endif() + + if (NOT ARG_NOSTRIP) + nanobind_strip(${name}) + endif() + + if (ARG_LTO) + nanobind_lto(${name}) + endif() + + if (ARG_NB_STATIC AND NOT ARG_MUSL_DYNAMIC_LIBCPP) + nanobind_musl_static_libcpp(${name}) + endif() + + nanobind_set_visibility(${name}) +endfunction() + +function (nanobind_add_stub name) + cmake_parse_arguments(PARSE_ARGV 1 ARG "VERBOSE;INCLUDE_PRIVATE;EXCLUDE_DOCSTRINGS;INSTALL_TIME;EXCLUDE_FROM_ALL" "MODULE;OUTPUT;MARKER_FILE;COMPONENT;PATTERN_FILE" "PYTHON_PATH;DEPENDS") + + if (EXISTS ${NB_DIR}/src/stubgen.py) + set(NB_STUBGEN "${NB_DIR}/src/stubgen.py") + elseif (EXISTS ${NB_DIR}/stubgen.py) + set(NB_STUBGEN "${NB_DIR}/stubgen.py") + else() + message(FATAL_ERROR "nanobind_add_stub(): could not locate 'stubgen.py'!") + endif() + + if (NOT ARG_VERBOSE) + list(APPEND NB_STUBGEN_ARGS -q) + else() + set(NB_STUBGEN_EXTRA USES_TERMINAL) + endif() + + if (ARG_INCLUDE_PRIVATE) + list(APPEND NB_STUBGEN_ARGS -P) + endif() + + if (ARG_EXCLUDE_DOCSTRINGS) + list(APPEND NB_STUBGEN_ARGS -D) + endif() + + foreach (TMP IN LISTS ARG_PYTHON_PATH) + list(APPEND NB_STUBGEN_ARGS -i "${TMP}") + endforeach() + + if (ARG_PATTERN_FILE) + list(APPEND NB_STUBGEN_ARGS -p "${ARG_PATTERN_FILE}") + endif() + + if (ARG_MARKER_FILE) + list(APPEND NB_STUBGEN_ARGS -M "${ARG_MARKER_FILE}") + list(APPEND NB_STUBGEN_OUTPUTS "${ARG_MARKER_FILE}") + endif() + + if (NOT ARG_MODULE) + message(FATAL_ERROR "nanobind_add_stub(): a 'MODULE' argument must be specified!") + else() + list(APPEND NB_STUBGEN_ARGS -m "${ARG_MODULE}") + endif() + + if (NOT ARG_OUTPUT) + message(FATAL_ERROR "nanobind_add_stub(): an 'OUTPUT' argument must be specified!") + else() + list(APPEND NB_STUBGEN_ARGS -o "${ARG_OUTPUT}") + list(APPEND NB_STUBGEN_OUTPUTS "${ARG_OUTPUT}") + endif() + + file(TO_CMAKE_PATH ${Python_EXECUTABLE} NB_Python_EXECUTABLE) + + set(NB_STUBGEN_CMD "${NB_Python_EXECUTABLE}" "${NB_STUBGEN}" ${NB_STUBGEN_ARGS}) + + if (NOT ARG_INSTALL_TIME) + add_custom_command( + OUTPUT ${NB_STUBGEN_OUTPUTS} + COMMAND ${NB_STUBGEN_CMD} + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + DEPENDS ${ARG_DEPENDS} "${NB_STUBGEN}" "${ARG_PATTERN_FILE}" + ${NB_STUBGEN_EXTRA} + ) + add_custom_target(${name} ALL DEPENDS ${NB_STUBGEN_OUTPUTS}) + else() + set(NB_STUBGEN_EXTRA "") + if (ARG_COMPONENT) + list(APPEND NB_STUBGEN_EXTRA COMPONENT ${ARG_COMPONENT}) + endif() + if (ARG_EXCLUDE_FROM_ALL) + list(APPEND NB_STUBGEN_EXTRA EXCLUDE_FROM_ALL) + endif() + # \${CMAKE_INSTALL_PREFIX} has same effect as $ + # This is for compatibility with CMake < 3.27. + # For more info: https://github.com/wjakob/nanobind/issues/420#issuecomment-1971353531 + install(CODE "set(CMD \"${NB_STUBGEN_CMD}\")\nexecute_process(\n COMMAND \$\{CMD\}\n WORKING_DIRECTORY \"\${CMAKE_INSTALL_PREFIX}\"\n)" ${NB_STUBGEN_EXTRA}) + endif() +endfunction() diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/ext/robin_map/CMakeLists.txt b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/ext/robin_map/CMakeLists.txt new file mode 100644 index 0000000..fab865a --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/ext/robin_map/CMakeLists.txt @@ -0,0 +1,86 @@ +cmake_minimum_required(VERSION 3.5) + +project(tsl-robin-map VERSION 1.3.0 LANGUAGES CXX) + +include(GNUInstallDirs) + + +add_library(robin_map INTERFACE) +# Use tsl::robin_map as target, more consistent with other libraries conventions (Boost, Qt, ...) +add_library(tsl::robin_map ALIAS robin_map) + +target_include_directories(robin_map INTERFACE + "$" + "$") + +list(APPEND headers "${CMAKE_CURRENT_SOURCE_DIR}/include/tsl/robin_growth_policy.h" + "${CMAKE_CURRENT_SOURCE_DIR}/include/tsl/robin_hash.h" + "${CMAKE_CURRENT_SOURCE_DIR}/include/tsl/robin_map.h" + "${CMAKE_CURRENT_SOURCE_DIR}/include/tsl/robin_set.h") +target_sources(robin_map INTERFACE "$") + +if(MSVC) + target_sources(robin_map INTERFACE + "$" + "$") +endif() + + + + +set(IS_SUBPROJECT TRUE) +if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) + set(IS_SUBPROJECT FALSE) +endif() + +# Installation +if(NOT IS_SUBPROJECT) + include(CMakePackageConfigHelpers) + + ## Install include directory and potential natvis file + install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/tsl" + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") + + if(MSVC) + install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/tsl-robin-map.natvis" + DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}") + endif() + + + + ## Create and install tsl-robin-mapConfig.cmake + configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/tsl-robin-mapConfig.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/tsl-robin-mapConfig.cmake" + INSTALL_DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/cmake/tsl-robin-map") + + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/tsl-robin-mapConfig.cmake" + DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/cmake/tsl-robin-map") + + + ## Create local tsl-robin-mapTargets.cmake + export(TARGETS robin_map NAMESPACE tsl:: FILE "${CMAKE_CURRENT_BINARY_DIR}/tsl-robin-mapTargets.cmake") + + ## Create and install global tsl-robin-mapTargets.cmake + install(TARGETS robin_map + EXPORT tsl-robin-mapTargets) + + install(EXPORT tsl-robin-mapTargets + NAMESPACE tsl:: + DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/cmake/tsl-robin-map") + + + + ## Create and install tsl-robin-mapConfigVersion.cmake + # tsl-robin-map is header-only and does not depend on the architecture. + # Remove CMAKE_SIZEOF_VOID_P from tsl-robin-mapConfigVersion.cmake so that a + # tsl-robin-mapConfig.cmake generated for a 64 bit target can be used for 32 bit + # targets and vice versa. + set(CMAKE_SIZEOF_VOID_P_BACKUP ${CMAKE_SIZEOF_VOID_P}) + unset(CMAKE_SIZEOF_VOID_P) + write_basic_package_version_file("${CMAKE_CURRENT_BINARY_DIR}/tsl-robin-mapConfigVersion.cmake" + COMPATIBILITY SameMajorVersion) + set(CMAKE_SIZEOF_VOID_P ${CMAKE_SIZEOF_VOID_P_BACKUP}) + + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/tsl-robin-mapConfigVersion.cmake" + DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/cmake/tsl-robin-map") +endif() diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/ext/robin_map/LICENSE b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/ext/robin_map/LICENSE new file mode 100644 index 0000000..e9c5ae9 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/ext/robin_map/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Thibaut Goetghebuer-Planchon + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/ext/robin_map/README.md b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/ext/robin_map/README.md new file mode 100644 index 0000000..09fc544 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/ext/robin_map/README.md @@ -0,0 +1,521 @@ +[![CI](https://github.com/Tessil/robin-map/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/Tessil/robin-map/actions/workflows/ci.yml) + +## A C++ implementation of a fast hash map and hash set using robin hood hashing + +The robin-map library is a C++ implementation of a fast hash map and hash set using open-addressing and linear robin hood hashing with backward shift deletion to resolve collisions. + +Four classes are provided: `tsl::robin_map`, `tsl::robin_set`, `tsl::robin_pg_map` and `tsl::robin_pg_set`. The first two are faster and use a power of two growth policy, the last two use a prime growth policy instead and are able to cope better with a poor hash function. Use the prime version if there is a chance of repeating patterns in the lower bits of your hash (e.g. you are storing pointers with an identity hash function). See [GrowthPolicy](#growth-policy) for details. + +A **benchmark** of `tsl::robin_map` against other hash maps may be found [here](https://tessil.github.io/2016/08/29/benchmark-hopscotch-map.html). This page also gives some advices on which hash table structure you should try for your use case (useful if you are a bit lost with the multiple hash tables implementations in the `tsl` namespace). + +### Key features + +- Header-only library, just add the [include](include/) directory to your include path and you are ready to go. If you use CMake, you can also use the `tsl::robin_map` exported target from the [CMakeLists.txt](CMakeLists.txt). +- Fast hash table, check the [benchmark](https://tessil.github.io/2016/08/29/benchmark-hopscotch-map.html) for some numbers. +- Support for move-only and non-default constructible key/value. +- Support for heterogeneous lookups allowing the usage of `find` with a type different than `Key` (e.g. if you have a map that uses `std::unique_ptr` as key, you can use a `foo*` or a `std::uintptr_t` as key parameter to `find` without constructing a `std::unique_ptr`, see [example](#heterogeneous-lookups)). +- No need to reserve any sentinel value from the keys. +- Possibility to store the hash value alongside the stored key-value for faster rehash and lookup if the hash or the key equal functions are expensive to compute. Note that hash may be stored even if not asked explicitly when the library can detect that it will have no impact on the size of the structure in memory due to alignment. See the [StoreHash](https://tessil.github.io/robin-map/classtsl_1_1robin__map.html#details) template parameter for details. +- If the hash is known before a lookup, it is possible to pass it as parameter to speed-up the lookup (see `precalculated_hash` parameter in [API](https://tessil.github.io/robin-map/classtsl_1_1robin__map.html#a35021b11aabb61820236692a54b3a0f8)). +- Support for efficient serialization and deserialization (see [example](#serialization) and the `serialize/deserialize` methods in the [API](https://tessil.github.io/robin-map/classtsl_1_1robin__map.html) for details). +- The library can be used with exceptions disabled (through `-fno-exceptions` option on Clang and GCC, without an `/EH` option on MSVC or simply by defining `TSL_NO_EXCEPTIONS`). `std::terminate` is used in replacement of the `throw` instruction when exceptions are disabled. +- API closely similar to `std::unordered_map` and `std::unordered_set`. + +### Differences compared to `std::unordered_map` + +`tsl::robin_map` tries to have an interface similar to `std::unordered_map`, but some differences exist. +- The **strong exception guarantee only holds** if the following statement is true `std::is_nothrow_swappable::value && std::is_nothrow_move_constructible::value` (where `value_type` is `Key` for `tsl::robin_set` and `std::pair` for `tsl::robin_map`). Otherwise if an exception is thrown during the swap or the move, the structure may end up in a undefined state. Note that per the standard, a `value_type` with a noexcept copy constructor and no move constructor also satisfies this condition and will thus guarantee the strong exception guarantee for the structure (see [API](https://tessil.github.io/robin-map/classtsl_1_1robin__map.html#details) for details). +- The type `Key`, and also `T` in case of map, must be swappable. They must also be copy and/or move constructible. +- Iterator invalidation doesn't behave in the same way, any operation modifying the hash table invalidate them (see [API](https://tessil.github.io/robin-map/classtsl_1_1robin__map.html#details) for details). +- References and pointers to keys or values in the map are invalidated in the same way as iterators to these keys-values. +- For iterators of `tsl::robin_map`, `operator*()` and `operator->()` return a reference and a pointer to `const std::pair` instead of `std::pair` making the value `T` not modifiable. To modify the value you have to call the `value()` method of the iterator to get a mutable reference. Example: +```c++ +tsl::robin_map map = {{1, 1}, {2, 1}, {3, 1}}; +for(auto it = map.begin(); it != map.end(); ++it) { + //it->second = 2; // Illegal + it.value() = 2; // Ok +} +``` +- No support for some buckets related methods (like `bucket_size`, `bucket`, ...). + +These differences also apply between `std::unordered_set` and `tsl::robin_set`. + +Thread-safety guarantees are the same as `std::unordered_map/set` (i.e. possible to have multiple readers with no writer). + +### Growth policy + +The library supports multiple growth policies through the `GrowthPolicy` template parameter. Three policies are provided by the library but you can easily implement your own if needed. + +* **[tsl::rh::power_of_two_growth_policy.](https://tessil.github.io/robin-map/classtsl_1_1rh_1_1power__of__two__growth__policy.html)** Default policy used by `tsl::robin_map/set`. This policy keeps the size of the bucket array of the hash table to a power of two. This constraint allows the policy to avoid the usage of the slow modulo operation to map a hash to a bucket, instead of hash % 2n, it uses hash & (2n - 1) (see [fast modulo](https://en.wikipedia.org/wiki/Modulo_operation#Performance_issues)). Fast but this may cause a lot of collisions with a poor hash function as the modulo with a power of two only masks the most significant bits in the end. +* **[tsl::rh::prime_growth_policy.](https://tessil.github.io/robin-map/classtsl_1_1rh_1_1prime__growth__policy.html)** Default policy used by `tsl::robin_pg_map/set`. The policy keeps the size of the bucket array of the hash table to a prime number. When mapping a hash to a bucket, using a prime number as modulo will result in a better distribution of the hash across the buckets even with a poor hash function. To allow the compiler to optimize the modulo operation, the policy use a lookup table with constant primes modulos (see [API](https://tessil.github.io/robin-map/classtsl_1_1rh_1_1prime__growth__policy.html#details) for details). Slower than `tsl::rh::power_of_two_growth_policy` but more secure. +* **[tsl::rh::mod_growth_policy.](https://tessil.github.io/robin-map/classtsl_1_1rh_1_1mod__growth__policy.html)** The policy grows the map by a customizable growth factor passed in parameter. It then just use the modulo operator to map a hash to a bucket. Slower but more flexible. + + +To implement your own policy, you have to implement the following interface. + +```c++ +struct custom_policy { + // Called on hash table construction and rehash, min_bucket_count_in_out is the minimum buckets + // that the hash table needs. The policy can change it to a higher number of buckets if needed + // and the hash table will use this value as bucket count. If 0 bucket is asked, then the value + // must stay at 0. + explicit custom_policy(std::size_t& min_bucket_count_in_out); + + // Return the bucket [0, bucket_count()) to which the hash belongs. + // If bucket_count() is 0, it must always return 0. + std::size_t bucket_for_hash(std::size_t hash) const noexcept; + + // Return the number of buckets that should be used on next growth + std::size_t next_bucket_count() const; + + // Maximum number of buckets supported by the policy + std::size_t max_bucket_count() const; + + // Reset the growth policy as if the policy was created with a bucket count of 0. + // After a clear, the policy must always return 0 when bucket_for_hash() is called. + void clear() noexcept; +} +``` + +### Installation + +To use robin-map, just add the [include](include/) directory to your include path. It is a **header-only** library. + +If you use CMake, you can also use the `tsl::robin_map` exported target from the [CMakeLists.txt](CMakeLists.txt) with `target_link_libraries`. +```cmake +# Example where the robin-map project is stored in a third-party directory +add_subdirectory(third-party/robin-map) +target_link_libraries(your_target PRIVATE tsl::robin_map) +``` + +If the project has been installed through `make install`, you can also use `find_package(tsl-robin-map REQUIRED)` instead of `add_subdirectory`. + +The library is available in [vcpkg](https://github.com/Microsoft/vcpkg/tree/master/ports/robin-map) and [conan](https://conan.io/center/tsl-robin-map). It's also present in [Debian](https://packages.debian.org/buster/robin-map-dev), [Ubuntu](https://packages.ubuntu.com/disco/robin-map-dev) and [Fedora](https://apps.fedoraproject.org/packages/robin-map-devel) package repositories. + +The code should work with any C++17 standard-compliant compiler. + +To run the tests you will need the Boost Test library and CMake. + +```bash +git clone https://github.com/Tessil/robin-map.git +cd robin-map/tests +mkdir build +cd build +cmake .. +cmake --build . +./tsl_robin_map_tests +``` + +### Usage + +The API can be found [here](https://tessil.github.io/robin-map/). + +All methods are not documented yet, but they replicate the behavior of the ones in `std::unordered_map` and `std::unordered_set`, except if specified otherwise. + + +### Example + +```c++ +#include +#include +#include +#include +#include + +int main() { + tsl::robin_map map = {{"a", 1}, {"b", 2}}; + map["c"] = 3; + map["d"] = 4; + + map.insert({"e", 5}); + map.erase("b"); + + for(auto it = map.begin(); it != map.end(); ++it) { + //it->second += 2; // Not valid. + it.value() += 2; + } + + // {d, 6} {a, 3} {e, 7} {c, 5} + for(const auto& key_value : map) { + std::cout << "{" << key_value.first << ", " << key_value.second << "}" << std::endl; + } + + + if(map.find("a") != map.end()) { + std::cout << "Found \"a\"." << std::endl; + } + + const std::size_t precalculated_hash = std::hash()("a"); + // If we already know the hash beforehand, we can pass it in parameter to speed-up lookups. + if(map.find("a", precalculated_hash) != map.end()) { + std::cout << "Found \"a\" with hash " << precalculated_hash << "." << std::endl; + } + + + /* + * Calculating the hash and comparing two std::string may be slow. + * We can store the hash of each std::string in the hash map to make + * the inserts and lookups faster by setting StoreHash to true. + */ + tsl::robin_map, + std::equal_to, + std::allocator>, + true> map2; + + map2["a"] = 1; + map2["b"] = 2; + + // {a, 1} {b, 2} + for(const auto& key_value : map2) { + std::cout << "{" << key_value.first << ", " << key_value.second << "}" << std::endl; + } + + + + + tsl::robin_set set; + set.insert({1, 9, 0}); + set.insert({2, -1, 9}); + + // {0} {1} {2} {9} {-1} + for(const auto& key : set) { + std::cout << "{" << key << "}" << std::endl; + } +} +``` + +#### Heterogeneous lookups + +Heterogeneous overloads allow the usage of other types than `Key` for lookup and erase operations as long as the used types are hashable and comparable to `Key`. + +To activate the heterogeneous overloads in `tsl::robin_map/set`, the qualified-id `KeyEqual::is_transparent` must be valid. It works the same way as for [`std::map::find`](http://en.cppreference.com/w/cpp/container/map/find). You can either use [`std::equal_to<>`](http://en.cppreference.com/w/cpp/utility/functional/equal_to_void) or define your own function object. + +Both `KeyEqual` and `Hash` will need to be able to deal with the different types. + +```c++ +#include +#include +#include +#include + + +struct employee { + employee(int id, std::string name) : m_id(id), m_name(std::move(name)) { + } + + // Either we include the comparators in the class and we use `std::equal_to<>`... + friend bool operator==(const employee& empl, int empl_id) { + return empl.m_id == empl_id; + } + + friend bool operator==(int empl_id, const employee& empl) { + return empl_id == empl.m_id; + } + + friend bool operator==(const employee& empl1, const employee& empl2) { + return empl1.m_id == empl2.m_id; + } + + + int m_id; + std::string m_name; +}; + +// ... or we implement a separate class to compare employees. +struct equal_employee { + using is_transparent = void; + + bool operator()(const employee& empl, int empl_id) const { + return empl.m_id == empl_id; + } + + bool operator()(int empl_id, const employee& empl) const { + return empl_id == empl.m_id; + } + + bool operator()(const employee& empl1, const employee& empl2) const { + return empl1.m_id == empl2.m_id; + } +}; + +struct hash_employee { + std::size_t operator()(const employee& empl) const { + return std::hash()(empl.m_id); + } + + std::size_t operator()(int id) const { + return std::hash()(id); + } +}; + + +int main() { + // Use std::equal_to<> which will automatically deduce and forward the parameters + tsl::robin_map> map; + map.insert({employee(1, "John Doe"), 2001}); + map.insert({employee(2, "Jane Doe"), 2002}); + map.insert({employee(3, "John Smith"), 2003}); + + // John Smith 2003 + auto it = map.find(3); + if(it != map.end()) { + std::cout << it->first.m_name << " " << it->second << std::endl; + } + + map.erase(1); + + + + // Use a custom KeyEqual which has an is_transparent member type + tsl::robin_map map2; + map2.insert({employee(4, "Johnny Doe"), 2004}); + + // 2004 + std::cout << map2.at(4) << std::endl; +} +``` + +#### Serialization + +The library provides an efficient way to serialize and deserialize a map or a set so that it can be saved to a file or send through the network. +To do so, it requires the user to provide a function object for both serialization and deserialization. + +```c++ +struct serializer { + // Must support the following types for U: std::int16_t, std::uint32_t, + // std::uint64_t, float and std::pair if a map is used or Key for + // a set. + template + void operator()(const U& value); +}; +``` + +```c++ +struct deserializer { + // Must support the following types for U: std::int16_t, std::uint32_t, + // std::uint64_t, float and std::pair if a map is used or Key for + // a set. + template + U operator()(); +}; +``` + +Note that the implementation leaves binary compatibility (endianness, float binary representation, size of int, ...) of the types it serializes/deserializes in the hands of the provided function objects if compatibility is required. + +More details regarding the `serialize` and `deserialize` methods can be found in the [API](https://tessil.github.io/robin-map/classtsl_1_1robin__map.html). + +```c++ +#include +#include +#include +#include +#include + + +class serializer { +public: + serializer(const char* file_name) { + m_ostream.exceptions(m_ostream.badbit | m_ostream.failbit); + m_ostream.open(file_name, std::ios::binary); + } + + template::value>::type* = nullptr> + void operator()(const T& value) { + m_ostream.write(reinterpret_cast(&value), sizeof(T)); + } + + void operator()(const std::pair& value) { + (*this)(value.first); + (*this)(value.second); + } + +private: + std::ofstream m_ostream; +}; + +class deserializer { +public: + deserializer(const char* file_name) { + m_istream.exceptions(m_istream.badbit | m_istream.failbit | m_istream.eofbit); + m_istream.open(file_name, std::ios::binary); + } + + template + T operator()() { + T value; + deserialize(value); + + return value; + } + +private: + template::value>::type* = nullptr> + void deserialize(T& value) { + m_istream.read(reinterpret_cast(&value), sizeof(T)); + } + + void deserialize(std::pair& value) { + deserialize(value.first); + deserialize(value.second); + } + +private: + std::ifstream m_istream; +}; + + +int main() { + const tsl::robin_map map = {{1, -1}, {2, -2}, {3, -3}, {4, -4}}; + + + const char* file_name = "robin_map.data"; + { + serializer serial(file_name); + map.serialize(serial); + } + + { + deserializer dserial(file_name); + auto map_deserialized = tsl::robin_map::deserialize(dserial); + + assert(map == map_deserialized); + } + + { + deserializer dserial(file_name); + + /** + * If the serialized and deserialized map are hash compatibles (see conditions in API), + * setting the argument to true speed-up the deserialization process as we don't have + * to recalculate the hash of each key. We also know how much space each bucket needs. + */ + const bool hash_compatible = true; + auto map_deserialized = + tsl::robin_map::deserialize(dserial, hash_compatible); + + assert(map == map_deserialized); + } +} +``` + +##### Serialization with Boost Serialization and compression with zlib + +It is possible to use a serialization library to avoid the boilerplate. + +The following example uses Boost Serialization with the Boost zlib compression stream to reduce the size of the resulting serialized file. The example requires C++20 due to the usage of the template parameter list syntax in lambdas, but it can be adapted to less recent versions. + +```c++ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace boost { namespace serialization { + template + void serialize(Archive & ar, tsl::robin_map& map, const unsigned int version) { + split_free(ar, map, version); + } + + template + void save(Archive & ar, const tsl::robin_map& map, const unsigned int /*version*/) { + auto serializer = [&ar](const auto& v) { ar & v; }; + map.serialize(serializer); + } + + template + void load(Archive & ar, tsl::robin_map& map, const unsigned int /*version*/) { + auto deserializer = [&ar]() { U u; ar & u; return u; }; + map = tsl::robin_map::deserialize(deserializer); + } +}} + + +int main() { + tsl::robin_map map = {{1, -1}, {2, -2}, {3, -3}, {4, -4}}; + + + const char* file_name = "robin_map.data"; + { + std::ofstream ofs; + ofs.exceptions(ofs.badbit | ofs.failbit); + ofs.open(file_name, std::ios::binary); + + boost::iostreams::filtering_ostream fo; + fo.push(boost::iostreams::zlib_compressor()); + fo.push(ofs); + + boost::archive::binary_oarchive oa(fo); + + oa << map; + } + + { + std::ifstream ifs; + ifs.exceptions(ifs.badbit | ifs.failbit | ifs.eofbit); + ifs.open(file_name, std::ios::binary); + + boost::iostreams::filtering_istream fi; + fi.push(boost::iostreams::zlib_decompressor()); + fi.push(ifs); + + boost::archive::binary_iarchive ia(fi); + + tsl::robin_map map_deserialized; + ia >> map_deserialized; + + assert(map == map_deserialized); + } +} +``` + +#### Performance pitfalls + +Two potential performance pitfalls involving `tsl::robin_map` and +`tsl::robin_set` are noteworthy: + +1. *Bad hashes*. Hash functions that produce many collisions can lead to the + following surprising behavior: when the number of collisions exceeds a + certain threshold, the hash table will automatically expand to fix the + problem. However, in degenerate cases, this expansion might have _no effect_ + on the collision count, causing a failure mode where a linear sequence of + insertion leads to exponential storage growth. + + This case has mainly been observed when using the default power-of-two + growth strategy with the default STL `std::hash` for arithmetic types + `T`, which is often an identity! See issue + [#39](https://github.com/Tessil/robin-map/issues/39) for an example. The + solution is simple: use a better hash function and/or `tsl::robin_pg_set` / + `tsl::robin_pg_map`. + +2. *Element erasure and low load factors*. `tsl::robin_map` and + `tsl::robin_set` mirror the STL map/set API, which exposes an `iterator + erase(iterator)` method that removes an element at a certain position, + returning a valid iterator that points to the next element. + + Constructing this new iterator object requires walking to the next nonempty + bucket in the table, which can be a expensive operation when the hash table + has a low *load factor* (i.e., when `capacity()` is much larger then + `size()`). + + The `erase()` method furthermore never shrinks & re-hashes the table as + this is not permitted by the specification of this function. A linear + sequence of random removals without intermediate insertions can then lead to + a degenerate case with quadratic runtime cost. + + In such cases, an iterator return value is often not even needed, so the + cost is entirely unnecessary. Both `tsl::robin_set` and `tsl::robin_map` + therefore provide an alternative erasure method `void erase_fast(iterator)` + that does not return an iterator to avoid having to find the next element. + +### License + +The code is licensed under the MIT license, see the [LICENSE file](LICENSE) for details. diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/ext/robin_map/cmake/tsl-robin-mapConfig.cmake.in b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/ext/robin_map/cmake/tsl-robin-mapConfig.cmake.in new file mode 100644 index 0000000..d2ce233 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/ext/robin_map/cmake/tsl-robin-mapConfig.cmake.in @@ -0,0 +1,9 @@ +# This module sets the following variables: +# * tsl-robin-map_FOUND - true if tsl-robin-map found on the system +# * tsl-robin-map_INCLUDE_DIRS - the directory containing tsl-robin-map headers +@PACKAGE_INIT@ + +if(NOT TARGET tsl::robin_map) + include("${CMAKE_CURRENT_LIST_DIR}/tsl-robin-mapTargets.cmake") + get_target_property(tsl-robin-map_INCLUDE_DIRS tsl::robin_map INTERFACE_INCLUDE_DIRECTORIES) +endif() diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/ext/robin_map/include/tsl/robin_growth_policy.h b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/ext/robin_map/include/tsl/robin_growth_policy.h new file mode 100644 index 0000000..2dc9c40 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/ext/robin_map/include/tsl/robin_growth_policy.h @@ -0,0 +1,415 @@ +/** + * MIT License + * + * Copyright (c) 2017 Thibaut Goetghebuer-Planchon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef TSL_ROBIN_GROWTH_POLICY_H +#define TSL_ROBIN_GROWTH_POLICY_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// A change of the major version indicates an API and/or ABI break (change of +// in-memory layout of the data structure) +#define TSL_RH_VERSION_MAJOR 1 +// A change of the minor version indicates the addition of a feature without +// impact on the API/ABI +#define TSL_RH_VERSION_MINOR 3 +// A change of the patch version indicates a bugfix without additional +// functionality +#define TSL_RH_VERSION_PATCH 0 + +#ifdef TSL_DEBUG +#define tsl_rh_assert(expr) assert(expr) +#else +#define tsl_rh_assert(expr) (static_cast(0)) +#endif + +/** + * If exceptions are enabled, throw the exception passed in parameter, otherwise + * call std::terminate. + */ +#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \ + (defined(_MSC_VER) && defined(_CPPUNWIND))) && \ + !defined(TSL_NO_EXCEPTIONS) +#define TSL_RH_THROW_OR_TERMINATE(ex, msg) throw ex(msg) +#else +#define TSL_RH_NO_EXCEPTIONS +#ifdef TSL_DEBUG +#include +#define TSL_RH_THROW_OR_TERMINATE(ex, msg) \ + do { \ + std::cerr << msg << std::endl; \ + std::terminate(); \ + } while (0) +#else +#define TSL_RH_THROW_OR_TERMINATE(ex, msg) std::terminate() +#endif +#endif + +#if defined(__GNUC__) || defined(__clang__) +#define TSL_RH_LIKELY(exp) (__builtin_expect(!!(exp), true)) +#else +#define TSL_RH_LIKELY(exp) (exp) +#endif + +#define TSL_RH_UNUSED(x) static_cast(x) + +namespace tsl { +namespace rh { + +/** + * Grow the hash table by a factor of GrowthFactor keeping the bucket count to a + * power of two. It allows the table to use a mask operation instead of a modulo + * operation to map a hash to a bucket. + * + * GrowthFactor must be a power of two >= 2. + */ +template +class power_of_two_growth_policy { + public: + /** + * Called on the hash table creation and on rehash. The number of buckets for + * the table is passed in parameter. This number is a minimum, the policy may + * update this value with a higher value if needed (but not lower). + * + * If 0 is given, min_bucket_count_in_out must still be 0 after the policy + * creation and bucket_for_hash must always return 0 in this case. + */ + explicit power_of_two_growth_policy(std::size_t& min_bucket_count_in_out) { + if (min_bucket_count_in_out > max_bucket_count()) { + TSL_RH_THROW_OR_TERMINATE(std::length_error, + "The hash table exceeds its maximum size."); + } + + if (min_bucket_count_in_out > 0) { + min_bucket_count_in_out = + round_up_to_power_of_two(min_bucket_count_in_out); + m_mask = min_bucket_count_in_out - 1; + } else { + m_mask = 0; + } + } + + /** + * Return the bucket [0, bucket_count()) to which the hash belongs. + * If bucket_count() is 0, it must always return 0. + */ + std::size_t bucket_for_hash(std::size_t hash) const noexcept { + return hash & m_mask; + } + + /** + * Return the number of buckets that should be used on next growth. + */ + std::size_t next_bucket_count() const { + if ((m_mask + 1) > max_bucket_count() / GrowthFactor) { + TSL_RH_THROW_OR_TERMINATE(std::length_error, + "The hash table exceeds its maximum size."); + } + + return (m_mask + 1) * GrowthFactor; + } + + /** + * Return the maximum number of buckets supported by the policy. + */ + std::size_t max_bucket_count() const { + // Largest power of two. + return (std::numeric_limits::max() / 2) + 1; + } + + /** + * Reset the growth policy as if it was created with a bucket count of 0. + * After a clear, the policy must always return 0 when bucket_for_hash is + * called. + */ + void clear() noexcept { m_mask = 0; } + + private: + static std::size_t round_up_to_power_of_two(std::size_t value) { + if (is_power_of_two(value)) { + return value; + } + + if (value == 0) { + return 1; + } + + --value; + for (std::size_t i = 1; i < sizeof(std::size_t) * CHAR_BIT; i *= 2) { + value |= value >> i; + } + + return value + 1; + } + + static constexpr bool is_power_of_two(std::size_t value) { + return value != 0 && (value & (value - 1)) == 0; + } + + protected: + static_assert(is_power_of_two(GrowthFactor) && GrowthFactor >= 2, + "GrowthFactor must be a power of two >= 2."); + + std::size_t m_mask; +}; + +/** + * Grow the hash table by GrowthFactor::num / GrowthFactor::den and use a modulo + * to map a hash to a bucket. Slower but it can be useful if you want a slower + * growth. + */ +template > +class mod_growth_policy { + public: + explicit mod_growth_policy(std::size_t& min_bucket_count_in_out) { + if (min_bucket_count_in_out > max_bucket_count()) { + TSL_RH_THROW_OR_TERMINATE(std::length_error, + "The hash table exceeds its maximum size."); + } + + if (min_bucket_count_in_out > 0) { + m_mod = min_bucket_count_in_out; + } else { + m_mod = 1; + } + } + + std::size_t bucket_for_hash(std::size_t hash) const noexcept { + return hash % m_mod; + } + + std::size_t next_bucket_count() const { + if (m_mod == max_bucket_count()) { + TSL_RH_THROW_OR_TERMINATE(std::length_error, + "The hash table exceeds its maximum size."); + } + + const double next_bucket_count = + std::ceil(double(m_mod) * REHASH_SIZE_MULTIPLICATION_FACTOR); + if (!std::isnormal(next_bucket_count)) { + TSL_RH_THROW_OR_TERMINATE(std::length_error, + "The hash table exceeds its maximum size."); + } + + if (next_bucket_count > double(max_bucket_count())) { + return max_bucket_count(); + } else { + return std::size_t(next_bucket_count); + } + } + + std::size_t max_bucket_count() const { return MAX_BUCKET_COUNT; } + + void clear() noexcept { m_mod = 1; } + + private: + static constexpr double REHASH_SIZE_MULTIPLICATION_FACTOR = + 1.0 * GrowthFactor::num / GrowthFactor::den; + static const std::size_t MAX_BUCKET_COUNT = + std::size_t(double(std::numeric_limits::max() / + REHASH_SIZE_MULTIPLICATION_FACTOR)); + + static_assert(REHASH_SIZE_MULTIPLICATION_FACTOR >= 1.1, + "Growth factor should be >= 1.1."); + + std::size_t m_mod; +}; + +namespace detail { + +#if SIZE_MAX >= ULLONG_MAX +#define TSL_RH_NB_PRIMES 51 +#elif SIZE_MAX >= ULONG_MAX +#define TSL_RH_NB_PRIMES 40 +#else +#define TSL_RH_NB_PRIMES 23 +#endif + +inline constexpr std::array PRIMES = {{ + 1u, + 5u, + 17u, + 29u, + 37u, + 53u, + 67u, + 79u, + 97u, + 131u, + 193u, + 257u, + 389u, + 521u, + 769u, + 1031u, + 1543u, + 2053u, + 3079u, + 6151u, + 12289u, + 24593u, + 49157u, +#if SIZE_MAX >= ULONG_MAX + 98317ul, + 196613ul, + 393241ul, + 786433ul, + 1572869ul, + 3145739ul, + 6291469ul, + 12582917ul, + 25165843ul, + 50331653ul, + 100663319ul, + 201326611ul, + 402653189ul, + 805306457ul, + 1610612741ul, + 3221225473ul, + 4294967291ul, +#endif +#if SIZE_MAX >= ULLONG_MAX + 6442450939ull, + 12884901893ull, + 25769803751ull, + 51539607551ull, + 103079215111ull, + 206158430209ull, + 412316860441ull, + 824633720831ull, + 1649267441651ull, + 3298534883309ull, + 6597069766657ull, +#endif +}}; + +template +static constexpr std::size_t mod(std::size_t hash) { + return hash % PRIMES[IPrime]; +} + +// MOD_PRIME[iprime](hash) returns hash % PRIMES[iprime]. This table allows for +// faster modulo as the compiler can optimize the modulo code better with a +// constant known at the compilation. +inline constexpr std::array + MOD_PRIME = {{ + &mod<0>, &mod<1>, &mod<2>, &mod<3>, &mod<4>, &mod<5>, + &mod<6>, &mod<7>, &mod<8>, &mod<9>, &mod<10>, &mod<11>, + &mod<12>, &mod<13>, &mod<14>, &mod<15>, &mod<16>, &mod<17>, + &mod<18>, &mod<19>, &mod<20>, &mod<21>, &mod<22>, +#if SIZE_MAX >= ULONG_MAX + &mod<23>, &mod<24>, &mod<25>, &mod<26>, &mod<27>, &mod<28>, + &mod<29>, &mod<30>, &mod<31>, &mod<32>, &mod<33>, &mod<34>, + &mod<35>, &mod<36>, &mod<37>, &mod<38>, &mod<39>, +#endif +#if SIZE_MAX >= ULLONG_MAX + &mod<40>, &mod<41>, &mod<42>, &mod<43>, &mod<44>, &mod<45>, + &mod<46>, &mod<47>, &mod<48>, &mod<49>, &mod<50>, +#endif + }}; + +} // namespace detail + +/** + * Grow the hash table by using prime numbers as bucket count. Slower than + * tsl::rh::power_of_two_growth_policy in general but will probably distribute + * the values around better in the buckets with a poor hash function. + * + * To allow the compiler to optimize the modulo operation, a lookup table is + * used with constant primes numbers. + * + * With a switch the code would look like: + * \code + * switch(iprime) { // iprime is the current prime of the hash table + * case 0: hash % 5ul; + * break; + * case 1: hash % 17ul; + * break; + * case 2: hash % 29ul; + * break; + * ... + * } + * \endcode + * + * Due to the constant variable in the modulo the compiler is able to optimize + * the operation by a series of multiplications, substractions and shifts. + * + * The 'hash % 5' could become something like 'hash - (hash * 0xCCCCCCCD) >> 34) + * * 5' in a 64 bits environment. + */ +class prime_growth_policy { + public: + explicit prime_growth_policy(std::size_t& min_bucket_count_in_out) { + auto it_prime = std::lower_bound( + detail::PRIMES.begin(), detail::PRIMES.end(), min_bucket_count_in_out); + if (it_prime == detail::PRIMES.end()) { + TSL_RH_THROW_OR_TERMINATE(std::length_error, + "The hash table exceeds its maximum size."); + } + + m_iprime = static_cast( + std::distance(detail::PRIMES.begin(), it_prime)); + if (min_bucket_count_in_out > 0) { + min_bucket_count_in_out = *it_prime; + } else { + min_bucket_count_in_out = 0; + } + } + + std::size_t bucket_for_hash(std::size_t hash) const noexcept { + return detail::MOD_PRIME[m_iprime](hash); + } + + std::size_t next_bucket_count() const { + if (m_iprime + 1 >= detail::PRIMES.size()) { + TSL_RH_THROW_OR_TERMINATE(std::length_error, + "The hash table exceeds its maximum size."); + } + + return detail::PRIMES[m_iprime + 1]; + } + + std::size_t max_bucket_count() const { return detail::PRIMES.back(); } + + void clear() noexcept { m_iprime = 0; } + + private: + unsigned int m_iprime; + + static_assert(std::numeric_limits::max() >= + detail::PRIMES.size(), + "The type of m_iprime is not big enough."); +}; + +} // namespace rh +} // namespace tsl + +#endif diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/ext/robin_map/include/tsl/robin_hash.h b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/ext/robin_map/include/tsl/robin_hash.h new file mode 100644 index 0000000..78043e4 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/ext/robin_map/include/tsl/robin_hash.h @@ -0,0 +1,1586 @@ +/** + * MIT License + * + * Copyright (c) 2017 Thibaut Goetghebuer-Planchon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef TSL_ROBIN_HASH_H +#define TSL_ROBIN_HASH_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "robin_growth_policy.h" + +namespace tsl { + +namespace detail_robin_hash { + +template +struct make_void { + using type = void; +}; + +template +struct has_is_transparent : std::false_type {}; + +template +struct has_is_transparent::type> + : std::true_type {}; + +template +struct is_power_of_two_policy : std::false_type {}; + +template +struct is_power_of_two_policy> + : std::true_type {}; + +template +static T numeric_cast(U value, + const char* error_message = "numeric_cast() failed.") { + T ret = static_cast(value); + if (static_cast(ret) != value) { + TSL_RH_THROW_OR_TERMINATE(std::runtime_error, error_message); + } + + const bool is_same_signedness = + (std::is_unsigned::value && std::is_unsigned::value) || + (std::is_signed::value && std::is_signed::value); + if (!is_same_signedness && (ret < T{}) != (value < U{})) { + TSL_RH_THROW_OR_TERMINATE(std::runtime_error, error_message); + } + + TSL_RH_UNUSED(error_message); + + return ret; +} + +template +static T deserialize_value(Deserializer& deserializer) { + // MSVC < 2017 is not conformant, circumvent the problem by removing the + // template keyword +#if defined(_MSC_VER) && _MSC_VER < 1910 + return deserializer.Deserializer::operator()(); +#else + return deserializer.Deserializer::template operator()(); +#endif +} + +/** + * Fixed size type used to represent size_type values on serialization. Need to + * be big enough to represent a std::size_t on 32 and 64 bits platforms, and + * must be the same size on both platforms. + */ +using slz_size_type = std::uint64_t; +static_assert(std::numeric_limits::max() >= + std::numeric_limits::max(), + "slz_size_type must be >= std::size_t"); + +using truncated_hash_type = std::uint32_t; + +/** + * Helper class that stores a truncated hash if StoreHash is true and nothing + * otherwise. + */ +template +class bucket_entry_hash { + public: + bool bucket_hash_equal(std::size_t /*hash*/) const noexcept { return true; } + + truncated_hash_type truncated_hash() const noexcept { return 0; } + + protected: + void set_hash(truncated_hash_type /*hash*/) noexcept {} +}; + +template <> +class bucket_entry_hash { + public: + bool bucket_hash_equal(std::size_t hash) const noexcept { + return m_hash == truncated_hash_type(hash); + } + + truncated_hash_type truncated_hash() const noexcept { return m_hash; } + + protected: + void set_hash(truncated_hash_type hash) noexcept { + m_hash = truncated_hash_type(hash); + } + + private: + truncated_hash_type m_hash; +}; + +/** + * Each bucket entry has: + * - A value of type `ValueType`. + * - An integer to store how far the value of the bucket, if any, is from its + * ideal bucket (ex: if the current bucket 5 has the value 'foo' and + * `hash('foo') % nb_buckets` == 3, `dist_from_ideal_bucket()` will return 2 as + * the current value of the bucket is two buckets away from its ideal bucket) If + * there is no value in the bucket (i.e. `empty()` is true) + * `dist_from_ideal_bucket()` will be < 0. + * - A marker which tells us if the bucket is the last bucket of the bucket + * array (useful for the iterator of the hash table). + * - If `StoreHash` is true, 32 bits of the hash of the value, if any, are also + * stored in the bucket. If the size of the hash is more than 32 bits, it is + * truncated. We don't store the full hash as storing the hash is a potential + * opportunity to use the unused space due to the alignment of the bucket_entry + * structure. We can thus potentially store the hash without any extra space + * (which would not be possible with 64 bits of the hash). + */ +template +class bucket_entry : public bucket_entry_hash { + using bucket_hash = bucket_entry_hash; + + public: + using value_type = ValueType; + using distance_type = std::int16_t; + + bucket_entry() noexcept + : bucket_hash(), + m_dist_from_ideal_bucket(EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET), + m_last_bucket(false) { + tsl_rh_assert(empty()); + } + + bucket_entry(bool last_bucket) noexcept + : bucket_hash(), + m_dist_from_ideal_bucket(EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET), + m_last_bucket(last_bucket) { + tsl_rh_assert(empty()); + } + + bucket_entry(const bucket_entry& other) noexcept( + std::is_nothrow_copy_constructible::value) + : bucket_hash(other), + m_dist_from_ideal_bucket(EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET), + m_last_bucket(other.m_last_bucket) { + if (!other.empty()) { + ::new (static_cast(std::addressof(m_value))) + value_type(other.value()); + m_dist_from_ideal_bucket = other.m_dist_from_ideal_bucket; + } + tsl_rh_assert(empty() == other.empty()); + } + + /** + * Never really used, but still necessary as we must call resize on an empty + * `std::vector`. and we need to support move-only types. See + * robin_hash constructor for details. + */ + bucket_entry(bucket_entry&& other) noexcept( + std::is_nothrow_move_constructible::value) + : bucket_hash(std::move(other)), + m_dist_from_ideal_bucket(EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET), + m_last_bucket(other.m_last_bucket) { + if (!other.empty()) { + ::new (static_cast(std::addressof(m_value))) + value_type(std::move(other.value())); + m_dist_from_ideal_bucket = other.m_dist_from_ideal_bucket; + } + tsl_rh_assert(empty() == other.empty()); + } + + bucket_entry& operator=(const bucket_entry& other) noexcept( + std::is_nothrow_copy_constructible::value) { + if (this != &other) { + clear(); + + bucket_hash::operator=(other); + if (!other.empty()) { + ::new (static_cast(std::addressof(m_value))) + value_type(other.value()); + } + + m_dist_from_ideal_bucket = other.m_dist_from_ideal_bucket; + m_last_bucket = other.m_last_bucket; + } + + return *this; + } + + bucket_entry& operator=(bucket_entry&&) = delete; + + ~bucket_entry() noexcept { clear(); } + + void clear() noexcept { + if (!empty()) { + destroy_value(); + m_dist_from_ideal_bucket = EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET; + } + } + + bool empty() const noexcept { + return m_dist_from_ideal_bucket == EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET; + } + + value_type& value() noexcept { + tsl_rh_assert(!empty()); + return *std::launder( + reinterpret_cast(std::addressof(m_value))); + } + + const value_type& value() const noexcept { + tsl_rh_assert(!empty()); + return *std::launder( + reinterpret_cast(std::addressof(m_value))); + } + + distance_type dist_from_ideal_bucket() const noexcept { + return m_dist_from_ideal_bucket; + } + + bool last_bucket() const noexcept { return m_last_bucket; } + + void set_as_last_bucket() noexcept { m_last_bucket = true; } + + template + void set_value_of_empty_bucket(distance_type dist_from_ideal_bucket, + truncated_hash_type hash, + Args&&... value_type_args) { + tsl_rh_assert(dist_from_ideal_bucket >= 0); + tsl_rh_assert(empty()); + + ::new (static_cast(std::addressof(m_value))) + value_type(std::forward(value_type_args)...); + this->set_hash(hash); + m_dist_from_ideal_bucket = dist_from_ideal_bucket; + + tsl_rh_assert(!empty()); + } + + void swap_with_value_in_bucket(distance_type& dist_from_ideal_bucket, + truncated_hash_type& hash, value_type& value) { + tsl_rh_assert(!empty()); + tsl_rh_assert(dist_from_ideal_bucket > m_dist_from_ideal_bucket); + + using std::swap; + swap(value, this->value()); + swap(dist_from_ideal_bucket, m_dist_from_ideal_bucket); + + if (StoreHash) { + const truncated_hash_type tmp_hash = this->truncated_hash(); + this->set_hash(hash); + hash = tmp_hash; + } else { + // Avoid warning of unused variable if StoreHash is false + TSL_RH_UNUSED(hash); + } + } + + static truncated_hash_type truncate_hash(std::size_t hash) noexcept { + return truncated_hash_type(hash); + } + + private: + void destroy_value() noexcept { + tsl_rh_assert(!empty()); + value().~value_type(); + } + + public: + static const distance_type EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET = -1; + static const distance_type DIST_FROM_IDEAL_BUCKET_LIMIT = 8192; + static_assert(DIST_FROM_IDEAL_BUCKET_LIMIT <= + std::numeric_limits::max() - 1, + "DIST_FROM_IDEAL_BUCKET_LIMIT must be <= " + "std::numeric_limits::max() - 1."); + + private: + distance_type m_dist_from_ideal_bucket; + bool m_last_bucket; + alignas(value_type) unsigned char m_value[sizeof(value_type)]; +}; + +/** + * Internal common class used by `robin_map` and `robin_set`. + * + * ValueType is what will be stored by `robin_hash` (usually `std::pair` + * for map and `Key` for set). + * + * `KeySelect` should be a `FunctionObject` which takes a `ValueType` in + * parameter and returns a reference to the key. + * + * `ValueSelect` should be a `FunctionObject` which takes a `ValueType` in + * parameter and returns a reference to the value. `ValueSelect` should be void + * if there is no value (in a set for example). + * + * The strong exception guarantee only holds if the expression + * `std::is_nothrow_swappable::value && + * std::is_nothrow_move_constructible::value` is true. + * + * Behaviour is undefined if the destructor of `ValueType` throws. + */ +template +class robin_hash : private Hash, private KeyEqual, private GrowthPolicy { + private: + template + using has_mapped_type = + typename std::integral_constant::value>; + + static_assert( + noexcept(std::declval().bucket_for_hash(std::size_t(0))), + "GrowthPolicy::bucket_for_hash must be noexcept."); + static_assert(noexcept(std::declval().clear()), + "GrowthPolicy::clear must be noexcept."); + + public: + template + class robin_iterator; + + using key_type = typename KeySelect::key_type; + using value_type = ValueType; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using hasher = Hash; + using key_equal = KeyEqual; + using allocator_type = Allocator; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = value_type*; + using const_pointer = const value_type*; + using iterator = robin_iterator; + using const_iterator = robin_iterator; + + private: + /** + * Either store the hash because we are asked by the `StoreHash` template + * parameter or store the hash because it doesn't cost us anything in size and + * can be used to speed up rehash. + */ + static constexpr bool STORE_HASH = + StoreHash || + ((sizeof(tsl::detail_robin_hash::bucket_entry) == + sizeof(tsl::detail_robin_hash::bucket_entry)) && + (sizeof(std::size_t) == sizeof(truncated_hash_type) || + is_power_of_two_policy::value) && + // Don't store the hash for primitive types with default hash. + (!std::is_arithmetic::value || + !std::is_same>::value)); + + /** + * Only use the stored hash on lookup if we are explicitly asked. We are not + * sure how slow the KeyEqual operation is. An extra comparison may slow + * things down with a fast KeyEqual. + */ + static constexpr bool USE_STORED_HASH_ON_LOOKUP = StoreHash; + + /** + * We can only use the hash on rehash if the size of the hash type is the same + * as the stored one or if we use a power of two modulo. In the case of the + * power of two modulo, we just mask the least significant bytes, we just have + * to check that the truncated_hash_type didn't truncated more bytes. + */ + static bool USE_STORED_HASH_ON_REHASH(size_type bucket_count) { + if (STORE_HASH && sizeof(std::size_t) == sizeof(truncated_hash_type)) { + TSL_RH_UNUSED(bucket_count); + return true; + } else if (STORE_HASH && is_power_of_two_policy::value) { + return bucket_count == 0 || + (bucket_count - 1) <= + std::numeric_limits::max(); + } else { + TSL_RH_UNUSED(bucket_count); + return false; + } + } + + using bucket_entry = + tsl::detail_robin_hash::bucket_entry; + using distance_type = typename bucket_entry::distance_type; + + using buckets_allocator = typename std::allocator_traits< + allocator_type>::template rebind_alloc; + using buckets_container_type = std::vector; + + public: + /** + * The 'operator*()' and 'operator->()' methods return a const reference and + * const pointer respectively to the stored value type. + * + * In case of a map, to get a mutable reference to the value associated to a + * key (the '.second' in the stored pair), you have to call 'value()'. + * + * The main reason for this is that if we returned a `std::pair&` + * instead of a `const std::pair&`, the user may modify the key which + * will put the map in a undefined state. + */ + template + class robin_iterator { + friend class robin_hash; + + private: + using bucket_entry_ptr = + typename std::conditional::type; + + robin_iterator(bucket_entry_ptr bucket) noexcept : m_bucket(bucket) {} + + public: + using iterator_category = std::forward_iterator_tag; + using value_type = const typename robin_hash::value_type; + using difference_type = std::ptrdiff_t; + using reference = value_type&; + using pointer = value_type*; + + robin_iterator() noexcept {} + + // Copy constructor from iterator to const_iterator. + template ::type* = nullptr> + robin_iterator(const robin_iterator& other) noexcept + : m_bucket(other.m_bucket) {} + + robin_iterator(const robin_iterator& other) = default; + robin_iterator(robin_iterator&& other) = default; + robin_iterator& operator=(const robin_iterator& other) = default; + robin_iterator& operator=(robin_iterator&& other) = default; + + const typename robin_hash::key_type& key() const { + return KeySelect()(m_bucket->value()); + } + + template ::value && + IsConst>::type* = nullptr> + const typename U::value_type& value() const { + return U()(m_bucket->value()); + } + + template ::value && + !IsConst>::type* = nullptr> + typename U::value_type& value() const { + return U()(m_bucket->value()); + } + + reference operator*() const { return m_bucket->value(); } + + pointer operator->() const { return std::addressof(m_bucket->value()); } + + robin_iterator& operator++() { + while (true) { + if (m_bucket->last_bucket()) { + ++m_bucket; + return *this; + } + + ++m_bucket; + if (!m_bucket->empty()) { + return *this; + } + } + } + + robin_iterator operator++(int) { + robin_iterator tmp(*this); + ++*this; + + return tmp; + } + + friend bool operator==(const robin_iterator& lhs, + const robin_iterator& rhs) { + return lhs.m_bucket == rhs.m_bucket; + } + + friend bool operator!=(const robin_iterator& lhs, + const robin_iterator& rhs) { + return !(lhs == rhs); + } + + private: + bucket_entry_ptr m_bucket; + }; + + public: + robin_hash(size_type bucket_count, const Hash& hash, const KeyEqual& equal, + const Allocator& alloc, + float min_load_factor = DEFAULT_MIN_LOAD_FACTOR, + float max_load_factor = DEFAULT_MAX_LOAD_FACTOR) + : Hash(hash), + KeyEqual(equal), + GrowthPolicy(bucket_count), + m_buckets_data(bucket_count, alloc), + m_buckets(m_buckets_data.empty() ? static_empty_bucket_ptr() + : m_buckets_data.data()), + m_bucket_count(bucket_count), + m_nb_elements(0), + m_grow_on_next_insert(false), + m_try_shrink_on_next_insert(false) { + if (bucket_count > max_bucket_count()) { + TSL_RH_THROW_OR_TERMINATE(std::length_error, + "The map exceeds its maximum bucket count."); + } + + if (m_bucket_count > 0) { + tsl_rh_assert(!m_buckets_data.empty()); + m_buckets_data.back().set_as_last_bucket(); + } + + this->min_load_factor(min_load_factor); + this->max_load_factor(max_load_factor); + } + + robin_hash(const robin_hash& other) + : Hash(other), + KeyEqual(other), + GrowthPolicy(other), + m_buckets_data(other.m_buckets_data), + m_buckets(m_buckets_data.empty() ? static_empty_bucket_ptr() + : m_buckets_data.data()), + m_bucket_count(other.m_bucket_count), + m_nb_elements(other.m_nb_elements), + m_load_threshold(other.m_load_threshold), + m_min_load_factor(other.m_min_load_factor), + m_max_load_factor(other.m_max_load_factor), + m_grow_on_next_insert(other.m_grow_on_next_insert), + m_try_shrink_on_next_insert(other.m_try_shrink_on_next_insert) {} + + robin_hash(robin_hash&& other) noexcept( + std::is_nothrow_move_constructible< + Hash>::value&& std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_constructible::value) + : Hash(std::move(static_cast(other))), + KeyEqual(std::move(static_cast(other))), + GrowthPolicy(std::move(static_cast(other))), + m_buckets_data(std::move(other.m_buckets_data)), + m_buckets(m_buckets_data.empty() ? static_empty_bucket_ptr() + : m_buckets_data.data()), + m_bucket_count(other.m_bucket_count), + m_nb_elements(other.m_nb_elements), + m_load_threshold(other.m_load_threshold), + m_min_load_factor(other.m_min_load_factor), + m_max_load_factor(other.m_max_load_factor), + m_grow_on_next_insert(other.m_grow_on_next_insert), + m_try_shrink_on_next_insert(other.m_try_shrink_on_next_insert) { + other.clear_and_shrink(); + } + + robin_hash& operator=(const robin_hash& other) { + if (&other != this) { + Hash::operator=(other); + KeyEqual::operator=(other); + GrowthPolicy::operator=(other); + + m_buckets_data = other.m_buckets_data; + m_buckets = m_buckets_data.empty() ? static_empty_bucket_ptr() + : m_buckets_data.data(); + m_bucket_count = other.m_bucket_count; + m_nb_elements = other.m_nb_elements; + + m_load_threshold = other.m_load_threshold; + m_min_load_factor = other.m_min_load_factor; + m_max_load_factor = other.m_max_load_factor; + + m_grow_on_next_insert = other.m_grow_on_next_insert; + m_try_shrink_on_next_insert = other.m_try_shrink_on_next_insert; + } + + return *this; + } + + robin_hash& operator=(robin_hash&& other) { + other.swap(*this); + other.clear_and_shrink(); + + return *this; + } + + allocator_type get_allocator() const { + return m_buckets_data.get_allocator(); + } + + /* + * Iterators + */ + iterator begin() noexcept { + std::size_t i = 0; + while (i < m_bucket_count && m_buckets[i].empty()) { + i++; + } + + return iterator(m_buckets + i); + } + + const_iterator begin() const noexcept { return cbegin(); } + + const_iterator cbegin() const noexcept { + std::size_t i = 0; + while (i < m_bucket_count && m_buckets[i].empty()) { + i++; + } + + return const_iterator(m_buckets + i); + } + + iterator end() noexcept { return iterator(m_buckets + m_bucket_count); } + + const_iterator end() const noexcept { return cend(); } + + const_iterator cend() const noexcept { + return const_iterator(m_buckets + m_bucket_count); + } + + /* + * Capacity + */ + bool empty() const noexcept { return m_nb_elements == 0; } + + size_type size() const noexcept { return m_nb_elements; } + + size_type max_size() const noexcept { return m_buckets_data.max_size(); } + + /* + * Modifiers + */ + void clear() noexcept { + if (m_min_load_factor > 0.0f) { + clear_and_shrink(); + } else { + for (auto& bucket : m_buckets_data) { + bucket.clear(); + } + + m_nb_elements = 0; + m_grow_on_next_insert = false; + } + } + + template + std::pair insert(P&& value) { + return insert_impl(KeySelect()(value), std::forward

(value)); + } + + template + iterator insert_hint(const_iterator hint, P&& value) { + if (hint != cend() && + compare_keys(KeySelect()(*hint), KeySelect()(value))) { + return mutable_iterator(hint); + } + + return insert(std::forward

(value)).first; + } + + template + void insert(InputIt first, InputIt last) { + if (std::is_base_of< + std::forward_iterator_tag, + typename std::iterator_traits::iterator_category>::value) { + const auto nb_elements_insert = std::distance(first, last); + const size_type nb_free_buckets = m_load_threshold - size(); + tsl_rh_assert(m_load_threshold >= size()); + + if (nb_elements_insert > 0 && + nb_free_buckets < size_type(nb_elements_insert)) { + reserve(size() + size_type(nb_elements_insert)); + } + } + + for (; first != last; ++first) { + insert(*first); + } + } + + template + std::pair insert_or_assign(K&& key, M&& obj) { + auto it = try_emplace(std::forward(key), std::forward(obj)); + if (!it.second) { + it.first.value() = std::forward(obj); + } + + return it; + } + + template + iterator insert_or_assign(const_iterator hint, K&& key, M&& obj) { + if (hint != cend() && compare_keys(KeySelect()(*hint), key)) { + auto it = mutable_iterator(hint); + it.value() = std::forward(obj); + + return it; + } + + return insert_or_assign(std::forward(key), std::forward(obj)).first; + } + + template + std::pair emplace(Args&&... args) { + return insert(value_type(std::forward(args)...)); + } + + template + iterator emplace_hint(const_iterator hint, Args&&... args) { + return insert_hint(hint, value_type(std::forward(args)...)); + } + + template + std::pair try_emplace(K&& key, Args&&... args) { + return insert_impl(key, std::piecewise_construct, + std::forward_as_tuple(std::forward(key)), + std::forward_as_tuple(std::forward(args)...)); + } + + template + iterator try_emplace_hint(const_iterator hint, K&& key, Args&&... args) { + if (hint != cend() && compare_keys(KeySelect()(*hint), key)) { + return mutable_iterator(hint); + } + + return try_emplace(std::forward(key), std::forward(args)...).first; + } + + void erase_fast(iterator pos) { + erase_from_bucket(pos); + } + + /** + * Here to avoid `template size_type erase(const K& key)` being used + * when we use an `iterator` instead of a `const_iterator`. + */ + iterator erase(iterator pos) { + erase_from_bucket(pos); + + /** + * Erase bucket used a backward shift after clearing the bucket. + * Check if there is a new value in the bucket, if not get the next + * non-empty. + */ + if (pos.m_bucket->empty()) { + ++pos; + } + + return pos; + } + + iterator erase(const_iterator pos) { return erase(mutable_iterator(pos)); } + + iterator erase(const_iterator first, const_iterator last) { + if (first == last) { + return mutable_iterator(first); + } + + auto first_mutable = mutable_iterator(first); + auto last_mutable = mutable_iterator(last); + for (auto it = first_mutable.m_bucket; it != last_mutable.m_bucket; ++it) { + if (!it->empty()) { + it->clear(); + m_nb_elements--; + } + } + + if (last_mutable == end()) { + m_try_shrink_on_next_insert = true; + return end(); + } + + /* + * Backward shift on the values which come after the deleted values. + * We try to move the values closer to their ideal bucket. + */ + std::size_t icloser_bucket = + static_cast(first_mutable.m_bucket - m_buckets); + std::size_t ito_move_closer_value = + static_cast(last_mutable.m_bucket - m_buckets); + tsl_rh_assert(ito_move_closer_value > icloser_bucket); + + const std::size_t ireturn_bucket = + ito_move_closer_value - + std::min( + ito_move_closer_value - icloser_bucket, + std::size_t( + m_buckets[ito_move_closer_value].dist_from_ideal_bucket())); + + while (ito_move_closer_value < m_bucket_count && + m_buckets[ito_move_closer_value].dist_from_ideal_bucket() > 0) { + icloser_bucket = + ito_move_closer_value - + std::min( + ito_move_closer_value - icloser_bucket, + std::size_t( + m_buckets[ito_move_closer_value].dist_from_ideal_bucket())); + + tsl_rh_assert(m_buckets[icloser_bucket].empty()); + const distance_type new_distance = distance_type( + m_buckets[ito_move_closer_value].dist_from_ideal_bucket() - + (ito_move_closer_value - icloser_bucket)); + m_buckets[icloser_bucket].set_value_of_empty_bucket( + new_distance, m_buckets[ito_move_closer_value].truncated_hash(), + std::move(m_buckets[ito_move_closer_value].value())); + m_buckets[ito_move_closer_value].clear(); + + ++icloser_bucket; + ++ito_move_closer_value; + } + + m_try_shrink_on_next_insert = true; + + return iterator(m_buckets + ireturn_bucket); + } + + template + size_type erase(const K& key) { + return erase(key, hash_key(key)); + } + + template + size_type erase(const K& key, std::size_t hash) { + auto it = find(key, hash); + if (it != end()) { + erase_from_bucket(it); + return 1; + } else { + return 0; + } + } + + void swap(robin_hash& other) { + using std::swap; + + swap(static_cast(*this), static_cast(other)); + swap(static_cast(*this), static_cast(other)); + swap(static_cast(*this), static_cast(other)); + swap(m_buckets_data, other.m_buckets_data); + swap(m_buckets, other.m_buckets); + swap(m_bucket_count, other.m_bucket_count); + swap(m_nb_elements, other.m_nb_elements); + swap(m_load_threshold, other.m_load_threshold); + swap(m_min_load_factor, other.m_min_load_factor); + swap(m_max_load_factor, other.m_max_load_factor); + swap(m_grow_on_next_insert, other.m_grow_on_next_insert); + swap(m_try_shrink_on_next_insert, other.m_try_shrink_on_next_insert); + } + + /* + * Lookup + */ + template ::value>::type* = nullptr> + typename U::value_type& at(const K& key) { + return at(key, hash_key(key)); + } + + template ::value>::type* = nullptr> + typename U::value_type& at(const K& key, std::size_t hash) { + return const_cast( + static_cast(this)->at(key, hash)); + } + + template ::value>::type* = nullptr> + const typename U::value_type& at(const K& key) const { + return at(key, hash_key(key)); + } + + template ::value>::type* = nullptr> + const typename U::value_type& at(const K& key, std::size_t hash) const { + auto it = find(key, hash); + if (it != cend()) { + return it.value(); + } else { + TSL_RH_THROW_OR_TERMINATE(std::out_of_range, "Couldn't find key."); + } + } + + template ::value>::type* = nullptr> + typename U::value_type& operator[](K&& key) { + return try_emplace(std::forward(key)).first.value(); + } + + template + size_type count(const K& key) const { + return count(key, hash_key(key)); + } + + template + size_type count(const K& key, std::size_t hash) const { + if (find(key, hash) != cend()) { + return 1; + } else { + return 0; + } + } + + template + iterator find(const K& key) { + return find_impl(key, hash_key(key)); + } + + template + iterator find(const K& key, std::size_t hash) { + return find_impl(key, hash); + } + + template + const_iterator find(const K& key) const { + return find_impl(key, hash_key(key)); + } + + template + const_iterator find(const K& key, std::size_t hash) const { + return find_impl(key, hash); + } + + template + bool contains(const K& key) const { + return contains(key, hash_key(key)); + } + + template + bool contains(const K& key, std::size_t hash) const { + return count(key, hash) != 0; + } + + template + std::pair equal_range(const K& key) { + return equal_range(key, hash_key(key)); + } + + template + std::pair equal_range(const K& key, std::size_t hash) { + iterator it = find(key, hash); + return std::make_pair(it, (it == end()) ? it : std::next(it)); + } + + template + std::pair equal_range(const K& key) const { + return equal_range(key, hash_key(key)); + } + + template + std::pair equal_range( + const K& key, std::size_t hash) const { + const_iterator it = find(key, hash); + return std::make_pair(it, (it == cend()) ? it : std::next(it)); + } + + /* + * Bucket interface + */ + size_type bucket_count() const { return m_bucket_count; } + + size_type max_bucket_count() const { + return std::min(GrowthPolicy::max_bucket_count(), + m_buckets_data.max_size()); + } + + /* + * Hash policy + */ + float load_factor() const { + if (bucket_count() == 0) { + return 0; + } + + return float(m_nb_elements) / float(bucket_count()); + } + + float min_load_factor() const { return m_min_load_factor; } + + float max_load_factor() const { return m_max_load_factor; } + + void min_load_factor(float ml) { + m_min_load_factor = std::clamp(ml, float(MINIMUM_MIN_LOAD_FACTOR), + float(MAXIMUM_MIN_LOAD_FACTOR)); + } + + void max_load_factor(float ml) { + m_max_load_factor = std::clamp(ml, float(MINIMUM_MAX_LOAD_FACTOR), + float(MAXIMUM_MAX_LOAD_FACTOR)); + m_load_threshold = size_type(float(bucket_count()) * m_max_load_factor); + tsl_rh_assert(bucket_count() == 0 || m_load_threshold < bucket_count()); + } + + void rehash(size_type count_) { + count_ = std::max(count_, + size_type(std::ceil(float(size()) / max_load_factor()))); + rehash_impl(count_); + } + + void reserve(size_type count_) { + rehash(size_type(std::ceil(float(count_) / max_load_factor()))); + } + + /* + * Observers + */ + hasher hash_function() const { return static_cast(*this); } + + key_equal key_eq() const { return static_cast(*this); } + + /* + * Other + */ + iterator mutable_iterator(const_iterator pos) { + return iterator(const_cast(pos.m_bucket)); + } + + template + void serialize(Serializer& serializer) const { + serialize_impl(serializer); + } + + template + void deserialize(Deserializer& deserializer, bool hash_compatible) { + deserialize_impl(deserializer, hash_compatible); + } + + private: + template + std::size_t hash_key(const K& key) const { + return Hash::operator()(key); + } + + template + bool compare_keys(const K1& key1, const K2& key2) const { + return KeyEqual::operator()(key1, key2); + } + + std::size_t bucket_for_hash(std::size_t hash) const { + const std::size_t bucket = GrowthPolicy::bucket_for_hash(hash); + tsl_rh_assert(bucket < m_bucket_count || + (bucket == 0 && m_bucket_count == 0)); + + return bucket; + } + + template ::value>::type* = + nullptr> + std::size_t next_bucket(std::size_t index) const noexcept { + tsl_rh_assert(index < bucket_count()); + + return (index + 1) & this->m_mask; + } + + template ::value>::type* = + nullptr> + std::size_t next_bucket(std::size_t index) const noexcept { + tsl_rh_assert(index < bucket_count()); + + index++; + return (index != bucket_count()) ? index : 0; + } + + template + iterator find_impl(const K& key, std::size_t hash) { + return mutable_iterator( + static_cast(this)->find(key, hash)); + } + + template + const_iterator find_impl(const K& key, std::size_t hash) const { + std::size_t ibucket = bucket_for_hash(hash); + distance_type dist_from_ideal_bucket = 0; + + while (dist_from_ideal_bucket <= + m_buckets[ibucket].dist_from_ideal_bucket()) { + if (TSL_RH_LIKELY( + (!USE_STORED_HASH_ON_LOOKUP || + m_buckets[ibucket].bucket_hash_equal(hash)) && + compare_keys(KeySelect()(m_buckets[ibucket].value()), key))) { + return const_iterator(m_buckets + ibucket); + } + + ibucket = next_bucket(ibucket); + dist_from_ideal_bucket++; + } + + return cend(); + } + + void erase_from_bucket(iterator pos) { + pos.m_bucket->clear(); + m_nb_elements--; + + /** + * Backward shift, swap the empty bucket, previous_ibucket, with the values + * on its right, ibucket, until we cross another empty bucket or if the + * other bucket has a distance_from_ideal_bucket == 0. + * + * We try to move the values closer to their ideal bucket. + */ + std::size_t previous_ibucket = + static_cast(pos.m_bucket - m_buckets); + std::size_t ibucket = next_bucket(previous_ibucket); + + while (m_buckets[ibucket].dist_from_ideal_bucket() > 0) { + tsl_rh_assert(m_buckets[previous_ibucket].empty()); + + const distance_type new_distance = + distance_type(m_buckets[ibucket].dist_from_ideal_bucket() - 1); + m_buckets[previous_ibucket].set_value_of_empty_bucket( + new_distance, m_buckets[ibucket].truncated_hash(), + std::move(m_buckets[ibucket].value())); + m_buckets[ibucket].clear(); + + previous_ibucket = ibucket; + ibucket = next_bucket(ibucket); + } + m_try_shrink_on_next_insert = true; + } + + template + std::pair insert_impl(const K& key, + Args&&... value_type_args) { + const std::size_t hash = hash_key(key); + + std::size_t ibucket = bucket_for_hash(hash); + distance_type dist_from_ideal_bucket = 0; + + while (dist_from_ideal_bucket <= + m_buckets[ibucket].dist_from_ideal_bucket()) { + if ((!USE_STORED_HASH_ON_LOOKUP || + m_buckets[ibucket].bucket_hash_equal(hash)) && + compare_keys(KeySelect()(m_buckets[ibucket].value()), key)) { + return std::make_pair(iterator(m_buckets + ibucket), false); + } + + ibucket = next_bucket(ibucket); + dist_from_ideal_bucket++; + } + + while (rehash_on_extreme_load(dist_from_ideal_bucket)) { + ibucket = bucket_for_hash(hash); + dist_from_ideal_bucket = 0; + + while (dist_from_ideal_bucket <= + m_buckets[ibucket].dist_from_ideal_bucket()) { + ibucket = next_bucket(ibucket); + dist_from_ideal_bucket++; + } + } + + if (m_buckets[ibucket].empty()) { + m_buckets[ibucket].set_value_of_empty_bucket( + dist_from_ideal_bucket, bucket_entry::truncate_hash(hash), + std::forward(value_type_args)...); + } else { + insert_value(ibucket, dist_from_ideal_bucket, + bucket_entry::truncate_hash(hash), + std::forward(value_type_args)...); + } + + m_nb_elements++; + /* + * The value will be inserted in ibucket in any case, either because it was + * empty or by stealing the bucket (robin hood). + */ + return std::make_pair(iterator(m_buckets + ibucket), true); + } + + template + void insert_value(std::size_t ibucket, distance_type dist_from_ideal_bucket, + truncated_hash_type hash, Args&&... value_type_args) { + value_type value(std::forward(value_type_args)...); + insert_value_impl(ibucket, dist_from_ideal_bucket, hash, value); + } + + void insert_value(std::size_t ibucket, distance_type dist_from_ideal_bucket, + truncated_hash_type hash, value_type&& value) { + insert_value_impl(ibucket, dist_from_ideal_bucket, hash, value); + } + + /* + * We don't use `value_type&& value` as last argument due to a bug in MSVC + * when `value_type` is a pointer, The compiler is not able to see the + * difference between `std::string*` and `std::string*&&` resulting in a + * compilation error. + * + * The `value` will be in a moved state at the end of the function. + */ + void insert_value_impl(std::size_t ibucket, + distance_type dist_from_ideal_bucket, + truncated_hash_type hash, value_type& value) { + tsl_rh_assert(dist_from_ideal_bucket > + m_buckets[ibucket].dist_from_ideal_bucket()); + m_buckets[ibucket].swap_with_value_in_bucket(dist_from_ideal_bucket, hash, + value); + ibucket = next_bucket(ibucket); + dist_from_ideal_bucket++; + + while (!m_buckets[ibucket].empty()) { + if (dist_from_ideal_bucket > + m_buckets[ibucket].dist_from_ideal_bucket()) { + if (dist_from_ideal_bucket > + bucket_entry::DIST_FROM_IDEAL_BUCKET_LIMIT) { + /** + * The number of probes is really high, rehash the map on the next + * insert. Difficult to do now as rehash may throw an exception. + */ + m_grow_on_next_insert = true; + } + + m_buckets[ibucket].swap_with_value_in_bucket(dist_from_ideal_bucket, + hash, value); + } + + ibucket = next_bucket(ibucket); + dist_from_ideal_bucket++; + } + + m_buckets[ibucket].set_value_of_empty_bucket(dist_from_ideal_bucket, hash, + std::move(value)); + } + + void rehash_impl(size_type count_) { + robin_hash new_table(count_, static_cast(*this), + static_cast(*this), get_allocator(), + m_min_load_factor, m_max_load_factor); + tsl_rh_assert(size() <= new_table.m_load_threshold); + + const bool use_stored_hash = + USE_STORED_HASH_ON_REHASH(new_table.bucket_count()); + for (auto& bucket : m_buckets_data) { + if (bucket.empty()) { + continue; + } + + const std::size_t hash = + use_stored_hash ? bucket.truncated_hash() + : new_table.hash_key(KeySelect()(bucket.value())); + + new_table.insert_value_on_rehash(new_table.bucket_for_hash(hash), 0, + bucket_entry::truncate_hash(hash), + std::move(bucket.value())); + } + + new_table.m_nb_elements = m_nb_elements; + new_table.swap(*this); + } + + void clear_and_shrink() noexcept { + GrowthPolicy::clear(); + m_buckets_data.clear(); + m_buckets = static_empty_bucket_ptr(); + m_bucket_count = 0; + m_nb_elements = 0; + m_load_threshold = 0; + m_grow_on_next_insert = false; + m_try_shrink_on_next_insert = false; + } + + void insert_value_on_rehash(std::size_t ibucket, + distance_type dist_from_ideal_bucket, + truncated_hash_type hash, value_type&& value) { + while (true) { + if (dist_from_ideal_bucket > + m_buckets[ibucket].dist_from_ideal_bucket()) { + if (m_buckets[ibucket].empty()) { + m_buckets[ibucket].set_value_of_empty_bucket(dist_from_ideal_bucket, + hash, std::move(value)); + return; + } else { + m_buckets[ibucket].swap_with_value_in_bucket(dist_from_ideal_bucket, + hash, value); + } + } + + dist_from_ideal_bucket++; + ibucket = next_bucket(ibucket); + } + } + + /** + * Grow the table if m_grow_on_next_insert is true or we reached the + * max_load_factor. Shrink the table if m_try_shrink_on_next_insert is true + * (an erase occurred) and we're below the min_load_factor. + * + * Return true if the table has been rehashed. + */ + bool rehash_on_extreme_load(distance_type curr_dist_from_ideal_bucket) { + if (m_grow_on_next_insert || + curr_dist_from_ideal_bucket > + bucket_entry::DIST_FROM_IDEAL_BUCKET_LIMIT || + size() >= m_load_threshold) { + rehash_impl(GrowthPolicy::next_bucket_count()); + m_grow_on_next_insert = false; + + return true; + } + + if (m_try_shrink_on_next_insert) { + m_try_shrink_on_next_insert = false; + if (m_min_load_factor != 0.0f && load_factor() < m_min_load_factor) { + reserve(size() + 1); + + return true; + } + } + + return false; + } + + template + void serialize_impl(Serializer& serializer) const { + const slz_size_type version = SERIALIZATION_PROTOCOL_VERSION; + serializer(version); + + // Indicate if the truncated hash of each bucket is stored. Use a + // std::int16_t instead of a bool to avoid the need for the serializer to + // support an extra 'bool' type. + const std::int16_t hash_stored_for_bucket = + static_cast(STORE_HASH); + serializer(hash_stored_for_bucket); + + const slz_size_type nb_elements = m_nb_elements; + serializer(nb_elements); + + const slz_size_type bucket_count = m_buckets_data.size(); + serializer(bucket_count); + + const float min_load_factor = m_min_load_factor; + serializer(min_load_factor); + + const float max_load_factor = m_max_load_factor; + serializer(max_load_factor); + + for (const bucket_entry& bucket : m_buckets_data) { + if (bucket.empty()) { + const std::int16_t empty_bucket = + bucket_entry::EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET; + serializer(empty_bucket); + } else { + const std::int16_t dist_from_ideal_bucket = + bucket.dist_from_ideal_bucket(); + serializer(dist_from_ideal_bucket); + if (STORE_HASH) { + const std::uint32_t truncated_hash = bucket.truncated_hash(); + serializer(truncated_hash); + } + serializer(bucket.value()); + } + } + } + + template + void deserialize_impl(Deserializer& deserializer, bool hash_compatible) { + tsl_rh_assert(m_buckets_data.empty()); // Current hash table must be empty + + const slz_size_type version = + deserialize_value(deserializer); + // For now we only have one version of the serialization protocol. + // If it doesn't match there is a problem with the file. + if (version != SERIALIZATION_PROTOCOL_VERSION) { + TSL_RH_THROW_OR_TERMINATE(std::runtime_error, + "Can't deserialize the ordered_map/set. " + "The protocol version header is invalid."); + } + + const bool hash_stored_for_bucket = + deserialize_value(deserializer) ? true : false; + if (hash_compatible && STORE_HASH != hash_stored_for_bucket) { + TSL_RH_THROW_OR_TERMINATE( + std::runtime_error, + "Can't deserialize a map with a different StoreHash " + "than the one used during the serialization when " + "hash compatibility is used"); + } + + const slz_size_type nb_elements = + deserialize_value(deserializer); + const slz_size_type bucket_count_ds = + deserialize_value(deserializer); + const float min_load_factor = deserialize_value(deserializer); + const float max_load_factor = deserialize_value(deserializer); + + if (min_load_factor < MINIMUM_MIN_LOAD_FACTOR || + min_load_factor > MAXIMUM_MIN_LOAD_FACTOR) { + TSL_RH_THROW_OR_TERMINATE( + std::runtime_error, + "Invalid min_load_factor. Check that the serializer " + "and deserializer support floats correctly as they " + "can be converted implicitly to ints."); + } + + if (max_load_factor < MINIMUM_MAX_LOAD_FACTOR || + max_load_factor > MAXIMUM_MAX_LOAD_FACTOR) { + TSL_RH_THROW_OR_TERMINATE( + std::runtime_error, + "Invalid max_load_factor. Check that the serializer " + "and deserializer support floats correctly as they " + "can be converted implicitly to ints."); + } + + this->min_load_factor(min_load_factor); + this->max_load_factor(max_load_factor); + + if (bucket_count_ds == 0) { + tsl_rh_assert(nb_elements == 0); + return; + } + + if (!hash_compatible) { + reserve(numeric_cast(nb_elements, + "Deserialized nb_elements is too big.")); + for (slz_size_type ibucket = 0; ibucket < bucket_count_ds; ibucket++) { + const distance_type dist_from_ideal_bucket = + deserialize_value(deserializer); + if (dist_from_ideal_bucket != + bucket_entry::EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET) { + if (hash_stored_for_bucket) { + TSL_RH_UNUSED(deserialize_value(deserializer)); + } + + insert(deserialize_value(deserializer)); + } + } + + tsl_rh_assert(nb_elements == size()); + } else { + m_bucket_count = numeric_cast( + bucket_count_ds, "Deserialized bucket_count is too big."); + + GrowthPolicy::operator=(GrowthPolicy(m_bucket_count)); + // GrowthPolicy should not modify the bucket count we got from + // deserialization + if (m_bucket_count != bucket_count_ds) { + TSL_RH_THROW_OR_TERMINATE(std::runtime_error, + "The GrowthPolicy is not the same even " + "though hash_compatible is true."); + } + + m_nb_elements = numeric_cast( + nb_elements, "Deserialized nb_elements is too big."); + m_buckets_data.resize(m_bucket_count); + m_buckets = m_buckets_data.data(); + + for (bucket_entry& bucket : m_buckets_data) { + const distance_type dist_from_ideal_bucket = + deserialize_value(deserializer); + if (dist_from_ideal_bucket != + bucket_entry::EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET) { + truncated_hash_type truncated_hash = 0; + if (hash_stored_for_bucket) { + tsl_rh_assert(hash_stored_for_bucket); + truncated_hash = deserialize_value(deserializer); + } + + bucket.set_value_of_empty_bucket( + dist_from_ideal_bucket, truncated_hash, + deserialize_value(deserializer)); + } + } + + if (!m_buckets_data.empty()) { + m_buckets_data.back().set_as_last_bucket(); + } + } + } + + public: + static const size_type DEFAULT_INIT_BUCKETS_SIZE = 0; + + static constexpr float DEFAULT_MAX_LOAD_FACTOR = 0.5f; + static constexpr float MINIMUM_MAX_LOAD_FACTOR = 0.2f; + static constexpr float MAXIMUM_MAX_LOAD_FACTOR = 0.95f; + + static constexpr float DEFAULT_MIN_LOAD_FACTOR = 0.0f; + static constexpr float MINIMUM_MIN_LOAD_FACTOR = 0.0f; + static constexpr float MAXIMUM_MIN_LOAD_FACTOR = 0.15f; + + static_assert(MINIMUM_MAX_LOAD_FACTOR < MAXIMUM_MAX_LOAD_FACTOR, + "MINIMUM_MAX_LOAD_FACTOR should be < MAXIMUM_MAX_LOAD_FACTOR"); + static_assert(MINIMUM_MIN_LOAD_FACTOR < MAXIMUM_MIN_LOAD_FACTOR, + "MINIMUM_MIN_LOAD_FACTOR should be < MAXIMUM_MIN_LOAD_FACTOR"); + static_assert(MAXIMUM_MIN_LOAD_FACTOR < MINIMUM_MAX_LOAD_FACTOR, + "MAXIMUM_MIN_LOAD_FACTOR should be < MINIMUM_MAX_LOAD_FACTOR"); + + private: + /** + * Protocol version currenlty used for serialization. + */ + static const slz_size_type SERIALIZATION_PROTOCOL_VERSION = 1; + + /** + * Return an always valid pointer to an static empty bucket_entry with + * last_bucket() == true. + */ + bucket_entry* static_empty_bucket_ptr() noexcept { + static bucket_entry empty_bucket(true); + tsl_rh_assert(empty_bucket.empty()); + return &empty_bucket; + } + + private: + buckets_container_type m_buckets_data; + + /** + * Points to m_buckets_data.data() if !m_buckets_data.empty() otherwise points + * to static_empty_bucket_ptr. This variable is useful to avoid the cost of + * checking if m_buckets_data is empty when trying to find an element. + * + * TODO Remove m_buckets_data and only use a pointer instead of a + * pointer+vector to save some space in the robin_hash object. Manage the + * Allocator manually. + */ + bucket_entry* m_buckets; + + /** + * Used a lot in find, avoid the call to m_buckets_data.size() which is a bit + * slower. + */ + size_type m_bucket_count; + + size_type m_nb_elements; + + size_type m_load_threshold; + + float m_min_load_factor; + float m_max_load_factor; + + bool m_grow_on_next_insert; + + /** + * We can't shrink down the map on erase operations as the erase methods need + * to return the next iterator. Shrinking the map would invalidate all the + * iterators and we could not return the next iterator in a meaningful way, On + * erase, we thus just indicate on erase that we should try to shrink the hash + * table on the next insert if we go below the min_load_factor. + */ + bool m_try_shrink_on_next_insert; +}; + +} // namespace detail_robin_hash + +} // namespace tsl + +#endif diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/ext/robin_map/include/tsl/robin_map.h b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/ext/robin_map/include/tsl/robin_map.h new file mode 100644 index 0000000..b594810 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/ext/robin_map/include/tsl/robin_map.h @@ -0,0 +1,815 @@ +/** + * MIT License + * + * Copyright (c) 2017 Thibaut Goetghebuer-Planchon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef TSL_ROBIN_MAP_H +#define TSL_ROBIN_MAP_H + +#include +#include +#include +#include +#include +#include + +#include "robin_hash.h" + +namespace tsl { + +/** + * Implementation of a hash map using open-addressing and the robin hood hashing + * algorithm with backward shift deletion. + * + * For operations modifying the hash map (insert, erase, rehash, ...), the + * strong exception guarantee is only guaranteed when the expression + * `std::is_nothrow_swappable>::value && + * std::is_nothrow_move_constructible>::value` is true, + * otherwise if an exception is thrown during the swap or the move, the hash map + * may end up in a undefined state. Per the standard a `Key` or `T` with a + * noexcept copy constructor and no move constructor also satisfies the + * `std::is_nothrow_move_constructible>::value` criterion (and + * will thus guarantee the strong exception for the map). + * + * When `StoreHash` is true, 32 bits of the hash are stored alongside the + * values. It can improve the performance during lookups if the `KeyEqual` + * function takes time (if it engenders a cache-miss for example) as we then + * compare the stored hashes before comparing the keys. When + * `tsl::rh::power_of_two_growth_policy` is used as `GrowthPolicy`, it may also + * speed-up the rehash process as we can avoid to recalculate the hash. When it + * is detected that storing the hash will not incur any memory penalty due to + * alignment (i.e. `sizeof(tsl::detail_robin_hash::bucket_entry) == sizeof(tsl::detail_robin_hash::bucket_entry)`) + * and `tsl::rh::power_of_two_growth_policy` is used, the hash will be stored + * even if `StoreHash` is false so that we can speed-up the rehash (but it will + * not be used on lookups unless `StoreHash` is true). + * + * `GrowthPolicy` defines how the map grows and consequently how a hash value is + * mapped to a bucket. By default the map uses + * `tsl::rh::power_of_two_growth_policy`. This policy keeps the number of + * buckets to a power of two and uses a mask to map the hash to a bucket instead + * of the slow modulo. Other growth policies are available and you may define + * your own growth policy, check `tsl::rh::power_of_two_growth_policy` for the + * interface. + * + * `std::pair` must be swappable. + * + * `Key` and `T` must be copy and/or move constructible. + * + * If the destructor of `Key` or `T` throws an exception, the behaviour of the + * class is undefined. + * + * Iterators invalidation: + * - clear, operator=, reserve, rehash: always invalidate the iterators. + * - insert, emplace, emplace_hint, operator[]: if there is an effective + * insert, invalidate the iterators. + * - erase: always invalidate the iterators. + */ +template , + class KeyEqual = std::equal_to, + class Allocator = std::allocator>, + bool StoreHash = false, + class GrowthPolicy = tsl::rh::power_of_two_growth_policy<2>> +class robin_map { + private: + template + using has_is_transparent = tsl::detail_robin_hash::has_is_transparent; + + class KeySelect { + public: + using key_type = Key; + + const key_type& operator()( + const std::pair& key_value) const noexcept { + return key_value.first; + } + + key_type& operator()(std::pair& key_value) noexcept { + return key_value.first; + } + }; + + class ValueSelect { + public: + using value_type = T; + + const value_type& operator()( + const std::pair& key_value) const noexcept { + return key_value.second; + } + + value_type& operator()(std::pair& key_value) noexcept { + return key_value.second; + } + }; + + using ht = detail_robin_hash::robin_hash, KeySelect, + ValueSelect, Hash, KeyEqual, + Allocator, StoreHash, GrowthPolicy>; + + public: + using key_type = typename ht::key_type; + using mapped_type = T; + using value_type = typename ht::value_type; + using size_type = typename ht::size_type; + using difference_type = typename ht::difference_type; + using hasher = typename ht::hasher; + using key_equal = typename ht::key_equal; + using allocator_type = typename ht::allocator_type; + using reference = typename ht::reference; + using const_reference = typename ht::const_reference; + using pointer = typename ht::pointer; + using const_pointer = typename ht::const_pointer; + using iterator = typename ht::iterator; + using const_iterator = typename ht::const_iterator; + + public: + /* + * Constructors + */ + robin_map() : robin_map(ht::DEFAULT_INIT_BUCKETS_SIZE) {} + + explicit robin_map(size_type bucket_count, const Hash& hash = Hash(), + const KeyEqual& equal = KeyEqual(), + const Allocator& alloc = Allocator()) + : m_ht(bucket_count, hash, equal, alloc) {} + + robin_map(size_type bucket_count, const Allocator& alloc) + : robin_map(bucket_count, Hash(), KeyEqual(), alloc) {} + + robin_map(size_type bucket_count, const Hash& hash, const Allocator& alloc) + : robin_map(bucket_count, hash, KeyEqual(), alloc) {} + + explicit robin_map(const Allocator& alloc) + : robin_map(ht::DEFAULT_INIT_BUCKETS_SIZE, alloc) {} + + template + robin_map(InputIt first, InputIt last, + size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE, + const Hash& hash = Hash(), const KeyEqual& equal = KeyEqual(), + const Allocator& alloc = Allocator()) + : robin_map(bucket_count, hash, equal, alloc) { + insert(first, last); + } + + template + robin_map(InputIt first, InputIt last, size_type bucket_count, + const Allocator& alloc) + : robin_map(first, last, bucket_count, Hash(), KeyEqual(), alloc) {} + + template + robin_map(InputIt first, InputIt last, size_type bucket_count, + const Hash& hash, const Allocator& alloc) + : robin_map(first, last, bucket_count, hash, KeyEqual(), alloc) {} + + robin_map(std::initializer_list init, + size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE, + const Hash& hash = Hash(), const KeyEqual& equal = KeyEqual(), + const Allocator& alloc = Allocator()) + : robin_map(init.begin(), init.end(), bucket_count, hash, equal, alloc) {} + + robin_map(std::initializer_list init, size_type bucket_count, + const Allocator& alloc) + : robin_map(init.begin(), init.end(), bucket_count, Hash(), KeyEqual(), + alloc) {} + + robin_map(std::initializer_list init, size_type bucket_count, + const Hash& hash, const Allocator& alloc) + : robin_map(init.begin(), init.end(), bucket_count, hash, KeyEqual(), + alloc) {} + + robin_map& operator=(std::initializer_list ilist) { + m_ht.clear(); + + m_ht.reserve(ilist.size()); + m_ht.insert(ilist.begin(), ilist.end()); + + return *this; + } + + allocator_type get_allocator() const { return m_ht.get_allocator(); } + + /* + * Iterators + */ + iterator begin() noexcept { return m_ht.begin(); } + const_iterator begin() const noexcept { return m_ht.begin(); } + const_iterator cbegin() const noexcept { return m_ht.cbegin(); } + + iterator end() noexcept { return m_ht.end(); } + const_iterator end() const noexcept { return m_ht.end(); } + const_iterator cend() const noexcept { return m_ht.cend(); } + + /* + * Capacity + */ + bool empty() const noexcept { return m_ht.empty(); } + size_type size() const noexcept { return m_ht.size(); } + size_type max_size() const noexcept { return m_ht.max_size(); } + + /* + * Modifiers + */ + void clear() noexcept { m_ht.clear(); } + + std::pair insert(const value_type& value) { + return m_ht.insert(value); + } + + template ::value>::type* = nullptr> + std::pair insert(P&& value) { + return m_ht.emplace(std::forward

(value)); + } + + std::pair insert(value_type&& value) { + return m_ht.insert(std::move(value)); + } + + iterator insert(const_iterator hint, const value_type& value) { + return m_ht.insert_hint(hint, value); + } + + template ::value>::type* = nullptr> + iterator insert(const_iterator hint, P&& value) { + return m_ht.emplace_hint(hint, std::forward

(value)); + } + + iterator insert(const_iterator hint, value_type&& value) { + return m_ht.insert_hint(hint, std::move(value)); + } + + template + void insert(InputIt first, InputIt last) { + m_ht.insert(first, last); + } + + void insert(std::initializer_list ilist) { + m_ht.insert(ilist.begin(), ilist.end()); + } + + template + std::pair insert_or_assign(const key_type& k, M&& obj) { + return m_ht.insert_or_assign(k, std::forward(obj)); + } + + template + std::pair insert_or_assign(key_type&& k, M&& obj) { + return m_ht.insert_or_assign(std::move(k), std::forward(obj)); + } + + template + iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj) { + return m_ht.insert_or_assign(hint, k, std::forward(obj)); + } + + template + iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj) { + return m_ht.insert_or_assign(hint, std::move(k), std::forward(obj)); + } + + /** + * Due to the way elements are stored, emplace will need to move or copy the + * key-value once. The method is equivalent to + * insert(value_type(std::forward(args)...)); + * + * Mainly here for compatibility with the std::unordered_map interface. + */ + template + std::pair emplace(Args&&... args) { + return m_ht.emplace(std::forward(args)...); + } + + /** + * Due to the way elements are stored, emplace_hint will need to move or copy + * the key-value once. The method is equivalent to insert(hint, + * value_type(std::forward(args)...)); + * + * Mainly here for compatibility with the std::unordered_map interface. + */ + template + iterator emplace_hint(const_iterator hint, Args&&... args) { + return m_ht.emplace_hint(hint, std::forward(args)...); + } + + template + std::pair try_emplace(const key_type& k, Args&&... args) { + return m_ht.try_emplace(k, std::forward(args)...); + } + + template + std::pair try_emplace(key_type&& k, Args&&... args) { + return m_ht.try_emplace(std::move(k), std::forward(args)...); + } + + template + iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args) { + return m_ht.try_emplace_hint(hint, k, std::forward(args)...); + } + + template + iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args) { + return m_ht.try_emplace_hint(hint, std::move(k), + std::forward(args)...); + } + + iterator erase(iterator pos) { return m_ht.erase(pos); } + iterator erase(const_iterator pos) { return m_ht.erase(pos); } + iterator erase(const_iterator first, const_iterator last) { + return m_ht.erase(first, last); + } + size_type erase(const key_type& key) { return m_ht.erase(key); } + + /** + * Erase the element at position 'pos'. In contrast to the regular erase() + * function, erase_fast() does not return an iterator. This allows it to be + * faster especially in hash tables with a low load factor, where finding the + * next nonempty bucket would be costly. + */ + void erase_fast(iterator pos) { return m_ht.erase_fast(pos); } + + /** + * Use the hash value 'precalculated_hash' instead of hashing the key. The + * hash value should be the same as hash_function()(key). Useful to speed-up + * the lookup to the value if you already have the hash. + */ + size_type erase(const key_type& key, std::size_t precalculated_hash) { + return m_ht.erase(key, precalculated_hash); + } + + /** + * This overload only participates in the overload resolution if the typedef + * KeyEqual::is_transparent exists. If so, K must be hashable and comparable + * to Key. + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if::value>::type* = nullptr> + size_type erase(const K& key) { + return m_ht.erase(key); + } + + /** + * @copydoc erase(const K& key) + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The + * hash value should be the same as hash_function()(key). Useful to speed-up + * the lookup to the value if you already have the hash. + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if::value>::type* = nullptr> + size_type erase(const K& key, std::size_t precalculated_hash) { + return m_ht.erase(key, precalculated_hash); + } + + void swap(robin_map& other) { other.m_ht.swap(m_ht); } + + /* + * Lookup + */ + T& at(const Key& key) { return m_ht.at(key); } + + /** + * Use the hash value 'precalculated_hash' instead of hashing the key. The + * hash value should be the same as hash_function()(key). Useful to speed-up + * the lookup if you already have the hash. + */ + T& at(const Key& key, std::size_t precalculated_hash) { + return m_ht.at(key, precalculated_hash); + } + + const T& at(const Key& key) const { return m_ht.at(key); } + + /** + * @copydoc at(const Key& key, std::size_t precalculated_hash) + */ + const T& at(const Key& key, std::size_t precalculated_hash) const { + return m_ht.at(key, precalculated_hash); + } + + /** + * This overload only participates in the overload resolution if the typedef + * KeyEqual::is_transparent exists. If so, K must be hashable and comparable + * to Key. + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if::value>::type* = nullptr> + T& at(const K& key) { + return m_ht.at(key); + } + + /** + * @copydoc at(const K& key) + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The + * hash value should be the same as hash_function()(key). Useful to speed-up + * the lookup if you already have the hash. + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if::value>::type* = nullptr> + T& at(const K& key, std::size_t precalculated_hash) { + return m_ht.at(key, precalculated_hash); + } + + /** + * @copydoc at(const K& key) + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if::value>::type* = nullptr> + const T& at(const K& key) const { + return m_ht.at(key); + } + + /** + * @copydoc at(const K& key, std::size_t precalculated_hash) + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if::value>::type* = nullptr> + const T& at(const K& key, std::size_t precalculated_hash) const { + return m_ht.at(key, precalculated_hash); + } + + T& operator[](const Key& key) { return m_ht[key]; } + T& operator[](Key&& key) { return m_ht[std::move(key)]; } + + size_type count(const Key& key) const { return m_ht.count(key); } + + /** + * Use the hash value 'precalculated_hash' instead of hashing the key. The + * hash value should be the same as hash_function()(key). Useful to speed-up + * the lookup if you already have the hash. + */ + size_type count(const Key& key, std::size_t precalculated_hash) const { + return m_ht.count(key, precalculated_hash); + } + + /** + * This overload only participates in the overload resolution if the typedef + * KeyEqual::is_transparent exists. If so, K must be hashable and comparable + * to Key. + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if::value>::type* = nullptr> + size_type count(const K& key) const { + return m_ht.count(key); + } + + /** + * @copydoc count(const K& key) const + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The + * hash value should be the same as hash_function()(key). Useful to speed-up + * the lookup if you already have the hash. + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if::value>::type* = nullptr> + size_type count(const K& key, std::size_t precalculated_hash) const { + return m_ht.count(key, precalculated_hash); + } + + iterator find(const Key& key) { return m_ht.find(key); } + + /** + * Use the hash value 'precalculated_hash' instead of hashing the key. The + * hash value should be the same as hash_function()(key). Useful to speed-up + * the lookup if you already have the hash. + */ + iterator find(const Key& key, std::size_t precalculated_hash) { + return m_ht.find(key, precalculated_hash); + } + + const_iterator find(const Key& key) const { return m_ht.find(key); } + + /** + * @copydoc find(const Key& key, std::size_t precalculated_hash) + */ + const_iterator find(const Key& key, std::size_t precalculated_hash) const { + return m_ht.find(key, precalculated_hash); + } + + /** + * This overload only participates in the overload resolution if the typedef + * KeyEqual::is_transparent exists. If so, K must be hashable and comparable + * to Key. + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if::value>::type* = nullptr> + iterator find(const K& key) { + return m_ht.find(key); + } + + /** + * @copydoc find(const K& key) + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The + * hash value should be the same as hash_function()(key). Useful to speed-up + * the lookup if you already have the hash. + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if::value>::type* = nullptr> + iterator find(const K& key, std::size_t precalculated_hash) { + return m_ht.find(key, precalculated_hash); + } + + /** + * @copydoc find(const K& key) + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if::value>::type* = nullptr> + const_iterator find(const K& key) const { + return m_ht.find(key); + } + + /** + * @copydoc find(const K& key) + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The + * hash value should be the same as hash_function()(key). Useful to speed-up + * the lookup if you already have the hash. + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if::value>::type* = nullptr> + const_iterator find(const K& key, std::size_t precalculated_hash) const { + return m_ht.find(key, precalculated_hash); + } + + bool contains(const Key& key) const { return m_ht.contains(key); } + + /** + * Use the hash value 'precalculated_hash' instead of hashing the key. The + * hash value should be the same as hash_function()(key). Useful to speed-up + * the lookup if you already have the hash. + */ + bool contains(const Key& key, std::size_t precalculated_hash) const { + return m_ht.contains(key, precalculated_hash); + } + + /** + * This overload only participates in the overload resolution if the typedef + * KeyEqual::is_transparent exists. If so, K must be hashable and comparable + * to Key. + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if::value>::type* = nullptr> + bool contains(const K& key) const { + return m_ht.contains(key); + } + + /** + * @copydoc contains(const K& key) const + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The + * hash value should be the same as hash_function()(key). Useful to speed-up + * the lookup if you already have the hash. + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if::value>::type* = nullptr> + bool contains(const K& key, std::size_t precalculated_hash) const { + return m_ht.contains(key, precalculated_hash); + } + + std::pair equal_range(const Key& key) { + return m_ht.equal_range(key); + } + + /** + * Use the hash value 'precalculated_hash' instead of hashing the key. The + * hash value should be the same as hash_function()(key). Useful to speed-up + * the lookup if you already have the hash. + */ + std::pair equal_range(const Key& key, + std::size_t precalculated_hash) { + return m_ht.equal_range(key, precalculated_hash); + } + + std::pair equal_range(const Key& key) const { + return m_ht.equal_range(key); + } + + /** + * @copydoc equal_range(const Key& key, std::size_t precalculated_hash) + */ + std::pair equal_range( + const Key& key, std::size_t precalculated_hash) const { + return m_ht.equal_range(key, precalculated_hash); + } + + /** + * This overload only participates in the overload resolution if the typedef + * KeyEqual::is_transparent exists. If so, K must be hashable and comparable + * to Key. + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if::value>::type* = nullptr> + std::pair equal_range(const K& key) { + return m_ht.equal_range(key); + } + + /** + * @copydoc equal_range(const K& key) + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The + * hash value should be the same as hash_function()(key). Useful to speed-up + * the lookup if you already have the hash. + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if::value>::type* = nullptr> + std::pair equal_range(const K& key, + std::size_t precalculated_hash) { + return m_ht.equal_range(key, precalculated_hash); + } + + /** + * @copydoc equal_range(const K& key) + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if::value>::type* = nullptr> + std::pair equal_range(const K& key) const { + return m_ht.equal_range(key); + } + + /** + * @copydoc equal_range(const K& key, std::size_t precalculated_hash) + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if::value>::type* = nullptr> + std::pair equal_range( + const K& key, std::size_t precalculated_hash) const { + return m_ht.equal_range(key, precalculated_hash); + } + + /* + * Bucket interface + */ + size_type bucket_count() const { return m_ht.bucket_count(); } + size_type max_bucket_count() const { return m_ht.max_bucket_count(); } + + /* + * Hash policy + */ + float load_factor() const { return m_ht.load_factor(); } + + float min_load_factor() const { return m_ht.min_load_factor(); } + float max_load_factor() const { return m_ht.max_load_factor(); } + + /** + * Set the `min_load_factor` to `ml`. When the `load_factor` of the map goes + * below `min_load_factor` after some erase operations, the map will be + * shrunk when an insertion occurs. The erase method itself never shrinks + * the map. + * + * The default value of `min_load_factor` is 0.0f, the map never shrinks by + * default. + */ + void min_load_factor(float ml) { m_ht.min_load_factor(ml); } + void max_load_factor(float ml) { m_ht.max_load_factor(ml); } + + void rehash(size_type count_) { m_ht.rehash(count_); } + void reserve(size_type count_) { m_ht.reserve(count_); } + + /* + * Observers + */ + hasher hash_function() const { return m_ht.hash_function(); } + key_equal key_eq() const { return m_ht.key_eq(); } + + /* + * Other + */ + + /** + * Convert a const_iterator to an iterator. + */ + iterator mutable_iterator(const_iterator pos) { + return m_ht.mutable_iterator(pos); + } + + /** + * Serialize the map through the `serializer` parameter. + * + * The `serializer` parameter must be a function object that supports the + * following call: + * - `template void operator()(const U& value);` where the types + * `std::int16_t`, `std::uint32_t`, `std::uint64_t`, `float` and + * `std::pair` must be supported for U. + * + * The implementation leaves binary compatibility (endianness, IEEE 754 for + * floats, ...) of the types it serializes in the hands of the `Serializer` + * function object if compatibility is required. + */ + template + void serialize(Serializer& serializer) const { + m_ht.serialize(serializer); + } + + /** + * Deserialize a previously serialized map through the `deserializer` + * parameter. + * + * The `deserializer` parameter must be a function object that supports the + * following call: + * - `template U operator()();` where the types `std::int16_t`, + * `std::uint32_t`, `std::uint64_t`, `float` and `std::pair` must be + * supported for U. + * + * If the deserialized hash map type is hash compatible with the serialized + * map, the deserialization process can be sped up by setting + * `hash_compatible` to true. To be hash compatible, the Hash, KeyEqual and + * GrowthPolicy must behave the same way than the ones used on the serialized + * map and the StoreHash must have the same value. The `std::size_t` must also + * be of the same size as the one on the platform used to serialize the map. + * If these criteria are not met, the behaviour is undefined with + * `hash_compatible` sets to true. + * + * The behaviour is undefined if the type `Key` and `T` of the `robin_map` are + * not the same as the types used during serialization. + * + * The implementation leaves binary compatibility (endianness, IEEE 754 for + * floats, size of int, ...) of the types it deserializes in the hands of the + * `Deserializer` function object if compatibility is required. + */ + template + static robin_map deserialize(Deserializer& deserializer, + bool hash_compatible = false) { + robin_map map(0); + map.m_ht.deserialize(deserializer, hash_compatible); + + return map; + } + + friend bool operator==(const robin_map& lhs, const robin_map& rhs) { + if (lhs.size() != rhs.size()) { + return false; + } + + for (const auto& element_lhs : lhs) { + const auto it_element_rhs = rhs.find(element_lhs.first); + if (it_element_rhs == rhs.cend() || + element_lhs.second != it_element_rhs->second) { + return false; + } + } + + return true; + } + + friend bool operator!=(const robin_map& lhs, const robin_map& rhs) { + return !operator==(lhs, rhs); + } + + friend void swap(robin_map& lhs, robin_map& rhs) { lhs.swap(rhs); } + + private: + ht m_ht; +}; + +/** + * Same as `tsl::robin_map`. + */ +template , + class KeyEqual = std::equal_to, + class Allocator = std::allocator>, + bool StoreHash = false> +using robin_pg_map = robin_map; + +} // end namespace tsl + +#endif diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/ext/robin_map/include/tsl/robin_set.h b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/ext/robin_map/include/tsl/robin_set.h new file mode 100644 index 0000000..e115007 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/ext/robin_map/include/tsl/robin_set.h @@ -0,0 +1,668 @@ +/** + * MIT License + * + * Copyright (c) 2017 Thibaut Goetghebuer-Planchon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef TSL_ROBIN_SET_H +#define TSL_ROBIN_SET_H + +#include +#include +#include +#include +#include +#include + +#include "robin_hash.h" + +namespace tsl { + +/** + * Implementation of a hash set using open-addressing and the robin hood hashing + * algorithm with backward shift deletion. + * + * For operations modifying the hash set (insert, erase, rehash, ...), the + * strong exception guarantee is only guaranteed when the expression + * `std::is_nothrow_swappable::value && + * std::is_nothrow_move_constructible::value` is true, otherwise if an + * exception is thrown during the swap or the move, the hash set may end up in a + * undefined state. Per the standard a `Key` with a noexcept copy constructor + * and no move constructor also satisfies the + * `std::is_nothrow_move_constructible::value` criterion (and will thus + * guarantee the strong exception for the set). + * + * When `StoreHash` is true, 32 bits of the hash are stored alongside the + * values. It can improve the performance during lookups if the `KeyEqual` + * function takes time (or engenders a cache-miss for example) as we then + * compare the stored hashes before comparing the keys. When + * `tsl::rh::power_of_two_growth_policy` is used as `GrowthPolicy`, it may also + * speed-up the rehash process as we can avoid to recalculate the hash. When it + * is detected that storing the hash will not incur any memory penalty due to + * alignment (i.e. `sizeof(tsl::detail_robin_hash::bucket_entry) == sizeof(tsl::detail_robin_hash::bucket_entry)`) + * and `tsl::rh::power_of_two_growth_policy` is used, the hash will be stored + * even if `StoreHash` is false so that we can speed-up the rehash (but it will + * not be used on lookups unless `StoreHash` is true). + * + * `GrowthPolicy` defines how the set grows and consequently how a hash value is + * mapped to a bucket. By default the set uses + * `tsl::rh::power_of_two_growth_policy`. This policy keeps the number of + * buckets to a power of two and uses a mask to set the hash to a bucket instead + * of the slow modulo. Other growth policies are available and you may define + * your own growth policy, check `tsl::rh::power_of_two_growth_policy` for the + * interface. + * + * `Key` must be swappable. + * + * `Key` must be copy and/or move constructible. + * + * If the destructor of `Key` throws an exception, the behaviour of the class is + * undefined. + * + * Iterators invalidation: + * - clear, operator=, reserve, rehash: always invalidate the iterators. + * - insert, emplace, emplace_hint, operator[]: if there is an effective + * insert, invalidate the iterators. + * - erase: always invalidate the iterators. + */ +template , + class KeyEqual = std::equal_to, + class Allocator = std::allocator, bool StoreHash = false, + class GrowthPolicy = tsl::rh::power_of_two_growth_policy<2>> +class robin_set { + private: + template + using has_is_transparent = tsl::detail_robin_hash::has_is_transparent; + + class KeySelect { + public: + using key_type = Key; + + const key_type& operator()(const Key& key) const noexcept { return key; } + + key_type& operator()(Key& key) noexcept { return key; } + }; + + using ht = detail_robin_hash::robin_hash; + + public: + using key_type = typename ht::key_type; + using value_type = typename ht::value_type; + using size_type = typename ht::size_type; + using difference_type = typename ht::difference_type; + using hasher = typename ht::hasher; + using key_equal = typename ht::key_equal; + using allocator_type = typename ht::allocator_type; + using reference = typename ht::reference; + using const_reference = typename ht::const_reference; + using pointer = typename ht::pointer; + using const_pointer = typename ht::const_pointer; + using iterator = typename ht::iterator; + using const_iterator = typename ht::const_iterator; + + /* + * Constructors + */ + robin_set() : robin_set(ht::DEFAULT_INIT_BUCKETS_SIZE) {} + + explicit robin_set(size_type bucket_count, const Hash& hash = Hash(), + const KeyEqual& equal = KeyEqual(), + const Allocator& alloc = Allocator()) + : m_ht(bucket_count, hash, equal, alloc) {} + + robin_set(size_type bucket_count, const Allocator& alloc) + : robin_set(bucket_count, Hash(), KeyEqual(), alloc) {} + + robin_set(size_type bucket_count, const Hash& hash, const Allocator& alloc) + : robin_set(bucket_count, hash, KeyEqual(), alloc) {} + + explicit robin_set(const Allocator& alloc) + : robin_set(ht::DEFAULT_INIT_BUCKETS_SIZE, alloc) {} + + template + robin_set(InputIt first, InputIt last, + size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE, + const Hash& hash = Hash(), const KeyEqual& equal = KeyEqual(), + const Allocator& alloc = Allocator()) + : robin_set(bucket_count, hash, equal, alloc) { + insert(first, last); + } + + template + robin_set(InputIt first, InputIt last, size_type bucket_count, + const Allocator& alloc) + : robin_set(first, last, bucket_count, Hash(), KeyEqual(), alloc) {} + + template + robin_set(InputIt first, InputIt last, size_type bucket_count, + const Hash& hash, const Allocator& alloc) + : robin_set(first, last, bucket_count, hash, KeyEqual(), alloc) {} + + robin_set(std::initializer_list init, + size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE, + const Hash& hash = Hash(), const KeyEqual& equal = KeyEqual(), + const Allocator& alloc = Allocator()) + : robin_set(init.begin(), init.end(), bucket_count, hash, equal, alloc) {} + + robin_set(std::initializer_list init, size_type bucket_count, + const Allocator& alloc) + : robin_set(init.begin(), init.end(), bucket_count, Hash(), KeyEqual(), + alloc) {} + + robin_set(std::initializer_list init, size_type bucket_count, + const Hash& hash, const Allocator& alloc) + : robin_set(init.begin(), init.end(), bucket_count, hash, KeyEqual(), + alloc) {} + + robin_set& operator=(std::initializer_list ilist) { + m_ht.clear(); + + m_ht.reserve(ilist.size()); + m_ht.insert(ilist.begin(), ilist.end()); + + return *this; + } + + allocator_type get_allocator() const { return m_ht.get_allocator(); } + + /* + * Iterators + */ + iterator begin() noexcept { return m_ht.begin(); } + const_iterator begin() const noexcept { return m_ht.begin(); } + const_iterator cbegin() const noexcept { return m_ht.cbegin(); } + + iterator end() noexcept { return m_ht.end(); } + const_iterator end() const noexcept { return m_ht.end(); } + const_iterator cend() const noexcept { return m_ht.cend(); } + + /* + * Capacity + */ + bool empty() const noexcept { return m_ht.empty(); } + size_type size() const noexcept { return m_ht.size(); } + size_type max_size() const noexcept { return m_ht.max_size(); } + + /* + * Modifiers + */ + void clear() noexcept { m_ht.clear(); } + + std::pair insert(const value_type& value) { + return m_ht.insert(value); + } + + std::pair insert(value_type&& value) { + return m_ht.insert(std::move(value)); + } + + iterator insert(const_iterator hint, const value_type& value) { + return m_ht.insert_hint(hint, value); + } + + iterator insert(const_iterator hint, value_type&& value) { + return m_ht.insert_hint(hint, std::move(value)); + } + + template + void insert(InputIt first, InputIt last) { + m_ht.insert(first, last); + } + + void insert(std::initializer_list ilist) { + m_ht.insert(ilist.begin(), ilist.end()); + } + + /** + * Due to the way elements are stored, emplace will need to move or copy the + * key-value once. The method is equivalent to + * insert(value_type(std::forward(args)...)); + * + * Mainly here for compatibility with the std::unordered_map interface. + */ + template + std::pair emplace(Args&&... args) { + return m_ht.emplace(std::forward(args)...); + } + + /** + * Due to the way elements are stored, emplace_hint will need to move or copy + * the key-value once. The method is equivalent to insert(hint, + * value_type(std::forward(args)...)); + * + * Mainly here for compatibility with the std::unordered_map interface. + */ + template + iterator emplace_hint(const_iterator hint, Args&&... args) { + return m_ht.emplace_hint(hint, std::forward(args)...); + } + + iterator erase(iterator pos) { return m_ht.erase(pos); } + iterator erase(const_iterator pos) { return m_ht.erase(pos); } + iterator erase(const_iterator first, const_iterator last) { + return m_ht.erase(first, last); + } + size_type erase(const key_type& key) { return m_ht.erase(key); } + + /** + * Erase the element at position 'pos'. In contrast to the regular erase() + * function, erase_fast() does not return an iterator. This allows it to be + * faster especially in hash sets with a low load factor, where finding the + * next nonempty bucket would be costly. + */ + void erase_fast(iterator pos) { return m_ht.erase_fast(pos); } + + /** + * Use the hash value 'precalculated_hash' instead of hashing the key. The + * hash value should be the same as hash_function()(key). Useful to speed-up + * the lookup to the value if you already have the hash. + */ + size_type erase(const key_type& key, std::size_t precalculated_hash) { + return m_ht.erase(key, precalculated_hash); + } + + /** + * This overload only participates in the overload resolution if the typedef + * KeyEqual::is_transparent exists. If so, K must be hashable and comparable + * to Key. + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if::value>::type* = nullptr> + size_type erase(const K& key) { + return m_ht.erase(key); + } + + /** + * @copydoc erase(const K& key) + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The + * hash value should be the same as hash_function()(key). Useful to speed-up + * the lookup to the value if you already have the hash. + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if::value>::type* = nullptr> + size_type erase(const K& key, std::size_t precalculated_hash) { + return m_ht.erase(key, precalculated_hash); + } + + void swap(robin_set& other) { other.m_ht.swap(m_ht); } + + /* + * Lookup + */ + size_type count(const Key& key) const { return m_ht.count(key); } + + /** + * Use the hash value 'precalculated_hash' instead of hashing the key. The + * hash value should be the same as hash_function()(key). Useful to speed-up + * the lookup if you already have the hash. + */ + size_type count(const Key& key, std::size_t precalculated_hash) const { + return m_ht.count(key, precalculated_hash); + } + + /** + * This overload only participates in the overload resolution if the typedef + * KeyEqual::is_transparent exists. If so, K must be hashable and comparable + * to Key. + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if::value>::type* = nullptr> + size_type count(const K& key) const { + return m_ht.count(key); + } + + /** + * @copydoc count(const K& key) const + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The + * hash value should be the same as hash_function()(key). Useful to speed-up + * the lookup if you already have the hash. + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if::value>::type* = nullptr> + size_type count(const K& key, std::size_t precalculated_hash) const { + return m_ht.count(key, precalculated_hash); + } + + iterator find(const Key& key) { return m_ht.find(key); } + + /** + * Use the hash value 'precalculated_hash' instead of hashing the key. The + * hash value should be the same as hash_function()(key). Useful to speed-up + * the lookup if you already have the hash. + */ + iterator find(const Key& key, std::size_t precalculated_hash) { + return m_ht.find(key, precalculated_hash); + } + + const_iterator find(const Key& key) const { return m_ht.find(key); } + + /** + * @copydoc find(const Key& key, std::size_t precalculated_hash) + */ + const_iterator find(const Key& key, std::size_t precalculated_hash) const { + return m_ht.find(key, precalculated_hash); + } + + /** + * This overload only participates in the overload resolution if the typedef + * KeyEqual::is_transparent exists. If so, K must be hashable and comparable + * to Key. + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if::value>::type* = nullptr> + iterator find(const K& key) { + return m_ht.find(key); + } + + /** + * @copydoc find(const K& key) + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The + * hash value should be the same as hash_function()(key). Useful to speed-up + * the lookup if you already have the hash. + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if::value>::type* = nullptr> + iterator find(const K& key, std::size_t precalculated_hash) { + return m_ht.find(key, precalculated_hash); + } + + /** + * @copydoc find(const K& key) + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if::value>::type* = nullptr> + const_iterator find(const K& key) const { + return m_ht.find(key); + } + + /** + * @copydoc find(const K& key) + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The + * hash value should be the same as hash_function()(key). Useful to speed-up + * the lookup if you already have the hash. + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if::value>::type* = nullptr> + const_iterator find(const K& key, std::size_t precalculated_hash) const { + return m_ht.find(key, precalculated_hash); + } + + bool contains(const Key& key) const { return m_ht.contains(key); } + + /** + * Use the hash value 'precalculated_hash' instead of hashing the key. The + * hash value should be the same as hash_function()(key). Useful to speed-up + * the lookup if you already have the hash. + */ + bool contains(const Key& key, std::size_t precalculated_hash) const { + return m_ht.contains(key, precalculated_hash); + } + + /** + * This overload only participates in the overload resolution if the typedef + * KeyEqual::is_transparent exists. If so, K must be hashable and comparable + * to Key. + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if::value>::type* = nullptr> + bool contains(const K& key) const { + return m_ht.contains(key); + } + + /** + * @copydoc contains(const K& key) const + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The + * hash value should be the same as hash_function()(key). Useful to speed-up + * the lookup if you already have the hash. + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if::value>::type* = nullptr> + bool contains(const K& key, std::size_t precalculated_hash) const { + return m_ht.contains(key, precalculated_hash); + } + + std::pair equal_range(const Key& key) { + return m_ht.equal_range(key); + } + + /** + * Use the hash value 'precalculated_hash' instead of hashing the key. The + * hash value should be the same as hash_function()(key). Useful to speed-up + * the lookup if you already have the hash. + */ + std::pair equal_range(const Key& key, + std::size_t precalculated_hash) { + return m_ht.equal_range(key, precalculated_hash); + } + + std::pair equal_range(const Key& key) const { + return m_ht.equal_range(key); + } + + /** + * @copydoc equal_range(const Key& key, std::size_t precalculated_hash) + */ + std::pair equal_range( + const Key& key, std::size_t precalculated_hash) const { + return m_ht.equal_range(key, precalculated_hash); + } + + /** + * This overload only participates in the overload resolution if the typedef + * KeyEqual::is_transparent exists. If so, K must be hashable and comparable + * to Key. + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if::value>::type* = nullptr> + std::pair equal_range(const K& key) { + return m_ht.equal_range(key); + } + + /** + * @copydoc equal_range(const K& key) + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The + * hash value should be the same as hash_function()(key). Useful to speed-up + * the lookup if you already have the hash. + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if::value>::type* = nullptr> + std::pair equal_range(const K& key, + std::size_t precalculated_hash) { + return m_ht.equal_range(key, precalculated_hash); + } + + /** + * @copydoc equal_range(const K& key) + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if::value>::type* = nullptr> + std::pair equal_range(const K& key) const { + return m_ht.equal_range(key); + } + + /** + * @copydoc equal_range(const K& key, std::size_t precalculated_hash) + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if::value>::type* = nullptr> + std::pair equal_range( + const K& key, std::size_t precalculated_hash) const { + return m_ht.equal_range(key, precalculated_hash); + } + + /* + * Bucket interface + */ + size_type bucket_count() const { return m_ht.bucket_count(); } + size_type max_bucket_count() const { return m_ht.max_bucket_count(); } + + /* + * Hash policy + */ + float load_factor() const { return m_ht.load_factor(); } + + float min_load_factor() const { return m_ht.min_load_factor(); } + float max_load_factor() const { return m_ht.max_load_factor(); } + + /** + * Set the `min_load_factor` to `ml`. When the `load_factor` of the set goes + * below `min_load_factor` after some erase operations, the set will be + * shrunk when an insertion occurs. The erase method itself never shrinks + * the set. + * + * The default value of `min_load_factor` is 0.0f, the set never shrinks by + * default. + */ + void min_load_factor(float ml) { m_ht.min_load_factor(ml); } + void max_load_factor(float ml) { m_ht.max_load_factor(ml); } + + void rehash(size_type count_) { m_ht.rehash(count_); } + void reserve(size_type count_) { m_ht.reserve(count_); } + + /* + * Observers + */ + hasher hash_function() const { return m_ht.hash_function(); } + key_equal key_eq() const { return m_ht.key_eq(); } + + /* + * Other + */ + + /** + * Convert a const_iterator to an iterator. + */ + iterator mutable_iterator(const_iterator pos) { + return m_ht.mutable_iterator(pos); + } + + friend bool operator==(const robin_set& lhs, const robin_set& rhs) { + if (lhs.size() != rhs.size()) { + return false; + } + + for (const auto& element_lhs : lhs) { + const auto it_element_rhs = rhs.find(element_lhs); + if (it_element_rhs == rhs.cend()) { + return false; + } + } + + return true; + } + + /** + * Serialize the set through the `serializer` parameter. + * + * The `serializer` parameter must be a function object that supports the + * following call: + * - `template void operator()(const U& value);` where the types + * `std::int16_t`, `std::uint32_t`, `std::uint64_t`, `float` and `Key` must be + * supported for U. + * + * The implementation leaves binary compatibility (endianness, IEEE 754 for + * floats, ...) of the types it serializes in the hands of the `Serializer` + * function object if compatibility is required. + */ + template + void serialize(Serializer& serializer) const { + m_ht.serialize(serializer); + } + + /** + * Deserialize a previously serialized set through the `deserializer` + * parameter. + * + * The `deserializer` parameter must be a function object that supports the + * following call: + * - `template U operator()();` where the types `std::int16_t`, + * `std::uint32_t`, `std::uint64_t`, `float` and `Key` must be supported for + * U. + * + * If the deserialized hash set type is hash compatible with the serialized + * set, the deserialization process can be sped up by setting + * `hash_compatible` to true. To be hash compatible, the Hash, KeyEqual and + * GrowthPolicy must behave the same way than the ones used on the serialized + * set and the StoreHash must have the same value. The `std::size_t` must also + * be of the same size as the one on the platform used to serialize the set. + * If these criteria are not met, the behaviour is undefined with + * `hash_compatible` sets to true. + * + * The behaviour is undefined if the type `Key` of the `robin_set` is not the + * same as the type used during serialization. + * + * The implementation leaves binary compatibility (endianness, IEEE 754 for + * floats, size of int, ...) of the types it deserializes in the hands of the + * `Deserializer` function object if compatibility is required. + */ + template + static robin_set deserialize(Deserializer& deserializer, + bool hash_compatible = false) { + robin_set set(0); + set.m_ht.deserialize(deserializer, hash_compatible); + + return set; + } + + friend bool operator!=(const robin_set& lhs, const robin_set& rhs) { + return !operator==(lhs, rhs); + } + + friend void swap(robin_set& lhs, robin_set& rhs) { lhs.swap(rhs); } + + private: + ht m_ht; +}; + +/** + * Same as `tsl::robin_set`. + */ +template , + class KeyEqual = std::equal_to, + class Allocator = std::allocator, bool StoreHash = false> +using robin_pg_set = robin_set; + +} // end namespace tsl + +#endif diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/eigen/dense.h b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/eigen/dense.h new file mode 100644 index 0000000..afc943e --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/eigen/dense.h @@ -0,0 +1,486 @@ +/* + nanobind/eigen/dense.h: type casters for dense Eigen + vectors and matrices + + Copyright (c) 2023 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include +#include + +static_assert(EIGEN_VERSION_AT_LEAST(3, 3, 1), + "Eigen matrix support in nanobind requires Eigen >= 3.3.1"); + +NAMESPACE_BEGIN(NB_NAMESPACE) + +/// Function argument types that are compatible with various array flavors +using DStride = Eigen::Stride; +template using DRef = Eigen::Ref; +template using DMap = Eigen::Map; + +NAMESPACE_BEGIN(detail) + +/// Determine the number of dimensions of the given Eigen type +template +constexpr int ndim_v = bool(T::IsVectorAtCompileTime) ? 1 : 2; + +/// Extract the compile-time strides of the given Eigen type +template struct stride { + using type = Eigen::Stride<0, 0>; +}; + +template struct stride> { + using type = StrideType; +}; + +template struct stride> { + using type = StrideType; +}; + +template using stride_t = typename stride::type; + +/** \brief Identify types with a contiguous memory representation. + * + * This includes all specializations of ``Eigen::Matrix``/``Eigen::Array`` and + * certain specializations of ``Eigen::Map`` and ``Eigen::Ref``. Note: Eigen + * interprets a compile-time stride of 0 as contiguous. + */ +template +constexpr bool is_contiguous_v = + (stride_t::InnerStrideAtCompileTime == 0 || + stride_t::InnerStrideAtCompileTime == 1) && + (ndim_v == 1 || stride_t::OuterStrideAtCompileTime == 0 || + (stride_t::OuterStrideAtCompileTime != Eigen::Dynamic && + int(stride_t::OuterStrideAtCompileTime) == int(T::InnerSizeAtCompileTime))); + +/// Identify types with a static or dynamic layout that support contiguous storage +template +constexpr bool can_map_contiguous_memory_v = + (stride_t::InnerStrideAtCompileTime == 0 || + stride_t::InnerStrideAtCompileTime == 1 || + stride_t::InnerStrideAtCompileTime == Eigen::Dynamic) && + (ndim_v == 1 || stride_t::OuterStrideAtCompileTime == 0 || + stride_t::OuterStrideAtCompileTime == Eigen::Dynamic || + int(stride_t::OuterStrideAtCompileTime) == int(T::InnerSizeAtCompileTime)); + +/* This type alias builds the most suitable 'ndarray' for the given Eigen type. + In particular, it + + - matches the underlying scalar type + - matches the number of dimensions (i.e. whether the type is a vector/matrix) + - matches the shape (if the row/column count is known at compile time) + - matches the in-memory ordering when the Eigen type is contiguous. + + This is helpful because type_caster> will then perform the + necessary conversion steps (if given incompatible input) to enable data + exchange with Eigen. + + A limitation of this approach is that ndarray does not support compile-time + strides besides c_contig and f_contig. If an Eigen type requires + non-contiguous strides (at compile-time) and we are given an ndarray with + unsuitable strides (at run-time), type casting will fail. Note, however, that + this is rather unusual, since the default stride type of Eigen::Map requires + contiguous memory, and the one of Eigen::Ref requires a contiguous inner + stride, while handling any outer stride. +*/ + +template +using array_for_eigen_t = ndarray< + Scalar, + numpy, + std::conditional_t< + ndim_v == 1, + shape, + shape>, + std::conditional_t< + is_contiguous_v, + std::conditional_t< + ndim_v == 1 || T::IsRowMajor, + c_contig, + f_contig>, + unused>>; + +/// Any kind of Eigen class +template constexpr bool is_eigen_v = is_base_of_template_v; + +/// Detects Eigen::Array, Eigen::Matrix, etc. +template constexpr bool is_eigen_plain_v = is_base_of_template_v; + +/// Detect Eigen::SparseMatrix +template constexpr bool is_eigen_sparse_v = is_base_of_template_v; + +/// Detects expression templates +template constexpr bool is_eigen_xpr_v = + is_eigen_v && !is_eigen_plain_v && !is_eigen_sparse_v && + !std::is_base_of_v, T>; + +template +struct type_caster && + is_ndarray_scalar_v>> { + using Scalar = typename T::Scalar; + using NDArray = array_for_eigen_t; + using NDArrayCaster = make_caster; + + NB_TYPE_CASTER(T, NDArrayCaster::Name) + + bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept { + // We're in any case making a copy, so non-writable inputs area also okay + using NDArrayConst = array_for_eigen_t; + make_caster caster; + if (!caster.from_python(src, flags, cleanup)) + return false; + + const NDArrayConst &array = caster.value; + if constexpr (ndim_v == 1) + value.resize(array.shape(0)); + else + value.resize(array.shape(0), array.shape(1)); + + // The layout is contiguous & compatible thanks to array_for_eigen_t + memcpy(value.data(), array.data(), array.size() * sizeof(Scalar)); + + return true; + } + + static handle from_cpp(T &&v, rv_policy policy, cleanup_list *cleanup) noexcept { + if (policy == rv_policy::automatic || + policy == rv_policy::automatic_reference) + policy = rv_policy::move; + + return from_cpp((const T &) v, policy, cleanup); + } + + static handle from_cpp(const T &v, rv_policy policy, cleanup_list *cleanup) noexcept { + size_t shape[ndim_v]; + int64_t strides[ndim_v]; + + if constexpr (ndim_v == 1) { + shape[0] = v.size(); + strides[0] = v.innerStride(); + } else { + shape[0] = v.rows(); + shape[1] = v.cols(); + strides[0] = v.rowStride(); + strides[1] = v.colStride(); + } + + void *ptr = (void *) v.data(); + + switch (policy) { + case rv_policy::automatic: + policy = rv_policy::copy; + break; + + case rv_policy::automatic_reference: + policy = rv_policy::reference; + break; + + case rv_policy::move: + // Don't bother moving when the data is static or occupies <1KB + if ((T::SizeAtCompileTime != Eigen::Dynamic || + (size_t) v.size() < (1024 / sizeof(Scalar)))) + policy = rv_policy::copy; + break; + + default: // leave policy unchanged + break; + } + + object owner; + if (policy == rv_policy::move) { + T *temp = new T(std::move(v)); + owner = capsule(temp, [](void *p) noexcept { delete (T *) p; }); + ptr = temp->data(); + policy = rv_policy::reference; + } else if (policy == rv_policy::reference_internal && cleanup->self()) { + owner = borrow(cleanup->self()); + policy = rv_policy::reference; + } + + object o = steal(NDArrayCaster::from_cpp( + NDArray(ptr, ndim_v, shape, owner, strides), + policy, cleanup)); + + return o.release(); + } +}; + +/// Caster for Eigen expression templates +template +struct type_caster && + is_ndarray_scalar_v>> { + using Array = Eigen::Array; + using Caster = make_caster; + static constexpr auto Name = Caster::Name; + template using Cast = T; + template static constexpr bool can_cast() { return true; } + + /// Generating an expression template from a Python object is, of course, not possible + bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept = delete; + + template + static handle from_cpp(T2 &&v, rv_policy policy, cleanup_list *cleanup) noexcept { + return Caster::from_cpp(std::forward(v), policy, cleanup); + } +}; + +/** \brief Type caster for ``Eigen::Map`` + + The ``Eigen::Map<..>`` type exists to efficiently access memory provided by a + caller. Given that, the nanobind type caster refuses to turn incompatible + inputs into a ``Eigen::Map`` when this would require an implicit + conversion. +*/ + +template +struct type_caster, + enable_if_t && + is_ndarray_scalar_v>> { + using Map = Eigen::Map; + using NDArray = + array_for_eigen_t, + const typename Map::Scalar, + typename Map::Scalar>>; + using NDArrayCaster = type_caster; + static constexpr auto Name = NDArrayCaster::Name; + template using Cast = Map; + template static constexpr bool can_cast() { return true; } + + NDArrayCaster caster; + + bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept { + // Disable implicit conversions + return from_python_(src, flags & ~(uint8_t)cast_flags::convert, cleanup); + } + + bool from_python_(handle src, uint8_t flags, cleanup_list* cleanup) noexcept { + if (!caster.from_python(src, flags, cleanup)) + return false; + + // Check for memory layout compatibility of non-contiguous 'Map' types + if constexpr (!is_contiguous_v) { + // Dynamic inner strides support any input, check the fixed case + if constexpr (StrideType::InnerStrideAtCompileTime != Eigen::Dynamic) { + // A compile-time stride of 0 implies "contiguous" .. + int64_t is_expected = StrideType::InnerStrideAtCompileTime == 0 + ? 1 /* .. and equals 1 for the inner stride */ + : StrideType::InnerStrideAtCompileTime, + is_actual = caster.value.stride( + (ndim_v != 1 && T::IsRowMajor) ? 1 : 0); + + if (is_expected != is_actual) + return false; + } + + // Analogous check for the outer strides + if constexpr (ndim_v == 2 && StrideType::OuterStrideAtCompileTime != Eigen::Dynamic) { + int64_t os_expected = StrideType::OuterStrideAtCompileTime == 0 + ? caster.value.shape(T::IsRowMajor ? 1 : 0) + : StrideType::OuterStrideAtCompileTime, + os_actual = caster.value.stride(T::IsRowMajor ? 0 : 1); + + if (os_expected != os_actual) + return false; + } + } + return true; + } + + static handle from_cpp(const Map &v, rv_policy policy, cleanup_list *cleanup) noexcept { + size_t shape[ndim_v]; + int64_t strides[ndim_v]; + + if constexpr (ndim_v == 1) { + shape[0] = v.size(); + strides[0] = v.innerStride(); + } else { + shape[0] = v.rows(); + shape[1] = v.cols(); + strides[0] = v.rowStride(); + strides[1] = v.colStride(); + } + + return NDArrayCaster::from_cpp( + NDArray((void *) v.data(), ndim_v, shape, handle(), strides), + (policy == rv_policy::automatic || + policy == rv_policy::automatic_reference) + ? rv_policy::reference + : policy, + cleanup); + } + + StrideType strides() const { + constexpr int IS = StrideType::InnerStrideAtCompileTime, + OS = StrideType::OuterStrideAtCompileTime; + + int64_t inner = caster.value.stride(0), + outer; + + if constexpr (ndim_v == 1) + outer = caster.value.shape(0); + else + outer = caster.value.stride(1); + + (void) inner; (void) outer; + if constexpr (ndim_v == 2 && T::IsRowMajor) + std::swap(inner, outer); + + // Eigen may expect a stride of 0 to avoid an assertion failure + if constexpr (IS == 0) + inner = 0; + + if constexpr (OS == 0) + outer = 0; + + if constexpr (std::is_same_v>) + return StrideType(inner); + else if constexpr (std::is_same_v>) + return StrideType(outer); + else + return StrideType(outer, inner); + } + + operator Map() { + NDArray &t = caster.value; + if constexpr (ndim_v == 1) + return Map(t.data(), t.shape(0), strides()); + else + return Map(t.data(), t.shape(0), t.shape(1), strides()); + } +}; + +/** \brief Caster for Eigen::Ref + + Compared to the ``Eigen::Map`` type caster above, the reference caster + accepts a wider set of inputs when it is used in *constant reference* mode + (i.e., ``Eigen::Ref``). In this case, it performs stride conversions + (except for unusual non-contiguous strides) as well as conversions of the + underlying scalar type (if implicit conversions are enabled). + + For non-constant references, the caster matches that of ``Eigen::Map`` and + requires an input with the expected layout (so that changes can propagate to + the caller). +*/ +template +struct type_caster, + enable_if_t && + is_ndarray_scalar_v>> { + using Ref = Eigen::Ref; + + /// Potentially convert strides/dtype when casting constant references + static constexpr bool MaybeConvert = + std::is_const_v && + // Restrict to contiguous 'T' (limitation in Eigen, see PR #215) + can_map_contiguous_memory_v; + + using NDArray = + array_for_eigen_t, + const typename Ref::Scalar, + typename Ref::Scalar>>; + using NDArrayCaster = type_caster; + + /// Eigen::Map caster with fixed strides + using Map = Eigen::Map; + using MapCaster = make_caster; + + // Extended version taking arbitrary strides + using DMap = Eigen::Map; + using DMapCaster = make_caster; + + /** + * The constructor of ``Ref`` uses one of two strategies + * depending on the input. It may either + * + * 1. Create a copy ``Ref::m_object`` (owned by Ref), or + * 2. Reference the existing input (non-owned). + * + * When the value below is ``true``, then it is guaranteed that + * ``Ref()`` owns the underlying data. + */ + static constexpr bool DMapConstructorOwnsData = + !Eigen::internal::traits::template match::type::value; + + static constexpr auto Name = + const_name(DMapCaster::Name, MapCaster::Name); + + template using Cast = Ref; + template static constexpr bool can_cast() { return true; } + + MapCaster caster; + struct Empty { }; + std::conditional_t dcaster; + + bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept { + // Try a direct cast without implicit conversion first + if (caster.from_python(src, flags, cleanup)) + return true; + + // Potentially convert strides/dtype when casting constant references + if constexpr (MaybeConvert) { + /* Generating an implicit copy requires some object to assume + ownership. During a function call, ``dcaster`` can serve that + role (this case is detected by checking whether ``flags`` has + the ``manual`` flag set). When used in other situations (e.g. + ``nb::cast()``), the created ``Eigen::Ref<..>`` must take + ownership of the copy. This is only guranteed to work if + DMapConstructorOwnsData. + + If neither of these is possible, we disable implicit + conversions. */ + + if ((flags & (uint8_t) cast_flags::manual) && + !DMapConstructorOwnsData) + flags &= ~(uint8_t) cast_flags::convert; + + if (dcaster.from_python_(src, flags, cleanup)) + return true; + } + + return false; + } + + static handle from_cpp(const Ref &v, rv_policy policy, cleanup_list *cleanup) noexcept { + // Copied from the Eigen::Map caster + + size_t shape[ndim_v]; + int64_t strides[ndim_v]; + + if constexpr (ndim_v == 1) { + shape[0] = v.size(); + strides[0] = v.innerStride(); + } else { + shape[0] = v.rows(); + shape[1] = v.cols(); + strides[0] = v.rowStride(); + strides[1] = v.colStride(); + } + + return NDArrayCaster::from_cpp( + NDArray((void *) v.data(), ndim_v, shape, handle(), strides), + (policy == rv_policy::automatic || + policy == rv_policy::automatic_reference) + ? rv_policy::reference + : policy, + cleanup); + } + + operator Ref() { + if constexpr (MaybeConvert) { + if (dcaster.caster.value.is_valid()) + return Ref(dcaster.operator DMap()); + } + + return Ref(caster.operator Map()); + } +}; + +NAMESPACE_END(detail) + +NAMESPACE_END(NB_NAMESPACE) diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/eigen/sparse.h b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/eigen/sparse.h new file mode 100644 index 0000000..718fef2 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/eigen/sparse.h @@ -0,0 +1,178 @@ +/* + nanobind/eigen/sparse.h: type casters for sparse Eigen matrices + + Copyright (c) 2023 Henri Menke and Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include +#include +#include + +#include +#include +#include + +NAMESPACE_BEGIN(NB_NAMESPACE) + +NAMESPACE_BEGIN(detail) + +/// Detect Eigen::SparseMatrix +template constexpr bool is_eigen_sparse_matrix_v = + is_eigen_sparse_v && + !std::is_base_of_v, T>; + + +/// Caster for Eigen::SparseMatrix +template struct type_caster>> { + using Scalar = typename T::Scalar; + using StorageIndex = typename T::StorageIndex; + using Index = typename T::Index; + using SparseMap = Eigen::Map; + + static_assert(std::is_same_v>, + "nanobind: Eigen sparse caster only implemented for matrices"); + + static constexpr bool RowMajor = T::IsRowMajor; + + using ScalarNDArray = ndarray>; + using StorageIndexNDArray = ndarray>; + + using ScalarCaster = make_caster; + using StorageIndexCaster = make_caster; + + NB_TYPE_CASTER(T, const_name("scipy.sparse.csr_matrix[", + "scipy.sparse.csc_matrix[") + + make_caster::Name + const_name("]")) + + ScalarCaster data_caster; + StorageIndexCaster indices_caster, indptr_caster; + + bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept { + object obj = borrow(src); + try { + object matrix_type = module_::import_("scipy.sparse").attr(RowMajor ? "csr_matrix" : "csc_matrix"); + if (!obj.type().is(matrix_type)) + obj = matrix_type(obj); + } catch (const python_error &) { + return false; + } + + if (object data_o = obj.attr("data"); !data_caster.from_python(data_o, flags, cleanup)) + return false; + ScalarNDArray& values = data_caster.value; + + if (object indices_o = obj.attr("indices"); !indices_caster.from_python(indices_o, flags, cleanup)) + return false; + StorageIndexNDArray& inner_indices = indices_caster.value; + + if (object indptr_o = obj.attr("indptr"); !indptr_caster.from_python(indptr_o, flags, cleanup)) + return false; + StorageIndexNDArray& outer_indices = indptr_caster.value; + + object shape_o = obj.attr("shape"), nnz_o = obj.attr("nnz"); + Index rows, cols, nnz; + try { + if (len(shape_o) != 2) + return false; + rows = cast(shape_o[0]); + cols = cast(shape_o[1]); + nnz = cast(nnz_o); + } catch (const python_error &) { + return false; + } + + value = SparseMap(rows, cols, nnz, outer_indices.data(), inner_indices.data(), values.data()); + + return true; + } + + static handle from_cpp(T &&v, rv_policy policy, cleanup_list *cleanup) noexcept { + if (policy == rv_policy::automatic || + policy == rv_policy::automatic_reference) + policy = rv_policy::move; + + return from_cpp((const T &) v, policy, cleanup); + } + + static handle from_cpp(const T &v, rv_policy policy, cleanup_list *) noexcept { + if (!v.isCompressed()) { + PyErr_SetString(PyExc_ValueError, + "nanobind: unable to return an Eigen sparse matrix that is not in a compressed format. " + "Please call `.makeCompressed()` before returning the value on the C++ end."); + return handle(); + } + + object matrix_type; + try { + matrix_type = module_::import_("scipy.sparse").attr(RowMajor ? "csr_matrix" : "csc_matrix"); + } catch (python_error &e) { + e.restore(); + return handle(); + } + + const Index rows = v.rows(), cols = v.cols(); + const size_t data_shape[] = { (size_t) v.nonZeros() }; + const size_t outer_indices_shape[] = { (size_t) ((RowMajor ? rows : cols) + 1) }; + + T *src = std::addressof(const_cast(v)); + object owner; + if (policy == rv_policy::move) { + src = new T(std::move(v)); + owner = capsule(src, [](void *p) noexcept { delete (T *) p; }); + } + + ScalarNDArray data(src->valuePtr(), 1, data_shape, owner); + StorageIndexNDArray outer_indices(src->outerIndexPtr(), 1, outer_indices_shape, owner); + StorageIndexNDArray inner_indices(src->innerIndexPtr(), 1, data_shape, owner); + + try { + return matrix_type(nanobind::make_tuple( + std::move(data), std::move(inner_indices), std::move(outer_indices)), + nanobind::make_tuple(rows, cols)) + .release(); + } catch (python_error &e) { + e.restore(); + return handle(); + } + } +}; + + +/// Caster for Eigen::Map, still needs to be implemented. +template +struct type_caster, enable_if_t>> { + using Map = Eigen::Map; + using SparseMatrixCaster = type_caster; + static constexpr auto Name = SparseMatrixCaster::Name; + template using Cast = Map; + template static constexpr bool can_cast() { return true; } + + bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept = delete; + + static handle from_cpp(const Map &v, rv_policy policy, cleanup_list *cleanup) noexcept = delete; +}; + + +/// Caster for Eigen::Ref, still needs to be implemented +template +struct type_caster, enable_if_t>> { + using Ref = Eigen::Ref; + using Map = Eigen::Map; + using MapCaster = make_caster; + static constexpr auto Name = MapCaster::Name; + template using Cast = Ref; + template static constexpr bool can_cast() { return true; } + + bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept = delete; + + static handle from_cpp(const Ref &v, rv_policy policy, cleanup_list *cleanup) noexcept = delete; +}; + +NAMESPACE_END(detail) + +NAMESPACE_END(NB_NAMESPACE) diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/eval.h b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/eval.h new file mode 100644 index 0000000..eb18f8e --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/eval.h @@ -0,0 +1,61 @@ +/* + nanobind/eval.h: Support for evaluating Python expressions and + statements from strings + + Adapted by Nico Schlömer from pybind11's eval.h. + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include + +NAMESPACE_BEGIN(NB_NAMESPACE) + +enum eval_mode { + // Evaluate a string containing an isolated expression + eval_expr = Py_eval_input, + + // Evaluate a string containing a single statement. Returns \c none + eval_single_statement = Py_single_input, + + // Evaluate a string containing a sequence of statement. Returns \c none + eval_statements = Py_file_input +}; + +template +object eval(const str &expr, handle global = handle(), handle local = handle()) { + if (!local.is_valid()) + local = global; + + // This used to be PyRun_String, but that function isn't in the stable ABI. + object codeobj = steal(Py_CompileString(expr.c_str(), "", start)); + if (!codeobj.is_valid()) + raise_python_error(); + + PyObject *result = PyEval_EvalCode(codeobj.ptr(), global.ptr(), local.ptr()); + if (!result) + raise_python_error(); + + return steal(result); +} + +template +object eval(const char (&s)[N], handle global = handle(), handle local = handle()) { + // Support raw string literals by removing common leading whitespace + str expr = (s[0] == '\n') ? str(module_::import_("textwrap").attr("dedent")(s)) : str(s); + return eval(expr, global, local); +} + +inline void exec(const str &expr, handle global = handle(), handle local = handle()) { + eval(expr, global, local); +} + +template +void exec(const char (&s)[N], handle global = handle(), handle local = handle()) { + eval(s, global, local); +} + +NAMESPACE_END(NB_NAMESPACE) diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/intrusive/counter.h b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/intrusive/counter.h new file mode 100644 index 0000000..cb15464 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/intrusive/counter.h @@ -0,0 +1,261 @@ +/* + nanobind/intrusive/counter.h: Intrusive reference counting sample + implementation. + + Intrusive reference counting is a simple solution for various lifetime and + ownership-related issues that can arise in Python bindings of C++ code. The + implementation here represents one of many ways in which intrusive + reference counting can be realized and is included for convenience. + + The code in this file is designed to be truly minimal: it depends neither + on Python, nanobind, nor the STL. This enables its use in small projects + with a 100% optional Python interface. + + Two section of nanobind's documentation discuss intrusive reference + counting in general: + + - https://nanobind.readthedocs.io/en/latest/ownership.html + - https://nanobind.readthedocs.io/en/latest/ownership_adv.html + + Comments below are specific to this sample implementation. + + Copyright (c) 2023 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include + +// Override this definition to specify DLL export/import declarations +#if !defined(NB_INTRUSIVE_EXPORT) +# define NB_INTRUSIVE_EXPORT +#endif + +#if !defined(Py_PYTHON_H) +/* While the implementation below does not directly depend on Python, the + PyObject type occurs in a few function interfaces (in a fully opaque + manner). The lines below forward-declare it. */ +extern "C" { + struct _object; + typedef _object PyObject; +}; +#endif + +#if !defined(NAMESPACE_BEGIN) +# define NAMESPACE_BEGIN(name) namespace name { +#endif + +#if !defined(NAMESPACE_END) +# define NAMESPACE_END(name) } +#endif + +NAMESPACE_BEGIN(nanobind) + +/** \brief Simple intrusive reference counter. + * + * Intrusive reference counting is a simple solution for various lifetime and + * ownership-related issues that can arise in Python bindings of C++ code. The + * implementation here represents one of many ways in which intrusive reference + * counting can be realized and is included for convenience. + * + * The ``intrusive_counter`` class represents an atomic counter that can be + * increased (via ``inc_ref()``) or decreased (via ``dec_ref()``). When the + * counter reaches zero, the object should be deleted, which ``dec_ref()`` + * indicates by returning ``true``. + * + * In addition to this simple counting mechanism, ownership of the object can + * also be transferred to Python (via ``set_self_py()``). In this case, + * subsequent calls to ``inc_ref()`` and ``dec_ref()`` modify the reference + * count of the underlying Python object. The ``intrusive_counter`` class + * supports both cases using only ``sizeof(void*)`` bytes of storage. + * + * To incorporate intrusive reference counting into your own project, you would + * usually add an ``intrusive_counter``-typed member to the base class of an + * object hierarchy and expose it as follows: + * + * ```cpp + * #include + * + * class Object { + * public: + * void inc_ref() noexcept { m_ref_count.inc_ref(); } + * bool dec_ref() noexcept { return m_ref_count.dec_ref(); } + * + * // Important: must declare virtual destructor + * virtual ~Object() = default; + * + * void set_self_py(PyObject *self) noexcept { + * m_ref_count.set_self_py(self); + * } + * + * private: + * nb::intrusive_counter m_ref_count; + * }; + * + * // Convenience function for increasing the reference count of an instance + * inline void inc_ref(Object *o) noexcept { + * if (o) + * o->inc_ref(); + * } + * + * // Convenience function for decreasing the reference count of an instance + * // and potentially deleting it when the count reaches zero + * inline void dec_ref(Object *o) noexcept { + * if (o && o->dec_ref()) + * delete o; + * } + * ``` + * + * Alternatively, you could also inherit from ``intrusive_base``, which obviates + * the need for all of the above declarations: + * + * ```cpp + * class Object : public intrusive_base { + * public: + * // ... + * }; + * ``` + * + * When binding the base class in Python, you must indicate to nanobind that + * this type uses intrusive reference counting and expose the ``set_self_py`` + * member. This must only be done once, as the attribute is automatically + * inherited by subclasses. + * + * ```cpp + * nb::class_( + * m, "Object", + * nb::intrusive_ptr( + * [](Object *o, PyObject *po) noexcept { o->set_self_py(po); })); + * ``` + * + * Also, somewhere in your binding initialization code, you must call + * + * ```cpp + * nb::intrusive_init( + * [](PyObject *o) noexcept { + * nb::gil_scoped_acquire guard; + * Py_INCREF(o); + * }, + * [](PyObject *o) noexcept { + * nb::gil_scoped_acquire guard; + * Py_DECREF(o); + * }); + * ``` + * + * For this all to compile, a single one of your .cpp files must include this + * header file from somewhere as follows: + * + * ```cpp + * #include + * ``` + * + * Calling the ``inc_ref()`` and ``dec_ref()`` members many times throughout + * the code can quickly become tedious. Nanobind also ships with a ``ref`` + * RAII helper class to help with this. + * + * ```cpp + * #include + * + * { + * ref x = new MyObject(); // <-- assigment to ref<..> automatically calls inc_ref() + * x->func(); // ref<..> can be used like a normal pointer + * } // <-- Destruction of ref<..> calls dec_ref(), deleting the instance in this example. + * ``` + * + * When the file ``nanobind/intrusive/ref.h`` is included following + * ``nanobind/nanobind.h``, it also exposes a custom type caster to bind + * functions taking or returning ``ref``-typed values. + */ +struct NB_INTRUSIVE_EXPORT intrusive_counter { +public: + intrusive_counter() noexcept = default; + + // The counter value is not affected by copy/move assignment/construction + intrusive_counter(const intrusive_counter &) noexcept { } + intrusive_counter(intrusive_counter &&) noexcept { } + intrusive_counter &operator=(const intrusive_counter &) noexcept { return *this; } + intrusive_counter &operator=(intrusive_counter &&) noexcept { return *this; } + + /// Increase the object's reference count + void inc_ref() const noexcept; + + /// Decrease the object's reference count, return ``true`` if it should be deallocated + bool dec_ref() const noexcept; + + /// Return the Python object associated with this instance (or NULL) + PyObject *self_py() const noexcept; + + /// Set the Python object associated with this instance + void set_self_py(PyObject *self) noexcept; + +protected: + /** + * \brief Mutable counter. Note that the value ``1`` actually encodes + * a zero reference count (see the file ``counter.inl`` for details). + */ + mutable uintptr_t m_state = 1; +}; + +static_assert( + sizeof(intrusive_counter) == sizeof(void *), + "The intrusive_counter class should always have the same size as a pointer."); + +/// Reference-counted base type of an object hierarchy +class NB_INTRUSIVE_EXPORT intrusive_base { +public: + /// Increase the object's reference count + void inc_ref() const noexcept { m_ref_count.inc_ref(); } + + /// Decrease the object's reference count, return ``true`` if it should be deallocated + bool dec_ref() const noexcept { return m_ref_count.dec_ref(); } + + /// Set the Python object associated with this instance + void set_self_py(PyObject *self) noexcept { m_ref_count.set_self_py(self); } + + /// Return the Python object associated with this instance (or NULL) + PyObject *self_py() const noexcept { return m_ref_count.self_py(); } + + /// Virtual destructor + virtual ~intrusive_base() = default; + +private: + mutable intrusive_counter m_ref_count; +}; + +/** + * \brief Increase the reference count of an intrusively reference-counted + * object ``o`` if ``o`` is non-NULL. + */ +inline void inc_ref(const intrusive_base *o) noexcept { + if (o) + o->inc_ref(); +} + +/** + * \brief Decrease the reference count and potentially delete an intrusively + * reference-counted object ``o`` if ``o`` is non-NULL. + */ +inline void dec_ref(const intrusive_base *o) noexcept { + if (o && o->dec_ref()) + delete o; +} + +/** + * \brief Install Python reference counting handlers + * + * The ``intrusive_counter`` class is designed so that the dependency on Python is + * *optional*: the code compiles in ordinary C++ projects, in which case the + * Python reference counting functionality will simply not be used. + * + * Python binding code must invoke ``intrusive_init`` once to supply two + * functions that increase and decrease the reference count of a Python object, + * while ensuring that the GIL is held. + */ +extern NB_INTRUSIVE_EXPORT +void intrusive_init(void (*intrusive_inc_ref_py)(PyObject *) noexcept, + void (*intrusive_dec_ref_py)(PyObject *) noexcept); + +NAMESPACE_END(nanobind) diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/intrusive/counter.inl b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/intrusive/counter.inl new file mode 100644 index 0000000..faf6a27 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/intrusive/counter.inl @@ -0,0 +1,148 @@ +/* + nanobind/intrusive/counter.inl: Intrusive reference counting sample + implementation; see 'counter.h' for an explanation of the interface. + + Copyright (c) 2023 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#include "counter.h" +#include +#include + +NAMESPACE_BEGIN(nanobind) + +// The code below uses intrinsics for atomic operations. This is not as nice +// and portable as ``std::atomic`` but avoids pulling in large amounts of +// STL header code + +#if !defined(_MSC_VER) +#define NB_ATOMIC_LOAD(ptr) __atomic_load_n(ptr, 0) +#define NB_ATOMIC_STORE(ptr, v) __atomic_store_n(ptr, v, 0) +#define NB_ATOMIC_CMPXCHG(ptr, cmp, xchg) \ + __atomic_compare_exchange_n(ptr, cmp, xchg, true, 0, 0) +#else +extern "C" void *_InterlockedCompareExchangePointer( + void *volatile *Destination, + void *Exchange, void *Comparand); +#pragma intrinsic(_InterlockedCompareExchangePointer) + +#define NB_ATOMIC_LOAD(ptr) *((volatile const uintptr_t *) ptr) +#define NB_ATOMIC_STORE(ptr, v) *((volatile uintptr_t *) ptr) = v; +#define NB_ATOMIC_CMPXCHG(ptr, cmp, xchg) nb_cmpxchg(ptr, cmp, xchg) + +static bool nb_cmpxchg(uintptr_t *ptr, uintptr_t *cmp, uintptr_t xchg) { + uintptr_t cmpv = *cmp; + uintptr_t prev = (uintptr_t) _InterlockedCompareExchangePointer( + (void * volatile *) ptr, (void *) xchg, (void *) cmpv); + if (prev == cmpv) { + return true; + } else { + *cmp = prev; + return false; + } +} +#endif + +static void (*intrusive_inc_ref_py)(PyObject *) noexcept = nullptr, + (*intrusive_dec_ref_py)(PyObject *) noexcept = nullptr; + +void intrusive_init(void (*intrusive_inc_ref_py_)(PyObject *) noexcept, + void (*intrusive_dec_ref_py_)(PyObject *) noexcept) { + intrusive_inc_ref_py = intrusive_inc_ref_py_; + intrusive_dec_ref_py = intrusive_dec_ref_py_; +} + +/** A few implementation details: + * + * The ``intrusive_counter`` constructor sets the ``m_state`` field to ``1``, + * which indicates that the instance is owned by C++. Bits 2..63 of this + * field are used to store the actual reference count value. The + * ``inc_ref()`` and ``dec_ref()`` functions increment or decrement this + * number. When ``dec_ref()`` removes the last reference, the instance + * returns ``true`` to indicate that it should be deallocated using a + * *delete expression* that would typically be handled using a polymorphic + * destructor. + * + * When an class with intrusive reference counting is returned from C++ to + * Python, nanobind will invoke ``set_self_py()``, which hands ownership + * over to Python/nanobind. Any remaining references will be moved from the + * ``m_state`` field to the Python reference count. In this mode, + * ``inc_ref()`` and ``dec_ref()`` wrap Python reference counting + * primitives (``Py_INCREF()`` / ``Py_DECREF()``) which must be made + * available by calling the function ``intrusive_init`` once during module + * initialization. Note that the `m_state` field is also used to store a + * pointer to the `PyObject *`. Python instance pointers are always aligned + * (i.e. bit 1 is zero), which disambiguates between the two possible + * configurations. + */ + +void intrusive_counter::inc_ref() const noexcept { + uintptr_t v = NB_ATOMIC_LOAD(&m_state); + + while (true) { + if (v & 1) { + if (!NB_ATOMIC_CMPXCHG(&m_state, &v, v + 2)) + continue; + } else { + intrusive_inc_ref_py((PyObject *) v); + } + + break; + } +} + +bool intrusive_counter::dec_ref() const noexcept { + uintptr_t v = NB_ATOMIC_LOAD(&m_state); + + while (true) { + if (v & 1) { + if (v == 1) { + fprintf(stderr, + "intrusive_counter::dec_ref(%p): reference count " + "underflow!", (void *) this); + abort(); + } + + if (!NB_ATOMIC_CMPXCHG(&m_state, &v, v - 2)) + continue; + + if (v == 3) + return true; + } else { + intrusive_dec_ref_py((PyObject *) v); + } + + return false; + } +} + +void intrusive_counter::set_self_py(PyObject *o) noexcept { + uintptr_t v = NB_ATOMIC_LOAD(&m_state); + + if (v & 1) { + v >>= 1; + for (uintptr_t i = 0; i < v; ++i) + intrusive_inc_ref_py(o); + + NB_ATOMIC_STORE(&m_state, (uintptr_t) o); + } else { + fprintf(stderr, + "intrusive_counter::set_self_py(%p): a Python object was " + "already present!", (void *) this); + abort(); + } +} + +PyObject *intrusive_counter::self_py() const noexcept { + uintptr_t v = NB_ATOMIC_LOAD(&m_state); + + if (v & 1) + return nullptr; + else + return (PyObject *) v; +} + +NAMESPACE_END(nanobind) diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/intrusive/ref.h b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/intrusive/ref.h new file mode 100644 index 0000000..3f113b6 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/intrusive/ref.h @@ -0,0 +1,153 @@ +/* + nanobind/intrusive/ref.h: This file defines the ``ref`` RAII scoped + reference counting helper class. + + When included following ``nanobind/nanobind.h``, the code below also + exposes a custom type caster to bind functions taking or returning + ``ref``-typed values. + + Copyright (c) 2023 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "counter.h" + +NAMESPACE_BEGIN(nanobind) + +/** + * \brief RAII scoped reference counting helper class + * + * ``ref`` is a simple RAII wrapper class that encapsulates a pointer to an + * instance with intrusive reference counting. + * + * It takes care of increasing and decreasing the reference count as needed and + * deleting the instance when the count reaches zero. + * + * For this to work, compatible functions ``inc_ref()`` and ``dec_ref()`` must + * be defined before including this file. Default implementations for + * subclasses of the type ``intrusive_base`` are already provided as part of the + * file ``counter.h``. + */ +template class ref { +public: + /// Create a null reference + ref() = default; + + /// Construct a reference from a pointer + ref(T *ptr) : m_ptr(ptr) { inc_ref((intrusive_base *) m_ptr); } + + /// Copy a reference, increases the reference count + ref(const ref &r) : m_ptr(r.m_ptr) { inc_ref((intrusive_base *) m_ptr); } + + /// Move a reference witout changing the reference count + ref(ref &&r) noexcept : m_ptr(r.m_ptr) { r.m_ptr = nullptr; } + + /// Destroy this reference + ~ref() { dec_ref((intrusive_base *) m_ptr); } + + /// Move-assign another reference into this one + ref &operator=(ref &&r) noexcept { + dec_ref((intrusive_base *) m_ptr); + m_ptr = r.m_ptr; + r.m_ptr = nullptr; + return *this; + } + + /// Copy-assign another reference into this one + ref &operator=(const ref &r) { + inc_ref((intrusive_base *) r.m_ptr); + dec_ref((intrusive_base *) m_ptr); + m_ptr = r.m_ptr; + return *this; + } + + /// Overwrite this reference with a pointer to another object + ref &operator=(T *ptr) { + inc_ref((intrusive_base *) ptr); + dec_ref((intrusive_base *) m_ptr); + m_ptr = ptr; + return *this; + } + + /// Clear the currently stored reference + void reset() { + dec_ref((intrusive_base *) m_ptr); + m_ptr = nullptr; + } + + /// Compare this reference with another reference + bool operator==(const ref &r) const { return m_ptr == r.m_ptr; } + + /// Compare this reference with another reference + bool operator!=(const ref &r) const { return m_ptr != r.m_ptr; } + + /// Compare this reference with a pointer + bool operator==(const T *ptr) const { return m_ptr == ptr; } + + /// Compare this reference with a pointer + bool operator!=(const T *ptr) const { return m_ptr != ptr; } + + /// Access the object referenced by this reference + T *operator->() { return m_ptr; } + + /// Access the object referenced by this reference + const T *operator->() const { return m_ptr; } + + /// Return a C++ reference to the referenced object + T &operator*() { return *m_ptr; } + + /// Return a const C++ reference to the referenced object + const T &operator*() const { return *m_ptr; } + + /// Return a pointer to the referenced object + operator T *() { return m_ptr; } + + /// Return a const pointer to the referenced object + operator const T *() const { return m_ptr; } + + /// Return a pointer to the referenced object + T *get() { return m_ptr; } + + /// Return a const pointer to the referenced object + const T *get() const { return m_ptr; } + +private: + T *m_ptr = nullptr; +}; + +// Registar a type caster for ``ref`` if nanobind was previously #included +#if defined(NB_VERSION_MAJOR) +NAMESPACE_BEGIN(detail) +template struct type_caster> { + using Caster = make_caster; + static constexpr bool IsClass = true; + NB_TYPE_CASTER(ref, Caster::Name) + + bool from_python(handle src, uint8_t flags, + cleanup_list *cleanup) noexcept { + Caster caster; + if (!caster.from_python(src, flags, cleanup)) + return false; + + value = Value(caster.operator T *()); + return true; + } + + static handle from_cpp(const ref &value, rv_policy policy, + cleanup_list *cleanup) noexcept { + if constexpr (std::is_base_of_v) + if (policy != rv_policy::copy && policy != rv_policy::move && value.get()) + if (PyObject* obj = value->self_py()) + return handle(obj).inc_ref(); + + return Caster::from_cpp(value.get(), policy, cleanup); + } +}; +NAMESPACE_END(detail) +#endif + +NAMESPACE_END(nanobind) diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/make_iterator.h b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/make_iterator.h new file mode 100644 index 0000000..9d54689 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/make_iterator.h @@ -0,0 +1,155 @@ +/* + nanobind/make_iterator.h: nb::make_[key,value_]iterator() + + This implementation is a port from pybind11 with minimal adjustments. + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include +#include + +NAMESPACE_BEGIN(NB_NAMESPACE) +NAMESPACE_BEGIN(detail) + +/* There are a large number of apparently unused template arguments because + each combination requires a separate nb::class_ registration. */ +template +struct iterator_state { + Iterator it; + Sentinel end; + bool first_or_done; +}; + +template +struct remove_rvalue_ref { using type = T; }; +template +struct remove_rvalue_ref { using type = T; }; + +// Note: these helpers take the iterator by non-const reference because some +// iterators in the wild can't be dereferenced when const. +template struct iterator_access { + using result_type = decltype(*std::declval()); + result_type operator()(Iterator &it) const { return *it; } +}; + +template struct iterator_key_access { + // Note double parens in decltype((...)) to capture the value category + // as well. This will be lvalue if the iterator's operator* returned an + // lvalue reference, and xvalue if the iterator's operator* returned an + // object (or rvalue reference but that's unlikely). decltype of an xvalue + // produces T&&, but we want to return a value T from operator() in that + // case, in order to avoid creating a Python object that references a + // C++ temporary. Thus, pass the result through remove_rvalue_ref. + using result_type = typename remove_rvalue_ref< + decltype(((*std::declval()).first))>::type; + result_type operator()(Iterator &it) const { return (*it).first; } +}; + +template struct iterator_value_access { + using result_type = typename remove_rvalue_ref< + decltype(((*std::declval()).second))>::type; + result_type operator()(Iterator &it) const { return (*it).second; } +}; + +template +typed make_iterator_impl(handle scope, const char *name, + Iterator &&first, Sentinel &&last, + Extra &&...extra) { + using State = iterator_state; + + static_assert( + !detail::is_base_caster_v> || + detail::is_copy_constructible_v || + (Policy != rv_policy::automatic_reference && + Policy != rv_policy::copy), + "make_iterator_impl(): the generated __next__ would copy elements, so the " + "element type must be copy-constructible"); + + if (!type().is_valid()) { + class_(scope, name) + .def("__iter__", [](handle h) { return h; }) + .def("__next__", + [](State &s) -> ValueType { + if (!s.first_or_done) + ++s.it; + else + s.first_or_done = false; + + if (s.it == s.end) { + s.first_or_done = true; + throw stop_iteration(); + } + + return Access()(s.it); + }, + std::forward(extra)..., + Policy); + } + + return borrow>(cast(State{ + std::forward(first), std::forward(last), true })); +} + +NAMESPACE_END(detail) + +/// Makes a python iterator from a first and past-the-end C++ InputIterator. +template ::result_type, + typename... Extra> +auto make_iterator(handle scope, const char *name, Iterator &&first, Sentinel &&last, Extra &&...extra) { + return detail::make_iterator_impl, Policy, + Iterator, Sentinel, ValueType, Extra...>( + scope, name, std::forward(first), + std::forward(last), std::forward(extra)...); +} + +/// Makes an iterator over the keys (`.first`) of a iterator over pairs from a +/// first and past-the-end InputIterator. +template ::result_type, + typename... Extra> +auto make_key_iterator(handle scope, const char *name, Iterator &&first, + Sentinel &&last, Extra &&...extra) { + return detail::make_iterator_impl, + Policy, Iterator, Sentinel, KeyType, + Extra...>( + scope, name, std::forward(first), + std::forward(last), std::forward(extra)...); +} + +/// Makes an iterator over the values (`.second`) of a iterator over pairs from a +/// first and past-the-end InputIterator. +template ::result_type, + typename... Extra> +auto make_value_iterator(handle scope, const char *name, Iterator &&first, Sentinel &&last, Extra &&...extra) { + return detail::make_iterator_impl, + Policy, Iterator, Sentinel, ValueType, + Extra...>( + scope, name, std::forward(first), + std::forward(last), std::forward(extra)...); +} + +/// Makes an iterator over values of a container supporting `std::begin()`/`std::end()` +template +auto make_iterator(handle scope, const char *name, Type &value, Extra &&...extra) { + return make_iterator(scope, name, std::begin(value), + std::end(value), + std::forward(extra)...); +} + +NAMESPACE_END(NB_NAMESPACE) diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nanobind.h b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nanobind.h new file mode 100644 index 0000000..7198785 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nanobind.h @@ -0,0 +1,58 @@ +/* + nanobind/nanobind.h: Main include file for core nanobind components + + Copyright (c) 2022 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#if __cplusplus < 201703L && (!defined(_MSVC_LANG) || _MSVC_LANG < 201703L) +# error The nanobind library requires C++17! +#endif + +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable: 4702) // unreachable code (e.g. when binding a noreturn function) + // The next two lines disable warnings that are "just noise" according to Stephan T. Lavavej (a MSFT STL maintainer) +# pragma warning(disable: 4275) // non dll-interface class 'std::exception' used as base for dll-interface class [..] +# pragma warning(disable: 4251) // [..] needs to have a dll-interface to be used by clients of class [..] +#endif + +#define NB_VERSION_MAJOR 2 +#define NB_VERSION_MINOR 2 +#define NB_VERSION_PATCH 0 +#define NB_VERSION_DEV 1 // A value > 0 indicates a development release + +// Core C++ headers that nanobind depends on +#include +#include +#include +#include +#include +#include +#include + +// Implementation. The nb_*.h files should only be included through nanobind.h +#include "nb_python.h" +#include "nb_defs.h" +#include "nb_enums.h" +#include "nb_traits.h" +#include "nb_tuple.h" +#include "nb_lib.h" +#include "nb_descr.h" +#include "nb_types.h" +#include "nb_accessor.h" +#include "nb_error.h" +#include "nb_attr.h" +#include "nb_cast.h" +#include "nb_misc.h" +#include "nb_call.h" +#include "nb_func.h" +#include "nb_class.h" + +#if defined(_MSC_VER) +# pragma warning(pop) +#endif diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_accessor.h b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_accessor.h new file mode 100644 index 0000000..f690360 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_accessor.h @@ -0,0 +1,225 @@ +/* + nanobind/nb_accessor.h: Accessor helper class for .attr(), operator[] + + Copyright (c) 2022 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +NAMESPACE_BEGIN(NB_NAMESPACE) +NAMESPACE_BEGIN(detail) + +template class accessor : public api> { + template friend void nanobind::del(accessor &); + template friend void nanobind::del(accessor &&); +public: + static constexpr auto Name = const_name("object"); + + template + accessor(handle obj, Key &&key) + : m_base(obj.ptr()), m_key(std::move(key)) { } + accessor(const accessor &) = delete; + accessor(accessor &&) = delete; + ~accessor() { + if constexpr (Impl::cache_dec_ref) + Py_XDECREF(m_cache); + } + + template accessor& operator=(T &&value); + + template > = 0> + operator T() const { return borrow(ptr()); } + NB_INLINE PyObject *ptr() const { + Impl::get(m_base, m_key, &m_cache); + return m_cache; + } + NB_INLINE handle base() const { return m_base; } + NB_INLINE object key() const { return steal(Impl::key(m_key)); } + +private: + NB_INLINE void del () { Impl::del(m_base, m_key); } + +private: + PyObject *m_base; + mutable PyObject *m_cache{nullptr}; + typename Impl::key_type m_key; +}; + +struct str_attr { + static constexpr bool cache_dec_ref = true; + using key_type = const char *; + + NB_INLINE static void get(PyObject *obj, const char *key, PyObject **cache) { + detail::getattr_or_raise(obj, key, cache); + } + + NB_INLINE static void set(PyObject *obj, const char *key, PyObject *v) { + setattr(obj, key, v); + } + + NB_INLINE static PyObject *key(const char *key) { + return PyUnicode_InternFromString(key); + } +}; + +struct obj_attr { + static constexpr bool cache_dec_ref = true; + using key_type = handle; + + NB_INLINE static void get(PyObject *obj, handle key, PyObject **cache) { + detail::getattr_or_raise(obj, key.ptr(), cache); + } + + NB_INLINE static void set(PyObject *obj, handle key, PyObject *v) { + setattr(obj, key.ptr(), v); + } + + NB_INLINE static PyObject *key(handle key) { + Py_INCREF(key.ptr()); + return key.ptr(); + } +}; + +struct str_item { + static constexpr bool cache_dec_ref = true; + using key_type = const char *; + + NB_INLINE static void get(PyObject *obj, const char *key, PyObject **cache) { + detail::getitem_or_raise(obj, key, cache); + } + + NB_INLINE static void set(PyObject *obj, const char *key, PyObject *v) { + setitem(obj, key, v); + } + + NB_INLINE static void del(PyObject *obj, const char *key) { + delitem(obj, key); + } +}; + +struct obj_item { + static constexpr bool cache_dec_ref = true; + using key_type = handle; + + NB_INLINE static void get(PyObject *obj, handle key, PyObject **cache) { + detail::getitem_or_raise(obj, key.ptr(), cache); + } + + NB_INLINE static void set(PyObject *obj, handle key, PyObject *v) { + setitem(obj, key.ptr(), v); + } + + NB_INLINE static void del(PyObject *obj, handle key) { + delitem(obj, key.ptr()); + } +}; + +struct num_item { + static constexpr bool cache_dec_ref = true; + using key_type = Py_ssize_t; + + NB_INLINE static void get(PyObject *obj, Py_ssize_t index, PyObject **cache) { + detail::getitem_or_raise(obj, index, cache); + } + + NB_INLINE static void set(PyObject *obj, Py_ssize_t index, PyObject *v) { + setitem(obj, index, v); + } + + NB_INLINE static void del(PyObject *obj, Py_ssize_t index) { + delitem(obj, index); + } +}; + +struct num_item_list { + #if defined(Py_GIL_DISABLED) + static constexpr bool cache_dec_ref = true; + #else + static constexpr bool cache_dec_ref = false; + #endif + + using key_type = Py_ssize_t; + + NB_INLINE static void get(PyObject *obj, Py_ssize_t index, PyObject **cache) { + #if defined(Py_GIL_DISABLED) + *cache = PyList_GetItemRef(obj, index); + #else + *cache = NB_LIST_GET_ITEM(obj, index); + #endif + } + + NB_INLINE static void set(PyObject *obj, Py_ssize_t index, PyObject *v) { +#if defined(Py_LIMITED_API) || defined(NB_FREE_THREADED) + Py_INCREF(v); + PyList_SetItem(obj, index, v); +#else + PyObject *old = NB_LIST_GET_ITEM(obj, index); + Py_INCREF(v); + NB_LIST_SET_ITEM(obj, index, v); + Py_DECREF(old); +#endif + } + + NB_INLINE static void del(PyObject *obj, Py_ssize_t index) { + delitem(obj, index); + } +}; + +struct num_item_tuple { + static constexpr bool cache_dec_ref = false; + using key_type = Py_ssize_t; + + NB_INLINE static void get(PyObject *obj, Py_ssize_t index, PyObject **cache) { + *cache = NB_TUPLE_GET_ITEM(obj, index); + } + + template static void set(Ts...) { + static_assert(false_v, "tuples are immutable!"); + } +}; + +template accessor api::attr(handle key) const { + return { derived(), borrow(key) }; +} + +template accessor api::attr(const char *key) const { + return { derived(), key }; +} + +template accessor api::doc() const { + return { derived(), "__doc__" }; +} + +template accessor api::operator[](handle key) const { + return { derived(), borrow(key) }; +} + +template accessor api::operator[](const char *key) const { + return { derived(), key }; +} + +template +template >> +accessor api::operator[](T index) const { + return { derived(), (Py_ssize_t) index }; +} + +NAMESPACE_END(detail) + +template >> +detail::accessor list::operator[](T index) const { + return { derived(), (Py_ssize_t) index }; +} + +template >> +detail::accessor tuple::operator[](T index) const { + return { derived(), (Py_ssize_t) index }; +} + +template str str::format(Args&&... args) { + return steal( + derived().attr("format")((detail::forward_t) args...).release()); +} + +NAMESPACE_END(NB_NAMESPACE) diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_attr.h b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_attr.h new file mode 100644 index 0000000..0476824 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_attr.h @@ -0,0 +1,433 @@ +/* + nanobind/nb_attr.h: Annotations for function and class declarations + + Copyright (c) 2022 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +NAMESPACE_BEGIN(NB_NAMESPACE) + +struct scope { + PyObject *value; + NB_INLINE scope(handle value) : value(value.ptr()) {} +}; + +struct name { + const char *value; + NB_INLINE name(const char *value) : value(value) {} +}; + +struct arg_v; +struct arg_locked; +struct arg_locked_v; + +// Basic function argument descriptor (no default value, not locked) +struct arg { + NB_INLINE constexpr explicit arg(const char *name = nullptr) : name_(name), signature_(nullptr) { } + + // operator= can be used to provide a default value + template NB_INLINE arg_v operator=(T &&value) const; + + // Mutators that don't change default value or locked state + NB_INLINE arg &noconvert(bool value = true) { + convert_ = !value; + return *this; + } + NB_INLINE arg &none(bool value = true) { + none_ = value; + return *this; + } + NB_INLINE arg &sig(const char *value) { + signature_ = value; + return *this; + } + + // After lock(), this argument is locked + NB_INLINE arg_locked lock(); + + const char *name_, *signature_; + uint8_t convert_{ true }; + bool none_{ false }; +}; + +// Function argument descriptor with default value (not locked) +struct arg_v : arg { + object value; + NB_INLINE arg_v(const arg &base, object &&value) + : arg(base), value(std::move(value)) {} + + private: + // Inherited mutators would slice off the default, and are not generally needed + using arg::noconvert; + using arg::none; + using arg::sig; + using arg::lock; +}; + +// Function argument descriptor that is locked (no default value) +struct arg_locked : arg { + NB_INLINE constexpr explicit arg_locked(const char *name = nullptr) : arg(name) { } + NB_INLINE constexpr explicit arg_locked(const arg &base) : arg(base) { } + + // operator= can be used to provide a default value + template NB_INLINE arg_locked_v operator=(T &&value) const; + + // Mutators must be respecified in order to not slice off the locked status + NB_INLINE arg_locked &noconvert(bool value = true) { + convert_ = !value; + return *this; + } + NB_INLINE arg_locked &none(bool value = true) { + none_ = value; + return *this; + } + NB_INLINE arg_locked &sig(const char *value) { + signature_ = value; + return *this; + } + + // Redundant extra lock() is allowed + NB_INLINE arg_locked &lock() { return *this; } +}; + +// Function argument descriptor that is potentially locked and has a default value +struct arg_locked_v : arg_locked { + object value; + NB_INLINE arg_locked_v(const arg_locked &base, object &&value) + : arg_locked(base), value(std::move(value)) {} + + private: + // Inherited mutators would slice off the default, and are not generally needed + using arg_locked::noconvert; + using arg_locked::none; + using arg_locked::sig; + using arg_locked::lock; +}; + +NB_INLINE arg_locked arg::lock() { return arg_locked{*this}; } + +template struct call_guard { + using type = detail::tuple; +}; + +struct dynamic_attr {}; +struct is_weak_referenceable {}; +struct is_method {}; +struct is_implicit {}; +struct is_operator {}; +struct is_arithmetic {}; +struct is_flag {}; +struct is_final {}; +struct is_generic {}; +struct kw_only {}; +struct lock_self {}; + +template struct keep_alive {}; +template struct supplement {}; +template struct intrusive_ptr { + intrusive_ptr(void (*set_self_py)(T *, PyObject *) noexcept) + : set_self_py(set_self_py) { } + void (*set_self_py)(T *, PyObject *) noexcept; +}; + +struct type_slots { + type_slots (const PyType_Slot *value) : value(value) { } + const PyType_Slot *value; +}; + +struct type_slots_callback { + using cb_t = void (*)(const detail::type_init_data *t, + PyType_Slot *&slots, size_t max_slots) noexcept; + type_slots_callback(cb_t callback) : callback(callback) { } + cb_t callback; +}; + +struct sig { + const char *value; + sig(const char *value) : value(value) { } +}; + +struct is_getter { }; + +NAMESPACE_BEGIN(literals) +constexpr arg operator"" _a(const char *name, size_t) { return arg(name); } +NAMESPACE_END(literals) + +NAMESPACE_BEGIN(detail) + +enum class func_flags : uint32_t { + /* Low 3 bits reserved for return value policy */ + + /// Did the user specify a name for this function, or is it anonymous? + has_name = (1 << 4), + /// Did the user specify a scope in which this function should be installed? + has_scope = (1 << 5), + /// Did the user specify a docstring? + has_doc = (1 << 6), + /// Did the user specify nb::arg/arg_v annotations for all arguments? + has_args = (1 << 7), + /// Does the function signature contain an *args-style argument? + has_var_args = (1 << 8), + /// Does the function signature contain an *kwargs-style argument? + has_var_kwargs = (1 << 9), + /// Is this function a method of a class? + is_method = (1 << 10), + /// Is this function a method called __init__? (automatically generated) + is_constructor = (1 << 11), + /// Can this constructor be used to perform an implicit conversion? + is_implicit = (1 << 12), + /// Is this function an arithmetic operator? + is_operator = (1 << 13), + /// When the function is GCed, do we need to call func_data_prelim::free_capture? + has_free = (1 << 14), + /// Should the func_new() call return a new reference? + return_ref = (1 << 15), + /// Does this overload specify a custom function signature (for docstrings, typing) + has_signature = (1 << 16), + /// Does this function have one or more nb::keep_alive() annotations? + has_keep_alive = (1 << 17) +}; + +enum cast_flags : uint8_t { + // Enable implicit conversions (code assumes this has value 1, don't reorder..) + convert = (1 << 0), + + // Passed to the 'self' argument in a constructor call (__init__) + construct = (1 << 1), + + // Indicates that the function dispatcher should accept 'None' arguments + accepts_none = (1 << 2), + + // Indicates that this cast is performed by nb::cast or nb::try_cast. + // This implies that objects added to the cleanup list may be + // released immediately after the caster's final output value is + // obtained, i.e., before it is used. + manual = (1 << 3) +}; + + +struct arg_data { + const char *name; + const char *signature; + PyObject *name_py; + PyObject *value; + uint8_t flag; +}; + +template struct func_data_prelim { + // A small amount of space to capture data used by the function/closure + void *capture[3]; + + // Callback to clean up the 'capture' field + void (*free_capture)(void *); + + /// Implementation of the function call + PyObject *(*impl)(void *, PyObject **, uint8_t *, rv_policy, + cleanup_list *); + + /// Function signature description + const char *descr; + + /// C++ types referenced by 'descr' + const std::type_info **descr_types; + + /// Supplementary flags + uint32_t flags; + + /// Total number of parameters accepted by the C++ function; nb::args + /// and nb::kwargs parameters are counted as one each. If the + /// 'has_args' flag is set, then there is one arg_data structure + /// for each of these. + uint16_t nargs; + + /// Number of paramters to the C++ function that may be filled from + /// Python positional arguments without additional ceremony. nb::args and + /// nb::kwargs parameters are not counted in this total, nor are any + /// parameters after nb::args or after a nb::kw_only annotation. + /// The parameters counted here may be either named (nb::arg("name")) + /// or unnamed (nb::arg()). If unnamed, they are effectively positional-only. + /// nargs_pos is always <= nargs. + uint16_t nargs_pos; + + // ------- Extra fields ------- + + const char *name; + const char *doc; + PyObject *scope; + + // *WARNING*: nanobind regularly receives requests from users who run it + // through Clang-Tidy, or who compile with increased warnings levels, like + // + // -Wpedantic, -Wcast-qual, -Wsign-conversion, etc. + // + // (i.e., beyond -Wall -Wextra and /W4 that are currently already used) + // + // Their next step is to open a big pull request needed to silence all of + // the resulting messages. This comment is strategically placed here + // because the zero-length array construction below will almost certainly + // be flagged in this process. + // + // My policy on this is as follows: I am always happy to fix issues in the + // codebase. However, many of the resulting change requests are in the + // "ritual purification" category: things that cause churn, decrease + // readability, and which don't fix actual problems. It's a never-ending + // cycle because each new revision of such tooling adds further warnings + // and purification rites. + // + // So just to be clear: I do not wish to pepper this codebase with + // "const_cast" and #pragmas/comments to avoid warnings in external + // tooling just so those users can have a "silent" build. I don't think it + // is reasonable for them to impose their own style on this project. + // + // As a workaround it is likely possible to restrict the scope of style + // checks to particular C++ namespaces or source code locations. +#if defined(_MSC_VER) + // MSVC doesn't support zero-length arrays + arg_data args[Size == 0 ? 1 : Size]; +#else + // GCC and Clang do. + arg_data args[Size]; +#endif +}; + +template +NB_INLINE void func_extra_apply(F &f, const name &name, size_t &) { + f.name = name.value; + f.flags |= (uint32_t) func_flags::has_name; +} + +template +NB_INLINE void func_extra_apply(F &f, const scope &scope, size_t &) { + f.scope = scope.value; + f.flags |= (uint32_t) func_flags::has_scope; +} + +template +NB_INLINE void func_extra_apply(F &f, const sig &s, size_t &) { + f.flags |= (uint32_t) func_flags::has_signature; + f.name = s.value; +} + +template +NB_INLINE void func_extra_apply(F &f, const char *doc, size_t &) { + f.doc = doc; + f.flags |= (uint32_t) func_flags::has_doc; +} + +template +NB_INLINE void func_extra_apply(F &f, is_method, size_t &) { + f.flags |= (uint32_t) func_flags::is_method; +} + +template +NB_INLINE void func_extra_apply(F &, is_getter, size_t &) { } + +template +NB_INLINE void func_extra_apply(F &f, is_implicit, size_t &) { + f.flags |= (uint32_t) func_flags::is_implicit; +} + +template +NB_INLINE void func_extra_apply(F &f, is_operator, size_t &) { + f.flags |= (uint32_t) func_flags::is_operator; +} + +template +NB_INLINE void func_extra_apply(F &f, rv_policy pol, size_t &) { + f.flags = (f.flags & ~0b111) | (uint16_t) pol; +} + +template +NB_INLINE void func_extra_apply(F &, std::nullptr_t, size_t &) { } + +template +NB_INLINE void func_extra_apply(F &f, const arg &a, size_t &index) { + uint8_t flag = 0; + if (a.none_) + flag |= (uint8_t) cast_flags::accepts_none; + if (a.convert_) + flag |= (uint8_t) cast_flags::convert; + + arg_data &arg = f.args[index]; + arg.flag = flag; + arg.name = a.name_; + arg.signature = a.signature_; + arg.value = nullptr; + index++; +} +// arg_locked will select the arg overload; the locking is added statically +// in nb_func.h + +template +NB_INLINE void func_extra_apply(F &f, const arg_v &a, size_t &index) { + arg_data &ad = f.args[index]; + func_extra_apply(f, (const arg &) a, index); + ad.value = a.value.ptr(); +} +template +NB_INLINE void func_extra_apply(F &f, const arg_locked_v &a, size_t &index) { + arg_data &ad = f.args[index]; + func_extra_apply(f, (const arg_locked &) a, index); + ad.value = a.value.ptr(); +} + +template +NB_INLINE void func_extra_apply(F &, kw_only, size_t &) {} + +template +NB_INLINE void func_extra_apply(F &, lock_self, size_t &) {} + +template +NB_INLINE void func_extra_apply(F &, call_guard, size_t &) {} + +template +NB_INLINE void func_extra_apply(F &f, nanobind::keep_alive, size_t &) { + f.flags |= (uint32_t) func_flags::has_keep_alive; +} + +template struct func_extra_info { + using call_guard = void; + static constexpr bool keep_alive = false; + static constexpr size_t nargs_locked = 0; +}; + +template struct func_extra_info + : func_extra_info { }; + +template +struct func_extra_info, Ts...> : func_extra_info { + static_assert(std::is_same_v::call_guard, void>, + "call_guard<> can only be specified once!"); + using call_guard = nanobind::call_guard; +}; + +template +struct func_extra_info, Ts...> : func_extra_info { + static constexpr bool keep_alive = true; +}; + +template +struct func_extra_info : func_extra_info { + static constexpr size_t nargs_locked = 1 + func_extra_info::nargs_locked; +}; + +template +struct func_extra_info : func_extra_info { + static constexpr size_t nargs_locked = 1 + func_extra_info::nargs_locked; +}; + +template +NB_INLINE void process_keep_alive(PyObject **, PyObject *, T *) { } + +template +NB_INLINE void +process_keep_alive(PyObject **args, PyObject *result, + nanobind::keep_alive *) { + keep_alive(Nurse == 0 ? result : args[Nurse - 1], + Patient == 0 ? result : args[Patient - 1]); +} + +NAMESPACE_END(detail) +NAMESPACE_END(NB_NAMESPACE) diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_call.h b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_call.h new file mode 100644 index 0000000..dfbeb45 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_call.h @@ -0,0 +1,150 @@ +/* + nanobind/nb_call.h: Functionality for calling Python functions from C++ + + Copyright (c) 2022 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +NAMESPACE_BEGIN(NB_NAMESPACE) +NAMESPACE_BEGIN(detail) + +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable: 6255) // _alloca indicates failure by raising a stack overflow exception +#endif + +class kwargs_proxy : public handle { +public: + explicit kwargs_proxy(handle h) : handle(h) { } +}; + +class args_proxy : public handle { +public: + explicit args_proxy(handle h) : handle(h) { } + kwargs_proxy operator*() const { return kwargs_proxy(*this); } +}; + +template +args_proxy api::operator*() const { + return args_proxy(derived().ptr()); +} + +/// Implementation detail of api::operator() (call operator) +template +NB_INLINE void call_analyze(size_t &nargs, size_t &nkwargs, const T &value) { + using D = std::decay_t; + static_assert(!std::is_base_of_v, + "nb::arg().lock() may be used only when defining functions, " + "not when calling them"); + + if constexpr (std::is_same_v) + nkwargs++; + else if constexpr (std::is_same_v) + nargs += len(value); + else if constexpr (std::is_same_v) + nkwargs += len(value); + else + nargs += 1; + + (void) nargs; (void) nkwargs; (void) value; +} + +/// Implementation detail of api::operator() (call operator) +template +NB_INLINE void call_init(PyObject **args, PyObject *kwnames, size_t &nargs, + size_t &nkwargs, const size_t kwargs_offset, + T &&value) { + using D = std::decay_t; + + if constexpr (std::is_same_v) { + args[kwargs_offset + nkwargs] = value.value.release().ptr(); + NB_TUPLE_SET_ITEM(kwnames, nkwargs++, + PyUnicode_InternFromString(value.name_)); + } else if constexpr (std::is_same_v) { + for (size_t i = 0, l = len(value); i < l; ++i) + args[nargs++] = borrow(value[i]).release().ptr(); + } else if constexpr (std::is_same_v) { + PyObject *key, *entry; + Py_ssize_t pos = 0; + ft_object_guard guard(value); + while (PyDict_Next(value.ptr(), &pos, &key, &entry)) { + Py_INCREF(key); Py_INCREF(entry); + args[kwargs_offset + nkwargs] = entry; + NB_TUPLE_SET_ITEM(kwnames, nkwargs++, key); + } + } else { + args[nargs++] = + make_caster::from_cpp((forward_t) value, policy, nullptr).ptr(); + } + (void) args; (void) kwnames; (void) nargs; + (void) nkwargs; (void) kwargs_offset; +} + +#define NB_DO_VECTORCALL() \ + PyObject *base, **args_p; \ + if constexpr (method_call) { \ + base = derived().key().release().ptr(); \ + args[0] = derived().base().inc_ref().ptr(); \ + args_p = args; \ + nargs++; \ + } else { \ + base = derived().inc_ref().ptr(); \ + args[0] = nullptr; \ + args_p = args + 1; \ + } \ + nargs |= NB_VECTORCALL_ARGUMENTS_OFFSET; \ + return steal(obj_vectorcall(base, args_p, nargs, kwnames, method_call)) + +template +template +object api::operator()(Args &&...args_) const { + static constexpr bool method_call = + std::is_same_v> || + std::is_same_v>; + + if constexpr (((std::is_same_v || + std::is_same_v || + std::is_same_v) || ...)) { + // Complex call with keyword arguments, *args/**kwargs expansion, etc. + size_t nargs = 0, nkwargs = 0, nargs2 = 0, nkwargs2 = 0; + + // Determine storage requirements for positional and keyword args + (call_analyze(nargs, nkwargs, (const Args &) args_), ...); + + // Allocate memory on the stack + PyObject **args = + (PyObject **) alloca((nargs + nkwargs + 1) * sizeof(PyObject *)); + + PyObject *kwnames = + nkwargs ? PyTuple_New((Py_ssize_t) nkwargs) : nullptr; + + // Fill 'args' and 'kwnames' variables + (call_init(args + 1, kwnames, nargs2, nkwargs2, nargs, + (forward_t) args_), ...); + + NB_DO_VECTORCALL(); + } else { + // Simple version with only positional arguments + PyObject *args[sizeof...(Args) + 1], *kwnames = nullptr; + size_t nargs = 0; + + ((args[1 + nargs++] = + detail::make_caster::from_cpp( + (detail::forward_t) args_, policy, nullptr) + .ptr()), + ...); + + NB_DO_VECTORCALL(); + } +} + +#undef NB_DO_VECTORCALL + +#if defined(_MSC_VER) +# pragma warning(pop) +#endif + +NAMESPACE_END(detail) +NAMESPACE_END(NB_NAMESPACE) diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_cast.h b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_cast.h new file mode 100644 index 0000000..74ad6ce --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_cast.h @@ -0,0 +1,699 @@ +/* + nanobind/nb_cast.h: Type caster interface and essential type casters + + Copyright (c) 2022 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#define NB_TYPE_CASTER(Value_, descr) \ + using Value = Value_; \ + static constexpr auto Name = descr; \ + template using Cast = movable_cast_t; \ + template static constexpr bool can_cast() { return true; } \ + template , Value>> = 0> \ + static handle from_cpp(T_ *p, rv_policy policy, cleanup_list *list) { \ + if (!p) \ + return none().release(); \ + return from_cpp(*p, policy, list); \ + } \ + explicit operator Value*() { return &value; } \ + explicit operator Value&() { return (Value &) value; } \ + explicit operator Value&&() { return (Value &&) value; } \ + Value value; + +#define NB_MAKE_OPAQUE(...) \ + namespace nanobind::detail { \ + template <> class type_caster<__VA_ARGS__> \ + : public type_caster_base<__VA_ARGS__> { }; } + +NAMESPACE_BEGIN(NB_NAMESPACE) +NAMESPACE_BEGIN(detail) + +/** + * Type casters expose a member 'Cast' which users of a type caster must + * query to determine what the caster actually can (and prefers) to produce. + * The convenience alias ``cast_t`` defined below performs this query for a + * given type ``T``. + * + * Often ``cast_t`` is simply equal to ``T`` or ``T&``. More significant + * deviations are also possible, which could be due to one of the following + * two reasons: + * + * 1. Efficiency: most STL type casters create a local copy (``value`` member) + * of the value being cast. The caller should move this value to its + * intended destination instead of making further copies along the way. + * Consequently, ``cast_t>`` yields ``cast_t> + * &&`` to enable such behavior. + * + * 2. STL pairs may contain references, and such pairs aren't + * default-constructible. The STL pair caster therefore cannot create a local + * copy and must construct the pair on the fly, which in turns means that it + * cannot return references. Therefore, ``cast_t&>`` + * yields ``std::pair``. + */ + +/// Ask a type caster what flavors of a type it can actually produce -- may be different from 'T' +template using cast_t = typename make_caster::template Cast; + +/// This is a default choice for the 'Cast' type alias described above. It +/// prefers to return rvalue references to allow the caller to move the object. +template +using movable_cast_t = + std::conditional_t, intrinsic_t *, + std::conditional_t, + intrinsic_t &, intrinsic_t &&>>; + +/// This version is more careful about what the caller actually requested and +/// only moves when this was explicitly requested. It is the default for the +/// base type caster (i.e., types bound via ``nanobind::class_<..>``) +template +using precise_cast_t = + std::conditional_t, intrinsic_t *, + std::conditional_t, + intrinsic_t &&, intrinsic_t &>>; + +/// Many type casters delegate to another caster using the pattern: +/// ~~~ .cc +/// bool from_python(handle src, uint8_t flags, cleanup_list *cl) noexcept { +/// SomeCaster c; +/// if (!c.from_python(src, flags, cl)) return false; +/// /* do something with */ c.operator T(); +/// return true; +/// } +/// ~~~ +/// This function adjusts the flags to avoid issues where the resulting T object +/// refers into storage that will dangle after SomeCaster is destroyed, and +/// causes a static assertion failure if that's not sufficient. Use it like: +/// ~~~ .cc +/// if (!c.from_python(src, flags_for_local_caster(flags), cl)) +/// return false; +/// ~~~ +/// where the template argument T is the type you plan to extract. +template +NB_INLINE uint8_t flags_for_local_caster(uint8_t flags) noexcept { + using Caster = make_caster; + constexpr bool is_ref = std::is_pointer_v || std::is_reference_v; + if constexpr (is_base_caster_v) { + if constexpr (is_ref) { + /* References/pointers to a type produced by implicit conversions + refer to storage owned by the cleanup_list. In a nb::cast() call, + that storage will be released before the reference can be used; + to prevent dangling, don't allow implicit conversions there. */ + if (flags & ((uint8_t) cast_flags::manual)) + flags &= ~((uint8_t) cast_flags::convert); + } + } else { + /* Any pointer produced by a non-base caster will generally point + into storage owned by the caster, which won't live long enough. + Exception: the 'char' caster produces a result that points to + storage owned by the incoming Python 'str' object, so it's OK. */ + static_assert(!is_ref || std::is_same_v || + (std::is_pointer_v && std::is_constructible_v), + "nanobind generally cannot produce objects that " + "contain interior pointers T* (or references T&) if " + "the pointee T is not handled by nanobind's regular " + "class binding mechanism. For example, you can write " + "a function that accepts int*, or std::vector, " + "but not std::vector."); + } + return flags; +} + +template +struct type_caster && !is_std_char_v>> { + NB_INLINE bool from_python(handle src, uint8_t flags, cleanup_list *) noexcept { + if constexpr (std::is_floating_point_v) { + if constexpr (sizeof(T) == 8) + return detail::load_f64(src.ptr(), flags, &value); + else + return detail::load_f32(src.ptr(), flags, &value); + } else { + if constexpr (std::is_signed_v) { + if constexpr (sizeof(T) == 8) + return detail::load_i64(src.ptr(), flags, (int64_t *) &value); + else if constexpr (sizeof(T) == 4) + return detail::load_i32(src.ptr(), flags, (int32_t *) &value); + else if constexpr (sizeof(T) == 2) + return detail::load_i16(src.ptr(), flags, (int16_t *) &value); + else + return detail::load_i8(src.ptr(), flags, (int8_t *) &value); + } else { + if constexpr (sizeof(T) == 8) + return detail::load_u64(src.ptr(), flags, (uint64_t *) &value); + else if constexpr (sizeof(T) == 4) + return detail::load_u32(src.ptr(), flags, (uint32_t *) &value); + else if constexpr (sizeof(T) == 2) + return detail::load_u16(src.ptr(), flags, (uint16_t *) &value); + else + return detail::load_u8(src.ptr(), flags, (uint8_t *) &value); + } + } + } + + NB_INLINE static handle from_cpp(T src, rv_policy, cleanup_list *) noexcept { + if constexpr (std::is_floating_point_v) { + return PyFloat_FromDouble((double) src); + } else { + if constexpr (std::is_signed_v) { + if constexpr (sizeof(T) <= sizeof(long)) + return PyLong_FromLong((long) src); + else + return PyLong_FromLongLong((long long) src); + } else { + if constexpr (sizeof(T) <= sizeof(unsigned long)) + return PyLong_FromUnsignedLong((unsigned long) src); + else + return PyLong_FromUnsignedLongLong((unsigned long long) src); + } + } + } + + NB_TYPE_CASTER(T, const_name>("int", "float")) +}; + +template +struct type_caster>> { + NB_INLINE bool from_python(handle src, uint8_t flags, cleanup_list *) noexcept { + int64_t result; + bool rv = enum_from_python(&typeid(T), src.ptr(), &result, flags); + value = (T) result; + return rv; + } + + NB_INLINE static handle from_cpp(T src, rv_policy, cleanup_list *) noexcept { + return enum_from_cpp(&typeid(T), (int64_t) src); + } + + NB_TYPE_CASTER(T, const_name()) +}; + +template <> struct type_caster { + static constexpr auto Name = const_name("None"); +}; + +template <> struct type_caster { + template using Cast = void *; + template static constexpr bool can_cast() { return true; } + using Value = void*; + static constexpr auto Name = const_name("types.CapsuleType"); + explicit operator void *() { return value; } + Value value; + + bool from_python(handle src, uint8_t, cleanup_list *) noexcept { + if (src.is_none()) { + value = nullptr; + return true; + } else { + value = PyCapsule_GetPointer(src.ptr(), "nb_handle"); + if (!value) { + PyErr_Clear(); + return false; + } + return true; + } + } + + static handle from_cpp(void *ptr, rv_policy, cleanup_list *) noexcept { + if (ptr) + return PyCapsule_New(ptr, "nb_handle", nullptr); + else + return none().release(); + } +}; + +template struct none_caster { + bool from_python(handle src, uint8_t, cleanup_list *) noexcept { + if (src.is_none()) + return true; + return false; + } + + static handle from_cpp(T, rv_policy, cleanup_list *) noexcept { + return none().release(); + } + + NB_TYPE_CASTER(T, const_name("None")) +}; + +template <> struct type_caster : none_caster { }; + +template <> struct type_caster { + bool from_python(handle src, uint8_t, cleanup_list *) noexcept { + if (src.ptr() == Py_True) { + value = true; + return true; + } else if (src.ptr() == Py_False) { + value = false; + return true; + } else { + return false; + } + } + + static handle from_cpp(bool src, rv_policy, cleanup_list *) noexcept { + return handle(src ? Py_True : Py_False).inc_ref(); + } + + NB_TYPE_CASTER(bool, const_name("bool")) +}; + +template <> struct type_caster { + using Value = const char *; + Value value; + Py_ssize_t size; + static constexpr auto Name = const_name("str"); + template + using Cast = std::conditional_t, const char *, char>; + + bool from_python(handle src, uint8_t, cleanup_list *) noexcept { + value = PyUnicode_AsUTF8AndSize(src.ptr(), &size); + if (!value) { + PyErr_Clear(); + return false; + } + return true; + } + + static handle from_cpp(const char *value, rv_policy, + cleanup_list *) noexcept { + if (value == nullptr) { + PyObject* result = Py_None; + Py_INCREF(result); + return result; + } + return PyUnicode_FromString(value); + } + + static handle from_cpp(char value, rv_policy, cleanup_list *) noexcept { + return PyUnicode_FromStringAndSize(&value, 1); + } + + template + NB_INLINE bool can_cast() const noexcept { + return std::is_pointer_v || (value && size == 1); + } + + explicit operator const char *() { return value; } + + explicit operator char() { + if (can_cast()) + return value[0]; + else + throw next_overload(); + } +}; + +template struct type_caster> { + using Caster = make_caster; + using T2 = pointer_and_handle; + NB_TYPE_CASTER(T2, Caster::Name) + + bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept { + Caster c; + if (!c.from_python(src, flags_for_local_caster(flags), cleanup) || + !c.template can_cast()) + return false; + value.h = src; + value.p = c.operator T*(); + return true; + } +}; + +template struct typed_name { + static constexpr auto Name = type_caster::Name; +}; + +#if PY_VERSION_HEX < 0x03090000 +#define NB_TYPED_NAME_PYTHON38(type, name) \ + template <> struct typed_name { \ + static constexpr auto Name = detail::const_name(name); \ + }; + +NB_TYPED_NAME_PYTHON38(nanobind::tuple, NB_TYPING_TUPLE) +NB_TYPED_NAME_PYTHON38(list, NB_TYPING_LIST) +NB_TYPED_NAME_PYTHON38(set, NB_TYPING_SET) +NB_TYPED_NAME_PYTHON38(dict, NB_TYPING_DICT) +NB_TYPED_NAME_PYTHON38(type_object, NB_TYPING_TYPE) +#endif + +template struct type_caster> { + using Caster = make_caster; + using Typed = typed; + + NB_TYPE_CASTER(Typed, typed_name>::Name + const_name("[") + + concat(const_name>(const_name("..."), + make_caster::Name)...) + const_name("]")) + + bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept { + Caster caster; + if (!caster.from_python(src, flags_for_local_caster(flags), cleanup) || + !caster.template can_cast()) + return false; + value = caster.operator cast_t(); + return true; + } + + static handle from_cpp(const Value &src, rv_policy policy, cleanup_list *cleanup) noexcept { + return Caster::from_cpp(src, policy, cleanup); + } +}; + +template +struct type_caster && !T::nb_typed>> { +public: + NB_TYPE_CASTER(T, T::Name) + + type_caster() : value(nullptr, ::nanobind::detail::steal_t()) { } + + bool from_python(handle src, uint8_t, cleanup_list *) noexcept { + if (!isinstance(src)) + return false; + + if constexpr (std::is_base_of_v) + value = borrow(src); + else + value = src; + + return true; + } + + static handle from_cpp(T&& src, rv_policy, cleanup_list *) noexcept { + if constexpr (std::is_base_of_v) + return src.release(); + else + return src.inc_ref(); + } + + static handle from_cpp(const T &src, rv_policy, cleanup_list *) noexcept { + return src.inc_ref(); + } +}; + +template NB_INLINE rv_policy infer_policy(rv_policy policy) { + if constexpr (is_pointer_v) { + if (policy == rv_policy::automatic) + policy = rv_policy::take_ownership; + else if (policy == rv_policy::automatic_reference) + policy = rv_policy::reference; + } else if constexpr (std::is_lvalue_reference_v) { + if (policy == rv_policy::automatic || + policy == rv_policy::automatic_reference) + policy = rv_policy::copy; + } else { + if (policy == rv_policy::automatic || + policy == rv_policy::automatic_reference || + policy == rv_policy::reference || + policy == rv_policy::reference_internal) + policy = rv_policy::move; + } + return policy; +} + +template struct type_hook : std::false_type { }; + +template struct type_caster_base : type_caster_base_tag { + using Type = Type_; + static constexpr auto Name = const_name(); + template using Cast = precise_cast_t; + + NB_INLINE bool from_python(handle src, uint8_t flags, + cleanup_list *cleanup) noexcept { + return nb_type_get(&typeid(Type), src.ptr(), flags, cleanup, + (void **) &value); + } + + template + NB_INLINE static handle from_cpp(T &&value, rv_policy policy, + cleanup_list *cleanup) noexcept { + Type *ptr; + if constexpr (is_pointer_v) + ptr = (Type *) value; + else + ptr = (Type *) &value; + + policy = infer_policy(policy); + const std::type_info *type = &typeid(Type); + + constexpr bool has_type_hook = + !std::is_base_of_v>; + if constexpr (has_type_hook) + type = type_hook::get(ptr); + + if constexpr (!std::is_polymorphic_v) { + return nb_type_put(type, ptr, policy, cleanup); + } else { + const std::type_info *type_p = + (!has_type_hook && ptr) ? &typeid(*ptr) : nullptr; + return nb_type_put_p(type, type_p, ptr, policy, cleanup); + } + } + + template + bool can_cast() const noexcept { + return std::is_pointer_v || (value != nullptr); + } + + operator Type*() { return value; } + + operator Type&() { + raise_next_overload_if_null(value); + return *value; + } + + operator Type&&() { + raise_next_overload_if_null(value); + return (Type &&) *value; + } + +private: + Type *value; +}; + +template +struct type_caster : type_caster_base { }; + +template +T cast_impl(handle h) { + using Caster = detail::make_caster; + + // A returned reference/pointer would usually refer into the type_caster + // object, which will be destroyed before the returned value can be used, + // so we prohibit it by default, with two exceptions that we know are safe: + // + // - If we're casting to a bound object type, the returned pointer points + // into storage owned by that object, not the type caster. Note this is + // only safe if we don't allow implicit conversions, because the pointer + // produced after an implicit conversion points into storage owned by + // a temporary object in the cleanup list, and we have to release those + // temporaries before we return. + // + // - If we're casting to const char*, the caster was provided by nanobind, + // and we know it will only accept Python 'str' objects, producing + // a pointer to storage owned by that object. + + constexpr bool is_ref = std::is_reference_v || std::is_pointer_v; + static_assert( + !is_ref || + is_base_caster_v || + std::is_same_v, + "nanobind::cast(): cannot return a reference to a temporary."); + + Caster caster; + bool rv; + if constexpr (Convert && !is_ref) { + // Release the values in the cleanup list only after we + // initialize the return object, since the initialization + // might access those temporaries. + struct raii_cleanup { + cleanup_list list{nullptr}; + ~raii_cleanup() { list.release(); } + } cleanup; + rv = caster.from_python(h.ptr(), + ((uint8_t) cast_flags::convert) | + ((uint8_t) cast_flags::manual), + &cleanup.list); + if (!rv) + detail::raise_cast_error(); + return caster.operator cast_t(); + } else { + rv = caster.from_python(h.ptr(), (uint8_t) cast_flags::manual, nullptr); + if (!rv) + detail::raise_cast_error(); + return caster.operator cast_t(); + } +} + +template +bool try_cast_impl(handle h, T &out) noexcept { + using Caster = detail::make_caster; + + // See comments in cast_impl above + constexpr bool is_ref = std::is_reference_v || std::is_pointer_v; + static_assert( + !is_ref || + is_base_caster_v || + std::is_same_v, + "nanobind::try_cast(): cannot return a reference to a temporary."); + + Caster caster; + bool rv; + if constexpr (Convert && !is_ref) { + cleanup_list cleanup(nullptr); + rv = caster.from_python(h.ptr(), + ((uint8_t) cast_flags::convert) | + ((uint8_t) cast_flags::manual), + &cleanup) && + caster.template can_cast(); + if (rv) { + out = caster.operator cast_t(); + } + cleanup.release(); // 'from_python' is 'noexcept', so this always runs + } else { + rv = caster.from_python(h.ptr(), (uint8_t) cast_flags::manual, nullptr) && + caster.template can_cast(); + if (rv) { + out = caster.operator cast_t(); + } + } + + return rv; +} + +NAMESPACE_END(detail) + +template +NB_INLINE T cast(const detail::api &value, bool convert = true) { + if constexpr (std::is_same_v) { + (void) value; (void) convert; + return; + } else { + if (convert) + return detail::cast_impl(value); + else + return detail::cast_impl(value); + } +} + +template +NB_INLINE bool try_cast(const detail::api &value, T &out, bool convert = true) noexcept { + if (convert) + return detail::try_cast_impl(value, out); + else + return detail::try_cast_impl(value, out); +} + +template +object cast(T &&value, rv_policy policy = rv_policy::automatic_reference) { + handle h = detail::make_caster::from_cpp((detail::forward_t) value, + policy, nullptr); + if (!h.is_valid()) + detail::raise_cast_error(); + + return steal(h); +} + +template +object cast(T &&value, rv_policy policy, handle parent) { + detail::cleanup_list cleanup(parent.ptr()); + handle h = detail::make_caster::from_cpp((detail::forward_t) value, + policy, &cleanup); + + cleanup.release(); + + if (!h.is_valid()) + detail::raise_cast_error(); + + return steal(h); +} + +template object find(const T &value) noexcept { + return steal(detail::make_caster::from_cpp(value, rv_policy::none, nullptr)); +} + +template +tuple make_tuple(Args &&...args) { + tuple result = steal(PyTuple_New((Py_ssize_t) sizeof...(Args))); + + size_t nargs = 0; + PyObject *o = result.ptr(); + + (NB_TUPLE_SET_ITEM(o, nargs++, + detail::make_caster::from_cpp( + (detail::forward_t) args, policy, nullptr) + .ptr()), + ...); + + detail::tuple_check(o, sizeof...(Args)); + + return result; +} + +template arg_v arg::operator=(T &&value) const { + return arg_v(*this, cast((detail::forward_t) value)); +} +template arg_locked_v arg_locked::operator=(T &&value) const { + return arg_locked_v(*this, cast((detail::forward_t) value)); +} + +template template +detail::accessor& detail::accessor::operator=(T &&value) { + object result = cast((detail::forward_t) value); + Impl::set(m_base, m_key, result.ptr()); + return *this; +} + +template void list::append(T &&value) { + object o = nanobind::cast((detail::forward_t) value); + if (PyList_Append(m_ptr, o.ptr())) + raise_python_error(); +} + +template void list::insert(Py_ssize_t index, T &&value) { + object o = nanobind::cast((detail::forward_t) value); + if (PyList_Insert(m_ptr, index, o.ptr())) + raise_python_error(); +} + +template bool dict::contains(T&& key) const { + object o = nanobind::cast((detail::forward_t) key); + int rv = PyDict_Contains(m_ptr, o.ptr()); + if (rv == -1) + raise_python_error(); + return rv == 1; +} + +template bool set::contains(T&& key) const { + object o = nanobind::cast((detail::forward_t) key); + int rv = PySet_Contains(m_ptr, o.ptr()); + if (rv == -1) + raise_python_error(); + return rv == 1; +} + +template void set::add(T&& key) { + object o = nanobind::cast((detail::forward_t) key); + int rv = PySet_Add(m_ptr, o.ptr()); + if (rv == -1) + raise_python_error(); +} + +template bool set::discard(T &&value) { + object o = nanobind::cast((detail::forward_t) value); + int rv = PySet_Discard(m_ptr, o.ptr()); + if (rv < 0) + raise_python_error(); + return rv == 1; +} + +template bool mapping::contains(T&& key) const { + object o = nanobind::cast((detail::forward_t) key); + int rv = PyMapping_HasKey(m_ptr, o.ptr()); + if (rv == -1) + raise_python_error(); + return rv == 1; +} + +NAMESPACE_END(NB_NAMESPACE) diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_class.h b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_class.h new file mode 100644 index 0000000..e8da8d5 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_class.h @@ -0,0 +1,808 @@ +/* + nanobind/nb_class.h: Functionality for binding C++ classes/structs + + Copyright (c) 2022 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +NAMESPACE_BEGIN(NB_NAMESPACE) +NAMESPACE_BEGIN(detail) + +/// Flags about a type that persist throughout its lifetime +enum class type_flags : uint32_t { + /// Does the type provide a C++ destructor? + is_destructible = (1 << 0), + + /// Does the type provide a C++ copy constructor? + is_copy_constructible = (1 << 1), + + /// Does the type provide a C++ move constructor? + is_move_constructible = (1 << 2), + + /// Is the 'destruct' field of the type_data structure set? + has_destruct = (1 << 4), + + /// Is the 'copy' field of the type_data structure set? + has_copy = (1 << 5), + + /// Is the 'move' field of the type_data structure set? + has_move = (1 << 6), + + /// Internal: does the type maintain a list of implicit conversions? + has_implicit_conversions = (1 << 7), + + /// Is this a python type that extends a bound C++ type? + is_python_type = (1 << 8), + + /// This type does not permit subclassing from Python + is_final = (1 << 9), + + /// Instances of this type support dynamic attribute assignment + has_dynamic_attr = (1 << 10), + + /// The class uses an intrusive reference counting approach + intrusive_ptr = (1 << 11), + + /// Is this a class that inherits from enable_shared_from_this? + /// If so, type_data::keep_shared_from_this_alive is also set. + has_shared_from_this = (1 << 12), + + /// Instances of this type can be referenced by 'weakref' + is_weak_referenceable = (1 << 13), + + /// A custom signature override was specified + has_signature = (1 << 14), + + /// The class implements __class_getitem__ similar to typing.Generic + is_generic = (1 << 15), + + /// Does the type implement a custom __new__ operator? + has_new = (1 << 16) + + // Two more bits bits available without needing a larger reorganization +}; + +/// Flags about a type that are only relevant when it is being created. +/// These are currently stored in type_data::flags alongside the type_flags +/// for more efficient memory layout, but could move elsewhere if we run +/// out of flags. +enum class type_init_flags : uint32_t { + /// Is the 'supplement' field of the type_init_data structure set? + has_supplement = (1 << 19), + + /// Is the 'doc' field of the type_init_data structure set? + has_doc = (1 << 20), + + /// Is the 'base' field of the type_init_data structure set? + has_base = (1 << 21), + + /// Is the 'base_py' field of the type_init_data structure set? + has_base_py = (1 << 22), + + /// This type provides extra PyType_Slot fields + has_type_slots = (1 << 23), + + all_init_flags = (0x1f << 19) +}; + +// See internals.h +struct nb_alias_chain; + +/// Information about a type that persists throughout its lifetime +struct type_data { + uint32_t size; + uint32_t align : 8; + uint32_t flags : 24; + const char *name; + const std::type_info *type; + PyTypeObject *type_py; + nb_alias_chain *alias_chain; +#if defined(Py_LIMITED_API) + PyObject* (*vectorcall)(PyObject *, PyObject * const*, size_t, PyObject *); +#endif + void *init; // Constructor nb_func + void (*destruct)(void *); + void (*copy)(void *, const void *); + void (*move)(void *, void *) noexcept; + union { + // Implicit conversions for C++ type bindings + struct { + const std::type_info **cpp; + bool (**py)(PyTypeObject *, PyObject *, cleanup_list *) noexcept; + } implicit; + + // Forward and reverse mappings for enumerations + struct { + void *fwd; + void *rev; + } enum_tbl; + }; + void (*set_self_py)(void *, PyObject *) noexcept; + bool (*keep_shared_from_this_alive)(PyObject *) noexcept; +#if defined(Py_LIMITED_API) + uint32_t dictoffset; + uint32_t weaklistoffset; +#endif +}; + +/// Information about a type that is only relevant when it is being created +struct type_init_data : type_data { + PyObject *scope; + const std::type_info *base; + PyTypeObject *base_py; + const char *doc; + const PyType_Slot *type_slots; + size_t supplement; +}; + +NB_INLINE void type_extra_apply(type_init_data &t, const handle &h) { + t.flags |= (uint32_t) type_init_flags::has_base_py; + t.base_py = (PyTypeObject *) h.ptr(); +} + +NB_INLINE void type_extra_apply(type_init_data &t, const char *doc) { + t.flags |= (uint32_t) type_init_flags::has_doc; + t.doc = doc; +} + +NB_INLINE void type_extra_apply(type_init_data &t, type_slots c) { + t.flags |= (uint32_t) type_init_flags::has_type_slots; + t.type_slots = c.value; +} + +template +NB_INLINE void type_extra_apply(type_init_data &t, intrusive_ptr ip) { + t.flags |= (uint32_t) type_flags::intrusive_ptr; + t.set_self_py = (void (*)(void *, PyObject *) noexcept) ip.set_self_py; +} + +NB_INLINE void type_extra_apply(type_init_data &t, is_final) { + t.flags |= (uint32_t) type_flags::is_final; +} + +NB_INLINE void type_extra_apply(type_init_data &t, dynamic_attr) { + t.flags |= (uint32_t) type_flags::has_dynamic_attr; +} + +NB_INLINE void type_extra_apply(type_init_data & t, is_weak_referenceable) { + t.flags |= (uint32_t) type_flags::is_weak_referenceable; +} + +NB_INLINE void type_extra_apply(type_init_data & t, is_generic) { + t.flags |= (uint32_t) type_flags::is_generic; +} + +NB_INLINE void type_extra_apply(type_init_data & t, const sig &s) { + t.flags |= (uint32_t) type_flags::has_signature; + t.name = s.value; +} + +template +NB_INLINE void type_extra_apply(type_init_data &t, supplement) { + static_assert(std::is_trivially_default_constructible_v, + "The supplement must be a POD (plain old data) type"); + static_assert(alignof(T) <= alignof(void *), + "The alignment requirement of the supplement is too high."); + t.flags |= (uint32_t) type_init_flags::has_supplement | (uint32_t) type_flags::is_final; + t.supplement = sizeof(T); +} + +enum class enum_flags : uint32_t { + /// Is this an arithmetic enumeration? + is_arithmetic = (1 << 1), + + /// Is the number type underlying the enumeration signed? + is_signed = (1 << 2), + + /// Is the underlying enumeration type Flag? + is_flag = (1 << 3) +}; + +struct enum_init_data { + const std::type_info *type; + PyObject *scope; + const char *name; + const char *docstr; + uint32_t flags; +}; + +NB_INLINE void enum_extra_apply(enum_init_data &e, is_arithmetic) { + e.flags |= (uint32_t) enum_flags::is_arithmetic; +} + +NB_INLINE void enum_extra_apply(enum_init_data &e, is_flag) { + e.flags |= (uint32_t) enum_flags::is_flag; +} + +NB_INLINE void enum_extra_apply(enum_init_data &e, const char *doc) { + e.docstr = doc; +} + +template +NB_INLINE void enum_extra_apply(enum_init_data &, T) { + static_assert( + std::is_void_v, + "Invalid enum binding annotation. The implementation of " + "enums changed nanobind 2.0.0: only nb::is_arithmetic and " + "docstrings can be passed since this change."); +} + +template void wrap_copy(void *dst, const void *src) { + new ((T *) dst) T(*(const T *) src); +} + +template void wrap_move(void *dst, void *src) noexcept { + new ((T *) dst) T(std::move(*(T *) src)); +} + +template void wrap_destruct(void *value) noexcept { + ((T *) value)->~T(); +} + +template typename, typename...> +struct extract; + +template typename Pred> +struct extract { + using type = T; +}; + +template typename Pred, + typename Tv, typename... Ts> +struct extract { + using type = std::conditional_t< + Pred::value, + Tv, + typename extract::type + >; +}; + +template using is_alias = std::is_base_of; +template using is_base = std::is_base_of; + +enum op_id : int; +enum op_type : int; +struct undefined_t; +template struct op_; + +// The header file include/nanobind/stl/detail/traits.h extends this type trait +template +struct is_copy_constructible : std::is_copy_constructible { }; + +template +constexpr bool is_copy_constructible_v = is_copy_constructible::value; + +NAMESPACE_END(detail) + +// Low level access to nanobind type objects +inline bool type_check(handle h) { return detail::nb_type_check(h.ptr()); } +inline size_t type_size(handle h) { return detail::nb_type_size(h.ptr()); } +inline size_t type_align(handle h) { return detail::nb_type_align(h.ptr()); } +inline const std::type_info& type_info(handle h) { return *detail::nb_type_info(h.ptr()); } +template +inline T &type_supplement(handle h) { return *(T *) detail::nb_type_supplement(h.ptr()); } +inline str type_name(handle h) { return steal(detail::nb_type_name(h.ptr())); }; + +// Low level access to nanobind instance objects +inline bool inst_check(handle h) { return type_check(h.type()); } +inline str inst_name(handle h) { + return steal(detail::nb_inst_name(h.ptr())); +}; +inline object inst_alloc(handle h) { + return steal(detail::nb_inst_alloc((PyTypeObject *) h.ptr())); +} +inline object inst_alloc_zero(handle h) { + return steal(detail::nb_inst_alloc_zero((PyTypeObject *) h.ptr())); +} +inline object inst_take_ownership(handle h, void *p) { + return steal(detail::nb_inst_take_ownership((PyTypeObject *) h.ptr(), p)); +} +inline object inst_reference(handle h, void *p, handle parent = handle()) { + return steal(detail::nb_inst_reference((PyTypeObject *) h.ptr(), p, parent.ptr())); +} +inline void inst_zero(handle h) { detail::nb_inst_zero(h.ptr()); } +inline void inst_set_state(handle h, bool ready, bool destruct) { + detail::nb_inst_set_state(h.ptr(), ready, destruct); +} +inline std::pair inst_state(handle h) { + return detail::nb_inst_state(h.ptr()); +} +inline void inst_mark_ready(handle h) { inst_set_state(h, true, true); } +inline bool inst_ready(handle h) { return inst_state(h).first; } +inline void inst_destruct(handle h) { detail::nb_inst_destruct(h.ptr()); } +inline void inst_copy(handle dst, handle src) { detail::nb_inst_copy(dst.ptr(), src.ptr()); } +inline void inst_move(handle dst, handle src) { detail::nb_inst_move(dst.ptr(), src.ptr()); } +inline void inst_replace_copy(handle dst, handle src) { detail::nb_inst_replace_copy(dst.ptr(), src.ptr()); } +inline void inst_replace_move(handle dst, handle src) { detail::nb_inst_replace_move(dst.ptr(), src.ptr()); } +template T *inst_ptr(handle h) { return (T *) detail::nb_inst_ptr(h.ptr()); } +inline void *type_get_slot(handle h, int slot_id) { +#if NB_TYPE_GET_SLOT_IMPL + return detail::type_get_slot((PyTypeObject *) h.ptr(), slot_id); +#else + return PyType_GetSlot((PyTypeObject *) h.ptr(), slot_id); +#endif +} + + +template struct init { + template friend class class_; + NB_INLINE init() {} + +private: + template + NB_INLINE static void execute(Class &cl, const Extra&... extra) { + using Type = typename Class::Type; + using Alias = typename Class::Alias; + cl.def( + "__init__", + [](pointer_and_handle v, Args... args) { + if constexpr (!std::is_same_v && + std::is_constructible_v) { + if (!detail::nb_inst_python_derived(v.h.ptr())) { + new (v.p) Type{ (detail::forward_t) args... }; + return; + } + } + new ((void *) v.p) Alias{ (detail::forward_t) args... }; + }, + extra...); + } +}; + +template struct init_implicit { + template friend class class_; + NB_INLINE init_implicit() { } + +private: + template + NB_INLINE static void execute(Class &cl, const Extra&... extra) { + using Type = typename Class::Type; + using Alias = typename Class::Alias; + + cl.def( + "__init__", + [](pointer_and_handle v, Arg arg) { + if constexpr (!std::is_same_v && + std::is_constructible_v) { + if (!detail::nb_inst_python_derived(v.h.ptr())) { + new ((Type *) v.p) Type{ (detail::forward_t) arg }; + return; + } + } + new ((Alias *) v.p) Alias{ (detail::forward_t) arg }; + }, is_implicit(), extra...); + + using Caster = detail::make_caster; + + if constexpr (!detail::is_class_caster_v) { + detail::implicitly_convertible( + [](PyTypeObject *, PyObject *src, + detail::cleanup_list *cleanup) noexcept -> bool { + return Caster().from_python( + src, detail::cast_flags::convert, cleanup); + }, + &typeid(Type)); + } + } +}; + +namespace detail { + // This is 'inline' so we can define it in a header and not pay + // for it if unused, and also 'noinline' so we don't generate + // multiple copies and produce code bloat. + NB_NOINLINE inline void wrap_base_new(handle cls, bool do_wrap) { + if (PyCFunction_Check(cls.attr("__new__").ptr())) { + if (do_wrap) { + cpp_function_def( + [](handle type) { + if (!type_check(type)) + detail::raise_cast_error(); + return inst_alloc(type); + }, + scope(cls), name("__new__")); + } + } else { + if (!do_wrap) { + // We already defined the wrapper, so this zero-arg overload + // would be unreachable. Raise an error rather than hiding it. + raise("nanobind: %s must define its zero-argument __new__ " + "before any other overloads", type_name(cls).c_str()); + } + } + } +} + +template > +struct new_; + +template +struct new_ { + std::remove_reference_t func; + + new_(Func &&f) : func((detail::forward_t) f) {} + + template + NB_INLINE void execute(Class &cl, const Extra&... extra) { + // If this is the first __new__ overload we're defining, then wrap + // nanobind's built-in __new__ so we overload with it instead of + // replacing it; this is important for pickle support. + // We can't do this if the user-provided __new__ takes no + // arguments, because it would make an ambiguous overload set. + detail::wrap_base_new(cl, sizeof...(Args) != 0); + + auto wrapper = [func = (detail::forward_t) func](handle, Args... args) { + return func((detail::forward_t) args...); + }; + + if constexpr ((std::is_base_of_v || ...)) { + // If any argument annotations are specified, add another for the + // extra class argument that we don't forward to Func, so visible + // arg() annotations stay aligned with visible function arguments. + cl.def_static("__new__", std::move(wrapper), arg("cls"), extra...); + } else { + cl.def_static("__new__", std::move(wrapper), extra...); + } + cl.def("__init__", [](handle, Args...) {}, extra...); + } +}; +template new_(Func&& f) -> new_; + +template struct for_setter { + T value; + for_setter(const T &value) : value(value) { } +}; + +template struct for_getter { + T value; + for_getter(const T &value) : value(value) { } +}; + +template for_getter(T) -> for_getter>; +template for_setter(T) -> for_setter>; + +namespace detail { + template auto filter_getter(const T &v) { return v; } + template auto filter_getter(const for_getter &v) { return v.value; } + template std::nullptr_t filter_getter(const for_setter &) { return nullptr; } + + template auto filter_setter(const T &v) { return v; } + template auto filter_setter(const for_setter &v) { return v.value; } + template std::nullptr_t filter_setter(const for_getter &) { return nullptr; } +} + +template +class class_ : public object { +public: + NB_OBJECT_DEFAULT(class_, object, "type", PyType_Check) + using Type = T; + using Base = typename detail::extract::type; + using Alias = typename detail::extract::type; + + static_assert(sizeof(Alias) < (1 << 24), "Instance size is too big!"); + static_assert(alignof(Alias) < (1 << 8), "Instance alignment is too big!"); + static_assert( + sizeof...(Ts) == !std::is_same_v + !std::is_same_v, + "nanobind::class_<> was invoked with extra arguments that could not be handled"); + + static_assert( + detail::is_base_caster_v>, + "You attempted to bind a type that is already intercepted by a type " + "caster. Having both at the same time is not allowed. Are you perhaps " + "binding an STL type, while at the same time including a matching " + "type caster from ? Or did you perhaps forget to " + "declare NB_MAKE_OPAQUE(..) to specifically disable the type caster " + "catch-all for a specific type? Please review the documentation " + "to learn about the difference between bindings and type casters."); + + template + NB_INLINE class_(handle scope, const char *name, const Extra &... extra) { + detail::type_init_data d; + + d.flags = 0; + d.align = (uint8_t) alignof(Alias); + d.size = (uint32_t) sizeof(Alias); + d.name = name; + d.scope = scope.ptr(); + d.type = &typeid(T); + + if constexpr (!std::is_same_v) { + d.base = &typeid(Base); + d.flags |= (uint32_t) detail::type_init_flags::has_base; + } + + if constexpr (detail::is_copy_constructible_v) { + d.flags |= (uint32_t) detail::type_flags::is_copy_constructible; + + if constexpr (!std::is_trivially_copy_constructible_v) { + d.flags |= (uint32_t) detail::type_flags::has_copy; + d.copy = detail::wrap_copy; + } + } + + if constexpr (std::is_move_constructible::value) { + d.flags |= (uint32_t) detail::type_flags::is_move_constructible; + + if constexpr (!std::is_trivially_move_constructible_v) { + d.flags |= (uint32_t) detail::type_flags::has_move; + d.move = detail::wrap_move; + } + } + + if constexpr (std::is_destructible_v) { + d.flags |= (uint32_t) detail::type_flags::is_destructible; + + if constexpr (!std::is_trivially_destructible_v) { + d.flags |= (uint32_t) detail::type_flags::has_destruct; + d.destruct = detail::wrap_destruct; + } + } + + if constexpr (detail::has_shared_from_this_v) { + d.flags |= (uint32_t) detail::type_flags::has_shared_from_this; + d.keep_shared_from_this_alive = [](PyObject *self) noexcept { + // weak_from_this().lock() is equivalent to shared_from_this(), + // except that it returns an empty shared_ptr instead of + // throwing an exception if there is no active shared_ptr + // for this object. (Added in C++17.) + if (auto sp = inst_ptr(self)->weak_from_this().lock()) { + detail::keep_alive(self, new auto(std::move(sp)), + [](void *p) noexcept { + delete (decltype(sp) *) p; + }); + return true; + } + return false; + }; + } + + (detail::type_extra_apply(d, extra), ...); + + m_ptr = detail::nb_type_new(&d); + } + + template + NB_INLINE class_ &def(const char *name_, Func &&f, const Extra &... extra) { + cpp_function_def((detail::forward_t) f, scope(*this), + name(name_), is_method(), extra...); + return *this; + } + + template + NB_INLINE class_ &def(init &&arg, const Extra &... extra) { + arg.execute(*this, extra...); + return *this; + } + + template + NB_INLINE class_ &def(init_implicit &&arg, const Extra &... extra) { + arg.execute(*this, extra...); + return *this; + } + + template + NB_INLINE class_ &def(new_ &&arg, const Extra &... extra) { + arg.execute(*this, extra...); + return *this; + } + + template + NB_INLINE class_ &def_static(const char *name_, Func &&f, + const Extra &... extra) { + static_assert( + !std::is_member_function_pointer_v, + "def_static(...) called with a non-static member function pointer"); + cpp_function_def((detail::forward_t) f, scope(*this), name(name_), + extra...); + return *this; + } + + template + NB_INLINE class_ &def_prop_rw(const char *name_, Getter &&getter, + Setter &&setter, const Extra &...extra) { + object get_p, set_p; + + if constexpr (!std::is_same_v) + get_p = cpp_function((detail::forward_t) getter, + is_method(), is_getter(), + rv_policy::reference_internal, + detail::filter_getter(extra)...); + + if constexpr (!std::is_same_v) + set_p = cpp_function((detail::forward_t) setter, + is_method(), detail::filter_setter(extra)...); + + detail::property_install(m_ptr, name_, get_p.ptr(), set_p.ptr()); + return *this; + } + + template + NB_INLINE class_ &def_prop_rw_static(const char *name_, Getter &&getter, + Setter &&setter, + const Extra &...extra) { + object get_p, set_p; + + if constexpr (!std::is_same_v) + get_p = cpp_function((detail::forward_t) getter, is_getter(), + rv_policy::reference, + detail::filter_getter(extra)...); + + if constexpr (!std::is_same_v) + set_p = cpp_function((detail::forward_t) setter, + detail::filter_setter(extra)...); + + detail::property_install_static(m_ptr, name_, get_p.ptr(), set_p.ptr()); + return *this; + } + + template + NB_INLINE class_ &def_prop_ro(const char *name_, Getter &&getter, + const Extra &...extra) { + return def_prop_rw(name_, getter, nullptr, extra...); + } + + template + NB_INLINE class_ &def_prop_ro_static(const char *name_, + Getter &&getter, + const Extra &...extra) { + return def_prop_rw_static(name_, getter, nullptr, extra...); + } + + template + NB_INLINE class_ &def_rw(const char *name, D C::*p, + const Extra &...extra) { + // Unions never satisfy is_base_of, thus the is_same alternative + static_assert(std::is_base_of_v || std::is_same_v, + "def_rw() requires a (base) class member!"); + + using Q = + std::conditional_t>, + const D &, D &&>; + + def_prop_rw(name, + [p](const T &c) -> const D & { return c.*p; }, + [p](T &c, Q value) { c.*p = (Q) value; }, + extra...); + + return *this; + } + + template + NB_INLINE class_ &def_rw_static(const char *name, D *p, + const Extra &...extra) { + using Q = + std::conditional_t>, + const D &, D &&>; + + def_prop_rw_static(name, + [p](handle) -> const D & { return *p; }, + [p](handle, Q value) { *p = (Q) value; }, extra...); + + return *this; + } + + template + NB_INLINE class_ &def_ro(const char *name, D C::*p, + const Extra &...extra) { + // Unions never satisfy is_base_of, thus the is_same alternative + static_assert(std::is_base_of_v || std::is_same_v, + "def_ro() requires a (base) class member!"); + + def_prop_ro(name, + [p](const T &c) -> const D & { return c.*p; }, extra...); + + return *this; + } + + template + NB_INLINE class_ &def_ro_static(const char *name, D *p, + const Extra &...extra) { + def_prop_ro_static(name, + [p](handle) -> const D & { return *p; }, extra...); + + return *this; + } + + template + class_ &def(const detail::op_ &op, const Extra&... extra) { + op.execute(*this, extra...); + return *this; + } + + template + class_ & def_cast(const detail::op_ &op, const Extra&... extra) { + op.execute_cast(*this, extra...); + return *this; + } +}; + +template class enum_ : public object { +public: + static_assert(std::is_enum_v, "nanobind::enum_<> requires an enumeration type!"); + + using Base = class_; + using Underlying = std::underlying_type_t; + + template + NB_INLINE enum_(handle scope, const char *name, const Extra &... extra) { + detail::enum_init_data ed { }; + ed.type = &typeid(T); + ed.scope = scope.ptr(); + ed.name = name; + ed.flags = std::is_signed_v + ? (uint32_t) detail::enum_flags::is_signed + : 0; + (detail::enum_extra_apply(ed, extra), ...); + m_ptr = detail::enum_create(&ed); + } + + NB_INLINE enum_ &value(const char *name, T value, const char *doc = nullptr) { + detail::enum_append(m_ptr, name, (int64_t) value, doc); + return *this; + } + + NB_INLINE enum_ &export_values() { detail::enum_export(m_ptr); return *this; } + + template + NB_INLINE enum_ &def(const char *name_, Func &&f, const Extra &... extra) { + cpp_function_def((detail::forward_t) f, scope(*this), + name(name_), is_method(), extra...); + return *this; + } + + template + NB_INLINE enum_ &def_static(const char *name_, Func &&f, + const Extra &... extra) { + static_assert( + !std::is_member_function_pointer_v, + "def_static(...) called with a non-static member function pointer"); + cpp_function_def((detail::forward_t) f, scope(*this), name(name_), + extra...); + return *this; + } + + template + NB_INLINE enum_ &def_prop_rw(const char *name_, Getter &&getter, + Setter &&setter, const Extra &...extra) { + object get_p, set_p; + + if constexpr (!std::is_same_v) + get_p = cpp_function((detail::forward_t) getter, + is_method(), is_getter(), + rv_policy::reference_internal, + detail::filter_getter(extra)...); + + if constexpr (!std::is_same_v) + set_p = cpp_function((detail::forward_t) setter, + is_method(), detail::filter_setter(extra)...); + + detail::property_install(m_ptr, name_, get_p.ptr(), set_p.ptr()); + return *this; + } + + + template + NB_INLINE enum_ &def_prop_ro(const char *name_, Getter &&getter, + const Extra &...extra) { + return def_prop_rw(name_, getter, nullptr, extra...); + } +}; + +template void implicitly_convertible() { + using Caster = detail::make_caster; + + if constexpr (detail::is_base_caster_v) { + detail::implicitly_convertible(&typeid(Source), &typeid(Target)); + } else { + detail::implicitly_convertible( + [](PyTypeObject *, PyObject *src, + detail::cleanup_list *cleanup) noexcept -> bool { + return Caster().from_python(src, detail::cast_flags::convert, + cleanup); + }, + &typeid(Target)); + } +} + +NAMESPACE_END(NB_NAMESPACE) diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_defs.h b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_defs.h new file mode 100644 index 0000000..071e41f --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_defs.h @@ -0,0 +1,203 @@ +/* + nanobind/nb_defs.h: Preprocessor definitions used by the project + + Copyright (c) 2022 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#define NB_STRINGIFY(x) #x +#define NB_TOSTRING(x) NB_STRINGIFY(x) +#define NB_CONCAT(first, second) first##second +#define NB_NEXT_OVERLOAD ((PyObject *) 1) // special failure return code + +#if !defined(NAMESPACE_BEGIN) +# define NAMESPACE_BEGIN(name) namespace name { +#endif + +#if !defined(NAMESPACE_END) +# define NAMESPACE_END(name) } +#endif + +#if defined(_WIN32) +# define NB_EXPORT __declspec(dllexport) +# define NB_IMPORT __declspec(dllimport) +# define NB_INLINE __forceinline +# define NB_NOINLINE __declspec(noinline) +# define NB_INLINE_LAMBDA +#else +# define NB_EXPORT __attribute__ ((visibility("default"))) +# define NB_IMPORT NB_EXPORT +# define NB_INLINE inline __attribute__((always_inline)) +# define NB_NOINLINE __attribute__((noinline)) +# if defined(__clang__) +# define NB_INLINE_LAMBDA __attribute__((always_inline)) +# else +# define NB_INLINE_LAMBDA +# endif +#endif + +#if defined(__GNUC__) && !defined(_WIN32) +# define NB_NAMESPACE nanobind __attribute__((visibility("hidden"))) +#else +# define NB_NAMESPACE nanobind +#endif + +#if defined(__GNUC__) +# define NB_UNLIKELY(x) __builtin_expect(bool(x), 0) +# define NB_LIKELY(x) __builtin_expect(bool(x), 1) +#else +# define NB_LIKELY(x) x +# define NB_UNLIKELY(x) x +#endif + +#if defined(NB_SHARED) +# if defined(NB_BUILD) +# define NB_CORE NB_EXPORT +# else +# define NB_CORE NB_IMPORT +# endif +#else +# define NB_CORE +#endif + +#if !defined(NB_SHARED) && defined(__GNUC__) && !defined(_WIN32) +# define NB_EXPORT_SHARED __attribute__ ((visibility("hidden"))) +#else +# define NB_EXPORT_SHARED +#endif + +#if defined(__cpp_lib_char8_t) && __cpp_lib_char8_t >= 201811L +# define NB_HAS_U8STRING +#endif + +#if defined(Py_TPFLAGS_HAVE_VECTORCALL) +# define NB_VECTORCALL PyObject_Vectorcall +# define NB_HAVE_VECTORCALL Py_TPFLAGS_HAVE_VECTORCALL +#elif defined(_Py_TPFLAGS_HAVE_VECTORCALL) +# define NB_VECTORCALL _PyObject_Vectorcall +# define NB_HAVE_VECTORCALL _Py_TPFLAGS_HAVE_VECTORCALL +#else +# define NB_HAVE_VECTORCALL (1UL << 11) +#endif + +#if defined(PY_VECTORCALL_ARGUMENTS_OFFSET) +# define NB_VECTORCALL_ARGUMENTS_OFFSET PY_VECTORCALL_ARGUMENTS_OFFSET +# define NB_VECTORCALL_NARGS PyVectorcall_NARGS +#else +# define NB_VECTORCALL_ARGUMENTS_OFFSET ((size_t) 1 << (8 * sizeof(size_t) - 1)) +# define NB_VECTORCALL_NARGS(n) ((n) & ~NB_VECTORCALL_ARGUMENTS_OFFSET) +#endif + +#if PY_VERSION_HEX < 0x03090000 +# define NB_TYPING_ABC "typing." +# define NB_TYPING_TUPLE "typing.Tuple" +# define NB_TYPING_LIST "typing.List" +# define NB_TYPING_DICT "typing.Dict" +# define NB_TYPING_SET "typing.Set" +# define NB_TYPING_TYPE "typing.Type" +#else +# define NB_TYPING_ABC "collections.abc." +# define NB_TYPING_TUPLE "tuple" +# define NB_TYPING_LIST "list" +# define NB_TYPING_DICT "dict" +# define NB_TYPING_SET "set" +# define NB_TYPING_TYPE "type" +#endif + +#define NB_TYPING_SEQUENCE NB_TYPING_ABC "Sequence" +#define NB_TYPING_MAPPING NB_TYPING_ABC "Mapping" +#define NB_TYPING_CALLABLE NB_TYPING_ABC "Callable" +#define NB_TYPING_ITERATOR NB_TYPING_ABC "Iterator" +#define NB_TYPING_ITERABLE NB_TYPING_ABC "Iterable" + +#if PY_VERSION_HEX < 0x03090000 +# define NB_TYPING_ABSTRACT_SET "typing.AbstractSet" +#else +# define NB_TYPING_ABSTRACT_SET "collections.abc.Set" +#endif + +#if defined(Py_LIMITED_API) +# if PY_VERSION_HEX < 0x030C0000 || defined(PYPY_VERSION) +# error "nanobind can target Python's limited API, but this requires CPython >= 3.12" +# endif +# define NB_TUPLE_GET_SIZE PyTuple_Size +# define NB_TUPLE_GET_ITEM PyTuple_GetItem +# define NB_TUPLE_SET_ITEM PyTuple_SetItem +# define NB_LIST_GET_SIZE PyList_Size +# define NB_LIST_GET_ITEM PyList_GetItem +# define NB_LIST_SET_ITEM PyList_SetItem +# define NB_DICT_GET_SIZE PyDict_Size +# define NB_SET_GET_SIZE PySet_Size +#else +# define NB_TUPLE_GET_SIZE PyTuple_GET_SIZE +# define NB_TUPLE_GET_ITEM PyTuple_GET_ITEM +# define NB_TUPLE_SET_ITEM PyTuple_SET_ITEM +# define NB_LIST_GET_SIZE PyList_GET_SIZE +# define NB_LIST_GET_ITEM PyList_GET_ITEM +# define NB_LIST_SET_ITEM PyList_SET_ITEM +# define NB_DICT_GET_SIZE PyDict_GET_SIZE +# define NB_SET_GET_SIZE PySet_GET_SIZE +#endif + +#if defined(PYPY_VERSION_NUM) && PYPY_VERSION_NUM < 0x07030a00 +# error "nanobind requires a newer PyPy version (>= 7.3.10)" +#endif + +#if defined(NB_FREE_THREADED) && !defined(Py_GIL_DISABLED) +# error "Free-threaded extensions require a free-threaded version of Python" +#endif + +#if defined(NB_DOMAIN) +# define NB_DOMAIN_STR NB_TOSTRING(NB_DOMAIN) +#else +# define NB_DOMAIN_STR nullptr +#endif + +#if !defined(PYPY_VERSION) +# if PY_VERSION_HEX < 0x030A0000 +# define NB_TYPE_GET_SLOT_IMPL 1 // Custom implementation of nb::type_get_slot +# else +# define NB_TYPE_GET_SLOT_IMPL 0 +# endif +# if PY_VERSION_HEX < 0x030C0000 +# define NB_TYPE_FROM_METACLASS_IMPL 1 // Custom implementation of PyType_FromMetaclass +# else +# define NB_TYPE_FROM_METACLASS_IMPL 0 +# endif +#else +# define NB_TYPE_FROM_METACLASS_IMPL 1 +# define NB_TYPE_GET_SLOT_IMPL 1 +#endif + +#define NB_NONCOPYABLE(X) \ + X(const X &) = delete; \ + X &operator=(const X &) = delete; + + +#define NB_MODULE_IMPL(name) \ + extern "C" [[maybe_unused]] NB_EXPORT PyObject *PyInit_##name(); \ + extern "C" NB_EXPORT PyObject *PyInit_##name() + +#define NB_MODULE(name, variable) \ + static PyModuleDef NB_CONCAT(nanobind_module_def_, name); \ + [[maybe_unused]] static void NB_CONCAT(nanobind_init_, \ + name)(::nanobind::module_ &); \ + NB_MODULE_IMPL(name) { \ + nanobind::detail::init(NB_DOMAIN_STR); \ + nanobind::module_ m = \ + nanobind::steal(nanobind::detail::module_new( \ + NB_TOSTRING(name), &NB_CONCAT(nanobind_module_def_, name))); \ + try { \ + NB_CONCAT(nanobind_init_, name)(m); \ + return m.release().ptr(); \ + } catch (const std::exception &e) { \ + PyErr_SetString(PyExc_ImportError, e.what()); \ + return nullptr; \ + } \ + } \ + void NB_CONCAT(nanobind_init_, name)(::nanobind::module_ & (variable)) + diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_descr.h b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_descr.h new file mode 100644 index 0000000..4975e13 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_descr.h @@ -0,0 +1,155 @@ +/* + nanobind/nb_descr.h: Constexpr string class for function signatures + + Copyright (c) 2022 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +NAMESPACE_BEGIN(NB_NAMESPACE) +NAMESPACE_BEGIN(detail) + +/// Helper type for concatenating type signatures at compile time +template +struct descr { + char text[N + 1]{'\0'}; + + constexpr descr() = default; + constexpr descr(char const (&s)[N+1]) : descr(s, std::make_index_sequence()) { } + + template + constexpr descr(char const (&s)[N+1], std::index_sequence) : text{s[Is]..., '\0'} { } + + template + constexpr descr(char c, Cs... cs) : text{c, static_cast(cs)..., '\0'} { } + + constexpr size_t type_count() const { return sizeof...(Ts); } + constexpr size_t size() const { return N; } + + NB_INLINE void put_types(const std::type_info **out) const { + size_t ctr = 0; + ((out[ctr++] = &typeid(Ts)), ...); + out[ctr++] = nullptr; + } +}; + +template +constexpr descr plus_impl(const descr &a, const descr &b, + std::index_sequence, std::index_sequence) { + return {a.text[Is1]..., b.text[Is2]...}; +} + +template +constexpr descr operator+(const descr &a, const descr &b) { + return plus_impl(a, b, std::make_index_sequence(), std::make_index_sequence()); +} + +template +constexpr descr const_name(char const(&text)[N]) { return descr(text); } +constexpr descr<0> const_name(char const(&)[1]) { return {}; } + +template +struct int_to_str : int_to_str {}; +template struct int_to_str<0, Digits...> { + static constexpr auto digits = descr(('0' + Digits)...); +}; + +constexpr auto const_name(char c) { return descr<1>(c); } + +// Ternary description (like std::conditional) +template +constexpr auto const_name(char const(&text1)[N1], char const(&text2)[N2]) { + (void) text1; (void) text2; + + if constexpr(B) + return const_name(text1); + else + return const_name(text2); +} + +template +constexpr auto const_name(const T1 &d1, const T2 &d2) { + (void) d1; (void) d2; + + if constexpr (B) + return d1; + else + return d2; +} + +// Use a different name based on whether the parameter is used as input or output +template +constexpr auto io_name(char const (&text1)[N1], char const (&text2)[N2]) { + return const_name('@') + const_name(text1) + const_name('@') + + const_name(text2) + const_name('@'); +} + +#if PY_VERSION_HEX < 0x030A0000 +template constexpr auto optional_name(const T &v) { + return const_name("typing.Optional[") + v + const_name("]"); +} +template constexpr auto union_name(const Ts&... vs) { + return const_name("typing.Union[") + concat(vs...) + const_name("]"); +} +#else +template constexpr auto optional_name(const T &v) { + return v + const_name(" | None"); +} +template constexpr auto union_name(const T &v) { + return v; +} +template +constexpr auto union_name(const T1 &v1, const T2 &v2, const Ts &...vs) { + return v1 + const_name(" | ") + union_name(v2, vs...); +} +#endif + +template +auto constexpr const_name() -> std::remove_cv_t::digits)> { + return int_to_str::digits; +} + +template constexpr descr<1, Type> const_name() { return {'%'}; } + +constexpr descr<0> concat() { return {}; } +constexpr descr<0> concat_maybe() { return {}; } + +template +constexpr descr concat(const descr &descr) { return descr; } + +template +constexpr descr concat_maybe(const descr &descr) { return descr; } + +template +constexpr auto concat(const descr &d, const Args &...args) + -> decltype(std::declval>() + concat(args...)) { + return d + const_name(", ") + concat(args...); +} + +template +constexpr auto concat_maybe(const descr<0> &, const descr<0> &, const Args &...args) + -> decltype(concat_maybe(args...)) { return concat_maybe(args...); } + +template +constexpr auto concat_maybe(const descr<0> &, const descr &arg, const Args &...args) + -> decltype(concat_maybe(arg, args...)) { return concat_maybe(arg, args...); } + +template +constexpr auto concat_maybe(const descr &arg, const descr<0> &, const Args &...args) + -> decltype(concat_maybe(arg, args...)) { return concat_maybe(arg, args...); } + +template = 0> +constexpr auto concat_maybe(const descr &arg0, const descr &arg1, const Args &...args) + -> decltype(concat(arg0, concat_maybe(arg1, args...))) { + return concat(arg0, concat_maybe(arg1, args...)); +} + +template +constexpr descr type_descr(const descr &descr) { + return const_name("{") + descr + const_name("}"); +} + +NAMESPACE_END(detail) +NAMESPACE_END(NB_NAMESPACE) diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_enums.h b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_enums.h new file mode 100644 index 0000000..cae5dcb --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_enums.h @@ -0,0 +1,26 @@ +/* + nanobind/nb_enums.h: enumerations used in nanobind (just rv_policy atm.) + + Copyright (c) 2022 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +NAMESPACE_BEGIN(NB_NAMESPACE) + +// Approach used to cast a previously unknown C++ instance into a Python object +enum class rv_policy { + automatic, + automatic_reference, + take_ownership, + copy, + move, + reference, + reference_internal, + none + /* Note to self: nb_func.h assumes that this value fits into 3 bits, + hence no further policies can be added. */ +}; + +NAMESPACE_END(NB_NAMESPACE) diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_error.h b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_error.h new file mode 100644 index 0000000..3abc960 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_error.h @@ -0,0 +1,152 @@ +/* + nanobind/nb_error.h: Python exception handling, binding of exceptions + + Copyright (c) 2022 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +NAMESPACE_BEGIN(NB_NAMESPACE) + +/// RAII wrapper that temporarily clears any Python error state +#if PY_VERSION_HEX >= 0x030C0000 +struct error_scope { + error_scope() { value = PyErr_GetRaisedException(); } + ~error_scope() { PyErr_SetRaisedException(value); } +private: + PyObject *value; +}; +#else +struct error_scope { + error_scope() { PyErr_Fetch(&type, &value, &trace); } + ~error_scope() { PyErr_Restore(type, value, trace); } +private: + PyObject *type, *value, *trace; +}; +#endif + +/// Wraps a Python error state as a C++ exception +class NB_EXPORT python_error : public std::exception { +public: + NB_EXPORT_SHARED python_error(); + NB_EXPORT_SHARED python_error(const python_error &); + NB_EXPORT_SHARED python_error(python_error &&) noexcept; + NB_EXPORT_SHARED ~python_error() override; + + bool matches(handle exc) const noexcept { +#if PY_VERSION_HEX < 0x030C0000 + return PyErr_GivenExceptionMatches(m_type, exc.ptr()) != 0; +#else + return PyErr_GivenExceptionMatches(m_value, exc.ptr()) != 0; +#endif + } + + /// Move the error back into the Python domain. This may only be called + /// once, and you should not reraise the exception in C++ afterward. + NB_EXPORT_SHARED void restore() noexcept; + + /// Pass the error to Python's `sys.unraisablehook`, which prints + /// a traceback to `sys.stderr` by default but may be overridden. + /// The *context* should be some object whose repr() helps clarify where + /// the error occurred. Like `.restore()`, this consumes the error and + /// you should not reraise the exception in C++ afterward. + void discard_as_unraisable(handle context) noexcept { + restore(); + PyErr_WriteUnraisable(context.ptr()); + } + + void discard_as_unraisable(const char *context) noexcept { + object context_s = steal(PyUnicode_FromString(context)); + discard_as_unraisable(context_s); + } + + handle value() const { return m_value; } + +#if PY_VERSION_HEX < 0x030C0000 + handle type() const { return m_type; } + object traceback() const { return borrow(m_traceback); } +#else + handle type() const { return value().type(); } + object traceback() const { return steal(PyException_GetTraceback(m_value)); } +#endif + [[deprecated]] + object trace() const { return traceback(); } + + NB_EXPORT_SHARED const char *what() const noexcept override; + +private: +#if PY_VERSION_HEX < 0x030C0000 + mutable PyObject *m_type = nullptr; + mutable PyObject *m_value = nullptr; + mutable PyObject *m_traceback = nullptr; +#else + mutable PyObject *m_value = nullptr; +#endif + mutable char *m_what = nullptr; +}; + +/// Thrown by nanobind::cast when casting fails +using cast_error = std::bad_cast; + +enum class exception_type { + runtime_error, stop_iteration, index_error, key_error, value_error, + type_error, buffer_error, import_error, attribute_error, next_overload +}; + +// Base interface used to expose common Python exceptions in C++ +class NB_EXPORT builtin_exception : public std::runtime_error { +public: + NB_EXPORT_SHARED builtin_exception(exception_type type, const char *what); + NB_EXPORT_SHARED builtin_exception(builtin_exception &&) = default; + NB_EXPORT_SHARED builtin_exception(const builtin_exception &) = default; + NB_EXPORT_SHARED ~builtin_exception(); + NB_EXPORT_SHARED exception_type type() const { return m_type; } +private: + exception_type m_type; +}; + +#define NB_EXCEPTION(name) \ + inline builtin_exception name(const char *what = nullptr) { \ + return builtin_exception(exception_type::name, what); \ + } + +NB_EXCEPTION(stop_iteration) +NB_EXCEPTION(index_error) +NB_EXCEPTION(key_error) +NB_EXCEPTION(value_error) +NB_EXCEPTION(type_error) +NB_EXCEPTION(buffer_error) +NB_EXCEPTION(import_error) +NB_EXCEPTION(attribute_error) +NB_EXCEPTION(next_overload) + +#undef NB_EXCEPTION + +inline void register_exception_translator(detail::exception_translator t, + void *payload = nullptr) { + detail::register_exception_translator(t, payload); +} + +template +class exception : public object { + NB_OBJECT_DEFAULT(exception, object, "Exception", PyExceptionClass_Check) + + exception(handle scope, const char *name, handle base = PyExc_Exception) + : object(detail::exception_new(scope.ptr(), name, base.ptr()), + detail::steal_t()) { + detail::register_exception_translator( + [](const std::exception_ptr &p, void *payload) { + try { + std::rethrow_exception(p); + } catch (T &e) { + PyErr_SetString((PyObject *) payload, e.what()); + } + }, m_ptr); + } +}; + +NB_CORE void chain_error(handle type, const char *fmt, ...) noexcept; +[[noreturn]] NB_CORE void raise_from(python_error &e, handle type, const char *fmt, ...); + +NAMESPACE_END(NB_NAMESPACE) diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_func.h b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_func.h new file mode 100644 index 0000000..1cd98a1 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_func.h @@ -0,0 +1,406 @@ +/* + nanobind/nb_func.h: Functionality for binding C++ functions/methods + + Copyright (c) 2022 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +NAMESPACE_BEGIN(NB_NAMESPACE) +NAMESPACE_BEGIN(detail) + +template +bool from_python_keep_alive(Caster &c, PyObject **args, uint8_t *args_flags, + cleanup_list *cleanup, size_t index) { + size_t size_before = cleanup->size(); + if (!c.from_python(args[index], args_flags[index], cleanup)) + return false; + + // If an implicit conversion took place, update the 'args' array so that + // the keep_alive annotation can later process this change + size_t size_after = cleanup->size(); + if (size_after != size_before) + args[index] = (*cleanup)[size_after - 1]; + + return true; +} + +// Return the number of nb::arg and nb::arg_v types in the first I types Ts. +// Invoke with std::make_index_sequence() to provide +// an index pack 'Is' that parallels the types pack Ts. +template +constexpr size_t count_args_before_index(std::index_sequence) { + static_assert(sizeof...(Is) == sizeof...(Ts)); + return ((Is < I && std::is_base_of_v) + ... + 0); +} + +#if defined(NB_FREE_THREADED) +struct ft_args_collector { + PyObject **args; + handle h1; + handle h2; + size_t index = 0; + + NB_INLINE explicit ft_args_collector(PyObject **a) : args(a) {} + NB_INLINE void apply(arg_locked *) { + if (h1.ptr() == nullptr) + h1 = args[index]; + h2 = args[index]; + ++index; + } + NB_INLINE void apply(arg *) { ++index; } + NB_INLINE void apply(...) {} +}; + +struct ft_args_guard { + NB_INLINE void lock(const ft_args_collector& info) { + PyCriticalSection2_Begin(&cs, info.h1.ptr(), info.h2.ptr()); + } + ~ft_args_guard() { + PyCriticalSection2_End(&cs); + } + PyCriticalSection2 cs; +}; +#endif + +struct no_guard {}; + +template +NB_INLINE PyObject *func_create(Func &&func, Return (*)(Args...), + std::index_sequence is, + const Extra &...extra) { + using Info = func_extra_info; + + if constexpr (CheckGuard && !std::is_same_v) { + return func_create( + [func = (forward_t) func](Args... args) NB_INLINE_LAMBDA { + typename Info::call_guard::type g; + (void) g; + return func((forward_t) args...); + }, + (Return(*)(Args...)) nullptr, is, extra...); + } + + (void) is; + + // Detect locations of nb::args / nb::kwargs (if they exist). + // Find the first and last occurrence of each; we'll later make sure these + // match, in order to guarantee there's only one instance. + static constexpr size_t + args_pos_1 = index_1_v, args>...>, + args_pos_n = index_n_v, args>...>, + kwargs_pos_1 = index_1_v, kwargs>...>, + kwargs_pos_n = index_n_v, kwargs>...>, + nargs = sizeof...(Args); + + // Determine the number of nb::arg/nb::arg_v annotations + constexpr size_t nargs_provided = + (std::is_base_of_v + ... + 0); + constexpr bool is_method_det = + (std::is_same_v + ... + 0) != 0; + constexpr bool is_getter_det = + (std::is_same_v + ... + 0) != 0; + constexpr bool has_arg_annotations = nargs_provided > 0 && !is_getter_det; + + // Determine the number of potentially-locked function arguments + constexpr bool lock_self_det = + (std::is_same_v + ... + 0) != 0; + static_assert(Info::nargs_locked <= 2, + "At most two function arguments can be locked"); + static_assert(!(lock_self_det && !is_method_det), + "The nb::lock_self() annotation only applies to methods"); + + // Detect location of nb::kw_only annotation, if supplied. As with args/kwargs + // we find the first and last location and later verify they match each other. + // Note this is an index in Extra... while args/kwargs_pos_* are indices in + // Args... . + constexpr size_t + kwonly_pos_1 = index_1_v...>, + kwonly_pos_n = index_n_v...>; + // Arguments after nb::args are implicitly keyword-only even if there is no + // nb::kw_only annotation + constexpr bool explicit_kw_only = kwonly_pos_1 != sizeof...(Extra); + constexpr bool implicit_kw_only = args_pos_1 + 1 < kwargs_pos_1; + + // A few compile-time consistency checks + static_assert(args_pos_1 == args_pos_n && kwargs_pos_1 == kwargs_pos_n, + "Repeated use of nb::kwargs or nb::args in the function signature!"); + static_assert(!has_arg_annotations || nargs_provided + is_method_det == nargs, + "The number of nb::arg annotations must match the argument count!"); + static_assert(kwargs_pos_1 == nargs || kwargs_pos_1 + 1 == nargs, + "nb::kwargs must be the last element of the function signature!"); + static_assert(args_pos_1 == nargs || args_pos_1 < kwargs_pos_1, + "nb::args must precede nb::kwargs if both are present!"); + static_assert(has_arg_annotations || (!implicit_kw_only && !explicit_kw_only), + "Keyword-only arguments must have names!"); + + // Find the index in Args... of the first keyword-only parameter. Since + // the 'self' parameter doesn't get a nb::arg annotation, we must adjust + // by 1 for methods. Note that nargs_before_kw_only is only used if + // a kw_only annotation exists (i.e., if explicit_kw_only is true); + // the conditional is just to save the compiler some effort otherwise. + constexpr size_t nargs_before_kw_only = + explicit_kw_only + ? is_method_det + count_args_before_index( + std::make_index_sequence()) + : nargs; + + if constexpr (explicit_kw_only) { + static_assert(kwonly_pos_1 == kwonly_pos_n, + "Repeated use of nb::kw_only annotation!"); + + // If both kw_only and *args are specified, kw_only must be + // immediately after the nb::arg for *args. + static_assert(args_pos_1 == nargs || nargs_before_kw_only == args_pos_1 + 1, + "Arguments after nb::args are implicitly keyword-only; any " + "nb::kw_only() annotation must be positioned to reflect that!"); + + // If both kw_only and **kwargs are specified, kw_only must be + // before the nb::arg for **kwargs. + static_assert(nargs_before_kw_only < kwargs_pos_1, + "Variadic nb::kwargs are implicitly keyword-only; any " + "nb::kw_only() annotation must be positioned to reflect that!"); + } + + // Collect function signature information for the docstring + using cast_out = make_caster< + std::conditional_t, void_type, Return>>; + + // Compile-time function signature + static constexpr auto descr = + const_name("(") + + concat(type_descr( + make_caster>>::Name)...) + + const_name(") -> ") + cast_out::Name; + + // std::type_info for all function arguments + const std::type_info* descr_types[descr.type_count() + 1]; + descr.put_types(descr_types); + + // Auxiliary data structure to capture the provided function/closure + struct capture { + std::remove_reference_t func; + }; + + // The following temporary record will describe the function in detail + func_data_prelim f; + f.flags = (args_pos_1 < nargs ? (uint32_t) func_flags::has_var_args : 0) | + (kwargs_pos_1 < nargs ? (uint32_t) func_flags::has_var_kwargs : 0) | + (ReturnRef ? (uint32_t) func_flags::return_ref : 0) | + (has_arg_annotations ? (uint32_t) func_flags::has_args : 0); + + /* Store captured function inside 'func_data_prelim' if there is space. Issues + with aliasing are resolved via separate compilation of libnanobind. */ + if constexpr (sizeof(capture) <= sizeof(f.capture)) { + capture *cap = (capture *) f.capture; + new (cap) capture{ (forward_t) func }; + + if constexpr (!std::is_trivially_destructible_v) { + f.flags |= (uint32_t) func_flags::has_free; + f.free_capture = [](void *p) { + ((capture *) p)->~capture(); + }; + } + } else { + void **cap = (void **) f.capture; + cap[0] = new capture{ (forward_t) func }; + + f.flags |= (uint32_t) func_flags::has_free; + f.free_capture = [](void *p) { + delete (capture *) ((void **) p)[0]; + }; + } + + f.impl = [](void *p, PyObject **args, uint8_t *args_flags, rv_policy policy, + cleanup_list *cleanup) NB_INLINE_LAMBDA -> PyObject * { + (void) p; (void) args; (void) args_flags; (void) policy; (void) cleanup; + + const capture *cap; + if constexpr (sizeof(capture) <= sizeof(f.capture)) + cap = (capture *) p; + else + cap = (capture *) ((void **) p)[0]; + + tuple...> in; + (void) in; + +#if defined(NB_FREE_THREADED) + std::conditional_t guard; + if constexpr (Info::nargs_locked) { + ft_args_collector collector{args}; + if constexpr (is_method_det) { + if constexpr (lock_self_det) + collector.apply((arg_locked *) nullptr); + else + collector.apply((arg *) nullptr); + } + (collector.apply((Extra *) nullptr), ...); + guard.lock(collector); + } +#endif + + if constexpr (Info::keep_alive) { + if ((!from_python_keep_alive(in.template get(), args, + args_flags, cleanup, Is) || ...)) + return NB_NEXT_OVERLOAD; + } else { + if ((!in.template get().from_python(args[Is], args_flags[Is], + cleanup) || ...)) + return NB_NEXT_OVERLOAD; + } + + PyObject *result; + if constexpr (std::is_void_v) { +#if defined(_WIN32) // temporary workaround for an internal compiler error in MSVC + cap->func(static_cast>(in.template get())...); +#else + cap->func(in.template get().operator cast_t()...); +#endif + result = Py_None; + Py_INCREF(result); + } else { +#if defined(_WIN32) // temporary workaround for an internal compiler error in MSVC + result = cast_out::from_cpp( + cap->func(static_cast>(in.template get())...), + policy, cleanup).ptr(); +#else + result = cast_out::from_cpp( + cap->func((in.template get()) + .operator cast_t()...), + policy, cleanup).ptr(); +#endif + } + + if constexpr (Info::keep_alive) + (process_keep_alive(args, result, (Extra *) nullptr), ...); + + return result; + }; + + f.descr = descr.text; + f.descr_types = descr_types; + f.nargs = nargs; + + // Set nargs_pos to the number of C++ function parameters (Args...) that + // can be filled from Python positional arguments in a one-to-one fashion. + // This ends at: + // - the location of the variadic *args parameter, if present; otherwise + // - the location of the first keyword-only parameter, if any; otherwise + // - the location of the variadic **kwargs parameter, if present; otherwise + // - the end of the parameter list + // It's correct to give *args priority over kw_only because we verified + // above that kw_only comes afterward if both are present. It's correct + // to give kw_only priority over **kwargs because we verified above that + // kw_only comes before if both are present. + f.nargs_pos = args_pos_1 < nargs ? args_pos_1 : + explicit_kw_only ? nargs_before_kw_only : + kwargs_pos_1 < nargs ? kwargs_pos_1 : nargs; + + // Fill remaining fields of 'f' + size_t arg_index = 0; + (void) arg_index; + (func_extra_apply(f, extra, arg_index), ...); + + return nb_func_new((const void *) &f); +} + +NAMESPACE_END(detail) + +// The initial template parameter to cpp_function/cpp_function_def is +// used by class_ to ensure that member pointers are treated as members +// of the class being defined; other users can safely leave it at its +// default of void. + +template +NB_INLINE object cpp_function(Return (*f)(Args...), const Extra&... extra) { + return steal(detail::func_create( + f, f, std::make_index_sequence(), extra...)); +} + +template +NB_INLINE void cpp_function_def(Return (*f)(Args...), const Extra&... extra) { + detail::func_create( + f, f, std::make_index_sequence(), extra...); +} + +/// Construct a cpp_function from a lambda function (pot. with internal state) +template < + typename = void, typename Func, typename... Extra, + detail::enable_if_t>> = 0> +NB_INLINE object cpp_function(Func &&f, const Extra &...extra) { + using am = detail::analyze_method::operator())>; + return steal(detail::func_create( + (detail::forward_t) f, (typename am::func *) nullptr, + std::make_index_sequence(), extra...)); +} + +template < + typename = void, typename Func, typename... Extra, + detail::enable_if_t>> = 0> +NB_INLINE void cpp_function_def(Func &&f, const Extra &...extra) { + using am = detail::analyze_method::operator())>; + detail::func_create( + (detail::forward_t) f, (typename am::func *) nullptr, + std::make_index_sequence(), extra...); +} + +/// Construct a cpp_function from a class method (non-const) +template +NB_INLINE object cpp_function(Return (Class::*f)(Args...), const Extra &...extra) { + using T = std::conditional_t, Class, Target>; + return steal(detail::func_create( + [f](T *c, Args... args) NB_INLINE_LAMBDA -> Return { + return (c->*f)((detail::forward_t) args...); + }, + (Return(*)(T *, Args...)) nullptr, + std::make_index_sequence(), extra...)); +} + +template +NB_INLINE void cpp_function_def(Return (Class::*f)(Args...), const Extra &...extra) { + using T = std::conditional_t, Class, Target>; + detail::func_create( + [f](T *c, Args... args) NB_INLINE_LAMBDA -> Return { + return (c->*f)((detail::forward_t) args...); + }, + (Return(*)(T *, Args...)) nullptr, + std::make_index_sequence(), extra...); +} + +/// Construct a cpp_function from a class method (const) +template +NB_INLINE object cpp_function(Return (Class::*f)(Args...) const, const Extra &...extra) { + using T = std::conditional_t, Class, Target>; + return steal(detail::func_create( + [f](const T *c, Args... args) NB_INLINE_LAMBDA -> Return { + return (c->*f)((detail::forward_t) args...); + }, + (Return(*)(const T *, Args...)) nullptr, + std::make_index_sequence(), extra...)); +} + +template +NB_INLINE void cpp_function_def(Return (Class::*f)(Args...) const, const Extra &...extra) { + using T = std::conditional_t, Class, Target>; + detail::func_create( + [f](const T *c, Args... args) NB_INLINE_LAMBDA -> Return { + return (c->*f)((detail::forward_t) args...); + }, + (Return(*)(const T *, Args...)) nullptr, + std::make_index_sequence(), extra...); +} + +template +module_ &module_::def(const char *name_, Func &&f, const Extra &...extra) { + cpp_function_def((detail::forward_t) f, scope(*this), + name(name_), extra...); + return *this; +} + +NAMESPACE_END(NB_NAMESPACE) diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_lib.h b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_lib.h new file mode 100644 index 0000000..3541bce --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_lib.h @@ -0,0 +1,566 @@ +/* + nanobind/nb_lib.h: Interface to libnanobind.so + + Copyright (c) 2022 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +NAMESPACE_BEGIN(NB_NAMESPACE) + +// Forward declarations for types in ndarray.h (1) +namespace dlpack { struct dltensor; struct dtype; } + +NAMESPACE_BEGIN(detail) + +// Forward declarations for types in ndarray.h (2) +struct ndarray_handle; +struct ndarray_config; + +/** + * Helper class to clean temporaries created by function dispatch. + * The first element serves a special role: it stores the 'self' + * object of method calls (for rv_policy::reference_internal). + */ +struct NB_CORE cleanup_list { +public: + static constexpr uint32_t Small = 6; + + cleanup_list(PyObject *self) : + m_size{1}, + m_capacity{Small}, + m_data{m_local} { + m_local[0] = self; + } + + ~cleanup_list() = default; + + /// Append a single PyObject to the cleanup stack + NB_INLINE void append(PyObject *value) noexcept { + if (m_size >= m_capacity) + expand(); + m_data[m_size++] = value; + } + + NB_INLINE PyObject *self() const { + return m_local[0]; + } + + /// Decrease the reference count of all appended objects + void release() noexcept; + + /// Does the list contain any entries? (besides the 'self' argument) + bool used() { return m_size != 1; } + + /// Return the size of the cleanup stack + size_t size() const { return m_size; } + + /// Subscript operator + PyObject *operator[](size_t index) const { return m_data[index]; } + +protected: + /// Out of memory, expand.. + void expand() noexcept; + +protected: + uint32_t m_size; + uint32_t m_capacity; + PyObject **m_data; + PyObject *m_local[Small]; +}; + +// ======================================================================== + +/// Raise a runtime error with the given message +#if defined(__GNUC__) + __attribute__((noreturn, __format__ (__printf__, 1, 2))) +#else + [[noreturn]] +#endif +NB_CORE void raise(const char *fmt, ...); + +/// Raise a type error with the given message +#if defined(__GNUC__) + __attribute__((noreturn, __format__ (__printf__, 1, 2))) +#else + [[noreturn]] +#endif +NB_CORE void raise_type_error(const char *fmt, ...); + +/// Abort the process with a fatal error +#if defined(__GNUC__) + __attribute__((noreturn, __format__ (__printf__, 1, 2))) +#else + [[noreturn]] +#endif +NB_CORE void fail(const char *fmt, ...) noexcept; + +/// Raise nanobind::python_error after an error condition was found +[[noreturn]] NB_CORE void raise_python_error(); + +/// Raise nanobind::next_overload +NB_CORE void raise_next_overload_if_null(void *p); + +/// Raise nanobind::cast_error +[[noreturn]] NB_CORE void raise_cast_error(); + +// ======================================================================== + +NB_CORE void init(const char *domain); + +// ======================================================================== + +/// Convert a Python object into a Python unicode string +NB_CORE PyObject *str_from_obj(PyObject *o); + +/// Convert an UTF8 null-terminated C string into a Python unicode string +NB_CORE PyObject *str_from_cstr(const char *c); + +/// Convert an UTF8 C string + size into a Python unicode string +NB_CORE PyObject *str_from_cstr_and_size(const char *c, size_t n); + +// ======================================================================== + +/// Convert a Python object into a Python byte string +NB_CORE PyObject *bytes_from_obj(PyObject *o); + +/// Convert an UTF8 null-terminated C string into a Python byte string +NB_CORE PyObject *bytes_from_cstr(const char *c); + +/// Convert a memory region into a Python byte string +NB_CORE PyObject *bytes_from_cstr_and_size(const void *c, size_t n); + +// ======================================================================== + +/// Convert a Python object into a Python byte array +NB_CORE PyObject *bytearray_from_obj(PyObject *o); + +/// Convert a memory region into a Python byte array +NB_CORE PyObject *bytearray_from_cstr_and_size(const void *c, size_t n); + +// ======================================================================== + +/// Convert a Python object into a Python boolean object +NB_CORE PyObject *bool_from_obj(PyObject *o); + +/// Convert a Python object into a Python integer object +NB_CORE PyObject *int_from_obj(PyObject *o); + +/// Convert a Python object into a Python floating point object +NB_CORE PyObject *float_from_obj(PyObject *o); + +// ======================================================================== + +/// Convert a Python object into a Python list +NB_CORE PyObject *list_from_obj(PyObject *o); + +/// Convert a Python object into a Python tuple +NB_CORE PyObject *tuple_from_obj(PyObject *o); + +/// Convert a Python object into a Python set +NB_CORE PyObject *set_from_obj(PyObject *o); + +// ======================================================================== + +/// Get an object attribute or raise an exception +NB_CORE PyObject *getattr(PyObject *obj, const char *key); +NB_CORE PyObject *getattr(PyObject *obj, PyObject *key); + +/// Get an object attribute or return a default value (never raises) +NB_CORE PyObject *getattr(PyObject *obj, const char *key, PyObject *def) noexcept; +NB_CORE PyObject *getattr(PyObject *obj, PyObject *key, PyObject *def) noexcept; + +/// Get an object attribute or raise an exception. Skip if 'out' is non-null +NB_CORE void getattr_or_raise(PyObject *obj, const char *key, PyObject **out); +NB_CORE void getattr_or_raise(PyObject *obj, PyObject *key, PyObject **out); + +/// Set an object attribute or raise an exception +NB_CORE void setattr(PyObject *obj, const char *key, PyObject *value); +NB_CORE void setattr(PyObject *obj, PyObject *key, PyObject *value); + +/// Delete an object attribute or raise an exception +NB_CORE void delattr(PyObject *obj, const char *key); +NB_CORE void delattr(PyObject *obj, PyObject *key); + +// ======================================================================== + +/// Index into an object or raise an exception. Skip if 'out' is non-null +NB_CORE void getitem_or_raise(PyObject *obj, Py_ssize_t, PyObject **out); +NB_CORE void getitem_or_raise(PyObject *obj, const char *key, PyObject **out); +NB_CORE void getitem_or_raise(PyObject *obj, PyObject *key, PyObject **out); + +/// Set an item or raise an exception +NB_CORE void setitem(PyObject *obj, Py_ssize_t, PyObject *value); +NB_CORE void setitem(PyObject *obj, const char *key, PyObject *value); +NB_CORE void setitem(PyObject *obj, PyObject *key, PyObject *value); + +/// Delete an item or raise an exception +NB_CORE void delitem(PyObject *obj, Py_ssize_t); +NB_CORE void delitem(PyObject *obj, const char *key); +NB_CORE void delitem(PyObject *obj, PyObject *key); + +// ======================================================================== + +/// Determine the length of a Python object +NB_CORE size_t obj_len(PyObject *o); + +/// Try to roughly determine the length of a Python object +NB_CORE size_t obj_len_hint(PyObject *o) noexcept; + +/// Obtain a string representation of a Python object +NB_CORE PyObject* obj_repr(PyObject *o); + +/// Perform a comparison between Python objects and handle errors +NB_CORE bool obj_comp(PyObject *a, PyObject *b, int value); + +/// Perform an unary operation on a Python object with error handling +NB_CORE PyObject *obj_op_1(PyObject *a, PyObject* (*op)(PyObject*)); + +/// Perform an unary operation on a Python object with error handling +NB_CORE PyObject *obj_op_2(PyObject *a, PyObject *b, + PyObject *(*op)(PyObject *, PyObject *)); + +// Perform a vector function call +NB_CORE PyObject *obj_vectorcall(PyObject *base, PyObject *const *args, + size_t nargsf, PyObject *kwnames, + bool method_call); + +/// Create an iterator from 'o', raise an exception in case of errors +NB_CORE PyObject *obj_iter(PyObject *o); + +/// Advance the iterator 'o', raise an exception in case of errors +NB_CORE PyObject *obj_iter_next(PyObject *o); + +// ======================================================================== + +// Conversion validity check done by nb::make_tuple +NB_CORE void tuple_check(PyObject *tuple, size_t nargs); + +// ======================================================================== + +// Append a single argument to a function call +NB_CORE void call_append_arg(PyObject *args, size_t &nargs, PyObject *value); + +// Append a variable-length sequence of arguments to a function call +NB_CORE void call_append_args(PyObject *args, size_t &nargs, PyObject *value); + +// Append a single keyword argument to a function call +NB_CORE void call_append_kwarg(PyObject *kwargs, const char *name, PyObject *value); + +// Append a variable-length dictionary of keyword arguments to a function call +NB_CORE void call_append_kwargs(PyObject *kwargs, PyObject *value); + +// ======================================================================== + +// If the given sequence has the size 'size', return a pointer to its contents. +// May produce a temporary. +NB_CORE PyObject **seq_get_with_size(PyObject *seq, size_t size, + PyObject **temp) noexcept; + +// Like the above, but return the size instead of checking it. +NB_CORE PyObject **seq_get(PyObject *seq, size_t *size, + PyObject **temp) noexcept; + +// ======================================================================== + +/// Create a new capsule object with a name +NB_CORE PyObject *capsule_new(const void *ptr, const char *name, + void (*cleanup)(void *) noexcept) noexcept; + +// ======================================================================== + +/// Create a Python function object for the given function record +NB_CORE PyObject *nb_func_new(const void *data) noexcept; + +// ======================================================================== + +/// Create a Python type object for the given type record +struct type_init_data; +NB_CORE PyObject *nb_type_new(const type_init_data *c) noexcept; + +/// Extract a pointer to a C++ type underlying a Python object, if possible +NB_CORE bool nb_type_get(const std::type_info *t, PyObject *o, uint8_t flags, + cleanup_list *cleanup, void **out) noexcept; + +/// Cast a C++ type instance into a Python object +NB_CORE PyObject *nb_type_put(const std::type_info *cpp_type, void *value, + rv_policy rvp, cleanup_list *cleanup, + bool *is_new = nullptr) noexcept; + +// Special version of nb_type_put for polymorphic classes +NB_CORE PyObject *nb_type_put_p(const std::type_info *cpp_type, + const std::type_info *cpp_type_p, void *value, + rv_policy rvp, cleanup_list *cleanup, + bool *is_new = nullptr) noexcept; + +// Special version of 'nb_type_put' for unique pointers and ownership transfer +NB_CORE PyObject *nb_type_put_unique(const std::type_info *cpp_type, + void *value, cleanup_list *cleanup, + bool cpp_delete) noexcept; + +// Special version of 'nb_type_put_unique' for polymorphic classes +NB_CORE PyObject *nb_type_put_unique_p(const std::type_info *cpp_type, + const std::type_info *cpp_type_p, + void *value, cleanup_list *cleanup, + bool cpp_delete) noexcept; + +/// Try to reliquish ownership from Python object to a unique_ptr; +/// return true if successful, false if not. (Failure is only +/// possible if `cpp_delete` is true.) +NB_CORE bool nb_type_relinquish_ownership(PyObject *o, bool cpp_delete) noexcept; + +/// Reverse the effects of nb_type_relinquish_ownership(). +NB_CORE void nb_type_restore_ownership(PyObject *o, bool cpp_delete) noexcept; + +/// Get a pointer to a user-defined 'extra' value associated with the nb_type t. +NB_CORE void *nb_type_supplement(PyObject *t) noexcept; + +/// Check if the given python object represents a nanobind type +NB_CORE bool nb_type_check(PyObject *t) noexcept; + +/// Return the size of the type wrapped by the given nanobind type object +NB_CORE size_t nb_type_size(PyObject *t) noexcept; + +/// Return the alignment of the type wrapped by the given nanobind type object +NB_CORE size_t nb_type_align(PyObject *t) noexcept; + +/// Return a unicode string representing the long-form name of the given type +NB_CORE PyObject *nb_type_name(PyObject *t) noexcept; + +/// Return a unicode string representing the long-form name of object's type +NB_CORE PyObject *nb_inst_name(PyObject *o) noexcept; + +/// Return the C++ type_info wrapped by the given nanobind type object +NB_CORE const std::type_info *nb_type_info(PyObject *t) noexcept; + +/// Get a pointer to the instance data of a nanobind instance (nb_inst) +NB_CORE void *nb_inst_ptr(PyObject *o) noexcept; + +/// Check if a Python type object wraps an instance of a specific C++ type +NB_CORE bool nb_type_isinstance(PyObject *obj, const std::type_info *t) noexcept; + +/// Search for the Python type object associated with a C++ type +NB_CORE PyObject *nb_type_lookup(const std::type_info *t) noexcept; + +/// Allocate an instance of type 't' +NB_CORE PyObject *nb_inst_alloc(PyTypeObject *t); + +/// Allocate an zero-initialized instance of type 't' +NB_CORE PyObject *nb_inst_alloc_zero(PyTypeObject *t); + +/// Allocate an instance of type 't' referencing the existing 'ptr' +NB_CORE PyObject *nb_inst_reference(PyTypeObject *t, void *ptr, + PyObject *parent); + +/// Allocate an instance of type 't' taking ownership of the existing 'ptr' +NB_CORE PyObject *nb_inst_take_ownership(PyTypeObject *t, void *ptr); + +/// Call the destructor of the given python object +NB_CORE void nb_inst_destruct(PyObject *o) noexcept; + +/// Zero-initialize a POD type and mark it as ready + to be destructed upon GC +NB_CORE void nb_inst_zero(PyObject *o) noexcept; + +/// Copy-construct 'dst' from 'src', mark it as ready and to be destructed (must have the same nb_type) +NB_CORE void nb_inst_copy(PyObject *dst, const PyObject *src) noexcept; + +/// Move-construct 'dst' from 'src', mark it as ready and to be destructed (must have the same nb_type) +NB_CORE void nb_inst_move(PyObject *dst, const PyObject *src) noexcept; + +/// Destruct 'dst', copy-construct 'dst' from 'src', mark ready and retain 'destruct' status (must have the same nb_type) +NB_CORE void nb_inst_replace_copy(PyObject *dst, const PyObject *src) noexcept; + +/// Destruct 'dst', move-construct 'dst' from 'src', mark ready and retain 'destruct' status (must have the same nb_type) +NB_CORE void nb_inst_replace_move(PyObject *dst, const PyObject *src) noexcept; + +/// Check if a particular instance uses a Python-derived type +NB_CORE bool nb_inst_python_derived(PyObject *o) noexcept; + +/// Overwrite the instance's ready/destruct flags +NB_CORE void nb_inst_set_state(PyObject *o, bool ready, bool destruct) noexcept; + +/// Query the 'ready' and 'destruct' flags of an instance +NB_CORE std::pair nb_inst_state(PyObject *o) noexcept; + +// ======================================================================== + +// Create and install a Python property object +NB_CORE void property_install(PyObject *scope, const char *name, + PyObject *getter, PyObject *setter) noexcept; + +NB_CORE void property_install_static(PyObject *scope, const char *name, + PyObject *getter, + PyObject *setter) noexcept; + +// ======================================================================== + +NB_CORE PyObject *get_override(void *ptr, const std::type_info *type, + const char *name, bool pure); + +// ======================================================================== + +// Ensure that 'patient' cannot be GCed while 'nurse' is alive +NB_CORE void keep_alive(PyObject *nurse, PyObject *patient); + +// Keep 'payload' alive until 'nurse' is GCed +NB_CORE void keep_alive(PyObject *nurse, void *payload, + void (*deleter)(void *) noexcept) noexcept; + + +// ======================================================================== + +/// Indicate to nanobind that an implicit constructor can convert 'src' -> 'dst' +NB_CORE void implicitly_convertible(const std::type_info *src, + const std::type_info *dst) noexcept; + +/// Register a callback to check if implicit conversion to 'dst' is possible +NB_CORE void implicitly_convertible(bool (*predicate)(PyTypeObject *, + PyObject *, + cleanup_list *), + const std::type_info *dst) noexcept; + +// ======================================================================== + +struct enum_init_data; + +/// Create a new enumeration type +NB_CORE PyObject *enum_create(enum_init_data *) noexcept; + +/// Append an entry to an enumeration +NB_CORE void enum_append(PyObject *tp, const char *name, + int64_t value, const char *doc) noexcept; + +// Query an enumeration's Python object -> integer value map +NB_CORE bool enum_from_python(const std::type_info *, PyObject *, int64_t *, + uint8_t flags) noexcept; + +// Query an enumeration's integer value -> Python object map +NB_CORE PyObject *enum_from_cpp(const std::type_info *, int64_t) noexcept; + +/// Export enum entries to the parent scope +NB_CORE void enum_export(PyObject *tp); + +// ======================================================================== + +/// Try to import a Python extension module, raises an exception upon failure +NB_CORE PyObject *module_import(const char *name); + +/// Try to import a Python extension module, raises an exception upon failure +NB_CORE PyObject *module_import(PyObject *name); + +/// Create a new extension module with the given name +NB_CORE PyObject *module_new(const char *name, PyModuleDef *def) noexcept; + +/// Create a submodule of an existing module +NB_CORE PyObject *module_new_submodule(PyObject *base, const char *name, + const char *doc) noexcept; + + +// ======================================================================== + +// Try to import a reference-counted ndarray object via DLPack +NB_CORE ndarray_handle *ndarray_import(PyObject *o, + const ndarray_config *c, + bool convert, + cleanup_list *cleanup) noexcept; + +// Describe a local ndarray object using a DLPack capsule +NB_CORE ndarray_handle *ndarray_create(void *value, size_t ndim, + const size_t *shape, PyObject *owner, + const int64_t *strides, + dlpack::dtype dtype, bool ro, + int device, int device_id, + char order); + +/// Increase the reference count of the given ndarray object; returns a pointer +/// to the underlying DLTensor +NB_CORE dlpack::dltensor *ndarray_inc_ref(ndarray_handle *) noexcept; + +/// Decrease the reference count of the given ndarray object +NB_CORE void ndarray_dec_ref(ndarray_handle *) noexcept; + +/// Wrap a ndarray_handle* into a PyCapsule +NB_CORE PyObject *ndarray_export(ndarray_handle *, int framework, + rv_policy policy, cleanup_list *cleanup) noexcept; + +/// Check if an object represents an ndarray +NB_CORE bool ndarray_check(PyObject *o) noexcept; + +// ======================================================================== + +/// Print to stdout using Python +NB_CORE void print(PyObject *file, PyObject *str, PyObject *end); + +// ======================================================================== + +typedef void (*exception_translator)(const std::exception_ptr &, void *); + +NB_CORE void register_exception_translator(exception_translator translator, + void *payload); + +NB_CORE PyObject *exception_new(PyObject *mod, const char *name, + PyObject *base); + +// ======================================================================== + +NB_CORE bool load_i8 (PyObject *o, uint8_t flags, int8_t *out) noexcept; +NB_CORE bool load_u8 (PyObject *o, uint8_t flags, uint8_t *out) noexcept; +NB_CORE bool load_i16(PyObject *o, uint8_t flags, int16_t *out) noexcept; +NB_CORE bool load_u16(PyObject *o, uint8_t flags, uint16_t *out) noexcept; +NB_CORE bool load_i32(PyObject *o, uint8_t flags, int32_t *out) noexcept; +NB_CORE bool load_u32(PyObject *o, uint8_t flags, uint32_t *out) noexcept; +NB_CORE bool load_i64(PyObject *o, uint8_t flags, int64_t *out) noexcept; +NB_CORE bool load_u64(PyObject *o, uint8_t flags, uint64_t *out) noexcept; +NB_CORE bool load_f32(PyObject *o, uint8_t flags, float *out) noexcept; +NB_CORE bool load_f64(PyObject *o, uint8_t flags, double *out) noexcept; + +// ======================================================================== + +/// Increase the reference count of 'o', and check that the GIL is held +NB_CORE void incref_checked(PyObject *o) noexcept; + +/// Decrease the reference count of 'o', and check that the GIL is held +NB_CORE void decref_checked(PyObject *o) noexcept; + +// ======================================================================== + +NB_CORE bool leak_warnings() noexcept; +NB_CORE bool implicit_cast_warnings() noexcept; +NB_CORE void set_leak_warnings(bool value) noexcept; +NB_CORE void set_implicit_cast_warnings(bool value) noexcept; + +// ======================================================================== + +NB_CORE bool iterable_check(PyObject *o) noexcept; + +// ======================================================================== + +NB_CORE void slice_compute(PyObject *slice, Py_ssize_t size, + Py_ssize_t &start, Py_ssize_t &stop, + Py_ssize_t &step, size_t &slice_length); + +// ======================================================================== + +NB_CORE bool issubclass(PyObject *a, PyObject *b); + +// ======================================================================== + +NB_CORE PyObject *repr_list(PyObject *o); +NB_CORE PyObject *repr_map(PyObject *o); + +NB_CORE bool is_alive() noexcept; + +#if NB_TYPE_GET_SLOT_IMPL +NB_CORE void *type_get_slot(PyTypeObject *t, int slot_id); +#endif + +NB_CORE PyObject *dict_get_item_ref_or_fail(PyObject *d, PyObject *k); + +NAMESPACE_END(detail) + +using detail::raise; +using detail::raise_type_error; +using detail::raise_python_error; + +NAMESPACE_END(NB_NAMESPACE) diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_misc.h b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_misc.h new file mode 100644 index 0000000..b29d6b6 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_misc.h @@ -0,0 +1,118 @@ +/* + nanobind/nb_misc.h: Miscellaneous bits (GIL, etc.) + + Copyright (c) 2022 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +NAMESPACE_BEGIN(NB_NAMESPACE) + +struct gil_scoped_acquire { +public: + NB_NONCOPYABLE(gil_scoped_acquire) + gil_scoped_acquire() noexcept : state(PyGILState_Ensure()) { } + ~gil_scoped_acquire() { PyGILState_Release(state); } + +private: + const PyGILState_STATE state; +}; + +struct gil_scoped_release { +public: + NB_NONCOPYABLE(gil_scoped_release) + gil_scoped_release() noexcept : state(PyEval_SaveThread()) { } + ~gil_scoped_release() { PyEval_RestoreThread(state); } + +private: + PyThreadState *state; +}; + +struct ft_mutex { +public: + NB_NONCOPYABLE(ft_mutex) + ft_mutex() = default; + +#if !defined(NB_FREE_THREADED) + void lock() { } + void unlock() { } +#else + void lock() { PyMutex_Lock(&mutex); } + void unlock() { PyMutex_Unlock(&mutex); } +private: + PyMutex mutex { 0 }; +#endif +}; + +struct ft_lock_guard { +public: + NB_NONCOPYABLE(ft_lock_guard) + ft_lock_guard(ft_mutex &m) : m(m) { m.lock(); } + ~ft_lock_guard() { m.unlock(); } +private: + ft_mutex &m; +}; + + +struct ft_object_guard { +public: + NB_NONCOPYABLE(ft_object_guard) +#if !defined(NB_FREE_THREADED) + ft_object_guard(handle) { } +#else + ft_object_guard(handle h) { PyCriticalSection_Begin(&cs, h.ptr()); } + ~ft_object_guard() { PyCriticalSection_End(&cs); } +private: + PyCriticalSection cs; +#endif +}; + +struct ft_object2_guard { +public: + NB_NONCOPYABLE(ft_object2_guard) +#if !defined(NB_FREE_THREADED) + ft_object2_guard(handle, handle) { } +#else + ft_object2_guard(handle h1, handle h2) { PyCriticalSection2_Begin(&cs, h1.ptr(), h2.ptr()); } + ~ft_object2_guard() { PyCriticalSection2_End(&cs); } +private: + PyCriticalSection2 cs; +#endif +}; + +inline bool leak_warnings() noexcept { + return detail::leak_warnings(); +} + +inline bool implicit_cast_warnings() noexcept { + return detail::implicit_cast_warnings(); +} + +inline void set_leak_warnings(bool value) noexcept { + detail::set_leak_warnings(value); +} + +inline void set_implicit_cast_warnings(bool value) noexcept { + detail::set_implicit_cast_warnings(value); +} + +inline dict globals() { + PyObject *p = PyEval_GetGlobals(); + if (!p) + raise("nanobind::globals(): no frame is currently executing!"); + return borrow(p); +} + +inline Py_hash_t hash(handle h) { + Py_hash_t rv = PyObject_Hash(h.ptr()); + if (rv == -1 && PyErr_Occurred()) + nanobind::raise_python_error(); + return rv; +} + +inline bool is_alive() noexcept { + return detail::is_alive(); +} + +NAMESPACE_END(NB_NAMESPACE) diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_python.h b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_python.h new file mode 100644 index 0000000..356500c --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_python.h @@ -0,0 +1,61 @@ +/* + nanobind/nb_python.h: Include CPython headers while temporarily disabling + certain warnings. Also, disable dangerous preprocessor definitions. + + Copyright (c) 2022 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +/// Include Python header, disable linking to pythonX_d.lib on Windows in debug mode + +#if defined(_MSC_VER) +# pragma warning(push) +# if defined(_DEBUG) && !defined(Py_DEBUG) +# define NB_DEBUG_MARKER +# undef _DEBUG +# endif +#endif + +#include +#include +#include +#include + +/* Python #defines overrides on all sorts of core functions, which + tends to weak havok in C++ codebases that expect these to work + like regular functions (potentially with several overloads) */ +#if defined(isalnum) +# undef isalnum +# undef isalpha +# undef islower +# undef isspace +# undef isupper +# undef tolower +# undef toupper +#endif + +#if defined(copysign) +# undef copysign +#endif + +#if defined(setter) +# undef setter +#endif + +#if defined(getter) +# undef getter +#endif + +#if defined(_MSC_VER) +# if defined(NB_DEBUG_MARKER) +# define _DEBUG +# undef NB_DEBUG_MARKER +# endif +# pragma warning(pop) +#endif + +#if PY_VERSION_HEX < 0x03080000 +# error The nanobind library requires Python 3.8 (or newer) +#endif diff --git a/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_traits.h b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_traits.h new file mode 100644 index 0000000..4480c86 --- /dev/null +++ b/third-party/RemoteInput/RemoteInput/Thirdparty/nanobind/include/nanobind/nb_traits.h @@ -0,0 +1,220 @@ +/* + nanobind/nb_traits.h: type traits for metaprogramming in nanobind + + Copyright (c) 2022 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +NAMESPACE_BEGIN(NB_NAMESPACE) +using ssize_t = std::make_signed_t; + +NAMESPACE_BEGIN(detail) + +struct void_type { }; + +template struct index_1; +template struct index_n; + +template <> struct index_1<> { constexpr static size_t value = 0; }; +template <> struct index_n<> { constexpr static size_t value = 0; }; + +template struct index_1 { + constexpr static size_t value_rec = index_1::value; + constexpr static size_t value = B ? 0 : (value_rec + 1); +}; + +template struct index_n { + constexpr static size_t value_rec = index_n::value; + constexpr static size_t value = + (value_rec < sizeof...(Bs) || !B) ? (value_rec + 1) : 0; +}; + +template constexpr size_t index_1_v = index_1::value; +template constexpr size_t index_n_v = index_n::value; + +/// Helper template to strip away type modifiers +template struct intrinsic_type { using type = T; }; +template struct intrinsic_type { using type = typename intrinsic_type::type; }; +template struct intrinsic_type { using type = typename intrinsic_type::type; }; +template struct intrinsic_type { using type = typename intrinsic_type::type; }; +template struct intrinsic_type { using type = typename intrinsic_type::type; }; +template struct intrinsic_type { using type = typename intrinsic_type::type; }; +template struct intrinsic_type { using type = typename intrinsic_type::type; }; +template using intrinsic_t = typename intrinsic_type::type; + +// More relaxed pointer test +template +constexpr bool is_pointer_v = std::is_pointer_v>; + +template +using forwarded_type = std::conditional_t, + std::remove_reference_t &, + std::remove_reference_t &&>; + +/// Forwards a value U as rvalue or lvalue according to whether T is rvalue or lvalue; typically +/// used for forwarding a container's elements. +template NB_INLINE forwarded_type forward_like_(U &&u) { + return (forwarded_type) u; +} + +template +constexpr bool is_std_char_v = + std::is_same_v +#if defined(NB_HAS_U8STRING) + || std::is_same_v /* std::u8string */ +#endif + || std::is_same_v || + std::is_same_v || std::is_same_v; + +template using enable_if_t = std::enable_if_t; + +/// Check if a function is a lambda function +template +constexpr bool is_lambda_v = !std::is_function_v && !std::is_pointer_v && + !std::is_member_pointer_v; + +/// Inspect the signature of a method call +template struct analyze_method { }; +template +struct analyze_method { + using func = Ret(Args...); + static constexpr size_t argc = sizeof...(Args); +}; + +template +struct analyze_method { + using func = Ret(Args...); + static constexpr size_t argc = sizeof...(Args); +}; + +template +struct analyze_method { + using func = Ret(Args...); + static constexpr size_t argc = sizeof...(Args); +}; + +template +struct analyze_method { + using func = Ret(Args...); + static constexpr size_t argc = sizeof...(Args); +}; + +template +struct strip_function_object { + using type = typename analyze_method::func; +}; + +// Extracts the function signature from a function, function pointer or lambda. +template > +using function_signature_t = std::conditional_t< + std::is_function_v, F, + typename std::conditional_t< + std::is_pointer_v || std::is_member_pointer_v, + std::remove_pointer, + strip_function_object>::type>; + +template +using forward_t = std::conditional_t, T, T &&>; + +template inline constexpr bool false_v = false; + +template struct overload_cast_impl { + template + constexpr auto operator()(Return (*pf)(Args...)) const noexcept + -> decltype(pf) { return pf; } + + template + constexpr auto operator()(Return (Class::*pmf)(Args...), std::false_type = {}) const noexcept + -> decltype(pmf) { return pmf; } + + template + constexpr auto operator()(Return (Class::*pmf)(Args...) const, std::true_type) const noexcept + -> decltype(pmf) { return pmf; } +}; + +/// Detector pattern +template typename Op, typename Arg> +struct detector : std::false_type { }; + +template