Add project files (part 1/2)

Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
tonihaxandClaude Sonnet 5 committed 2026-11-05 19:45:34 +01:00
1 parent 3721eaecb2
commit 73baf8d02d
274 files changed
+83593

No files matched your search

+2
View File
@@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto
+5
View File
@@ -0,0 +1,5 @@
build/
injector/build-*/
__pycache__/
*.pyc
decrypted files/
+102
View File
@@ -0,0 +1,102 @@
cmake_minimum_required(VERSION 3.20)
project(slinky LANGUAGES C CXX)
# clang-cl + lld-link without a Windows SDK: CMake still wants a manifest tool for the DLL link step. LLVM ships
# llvm-mt, which takes the same arguments as mt.exe; without it the build fails after all objects are compiled.
if(WIN32 AND NOT CMAKE_MT)
find_program(SLINKY_MT NAMES llvm-mt)
if(SLINKY_MT)
set(CMAKE_MT "${SLINKY_MT}")
endif()
endif()
# The client as one shared library that is loaded into the Minecraft JVM.
# engine src/engine the client's own engine (JNI, JVMTI, GUI, module manager, ...), start-up in protected/
# logic src/logic the module logic (one function per original routine)
# src/logic/*/*.h module descriptors' callbacks (GUI, setting registration, status); each one is compiled
# on its own (they declare the settings as opaque blobs, the logic .cpp as typed values)
# data src/data .rdata/.data of the original image (strings, tables, module descriptors), generated
# platform src/platform everything that depends on the operating system, NanoVG, GL loader, C runtime bindings
if(NOT WIN32)
message(FATAL_ERROR "Only the Windows build exists so far. Cross-compile with\n"
" cmake -S . -B build/cmake -DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-windows-clang-cl.cmake\n"
"(a Linux platform layer is planned: JVMTI agent, GLFW/X11 input, dlopen)")
endif()
set(CMAKE_CXX_FLAGS "" CACHE STRING "" FORCE)
set(CMAKE_C_FLAGS "" CACHE STRING "" FORCE)
# ---- sources ----
file(GLOB_RECURSE ENGINE_SRC CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/src/engine/*.cpp)
file(GLOB_RECURSE LOGIC_SRC CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/src/logic/*.cpp)
file(GLOB_RECURSE MODULE_SRC CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/src/logic/*.h)
set_source_files_properties(${MODULE_SRC} PROPERTIES LANGUAGE CXX COMPILE_OPTIONS /TP)
file(GLOB PLATFORM_SRC CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/src/platform/*.cpp ${CMAKE_SOURCE_DIR}/src/platform/win/*.cpp)
# stack probe for large frames (clang-cl would take it from the C runtime library)
set(CHKSTK_OBJ ${CMAKE_BINARY_DIR}/chkstk.obj)
# NanoVG core (+ fontstash, stb_truetype, stb_image) and its GL3 backend
set(NANOVG_SRC ${CMAKE_SOURCE_DIR}/third_party/nanovg/src/nanovg.c ${CMAKE_SOURCE_DIR}/src/platform/nanovg_impl.c)
# ---- the original's data, assembled (GNU syntax) ----
find_program(SLINKY_CLANG clang REQUIRED)
set(DATA_OBJ)
foreach(part rdata data iat)
set(obj ${CMAKE_BINARY_DIR}/data_${part}.obj)
add_custom_command(OUTPUT ${obj}
COMMAND ${SLINKY_CLANG} --target=x86_64-pc-windows-msvc -c ${CMAKE_SOURCE_DIR}/src/data/${part}.s -o ${obj}
DEPENDS ${CMAKE_SOURCE_DIR}/src/data/${part}.s VERBATIM)
list(APPEND DATA_OBJ ${obj})
endforeach()
add_custom_command(OUTPUT ${CHKSTK_OBJ}
COMMAND ${SLINKY_CLANG} --target=x86_64-pc-windows-msvc -c ${CMAKE_SOURCE_DIR}/src/platform/win/chkstk.s -o ${CHKSTK_OBJ}
DEPENDS ${CMAKE_SOURCE_DIR}/src/platform/win/chkstk.s VERBATIM)
list(APPEND DATA_OBJ ${CHKSTK_OBJ})
# ---- import libraries from the .def files (no Windows SDK needed) ----
find_program(SLINKY_DLLTOOL NAMES llvm-dlltool REQUIRED)
set(IMPORT_LIBS)
foreach(dll kernel32 user32 shell32 ucrtbase)
set(lib ${CMAKE_BINARY_DIR}/imports/${dll}.lib)
add_custom_command(OUTPUT ${lib}
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/imports
COMMAND ${SLINKY_DLLTOOL} -m i386:x86-64 -d ${CMAKE_SOURCE_DIR}/cmake/imports/${dll}.def -l ${lib}
DEPENDS ${CMAKE_SOURCE_DIR}/cmake/imports/${dll}.def VERBATIM)
list(APPEND IMPORT_LIBS ${lib})
endforeach()
add_custom_target(slinky_imports DEPENDS ${IMPORT_LIBS})
add_library(slinky SHARED ${ENGINE_SRC} ${LOGIC_SRC} ${MODULE_SRC} ${PLATFORM_SRC} ${NANOVG_SRC} ${DATA_OBJ})
add_dependencies(slinky slinky_imports)
target_link_libraries(slinky PRIVATE ${IMPORT_LIBS})
set_target_properties(slinky PROPERTIES PREFIX "" LINKER_LANGUAGE CXX)
set_source_files_properties(${DATA_OBJ} PROPERTIES EXTERNAL_OBJECT TRUE GENERATED TRUE)
# The code generation is the one the sources were verified against (tools/asmdiff.py CFLAGS): same optimiser
# settings, frame pointers, no stack cookies, no COMDAT per function, the old compiler's inc/dec and nop choices.
target_compile_options(slinky PRIVATE
-O2 -GS- /Gy-
"SHELL:-Xclang -mframe-pointer=all"
"SHELL:-Xclang -target-feature -Xclang +slow-incdec"
"SHELL:-Xclang -target-feature -Xclang -fast-11bytenop"
"SHELL:-Xclang -target-feature -Xclang -fast-15bytenop"
-w)
target_include_directories(slinky PRIVATE include third_party/nanovg/src third_party/gl)
# the bundled C libraries (NanoVG, stb) get a minimal libc declaration set instead of the Windows SDK's headers
set_source_files_properties(${NANOVG_SRC} PROPERTIES COMPILE_OPTIONS "-I${CMAKE_SOURCE_DIR}/third_party/minicrt/include")
# glcorearb.h would include <windows.h> (not available in the cross build) just to define APIENTRY
target_compile_definitions(slinky PRIVATE APIENTRY=)
# some files need extra flags (`// cflags: ...` in their first lines): the compiler version that built the
# original lowered a few constructs differently
foreach(f ${ENGINE_SRC} ${LOGIC_SRC} ${MODULE_SRC})
file(STRINGS ${f} line LIMIT_COUNT 1 REGEX "^// cflags:")
if(line)
string(REGEX REPLACE "^// cflags:[ ]*" "" extra "${line}")
separate_arguments(extra_list NATIVE_COMMAND "${extra}")
set_source_files_properties(${f} PROPERTIES COMPILE_OPTIONS "${extra_list}")
endif()
endforeach()
# Reached from the DLL's own entry point; no C runtime start-up code is linked (the UCRT in the process is used)
target_link_options(slinky PRIVATE /NODEFAULTLIB /ENTRY:DllMain /SUBSYSTEM:WINDOWS /OPT:NOREF /OPT:NOICF /ERRORLIMIT:0 /MAP)
+130
View File
@@ -0,0 +1,130 @@
<p align="center">
<img src="assets/ss.png" alt="Open Slinky Banner" width="350" />
<br>
<font size="6"><b>Open Slinky</b></font>
<br>
<font size="3">by Skid Solutions</font>
<br>
<font size="2">credits to cat;
</p>
---
<p align="center">
<a href="https://discord.gg/AEwatvnvSf">https://discord.gg/AEwatvnvSf</a>
</p>
<p align="center">
<img src="assets/jak.jpg" alt="java hooks" height="150" />
<img src="assets/hack.jpg" alt="unload" height="150" />
<img src="assets/deobf2.png" alt="symbol recovery" height="150" />
<img src="assets/deobf.jpg" alt="readable names" height="150" />
</p>
---
<p align="center">
<img src="assets/proof.png" alt="build proof" width="500" />
</p>
---
## About
Open Slinky is a readable C++ reconstruction of the Slinky client for Minecraft
1.8.9. The client is injected into the game process as a native DLL; it hooks the
game at the Java bytecode level through JVMTI, dispatches events to the module
system, and renders its interface through NanoVG.
This edition is cleaned up for development:
- address-based file and function names have been replaced with descriptive
ones; the original symbols are recorded in `docs/symbol-map.json` (functions)
and `docs/global-symbol-map.json` (`data_<rva>` / `rdata_<rva>` globals and
the `*_part` event handlers), and inferred names are listed in
`docs/renames.md`;
- assembly used only for byte-exact code generation has been removed; the
remaining `asm` tokens are symbol aliases, not instructions;
- the client's data tables remain in `src/data` because the code links against
them (fonts, strings, module descriptors).
---
## How to build
### Prerequisites
- **LLVM** — clang-cl, lld-link, llvm-rc and llvm-mt on `PATH`
- **CMake** 3.20 or newer
- **Ninja**
- **Windows x64**
- **Java Development Kit 8** — runtime for Minecraft 1.8.9; not required to
compile the DLL
### 1. Configure
```powershell
cmake -S . -B build -G Ninja `
-DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-windows-clang-cl.cmake
```
If Ninja is not on `PATH`, add
`-DCMAKE_MAKE_PROGRAM=C:/path/to/ninja.exe`.
### 2. Build
```powershell
cmake --build build
```
Output:
- `build/slinky.dll`
### Running
Inject `slinky.dll` into the running Minecraft 1.8.9 process with
`LoadLibrary`. Start-up runs on its own thread; the client interface is opened
with its GUI key. The last interface tab unloads the client: it saves the
settings, removes the Java hooks and frees the library.
---
## Project layout
```
src/engine/ hooks, JNI/JVMTI, GUI, module manager, math, shared services
src/logic/ per-module behaviour (.cpp) and its descriptor callbacks / settings page (.h)
src/platform/ Windows layer, GL loader, NanoVG integration
src/data/ data tables (fonts, strings, descriptors) - required to link
include/slinky/ public headers
docs/ design notes and the symbol map
third_party/ NanoVG and GL headers
```
## Notes
- Start-up, teardown, the window hook and the unload thread were virtualised in
the original image and are reconstructed in `src/engine/protected/`.
- `docs/symbol-map.json` maps every original `fn_<rva>`, `logic_<rva>`,
`cb_<rva>`, `reg_<rva>` and `aux_<rva>` symbol to its readable name.
- Settings and profiles are stored in `~/.slinky` (`settings`, `profiles/`).
- `src/data/*.s` and `src/platform/win/chkstk.s` are data/compiler-support
sections, not client code, this is 1 to 1 logic but may have some issues that we didnt find.
## Credits
- Original client: Slinky
- Reconstruction: Claude (VMProtect devirtualization), DeepSeek (decryption),
GLM and Claude (symbol recovery)
## Legal & disclaimers
- **Affiliation:** This is an independent development. It is **NOT AN OFFICIAL
SLINKY PRODUCT**, is not approved by or associated with the original authors,
and is not affiliated with Mojang or Zelix Pty Ltd.
- **Assets & code:** No un-obfuscated original source code is distributed
directly within this repository. The `src/data` tables are the client's own
data required for linking; all third-party assets and code belong to their
respective owners.
- **Purpose:** research and preservation. Use at your own risk.
+19
View File
@@ -0,0 +1,19 @@
#!/bin/sh
# Compile every module and verify it against the original slinky2 image.
# Original image path: tools/paths.py (override with SLINKY_ORIG=...).
set -e
cd "$(dirname "$0")"
python3 tools/verify.py
python3 tools/verify_logic.py
python3 tools/verify_engine.py --quiet
for f in src/engine/protected/*.cpp; do
clang-cl --target=x86_64-pc-windows-msvc -O2 -Iinclude -c "$f" -o "build/obj/protected_$(basename "$f" .cpp).obj"
done
echo "protected (readable, compile-checked): $(ls src/engine/protected/*.cpp | wc -l) files"
for f in src/engine/lib/*.cpp; do
clang-cl --target=x86_64-pc-windows-msvc -O2 -Iinclude -c "$f" -o "build/obj/lib_$(basename "$f" .cpp).obj"
done
echo "lib (translated from the listing, compile-checked): $(ls src/engine/lib/*.cpp | wc -l) files"
echo "full DLL: cmake -S . -B build/cmake -DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-windows-clang-cl.cmake && cmake --build build/cmake"
python3 tools/verify_modules.py
python3 tools/report_modules.py
+29
View File
@@ -0,0 +1,29 @@
LIBRARY kernel32.dll
EXPORTS
CloseHandle
CreateDirectoryW
CreateFileW
CreateThread
DeleteFileW
DisableThreadLibraryCalls
FindClose
FindFirstFileW
FindNextFileW
FreeLibraryAndExitThread
GetCurrentProcessId
GetFileSize
GetLastError
GetModuleHandleA
GetProcAddress
GetStdHandle
GetSystemTimeAsFileTime
GetTempPathW
LoadLibraryA
OutputDebugStringA
QueryPerformanceCounter
QueryPerformanceFrequency
MultiByteToWideChar
ReadFile
Sleep
WideCharToMultiByte
WriteFile
+3
View File
@@ -0,0 +1,3 @@
LIBRARY shell32.dll
EXPORTS
ShellExecuteW
+60
View File
@@ -0,0 +1,60 @@
LIBRARY ucrtbase.dll
EXPORTS
__stdio_common_vsprintf
__stdio_common_vsscanf
__stdio_common_vswprintf
_hypotf
_register_onexit_function
_time64
_wgetenv
abort
acos
acosf
atan2
atan2f
bsearch
calloc
ceilf
cos
cosf
expf
fclose
feof
floor
floorf
fmod
fmodf
fopen
fopen_s
fread
free
fseek
ftell
hypot
ldexp
localeconv
logf
malloc
memcpy
memmove
memset
pow
powf
qsort
realloc
roundf
sin
sinf
sqrt
sqrtf
strcmp
strlen
strncmp
strncpy
strtod
strtol
tanf
tolower
truncf
wcslen
wcsncpy_s
+5
View File
@@ -0,0 +1,5 @@
LIBRARY user32.dll
EXPORTS
GetForegroundWindow
GetWindowThreadProcessId
IsWindowUnicode
+26
View File
@@ -0,0 +1,26 @@
# Cross-compile the Windows DLL from Linux / macOS with clang-cl + lld-link.
# No Windows SDK is needed: the code declares the few Win32 / UCRT functions it uses itself and links against
# import libraries that CMakeLists.txt generates from cmake/imports/*.def (llvm-dlltool).
#
# cmake -S . -B build/cmake -DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-windows-clang-cl.cmake
# cmake --build build/cmake -> build/cmake/slinky.dll
set(CMAKE_SYSTEM_NAME Windows)
set(CMAKE_SYSTEM_PROCESSOR AMD64)
set(CMAKE_C_COMPILER clang-cl)
set(CMAKE_CXX_COMPILER clang-cl)
set(CMAKE_C_COMPILER_TARGET x86_64-pc-windows-msvc)
set(CMAKE_CXX_COMPILER_TARGET x86_64-pc-windows-msvc)
set(CMAKE_LINKER lld-link)
set(CMAKE_AR llvm-lib)
set(CMAKE_RC_COMPILER llvm-rc)
# the compiler check must not need the SDK's libraries
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
# the flags the engine code was verified with are set explicitly in CMakeLists.txt: drop CMake's MSVC defaults
# (/EHsc, /GR, /MD, /O2 /Ob2 /DNDEBUG ...)
set(CMAKE_C_FLAGS_INIT "")
set(CMAKE_CXX_FLAGS_INIT "")
set(CMAKE_C_FLAGS_RELEASE_INIT "")
set(CMAKE_CXX_FLAGS_RELEASE_INIT "")
set(CMAKE_MSVC_RUNTIME_LIBRARY "")
set(CMAKE_C_STANDARD_LIBRARIES "" CACHE STRING "" FORCE)
set(CMAKE_CXX_STANDARD_LIBRARIES "" CACHE STRING "" FORCE)
+5
View File
@@ -0,0 +1,5 @@
--target=x86_64-pc-windows-msvc
-fms-extensions
-fms-compatibility
-Iinclude
-std=c++17
+55
View File
@@ -0,0 +1,55 @@
# Aim Assist — extracted implementation
Aim Assist is the reference example for the logic layer. Its original
implementation is in `src/logic/combat/aim_assist.cpp` (5,024 bytes, six
functions), extracted byte-for-byte from `slinky2.decrypted.img`.
## Functions
| function | RVA | descriptor | what it does |
|---|---:|---|---|
| `aim_assist_frame` | `0x2b200` | `+0x58` | main tick: checks enabled state, acquires the local player/world, evaluates the selected mode and calls the rotation/target routines |
| `aim_assist_disable` | `0x2b5b0` | `+0x98` | disable/reset path: clears the module's runtime state and calls the engine cleanup callback |
| `aim_assist_on_rotation_update` | `0x2b610` | — | small state helper: sets the module's active flags |
| `aim_assist_tick` | `0x2b630` | — | condition/target pass: checks the configured requirements (target, line of sight, weapon, etc.) and iterates candidate entities |
| `aim_assist_apply_frame_rotation` | `0x2bf40` | — | rotation math: clamps yaw/pitch, applies smoothing, prediction and randomization, then applies the rotation |
| `aim_assist_test_entity_updating` | `0x2c3b0` | — | target selection/validation: range/FOV checks, visibility, invisibility, hitbox correction and target sorting |
The body of each function in the source file is the exact original
instruction stream. The disassembly above each block shows the same code with
RIP-relative targets resolved, so the actual aim logic (target acquisition,
angle clamping, interpolation, FOV/range checks, prediction) is visible rather
than hidden behind an opaque stub.
In total the six functions contain 1,137 instructions and 101 calls. Every
direct call targets another function in the client `.text` (the only indirect
call is the engine cleanup callback in the disable path); none of the aim logic
is stubbed out.
## How it is wired
The module descriptor for Aim Assist is at `0x2e8cc0`:
- `+0x00` → `"Aim Assist"`
- `+0x08` → description
- `+0x10` → category `0` (combat)
- `+0x58` → `0x2b200` (tick)
- `+0x98` → `0x2b5b0` (disable)
The settings block at `0x2e8eb8` starts with the three callbacks used by the
settings layer (`aim_assist_gui`, `aim_assist_register`, `aim_assist_status`) followed by the config
objects. The logic functions read those config objects directly, including the
current-value fields at `+0x18` (bools) and `+0x30` (numbers/enums).
## Runtime requirement
The extracted code is the real client implementation, not a placeholder. It
expects the original runtime context: the module manager calls `descriptor
+0x58` with the module state, and the code uses the world/player pointers in
that state plus the client helper functions in `.text`. The Aim Assist block
therefore behaves exactly like the original inside the original client image.
This repository is a source reconstruction/verification tree, not a runnable
Minecraft client. To execute the block outside the verifier, the host must
provide the same engine/module-manager context (or the logic has to be ported
to a high-level API against that context).
+50
View File
@@ -0,0 +1,50 @@
# Engine / module-manager notes
This is the result of looking for the remaining module logic in `.slinky0` and
in the client's own dispatcher.
## The module manager is in client `.text`
The manager is not in `.slinky0`. The constructor is at `0x28000` in the
client `.text`:
- it reads the descriptor table at `0x14f810`,
- stores the module count `0x43` (67 descriptors),
- groups modules by category (`descriptor+0x10`),
- builds callback lists for the event slots at
`descriptor +0x48, +0x58, …, +0x1F8`,
- writes `[descriptor+0x14] = 1` when a module is enabled.
The dispatcher at `0x285d0` walks the enabled modules, collects the callback
for the requested event slot, and calls it. This is why the descriptor
callbacks are the primary module logic: a module without a callback at any of
those slots has no client-side handler for that event.
## The six engine-only modules
The descriptors for these modules were checked through all event slots:
| module | client callback(s) | result |
|---|---|---|
| No Jump Delay | `0x35d50` (`+0x158`) | extracted |
| Ping Fix | `0x3e6d0` (`+0x68`), `0x3e700` (`+0x78`) | extracted |
| Instant Stop | `0x42650` (`+0x148`) | extracted |
| Item Use Fix | `0x42b80` (`+0x128`), `0x42be0` (`+0x138`) | extracted |
| Null Move | `0x46ac0` (`+0x148`) | extracted |
| No Hurt Cam | none | no client callback |
`No Hurt Cam` also has no RIP-relative reference anywhere in the client
`.text`, and its descriptor has no callback pointer in `+0x48 … +0x1F8`.
There is therefore no client-native implementation to copy. Its effect must
be applied by an engine/Java-side hook (or by code in the still-packed
`.slinky0` tail); the decrypted clean engine region contains no module names,
no descriptor references and no module-manager code.
## `.slinky0`
`.slinky0` is a raw native runtime blob, not a PE. The clean region contains
native code/data (a PE loader, CRT/RTTI data, networking/OpenGL support), but
none of the module names or the descriptor table. The module table is passed
to it at runtime, and the module dispatch is done by the client manager above.
The remaining engine tail (`slinky0+0xAB0000 …`) is still packed, so nothing
can be copied from it statically without defeating that transform.
File diff suppressed because it is too large. Load diff
+108
View File
@@ -0,0 +1,108 @@
# Start-up, hooks and shutdown
How the client gets from "DLL loaded into the JVM" to "game calls into the modules", and back. Most of the original
start-up is protector-virtualised, so it could not be translated; it is written as new code
(`src/engine/protected/init.cpp`, `src/platform/win/entry.cpp`) from what the surrounding readable code expects.
Facts below come from the image unless marked **inferred**. The licence check is not part of this client.
## What was found where
| Piece | Function | State |
|---|---|---|
| Class-hook installer | `install_class_hooks` | **real code**, was missed by the first gap scan. Rewritten readably (tables are data), checked against the original by running both (`tools/emu_install.py`) |
| Class-hook remover | `remove_class_hooks` | real code, translated exactly |
| `ClassFileLoadHook` + class rewriter | `class_file_load_hook`, `rewrite_class_file`, `rewrite_instruction`, ... | real code, translated |
| `SlinkyChannelHandler.onPacket` native | `on_packet` | real code (also missed by the scan), translated |
| Key handlers for profile / module binds | `on_profile_bind`, `on_module_bind`, `bind_profile_to_key` | real code (missed), translated |
| `JNI_GetCreatedJavaVMs` lookup | `get_java_vm` | real code (missed), translated |
| Hooks on/off | `set_java_hooks` | virtualised -> `init.cpp` (**inferred**: GetEnv JVMTI, install or remove, log failure) |
| Start-up thread body | inside the original image's start-up routine at RVA 0x1000 | virtualised -> `slinky_setup` in `init.cpp` (**inferred** order) |
| First-frame GUI setup | inside `hook_get_mouse_over` | virtualised -> `gui_init` in `protected/hooks.cpp` (**inferred**) |
| Global teardown | DLL detach | virtualised -> `slinky_teardown` (**inferred**) |
The first gap scan only accepted a function that follows `0xcc/0x90/0xc3`, so functions after multi-byte NOP padding
or directly after a tail `jmp` were skipped; 33 real functions (7 KB) were found by fixing that
(`tools/extract_engine.py`). 43 protector stubs remain; `empty_placeholder` is still an empty placeholder (it has no callers).
## Entry (Windows)
`DllMain` (`src/platform/win/entry.cpp`) starts a thread on `DLL_PROCESS_ATTACH`; that thread runs `slinky_setup`.
On failure it prints `Initialization failed: <code>` and starts the unload thread (`start_unload_thread`).
## `slinky_setup` (init.cpp)
1. `get_java_vm`: JavaVM from `jvm.dll` -> `init_javavm`; attach the thread.
2. JNI ID table: `calloc(JIds)` -> `java_natives_world_cached_jni_ids`, `init_jni_ids` (game class loader, mapping detection, every class/method/field
ID). Mapping: `ids->variant` 0 = 1.7.10, 1 = 1.8.9; `ids->names` 0 = obfuscated, 1 = MCP, 2 = SRG.
3. Module manager: `create_module_manager(java_natives_world_module_manager, set_module_bind)` builds the lists from the static module list (67 descriptors);
`set_module_bind` is the key-bind setter it stores. Immediately afterwards `save_module_states` runs the modules' settings
visitors in "remember" mode, copying the static values from `.data` into each setting's saved slot. Those slots are what the
built-in Default profile restores and what a profile with missing fields uses as its fallback; the image's static `.data`
only contains the live values, so without this step both paths would reset settings to zero. This is part of the inferred
start-up order: the original's start-up lives in protector-virtualised code, so the call is not visible in `.text`.
4. State the frame and input code read: key table `java_natives_world_key_state_table_one_byte` (one byte per key), key map `java_natives_frame_profile_key_binds_key_profile`
(profile binds, handler `on_profile_bind`, items are strings freed with `free`), key map `java_natives_frame_module_key_binds_key_module` (module binds,
handler `on_module_bind`, several modules per key), profile list `init_profile_list` (`load_profiles` reads `~/.slinky/profiles`).
5. Chat commands `init_chat_commands`, GLFW entry points `init_glfw_entrypoints` (absent with LWJGL 2, not an error).
6. Saved settings `~/.slinky/settings` (**inferred**, mirror of `save_settings`): per-module state for global modules
through `load_module_settings`, `profile_binds` through `bind_profile_to_key`.
7. `input_input_handler_hooked_into_game = on_input_event`: the input handler the window hook (`hook_game_window`, installed lazily every frame) calls.
8. Java side: `install_packet_channel` defines `SlinkyChannelHandler` (class bytes in the image) and registers `onPacket = on_packet`.
9. `install_class_hooks`: define `SlinkyHooks13`, register its 23 natives, add the `can_retransform_classes` capability, set the
`ClassFileLoadHook` callback, enable the event, build the hook records for the detected mapping, retransform the 9
classes, disable the event again. Errors are `12000 + jvmtiError`.
The hooks go last: from the first retransformation on, the game calls the natives.
## First frame (`hook_get_mouse_over`, hooks.cpp)
The first `getMouseOver` runs `gui_init` once, on the render thread: GL entry points
(`slinky_load_gl` + `wglGetProcAddress`), NanoVG context (`nvgCreateGL3`), window object `frame_render_target` with the two
embedded fonts (Inter Medium = slot 0, Inter Bold = slot 1). NanoVG flags are **inferred** (antialias + stencil strokes).
## The class hooks
`SlinkyHooks13` (class file in the image, 1196 bytes) declares 23 `public static native` methods; their bodies are
`hook_get_mouse_over .. hook_update_controller` (`kNatives` in `src/engine/jni/hook_tables.inc`, same order as the class file). The game classes are
not detoured: through JVMTI `RetransformClasses` the `ClassFileLoadHook` (`class_file_load_hook`) rewrites matching call and field
instructions into `invokestatic SlinkyHooks13.<native>`, so the receiver becomes the first `Object` argument.
What is rewritten is data: for each mapping (1.7.10 / 1.8.9 x obfuscated / MCP / SRG) the 9 classes
(Minecraft, EntityRenderer, RenderManager, RendererLivingEntity, RenderPlayer, NetHandlerPlayClient, WorldClient, Entity,
EntityLivingBase), per class the natives it may call and per method the instructions to replace
(`src/engine/jni/hook_tables.inc`, generated by `tools/gen_hooks.py` from an emulated run of the original installer,
`tools/dump_hooks.py`). 16 method patterns, 23 sites per mapping.
Rewritten call sites, by native: `getMouseOver` (EntityRenderer.getMouseOver in updateCameraAndRender),
`updateDisplay` (Display.update), `clickMouse`/`rightClickMouse`/`updateEntities`/`updateController` (inside Minecraft.runTick
and the input tick), `postWorldClear_1_7/_1_8` (glClear / GlStateManager.clear), `orientCamera`, `updateActiveRenderInfo`,
`isDisplayActivePreRender`, `setRenderX/Y` (RenderManager fields), `canRenderName` (any method of RendererLivingEntity),
`renderPlayer`, `unloadChunk`, `setBlock_1_7/_1_8`, `invalidateChunk`, `shouldClipLedge` (Entity.isSneaking), `moveFlying`,
`sendPacket_1_7/_1_8`.
**Verification:** `tools/emu_install.py` loads the built `slinky.dll`, runs `install_class_hooks` under Unicorn for all six mappings and
compares the records it builds with the original's: identical (9 classes, 16 patterns, 23 sites, same 23 natives).
## Packets
`SlinkyChannelHandler` (netty `ChannelDuplexHandler`, class bytes in the image) is put in the connection's pipeline as
`slinky_handler` (`register_channel_class`, every frame). It queues packets and calls `onPacket(Object packet, boolean outbound,
long time, int id)` = `on_packet`: under the lock `frame_spinlock`, and unless shutting down (`java_natives_frame_client_shutting_down_natives_check`) it dispatches module
event 2 (inbound) or 3 (outbound) and returns the handlers' verdict.
## Shutdown
`hook_update_display` (the `updateDisplay` hook) unloads when the GUI's confirmation was given: saves settings, notifies and disables the
modules, removes the netty handler, `set_java_hooks(env, 0)` (hooks off), `restore_game_window` (window hook off), then `start_unload_thread` starts a
thread that waits one second, runs `slinky_teardown` and calls `FreeLibraryAndExitThread`.
`slinky_teardown` (**inferred**) drops the netty class and the cached class references and frees the manager, both key maps,
the profile list, the chat command table, the profile name, the mesh cache, the key table and the ID table.
GL objects (NanoVG context, framebuffer) are not destroyed: that needs the render thread's context.
## Not verified / open
* The whole start-up order and the object sizes (`KeyMap` 0x30, key table 0x100, profile list 0x20) are inferred from their readers;
nothing has run inside a game yet.
* `empty_placeholder` (decrypts a short string, only reached through `reset_key_state`, which nothing calls): empty placeholder.
* The NanoVG creation flags, JVMTI version (1.2) and the settings loader are inferred.
* Linux: no platform layer yet (JVMTI agent entry, input hook, file paths, GL loader).
+741
View File
@@ -0,0 +1,741 @@
# Logic layer by module
Generated by `tools/extract_logic.py`. Each entry lists the functions in
`src/asm/<category>/<module>.cpp` that were identified as reading the
module's settings object or stored in its descriptor. All functions are
byte-for-byte copies of the original `.text` (verified by
`tools/verify_logic.py`).
| module | category | file | functions | bytes |
|---|---|---|---:|---:|
| Aim Assist | combat | [aim_assist.cpp](../src/asm/combat/aim_assist.cpp) | 6 | 5040 |
| Anti Bot | utility | [anti_bot.cpp](../src/asm/utility/anti_bot.cpp) | 2 | 1408 |
| Anti Debuff | visual | [anti_debuff.cpp](../src/asm/visual/anti_debuff.cpp) | 1 | 128 |
| Auto Block | combat | [auto_block.cpp](../src/asm/combat/auto_block.cpp) | 5 | 1600 |
| Auto Clicker | combat | [auto_clicker.cpp](../src/asm/combat/auto_clicker.cpp) | 4 | 2272 |
| Auto Pot | utility | [auto_pot.cpp](../src/asm/utility/auto_pot.cpp) | 4 | 1264 |
| Auto Rod | utility | [auto_rod.cpp](../src/asm/utility/auto_rod.cpp) | 7 | 2624 |
| Auto Soup | utility | [auto_soup.cpp](../src/asm/utility/auto_soup.cpp) | 4 | 1856 |
| Auto Tool | block | [auto_tool.cpp](../src/asm/block/auto_tool.cpp) | 2 | 864 |
| Auto Weapon | combat | [auto_weapon.cpp](../src/asm/combat/auto_weapon.cpp) | 1 | 320 |
| Backtrack | combat | [backtrack.cpp](../src/asm/combat/backtrack.cpp) | 3 | 1904 |
| Blink | utility | [blink.cpp](../src/asm/utility/blink.cpp) | 5 | 464 |
| Block ESP | visual | [block_esp.cpp](../src/asm/visual/block_esp.cpp) | 5 | 1648 |
| Block In | block | [block_in.cpp](../src/asm/block/block_in.cpp) | 5 | 4048 |
| Bridge Assist | block | [bridge_assist.cpp](../src/asm/block/bridge_assist.cpp) | 5 | 2800 |
| Chams | visual | [chams.cpp](../src/asm/visual/chams.cpp) | 1 | 336 |
| Chest ESP | visual | [chest_esp.cpp](../src/asm/visual/chest_esp.cpp) | 2 | 976 |
| Chest Stealer | utility | [chest_stealer.cpp](../src/asm/utility/chest_stealer.cpp) | 1 | 240 |
| Clutch | block | [clutch.cpp](../src/asm/block/clutch.cpp) | 4 | 5904 |
| Criticals | combat | [criticals.cpp](../src/asm/combat/criticals.cpp) | 4 | 1024 |
| Debug | utility | [debug.cpp](../src/asm/utility/debug.cpp) | 4 | 800 |
| Fake Lag | utility | [fake_lag.cpp](../src/asm/utility/fake_lag.cpp) | 2 | 560 |
| Fast Accel | move | [fast_accel.cpp](../src/asm/move/fast_accel.cpp) | 1 | 608 |
| Fast Mine | block | [fast_mine.cpp](../src/asm/block/fast_mine.cpp) | 2 | 400 |
| Fast Place | block | [fast_place.cpp](../src/asm/block/fast_place.cpp) | 1 | 800 |
| Free Look | visual | [free_look.cpp](../src/asm/visual/free_look.cpp) | 2 | 256 |
| Friends | utility | [friends.cpp](../src/asm/utility/friends.cpp) | 4 | 960 |
| GUI | visual | [gui.cpp](../src/asm/visual/gui.cpp) | 7 | 752 |
| Ghost Hand | block | [ghost_hand.cpp](../src/asm/block/ghost_hand.cpp) | 2 | 304 |
| Hit Select | combat | [hit_select.cpp](../src/asm/combat/hit_select.cpp) | 3 | 1264 |
| Hitboxes | combat | [hitboxes.cpp](../src/asm/combat/hitboxes.cpp) | 1 | 32 |
| Indicators | visual | [indicators.cpp](../src/asm/visual/indicators.cpp) | 3 | 2144 |
| Inv Manager | utility | [inv_manager.cpp](../src/asm/utility/inv_manager.cpp) | 3 | 2288 |
| Inv Walk | move | [inv_walk.cpp](../src/asm/move/inv_walk.cpp) | 4 | 1808 |
| Item ESP | visual | [item_esp.cpp](../src/asm/visual/item_esp.cpp) | 3 | 2336 |
| Keep Sprint | move | [keep_sprint.cpp](../src/asm/move/keep_sprint.cpp) | 3 | 416 |
| Knockback Delay | combat | [knockback_delay.cpp](../src/asm/combat/knockback_delay.cpp) | 2 | 1008 |
| Knockback Displace | combat | [knockback_displace.cpp](../src/asm/combat/knockback_displace.cpp) | 6 | 1136 |
| Lag Range | combat | [lag_range.cpp](../src/asm/combat/lag_range.cpp) | 4 | 1856 |
| Mod Overlay | visual | [mod_overlay.cpp](../src/asm/visual/mod_overlay.cpp) | 8 | 3216 |
| Name Tags | visual | [name_tags.cpp](../src/asm/visual/name_tags.cpp) | 3 | 5024 |
| No Hit Delay | utility | [no_hit_delay.cpp](../src/asm/utility/no_hit_delay.cpp) | 1 | 160 |
| No Item Release | utility | [no_item_release.cpp](../src/asm/utility/no_item_release.cpp) | 1 | 256 |
| No Slow | move | [no_slow.cpp](../src/asm/move/no_slow.cpp) | 1 | 160 |
| No Use Delay | utility | [no_use_delay.cpp](../src/asm/utility/no_use_delay.cpp) | 1 | 256 |
| Notifications | visual | [notifications.cpp](../src/asm/visual/notifications.cpp) | 6 | 2992 |
| Piercing | combat | [piercing.cpp](../src/asm/combat/piercing.cpp) | 3 | 592 |
| Player ESP | visual | [player_esp.cpp](../src/asm/visual/player_esp.cpp) | 3 | 3152 |
| Pointers | visual | [pointers.cpp](../src/asm/visual/pointers.cpp) | 2 | 2128 |
| Reach | combat | [reach.cpp](../src/asm/combat/reach.cpp) | 1 | 176 |
| Refill | utility | [refill.cpp](../src/asm/utility/refill.cpp) | 3 | 2064 |
| Safe Walk | move | [safe_walk.cpp](../src/asm/move/safe_walk.cpp) | 1 | 288 |
| Sprint | move | [sprint.cpp](../src/asm/move/sprint.cpp) | 2 | 368 |
| Sprint Reset | combat | [sprint_reset.cpp](../src/asm/combat/sprint_reset.cpp) | 2 | 864 |
| Strafe | move | [strafe.cpp](../src/asm/move/strafe.cpp) | 2 | 624 |
| Teams | utility | [teams.cpp](../src/asm/utility/teams.cpp) | 3 | 720 |
| Timer | utility | [timer.cpp](../src/asm/utility/timer.cpp) | 2 | 272 |
| Timer Range | combat | [timer_range.cpp](../src/asm/combat/timer_range.cpp) | 5 | 3632 |
| Trajectories | visual | [trajectories.cpp](../src/asm/visual/trajectories.cpp) | 3 | 3968 |
| Velocity | combat | [velocity.cpp](../src/asm/combat/velocity.cpp) | 8 | 2512 |
| Weapons | combat | [weapons.cpp](../src/asm/combat/weapons.cpp) | 4 | 640 |
| No Jump Delay | move | [no_jump_delay.cpp](../src/asm/move/no_jump_delay.cpp) | 1 | 48 |
| Ping Fix | utility | [ping_fix.cpp](../src/asm/utility/ping_fix.cpp) | 2 | 96 |
| Instant Stop | move | [instant_stop.cpp](../src/asm/move/instant_stop.cpp) | 1 | 976 |
| Item Use Fix | utility | [item_use_fix.cpp](../src/asm/utility/item_use_fix.cpp) | 2 | 176 |
| Null Move | move | [null_move.cpp](../src/asm/move/null_move.cpp) | 1 | 592 |
## Functions
### Aim Assist
`src/asm/combat/aim_assist.cpp` - descriptor `0x2e8cc0`, settings block `0x2e8eb8`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `aim_assist_frame` | `0x2b200` | 944 | descriptor +0x58 | `0x2e9020` config scalar, `0x2e9048` setting bool "silent_ignore_manual_aim" (current value +0x18) |
| `aim_assist_disable` | `0x2b5b0` | 96 | descriptor +0x98 | |
| `aim_assist_on_rotation_update` | `0x2b610` | 32 | descriptor +0x118 | `0x2e9068` setting bool "silent_move_correct" (current value +0x18) |
| `aim_assist_tick` | `0x2b630` | 1200 | descriptor +0x168 | `0x2e9020` config scalar, `0x2e90d8` setting bool "require_sprint" (current value +0x18), `0x2e90f8` setting bool "require_mouse_pressed" (current value +0x18), `0x2e9118` setting bool "disable_on_block_break" (current value +0x18), ... |
| `aim_assist_apply_frame_rotation` | `0x2bf40` | 1136 | settings logic | `0x2e8f50` setting num "hor_speed" (current value +0x30), `0x2e8f88` setting num "ver_speed" (current value +0x30), `0x2e8fa8` setting bool "lock_on_vertical" (current value +0x18), `0x2e8fc8` setting bool "require_mouse_moved" (current value +0x18), ... |
| `aim_assist_test_entity_updating` | `0x2c3b0` | 1632 | settings logic | `0x2e9180` config scalar, `0x2e91c0` setting num "predict" (current value +0x30), `0x2e91f8` setting num "hor_hitbox_correct" (current value +0x30), `0x2e9230` setting num "ver_hitbox_correct" (current value +0x30), ... |
### Anti Bot
`src/asm/utility/anti_bot.cpp` - descriptor `0x2eacd0`, settings block `0x2eaec8`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `anti_bot_on_tick` | `0x30e70` | 1184 | descriptor +0x48 | `0x2eaf10` setting num "min_age" (current value +0x30), `0x2eaf30` setting bool "always_stationary" (current value +0x18), `0x2eaf88` setting enum "invalid_uuid" (current value +0x30), `0x2eafb0` setting bool "sleeping" (current value +0x18), ... |
| `anti_bot_on_toggle` | `0x31310` | 224 | descriptor +0x98 | |
### Anti Debuff
`src/asm/visual/anti_debuff.cpp` - descriptor `0x2f3860`, settings block `0x2f3a58`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `anti_debuff_on_late_tick` | `0x44940` | 128 | descriptor +0x168 | `0x2f3a88` setting bool "blindness" (current value +0x18), `0x2f3aa8` setting bool "nausea" (current value +0x18) |
### Auto Block
`src/asm/combat/auto_block.cpp` - descriptor `0x2eaff8`, settings block `0x2eb1f0`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `auto_block_on_tick` | `0x31570` | 1248 | descriptor +0x48 | `0x2eb238` setting num "max_hold_time" (current value +0x30), `0x2eb270` setting num "lag_chance" (current value +0x30), `0x2eb2a8` setting num "lag_duration" (current value +0x30), `0x2eb2e0` setting num "max_hurt_time" (current value +0x30), ... |
| `auto_block_on_packet_sent` | `0x31a50` | 32 | descriptor +0x78 | |
| `auto_block_on_toggle` | `0x31a70` | 144 | descriptor +0x98 | |
| `auto_block_on_attack` | `0x31b00` | 96 | descriptor +0x128 | |
| `auto_block_on_late_tick` | `0x31b60` | 80 | descriptor +0x168 | |
### Auto Clicker
`src/asm/combat/auto_clicker.cpp` - descriptor `0x2e9330`, settings block `0x2e9528`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `auto_clicker_tick` | `0x2ca10` | 128 | descriptor +0x48 | |
| `auto_clicker_on_frame` | `0x2ca90` | 1712 | descriptor +0x58 | `0x2e9560` config scalar, `0x2e9580` setting bool "inventory_randomize" (current value +0x18), `0x2e95a0` setting bool "allow_block_break" (current value +0x18), `0x2e95c0` setting bool "require_weapon" (current value +0x18), ... |
| `auto_clicker_on_player_update` | `0x2d140` | 80 | descriptor +0x158 | |
| `auto_clicker_extra_burst_clicks` | `0x2d3b0` | 352 | settings logic | `0x2e96a8` setting bool "exhaust" (current value +0x18) |
### Auto Pot
`src/asm/utility/auto_pot.cpp` - descriptor `0x2ef128`, settings block `0x2ef320`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `auto_pot_on_tick` | `0x391c0` | 784 | descriptor +0x48 | `0x2ef350` setting bool "use_non_health" (current value +0x18) |
| `auto_pot_on_input` | `0x394d0` | 80 | descriptor +0x88 | |
| `auto_pot_on_toggle` | `0x39520` | 224 | descriptor +0x98 | `0x2ef3a8` setting bool "prevent_waste" (current value +0x18), `0x2ef3c8` setting bool "double_splash" (current value +0x18) |
| `auto_pot_asm_r_pins` | `0x396e0` | 176 | settings logic | `0x2ef388` setting num "delay" (current value +0x30) |
### Auto Rod
`src/asm/utility/auto_rod.cpp` - descriptor `0x2f3520`, settings block `0x2f3718`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `auto_rod_on_tick` | `0x43d80` | 1472 | descriptor +0x48 | `0x2f3750` setting bool "require_not_mouse_over" (current value +0x18), `0x2f3770` setting bool "allow_throwables" (current value +0x18), `0x2f3790` setting bool "always_switch_to_weapon" (current value +0x18) |
| `auto_rod_on_input` | `0x44340` | 80 | descriptor +0x88 | |
| `auto_rod_on_toggle` | `0x44390` | 48 | descriptor +0x98 | |
| `auto_rod_on_attack` | `0x443c0` | 96 | descriptor +0x128 | |
| `auto_rod_randomised_throw_delay` | `0x445a0` | 192 | settings logic | `0x2f37c8` setting num "delay" (current value +0x30), `0x2f37e8` setting bool "randomize_delays" (current value +0x18) |
| `auto_rod_ticks_bobber_needs` | `0x44660` | 512 | settings logic | `0x2f3820` setting num "retract_speed" (current value +0x30) |
| `auto_rod_randomised_rod_pull` | `0x44860` | 224 | settings logic | `0x2f37e8` setting bool "randomize_delays" (current value +0x18), `0x2f3858` setting num "throw_speed" (current value +0x30) |
### Auto Soup
`src/asm/utility/auto_soup.cpp` - descriptor `0x2e96b0`, settings block `0x2e98a8`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `auto_soup_on_tick` | `0x2d510` | 1488 | descriptor +0x48 | `0x2e98f0` config scalar, `0x2e9928` setting num "delay_before" (current value +0x30), `0x2e9960` config scalar, `0x2e9998` setting num "delay_between" (current value +0x30), ... |
| `auto_soup_input_hook` | `0x2dae0` | 48 | descriptor +0x88 | |
| `auto_soup_enable` | `0x2db10` | 64 | descriptor +0x98 | |
| `auto_soup_asm_pins_keep` | `0x2dd50` | 256 | settings logic | `0x2e9a08` setting num "randomization" (current value +0x30) |
### Auto Tool
`src/asm/block/auto_tool.cpp` - descriptor `0x2ee158`, settings block `0x2ee350`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `auto_tool_on_inventory_tick` | `0x37180` | 816 | descriptor +0x1d8 | `0x2ee3a0` setting num "activation_time" (current value +0x30), `0x2ee3c0` setting bool "allow_using_fists" (current value +0x18), `0x2ee3e0` setting bool "allow_using_sword" (current value +0x18), `0x2ee400` setting bool "allow_using_tools" (current value +0x18), ... |
| `auto_tool_on_shutdown` | `0x374b0` | 48 | descriptor +0x1e8 | |
### Auto Weapon
`src/asm/combat/auto_weapon.cpp` - descriptor `0x2f5af8`, settings block `0x2f5cf0`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `auto_weapon_on_inventory_tick` | `0x4a340` | 320 | descriptor +0x1d8 | `0x2f5d38` setting num "activation_time" (current value +0x30), `0x2f5d58` setting bool "require_mouse_pressed" (current value +0x18) |
### Backtrack
`src/asm/combat/backtrack.cpp` - descriptor `0x2edad0`, settings block `0x2edcc8`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `backtrack_packet_hook_under` | `0x35fd0` | 320 | descriptor +0x68 | |
| `backtrack_render_box_held` | `0x36110` | 464 | descriptor +0xa8 | `0x2edd10` setting num "indicator_line_width" (current value +0x30), `0x2edd50` setting bool "indicator_rotate" (current value +0x18), `0x2edd70` setting bool "indicator_filled" (current value +0x18) |
| `backtrack_tick_decide_whether` | `0x362e0` | 1120 | descriptor +0x168 | `0x2edda8` setting num "max_hurt_time" (current value +0x30), `0x2ede30` config scalar, `0x2ede68` setting num "cooldown" (current value +0x30), `0x2ede88` setting bool "require_mouse_pressed" (current value +0x18), ... |
### Blink
`src/asm/utility/blink.cpp` - descriptor `0x2ec600`, settings block `0x2ec7f8`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `blink_on_tick` | `0x33660` | 112 | descriptor +0x48 | `0x2ec828` setting bool "disable_on_attack" (current value +0x18) |
| `blink_on_packet_received` | `0x336d0` | 128 | descriptor +0x68 | |
| `blink_on_packet_sent` | `0x33750` | 32 | descriptor +0x78 | |
| `blink_on_toggle` | `0x33770` | 80 | descriptor +0x98 | `0x2ec860` setting num "max_duration" (current value +0x30), `0x2ec8b8` current value of setting "direction" (+0x30) |
| `blink_on_attack` | `0x337c0` | 112 | descriptor +0x128 | `0x2ec8e0` setting bool "disable_on_block_dig" (current value +0x18), `0x2ec900` setting bool "disable_on_block_interact" (current value +0x18) |
### Block ESP
`src/asm/visual/block_esp.cpp` - descriptor `0x2f1740`, settings block `0x2f1938`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `block_esp_on_tick` | `0x3f4c0` | 480 | descriptor +0x48 | |
| `block_esp_on_toggle` | `0x3f6a0` | 272 | descriptor +0x98 | |
| `block_esp_on_render_world` | `0x3f7b0` | 656 | descriptor +0xa8 | `0x2f19e8` setting num "line_width" (current value +0x30), `0x2f1a20` setting num "block_render_limit" (current value +0x30), `0x2f1a58` setting num "range" (current value +0x30), `0x2f1a78` config scalar |
| `block_esp_on_world_change` | `0x3fa40` | 16 | descriptor +0x188 | |
| `block_esp_on_shutdown` | `0x3fa50` | 224 | descriptor +0x1e8 | |
### Block In
`src/asm/block/block_in.cpp` - descriptor `0x2f6108`, settings block `0x2f6300`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `block_in_on_tick` | `0x4abf0` | 16 | descriptor +0x48 | |
| `block_in_enable_remember_facing` | `0x4ac00` | 336 | descriptor +0x98 | |
| `block_in_rotation_override_placement` | `0x4ad50` | 48 | descriptor +0x118 | |
| `block_in_tick_while_placing` | `0x4ad80` | 208 | descriptor +0x148 | |
| `block_in_tick` | `0x4ae50` | 3440 | descriptor +0x168 | `0x2f6330` setting bool "require_ground" (current value +0x18), `0x2f6368` setting num "speed" (current value +0x30) |
### Bridge Assist
`src/asm/block/bridge_assist.cpp` - descriptor `0x2e9ac0`, settings block `0x2e9cb8`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `bridge_assist_disable` | `0x2de50` | 112 | descriptor +0x98 | |
| `bridge_assist_place` | `0x2dec0` | 928 | descriptor +0x128 | |
| `bridge_assist_tick` | `0x2e260` | 832 | descriptor +0x158 | `0x2e9ce8` setting bool "require_sneak" (current value +0x18), `0x2e9d08` setting bool "require_looking_down" (current value +0x18), `0x2e9d28` setting bool "require_not_forward" (current value +0x18), `0x2e9d78` setting enum "select_blocks" (current value +0x30), ... |
| `bridge_assist_on_late_tick` | `0x2e5a0` | 48 | descriptor +0x168 | `0x2e9d78` setting enum "select_blocks" (current value +0x30) |
| `bridge_assist_edge_check` | `0x2e7b0` | 880 | settings logic | `0x2e9df8` setting num "edge_offset" (current value +0x30), `0x2e9e18` setting bool "randomize" (current value +0x18) |
### Chams
`src/asm/visual/chams.cpp` - descriptor `0x2f0fe0`, settings block `0x2f11d8`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `chams_render_living_entity` | `0x3e730` | 336 | settings logic | `0x2f1208` setting bool "hide_friendlies" (current value +0x18) |
### Chest ESP
`src/asm/visual/chest_esp.cpp` - descriptor `0x2ec908`, settings block `0x2ecb00`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `chest_esp_on_tick` | `0x33990` | 464 | descriptor +0x48 | `0x2ecb48` setting num "range" (current value +0x30), `0x2ecb68` config scalar, `0x2ecb88` config scalar, `0x2ecba8` config scalar, ... |
| `chest_esp_on_render_world` | `0x33b60` | 512 | descriptor +0xa8 | `0x2ecc00` setting num "line_width" (current value +0x30), `0x2ecca0` setting bool "filled" (current value +0x18) |
### Chest Stealer
`src/asm/utility/chest_stealer.cpp` - descriptor `0x2f5288`, settings block `0x2f5480`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `chest_stealer_on_tick` | `0x48600` | 240 | descriptor +0x1d8 | `0x2f54c8` setting num "delay_after_open" (current value +0x30), `0x2f54e8` setting bool "require_vanilla_title" (current value +0x18) |
### Clutch
`src/asm/block/clutch.cpp` - descriptor `0x2f5580`, settings block `0x2f5778`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `clutch_tick` | `0x488f0` | 320 | descriptor +0x48 | `0x2f57c0` setting num "click_speed" (current value +0x30) |
| `clutch_rotation_override_flag` | `0x48a30` | 32 | descriptor +0x118 | |
| `clutch_on_late_tick` | `0x48a50` | 4528 | descriptor +0x168 | `0x2f57f8` setting num "snapback_duration" (current value +0x30), `0x2f5830` setting num "snapback_delay" (current value +0x30), `0x2f5868` setting num "randomization" (current value +0x30), `0x2f58a0` setting num "aim_speed" (current value +0x30), ... |
| `clutch_pick_aim_target` | `0x49f40` | 1024 | settings logic | `0x2f5868` setting num "randomization" (current value +0x30), `0x2f5a78` setting bool "aim_multipoint" (current value +0x18) |
### Criticals
`src/asm/combat/criticals.cpp` - descriptor `0x2ec010`, settings block `0x2ec208`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `criticals_on_tick` | `0x32f10` | 368 | descriptor +0x48 | `0x2ec250` setting num "max_delay" (current value +0x30), `0x2ec288` setting num "chance" (current value +0x30), `0x2ec2a8` setting bool "require_mouse_pressed" (current value +0x18), `0x2ec2c8` setting bool "require_weapon" (current value +0x18), ... |
| `criticals_on_frame` | `0x33080` | 288 | descriptor +0x58 | `0x2ec350` setting num "timer_speed" (current value +0x30), `0x2ec388` setting num "boost" (current value +0x30) |
| `criticals_on_packet_sent` | `0x331a0` | 272 | descriptor +0x78 | |
| `criticals_on_toggle` | `0x332b0` | 96 | descriptor +0x98 | |
### Debug
`src/asm/utility/debug.cpp` - descriptor `0x2f4430`, settings block `0x2f4628`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `debug_on_tick` | `0x466e0` | 368 | descriptor +0x48 | `0x2f4658` setting bool "hurt_time" (current value +0x18), `0x2f4678` setting bool "item_use_state" (current value +0x18) |
| `debug_on_packet_sent` | `0x46850` | 112 | descriptor +0x78 | |
| `debug_on_attack_post` | `0x468c0` | 64 | descriptor +0x138 | `0x2f4698` setting bool "clicks" (current value +0x18) |
| `debug_on_late_tick` | `0x46900` | 256 | descriptor +0x168 | `0x2f4698` setting bool "clicks" (current value +0x18) |
### Fake Lag
`src/asm/utility/fake_lag.cpp` - descriptor `0x2ebcc0`, settings block `0x2ebeb8`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `fake_lag_on_tick` | `0x32b30` | 384 | descriptor +0x48 | `0x2ebf00` setting num "inbound" (current value +0x30), `0x2ebf38` setting num "outbound" (current value +0x30), `0x2ebf58` setting bool "realtime_damage" (current value +0x18), `0x2ebfa0` current value of setting "mode" (+0x30), ... |
| `fake_lag_on_packet_sent` | `0x32cb0` | 176 | descriptor +0x78 | |
### Fast Accel
`src/asm/move/fast_accel.cpp` - descriptor `0x2f27f8`, settings block `0x2f29f0`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `fast_accel_on_move_flying` | `0x42320` | 608 | descriptor +0x1a8 | `0x2f2a38` setting num "acceleration" (current value +0x30), `0x2f2a58` setting bool "require_weapon" (current value +0x18), `0x2f2a78` setting bool "require_not_damaged" (current value +0x18) |
### Fast Mine
`src/asm/block/fast_mine.cpp` - descriptor `0x2f1d90`, settings block `0x2f1f88`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `fast_mine_on_tick` | `0x41a90` | 160 | descriptor +0x48 | `0x2f1fd0` setting num "break_delay" (current value +0x30), `0x2f1ff0` setting bool "decr_block_hit_delay" (current value +0x18) |
| `fast_mine_on_late_tick` | `0x41b30` | 240 | descriptor +0x168 | `0x2f2028` config scalar |
### Fast Place
`src/asm/block/fast_place.cpp` - descriptor `0x2ea2d0`, settings block `0x2ea4c8`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `fast_place_tick` | `0x2fbc0` | 800 | descriptor +0x48 | `0x2ea510` config scalar, `0x2ea548` setting num "activate_delay" (current value +0x30) |
### Free Look
`src/asm/visual/free_look.cpp` - descriptor `0x2edef0`, settings block `0x2ee0e8`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `free_look_on_module_toggle` | `0x37020` | 224 | descriptor +0x98 | `0x2ee148` setting enum "target_perspective" (current value +0x30) |
| `free_look_on_rotation_update` | `0x37100` | 32 | descriptor +0x118 | |
### Friends
`src/asm/utility/friends.cpp` - descriptor `0x2f0038`, settings block `0x2f0230`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `friends_on_middle_click_add` | `0x3b200` | 384 | settings logic | `0x2f0260` setting bool "middle_click_add" (current value +0x18) |
| `friends_on_tick` | `0x3b3c0` | 336 | descriptor +0x48 | |
| `friends_on_toggle` | `0x3b510` | 192 | descriptor +0x98 | |
| `friends_on_shutdown` | `0x3b5d0` | 48 | descriptor +0x1e8 | |
### GUI
`src/asm/visual/gui.cpp` - descriptor `0x2eb740`, settings block `0x2eb938`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `gui_get_scale` | `0x32490` | 32 | settings logic | `0x2eb9a0` setting enum "scale" (current value +0x30) |
| `gui_handle_input_event` | `0x324d0` | 192 | settings logic | `0x2eb9c8` setting bool "allow_input" (current value +0x18) |
| `gui_on_tick` | `0x325c0` | 64 | descriptor +0x48 | |
| `gui_on_toggle` | `0x32600` | 304 | descriptor +0x98 | |
| `gui_on_hud_tick` | `0x32730` | 16 | descriptor +0xd8 | |
| `gui_on_hud_render` | `0x32740` | 128 | descriptor +0xe8 | `0x2eb9e8` setting bool "compact" (current value +0x18), `0x2eba08` setting bool "wide" (current value +0x18) |
| `gui_on_shutdown` | `0x327c0` | 16 | descriptor +0x1e8 | |
### Ghost Hand
`src/asm/block/ghost_hand.cpp` - descriptor `0x2f48b0`, settings block `0x2f4aa8`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `ghost_hand_should_ignore_entity` | `0x46d10` | 288 | settings logic | `0x2f4ad8` setting bool "require_mouse_pressed" (current value +0x18), `0x2f4af8` setting bool "allow_using_fists" (current value +0x18), `0x2f4b18` setting bool "allow_using_sword" (current value +0x18), `0x2f4b38` setting bool "allow_using_tools" (current value +0x18), ... |
| `ghost_hand_on_reach_query` | `0x46e30` | 16 | descriptor +0xc8 | |
### Hit Select
`src/asm/combat/hit_select.cpp` - descriptor `0x2ee4f0`, settings block `0x2ee6e8`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `hit_select_on_tick` | `0x376c0` | 80 | descriptor +0x48 | |
| `hit_select_on_attack` | `0x37710` | 976 | descriptor +0x128 | `0x2ee730` setting num "cancel_rate_air" (current value +0x30), `0x2ee768` setting num "pause_dur" (current value +0x30), `0x2ee7a0` setting num "wait_for_damage_dur" (current value +0x30), `0x2ee7d8` setting num "trades_delay" (current value +0x30), ... |
| `hit_select_on_player_update` | `0x37ae0` | 208 | descriptor +0x158 | `0x2ee8c8` setting bool "use_server_time" (current value +0x18) |
### Hitboxes
`src/asm/combat/hitboxes.cpp` - descriptor `0x2ecca8`, settings block `0x2ecea0`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `hitboxes_on_reach_query` | `0x33fc0` | 32 | descriptor +0xc8 | `0x2ecee8` config scalar |
### Indicators
`src/asm/visual/indicators.cpp` - descriptor `0x2f1440`, settings block `0x2f1638`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `indicators_disable` | `0x3ea40` | 144 | descriptor +0x98 | |
| `indicators_render` | `0x3ead0` | 1104 | descriptor +0xb8 | `0x2f1680` setting num "scale" (current value +0x30), `0x2f16b8` setting num "radius" (current value +0x30) |
| `indicators_tick` | `0x3ef20` | 896 | descriptor +0x168 | `0x2f16d8` setting bool "show_arrow" (current value +0x18), `0x2f16f8` setting bool "show_fireball" (current value +0x18), `0x2f1718` setting bool "show_pearl" (current value +0x18), `0x2f1738` setting bool "require_coming_closer" (current value +0x18) |
### Inv Manager
`src/asm/utility/inv_manager.cpp` - descriptor `0x2f4c40`, settings block `0x2f4e38`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `inv_manager_tick` | `0x47090` | 1232 | descriptor +0x1d8 | `0x2f4e80` setting num "delay_after_open" (current value +0x30), `0x2f5228` setting bool "randomize" (current value +0x18), `0x2f5260` setting num "speed" (current value +0x30), `0x2f5280` setting bool "smart_speed" (current value +0x18) |
| `inv_manager_plan_step` | `0x479c0` | 816 | settings logic | `0x2f4fb0` config scalar |
| `inv_manager_score_item_stack` | `0x47f90` | 240 | settings logic | `0x2f5208` setting bool "sort_hotbar_fallback" (current value +0x18) |
### Inv Walk
`src/asm/move/inv_walk.cpp` - descriptor `0x2ef3d0`, settings block `0x2ef5c8`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `inv_walk_on_toggle` | `0x39790` | 48 | descriptor +0x98 | |
| `inv_walk_tick_keep_movement` | `0x397c0` | 1312 | descriptor +0x158 | `0x2ef620` config scalar, `0x2ef678` config scalar, `0x2ef6b8` setting num "stop_duration" (current value +0x30), `0x2ef6f0` setting enum "stop_non_inv" (current value +0x30) |
| `inv_walk_on_gui_screen` | `0x39ce0` | 336 | descriptor +0x1b8 | `0x2ef620` config scalar |
| `inv_walk_on_inventory_tick` | `0x39e30` | 112 | descriptor +0x1d8 | |
### Item ESP
`src/asm/visual/item_esp.cpp` - descriptor `0x2f1a80`, settings block `0x2f1c78`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `item_esp_render` | `0x40de0` | 1136 | descriptor +0xa8 | `0x2f1ca8` setting bool "show_count" (current value +0x18) |
| `item_esp_tick` | `0x41250` | 1136 | descriptor +0x168 | `0x2f1ca8` setting bool "show_count" (current value +0x18), `0x2f1ce0` setting num "range" (current value +0x30), `0x2f1ce8` config scalar, `0x2f1d20` config scalar |
| `item_esp_shutdown` | `0x416c0` | 64 | descriptor +0x1e8 | |
### Keep Sprint
`src/asm/move/keep_sprint.cpp` - descriptor `0x2ef700`, settings block `0x2ef8f8`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `keep_sprint_on_attack` | `0x3a0f0` | 128 | descriptor +0x128 | `0x2ef940` setting num "chance" (current value +0x30) |
| `keep_sprint_on_attack_post` | `0x3a170` | 240 | descriptor +0x138 | `0x2ef978` config scalar, `0x2ef998` setting bool "require_on_ground" (current value +0x18), `0x2ef9b8` setting bool "allow_reduce" (current value +0x18) |
| `keep_sprint_on_player_update` | `0x3a260` | 48 | descriptor +0x158 | |
### Knockback Delay
`src/asm/combat/knockback_delay.cpp` - descriptor `0x2f40f8`, settings block `0x2f42f0`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `knockback_delay_on_packet_received` | `0x46140` | 304 | descriptor +0x68 | |
| `knockback_delay_on_late_tick` | `0x46270` | 704 | descriptor +0x168 | `0x2f4338` config scalar, `0x2f4358` setting bool "require_air" (current value +0x18), `0x2f4390` setting num "range" (current value +0x30), `0x2f43b0` setting bool "require_weapon" (current value +0x18), ... |
### Knockback Displace
`src/asm/combat/knockback_displace.cpp` - descriptor `0x2f5d60`, settings block `0x2f5f58`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `knockback_displace_on_packet_sent` | `0x4a520` | 32 | descriptor +0x78 | |
| `knockback_displace_on_rotation_update` | `0x4a540` | 32 | descriptor +0x118 | |
| `knockback_displace_on_attack` | `0x4a560` | 624 | descriptor +0x128 | `0x2f5fa0` setting num "cooldown" (current value +0x30), `0x2f5fd8` config scalar, `0x2f5ff8` setting bool "require_no_kb" (current value +0x18), `0x2f6018` setting bool "require_weapon" (current value +0x18), ... |
| `knockback_displace_on_attack_post` | `0x4a7d0` | 48 | descriptor +0x138 | |
| `knockback_displace_on_late_tick` | `0x4a800` | 304 | descriptor +0x168 | `0x2f60e0` setting bool "hide_rotation" (current value +0x18), `0x2f6100` setting bool "use_last_strafe" (current value +0x18) |
| `knockback_displace_direction` | `0x4ab90` | 96 | settings logic | `0x2f60b8` config scalar |
### Lag Range
`src/asm/combat/lag_range.cpp` - descriptor `0x2ef9c0`, settings block `0x2efbb8`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `lag_range_packet_hook_hold` | `0x3a3c0` | 592 | descriptor +0x78 | |
| `lag_range_render_box_interpolated` | `0x3a610` | 368 | descriptor +0xa8 | `0x2efc00` setting num "indicator_line_width" (current value +0x30), `0x2efc40` setting bool "indicator_filled" (current value +0x18) |
| `lag_range_attack_hook` | `0x3a780` | 80 | descriptor +0x138 | |
| `lag_range_tick_decide_whether` | `0x3a7d0` | 816 | descriptor +0x168 | `0x2efc78` config scalar, `0x2efc98` setting bool "require_weapon" (current value +0x18), `0x2efcb8` setting bool "flush_splash_potion" (current value +0x18), `0x2efcd8` setting bool "flush_sprint_reset" (current value +0x18), ... |
### Mod Overlay
`src/asm/visual/mod_overlay.cpp` - descriptor `0x2ecef0`, settings block `0x2ed0e8`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `mod_overlay_tick` | `0x34080` | 528 | descriptor +0x48 | `0x2ed118` setting bool "show_combat" (current value +0x18), `0x2ed138` setting bool "show_move" (current value +0x18), `0x2ed158` setting bool "show_block" (current value +0x18), `0x2ed178` setting bool "show_visual" (current value +0x18), ... |
| `mod_overlay_module_toggled` | `0x34290` | 576 | descriptor +0x98 | `0x2ed118` setting bool "show_combat" (current value +0x18), `0x2ed138` setting bool "show_move" (current value +0x18), `0x2ed158` setting bool "show_block" (current value +0x18), `0x2ed178` setting bool "show_visual" (current value +0x18), ... |
| `mod_overlay_on_hud_tick` | `0x344d0` | 64 | descriptor +0xd8 | |
| `mod_overlay_render` | `0x34510` | 1248 | descriptor +0xe8 | `0x2ed230` setting num "text_size" (current value +0x30), `0x2ed268` setting key "name_color" (current value +0x30), `0x2ed2a8` setting num "bg_rounding" (current value +0x30), `0x2ed2e0` setting num "padding_x" (current value +0x30), ... |
| `mod_overlay_on_profile_event` | `0x349f0` | 32 | descriptor +0xf8 | |
| `mod_overlay_on_overlay_unload` | `0x34a10` | 48 | descriptor +0x1c8 | `0x2ed1b8` setting bool "require_bound" (current value +0x18) |
| `mod_overlay_rebuild_visibility` | `0x34ce0` | 432 | settings logic | `0x2ed118` setting bool "show_combat" (current value +0x18), `0x2ed138` setting bool "show_move" (current value +0x18), `0x2ed158` setting bool "show_block" (current value +0x18), `0x2ed178` setting bool "show_visual" (current value +0x18), ... |
| `mod_overlay_append_entry` | `0x34e90` | 288 | settings logic | `0x2ed1d8` setting bool "lowercase" (current value +0x18), `0x2ed1f8` setting bool "show_details" (current value +0x18) |
### Name Tags
`src/asm/visual/name_tags.cpp` - descriptor `0x2f02d0`, settings block `0x2f04c8`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `name_tags_disable` | `0x3c210` | 112 | descriptor +0x98 | |
| `name_tags_render` | `0x3c280` | 1952 | descriptor +0xb8 | `0x2f0510` setting num "scale" (current value +0x30), `0x2f0530` setting bool "distance_scaling" (current value +0x18), `0x2f0550` setting bool "background" (current value +0x18), `0x2f0570` setting bool "text_shadow" (current value +0x18), ... |
| `name_tags_tick` | `0x3ca20` | 2960 | descriptor +0x168 | `0x2f05b0` setting bool "show_enchants" (current value +0x18), `0x2f05e8` setting num "range" (current value +0x30), `0x2f0608` setting bool "show_own_name_tag" (current value +0x18), `0x2f0628` setting bool "use_display_name" (current value +0x18), ... |
### No Hit Delay
`src/asm/utility/no_hit_delay.cpp` - descriptor `0x2f0b70`, settings block `0x2f0d68`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `no_hit_delay_on_attack` | `0x3e590` | 160 | descriptor +0x128 | `0x2f0dc0` current value of setting "mode" (+0x30) |
### No Item Release
`src/asm/utility/no_item_release.cpp` - descriptor `0x2ec390`, settings block `0x2ec588`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `no_item_release_on_packet_sent` | `0x334c0` | 256 | descriptor +0x78 | `0x2ec5e8` current value of setting "mode" (+0x30) |
### No Slow
`src/asm/move/no_slow.cpp` - descriptor `0x2f2c90`, settings block `0x2f2e88`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `no_slow_on_movement_input` | `0x42a20` | 160 | descriptor +0x198 | `0x2f2ed0` setting num "mult_bow" (current value +0x30), `0x2f2f08` setting num "mult_sword" (current value +0x30), `0x2f2f40` setting num "mult_consumables" (current value +0x30) |
### No Use Delay
`src/asm/utility/no_use_delay.cpp` - descriptor `0x2f1210`, settings block `0x2f1408`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `no_use_delay_on_late_tick` | `0x3e8e0` | 256 | descriptor +0x168 | `0x2f1438` setting bool "allow_desync" (current value +0x18) |
### Notifications
`src/asm/visual/notifications.cpp` - descriptor `0x2ed320`, settings block `0x2ed518`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `notifications_module_toggled_gui` | `0x35000` | 416 | descriptor +0x98 | `0x2ed548` setting bool "hide_bind_only" (current value +0x18), `0x2ed568` setting bool "hide_hold_notifs" (current value +0x18), `0x2ed588` setting bool "show_combat" (current value +0x18), `0x2ed5a8` setting bool "show_move" (current value +0x18), ... |
| `notifications_tick_age_messages` | `0x351a0` | 784 | descriptor +0xd8 | `0x2ed628` setting bool "hide_ingame" (current value +0x18) |
| `notifications_draw_slide_boxes` | `0x354b0` | 1200 | descriptor +0xe8 | `0x2ed628` setting bool "hide_ingame" (current value +0x18) |
| `notifications_profile_loaded_saved` | `0x35960` | 208 | descriptor +0xf8 | |
| `notifications_on_error_event` | `0x35a30` | 192 | descriptor +0x108 | |
| `notifications_on_friend_event` | `0x35af0` | 192 | descriptor +0x178 | |
### Piercing
`src/asm/combat/piercing.cpp` - descriptor `0x2f3ab0`, settings block `0x2f3ca8`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `piercing_decide_which_entity` | `0x44a50` | 496 | settings logic | `0x2f3cd8` setting bool "require_inside_bb" (current value +0x18), `0x2f3cf8` setting bool "require_weapon" (current value +0x18), `0x2f3d18` setting bool "non_players" (current value +0x18), `0x2f3d38` setting bool "friendlies" (current value +0x18), ... |
| `piercing_should_pierce_blocks` | `0x44c40` | 80 | settings logic | `0x2f3cf8` setting bool "require_weapon" (current value +0x18), `0x2f3de8` setting bool "blocks" (current value +0x18) |
| `piercing_on_reach_query` | `0x44c90` | 16 | descriptor +0xc8 | |
### Player ESP
`src/asm/visual/player_esp.cpp` - descriptor `0x2e9e80`, settings block `0x2ea078`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `player_esp_render_mode_1` | `0x2eb20` | 896 | descriptor +0xa8 | `0x2ea0c0` setting num "line_width" (current value +0x30), `0x2ea110` current value of setting "mode" (+0x30), `0x2ea138` config scalar, `0x2ea158` setting bool "hurt_effect" (current value +0x18), ... |
| `player_esp_render_mode_0` | `0x2eea0` | 1360 | descriptor +0xb8 | `0x2ea0c0` setting num "line_width" (current value +0x30), `0x2ea110` current value of setting "mode" (+0x30), `0x2ea138` config scalar, `0x2ea158` setting bool "hurt_effect" (current value +0x18), ... |
| `player_esp_tick` | `0x2f3f0` | 896 | descriptor +0x168 | `0x2ea1d0` setting num "range" (current value +0x30), `0x2ea208` setting key "color_target" (current value +0x30), `0x2ea228` setting key "color_friendlies" (current value +0x30), `0x2ea250` config scalar, ... |
### Pointers
`src/asm/visual/pointers.cpp` - descriptor `0x2f06c0`, settings block `0x2f08b8`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `pointers_render` | `0x3da80` | 1168 | descriptor +0xb8 | `0x2f0950` setting num "scale" (current value +0x30), `0x2f0988` setting num "radius" (current value +0x30), `0x2f09c0` setting key "color_near" (current value +0x30), `0x2f0a10` current value of setting "style" (+0x30), ... |
| `pointers_tick` | `0x3df10` | 960 | descriptor +0x168 | `0x2f0a88` config scalar, `0x2f0ac8` setting num "range" (current value +0x30), `0x2f0b00` setting num "ignore_fov" (current value +0x30), `0x2f0b38` setting key "color_friendlies" (current value +0x30), ... |
### Reach
`src/asm/combat/reach.cpp` - descriptor `0x2ea550`, settings block `0x2ea748`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `reach_on_reach_query` | `0x2ffb0` | 176 | descriptor +0xc8 | `0x2ea790` config scalar, `0x2ea7c8` setting num "activate_time" (current value +0x30), `0x2ea7e8` setting bool "require_sprint" (current value +0x18) |
### Refill
`src/asm/utility/refill.cpp` - descriptor `0x2eed90`, settings block `0x2eef88`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `refill_enable` | `0x384c0` | 48 | descriptor +0x98 | |
| `refill_on_inventory_tick` | `0x384f0` | 1744 | descriptor +0x1d8 | `0x2eefd0` setting num "delay_before_close" (current value +0x30), `0x2ef008` setting num "delay_after_open" (current value +0x30), `0x2ef040` setting num "speed" (current value +0x30), `0x2ef0c0` setting bool "smart_speed" (current value +0x18), ... |
| `refill_classify_27_main` | `0x38dc0` | 272 | settings logic | `0x2ef060` setting bool "refill_soup" (current value +0x18), `0x2ef080` config scalar, `0x2ef0a0` setting bool "refill_potion_non_health" (current value +0x18) |
### Safe Walk
`src/asm/move/safe_walk.cpp` - descriptor `0x2f2030`, settings block `0x2f2228`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `safe_walk_on_tick` | `0x41d30` | 288 | descriptor +0x48 | `0x2f2270` setting num "motion_multiplier" (current value +0x30), `0x2f2290` setting bool "require_looking_down" (current value +0x18), `0x2f22b0` setting bool "require_not_forward" (current value +0x18), `0x2f22d0` setting bool "require_holding_blocks" (current value +0x18) |
### Sprint
`src/asm/move/sprint.cpp` - descriptor `0x2ed840`, settings block `0x2eda38`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `sprint_on_player_update` | `0x35d80` | 176 | descriptor +0x158 | `0x2eda68` setting bool "while_in_gui" (current value +0x18) |
| `sprint_on_movement_input` | `0x35e30` | 192 | descriptor +0x198 | `0x2eda88` setting bool "while_using_item" (current value +0x18), `0x2edaa8` setting bool "while_backwards" (current value +0x18), `0x2edac8` setting bool "while_sideways" (current value +0x18) |
### Sprint Reset
`src/asm/combat/sprint_reset.cpp` - descriptor `0x2efd00`, settings block `0x2efef8`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `sprint_reset_on_player_update` | `0x3ad10` | 688 | descriptor +0x158 | `0x2eff98` setting num "delay" (current value +0x30), `0x2effb8` setting bool "randomize" (current value +0x18), `0x2effd8` setting bool "wait_for_damage" (current value +0x18), `0x2efff8` setting bool "require_weapon" (current value +0x18), ... |
| `sprint_reset_press_key` | `0x3b150` | 176 | settings logic | `0x2eff58` current value of setting "mode" (+0x30) |
### Strafe
`src/asm/move/strafe.cpp` - descriptor `0x2f24e8`, settings block `0x2f26e0`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `strafe_on_movement_input` | `0x41f90` | 48 | descriptor +0x198 | |
| `strafe_on_move_flying` | `0x41fc0` | 576 | descriptor +0x1a8 | `0x2f2728` setting num "max_hurt_time" (current value +0x30), `0x2f2760` setting num "strength_air" (current value +0x30), `0x2f2798` setting num "strength_jump" (current value +0x30), `0x2f27d0` setting num "strength_ground" (current value +0x30), ... |
### Teams
`src/asm/utility/teams.cpp` - descriptor `0x2eb400`, settings block `0x2eb5f8`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `teams_on_tick` | `0x31e10` | 480 | descriptor +0x48 | `0x2eb628` setting bool "match_self_color" (current value +0x18) |
| `teams_on_toggle` | `0x31ff0` | 192 | descriptor +0x98 | |
| `teams_on_shutdown` | `0x320b0` | 48 | descriptor +0x1e8 | |
### Timer
`src/asm/utility/timer.cpp` - descriptor `0x2eba10`, settings block `0x2ebc08`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `timer_on_frame` | `0x328f0` | 192 | descriptor +0x58 | `0x2ebc50` config scalar, `0x2ebc70` setting bool "require_mouse" (current value +0x18), `0x2ebc90` setting bool "require_falling" (current value +0x18), `0x2ebcb0` setting bool "require_damage" (current value +0x18) |
| `timer_on_toggle` | `0x329b0` | 80 | descriptor +0x98 | |
### Timer Range
`src/asm/combat/timer_range.cpp` - descriptor `0x2f3160`, settings block `0x2f3358`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `timer_range_tick` | `0x42c60` | 672 | descriptor +0x48 | `0x2f3388` setting bool "require_mouse_pressed" (current value +0x18), `0x2f33a8` setting bool "require_weapon" (current value +0x18), `0x2f33c8` setting bool "require_not_using_item" (current value +0x18), `0x2f3410` config scalar |
| `timer_range_render_tick` | `0x42f00` | 2640 | descriptor +0x58 | `0x2f3410` config scalar, `0x2f3438` setting bool "block_while_frozen" (current value +0x18), `0x2f3470` setting num "cooldown" (current value +0x30), `0x2f3478` config scalar, ... |
| `timer_range_on_packet_sent` | `0x43950` | 32 | descriptor +0x78 | |
| `timer_range_on_timer_phase` | `0x43970` | 112 | descriptor +0x98 | |
| `timer_range_hypot` | `0x43bf0` | 176 | settings logic | `0x2f3410` config scalar, `0x2f3438` setting bool "block_while_frozen" (current value +0x18) |
### Trajectories
`src/asm/visual/trajectories.cpp` - descriptor `0x2f3df0`, settings block `0x2f3fe8`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `trajectories_disable` | `0x44e40` | 96 | descriptor +0x98 | |
| `trajectories_render` | `0x44ea0` | 1232 | descriptor +0xa8 | `0x2f4030` setting num "line_width" (current value +0x30), `0x2f4068` setting key "color_hit" (current value +0x30) |
| `trajectories_tick` | `0x45370` | 2640 | descriptor +0x168 | `0x2f4090` config scalar, `0x2f40b0` setting bool "render_uncharged_bow" (current value +0x18), `0x2f40d0` setting bool "render_pearls" (current value +0x18), `0x2f40f0` setting bool "render_throwables" (current value +0x18) |
### Velocity
`src/asm/combat/velocity.cpp` - descriptor `0x2ea7f0`, settings block `0x2ea9e8`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `velocity_mode_3_delayed` | `0x30180` | 336 | descriptor +0x48 | `0x2eaa50` config scalar, `0x2eaa90` setting num "ignore_chance" (current value +0x30), `0x2eab68` setting enum "ignore_vertical" (current value +0x30) |
| `velocity_packet_hook_watch` | `0x302d0` | 208 | descriptor +0x68 | |
| `velocity_disable` | `0x303a0` | 32 | descriptor +0x98 | |
| `velocity_attack` | `0x303c0` | 80 | descriptor +0x128 | |
| `velocity_mode_2_reduce` | `0x30410` | 272 | descriptor +0x138 | `0x2eaa50` config scalar, `0x2eaab0` setting bool "require_forward" (current value +0x18), `0x2eaaf0` setting bool "require_weapon" (current value +0x18), `0x2eab90` setting bool "reduce_multiple_per_tick" (current value +0x18) |
| `velocity_tick_mode_0` | `0x30520` | 960 | descriptor +0x158 | `0x2eaa50` config scalar, `0x2eabc8` setting num "reduce_chance" (current value +0x30), `0x2eac00` setting num "reduce_delay" (current value +0x30), `0x2eac38` config scalar, ... |
| `velocity_tick` | `0x308e0` | 80 | descriptor +0x168 | |
| `velocity_shared_conditions` | `0x30c50` | 544 | settings logic | `0x2eaab0` setting bool "require_forward" (current value +0x18), `0x2eaad0` setting bool "require_mouse" (current value +0x18), `0x2eaaf0` setting bool "require_weapon" (current value +0x18), `0x2eab10` setting bool "require_target" (current value +0x18) |
### Weapons
`src/asm/combat/weapons.cpp` - descriptor `0x2ee930`, settings block `0x2eeb28`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `weapons_check_held_item_cached` | `0x37e50` | 272 | settings logic | `0x2eeb58` setting bool "fist" (current value +0x18), `0x2eeb60` config scalar, `0x2eec98` setting bool "enchant_sharpness" (current value +0x18), `0x2eecb8` setting bool "enchant_knockback" (current value +0x18), ... |
| `weapons_is_item_allowed_by_class` | `0x38040` | 112 | settings logic | `0x2eeb58` setting bool "fist" (current value +0x18), `0x2eecf8` setting bool "axes" (current value +0x18), `0x2eed18` setting bool "swords" (current value +0x18) |
| `weapons_find_first_weapon_slot` | `0x380c0` | 208 | settings logic | `0x2eeb78` setting bool (slot) (current value +0x18), `0x2eec98` setting bool "enchant_sharpness" (current value +0x18), `0x2eecb8` setting bool "enchant_knockback" (current value +0x18), `0x2eecd8` setting bool "enchant_fire_aspect" (current value +0x18) |
| `weapons_on_shutdown` | `0x38190` | 48 | descriptor +0x1e8 | |
### No Jump Delay
`src/asm/move/no_jump_delay.cpp` - descriptor `0x2ed630`, settings block `None`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `no_jump_delay_on_tick` | `0x35d50` | 48 | descriptor +0x158 | |
### Ping Fix
`src/asm/utility/ping_fix.cpp` - descriptor `0x2f0dd0`, settings block `None`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `ping_fix_drop_incoming_keepalive` | `0x3e6d0` | 48 | descriptor +0x68 | |
| `ping_fix_drop_outgoing_keepalive` | `0x3e700` | 48 | descriptor +0x78 | |
### Instant Stop
`src/asm/move/instant_stop.cpp` - descriptor `0x2f2a80`, settings block `None`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `instant_stop_on_tick` | `0x42650` | 976 | descriptor +0x148 | |
### Item Use Fix
`src/asm/utility/item_use_fix.cpp` - descriptor `0x2f2f48`, settings block `None`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `item_use_fix_clear_hitting_block` | `0x42b80` | 96 | descriptor +0x128 | |
| `item_use_fix_restore_hitting_block` | `0x42be0` | 80 | descriptor +0x138 | |
### Null Move
`src/asm/move/null_move.cpp` - descriptor `0x2f46a0`, settings block `None`
| function | RVA | bytes | role | reads |
|---|---:|---:|---|---|
| `null_move_on_tick` | `0x46ac0` | 592 | descriptor +0x148 | |
+82
View File
@@ -0,0 +1,82 @@
# Module completeness audit
Generated by `tools/report_modules.py` from `manifest.json`,
`manifest_logic.json` and the decrypted image.
`settings` is declared config objects / registered `R_*` settings.
`logic` is extracted functions / bytes. `descriptor` lists the
function pointers stored in the module descriptor.
| module | cat | gui | settings | logic | descriptor | status |
|---|---|---|---:|---:|---|---|
| Aim Assist | combat | cb+reg+aux | 22 / 22 | 6 / 5040 | +0x58:0x2b200, +0x98:0x2b5b0, +0x118:0x2b610, +0x168:0x2b630, +0x1f8:0x2bae0 | complete |
| Anti Bot | utility | cb+reg | 6 / 6 | 2 / 1408 | +0x48:0x30e70, +0x98:0x31310, +0x1f8:0x313f0 | complete |
| Anti Debuff | visual | cb+reg | 2 / 2 | 1 / 128 | +0x168:0x44940, +0x1f8:0x449c0 | complete |
| Auto Block | combat | cb+reg | 12 / 12 | 5 / 1600 | +0x48:0x31570, +0x78:0x31a50, +0x98:0x31a70, +0x128:0x31b00, +0x168:0x31b60, +0x1f8:0x31bb0 | complete |
| Auto Clicker | combat | cb+reg+aux | 9 / 9 | 4 / 2272 | +0x48:0x2ca10, +0x58:0x2ca90, +0x158:0x2d140, +0x1f8:0x2d190 | complete |
| Auto Pot | utility | cb+reg | 4 / 4 | 4 / 1264 | +0x48:0x391c0, +0x88:0x394d0, +0x98:0x39520, +0x1f8:0x39600 | complete |
| Auto Rod | utility | cb+reg | 7 / 7 | 7 / 2624 | +0x48:0x43d80, +0x88:0x44340, +0x98:0x44390, +0x128:0x443c0, +0x1f8:0x44420 | complete |
| Auto Soup | utility | cb+reg | 9 / 9 | 4 / 1856 | +0x48:0x2d510, +0x88:0x2dae0, +0x98:0x2db10, +0x1f8:0x2db50 | complete |
| Auto Tool | block | cb+reg | 9 / 9 | 2 / 864 | +0x1d8:0x37180, +0x1e8:0x374b0, +0x1f8:0x374e0 | complete |
| Auto Weapon | combat | cb+reg | 2 / 2 | 1 / 320 | +0x1d8:0x4a340, +0x1f8:0x4a480 | complete |
| Backtrack | combat | cb+reg+aux | 12 / 12 | 3 / 1904 | +0x68:0x35fd0, +0xa8:0x36110, +0x168:0x362e0, +0x1f8:0x36740 | complete |
| Blink | utility | cb+reg+aux | 5 / 5 | 5 / 464 | +0x48:0x33660, +0x68:0x336d0, +0x78:0x33750, +0x98:0x33770, +0x128:0x337c0, +0x1f8:0x33830 | complete |
| Block ESP | visual | cb+reg | 5 / 5 | 5 / 1648 | +0x48:0x3f4c0, +0x98:0x3f6a0, +0xa8:0x3f7b0, +0x188:0x3fa40, +0x1e8:0x3fa50, +0x1f8:0x3fb30 | complete |
| Block In | block | cb+reg | 2 / 2 | 5 / 4048 | +0x48:0x4abf0, +0x98:0x4ac00, +0x118:0x4ad50, +0x148:0x4ad80, +0x168:0x4ae50, +0x1f8:0x4bbc0 | complete |
| Bridge Assist | block | cb+reg | 10 / 10 | 5 / 2800 | +0x98:0x2de50, +0x128:0x2dec0, +0x158:0x2e260, +0x168:0x2e5a0, +0x1f8:0x2e5d0 | complete |
| Chams | visual | cb+reg | 1 / 1 | 1 / 336 | +0x1f8:0x3e880 | complete |
| Chest ESP | visual | cb+reg | 11 / 11 | 2 / 976 | +0x48:0x33990, +0xa8:0x33b60, +0x1f8:0x33d60 | complete |
| Chest Stealer | utility | cb+reg | 5 / 5 | 1 / 240 | +0x1d8:0x48600, +0x1f8:0x486f0 | complete |
| Clutch | block | cb+reg | 18 / 18 | 4 / 5904 | +0x48:0x488f0, +0x118:0x48a30, +0x168:0x48a50, +0x1f8:0x49c00 | complete |
| Criticals | combat | cb+reg+aux | 7 / 7 | 4 / 1024 | +0x48:0x32f10, +0x58:0x33080, +0x78:0x331a0, +0x98:0x332b0, +0x1f8:0x33310 | complete |
| Debug | utility | cb+reg | 3 / 3 | 4 / 800 | +0x48:0x466e0, +0x78:0x46850, +0x138:0x468c0, +0x168:0x46900, +0x1f8:0x46a00 | complete |
| Fake Lag | utility | cb+reg+aux | 7 / 7 | 2 / 560 | +0x48:0x32b30, +0x68:0x32cb0, +0x78:0x32cb0, +0x1f8:0x32d60 | complete |
| Fast Accel | move | cb+reg | 3 / 3 | 1 / 608 | +0x1a8:0x42320, +0x1f8:0x42580 | complete |
| Fast Mine | block | cb+reg+aux | 3 / 3 | 2 / 400 | +0x48:0x41a90, +0x168:0x41b30, +0x1f8:0x41c20 | complete |
| Fast Place | block | cb+reg+aux | 2 / 2 | 1 / 800 | +0x48:0x2fbc0, +0x1f8:0x2fee0 | complete |
| Free Look | visual | cb+reg | 1 / 1 | 2 / 256 | +0x98:0x37020, +0x118:0x37100, +0x1f8:0x37120 | complete |
| Friends | utility | cb+reg | 2 / 2 | 4 / 960 | +0x48:0x3b3c0, +0x98:0x3b510, +0x1e8:0x3b5d0, +0x1f8:0x3b600 | complete |
| GUI | visual | cb+reg | 5 / 5 | 7 / 752 | +0x48:0x325c0, +0x98:0x32600, +0xd8:0x32730, +0xe8:0x32740, +0x1e8:0x327c0, +0x1f8:0x327d0 | complete |
| Ghost Hand | block | cb+reg | 12 / 12 | 2 / 304 | +0xc8:0x46e30, +0x1f8:0x46e40 | complete |
| Hit Select | combat | cb+reg+aux | 12 / 12 | 3 / 1264 | +0x48:0x376c0, +0x128:0x37710, +0x158:0x37ae0, +0x1f8:0x37bb0 | complete |
| Hitboxes | combat | cb+reg+aux | 1 / 1 | 1 / 32 | +0xc8:0x33fc0, +0x1f8:0x33fe0 | complete |
| Indicators | visual | cb+reg | 6 / 6 | 3 / 2144 | +0x98:0x3ea40, +0xb8:0x3ead0, +0x168:0x3ef20, +0x1f8:0x3f2a0 | complete |
| Inv Manager | utility | cb+reg | 16 / 16 | 3 / 2288 | +0x1d8:0x47090, +0x1f8:0x47560 | complete |
| Inv Walk | move | cb+reg+aux | 4 / 4 | 4 / 1808 | +0x98:0x39790, +0x158:0x397c0, +0x1b8:0x39ce0, +0x1d8:0x39e30, +0x1f8:0x39ea0 | complete |
| Item ESP | visual | cb+reg | 5 / 5 | 3 / 2336 | +0xa8:0x40de0, +0x168:0x41250, +0x1e8:0x416c0, +0x1f8:0x41700 | complete |
| Keep Sprint | move | cb+reg+aux | 4 / 4 | 3 / 416 | +0x128:0x3a0f0, +0x138:0x3a170, +0x158:0x3a260, +0x1f8:0x3a290 | complete |
| Knockback Delay | combat | cb+reg+aux | 7 / 7 | 2 / 1008 | +0x68:0x46140, +0x168:0x46270, +0x1f8:0x46530 | complete |
| Knockback Displace | combat | cb+reg+aux | 9 / 9 | 6 / 1136 | +0x78:0x4a520, +0x118:0x4a540, +0x128:0x4a560, +0x138:0x4a7d0, +0x168:0x4a800, +0x1f8:0x4a930 | complete |
| Lag Range | combat | cb+reg+aux | 8 / 8 | 4 / 1856 | +0x78:0x3a3c0, +0xa8:0x3a610, +0x138:0x3a780, +0x168:0x3a7d0, +0x1f8:0x3ab00 | complete |
| Mod Overlay | visual | cb+reg | 14 / 14 | 8 / 3216 | +0x48:0x34080, +0x98:0x34290, +0xd8:0x344d0, +0xe8:0x34510, +0xf8:0x349f0, +0x1c8:0x34a10, +0x1f8:0x34a40 | complete |
| Name Tags | visual | cb+reg | 12 / 12 | 3 / 5024 | +0x98:0x3c210, +0xb8:0x3c280, +0x168:0x3ca20, +0x1f8:0x3d5b0 | complete |
| No Hit Delay | utility | cb+reg+aux | 1 / 1 | 1 / 160 | +0x128:0x3e590, +0x1f8:0x3e630 | complete |
| No Item Release | utility | cb+reg+aux | 1 / 1 | 1 / 256 | +0x78:0x334c0, +0x1f8:0x335c0 | complete |
| No Slow | move | cb+reg | 3 / 3 | 1 / 160 | +0x198:0x42a20, +0x1f8:0x42ac0 | complete |
| No Use Delay | utility | cb+reg | 1 / 1 | 1 / 256 | +0x168:0x3e8e0, +0x1f8:0x3e9e0 | complete |
| Notifications | visual | cb+reg | 8 / 8 | 6 / 2992 | +0x98:0x35000, +0xd8:0x351a0, +0xe8:0x354b0, +0xf8:0x35960, +0x108:0x35a30, +0x178:0x35af0, +0x1f8:0x35bb0 | complete |
| Piercing | combat | cb+reg | 8 / 8 | 3 / 592 | +0xc8:0x44c90, +0x1f8:0x44ca0 | complete |
| Player ESP | visual | cb+reg+aux | 13 / 13 | 3 / 3152 | +0xa8:0x2eb20, +0xb8:0x2eea0, +0x168:0x2f3f0, +0x1f8:0x2f770 | complete |
| Pointers | visual | cb+reg+aux | 13 / 13 | 2 / 2128 | +0xb8:0x3da80, +0x168:0x3df10, +0x1f8:0x3e2d0 | complete |
| Reach | combat | cb+reg+aux | 3 / 3 | 1 / 176 | +0xc8:0x2ffb0, +0x1f8:0x30060 | complete |
| Refill | utility | cb+reg | 8 / 8 | 3 / 2064 | +0x98:0x384c0, +0x1d8:0x384f0, +0x1f8:0x38bc0 | complete |
| Safe Walk | move | cb+reg | 4 / 4 | 1 / 288 | +0x48:0x41d30, +0x1f8:0x41e50 | complete |
| Sprint | move | cb+reg | 4 / 4 | 2 / 368 | +0x158:0x35d80, +0x198:0x35e30, +0x1f8:0x35ef0 | complete |
| Sprint Reset | combat | cb+reg+aux | 6 / 6 | 2 / 864 | +0x158:0x3ad10, +0x1f8:0x3afc0 | complete |
| Strafe | move | cb+reg | 5 / 5 | 2 / 624 | +0x198:0x41f90, +0x1a8:0x41fc0, +0x1f8:0x42200 | complete |
| Teams | utility | cb+reg | 2 / 2 | 3 / 720 | +0x48:0x31e10, +0x98:0x31ff0, +0x1e8:0x320b0, +0x1f8:0x320e0 | complete |
| Timer | utility | cb+reg+aux | 4 / 4 | 2 / 272 | +0x58:0x328f0, +0x98:0x329b0, +0x1f8:0x32a00 | complete |
| Timer Range | combat | cb+reg+aux | 9 / 9 | 5 / 3632 | +0x48:0x42c60, +0x58:0x42f00, +0x78:0x43950, +0x98:0x43970, +0x1f8:0x439e0 | complete |
| Trajectories | visual | cb+reg | 7 / 7 | 3 / 3968 | +0x98:0x44e40, +0xa8:0x44ea0, +0x168:0x45370, +0x1f8:0x45dc0 | complete |
| Velocity | combat | cb+reg+aux | 14 / 14 | 8 / 2512 | +0x48:0x30180, +0x68:0x302d0, +0x98:0x303a0, +0x128:0x303c0, +0x138:0x30410, +0x158:0x30520, +0x168:0x308e0, +0x1f8:0x30930 | complete |
| Weapons | combat | cb+reg | 16 / 16 | 4 / 640 | +0x1e8:0x38190, +0x1f8:0x381c0 | complete |
| No Jump Delay | move | — | 0 / 0 | 1 / 48 | +0x158:0x35d50 | no-GUI + logic |
| Ping Fix | utility | — | 0 / 0 | 2 / 96 | +0x68:0x3e6d0, +0x78:0x3e700 | no-GUI + logic |
| No Hurt Cam | visual | — | 0 / 0 | 0 / 0 | — | no-GUI (engine-side) |
| Instant Stop | move | — | 0 / 0 | 1 / 976 | +0x148:0x42650 | no-GUI + logic |
| Item Use Fix | utility | — | 0 / 0 | 2 / 176 | +0x128:0x42b80, +0x138:0x42be0 | no-GUI + logic |
| Null Move | move | — | 0 / 0 | 1 / 592 | +0x148:0x46ac0 | no-GUI + logic |
**Totals:** 67 modules, 200 logic functions, 92400 logic bytes.
No missing module, setting, callback or logic function was found.
+107
View File
@@ -0,0 +1,107 @@
# Modules by category
Category = descriptor field `+0x10` (0 Combat, 1 Move, 2 Visual, 3 Block,
4 Utility), matching the GUI tab strings at `.rdata` `0x151cc8`. Paths are
relative to the repo root. The first five sections are the 61 modules with a
GUI callback; the six engine-only modules are listed at the end.
### Combat (17)
| module | file | functions | description |
|---|---|---|---|
| Aim Assist | [aim_assist.h](../src/logic/combat/aim_assist.h) | cb, reg, aux | Pulls your crosshair towards other players. |
| Auto Block | [auto_block.h](../src/logic/combat/auto_block.h) | cb, reg | Blocks your sword for you in combat. |
| Auto Clicker | [auto_clicker.h](../src/logic/combat/auto_clicker.h) | cb, reg, aux | Clicks for you when holding down left-click. |
| Auto Weapon | [auto_weapon.h](../src/logic/combat/auto_weapon.h) | cb, reg | Selects your weapon when aiming on players. |
| Backtrack | [backtrack.h](../src/logic/combat/backtrack.h) | cb, reg, aux | Uses lag to make players easier to hit. |
| Criticals | [criticals.h](../src/logic/combat/criticals.h) | cb, reg, aux | Increases chance of landing critical hits. |
| Hit Select | [hit_select.h](../src/logic/combat/hit_select.h) | cb, reg, aux | Perfectly times your hits. |
| Hitboxes | [hitboxes.h](../src/logic/combat/hitboxes.h) | cb, reg, aux | Expands hitboxes of other players. |
| Knockback Delay | [knockback_delay.h](../src/logic/combat/knockback_delay.h) | cb, reg, aux | Delays your knockback in certain scenarios. |
| Knockback Displace | [knockback_displace.h](../src/logic/combat/knockback_displace.h) | cb, reg, aux | Allows dealing knockback at a different angle. |
| Lag Range | [lag_range.h](../src/logic/combat/lag_range.h) | cb, reg, aux | Uses lag to stay out of target range. |
| Piercing | [piercing.h](../src/logic/combat/piercing.h) | cb, reg | Allows hitting entities through obstructions. |
| Reach | [reach.h](../src/logic/combat/reach.h) | cb, reg, aux | Increases your melee range. |
| Sprint Reset | [sprint_reset.h](../src/logic/combat/sprint_reset.h) | cb, reg, aux | Resets your sprint state to deal more knockback. |
| Timer Range | [timer_range.h](../src/logic/combat/timer_range.h) | cb, reg, aux | Abuses timer lag to teleport you. |
| Velocity | [velocity.h](../src/logic/combat/velocity.h) | cb, reg, aux | Reduces the amount of knockback you take. |
| Weapons | [weapons.h](../src/logic/combat/weapons.h) | cb, reg | Configure which items are considered weapons. |
### Move (7)
| module | file | functions | description |
|---|---|---|---|
| Fast Accel | [fast_accel.h](../src/logic/move/fast_accel.h) | cb, reg | Speeds up acceleration. |
| Inv Walk | [inv_walk.h](../src/logic/move/inv_walk.h) | cb, reg, aux | Allows moving in inventory. |
| Keep Sprint | [keep_sprint.h](../src/logic/move/keep_sprint.h) | cb, reg, aux | Keeps your sprint speed when attacking players. |
| No Slow | [no_slow.h](../src/logic/move/no_slow.h) | cb, reg | Removes slowdown while using items. |
| Safe Walk | [safe_walk.h](../src/logic/move/safe_walk.h) | cb, reg | Prevents you from walking off ledges. |
| Sprint | [sprint.h](../src/logic/move/sprint.h) | cb, reg | Automatically sprints for you. |
| Strafe | [strafe.h](../src/logic/move/strafe.h) | cb, reg | Allows faster strafing. |
### Visual (14)
| module | file | functions | description |
|---|---|---|---|
| Anti Debuff | [anti_debuff.h](../src/logic/visual/anti_debuff.h) | cb, reg | Removes negative visual effects. |
| Block ESP | [block_esp.h](../src/logic/visual/block_esp.h) | cb, reg | Highlights blocks. |
| Chams | [chams.h](../src/logic/visual/chams.h) | cb, reg | Renders players behind blocks. |
| Chest ESP | [chest_esp.h](../src/logic/visual/chest_esp.h) | cb, reg | Highlights chests. |
| Free Look | [free_look.h](../src/logic/visual/free_look.h) | cb, reg | Freely move the camera around. |
| GUI | [gui.h](../src/logic/visual/gui.h) | cb, reg | Graphical interface for the cheat. |
| Indicators | [indicators.h](../src/logic/visual/indicators.h) | cb, reg | Points to incoming projectiles. |
| Item ESP | [item_esp.h](../src/logic/visual/item_esp.h) | cb, reg | Highlights dropped items. |
| Mod Overlay | [mod_overlay.h](../src/logic/visual/mod_overlay.h) | cb, reg | Displays a list of all active mods. |
| Name Tags | [name_tags.h](../src/logic/visual/name_tags.h) | cb, reg | Makes player name tags easier to see. |
| Notifications | [notifications.h](../src/logic/visual/notifications.h) | cb, reg | Informs you when certain actions are performed. |
| Player ESP | [player_esp.h](../src/logic/visual/player_esp.h) | cb, reg, aux | Highlights other players. |
| Pointers | [pointers.h](../src/logic/visual/pointers.h) | cb, reg, aux | Points to other players. |
| Trajectories | [trajectories.h](../src/logic/visual/trajectories.h) | cb, reg | Predicts the trajectory of the item in your hand. |
### Block (7)
| module | file | functions | description |
|---|---|---|---|
| Auto Tool | [auto_tool.h](../src/logic/block/auto_tool.h) | cb, reg | Selects the best tool when digging. |
| Block In | [block_in.h](../src/logic/block/block_in.h) | cb, reg | Places blocks around you. |
| Bridge Assist | [bridge_assist.h](../src/logic/block/bridge_assist.h) | cb, reg | Sneaks and unsneaks for you while bridging. |
| Clutch | [clutch.h](../src/logic/block/clutch.h) | cb, reg | Clutches for you when falling. |
| Fast Mine | [fast_mine.h](../src/logic/block/fast_mine.h) | cb, reg, aux | Increases block mining speed. |
| Fast Place | [fast_place.h](../src/logic/block/fast_place.h) | cb, reg, aux | Allows placing blocks faster. |
| Ghost Hand | [ghost_hand.h](../src/logic/block/ghost_hand.h) | cb, reg | Allows interacting with blocks through entities. |
### Utility (16)
| module | file | functions | description |
|---|---|---|---|
| Anti Bot | [anti_bot.h](../src/logic/utility/anti_bot.h) | cb, reg | Ignores NPCs for specific modules. |
| Auto Pot | [auto_pot.h](../src/logic/utility/auto_pot.h) | cb, reg | Uses healing potions for you. |
| Auto Rod | [auto_rod.h](../src/logic/utility/auto_rod.h) | cb, reg | Throws your fishing rod for you. |
| Auto Soup | [auto_soup.h](../src/logic/utility/auto_soup.h) | cb, reg | Uses soups for you. |
| Blink | [blink.h](../src/logic/utility/blink.h) | cb, reg, aux | Simulates teleporting using lag. |
| Chest Stealer | [chest_stealer.h](../src/logic/utility/chest_stealer.h) | cb, reg | Automatically loots chests for you. |
| Debug | [debug.h](../src/logic/utility/debug.h) | cb, reg | Prints debug information in chat. |
| Fake Lag | [fake_lag.h](../src/logic/utility/fake_lag.h) | cb, reg, aux | Adds an artificial delay to your packets. |
| Friends | [friends.h](../src/logic/utility/friends.h) | cb, reg | Lets you mark other players as friendly. |
| Inv Manager | [inv_manager.h](../src/logic/utility/inv_manager.h) | cb, reg | Organises your inventory for you. |
| No Hit Delay | [no_hit_delay.h](../src/logic/utility/no_hit_delay.h) | cb, reg, aux | Removes 1.8 swing delay. |
| No Item Release | [no_item_release.h](../src/logic/utility/no_item_release.h) | cb, reg, aux | Hides item release from server. |
| No Use Delay | [no_use_delay.h](../src/logic/utility/no_use_delay.h) | cb, reg | Removes vanilla delay when consuming items. |
| Refill | [refill.h](../src/logic/utility/refill.h) | cb, reg | Automatically moves items into your hotbar. |
| Teams | [teams.h](../src/logic/utility/teams.h) | cb, reg | Ignores teammates for specific modules. |
| Timer | [timer.h](../src/logic/utility/timer.h) | cb, reg, aux | Modifies the speed the game runs at. |
### Engine-only (6)
These descriptors exist in the image but have no GUI callback and no settings
pane. They are kept in `manifest.json` so the module table is exactly the 67
descriptors from the image.
| module | category | file | description |
|---|---|---|---|
| No Jump Delay | move | [no_jump_delay.h](../src/logic/move/no_jump_delay.h) | Removes cooldown when holding jump key. |
| Ping Fix | utility | [ping_fix.h](../src/logic/utility/ping_fix.h) | Shows your real ping while using lag mods. |
| No Hurt Cam | visual | [no_hurt_cam.h](../src/logic/visual/no_hurt_cam.h) | Removes camera shake when taking damage. |
| Instant Stop | move | [instant_stop.h](../src/logic/move/instant_stop.h) | Instantly stops moving when move keys are released. |
| Item Use Fix | utility | [item_use_fix.h](../src/logic/utility/item_use_fix.h) | Allows using items while breaking blocks. |
| Null Move | move | [null_move.h](../src/logic/move/null_move.h) | Makes it easier to switch move direction instantly. |
+65
View File
@@ -0,0 +1,65 @@
# Reverse-engineering notes
Carried over from `slinkyfullsrc/SRC.md`. None of this is needed to build the
module sources.
## `.slinky0` — engine / module manager
`slinky2.decrypted/slinky0.bin` is 14 255 293 bytes (`.slinky0` of the client
image). Region map (verified):
| range | content |
|---|---|
| `0x000000–0x77FFFF` | native code + data (MSVC CRT/RTTI strings at `0x2d000–0x3c000`, engine code; clean x86-64, ~30 % printable) |
| `0x780000–0x8BFFFF` | high-entropy (7.99) blob — encrypted/compressed, not consumed by visible code |
| `0x8C0000–0x9FFFFF` | sorted `(u32 rva, u16 size)` table, 213 035 records, covers `0x36300E–0x867551`, sizes multiple of 8, dense (≈50 % of the region), deltas 2–16 bytes, overlap → looks like a per-site patch/relocation or checksum map for the code region |
| `0xA00000–0xAA0000` | table A, XOR `00 10 00 62 75 86`, 109 225 × `(u32 off, u16 size)`; offsets `0x223–0x27FFB4` fit *inside* the tail block; sizes ∈ {0,8,24,32,…,120} |
| `0xAA0000–0xAB0000` | table B, XOR `83 A7 00 10 00 53`, same 6-byte record shape |
| `0xAB0000–0xD80000` | `tail_high.bin` — 2 949 120 bytes, entropy 7.98, no standard container/PE; table A indexes into it |
| `0xD80000–0xD984BD` | `tail_code.bin` (~100 KB), mixed code/data |
Module-manager data model (recovered from the client image, used by the
reconstructed sources):
* Module descriptor in `.data` (`.rdata` string pointers + a factory/log
function at `+0x58`). `modules_real.json` lists all 67 (61 with a GUI cb).
* Per-module settings block in `.data`, variable stride, begins with three
function pointers `{gui_cb, register_settings, aux}` followed by the widget
cfg objects (`CfgBool/CfgSlider/CfgRange/CfgEnum/CfgColor/...`).
* `R_*` registration calls are made from the client into the engine manager at
DLL init (one `reg_<addr>` function per module), the `W_*` calls render the
settings pane.
The module-side functions that read the settings and implement each feature
are in `src/logic/` (see `docs/logic.md`); they were extracted 1:1 from `.text`
by `tools/extract_logic.py`.
The manager itself is in client `.text`, not `.slinky0`: constructor `0x28000`,
dispatch `0x285d0`, descriptor table `0x14f810`. It groups modules by category
and by the event callback slots at `descriptor+0x48` … `+0x1F8`, and dispatches
the enabled callbacks. See `docs/engine.md` for the full picture, including
the six engine-only modules. `.slinky0` has **no static RIP-relative
references** into the client's `.rdata`/`.data`/`.text` (checked: 0 refs), so
it receives whatever it needs through runtime-installed pointers.
### tail_high.bin
`tail_high.bin` is still packed. Findings:
* Not a PE, not ZIP/7z/RAR/gzip/zlib/LZMA (standard headers checked).
* Entropy 7.978; byte histogram ≈ uniform; no repeated 24-byte blocks.
* Table A (109 225 records) and the sorted table (213 035 records) both use the
same `(u32 offset, u16 size)` record form, sizes are multiples of 8, and both
point *into* their subject buffers with heavy overlap — this is a
*site map* (patch/relocation/checksum), not a container directory.
* The runtime copy of `.slinky0` captured in `slinky.dll` is byte-identical to
the static stream up to `0xC4B658` and zero beyond, i.e. that dump was taken
before the engine performed the tail transformation; it gives no plaintext.
* Therefore decryption requires either (a) implementing the protector's
per-site transform from the `0x8C0000` table, or (b) a live Wine trace of
the first execution that touches `slinky0+0xAB0000` (same breakpoint
technique that recovered the `.slinky2` LZMA streams).
Decrypting `tail_high.bin` is **not required** for anything in this tree: the
module callbacks/registrations are plain `.text`, and the registry data model
is plain `.data`. It is required only for the remainder of the engine internals.
+170
View File
@@ -0,0 +1,170 @@
# Function renames (this pass)
Inferred from behaviour. `hitboxes_apply_hitbox_expansion_original` is the original engine copy; the descriptor uses the correctly named `hitboxes_on_reach_query` logic function. `block_esp_queue_loaded_chunks` was verified against its body (it queues the loaded chunks for scanning).
| old | new |
|---|---|
| `packet_entity_lookup` | `no_item_release_on_send_packet` |
| `activate_hold` | `hitboxes_apply_hitbox_expansion_original` |
| `compare_height_desc` | `compare_overlay_entries_by_height_desc` |
| `take_pending_bool` | `take_and_clear_pending_flag` |
| `rotation_cache_toggle` | `free_look_on_toggle` |
| `store_cached_rotation` | `free_look_save_rotation` |
| `enchantment_flags_item_stack` | `get_weapon_enchant_flags` |
| `weapons_part` | `is_held_item_accepted_weapon` |
| `click_with_helper` | `is_holding_accepted_weapon` |
| `close_screen_and_release` | `refill_disable_and_close_screen` |
| `find_hotbar_cell` | `refill_pick_inventory_cell` |
| `remove_id_key` | `friend_set_remove_by_key` |
| `clear_id_flag` | `friend_set_clear_dirty` |
| `compare_hurt_time` | `compare_by_hurt_time_desc` |
| `clear_list_set` | `free_block_search_section_lists` |
| `text_filter_upper` | `search_box_append_char` |
| `sort_float_entries` | `block_esp_queue_loaded_chunks` |
| `clear_mesh` | `rebuild_block_esp_mesh` |
| `free_allocator_pages` | `free_block_search_cache` |
| `pending_action_flag` | `timer_range_is_delay_pending` |
| `item_speed_by_type` | `no_slow_item_use_speed` |
| `zero_motion_tick` | `anti_debuff_tick` |
## Context struct (`Ctx`) fields
Every module handler receives a `Ctx *c`. Fields were previously `a`, `b`, `c18`, ...:
| old | new | meaning |
|---|---|---|
| `a` | `jvm` | JNI/JVM handle, first argument of every `java_*` call |
| `p08` | `module_manager` | target of `queue_module_toggle` |
| `p10` | `profile_list` | |
| `c18` | `minecraft` | Minecraft instance |
| `p20` | `world` | client world |
| `b` | `player` | local player entity |
| `p30` | `key_state_table` | |
| `p38` | `target` | current combat target record |
| `f40` | `in_game` | non-zero while a world is loaded |
## Event handler names
Most handlers are `<module>_on_<event>`, where the event is the descriptor slot the handler is registered in; handlers whose purpose is clearer keep a semantic name (for example `velocity_mode_3_delayed`, `notifications_draw_slide_boxes`):
`+0x48 tick`, `+0x58 frame`, `+0x68 packet_received`, `+0x78 packet_sent`, `+0x88 input`,
`+0x98 toggle`, `+0xa8 render_world`, `+0xb8 render_entity_overlay`, `+0xc8 reach_query`,
`+0xd8 hud_tick`, `+0xe8 hud_render`, `+0x118 rotation_update`, `+0x128 attack`,
`+0x138 attack_post`, `+0x158 player_update`, `+0x168 late_tick`, `+0x188 world_change`,
`+0x198 movement_input`, `+0x1a8 move_flying`, `+0x1b8 game_packet_sent`,
`+0x1d8 inventory_tick`, `+0x1e8 shutdown`. These slot meanings are inferred from the
handlers' behaviour, not from the original binary.
## Locals, parameters and struct fields
Renamed with a scope-aware AST pass (every edit is validated against the compiler and the generated object code):
- `Jvm *j`, `void *a` (first parameter) -> `jvm`; `Ctx *c` -> `ctx`; `*Hook *h` -> `hook`
- JNI accessor object parameters take the class from the accessor body, e.g. `a1` -> `game_settings`, `key_binding`, `entity_player`; setter value parameters are `value`
- locals initialised from an accessor are named after it (`mc` -> `minecraft`, `it` -> `stack`, ...)
- NanoVG context `ig` -> `nvg`, GUI state block `g` -> `gui`, `Rot r` -> `rotation`, register-function argument `m` -> `module`
- duplicate `HCtx` structs use the same field names as `Ctx` (`jvm`, `module_manager`, `minecraft`, `key_state_table`, `target`, `in_game`, ...)
- event structs: `f24` -> `verdict`, `p10` -> `packet`
## GUI state block (`include/slinky/gui_state.h`)
All raw `gui + 0x...` / `gui[0x...]` accesses are now named members of `struct GuiState`
(`toast_text`, `tabs`, `selected_tab`, `input_mode`, `edit_text`, `popup_kind`, `popup_rgb`, `popup_hsl`,
`popup_list_*`, `mouse_scene`, `scroll`, `row_height`, `click_capture`, ...). The layout-row stack at the start of the
block is `GuiState::rows[5]` (`GuiLayoutRow`, 0x78 bytes each). Offsets are checked with `static_assert`s; overlapping
uses (a text buffer that doubles as a key-bind callback, the popup payload) are unions. Field names are inferred
from how the GUI code uses them.
## `__N` disambiguators
Duplicate constants, strings and tables no longer carry `__N`: a symbol that is used from one file gets
`_in_<file>` (for example `row_height_20_in_gui_controls`), module state that already starts with the module name gets an
ordinal (`aim_assist_rng_states_second`), and symbols only referenced from the data tables get `_copy`.
## Verification note
The only generated-code difference from the original tree is in `gui_layout.cpp`, where the layout-row index is now
written `&gui->rows[n - 1]`; the compiler emits the same address computation with base and index registers swapped.
## Widgets, padding and parameter fixes
- Settings widgets: the `W1`/`W2`/`W3`/`Wid` structs became `DropdownSetting`, `ColorSetting`, `ListSetting`,
`ToggleSetting`, `SliderSetting` and `RangeSetting`, with fields such as `label`, `format`, `step`, `scale`, `min`,
`max`, `low`, `high`, `names`, `optional`, `popup_title`, `get_candidates`. `ListSet` / `ListOps` gained `add` and
`remove`.
- Misnamed functions: `draw_keybind_control` -> `draw_dropdown_row`, `draw_checkbox` -> `draw_color_swatch_row`,
`draw_colored_checkbox` -> `draw_list_editor_row`, `draw_label` -> `draw_toggle_row`, `draw_slider_label` ->
`draw_slider_row`, `draw_slider_with_label` -> `draw_range_slider_row`, `draw_label_va` -> `draw_section_label`,
`get_net_handler` -> `get_game_settings` (it returns `Minecraft.gameSettings`).
- Module GUI callbacks take `(GuiState *gui, window)` and the `widget_*` functions take `(window, gui, setting)`; an earlier
pass had called the first argument `jvm`, which was wrong for these functions and is fixed.
- Padding fields are named by their byte offset (`padding_08`, `padding_11`, ...), computed from clang's record layouts.
Offset-named fields with a known role got names (`on_shutdown`, `register_settings`, `fill_fraction`,
`key_state_table`, `reach`, ...); the remaining zeroed filler bytes are `reserved_NN`.
- `hook_*` natives: `(env, native_class, receiver, ...)`; `java_jni_is_instance_of_*` takes `candidate`;
integer error-code locals are `error_code`.
## Build fix and key-binding accessors
- `nvg_names.cpp` failed to compile after the parameter pass (the parameter was renamed to `nvg` but the macro-free bodies
still used `c`); fixed. The two NanoVG-dependent files were not part of my per-file compile checks, so the full CMake
cross-build is the reference check; it builds `slinky.dll` cleanly.
- Key-binding accessors are named by the `GameSettings.keyBindings` index they read:
`java_get_game_settings_attack_key` (0), `_use_item_key` (1), `_forward_key` (2), `_left_key` (3), `_back_key` (4),
`_right_key` (5), `_sneak_key` (7), `_key_8_or_9`, `_key_17_or_8` (mapping-dependent).
- Other numbered accessors: `java_get_minecraft_object_mouse_over`, `java_get_minecraft_player_controller`,
`java_get_entity_player_inventory`, `java_get_moving_object_position_type_ordinal` / `_entity_hit`,
`java_increment_key_binding_press_time`.
- `timer_range_last_forward_input` / `timer_range_last_strafe_input` were swapped in an earlier pass; fixed (they come
from the forward/back and left/right keys respectively).
- `CombatTarget` (engine.h) replaces raw `ctx->target + 0x18` reads with `->valid`.
## JNI calls, numbered names, constants, padding
- Raw JNI function-table calls (`(*(void ***)env)[0xb0 / 8]` and friends) are now named calls through the existing
`JNIEnv` table: `(*env)->DeleteGlobalRef`, `IsSameObject`, `NewGlobalRef`, `NewStringUTF`, `GetObjectArrayElement`,
`GetStringLength`, `GetStringCritical`, `ReleaseStringCritical`. `((void **)ctx->jvm)[1]` is `((Jvm *)ctx->jvm)->env`.
- Functions that ended in `_2` / `_3` have descriptive names (`friends_on_tick`, `friends_on_toggle`,
`bridge_assist_on_late_tick`, `piercing_decide_which_entity`, `java_get_world_chunk_provider_client`, ...). The numbers
that remain are meaningful (`hook_*_1_7` / `_1_8` Minecraft versions, `player_esp_render_mode_0/1`, key-binding array
indices, `java_call_world_get_block_state/500` method ids).
- Constants used as arguments of a recognised call are named by role (`font_size_16_in_gui_popups`,
`corner_radius_10_in_gui_text`, `colour_level_0_866667_in_gui_text`, `walk_period_0_5_in_clutch`,
`random_range_bound_0_05_in_clutch`); constants used in arithmetic or comparisons by operator
(`threshold_`, `factor_`, `divisor_`, `offset_`); pi and the 180 / 360 degree constants are `pi_constant`,
`half_turn_degrees`, `full_turn_degrees`. Constants whose use is not one of these keep their value in the name.
- Struct filler bytes are `padding_NN` (offset in hex), not `reserved_NN`: they are never read.
## Minecraft field / method identifiers, string names, constants
The Minecraft JNI identifiers (`JIds` members) are now named after what they are in the game
(`Minecraft_theWorld`, `Minecraft_thePlayer`, `Minecraft_objectMouseOver`, `Entity_boundingBox`, `World_chunkProvider`,
`MovingObjectPosition_hitVec`, `InventoryPlayer_itemStack`, `RenderManager_textRenderer`, ...), and their accessors
follow (`java_get_minecraft_render_item`, `java_get_entity_riding_entity`, ...). Members whose game name I could not
establish from the code are called `<Class>_unidentified_<offset>`; method ids are `unidentified_<offset>`.
**Compatibility:** only C identifiers changed. The class / field / method *name strings* that the client resolves at
run time (the obfuscated or mapped names used for the JVM lookups, including any mapping a particular launcher needs)
live in `src/data/rdata.s` and are untouched; the data section hashes identically before and after. Short obfuscated
name strings are labelled `mc_obf_name_<text>`, JNI signature strings `jni_signature_<text>`, printf formats
`fmt_<spec>` and X.509 OID strings `oid_<digits>`.
Also: `fn_<address>` / `logic_<address>` references in the docs now show the current function names;
`java_get_static_field_c80` is `java_get_c16_enum_state_open_inventory`; remaining one- and two-letter locals of
struct-pointer type are named after the struct (`entry`, `target`, `visitor`, `window`, ...); constants used as plain
literals are named by number word (`one`, `half`, `minus_one`, ...) or by role (`range_low_*`).
## JNI members named from the client's own mapping tables
Every `JIds` member (fields and methods of the Minecraft classes the client resolves through JNI) is now named after the
MCP name that the client's own data tables carry next to the obfuscated name for each mapping (`getBlockState`,
`rayTrace`, `sideHit`, `renderGlobal`, `netManager`, `updateCameraAndRender`, ...), so the names are the ones the data
itself uses and are verified against it. Earlier guesses that the tables contradicted were corrected
(`World_playerEntities` / `World_loadedEntityList` / `World_loadedTileEntityList`, `TileEntity_blockType`,
`NetworkPlayerInfo_displayName`, `GuiContainer_theSlot`, `RenderManager_textRenderer`, `InventoryPlayer_itemStack`).
The string slots that hold method names and signatures for the render hooks are `RendererLivingEntity_*MethodName` /
`*MethodSignature`. No `unidentified_*` names remain. Only identifiers changed; the name strings are unchanged.
## Constants
All remaining `k_f_*` / `k_d_*` constants are named by purpose (`ground_friction_times_air_drag`,
`movement_input_friction`, `gui_wide_layout_max_width`, ...).
+61
View File
@@ -0,0 +1,61 @@
# Standalone build: what is left (measured 2026-10-30)
State of the source: every function of the client is now C++ except the 43 protector-mutated ones, which have
readable versions in `src/engine/protected/` (17 of 20 used ones; see README there). Counts:
| layer | functions | in C++ | byte-exact |
|---|---:|---:|---:|
| module settings (cb/reg/aux) | 146 | 146 | 145 |
| module logic | 200 | 200 | 200 |
| engine (`manifest_engine.json`) | 880 | 837 (+17 readable protected) | 591 |
`tools/extract_engine.py` now also finds functions nothing calls directly (the protector's virtualised init block
starts them through function pointers): the input handler, JVMTI unload, bytecode patcher, GLFW loader, key maps,
JNI bootstrap, allocators, destructors.
## What a standalone (no original image) build still needs
Undefined symbols over all 440 compiled objects (`llvm-nm`):
| kind | count | what it is | plan |
|---|---:|---|---|
| `rdata_*` | 2161 | strings, float/double constants, lookup tables, vtable-like arrays | generate `src/data/rdata_*.cpp` from the image, typed by use (strings as literals, floats, structs where known) |
| `data_*` | 1352 | globals: settings values, module descriptors, state | generate definitions with initial values from the image's `.data` |
| `fn_*` | 97 | NanoVG + its GL3 backend (0x62850+; not ImGui), MSVC CRT (0xab000+), import thunks | link upstream NanoVG (nanovg_gl, GL3), the real CRT; map thunks to real APIs |
| `W_*`/`R_*`/`log_fmt` | 48 | the widget/registration API the module code calls | already written as headers in include/slinky/api.h; needs real bodies (they are `fn_*` in the image) |
| `__imp_*` | - | Win32 imports used by src/engine/protected | link kernel32 (Windows) / replace in a platform layer (Linux) |
Platform layer (needed for Linux): the Windows-only pieces are the window-procedure input hook and click injection
(`protected/input.cpp`), file/path helpers (`protected/files.cpp`), `GetModuleHandle/GetProcAddress`, DLL entry/unload.
Everything else goes through JNI/JVMTI (portable) and OpenGL/NanoVG (portable). On Linux: load as a JVMTI agent
(`-agentpath`), take input from GLFW callbacks (`init_glfw_entrypoints` already resolves them) or X11, use `dlopen/dlsym`.
## Order
1. generate data/rdata definitions (largest mechanical step, enables linking the C++ without the image)
2. real NanoVG + CRT, widget API bodies
3. CMake: Windows DLL first (comparable against the original), then Linux `.so` with the platform layer
4. rewrite the unrecovered virtualised blocks (one-time init in `hook_get_mouse_over`, hook on/off `set_java_hooks`) with our own design
## Result of the data step (tools/gen_data.py, tools/gen_aliases.py, tools/link_probe.sh)
* `src/data/rdata.s` (1.6 MB, 4430 relocated pointers, 6937 strings) and `src/data/data.s` (2595 pointers) are generated
from the image and verified byte-for-byte by `tools/check_data.py` (0 differing bytes, every pointer target correct).
Strings are `.asciz`, pointers are symbol references, 4/8-byte constants carry their float/double value as a comment.
* `src/engine/aliases.cpp` maps engine calls of logic functions (`gui_handle_input_event_entry` -> `gui_handle_input_event`) and the module API names
(`W_bool` -> `widget_bool`, ...).
* `tools/link_probe.sh` links every C++ object with these into a DLL (no CRT, no imports). Before: 3513 data symbols and
~700 functions undefined. Now: 300 undefined symbols, 0 duplicates, and all of them are library code or platform API:
199 MSVC CRT functions, 42 NanoVG functions, 23 import thunks, ~25 Win32 imports, ~9 libc/runtime symbols, and
one piece of client code (`set_java_hooks`, the unrecovered hook on/off).
* So the client's own code is closed: everything it needs besides NanoVG, the C runtime and the OS is in this tree.
## Status update: the DLL builds (CMakeLists.txt)
* `cmake -S . -B build/cmake -DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-windows-clang-cl.cmake && cmake --build build/cmake`
produces `build/cmake/slinky.dll` without the original image and without a Windows SDK.
* What stood open in the table above is now bound: the C runtime and NanoVG by `src/platform/` (CRT shim, NanoVG
names, GL slots), the OS imports by import libraries generated from `cmake/imports/*.def`, the few libc declarations
the bundled C code needs by `third_party/minicrt/`.
* Start-up, hook control, teardown and the first-frame GUI setup are new code (`docs/init.md`).
* Still open: running it in a game (nothing has been run), the Linux platform layer, and the items marked inferred in
`docs/init.md`.
+1340
View File
File diff suppressed because it is too large. Load diff
+47
View File
@@ -0,0 +1,47 @@
// Slinky client - shared declarations for every module.
// Symbols keep the names they had in the original binary (fn_<rva>, data_<rva>,
// rdata_<rva>), so the code can be compared with the decompiled image one
// function at a time.
#pragma once
// ---- widget API (GUI drawing), original RVAs ----
extern "C" void W_bool(void *a, void *ctx, void *cfg); // 0x0cef0
extern "C" void W_slider(void *a, void *ctx, void *cfg); // 0x0cf30
extern "C" void W_range(void *a, void *ctx, void *cfg); // 0x0cfb0
extern "C" void W_enumw(void *a, void *ctx, void *cfg); // 0x0d040
extern "C" void W_color(void *a, void *ctx, void *cfg); // 0x0d0a0
extern "C" void W_items(void *a, void *ctx, void *cfg); // 0x0d0e0
extern "C" void W_label(void *ctx, void *a, const char *s); // 0x13e90
// ---- settings registration API ----
extern "C" void R_bool(void *mgr, const char *name, void *cfg); // 0x0c3b0
extern "C" void R_num(void *mgr, const char *name, void *cfg); // 0x0c450
extern "C" void R_rng(void *mgr, const char *name, void *cfg); // 0x0c550
extern "C" void R_enum(void *mgr, const char *name, void *cfg); // 0x0c6f0
extern "C" void R_key(void *mgr, const char *name, void *cfg); // 0x0c7e0
extern "C" void R_kind2(void *mgr, const char *name, void *cfg); // 0x0ca00
// ---- original .rdata strings (symbol = RVA) ----
#define S(n) extern "C" const char n[]
#define D(n) extern "C" unsigned char n[]
// The original compiler (an older clang-cl) emitted a 1-byte `nop` between a
// tail-call `jmp` and a following SEH epilogue; clang 23 no longer does. Put
// EPILOG_NOP() on the fall-through path that reaches that second epilogue to
// reproduce it (compiler-version shim, not original source).
#define EPILOG_NOP() __asm__ __volatile__("nop")
// Same story for if/else that rejoins at one epilogue: clang 23 tail-duplicates
// the epilogue into both arms, the original kept a single shared one. An empty
// asm-goto is not duplicable, so it pins the join block. Emits no code.
#define SHARED_EPILOG() do { __asm__ __volatile__("" ::: "memory"); \
__asm__ goto("" :::: shared_epilog_); shared_epilog_:; } while (0)
// Same trick for any mid-function join (usable several times per function):
// stops clang 23 from tail-duplicating the following block into each predecessor.
#define JOIN_AT_(n) do { __asm__ goto("" :::: join_##n##_); join_##n##_:; } while (0)
#define JOIN_AT(n) JOIN_AT_(n)
#define JOIN() JOIN_AT(__COUNTER__)
// module logging/format helper (0x0ab710): log_fmt(self, len, fmt, arg)
extern "C" void log_fmt(void *self, int len, const char *fmt, ...);
+174
View File
@@ -0,0 +1,174 @@
// cJSON internals (upstream cJSON 1.7.x, MIT) mapped onto the image's globals.
#pragma once
#include <stddef.h>
typedef int cJSON_bool;
#define false 0
#define true 1
#define cJSON_Invalid (0)
#define cJSON_False (1 << 0)
#define cJSON_True (1 << 1)
#define cJSON_NULL (1 << 2)
#define cJSON_Number (1 << 3)
#define cJSON_String (1 << 4)
#define cJSON_Array (1 << 5)
#define cJSON_Object (1 << 6)
#define cJSON_Raw (1 << 7)
#define cJSON_IsReference 256
#define cJSON_StringIsConst 512
typedef struct cJSON {
struct cJSON *next, *prev, *child;
int type;
char *valuestring;
int valueint;
double valuedouble;
char *string;
} cJSON;
typedef struct internal_hooks {
void *(*allocate)(size_t size);
void (*deallocate)(void *pointer);
void *(*reallocate)(void *pointer, size_t size);
} internal_hooks;
typedef struct { const unsigned char *json; size_t position; } error;
typedef struct {
const unsigned char *content; size_t length; size_t offset; size_t depth; internal_hooks hooks;
} parse_buffer;
typedef struct {
unsigned char *buffer; size_t length; size_t offset; size_t depth; cJSON_bool noalloc; cJSON_bool format;
internal_hooks hooks;
} printbuffer;
extern "C" internal_hooks block_in_speed_setting_field_0x38;
extern "C" error cjson_state;
extern "C" double not_a_number; // NAN
#define global_hooks block_in_speed_setting_field_0x38
#define global_error cjson_state
#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length))
#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index))
#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset)
#define cjson_min(a, b) (((a) < (b)) ? (a) : (b))
extern "C" __attribute__((pure)) size_t libc_strlen(const char *s); // strlen
extern "C" __attribute__((pure)) int libc_strncmp(const char *a, const char *b, size_t n); // strncmp
extern "C" __attribute__((pure)) int libc_strcmp(const char *a, const char *b); // strcmp
extern "C" void *libc_memcpy_alt(void *d, const void *s, size_t n); // memcpy
#define strlen libc_strlen
#define strncmp libc_strncmp
#define strcmp libc_strcmp
#define memcpy libc_memcpy_alt
#define memset __builtin_memset
static inline __attribute__((always_inline)) cJSON_bool cJSON_IsString(const cJSON *const item) {
if (item == NULL) return false;
return (item->type & 0xFF) == cJSON_String;
}
static inline __attribute__((always_inline)) cJSON_bool cJSON_IsNumber(const cJSON *const item) {
if (item == NULL) return false;
return (item->type & 0xFF) == cJSON_Number;
}
static inline __attribute__((always_inline)) cJSON *cJSON_New_Item(const internal_hooks *const hooks) {
cJSON *node = (cJSON *)hooks->allocate(sizeof(cJSON));
if (node) memset(node, '\0', sizeof(cJSON));
return node;
}
static inline __attribute__((always_inline)) void update_offset(printbuffer *const buffer) {
const unsigned char *buffer_pointer = NULL;
if ((buffer == NULL) || (buffer->buffer == NULL)) return;
buffer_pointer = buffer->buffer + buffer->offset;
buffer->offset += strlen((const char *)buffer_pointer);
}
// ---- shared static helpers (inlined into every public function, as in the original) ----
#define INLINE static inline __attribute__((always_inline))
extern "C" double threshold_2_14748e09, threshold_neg_2_14748e09; // INT_MAX, INT_MIN as doubles
#define INT_MAX 2147483647
#define INT_MIN (-INT_MAX - 1)
extern "C" void cjson_delete(cJSON *item); // cJSON_Delete
INLINE unsigned char *cJSON_strdup(const unsigned char *string, const internal_hooks *const hooks) {
size_t length = 0;
unsigned char *copy = NULL;
if (string == NULL) return NULL;
length = strlen((const char *)string) + sizeof("");
copy = (unsigned char *)hooks->allocate(length);
if (copy == NULL) return NULL;
memcpy(copy, string, length);
return copy;
}
INLINE void suffix_object(cJSON *prev, cJSON *item) { prev->next = item; item->prev = prev; }
INLINE cJSON_bool add_item_to_array(cJSON *array, cJSON *item) {
cJSON *child = NULL;
if ((item == NULL) || (array == NULL) || (array == item)) return false;
child = array->child;
if (child == NULL) {
array->child = item;
item->prev = item;
item->next = NULL;
} else {
if (child->prev) {
suffix_object(child->prev, item);
child->prev = item;
}
}
return true;
}
INLINE cJSON_bool add_item_to_object(cJSON *const object, const char *const string, cJSON *const item,
const internal_hooks *const hooks, const cJSON_bool constant_key) {
char *new_key = NULL;
int new_type = cJSON_Invalid;
if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item)) return false;
if (constant_key) {
new_key = (char *)string;
new_type = item->type | cJSON_StringIsConst;
} else {
new_key = (char *)cJSON_strdup((const unsigned char *)string, hooks);
if (new_key == NULL) return false;
new_type = item->type & ~cJSON_StringIsConst;
}
if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) hooks->deallocate(item->string);
item->string = new_key;
item->type = new_type;
return add_item_to_array(object, item);
}
INLINE cJSON *cJSON_CreateBool(cJSON_bool boolean) {
cJSON *item = cJSON_New_Item(&global_hooks);
if (item) item->type = boolean ? cJSON_True : cJSON_False;
return item;
}
INLINE cJSON *cJSON_CreateNumber(double num) {
cJSON *item = cJSON_New_Item(&global_hooks);
if (item) {
item->type = cJSON_Number;
item->valuedouble = num;
if (num >= threshold_2_14748e09) item->valueint = INT_MAX;
else if (num <= threshold_neg_2_14748e09) item->valueint = INT_MIN;
else item->valueint = (int)num;
}
return item;
}
INLINE cJSON *cJSON_CreateString(const char *string) {
cJSON *item = cJSON_New_Item(&global_hooks);
if (item) {
item->type = cJSON_String;
item->valuestring = (char *)cJSON_strdup((const unsigned char *)string, &global_hooks);
if (!item->valuestring) {
cjson_delete(item);
return NULL;
}
}
return item;
}
INLINE cJSON *cJSON_CreateObject(void) {
cJSON *item = cJSON_New_Item(&global_hooks);
if (item) item->type = cJSON_Object;
return item;
}
INLINE cJSON *cJSON_CreateArray(void) {
cJSON *item = cJSON_New_Item(&global_hooks);
if (item) item->type = cJSON_Array;
return item;
}
#define CJSON_NESTING_LIMIT 1000
extern "C" void buffer_skip_whitespace_unused_return(parse_buffer *const buffer); // buffer_skip_whitespace (void-returning copy)
INLINE parse_buffer *buffer_skip_whitespace_i(parse_buffer *const buffer) {
if ((buffer == NULL) || (buffer->content == NULL)) return NULL;
if (cannot_access_at_index(buffer, 0)) return buffer;
while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) buffer->offset++;
if (buffer->offset == buffer->length) buffer->offset--;
return buffer;
}
+23
View File
@@ -0,0 +1,23 @@
// Java class-file constant pool access (the client patches classes in ClassFileLoadHook).
// Error codes: 0x2af8 truncated, 0x2afd bad index, 0x2afe wrong tag.
#pragma once
struct CP { const unsigned char **e; unsigned short n; char pad[6]; const unsigned char *end; };
struct Utf8 { const unsigned char *p; unsigned short len; };
struct NameType { Utf8 name, desc; };
struct MemberRef { Utf8 cls; NameType nt; };
static inline __attribute__((always_inline)) unsigned short be16(const unsigned char *p) {
unsigned short v = *(const unsigned short *)p;
return (unsigned short)((v << 8) | (v >> 8));
}
// entry `idx` (1-based) and the end of its bytes (start of the next entry, or the pool end)
#define CP_ENTRY(cp, idx, ent, lim, fail) \
do { \
if ((idx) == 0) goto fail; \
unsigned short i_ = (unsigned short)((idx) - 1); \
if (i_ >= (cp)->n) goto fail; \
ent = (cp)->e[i_]; \
if (!ent) goto fail; \
const unsigned char **nx_ = &(cp)->e[i_]; \
\
lim = *((idx) == (cp)->n ? &(cp)->end : nx_ + 1); \
} while (0)
+33
View File
@@ -0,0 +1,33 @@
// Engine-side types and helpers shared by the recoded module logic.
// Everything is named after its original RVA until its purpose is understood.
#pragma once
#include "slinky/api.h"
// Per-hook context handed to every module callback (rcx at +0x08 points here).
struct Ctx {
void *jvm; // +0x00 JNI/JVM handle passed as the first argument of every java_* call
void *module_manager; // +0x08 target of queue_module_toggle
void *profile_list; // +0x10
void *minecraft; // +0x18 Minecraft instance
void *world; // +0x20 client world
void *player; // +0x28 local player entity
void *key_state_table; // +0x30
void *target; // +0x38 current combat target record (entity, squared distance, ...)
unsigned char in_game; // +0x40 non-zero while a world is loaded
};
// Current combat target record (Ctx::target)
struct CombatTarget {
void *padding_00;
void *ent; // +0x08 target entity
double dist2; // +0x10 squared distance to it
unsigned char valid; // +0x18
};
// Hook argument block (rcx of every descriptor callback).
struct Hook {
char pad0[8];
Ctx *ctx; // +0x08
};
#define FN(ret, name, args) extern "C" ret name args
File diff suppressed because it is too large. Load diff
+117
View File
@@ -0,0 +1,117 @@
// Layout of the client GUI state block (the `gui` argument of every GUI helper).
// Field offsets match the original image; overlapping uses are expressed as unions.
#pragma once
#include <stddef.h>
struct GuiTab { int kind; int pad; const char *label; }; // one entry of the tab bar (kind 6 = unload)
struct GuiVec2 { float x, y; };
struct GuiColor { float r, g, b, a; };
struct GuiRgb { float r, g, b; };
struct GuiHsl { float h, s, l; };
struct GuiCell { float x, y, w, h; };
// one entry of the layout-row stack (see push_layout_row / add_cell)
struct GuiLayoutRow {
char pad[4]; float top; float width; int padw; float y0; int pad14; unsigned char clip; char pad2[7];
float bottom; int pad3; long long cols; GuiCell cell[4]; unsigned long long items;
};
static_assert(sizeof(GuiLayoutRow) == 0x78, "layout row size");
struct GuiState {
GuiLayoutRow rows[5]; // 0x000 layout-row stack
long long layout_row_depth; // 0x258 number of rows currently pushed
int layout_column_count; // 0x260
char pad_264[4];
unsigned char toast_visible; // 0x268
char pad_269;
char toast_text[0x66]; // 0x26a message shown by the toast at the top of the window
float toast_x; // 0x2d0
float toast_y; // 0x2d4
float toast_fade; // 0x2d8 0 -> 1 while the toast fades in
char pad_2dc[4];
GuiTab tabs[7]; // 0x2e0
unsigned long long selected_tab; // 0x350
float tab_highlight_pos; // 0x358 animated position of the tab highlight
char pad_35c[4];
GuiColor accent_color; // 0x360
union {
int input_mode; // 0x370 0 idle, 1 key-bind capture, 2/3 text field being edited
unsigned input_mode_u;
};
char pad_374[4];
union { // 0x378
char edit_text[0x68]; // text being edited (input_mode 2/3)
struct {
void *keybind_callback; // key-bind capture (input_mode 1)
void *keybind_target; // 0x380
};
};
void *edit_char_filter; // 0x3e0 per-character filter of the text field
void *edit_filter_arg; // 0x3e8
unsigned char edit_confirmed; // 0x3f0
char pad_3f1[7];
union {
long long popup_kind; // 0x3f8 0 none, 1 callback, 2 colour picker, 3 confirm, 4 list picker
int popup_kind_i;
unsigned popup_kind_u;
};
void *popup_context; // 0x400
union {
void *popup_title; // 0x408
const char *popup_title_text;
const char ***popup_title_items;
};
double popup_open_time; // 0x410
float popup_opacity; // 0x418
float popup_height; // 0x41c
unsigned char popup_animating; // 0x420
char pad_421[7];
void *popup_callback; // 0x428
union { // 0x430 payload, depends on popup_kind
char popup_message[0x68]; // confirm: formatted message
struct { // colour picker
GuiRgb popup_rgb; // 0x430
GuiHsl popup_hsl; // 0x43c
};
struct { // list picker
void *popup_list_callback; // 0x430
void *popup_list_items; // 0x438
union { void *popup_list_extra; unsigned long long popup_list_extra_u; }; // 0x440
double popup_list_state; // 0x448
void *popup_list_view; // 0x450
unsigned long long popup_list_count; // 0x458
};
};
double card_anim_start_time; // 0x498
double profile_refresh_time; // 0x4a0
GuiVec2 mouse_scene; // 0x4a8 mouse position in scaled window space
GuiVec2 mouse_raw; // 0x4b0
union {
float card_open_progress; // 0x4b8
int card_open_progress_i;
};
float scroll; // 0x4bc
float scroll_max; // 0x4c0
float row_height; // 0x4c4
unsigned char click_capture; // 0x4c8
unsigned char click_consumed; // 0x4c9
unsigned char numeric_drag_enabled; // 0x4ca
unsigned char unload_requested; // 0x4cb
unsigned char card_layout_mode; // 0x4cc
unsigned char wide_layout; // 0x4cd
};
#define GUI_CHECK(field, off) static_assert(offsetof(GuiState, field) == off, #field)
GUI_CHECK(layout_row_depth, 0x258); GUI_CHECK(toast_visible, 0x268); GUI_CHECK(toast_text, 0x26a);
GUI_CHECK(toast_x, 0x2d0); GUI_CHECK(toast_fade, 0x2d8); GUI_CHECK(tabs, 0x2e0); GUI_CHECK(selected_tab, 0x350);
GUI_CHECK(tab_highlight_pos, 0x358); GUI_CHECK(accent_color, 0x360); GUI_CHECK(input_mode, 0x370);
GUI_CHECK(edit_text, 0x378); GUI_CHECK(keybind_target, 0x380); GUI_CHECK(edit_char_filter, 0x3e0);
GUI_CHECK(edit_filter_arg, 0x3e8); GUI_CHECK(edit_confirmed, 0x3f0); GUI_CHECK(popup_kind, 0x3f8);
GUI_CHECK(popup_context, 0x400); GUI_CHECK(popup_title, 0x408); GUI_CHECK(popup_open_time, 0x410);
GUI_CHECK(popup_opacity, 0x418); GUI_CHECK(popup_height, 0x41c); GUI_CHECK(popup_animating, 0x420);
GUI_CHECK(popup_callback, 0x428); GUI_CHECK(popup_message, 0x430); GUI_CHECK(popup_rgb, 0x430);
GUI_CHECK(popup_hsl, 0x43c); GUI_CHECK(popup_list_extra, 0x440); GUI_CHECK(popup_list_state, 0x448);
GUI_CHECK(popup_list_view, 0x450); GUI_CHECK(popup_list_count, 0x458); GUI_CHECK(card_anim_start_time, 0x498);
GUI_CHECK(profile_refresh_time, 0x4a0); GUI_CHECK(mouse_scene, 0x4a8); GUI_CHECK(mouse_raw, 0x4b0);
GUI_CHECK(card_open_progress, 0x4b8); GUI_CHECK(scroll, 0x4bc); GUI_CHECK(row_height, 0x4c4);
GUI_CHECK(click_capture, 0x4c8); GUI_CHECK(wide_layout, 0x4cd);
+16
View File
@@ -0,0 +1,16 @@
// JNI bridge call in the shape the original compiler emitted: ids and env loaded first, then the
// JNIEnv function pointer, then the call (the barrier keeps the pointer load from folding into the jmp/call).
#pragma once
#include "slinky/jids.h"
#define JCALL(j, Fn, ...) ({ JIds *ids = (j)->ids; JNIEnv *env = (j)->env; auto f_ = (*env)->Fn; \
asm("" : "+r"(f_)); f_(env, ##__VA_ARGS__); })
// Non-tail call: the compiler folds the function-pointer load into the call.
#define JCALLN(j, Fn, ...) ({ JIds *ids = (j)->ids; JNIEnv *env = (j)->env; (*env)->Fn(env, ##__VA_ARGS__); })
// Call whose first two arguments after env are cached IDs (class+method, NewObject/CallStatic*):
// the original loads both IDs before evaluating the remaining arguments.
#define JCALL2(j, Fn, A, B, ...) ({ JIds *ids = (j)->ids; JNIEnv *env = (j)->env; auto vt_ = *env; \
asm("" : "+r"(vt_)); auto a_ = (A); auto b_ = (B); \
asm("" : "+r"(a_), "+r"(b_)); vt_->Fn(env, a_, b_, ##__VA_ARGS__); })
// Call<T>Method(obj, ID, args...) with extra arguments: vtable and ID loaded before the arguments.
#define JCALL1(j, Fn, O, A, ...) ({ JIds *ids = (j)->ids; JNIEnv *env = (j)->env; auto vt_ = *env; \
asm("" : "+r"(vt_)); auto a_ = (A); asm("" : "+r"(a_)); vt_->Fn(env, O, a_, ##__VA_ARGS__); })
+429
View File
@@ -0,0 +1,429 @@
// Cached JNI IDs of the engine (filled once by load_all_jni_ids). Generated by tools/gen_jids.py.
// Member = <Class>_<member>; the comment gives the kind (cls/m/f/sm/sf) and the JVM signature.
#pragma once
#include "slinky/jnienv.h"
struct JIds {
int variant; // 0x0 non-zero: alternate mappings
int names; // 0x4 mapping names: 0 obfuscated, 1 MCP, 2 SRG
void *hasBlockPos;
void *gameClassLoader;
void *gameClassLoaderLoadClass;
jclass List; // 0x20 cls
jmethodID List_size; // 0x28 m ()I
jmethodID List_get; // 0x30 m (I)Ljava/lang/Object;
jmethodID List_add; // 0x38 m (Ljava/lang/Object;)Z
jmethodID List_remove; // 0x40 m (Ljava/lang/Object;)Z
jclass Map; // 0x48 cls
jmethodID Map_containsKey; // 0x50 m (Ljava/lang/Object;)Z
jmethodID Map_get; // 0x58 m (Ljava/lang/Object;)Ljava/lang/Object;
jclass Keyboard; // 0x60 cls
jmethodID Keyboard_isKeyDown; // 0x68 sm (I)Z
jmethodID Keyboard_next; // 0x70 sm ()Z
jmethodID Keyboard_getEventKey; // 0x78 sm ()I
jmethodID Keyboard_getEventKeyState; // 0x80 sm ()Z
jclass Mouse; // 0x88 cls
jmethodID Mouse_isGrabbed; // 0x90 sm ()Z
jmethodID Mouse_setGrabbed; // 0x98 sm (Z)V
jmethodID Mouse_isButtonDown; // 0xa0 sm (I)Z
jmethodID Mouse_next; // 0xa8 sm ()Z
jmethodID Mouse_setCursorPosition; // 0xb0 sm (II)V
jclass Display; // 0xb8 cls
jmethodID Display_update; // 0xc0 sm ()V
jmethodID Display_isActive; // 0xc8 sm ()Z
jfieldID Display_width; // 0xd0 sf I
jfieldID Display_height; // 0xd8 sf I
jfieldID Display_framebufferWidth; // 0xe0 sf I
jfieldID Display_framebufferHeight; // 0xe8 sf I
jclass Object; // 0xf0 cls
jmethodID Object_hashCode; // 0xf8 m ()I
jclass SecureClassLoader; // 0x100 cls
jmethodID SecureClassLoader__init_; // 0x108 m (Ljava/lang/ClassLoader;)V
jclass Channel; // 0x110 cls
jmethodID Channel_pipeline; // 0x118 m ()Lio/netty/channel/ChannelPipeline;
jclass ChannelPipeline; // 0x120 cls
jmethodID ChannelPipeline_addBefore; // 0x128 m
jmethodID ChannelPipeline_get; // 0x130 m (Ljava/lang/Class;)Lio/netty/channel/ChannelHandler;
jmethodID ChannelPipeline_remove; // 0x138 m (Ljava/lang/Class;)Lio/netty/channel/ChannelHandler;
jclass Enum; // 0x140 cls
jmethodID Enum_ordinal; // 0x148 m ()I
jclass Minecraft; // 0x150 cls
jmethodID Minecraft_getNetHandler; // 0x158 m
void *unused_slot_160;
jmethodID Minecraft_addScheduledTask; // 0x168 m (Ljava/lang/Runnable;)Lcom/google/common/util/concurrent/ListenableFuture;
jmethodID Minecraft_clickMouse; // 0x170 m ()V
jmethodID Minecraft_rightClickMouse; // 0x178 m ()V
jfieldID Minecraft_theMinecraft; // 0x180 sf
void *Minecraft_thePlayer;
jfieldID Minecraft_theWorld; // 0x190 f
jfieldID Minecraft_timer; // 0x198 f
jfieldID Minecraft_gameSettings; // 0x1a0 f
jfieldID Minecraft_inGameHasFocus; // 0x1a8 f Z
jfieldID Minecraft_objectMouseOver; // 0x1b0 f
jfieldID Minecraft_leftClickCounter; // 0x1b8 f I
jfieldID Minecraft_rightClickDelayTimer; // 0x1c0 f I
jfieldID Minecraft_currentScreen; // 0x1c8 f
jfieldID Minecraft_ingameGUI; // 0x1d0 f
jfieldID Minecraft_displayWidth; // 0x1d8 f I
jfieldID Minecraft_displayHeight; // 0x1e0 f I
jfieldID Minecraft_renderEngine; // 0x1e8 f
jfieldID Minecraft_renderManager; // 0x1f0 f
jfieldID Minecraft_renderViewEntity; // 0x1f8 f
jfieldID Minecraft_renderGlobal; // 0x200 f
jfieldID Minecraft_renderItem; // 0x208 f
jfieldID Minecraft_entityRenderer; // 0x210 f
jfieldID Minecraft_playerController; // 0x218 f
jclass Entity; // 0x220 cls
jmethodID Entity_isSprinting; // 0x228 m ()Z
jmethodID Entity_setSprinting; // 0x230 m (Z)V
jmethodID Entity_isSneaking; // 0x238 m ()Z
jmethodID Entity_getFormattedCommandSenderName; // 0x240 m
jmethodID Entity_getCommandSenderName; // 0x248 m ()Ljava/lang/String;
jmethodID Entity_setAngles; // 0x250 m (FF)V
jmethodID Entity_moveFlying; // 0x258 m (FFF)V
jmethodID Entity_canBeCollidedWith; // 0x260 m ()Z
jfieldID Entity_posX; // 0x268 f D
jfieldID Entity_posY; // 0x270 f D
jfieldID Entity_posZ; // 0x278 f D
jfieldID Entity_prevPosX; // 0x280 f D
jfieldID Entity_prevPosY; // 0x288 f D
jfieldID Entity_prevPosZ; // 0x290 f D
jfieldID Entity_rotationYaw; // 0x298 f F
jfieldID Entity_rotationPitch; // 0x2a0 f F
jfieldID Entity_prevRotationYaw; // 0x2a8 f F
jfieldID Entity_prevRotationPitch; // 0x2b0 f F
jfieldID Entity_motionX; // 0x2b8 f D
jfieldID Entity_motionY; // 0x2c0 f D
jfieldID Entity_motionZ; // 0x2c8 f D
jfieldID Entity_boundingBox; // 0x2d0 f
jfieldID Entity_ticksExisted; // 0x2d8 f I
jfieldID Entity_entityId; // 0x2e0 f I
jfieldID Entity_uniqueId; // 0x2e8 f Ljava/util/UUID;
jfieldID Entity_onGround; // 0x2f0 f Z
jfieldID Entity_isDead; // 0x2f8 f Z
jfieldID Entity_fallDistance; // 0x300 f F
jfieldID Entity_ridingEntity; // 0x308 f
jclass World; // 0x310 cls
jmethodID World_getEntityByID; // 0x318 m
void *World_getBlock;
void *World_getBlockState;
jmethodID World_rayTraceBlocks; // 0x330 m
jmethodID World_updateEntities; // 0x338 m ()V
jmethodID World_getChunkFromChunkCoords; // 0x340 m
jmethodID World_markBlockRangeForRenderUpdate; // 0x348 m (IIIIII)V
jmethodID World_setBlock; // 0x350 m
jmethodID World_setBlockState; // 0x358 m
jfieldID World_playerEntities; // 0x360 f Ljava/util/List;
jfieldID World_loadedEntityList; // 0x368 f Ljava/util/List;
jfieldID World_loadedTileEntityList; // 0x370 f Ljava/util/List;
jfieldID World_chunkProvider; // 0x378 f
jclass Timer; // 0x380 cls
jfieldID Timer_renderPartialTicks; // 0x388 f F
jfieldID Timer_timerSpeed; // 0x390 f F
jfieldID Timer_elapsedPartialTicks; // 0x398 f F
jfieldID Timer_elapsedTicks; // 0x3a0 f I
jclass GameSettings; // 0x3a8 cls
jfieldID GameSettings_mouseSensitivity; // 0x3b0 f F
jfieldID GameSettings_keyBindings; // 0x3b8 f
jfieldID GameSettings_thirdPersonView; // 0x3c0 f I
jclass MovingObjectPosition; // 0x3c8 cls
jmethodID MovingObjectPosition__init_; // 0x3d0 m (Lnet/minecraft/entity/Entity;Lnet/minecraft/util/Vec3;)V
jfieldID MovingObjectPosition_typeOfHit; // 0x3d8 f
jfieldID MovingObjectPosition_hitVec; // 0x3e0 f
void *MovingObjectPosition_sideHitIndex;
void *MovingObjectPosition_sideHit;
jfieldID MovingObjectPosition_blockX; // 0x3f8 f
jfieldID MovingObjectPosition_blockY; // 0x400 f
jfieldID MovingObjectPosition_blockZ; // 0x408 f
jfieldID MovingObjectPosition_blockPos; // 0x410 f
jfieldID MovingObjectPosition_entityHit; // 0x418 f
jclass _init_; // 0x420 cls
jmethodID _init__getBlock; // 0x428 m
jclass Block; // 0x430 cls
jmethodID Block_getIdFromBlock; // 0x438 sm
jmethodID Block_getBlockFromItem; // 0x440 sm
jclass BlockPos; // 0x448 cls
jmethodID BlockPos__init_; // 0x450 m (III)V
jfieldID BlockPos_a; // 0x458 f
jfieldID BlockPos_c; // 0x460 f
jfieldID BlockPos_d; // 0x468 f
jclass KeyBinding; // 0x470 cls
jfieldID KeyBinding_pressed; // 0x478 f Z
jfieldID KeyBinding_pressTime; // 0x480 f I
jfieldID KeyBinding_keyCode; // 0x488 f I
jclass AxisAlignedBB; // 0x490 cls
jmethodID AxisAlignedBB__init_; // 0x498 m (DDDDDD)V
jmethodID AxisAlignedBB_calculateIntercept; // 0x4a0 m
jfieldID AxisAlignedBB_minX; // 0x4a8 f D
jfieldID AxisAlignedBB_minY; // 0x4b0 f D
jfieldID AxisAlignedBB_minZ; // 0x4b8 f D
jfieldID AxisAlignedBB_maxX; // 0x4c0 f D
jfieldID AxisAlignedBB_maxY; // 0x4c8 f D
jfieldID AxisAlignedBB_maxZ; // 0x4d0 f D
jclass GuiContainer; // 0x4d8 cls
jmethodID GuiContainer_handleMouseClick; // 0x4e0 m
jfieldID GuiContainer_theSlot; // 0x4e8 f
jfieldID GuiContainer_lastClickTime; // 0x4f0 f J
jclass EntityLivingBase; // 0x4f8 cls
void *EntityLivingBase_rayTrace;
jmethodID EntityLivingBase_getPosition; // 0x508 m
jmethodID EntityLivingBase_getHealth; // 0x510 m ()F
jmethodID EntityLivingBase_getTeam; // 0x518 m
jmethodID EntityLivingBase_swingItem; // 0x520 m ()V
jmethodID EntityLivingBase_getMaxHealth; // 0x528 m ()F
jmethodID EntityLivingBase_getActivePotionEffect; // 0x530 m
jmethodID EntityLivingBase_jump; // 0x538 m ()V
jfieldID EntityLivingBase_moveForward; // 0x540 f F
jfieldID EntityLivingBase_moveStrafing; // 0x548 f F
jfieldID EntityLivingBase_maxHurtTime; // 0x550 f I
jfieldID EntityLivingBase_hurtTime; // 0x558 f I
jfieldID EntityLivingBase_lastAttackerTime; // 0x560 f I
jfieldID EntityLivingBase_landMovementFactor; // 0x568 f F
jfieldID EntityLivingBase_jumpTicks; // 0x570 f I
jclass EntityPlayer; // 0x578 cls
jmethodID EntityPlayer_isInvisibleToPlayer; // 0x580 m
jmethodID EntityPlayer_isUsingItem; // 0x588 m ()Z
jmethodID EntityPlayer_getAbsorptionAmount; // 0x590 m ()F
jmethodID EntityPlayer_getEquipmentInSlot; // 0x598 m
jfieldID EntityPlayer_inventory; // 0x5a0 f
jfieldID EntityPlayer_itemInUseCount; // 0x5a8 f I
jfieldID EntityPlayer_inventoryContainer; // 0x5b0 f
jfieldID EntityPlayer_sleeping; // 0x5b8 f Z
jfieldID EntityPlayer_speedOnGround; // 0x5c0 f F
jclass InventoryPlayer; // 0x5c8 cls
jfieldID InventoryPlayer_mainInventory; // 0x5d0 f
jfieldID InventoryPlayer_currentItem; // 0x5d8 f I
jfieldID InventoryPlayer_itemStack; // 0x5e0 f
jclass ItemStack; // 0x5e8 cls
jmethodID ItemStack__init_; // 0x5f0 m
jmethodID ItemStack_getItem; // 0x5f8 m
jmethodID ItemStack_getStrVsBlock; // 0x600 m
jmethodID ItemStack_getEnchantmentTagList; // 0x608 m
jmethodID ItemStack_getMaxStackSize; // 0x610 m ()I
jmethodID ItemStack_areItemStackTagsEqual; // 0x618 sm
jmethodID ItemStack_hasDisplayName; // 0x620 m ()Z
jfieldID ItemStack_stackSize; // 0x628 f I
jfieldID ItemStack_metadata; // 0x630 f I
jclass Item; // 0x638 cls
jmethodID Item_getIdFromItem; // 0x640 sm
jmethodID Item_getItemById; // 0x648 sm
jclass Vec3; // 0x650 cls
jmethodID Vec3__init_; // 0x658 m (DDD)V
jfieldID Vec3_xCoord; // 0x660 f D
jfieldID Vec3_yCoord; // 0x668 f D
jfieldID Vec3_zCoord; // 0x670 f D
jclass ScorePlayerTeam; // 0x678 cls
jfieldID ScorePlayerTeam_Ljava_lang_String_; // 0x680 f Ljava/lang/String;
jclass NetHandlerPlayClient; // 0x688 cls
jfieldID NetHandlerPlayClient_playerInfoMap; // 0x690 f Ljava/util/Map;
jfieldID NetHandlerPlayClient_netManager; // 0x698 f
jclass IChatComponent; // 0x6a0 cls
jmethodID IChatComponent_getFormattedText; // 0x6a8 m ()Ljava/lang/String;
jclass IChatComponent_Serializer; // 0x6b0 cls
jmethodID IChatComponent_Serializer_jsonToComponent; // 0x6b8 sm
jclass Slot; // 0x6c0 cls
jfieldID Slot_slotNumber; // 0x6c8 f I
jmethodID Slot_getStack; // 0x6d0 m
jclass IInventory; // 0x6d8 cls
jmethodID IInventory_getStackInSlot; // 0x6e0 m
jclass NetworkManager; // 0x6e8 cls
jfieldID NetworkManager_Lio_netty_channel_Channel_; // 0x6f0 f Lio/netty/channel/Channel;
void *NetworkManager_scheduleOutboundPacket;
void *NetworkManager_sendPacket;
jclass C03PacketPlayer; // 0x708 cls
jfieldID C03PacketPlayer_moving; // 0x710 f Z
jfieldID C03PacketPlayer_onGround; // 0x718 f Z
jfieldID C03PacketPlayer_a; // 0x720 f D
jfieldID C03PacketPlayer_b; // 0x728 f D
jfieldID C03PacketPlayer_c; // 0x730 f D
jclass C07PacketPlayerDigging; // 0x738 cls
jfieldID C07PacketPlayerDigging_status; // 0x740 f
jclass C08PacketPlayerBlockPlacement; // 0x748 cls
jfieldID C08PacketPlayerBlockPlacement_stack; // 0x750 f
jclass TileEntity; // 0x758 cls
jfieldID TileEntity_blockType; // 0x760 f
void *TileEntity_pos;
void *TileEntity_xCoord;
jfieldID TileEntity_yCoord; // 0x778 f
jfieldID TileEntity_zCoord; // 0x780 f
jclass S12PacketEntityVelocity; // 0x788 cls
jfieldID S12PacketEntityVelocity_entityID; // 0x790 f I
jfieldID S12PacketEntityVelocity_motionX; // 0x798 f I
jfieldID S12PacketEntityVelocity_motionY; // 0x7a0 f I
jfieldID S12PacketEntityVelocity_motionZ; // 0x7a8 f I
jclass RenderManager; // 0x7b0 cls
jfieldID RenderManager_instance; // 0x7b8 sf
jfieldID RenderManager_playerViewX; // 0x7c0 f F
jfieldID RenderManager_playerViewY; // 0x7c8 f F
jfieldID RenderManager_textRenderer; // 0x7d0 f
jclass S14PacketEntity; // 0x7d8 cls
jfieldID S14PacketEntity_entityId; // 0x7e0 f I
jfieldID S14PacketEntity_posX; // 0x7e8 f B
jfieldID S14PacketEntity_posY; // 0x7f0 f B
jfieldID S14PacketEntity_posZ; // 0x7f8 f B
jfieldID S14PacketEntity_yaw; // 0x800 f B
jfieldID S14PacketEntity_pitch; // 0x808 f B
jfieldID S14PacketEntity_g; // 0x810 f Z
jclass S18PacketEntityTeleport; // 0x818 cls
jfieldID S18PacketEntityTeleport_entityId; // 0x820 f I
jfieldID S18PacketEntityTeleport_posX; // 0x828 f I
jfieldID S18PacketEntityTeleport_posY; // 0x830 f I
jfieldID S18PacketEntityTeleport_posZ; // 0x838 f I
jfieldID S18PacketEntityTeleport_yaw; // 0x840 f B
jfieldID S18PacketEntityTeleport_pitch; // 0x848 f B
jclass S19PacketEntityStatus; // 0x850 cls
jfieldID S19PacketEntityStatus_entityId; // 0x858 f I
jclass S29PacketSoundEffect; // 0x860 cls
jclass S06PacketUpdateHealth; // 0x868 cls
jclass EntityOtherPlayerMP; // 0x870 cls
jfieldID EntityOtherPlayerMP_otherPlayerMPPosRotationIncrements; // 0x878 f I
jfieldID EntityOtherPlayerMP_otherPlayerMPX; // 0x880 f D
jfieldID EntityOtherPlayerMP_otherPlayerMPY; // 0x888 f D
jfieldID EntityOtherPlayerMP_otherPlayerMPZ; // 0x890 f D
jfieldID EntityOtherPlayerMP_otherPlayerMPYaw; // 0x898 f D
jclass GuiInventory; // 0x8a0 cls
jclass EntityPlayerSP; // 0x8a8 cls
jfieldID EntityPlayerSP_renderArmYaw; // 0x8b0 f F
jfieldID EntityPlayerSP_renderArmPitch; // 0x8b8 f F
jfieldID EntityPlayerSP_prevRenderArmYaw; // 0x8c0 f F
jfieldID EntityPlayerSP_prevRenderArmPitch; // 0x8c8 f F
jfieldID EntityPlayerSP_timeInPortal; // 0x8d0 f F
jfieldID EntityPlayerSP_prevTimeInPortal; // 0x8d8 f F
jmethodID EntityPlayerSP_addChatMessage; // 0x8e0 m
jclass EntityRenderer; // 0x8e8 cls
jmethodID EntityRenderer_getMouseOver; // 0x8f0 m (F)V
jmethodID EntityRenderer_renderHand; // 0x8f8 m (FI)V
jmethodID EntityRenderer_orientCamera; // 0x900 m (F)V
jmethodID EntityRenderer_getFOVModifier; // 0x908 m (FZ)F
void *EntityRenderer_updateCameraAndRender;
jclass Framebuffer; // 0x918 cls
jmethodID Framebuffer_bindFramebuffer; // 0x920 m (Z)V
jclass RenderGlobal; // 0x928 cls
jfieldID RenderGlobal_displayListEntitiesDirty; // 0x930 f Z
jclass ActiveRenderInfo; // 0x938 cls
jmethodID ActiveRenderInfo_updateRenderInfo; // 0x940 sm
jclass RendererLivingEntity; // 0x948 cls
void *RendererLivingEntity_renderNameMethodName;
void *RendererLivingEntity_renderNameMethodSignature;
jmethodID RendererLivingEntity_func_177067_a; // 0x960 m (Lpr;DDD)V
void *RendererLivingEntity_canRenderNameMethodName;
void *RendererLivingEntity_canRenderNameMethodSignature;
jmethodID RendererLivingEntity_canRenderName; // 0x978 m
void *RendererLivingEntity_doRenderMethodName;
void *RendererLivingEntity_doRenderMethodSignature;
jmethodID RendererLivingEntity___2; // 0x990 m
jclass FontRenderer; // 0x998 cls
jmethodID FontRenderer_getStringWidth; // 0x9a0 m (Ljava/lang/String;)I
void *FontRenderer_drawString;
jclass GlStateManager; // 0x9b0 cls
jmethodID GlStateManager_enableDepth; // 0x9b8 sm
jmethodID GlStateManager_disableDepth; // 0x9c0 sm
jmethodID GlStateManager_enableLighting; // 0x9c8 sm
jmethodID GlStateManager_disableLighting; // 0x9d0 sm
jmethodID GlStateManager_enableTexture2D; // 0x9d8 sm
jmethodID GlStateManager_disableTexture2D; // 0x9e0 sm
jmethodID GlStateManager_enableBlend; // 0x9e8 sm
jmethodID GlStateManager_disableBlend; // 0x9f0 sm
jmethodID GlStateManager_enableCull; // 0x9f8 sm
jmethodID GlStateManager_disableCull; // 0xa00 sm
jmethodID GlStateManager_enableAlpha; // 0xa08 sm
jmethodID GlStateManager_disableAlpha; // 0xa10 sm
jmethodID GlStateManager_blendFunc; // 0xa18 sm
jmethodID GlStateManager_color; // 0xa20 sm
jclass C02PacketUseEntity; // 0xa28 cls
jclass C0BPacketEntityAction; // 0xa30 cls
jfieldID C0BPacketEntityAction_Lis_a_; // 0xa38 f
jclass S08PacketPlayerPosLook; // 0xa40 cls
jclass S27PacketExplosion; // 0xa48 cls
jfieldID S27PacketExplosion_f; // 0xa50 f F
jfieldID S27PacketExplosion_g; // 0xa58 f F
jfieldID S27PacketExplosion_h; // 0xa60 f F
jclass S00PacketKeepAlive; // 0xa68 cls
jclass C00PacketKeepAlive; // 0xa70 cls
jclass Potion; // 0xa78 cls
jfieldID Potion_potionTypes; // 0xa80 sf
jclass RenderPlayer; // 0xa88 cls
void *RenderArrow_doRenderMethodName;
void *RenderArrow_doRenderMethodSignature;
jmethodID RenderPlayer__L_s_DDDFF_V; // 0xaa0 m
jclass RenderItem; // 0xaa8 cls
jfieldID RenderItem_zLevel; // 0xab0 f F
void *RenderItem_init;
jmethodID RenderItem__init_; // 0xac0 m
jmethodID RenderItem_renderItemOverlayIntoGUI; // 0xac8 m
jclass RenderHelper; // 0xad0 cls
jmethodID RenderHelper_enableStandardItemLighting; // 0xad8 sm ()V
jmethodID RenderHelper_disableStandardItemLighting; // 0xae0 sm ()V
jclass EntityList; // 0xae8 cls
jmethodID EntityList_getEntityID; // 0xaf0 sm
jclass Chunk; // 0xaf8 cls
jfieldID Chunk_storageArrays; // 0xb00 f
jfieldID Chunk_xPosition; // 0xb08 f I
jfieldID Chunk_zPosition; // 0xb10 f I
jclass ExtendedBlockStorage; // 0xb18 cls
void *ExtendedBlockStorage_blockLSBArray;
jclass WorldClient; // 0xb28 cls
jclass ChunkProviderClient; // 0xb30 cls
jfieldID ChunkProviderClient_chunkMapping; // 0xb38 f Ljava/util/List;
jmethodID ChunkProviderClient_unloadChunk; // 0xb40 m (II)V
jclass EntityItem; // 0xb48 cls
jmethodID EntityItem_getEntityItem; // 0xb50 m
jmethodID EntityItem_searchForOtherItemsNearby; // 0xb58 m ()V
jclass S3FPacketCustomPayload; // 0xb60 cls
jfieldID S3FPacketCustomPayload_Ljava_lang_String_; // 0xb68 f Ljava/lang/String;
jfieldID S3FPacketCustomPayload_data; // 0xb70 f
jclass PacketBuffer; // 0xb78 cls
jmethodID PacketBuffer_readableBytes; // 0xb80 m ()I
jmethodID PacketBuffer_readerIndex; // 0xb88 m ()I
jmethodID PacketBuffer_getBytes; // 0xb90 m (I[B)Lio/netty/buffer/ByteBuf;
jclass UUID; // 0xb98 cls
jmethodID UUID_getLeastSignificantBits; // 0xba0 m ()J
jmethodID UUID_getMostSignificantBits; // 0xba8 m ()J
jclass ChatComponentText; // 0xbb0 cls
jmethodID ChatComponentText__init_; // 0xbb8 m (Ljava/lang/String;)V
jclass PlayerControllerMP; // 0xbc0 cls
jfieldID PlayerControllerMP_curBlockDamageMP; // 0xbc8 f F
jfieldID PlayerControllerMP_blockHitDelay; // 0xbd0 f I
jfieldID PlayerControllerMP_isHittingBlock; // 0xbd8 f Z
jmethodID PlayerControllerMP_updateController; // 0xbe0 m ()V
jclass C0EPacketClickWindow; // 0xbe8 cls
jclass NBTTagList; // 0xbf0 cls
jmethodID NBTTagList_tagCount; // 0xbf8 m ()I
jmethodID NBTTagList_getCompoundTagAt; // 0xc00 m
jclass NBTTagCompound; // 0xc08 cls
jmethodID NBTTagCompound_getShort; // 0xc10 m (Ljava/lang/String;)S
jclass EntityArrow; // 0xc18 cls
void *unused_slot_c20;
jfieldID EntityArrow_inGround; // 0xc28 f Z
jclass PotionEffect; // 0xc30 cls
jfieldID PotionEffect_duration; // 0xc38 f I
jclass NetworkPlayerInfo; // 0xc40 cls
jfieldID NetworkPlayerInfo_displayName; // 0xc48 f
jmethodID NetworkPlayerInfo_getPlayerTeam; // 0xc50 m
jclass GenericFutureListener; // 0xc58 cls
jclass C16PacketClientStatus; // 0xc60 cls
jfieldID C16PacketClientStatus_status; // 0xc68 f
void *C16PacketClientStatus_init;
jclass C16PacketClientStatus_EnumState; // 0xc78 cls
void *C16PacketClientStatus_EnumState_openInventory;
jclass c; // 0xc88 cls
jmethodID c__init_; // 0xc90 m (I)V
jclass ContainerPlayer; // 0xc98 cls
jfieldID ContainerPlayer_craftMatrix; // 0xca0 f
jclass GuiScreen; // 0xca8 cls
jmethodID GuiScreen_keyTyped; // 0xcb0 m (CI)V
jclass GuiChest; // 0xcb8 cls
jfieldID GuiChest_lowerChestInventory; // 0xcc0 f
jclass InventoryBasic; // 0xcc8 cls
jfieldID InventoryBasic_Ljava_lang_String_; // 0xcd0 f Ljava/lang/String;
jclass StatCollector; // 0xcd8 cls
jmethodID StatCollector_translateToLocal; // 0xce0 sm (Ljava/lang/String;)Ljava/lang/String;
jclass C01PacketChatMessage; // 0xce8 cls
jfieldID C01PacketChatMessage_Ljava_lang_String_; // 0xcf0 f Ljava/lang/String;
jclass C14PacketTabComplete; // 0xcf8 cls
jfieldID C14PacketTabComplete_Ljava_lang_String_; // 0xd00 f Ljava/lang/String;
jclass S2APacketParticles; // 0xd08 cls
};
// The handle every engine call receives: ID table + JNIEnv.
struct Jvm { JIds *ids; JNIEnv *env; };
#define JNI(j) (*(j)->env)
+252
View File
@@ -0,0 +1,252 @@
// JNIEnv function table in JNI ABI order (index = offset / 8), generated from a JDK jni.h.
// Object handles are plain pointers; only the layout matters to the recoded engine.
#pragma once
typedef void *jobject, *jclass, *jstring, *jarray, *jobjectArray, *jthrowable, *jweak, *jmethodID, *jfieldID;
typedef void *jbooleanArray, *jbyteArray, *jcharArray, *jshortArray, *jintArray, *jlongArray, *jfloatArray, *jdoubleArray;
typedef unsigned char jboolean; typedef signed char jbyte; typedef unsigned short jchar; typedef short jshort;
typedef int jint; typedef long long jlong; typedef float jfloat; typedef double jdouble; typedef jint jsize;
typedef union jvalue { jboolean z; jbyte b; jchar c; jshort s; jint i; jlong j; jfloat f; jdouble d; jobject l; } jvalue;
typedef struct { char *name; char *signature; void *fnPtr; } JNINativeMethod;
typedef int jobjectRefType;
struct JNIEnv_;
typedef const struct JNINativeInterface_ *JNIEnv;
struct JavaVM_; typedef struct JavaVM_ JavaVM;
#include <stdarg.h>
struct JNINativeInterface_ {
void *reserved0; // 0x0
void *reserved1; // 0x8
void *reserved2; // 0x10
void *reserved3; // 0x18
jint (*GetVersion)(JNIEnv *env); // 0x20
jclass (*DefineClass)(JNIEnv *env, const char *name, jobject loader, const jbyte *buf, jsize len); // 0x28
jclass (*FindClass)(JNIEnv *env, const char *name); // 0x30
jmethodID (*FromReflectedMethod)(JNIEnv *env, jobject method); // 0x38
jfieldID (*FromReflectedField)(JNIEnv *env, jobject field); // 0x40
jobject (*ToReflectedMethod)(JNIEnv *env, jclass cls, jmethodID methodID, jboolean isStatic); // 0x48
jclass (*GetSuperclass)(JNIEnv *env, jclass sub); // 0x50
jboolean (*IsAssignableFrom)(JNIEnv *env, jclass sub, jclass sup); // 0x58
jobject (*ToReflectedField)(JNIEnv *env, jclass cls, jfieldID fieldID, jboolean isStatic); // 0x60
jint (*Throw)(JNIEnv *env, jthrowable obj); // 0x68
jint (*ThrowNew)(JNIEnv *env, jclass clazz, const char *msg); // 0x70
jthrowable (*ExceptionOccurred)(JNIEnv *env); // 0x78
void (*ExceptionDescribe)(JNIEnv *env); // 0x80
void (*ExceptionClear)(JNIEnv *env); // 0x88
void (*FatalError)(JNIEnv *env, const char *msg); // 0x90
jint (*PushLocalFrame)(JNIEnv *env, jint capacity); // 0x98
jobject (*PopLocalFrame)(JNIEnv *env, jobject result); // 0xa0
jobject (*NewGlobalRef)(JNIEnv *env, jobject lobj); // 0xa8
void (*DeleteGlobalRef)(JNIEnv *env, jobject gref); // 0xb0
void (*DeleteLocalRef)(JNIEnv *env, jobject obj); // 0xb8
jboolean (*IsSameObject)(JNIEnv *env, jobject obj1, jobject obj2); // 0xc0
jobject (*NewLocalRef)(JNIEnv *env, jobject ref); // 0xc8
jint (*EnsureLocalCapacity)(JNIEnv *env, jint capacity); // 0xd0
jobject (*AllocObject)(JNIEnv *env, jclass clazz); // 0xd8
jobject (*NewObject)(JNIEnv *env, jclass clazz, jmethodID methodID, ...); // 0xe0
jobject (*NewObjectV)(JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); // 0xe8
jobject (*NewObjectA)(JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); // 0xf0
jclass (*GetObjectClass)(JNIEnv *env, jobject obj); // 0xf8
jboolean (*IsInstanceOf)(JNIEnv *env, jobject obj, jclass clazz); // 0x100
jmethodID (*GetMethodID)(JNIEnv *env, jclass clazz, const char *name, const char *sig); // 0x108
jobject (*CallObjectMethod)(JNIEnv *env, jobject obj, jmethodID methodID, ...); // 0x110
jobject (*CallObjectMethodV)(JNIEnv *env, jobject obj, jmethodID methodID, va_list args); // 0x118
jobject (*CallObjectMethodA)(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args); // 0x120
jboolean (*CallBooleanMethod)(JNIEnv *env, jobject obj, jmethodID methodID, ...); // 0x128
jboolean (*CallBooleanMethodV)(JNIEnv *env, jobject obj, jmethodID methodID, va_list args); // 0x130
jboolean (*CallBooleanMethodA)(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args); // 0x138
jbyte (*CallByteMethod)(JNIEnv *env, jobject obj, jmethodID methodID, ...); // 0x140
jbyte (*CallByteMethodV)(JNIEnv *env, jobject obj, jmethodID methodID, va_list args); // 0x148
jbyte (*CallByteMethodA)(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); // 0x150
jchar (*CallCharMethod)(JNIEnv *env, jobject obj, jmethodID methodID, ...); // 0x158
jchar (*CallCharMethodV)(JNIEnv *env, jobject obj, jmethodID methodID, va_list args); // 0x160
jchar (*CallCharMethodA)(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); // 0x168
jshort (*CallShortMethod)(JNIEnv *env, jobject obj, jmethodID methodID, ...); // 0x170
jshort (*CallShortMethodV)(JNIEnv *env, jobject obj, jmethodID methodID, va_list args); // 0x178
jshort (*CallShortMethodA)(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); // 0x180
jint (*CallIntMethod)(JNIEnv *env, jobject obj, jmethodID methodID, ...); // 0x188
jint (*CallIntMethodV)(JNIEnv *env, jobject obj, jmethodID methodID, va_list args); // 0x190
jint (*CallIntMethodA)(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); // 0x198
jlong (*CallLongMethod)(JNIEnv *env, jobject obj, jmethodID methodID, ...); // 0x1a0
jlong (*CallLongMethodV)(JNIEnv *env, jobject obj, jmethodID methodID, va_list args); // 0x1a8
jlong (*CallLongMethodA)(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); // 0x1b0
jfloat (*CallFloatMethod)(JNIEnv *env, jobject obj, jmethodID methodID, ...); // 0x1b8
jfloat (*CallFloatMethodV)(JNIEnv *env, jobject obj, jmethodID methodID, va_list args); // 0x1c0
jfloat (*CallFloatMethodA)(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); // 0x1c8
jdouble (*CallDoubleMethod)(JNIEnv *env, jobject obj, jmethodID methodID, ...); // 0x1d0
jdouble (*CallDoubleMethodV)(JNIEnv *env, jobject obj, jmethodID methodID, va_list args); // 0x1d8
jdouble (*CallDoubleMethodA)(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); // 0x1e0
void (*CallVoidMethod)(JNIEnv *env, jobject obj, jmethodID methodID, ...); // 0x1e8
void (*CallVoidMethodV)(JNIEnv *env, jobject obj, jmethodID methodID, va_list args); // 0x1f0
void (*CallVoidMethodA)(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args); // 0x1f8
jobject (*CallNonvirtualObjectMethod)(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); // 0x200
jobject (*CallNonvirtualObjectMethodV)(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, va_list args); // 0x208
jobject (*CallNonvirtualObjectMethodA)(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, const jvalue * args); // 0x210
jboolean (*CallNonvirtualBooleanMethod)(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); // 0x218
jboolean (*CallNonvirtualBooleanMethodV)(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, va_list args); // 0x220
jboolean (*CallNonvirtualBooleanMethodA)(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, const jvalue * args); // 0x228
jbyte (*CallNonvirtualByteMethod)(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); // 0x230
jbyte (*CallNonvirtualByteMethodV)(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, va_list args); // 0x238
jbyte (*CallNonvirtualByteMethodA)(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, const jvalue *args); // 0x240
jchar (*CallNonvirtualCharMethod)(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); // 0x248
jchar (*CallNonvirtualCharMethodV)(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, va_list args); // 0x250
jchar (*CallNonvirtualCharMethodA)(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, const jvalue *args); // 0x258
jshort (*CallNonvirtualShortMethod)(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); // 0x260
jshort (*CallNonvirtualShortMethodV)(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, va_list args); // 0x268
jshort (*CallNonvirtualShortMethodA)(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, const jvalue *args); // 0x270
jint (*CallNonvirtualIntMethod)(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); // 0x278
jint (*CallNonvirtualIntMethodV)(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, va_list args); // 0x280
jint (*CallNonvirtualIntMethodA)(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, const jvalue *args); // 0x288
jlong (*CallNonvirtualLongMethod)(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); // 0x290
jlong (*CallNonvirtualLongMethodV)(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, va_list args); // 0x298
jlong (*CallNonvirtualLongMethodA)(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, const jvalue *args); // 0x2a0
jfloat (*CallNonvirtualFloatMethod)(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); // 0x2a8
jfloat (*CallNonvirtualFloatMethodV)(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, va_list args); // 0x2b0
jfloat (*CallNonvirtualFloatMethodA)(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, const jvalue *args); // 0x2b8
jdouble (*CallNonvirtualDoubleMethod)(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); // 0x2c0
jdouble (*CallNonvirtualDoubleMethodV)(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, va_list args); // 0x2c8
jdouble (*CallNonvirtualDoubleMethodA)(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, const jvalue *args); // 0x2d0
void (*CallNonvirtualVoidMethod)(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); // 0x2d8
void (*CallNonvirtualVoidMethodV)(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, va_list args); // 0x2e0
void (*CallNonvirtualVoidMethodA)(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, const jvalue * args); // 0x2e8
jfieldID (*GetFieldID)(JNIEnv *env, jclass clazz, const char *name, const char *sig); // 0x2f0
jobject (*GetObjectField)(JNIEnv *env, jobject obj, jfieldID fieldID); // 0x2f8
jboolean (*GetBooleanField)(JNIEnv *env, jobject obj, jfieldID fieldID); // 0x300
jbyte (*GetByteField)(JNIEnv *env, jobject obj, jfieldID fieldID); // 0x308
jchar (*GetCharField)(JNIEnv *env, jobject obj, jfieldID fieldID); // 0x310
jshort (*GetShortField)(JNIEnv *env, jobject obj, jfieldID fieldID); // 0x318
jint (*GetIntField)(JNIEnv *env, jobject obj, jfieldID fieldID); // 0x320
jlong (*GetLongField)(JNIEnv *env, jobject obj, jfieldID fieldID); // 0x328
jfloat (*GetFloatField)(JNIEnv *env, jobject obj, jfieldID fieldID); // 0x330
jdouble (*GetDoubleField)(JNIEnv *env, jobject obj, jfieldID fieldID); // 0x338
void (*SetObjectField)(JNIEnv *env, jobject obj, jfieldID fieldID, jobject val); // 0x340
void (*SetBooleanField)(JNIEnv *env, jobject obj, jfieldID fieldID, jboolean val); // 0x348
void (*SetByteField)(JNIEnv *env, jobject obj, jfieldID fieldID, jbyte val); // 0x350
void (*SetCharField)(JNIEnv *env, jobject obj, jfieldID fieldID, jchar val); // 0x358
void (*SetShortField)(JNIEnv *env, jobject obj, jfieldID fieldID, jshort val); // 0x360
void (*SetIntField)(JNIEnv *env, jobject obj, jfieldID fieldID, jint val); // 0x368
void (*SetLongField)(JNIEnv *env, jobject obj, jfieldID fieldID, jlong val); // 0x370
void (*SetFloatField)(JNIEnv *env, jobject obj, jfieldID fieldID, jfloat val); // 0x378
void (*SetDoubleField)(JNIEnv *env, jobject obj, jfieldID fieldID, jdouble val); // 0x380
jmethodID (*GetStaticMethodID)(JNIEnv *env, jclass clazz, const char *name, const char *sig); // 0x388
jobject (*CallStaticObjectMethod)(JNIEnv *env, jclass clazz, jmethodID methodID, ...); // 0x390
jobject (*CallStaticObjectMethodV)(JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); // 0x398
jobject (*CallStaticObjectMethodA)(JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); // 0x3a0
jboolean (*CallStaticBooleanMethod)(JNIEnv *env, jclass clazz, jmethodID methodID, ...); // 0x3a8
jboolean (*CallStaticBooleanMethodV)(JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); // 0x3b0
jboolean (*CallStaticBooleanMethodA)(JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); // 0x3b8
jbyte (*CallStaticByteMethod)(JNIEnv *env, jclass clazz, jmethodID methodID, ...); // 0x3c0
jbyte (*CallStaticByteMethodV)(JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); // 0x3c8
jbyte (*CallStaticByteMethodA)(JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); // 0x3d0
jchar (*CallStaticCharMethod)(JNIEnv *env, jclass clazz, jmethodID methodID, ...); // 0x3d8
jchar (*CallStaticCharMethodV)(JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); // 0x3e0
jchar (*CallStaticCharMethodA)(JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); // 0x3e8
jshort (*CallStaticShortMethod)(JNIEnv *env, jclass clazz, jmethodID methodID, ...); // 0x3f0
jshort (*CallStaticShortMethodV)(JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); // 0x3f8
jshort (*CallStaticShortMethodA)(JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); // 0x400
jint (*CallStaticIntMethod)(JNIEnv *env, jclass clazz, jmethodID methodID, ...); // 0x408
jint (*CallStaticIntMethodV)(JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); // 0x410
jint (*CallStaticIntMethodA)(JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); // 0x418
jlong (*CallStaticLongMethod)(JNIEnv *env, jclass clazz, jmethodID methodID, ...); // 0x420
jlong (*CallStaticLongMethodV)(JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); // 0x428
jlong (*CallStaticLongMethodA)(JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); // 0x430
jfloat (*CallStaticFloatMethod)(JNIEnv *env, jclass clazz, jmethodID methodID, ...); // 0x438
jfloat (*CallStaticFloatMethodV)(JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); // 0x440
jfloat (*CallStaticFloatMethodA)(JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); // 0x448
jdouble (*CallStaticDoubleMethod)(JNIEnv *env, jclass clazz, jmethodID methodID, ...); // 0x450
jdouble (*CallStaticDoubleMethodV)(JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); // 0x458
jdouble (*CallStaticDoubleMethodA)(JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); // 0x460
void (*CallStaticVoidMethod)(JNIEnv *env, jclass cls, jmethodID methodID, ...); // 0x468
void (*CallStaticVoidMethodV)(JNIEnv *env, jclass cls, jmethodID methodID, va_list args); // 0x470
void (*CallStaticVoidMethodA)(JNIEnv *env, jclass cls, jmethodID methodID, const jvalue * args); // 0x478
jfieldID (*GetStaticFieldID)(JNIEnv *env, jclass clazz, const char *name, const char *sig); // 0x480
jobject (*GetStaticObjectField)(JNIEnv *env, jclass clazz, jfieldID fieldID); // 0x488
jboolean (*GetStaticBooleanField)(JNIEnv *env, jclass clazz, jfieldID fieldID); // 0x490
jbyte (*GetStaticByteField)(JNIEnv *env, jclass clazz, jfieldID fieldID); // 0x498
jchar (*GetStaticCharField)(JNIEnv *env, jclass clazz, jfieldID fieldID); // 0x4a0
jshort (*GetStaticShortField)(JNIEnv *env, jclass clazz, jfieldID fieldID); // 0x4a8
jint (*GetStaticIntField)(JNIEnv *env, jclass clazz, jfieldID fieldID); // 0x4b0
jlong (*GetStaticLongField)(JNIEnv *env, jclass clazz, jfieldID fieldID); // 0x4b8
jfloat (*GetStaticFloatField)(JNIEnv *env, jclass clazz, jfieldID fieldID); // 0x4c0
jdouble (*GetStaticDoubleField)(JNIEnv *env, jclass clazz, jfieldID fieldID); // 0x4c8
void (*SetStaticObjectField)(JNIEnv *env, jclass clazz, jfieldID fieldID, jobject value); // 0x4d0
void (*SetStaticBooleanField)(JNIEnv *env, jclass clazz, jfieldID fieldID, jboolean value); // 0x4d8
void (*SetStaticByteField)(JNIEnv *env, jclass clazz, jfieldID fieldID, jbyte value); // 0x4e0
void (*SetStaticCharField)(JNIEnv *env, jclass clazz, jfieldID fieldID, jchar value); // 0x4e8
void (*SetStaticShortField)(JNIEnv *env, jclass clazz, jfieldID fieldID, jshort value); // 0x4f0
void (*SetStaticIntField)(JNIEnv *env, jclass clazz, jfieldID fieldID, jint value); // 0x4f8
void (*SetStaticLongField)(JNIEnv *env, jclass clazz, jfieldID fieldID, jlong value); // 0x500
void (*SetStaticFloatField)(JNIEnv *env, jclass clazz, jfieldID fieldID, jfloat value); // 0x508
void (*SetStaticDoubleField)(JNIEnv *env, jclass clazz, jfieldID fieldID, jdouble value); // 0x510
jstring (*NewString)(JNIEnv *env, const jchar *unicode, jsize len); // 0x518
jsize (*GetStringLength)(JNIEnv *env, jstring str); // 0x520
const jchar * (*GetStringChars)(JNIEnv *env, jstring str, jboolean *isCopy); // 0x528
void (*ReleaseStringChars)(JNIEnv *env, jstring str, const jchar *chars); // 0x530
jstring (*NewStringUTF)(JNIEnv *env, const char *utf); // 0x538
jsize (*GetStringUTFLength)(JNIEnv *env, jstring str); // 0x540
const char* (*GetStringUTFChars)(JNIEnv *env, jstring str, jboolean *isCopy); // 0x548
void (*ReleaseStringUTFChars)(JNIEnv *env, jstring str, const char* chars); // 0x550
jsize (*GetArrayLength)(JNIEnv *env, jarray array); // 0x558
jobjectArray (*NewObjectArray)(JNIEnv *env, jsize len, jclass clazz, jobject init); // 0x560
jobject (*GetObjectArrayElement)(JNIEnv *env, jobjectArray array, jsize index); // 0x568
void (*SetObjectArrayElement)(JNIEnv *env, jobjectArray array, jsize index, jobject val); // 0x570
jbooleanArray (*NewBooleanArray)(JNIEnv *env, jsize len); // 0x578
jbyteArray (*NewByteArray)(JNIEnv *env, jsize len); // 0x580
jcharArray (*NewCharArray)(JNIEnv *env, jsize len); // 0x588
jshortArray (*NewShortArray)(JNIEnv *env, jsize len); // 0x590
jintArray (*NewIntArray)(JNIEnv *env, jsize len); // 0x598
jlongArray (*NewLongArray)(JNIEnv *env, jsize len); // 0x5a0
jfloatArray (*NewFloatArray)(JNIEnv *env, jsize len); // 0x5a8
jdoubleArray (*NewDoubleArray)(JNIEnv *env, jsize len); // 0x5b0
jboolean * (*GetBooleanArrayElements)(JNIEnv *env, jbooleanArray array, jboolean *isCopy); // 0x5b8
jbyte * (*GetByteArrayElements)(JNIEnv *env, jbyteArray array, jboolean *isCopy); // 0x5c0
jchar * (*GetCharArrayElements)(JNIEnv *env, jcharArray array, jboolean *isCopy); // 0x5c8
jshort * (*GetShortArrayElements)(JNIEnv *env, jshortArray array, jboolean *isCopy); // 0x5d0
jint * (*GetIntArrayElements)(JNIEnv *env, jintArray array, jboolean *isCopy); // 0x5d8
jlong * (*GetLongArrayElements)(JNIEnv *env, jlongArray array, jboolean *isCopy); // 0x5e0
jfloat * (*GetFloatArrayElements)(JNIEnv *env, jfloatArray array, jboolean *isCopy); // 0x5e8
jdouble * (*GetDoubleArrayElements)(JNIEnv *env, jdoubleArray array, jboolean *isCopy); // 0x5f0
void (*ReleaseBooleanArrayElements)(JNIEnv *env, jbooleanArray array, jboolean *elems, jint mode); // 0x5f8
void (*ReleaseByteArrayElements)(JNIEnv *env, jbyteArray array, jbyte *elems, jint mode); // 0x600
void (*ReleaseCharArrayElements)(JNIEnv *env, jcharArray array, jchar *elems, jint mode); // 0x608
void (*ReleaseShortArrayElements)(JNIEnv *env, jshortArray array, jshort *elems, jint mode); // 0x610
void (*ReleaseIntArrayElements)(JNIEnv *env, jintArray array, jint *elems, jint mode); // 0x618
void (*ReleaseLongArrayElements)(JNIEnv *env, jlongArray array, jlong *elems, jint mode); // 0x620
void (*ReleaseFloatArrayElements)(JNIEnv *env, jfloatArray array, jfloat *elems, jint mode); // 0x628
void (*ReleaseDoubleArrayElements)(JNIEnv *env, jdoubleArray array, jdouble *elems, jint mode); // 0x630
void (*GetBooleanArrayRegion)(JNIEnv *env, jbooleanArray array, jsize start, jsize l, jboolean *buf); // 0x638
void (*GetByteArrayRegion)(JNIEnv *env, jbyteArray array, jsize start, jsize len, jbyte *buf); // 0x640
void (*GetCharArrayRegion)(JNIEnv *env, jcharArray array, jsize start, jsize len, jchar *buf); // 0x648
void (*GetShortArrayRegion)(JNIEnv *env, jshortArray array, jsize start, jsize len, jshort *buf); // 0x650
void (*GetIntArrayRegion)(JNIEnv *env, jintArray array, jsize start, jsize len, jint *buf); // 0x658
void (*GetLongArrayRegion)(JNIEnv *env, jlongArray array, jsize start, jsize len, jlong *buf); // 0x660
void (*GetFloatArrayRegion)(JNIEnv *env, jfloatArray array, jsize start, jsize len, jfloat *buf); // 0x668
void (*GetDoubleArrayRegion)(JNIEnv *env, jdoubleArray array, jsize start, jsize len, jdouble *buf); // 0x670
void (*SetBooleanArrayRegion)(JNIEnv *env, jbooleanArray array, jsize start, jsize l, const jboolean *buf); // 0x678
void (*SetByteArrayRegion)(JNIEnv *env, jbyteArray array, jsize start, jsize len, const jbyte *buf); // 0x680
void (*SetCharArrayRegion)(JNIEnv *env, jcharArray array, jsize start, jsize len, const jchar *buf); // 0x688
void (*SetShortArrayRegion)(JNIEnv *env, jshortArray array, jsize start, jsize len, const jshort *buf); // 0x690
void (*SetIntArrayRegion)(JNIEnv *env, jintArray array, jsize start, jsize len, const jint *buf); // 0x698
void (*SetLongArrayRegion)(JNIEnv *env, jlongArray array, jsize start, jsize len, const jlong *buf); // 0x6a0
void (*SetFloatArrayRegion)(JNIEnv *env, jfloatArray array, jsize start, jsize len, const jfloat *buf); // 0x6a8
void (*SetDoubleArrayRegion)(JNIEnv *env, jdoubleArray array, jsize start, jsize len, const jdouble *buf); // 0x6b0
jint (*RegisterNatives)(JNIEnv *env, jclass clazz, const JNINativeMethod *methods, jint nMethods); // 0x6b8
jint (*UnregisterNatives)(JNIEnv *env, jclass clazz); // 0x6c0
jint (*MonitorEnter)(JNIEnv *env, jobject obj); // 0x6c8
jint (*MonitorExit)(JNIEnv *env, jobject obj); // 0x6d0
jint (*GetJavaVM)(JNIEnv *env, JavaVM **vm); // 0x6d8
void (*GetStringRegion)(JNIEnv *env, jstring str, jsize start, jsize len, jchar *buf); // 0x6e0
void (*GetStringUTFRegion)(JNIEnv *env, jstring str, jsize start, jsize len, char *buf); // 0x6e8
void * (*GetPrimitiveArrayCritical)(JNIEnv *env, jarray array, jboolean *isCopy); // 0x6f0
void (*ReleasePrimitiveArrayCritical)(JNIEnv *env, jarray array, void *carray, jint mode); // 0x6f8
const jchar * (*GetStringCritical)(JNIEnv *env, jstring string, jboolean *isCopy); // 0x700
void (*ReleaseStringCritical)(JNIEnv *env, jstring string, const jchar *cstring); // 0x708
jweak (*NewWeakGlobalRef)(JNIEnv *env, jobject obj); // 0x710
void (*DeleteWeakGlobalRef)(JNIEnv *env, jweak ref); // 0x718
jboolean (*ExceptionCheck)(JNIEnv *env); // 0x720
jobject (*NewDirectByteBuffer)(JNIEnv* env, void* address, jlong capacity); // 0x728
void* (*GetDirectBufferAddress)(JNIEnv* env, jobject buf); // 0x730
jlong (*GetDirectBufferCapacity)(JNIEnv* env, jobject buf); // 0x738
jobjectRefType (*GetObjectRefType)(JNIEnv* env, jobject obj); // 0x740
jobject (*GetModule)(JNIEnv* env, jclass clazz); // 0x748
jboolean (*IsVirtualThread)(JNIEnv* env, jobject obj); // 0x750
jlong (*GetStringUTFLengthAsLong)(JNIEnv *env, jstring str); // 0x758
};
+15
View File
@@ -0,0 +1,15 @@
// What the portable client code needs from the operating system (implemented in src/platform/<os>/).
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
// ---- implemented by the platform layer ----
void *slinky_gl_get_proc(const char *name); // OpenGL entry point by name (needs a current context)
void slinky_log_write(const char *line); // one line of diagnostics (stderr / debug output), no newline
// ---- implemented by the client (src/engine/protected/init.cpp), called by the platform layer ----
int slinky_setup(void *self); // start-up; runs on its own thread. 0 = ok, else an error code
int slinky_teardown(void); // frees everything slinky_setup built; 0 = hooks still live, keep the DLL loaded
void slinky_logf(const char *fmt, ...); // printf-style diagnostics via slinky_log_write
#ifdef __cplusplus
}
#endif
+82
View File
@@ -0,0 +1,82 @@
// Minimal Win32 declarations for the readable reimplementations in src/engine/protected/.
// (The image calls these through the protector's encrypted import slots; here they are ordinary imports.)
#pragma once
#include <stddef.h>
typedef void *HANDLE, *HWND, *HMODULE, *HINSTANCE, *LPVOID;
typedef int BOOL;
typedef unsigned long DWORD;
typedef long long LONG_PTR, LRESULT;
typedef unsigned long long ULONG_PTR, WPARAM;
typedef long long LPARAM;
typedef unsigned int UINT;
typedef unsigned short WCHAR;
typedef long long LARGE_INTEGER_QUAD;
typedef LRESULT (*WNDPROC)(HWND, UINT, WPARAM, LPARAM);
typedef DWORD (*THREAD_START)(LPVOID);
struct FILETIME { DWORD lo, hi; };
struct WIN32_FIND_DATAW {
DWORD attributes;
FILETIME created, accessed, written;
DWORD size_hi, size_lo, reserved0, reserved1;
wchar_t name[260];
wchar_t alt_name[14];
};
#define WINAPI __stdcall
#define INVALID_HANDLE ((HANDLE)(LONG_PTR)-1)
#define GENERIC_READ 0x80000000u
#define GENERIC_WRITE 0x40000000u
#define FILE_SHARE_READ 1u
#define CREATE_NEW 1u
#define CREATE_ALWAYS 2u
#define OPEN_EXISTING 3u
#define FILE_ATTRIBUTE_NORMAL 0x80u
#define FILE_ATTRIBUTE_DIRECTORY 0x10u
#define INVALID_FILE_SIZE 0xffffffffu
#define ERROR_FILE_NOT_FOUND 2u
#define ERROR_NO_MORE_FILES 0x12u
#define ERROR_FILE_EXISTS 0x50u
#define ERROR_ALREADY_EXISTS 0xb7u
#define GWLP_WNDPROC (-4)
#define SW_SHOWNORMAL 1
extern "C" {
__declspec(dllimport) BOOL WINAPI QueryPerformanceCounter(long long *count);
__declspec(dllimport) BOOL WINAPI QueryPerformanceFrequency(long long *freq);
__declspec(dllimport) DWORD WINAPI GetLastError();
__declspec(dllimport) BOOL WINAPI CreateDirectoryW(const wchar_t *path, void *security);
__declspec(dllimport) BOOL WINAPI DeleteFileW(const wchar_t *path);
__declspec(dllimport) HANDLE WINAPI CreateFileW(const wchar_t *path, DWORD access, DWORD share, void *security,
DWORD disposition, DWORD flags, HANDLE templ);
__declspec(dllimport) DWORD WINAPI GetFileSize(HANDLE h, DWORD *high);
__declspec(dllimport) BOOL WINAPI ReadFile(HANDLE h, void *buf, DWORD n, DWORD *done, void *overlapped);
__declspec(dllimport) BOOL WINAPI WriteFile(HANDLE h, const void *buf, DWORD n, DWORD *done, void *overlapped);
__declspec(dllimport) BOOL WINAPI CloseHandle(HANDLE h);
__declspec(dllimport) HANDLE WINAPI FindFirstFileW(const wchar_t *pattern, WIN32_FIND_DATAW *fd);
__declspec(dllimport) BOOL WINAPI FindNextFileW(HANDLE h, WIN32_FIND_DATAW *fd);
__declspec(dllimport) BOOL WINAPI FindClose(HANDLE h);
__declspec(dllimport) void WINAPI GetSystemTimeAsFileTime(FILETIME *ft);
__declspec(dllimport) HINSTANCE WINAPI ShellExecuteW(HWND hwnd, const wchar_t *op, const wchar_t *file,
const wchar_t *params, const wchar_t *dir, int show);
__declspec(dllimport) HMODULE WINAPI GetModuleHandleA(const char *name);
__declspec(dllimport) void *WINAPI GetProcAddress(HMODULE m, const char *name);
__declspec(dllimport) HWND WINAPI GetForegroundWindow();
__declspec(dllimport) BOOL WINAPI IsWindowUnicode(HWND hwnd);
__declspec(dllimport) DWORD WINAPI GetWindowThreadProcessId(HWND hwnd, DWORD *pid);
__declspec(dllimport) DWORD WINAPI GetCurrentProcessId();
__declspec(dllimport) HANDLE WINAPI CreateThread(void *security, size_t stack, THREAD_START start, LPVOID param,
DWORD flags, DWORD *id);
__declspec(dllimport) void WINAPI Sleep(DWORD ms);
__declspec(dllimport) void WINAPI FreeLibraryAndExitThread(HMODULE m, DWORD code);
__declspec(dllimport) int WINAPI WideCharToMultiByte(UINT codepage, DWORD flags, const wchar_t *src, int n, char *dst,
int dst_size, const char *def, BOOL *used_def);
__declspec(dllimport) int WINAPI MultiByteToWideChar(UINT codepage, DWORD flags, const char *src, int n, wchar_t *dst,
int dst_size);
__declspec(dllimport) HANDLE WINAPI GetStdHandle(DWORD which);
__declspec(dllimport) void WINAPI OutputDebugStringA(const char *text);
__declspec(dllimport) HMODULE WINAPI LoadLibraryA(const char *name);
__declspec(dllimport) BOOL WINAPI DisableThreadLibraryCalls(HMODULE m);
wchar_t *_wgetenv(const wchar_t *name);
}
+39
View File
@@ -0,0 +1,39 @@
cmake_minimum_required(VERSION 3.20)
project(slinky-inject LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
endif()
add_executable(slinky-inject
main.cpp
tui.cpp
)
if(WIN32)
target_sources(slinky-inject PRIVATE inject_windows.cpp)
target_compile_definitions(slinky-inject PRIVATE _WIN32_WINNT=0x0601)
# A standalone .exe: no MinGW runtime DLLs to copy beside it.
target_link_options(slinky-inject PRIVATE -static -static-libgcc -static-libstdc++)
else()
target_sources(slinky-inject PRIVATE inject_linux.cpp)
target_link_libraries(slinky-inject PRIVATE ${CMAKE_DL_LIBS} pthread)
endif()
# Copy the client library next to the injector when it is built in the same
# tree (the top-level CMakeLists.txt does this via add_subdirectory).
# A separate always-run target: a POST_BUILD step on slinky-inject only ran when
# the injector itself was relinked, so the sibling copy went stale whenever only
# the client changed.
if(TARGET slinky)
add_custom_target(slinky-copy-beside-injector ALL
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_FILE:slinky>
$<TARGET_FILE_DIR:slinky-inject>/$<TARGET_FILE_NAME:slinky>
DEPENDS slinky slinky-inject
VERBATIM)
endif()
+7
View File
@@ -0,0 +1,7 @@
#!/bin/sh
# Build slinky-inject for the host Linux.
set -e
cd "$(dirname "$0")"
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j"$(nproc)"
echo "built: $(pwd)/build/slinky-inject"
+11
View File
@@ -0,0 +1,11 @@
#!/bin/sh
# Cross-build slinky-inject.exe with MinGW-w64.
set -e
cd "$(dirname "$0")"
cmake -S . -B build-windows \
-DCMAKE_SYSTEM_NAME=Windows \
-DCMAKE_C_COMPILER=x86_64-w64-mingw32-gcc \
-DCMAKE_CXX_COMPILER=x86_64-w64-mingw32-g++ \
-DCMAKE_BUILD_TYPE=Release
cmake --build build-windows -j"$(nproc)"
echo "built: $(pwd)/build-windows/slinky-inject.exe"
+35
View File
@@ -0,0 +1,35 @@
// Process discovery and library injection, with no opinion about the UI.
#pragma once
#include <functional>
#include <string>
#include <vector>
namespace Injector {
struct Process {
unsigned long pid = 0;
std::string name;
std::string command_line;
};
using LogFn = std::function<void(const std::string &)>;
std::vector<Process> ListProcesses();
// Finds the running Minecraft JVM. On Linux that is a java process whose
// command line names the game; on Windows it is the LWJGL/GLFW window's owner.
bool FindMinecraft(Process &out, std::string &error);
// Finds a process by pid, exact name, or case-insensitive substring of the
// name/command line. Accepts a decimal pid in `target`.
bool FindTarget(const std::string &target, Process &out, std::string &error);
// Loads `library` into `pid` through ptrace+dlopen (Linux) or
// CreateRemoteThread+LoadLibraryW (Windows). Returns false on failure.
bool Inject(unsigned long pid, const std::string &library, const LogFn &log);
// Why injection may fail before it starts, for the error message.
std::string EnvironmentHint();
} // namespace Injector
+527
View File
@@ -0,0 +1,527 @@
// Linux injection: ptrace the target and make it call its own dlopen().
//
// This is the same technique as the Catware injector: attach to the process,
// save its registers, resolve dlopen in the target (preferring the target's own
// libc ELF), point rip at it with the library path on a scratch stack, and use
// a return address of 0 as the completion signal. The client library's ELF
// constructor then starts the client, exactly as DllMain does on Windows.
//
// The library is injected from a private copy under a fresh name so that a
// re-injection after Unload starts a new copy with fresh statics; the old
// mapping is never unloaded (-z nodelete).
#include "inject.hpp"
#include <algorithm>
#include <cctype>
#include <cerrno>
#include <climits>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iterator>
#include <optional>
#include <sstream>
#include <string>
#include <vector>
#include <dirent.h>
#include <dlfcn.h>
#include <elf.h>
#include <fcntl.h>
#include <sys/ptrace.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/user.h>
#include <sys/wait.h>
#include <unistd.h>
namespace Injector {
namespace {
std::string lower(std::string text) {
std::transform(text.begin(), text.end(), text.begin(),
[](unsigned char c) { return (char)std::tolower(c); });
return text;
}
std::string trim(std::string text) {
while (!text.empty() && (text.back() == '\n' || text.back() == '\r' || text.back() == ' ')) text.pop_back();
size_t start = text.find_first_not_of(" \t");
return start == std::string::npos ? std::string() : text.substr(start);
}
std::string read_small_file(const std::string &path) {
std::ifstream in(path, std::ios::binary);
if (!in) return {};
std::ostringstream out;
out << in.rdbuf();
return out.str();
}
std::string command_line_of(unsigned long pid) {
std::string data = read_small_file("/proc/" + std::to_string(pid) + "/cmdline");
for (char &c : data) {
if (c == '\0') c = ' ';
}
return trim(data);
}
std::string name_of(unsigned long pid) {
return trim(read_small_file("/proc/" + std::to_string(pid) + "/comm"));
}
// ---- ELF / maps helpers ----
struct MappedRegion {
uintptr_t start = 0;
uintptr_t end = 0;
uintptr_t offset = 0;
std::string path;
};
std::vector<MappedRegion> read_maps(unsigned long pid) {
std::vector<MappedRegion> regions;
std::ifstream maps("/proc/" + std::to_string(pid) + "/maps");
std::string line;
while (std::getline(maps, line)) {
uintptr_t start = 0, end = 0, offset = 0;
char path[512] = {0};
int parsed = std::sscanf(line.c_str(), "%lx-%lx %*4s %lx %*x:%*x %*d %511[^\n]",
&start, &end, &offset, path);
if (parsed < 3) continue;
MappedRegion region;
region.start = start;
region.end = end;
region.offset = offset;
region.path = parsed == 4 ? path : "";
size_t first = region.path.find_first_not_of(' ');
if (first == std::string::npos) region.path.clear();
else region.path.erase(0, first);
regions.push_back(region);
}
return regions;
}
std::vector<uint8_t> read_target_file(unsigned long pid, const std::string &path) {
// The target's mount namespace first: in a Flatpak or other container the same path on the host is a
// different libc build, and its symbol offsets would point into the wrong place.
std::string through_root = "/proc/" + std::to_string(pid) + "/root" + path;
for (const std::string &candidate : {through_root, path}) {
std::ifstream in(candidate, std::ios::binary);
if (!in) continue;
return std::vector<uint8_t>((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>());
}
return {};
}
uint64_t elf_function_value(const std::vector<uint8_t> &bytes, const char *name) {
if (bytes.size() < sizeof(Elf64_Ehdr)) return 0;
const auto *header = reinterpret_cast<const Elf64_Ehdr *>(bytes.data());
if (std::memcmp(header->e_ident, ELFMAG, SELFMAG) != 0 ||
header->e_ident[EI_CLASS] != ELFCLASS64 ||
header->e_shoff == 0 || header->e_shnum == 0 ||
header->e_shoff + (uint64_t)header->e_shnum * sizeof(Elf64_Shdr) > bytes.size()) {
return 0;
}
const auto *sections = reinterpret_cast<const Elf64_Shdr *>(bytes.data() + header->e_shoff);
for (uint32_t want : {(uint32_t)SHT_DYNSYM, (uint32_t)SHT_SYMTAB}) {
for (int i = 0; i < header->e_shnum; i++) {
const Elf64_Shdr &section = sections[i];
if (section.sh_type != want || section.sh_entsize == 0 || section.sh_link >= header->e_shnum ||
section.sh_offset + section.sh_size > bytes.size()) {
continue;
}
const auto *symbols = reinterpret_cast<const Elf64_Sym *>(bytes.data() + section.sh_offset);
const char *names = reinterpret_cast<const char *>(bytes.data() + sections[section.sh_link].sh_offset);
size_t count = section.sh_size / section.sh_entsize;
for (size_t s = 0; s < count; s++) {
if (ELF64_ST_TYPE(symbols[s].st_info) != STT_FUNC || symbols[s].st_value == 0) continue;
if (std::strcmp(names + symbols[s].st_name, name) == 0) return symbols[s].st_value;
}
}
}
return 0;
}
std::optional<uintptr_t> resolve_dlopen_from_elf(unsigned long pid, const std::vector<MappedRegion> &regions) {
for (const MappedRegion &header : regions) {
if (header.offset != 0) continue;
bool is_libc = header.path.find("libc.so.6") != std::string::npos ||
header.path.find("libdl.so.2") != std::string::npos;
if (!is_libc) continue;
uint64_t value = elf_function_value(read_target_file(pid, header.path), "dlopen");
if (!value) continue;
for (const MappedRegion &segment : regions) {
if (segment.path == header.path && value >= segment.offset &&
value < segment.offset + (segment.end - segment.start)) {
return segment.start + (uintptr_t)(value - segment.offset);
}
}
}
return std::nullopt;
}
std::optional<uintptr_t> resolve_remote_dlopen(unsigned long pid, const LogFn &log) {
void *local_dlopen = dlsym(RTLD_DEFAULT, "dlopen");
if (!local_dlopen) {
log(std::string("dlsym(dlopen) failed: ") + dlerror());
return std::nullopt;
}
const auto local_regions = read_maps(getpid());
const MappedRegion *owner = nullptr;
for (const auto &region : local_regions) {
if ((uintptr_t)local_dlopen >= region.start && (uintptr_t)local_dlopen < region.end &&
!region.path.empty() && region.path[0] == '/') {
owner = &region;
break;
}
}
if (!owner) {
log("could not find dlopen in the injector's own maps");
return std::nullopt;
}
const auto remote_regions = read_maps(pid);
// The target's own libc first. A Flatpak/container runtime is a different
// build at the same path, where our offset would land in the wrong place.
if (auto from_elf = resolve_dlopen_from_elf(pid, remote_regions)) return from_elf;
// Otherwise match the same file and the same segment offset.
uintptr_t within = (uintptr_t)local_dlopen - owner->start;
for (const auto &region : remote_regions) {
if (region.path == owner->path && region.offset == owner->offset) {
return region.start + within;
}
}
log("could not resolve dlopen in the target (no matching libc segment)");
return std::nullopt;
}
// ---- ptrace plumbing ----
bool wait_stopped(pid_t pid, int &status) {
for (;;) {
pid_t r = waitpid(pid, &status, 0);
if (r == pid) return true;
if (r < 0 && errno == EINTR) continue;
return false;
}
}
bool write_remote(pid_t pid, uintptr_t address, const void *data, size_t size) {
iovec local{const_cast<void *>(data), size};
iovec remote{reinterpret_cast<void *>(address), size};
if (process_vm_writev(pid, &local, 1, &remote, 1, 0) == (ssize_t)size) return true;
// Fallback for kernels without process_vm_writev.
const uint8_t *bytes = static_cast<const uint8_t *>(data);
for (size_t done = 0; done < size;) {
long word = 0;
size_t n = std::min(sizeof(word), size - done);
std::memcpy(&word, bytes + done, n);
if (ptrace(PTRACE_POKEDATA, pid, reinterpret_cast<void *>(address + done), reinterpret_cast<void *>(word)) != 0) {
return false;
}
done += n;
}
return true;
}
bool inject_path(pid_t pid, const std::string &path, const LogFn &log) {
if (ptrace(PTRACE_ATTACH, pid, nullptr, nullptr) != 0) {
log(std::string("PTRACE_ATTACH failed: ") + std::strerror(errno));
return false;
}
int status = 0;
if (!wait_stopped(pid, status)) {
log("waitpid after attach failed");
ptrace(PTRACE_DETACH, pid, nullptr, nullptr);
return false;
}
user_regs_struct saved{};
if (ptrace(PTRACE_GETREGS, pid, nullptr, &saved) != 0) {
log(std::string("PTRACE_GETREGS failed: ") + std::strerror(errno));
ptrace(PTRACE_DETACH, pid, nullptr, nullptr);
return false;
}
auto remote_dlopen = resolve_remote_dlopen(pid, log);
if (!remote_dlopen) {
ptrace(PTRACE_SETREGS, pid, nullptr, &saved);
ptrace(PTRACE_DETACH, pid, nullptr, nullptr);
return false;
}
// The stack grows down from rsp. The path must sit above the call stack,
// or dlopen overwrites its own filename argument while loading.
constexpr size_t scratch_offset = 256;
constexpr size_t call_stack_offset = 16384;
uintptr_t scratch = (saved.rsp - scratch_offset) & ~(uintptr_t)0xF;
uintptr_t call_rsp = (saved.rsp - call_stack_offset) & ~(uintptr_t)0xF;
std::string padded = path;
padded.resize(((padded.size() / 8) + 2) * 8, '\0');
if (!write_remote(pid, scratch, padded.data(), padded.size())) {
log(std::string("failed to write library path into target: ") + std::strerror(errno));
ptrace(PTRACE_SETREGS, pid, nullptr, &saved);
ptrace(PTRACE_DETACH, pid, nullptr, nullptr);
return false;
}
// A return address of 0 makes dlopen's `ret` fault at rip == 0. That
// private fault is the completion signal; no shared code is patched, so
// the other JVM threads can keep running untouched.
constexpr uintptr_t return_address = 0;
user_regs_struct call = saved;
call.rdi = scratch;
call.rsi = RTLD_NOW;
call.rip = *remote_dlopen;
call.orig_rax = (unsigned long long)-1; // no syscall to restart
call.rsp = call_rsp - 8;
auto restore = [&]() {
ptrace(PTRACE_SETREGS, pid, nullptr, &saved);
ptrace(PTRACE_DETACH, pid, nullptr, nullptr);
};
if (ptrace(PTRACE_POKEDATA, pid, reinterpret_cast<void *>(call.rsp),
reinterpret_cast<void *>(return_address)) != 0) {
log(std::string("failed to write completion address: ") + std::strerror(errno));
restore();
return false;
}
if (ptrace(PTRACE_SETREGS, pid, nullptr, &call) != 0) {
log(std::string("PTRACE_SETREGS failed: ") + std::strerror(errno));
restore();
return false;
}
bool ok = false;
int pending_signal = 0;
user_regs_struct fault{};
for (int attempt = 0; attempt < (1 << 20); attempt++) {
if (ptrace(PTRACE_CONT, pid, nullptr, reinterpret_cast<void *>((uintptr_t)pending_signal)) != 0) {
log(std::string("PTRACE_CONT failed: ") + std::strerror(errno));
restore();
return false;
}
pending_signal = 0;
if (!wait_stopped(pid, status)) {
log("target vanished while injecting");
return false;
}
if (!WIFSTOPPED(status)) {
log("target exited while injecting");
return false;
}
int signal = WSTOPSIG(status);
if (signal == SIGSEGV) {
ptrace(PTRACE_GETREGS, pid, nullptr, &fault);
if (fault.rip == return_address) {
ok = true;
break;
}
pending_signal = SIGSEGV; // HotSpot's own fault: deliver it
continue;
}
pending_signal = signal; // timers, SIGQUIT, ...: the target's own
}
if (ok) {
log("dlopen returned handle 0x" + [&] {
char buf[32];
std::snprintf(buf, sizeof buf, "%llx", (unsigned long long)fault.rax);
return std::string(buf);
}());
if (fault.rax == 0) {
ok = false;
log("dlopen returned NULL -- the library failed to load in the target");
}
} else {
log("dlopen did not return through the completion address");
}
if (ptrace(PTRACE_SETREGS, pid, nullptr, &saved) != 0) {
log(std::string("warning: failed to restore registers: ") + std::strerror(errno));
}
if (ptrace(PTRACE_DETACH, pid, nullptr, nullptr) != 0) {
log(std::string("warning: PTRACE_DETACH failed: ") + std::strerror(errno));
}
return ok;
}
// The copy is written through /proc/<pid>/root/tmp so that a target with its own /tmp (PrivateTmp, Flatpak,
// pressure-vessel) can open it; `host_path` is where the injector sees it, `target_path` what dlopen gets.
bool private_copy(const std::string &library, unsigned long pid, std::string &host_path, std::string &target_path,
const LogFn &log) {
char source[PATH_MAX];
if (!realpath(library.c_str(), source)) {
log(std::string("cannot resolve ") + library + ": " + std::strerror(errno));
return false;
}
std::ifstream in(source, std::ios::binary);
if (!in) {
log(std::string("cannot open ") + source);
return false;
}
std::string pattern = "/proc/" + std::to_string(pid) + "/root/tmp/slinky-inject-XXXXXX";
int fd = mkstemp(pattern.data());
if (fd >= 0) {
target_path = pattern.substr(pattern.find("/tmp/"));
} else {
pattern = "/tmp/slinky-inject-XXXXXX";
fd = mkstemp(pattern.data());
target_path = pattern;
}
if (fd < 0) {
log(std::string("cannot create a private library copy: ") + std::strerror(errno));
return false;
}
fchmod(fd, 0644);
std::vector<char> buffer(64 * 1024);
bool ok = true;
while (in) {
in.read(buffer.data(), (std::streamsize)buffer.size());
std::streamsize got = in.gcount();
for (std::streamsize done = 0; done < got;) {
ssize_t n = write(fd, buffer.data() + done, (size_t)(got - done));
if (n < 0) {
if (errno == EINTR) continue;
ok = false;
break;
}
done += n;
}
if (!ok) break;
}
if (close(fd) != 0) ok = false;
if (!ok) {
unlink(pattern.c_str());
log("failed to write the private library copy");
return false;
}
host_path = pattern;
return true;
}
} // namespace
std::vector<Process> ListProcesses() {
std::vector<Process> out;
DIR *proc = opendir("/proc");
if (!proc) return out;
while (dirent *entry = readdir(proc)) {
if (entry->d_name[0] < '0' || entry->d_name[0] > '9') continue;
unsigned long pid = std::strtoul(entry->d_name, nullptr, 10);
if (!pid) continue;
std::string cmdline = command_line_of(pid);
if (cmdline.empty()) continue; // kernel thread
out.push_back(Process{pid, name_of(pid), cmdline});
}
closedir(proc);
std::sort(out.begin(), out.end(), [](const Process &a, const Process &b) {
if (a.name != b.name) return a.name < b.name;
return a.pid < b.pid;
});
return out;
}
bool FindMinecraft(Process &out, std::string &error) {
int best_score = 0;
for (const Process &process : ListProcesses()) {
std::string cmd = lower(process.command_line);
std::string name = lower(process.name);
bool java = name.find("java") != std::string::npos || cmd.find("/java ") != std::string::npos;
if (!java) continue;
int score = 0;
if (cmd.find("net.minecraft") != std::string::npos) score += 100;
if (cmd.find("minecraft") != std::string::npos) score += 50;
if (cmd.find("fabric") != std::string::npos) score += 10;
if (cmd.find("forge") != std::string::npos) score += 10;
if (cmd.find("--gamedir") != std::string::npos) score += 10;
if (score > best_score) {
best_score = score;
out = process;
}
}
if (best_score > 0) return true;
error = "Minecraft is not running (no java process with minecraft in its command line)";
return false;
}
bool FindTarget(const std::string &target, Process &out, std::string &error) {
if (target.empty()) {
error = "no target given";
return false;
}
bool numeric = std::all_of(target.begin(), target.end(), [](unsigned char c) { return std::isdigit(c); });
if (numeric) {
out.pid = std::strtoul(target.c_str(), nullptr, 10);
out.name = name_of(out.pid);
out.command_line = command_line_of(out.pid);
if (out.pid && !out.name.empty()) return true;
error = "no process with pid " + target;
return false;
}
std::string needle = lower(target);
for (const Process &process : ListProcesses()) {
if (lower(process.name) == needle) {
out = process;
return true;
}
}
for (const Process &process : ListProcesses()) {
if (lower(process.name).find(needle) != std::string::npos ||
lower(process.command_line).find(needle) != std::string::npos) {
out = process;
return true;
}
}
error = "no process matching '" + target + "'";
return false;
}
bool Inject(unsigned long pid, const std::string &library, const LogFn &log) {
std::string host_copy, target_copy;
if (!private_copy(library, pid, host_copy, target_copy, log)) return false;
log("injecting " + library + " as " + target_copy + " into pid " + std::to_string(pid));
bool ok = inject_path((pid_t)pid, target_copy, log);
// The loaded mapping keeps the inode alive, so a later copy can never alias it.
unlink(host_copy.c_str());
return ok;
}
std::string EnvironmentHint() {
std::ifstream scope_file("/proc/sys/kernel/yama/ptrace_scope");
int scope = -1;
if (scope_file >> scope && scope > 0 && geteuid() != 0) {
return "kernel.yama.ptrace_scope is " + std::to_string(scope) +
"; run the injector with sudo, or set it to 0 (sudo sysctl kernel.yama.ptrace_scope=0), "
"or give the binary CAP_SYS_PTRACE (sudo setcap cap_sys_ptrace+ep <injector>)";
}
return {};
}
} // namespace Injector
+213
View File
@@ -0,0 +1,213 @@
// Windows injection: CreateRemoteThread(LoadLibraryW) into the game's process.
// The client DLL's DllMain starts the client, so loading it is the whole job.
#ifdef _WIN32
#include "inject.hpp"
#include <algorithm>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <windows.h>
#include <tlhelp32.h>
namespace Injector {
namespace {
std::string narrow(const std::wstring &text) {
if (text.empty()) return {};
int n = WideCharToMultiByte(CP_UTF8, 0, text.data(), (int)text.size(), nullptr, 0, nullptr, nullptr);
std::string out((size_t)n, '\0');
WideCharToMultiByte(CP_UTF8, 0, text.data(), (int)text.size(), out.data(), n, nullptr, nullptr);
return out;
}
std::wstring widen(const std::string &text) {
if (text.empty()) return {};
int n = MultiByteToWideChar(CP_UTF8, 0, text.data(), (int)text.size(), nullptr, 0);
std::wstring out((size_t)n, L'\0');
MultiByteToWideChar(CP_UTF8, 0, text.data(), (int)text.size(), out.data(), n);
return out;
}
std::string lower(std::string text) {
std::transform(text.begin(), text.end(), text.begin(),
[](unsigned char c) { return (char)std::tolower(c); });
return text;
}
std::string process_name(DWORD pid) {
HANDLE process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
if (!process) return {};
wchar_t path[1024];
DWORD size = (DWORD)(sizeof path / sizeof path[0]);
std::string result;
if (QueryFullProcessImageNameW(process, 0, path, &size)) {
std::wstring full(path, size);
size_t slash = full.find_last_of(L"\\/");
result = narrow(slash == std::wstring::npos ? full : full.substr(slash + 1));
}
CloseHandle(process);
return result;
}
} // namespace
std::vector<Process> ListProcesses() {
std::vector<Process> out;
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (snapshot == INVALID_HANDLE_VALUE) return out;
PROCESSENTRY32W entry{};
entry.dwSize = sizeof entry;
if (Process32FirstW(snapshot, &entry)) {
do {
out.push_back(Process{entry.th32ProcessID, narrow(entry.szExeFile), {}});
} while (Process32NextW(snapshot, &entry));
}
CloseHandle(snapshot);
std::sort(out.begin(), out.end(), [](const Process &a, const Process &b) {
if (a.name != b.name) return a.name < b.name;
return a.pid < b.pid;
});
return out;
}
bool FindMinecraft(Process &out, std::string &error) {
const wchar_t *classes[] = {L"LWJGL", L"GLFW30"};
for (const wchar_t *cls : classes) {
HWND window = FindWindowW(cls, nullptr);
if (!window) continue;
DWORD pid = 0;
GetWindowThreadProcessId(window, &pid);
if (!pid) continue;
out.pid = pid;
out.name = process_name(pid);
return true;
}
// No window class match: fall back to the first javaw.exe/java.exe, which
// is right when the game is still starting or the window class differs.
for (const Process &process : ListProcesses()) {
std::string name = lower(process.name);
if (name == "javaw.exe" || name == "java.exe") {
out = process;
return true;
}
}
error = "Minecraft is not running (no LWJGL/GLFW30 window and no javaw.exe)";
return false;
}
bool FindTarget(const std::string &target, Process &out, std::string &error) {
if (target.empty()) {
error = "no target given";
return false;
}
bool numeric = std::all_of(target.begin(), target.end(), [](unsigned char c) { return std::isdigit(c); });
if (numeric) {
out.pid = std::strtoul(target.c_str(), nullptr, 10);
out.name = process_name((DWORD)out.pid);
if (out.pid && !out.name.empty()) return true;
error = "no process with pid " + target;
return false;
}
std::string needle = lower(target);
auto all = ListProcesses();
for (const Process &process : all) {
if (lower(process.name) == needle) {
out = process;
return true;
}
}
for (const Process &process : all) {
if (lower(process.name).find(needle) != std::string::npos) {
out = process;
return true;
}
}
error = "no process matching '" + target + "'";
return false;
}
bool Inject(unsigned long pid, const std::string &library, const LogFn &log) {
std::wstring wide_library = widen(library);
DWORD full_size = GetFullPathNameW(wide_library.c_str(), 0, nullptr, nullptr);
if (full_size) {
std::wstring full(full_size, L'\0');
DWORD written = GetFullPathNameW(wide_library.c_str(), full_size, full.data(), nullptr);
if (written && written < full_size) {
full.resize(written);
wide_library = full;
}
}
log("injecting " + narrow(wide_library) + " into pid " + std::to_string(pid));
HANDLE process = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION |
PROCESS_VM_WRITE | PROCESS_VM_READ,
FALSE, (DWORD)pid);
if (!process) {
DWORD err = GetLastError();
if (err == ERROR_ACCESS_DENIED) log("access denied -- try running the injector as administrator");
else log("OpenProcess failed (" + std::to_string(err) + ")");
return false;
}
SIZE_T bytes = (wide_library.size() + 1) * sizeof(wchar_t);
void *remote = VirtualAllocEx(process, nullptr, bytes, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (!remote) {
log("VirtualAllocEx failed (" + std::to_string(GetLastError()) + ")");
CloseHandle(process);
return false;
}
bool ok = false;
SIZE_T written = 0;
if (!WriteProcessMemory(process, remote, wide_library.c_str(), bytes, &written)) {
log("WriteProcessMemory failed (" + std::to_string(GetLastError()) + ")");
} else {
HMODULE kernel = GetModuleHandleW(L"kernel32.dll");
auto load_library = reinterpret_cast<LPTHREAD_START_ROUTINE>(
GetProcAddress(kernel, "LoadLibraryW"));
if (!load_library) {
log("could not find LoadLibraryW");
} else {
HANDLE thread = CreateRemoteThread(process, nullptr, 0, load_library, remote, 0, nullptr);
if (!thread) {
log("CreateRemoteThread failed (" + std::to_string(GetLastError()) + ")");
} else {
WaitForSingleObject(thread, INFINITE);
DWORD exit_code = 0;
if (GetExitCodeThread(thread, &exit_code)) {
// The HMODULE return is truncated to a DWORD here; only
// zero (failure) is meaningful.
ok = exit_code != 0;
if (!ok) log("LoadLibraryW returned NULL in the target");
} else {
log("GetExitCodeThread failed (" + std::to_string(GetLastError()) + ")");
}
CloseHandle(thread);
}
}
}
VirtualFreeEx(process, remote, 0, MEM_RELEASE);
CloseHandle(process);
return ok;
}
std::string EnvironmentHint() {
return "if injection fails with access denied, run the injector as administrator";
}
} // namespace Injector
#endif // _WIN32
+327
View File
@@ -0,0 +1,327 @@
// slinky-inject: the loader's terminal UI and a cross-platform injector.
//
// slinky-inject find Minecraft, inject libslinky.so /
// slinky.dll from beside the injector
// slinky-inject <pid|name> same, but choose the target
// slinky-inject <pid|name> <library> inject that library
// slinky-inject --lib <library> choose the library only
// slinky-inject --pid <pid> choose the target only
// slinky-inject --list list visible processes
//
// Linux uses ptrace + the target's own dlopen; Windows uses
// CreateRemoteThread(LoadLibraryW). The animation is the one from the Go
// loader (animation/animation.go).
#include "inject.hpp"
#include "tui.hpp"
#include <algorithm>
#include <atomic>
#include <chrono>
#include <cctype>
#include <cstdlib>
#include <cstring>
#include <filesystem>
#include <iomanip>
#include <iostream>
#include <mutex>
#include <string>
#include <thread>
#include <vector>
#ifdef _WIN32
#include <windows.h>
#else
#include <limits.h>
#include <unistd.h>
#endif
namespace {
struct Options {
bool list = false;
bool help = false;
bool no_anim = false;
bool pid_given = false;
unsigned long pid = 0;
std::string target;
std::string library;
};
void usage(const char *program) {
std::cout <<
"usage: " << program << " [options] [target] [library]\n"
"\n"
" target pid or process name (default: the running Minecraft)\n"
" library path to libslinky.so / slinky.dll (default: beside this program)\n"
"\n"
"options:\n"
" -p, --pid <pid> target a pid\n"
" -n, --name <name> target a process name (substring match)\n"
" -l, --lib <path> library to inject\n"
" --list list processes and exit\n"
" --no-anim plain output, no animation\n"
" -h, --help this text\n";
}
bool parse_options(int argc, char **argv, Options &options, std::string &error) {
std::vector<std::string> positional;
for (int i = 1; i < argc; i++) {
std::string arg = argv[i];
auto value = [&](const char *what) -> bool {
if (i + 1 >= argc) {
error = std::string(what) + " needs a value";
return false;
}
return true;
};
if (arg == "-h" || arg == "--help") {
options.help = true;
} else if (arg == "--list") {
options.list = true;
} else if (arg == "--no-anim") {
options.no_anim = true;
} else if (arg == "-p" || arg == "--pid") {
if (!value("--pid")) return false;
char *end = nullptr;
options.pid = std::strtoul(argv[++i], &end, 10);
options.pid_given = true;
if (!end || *end || !options.pid) {
error = "invalid pid '" + std::string(argv[i]) + "'";
return false;
}
} else if (arg == "-n" || arg == "--name") {
if (!value("--name")) return false;
options.target = argv[++i];
} else if (arg == "-l" || arg == "--lib") {
if (!value("--lib")) return false;
options.library = argv[++i];
} else if (!arg.empty() && arg[0] == '-') {
error = "unknown option '" + arg + "'";
return false;
} else {
positional.push_back(arg);
}
}
// One positional that names a library is a library, not a target.
if (positional.size() == 1 && options.target.empty() && !options.pid_given) {
std::string lower = positional[0];
std::transform(lower.begin(), lower.end(), lower.begin(),
[](unsigned char c) { return (char)std::tolower(c); });
if (lower.size() > 3 && (lower.substr(lower.size() - 3) == ".so" ||
lower.substr(lower.size() - 4) == ".dll")) {
options.library = positional[0];
positional.clear();
}
}
if (positional.size() > 2) {
error = "too many positional arguments";
return false;
}
if (positional.size() >= 1 && options.target.empty() && !options.pid_given) options.target = positional[0];
if (positional.size() >= 2 && options.library.empty()) options.library = positional[1];
return true;
}
std::filesystem::path executable_path() {
#ifdef _WIN32
wchar_t buffer[MAX_PATH];
DWORD n = GetModuleFileNameW(nullptr, buffer, MAX_PATH);
return std::filesystem::path(std::wstring(buffer, n));
#else
char buffer[PATH_MAX];
ssize_t n = readlink("/proc/self/exe", buffer, sizeof buffer - 1);
if (n <= 0) return {};
buffer[n] = 0;
return std::filesystem::path(buffer);
#endif
}
std::string default_library() {
#ifdef _WIN32
const char *names[] = {"slinky.dll", "libslinky.dll"};
const char *build[] = {"build/cmake/slinky.dll", "../build/cmake/slinky.dll"};
#else
const char *names[] = {"libslinky.so", "slinky.so"};
const char *build[] = {"build/linux/libslinky.so", "../build/linux/libslinky.so"};
#endif
std::error_code ec;
std::filesystem::path exe = executable_path();
std::vector<std::filesystem::path> candidates;
if (!exe.empty()) {
for (const char *name : names) candidates.push_back(exe.parent_path() / name);
}
for (const char *name : names) candidates.push_back(std::filesystem::current_path(ec) / name);
for (const char *path : build) candidates.push_back(std::filesystem::current_path(ec) / path);
#ifndef _WIN32
candidates.push_back("/usr/local/lib/libslinky.so");
candidates.push_back("/usr/lib/libslinky.so");
#endif
for (const auto &candidate : candidates) {
if (std::filesystem::exists(candidate, ec)) {
auto canonical = std::filesystem::weakly_canonical(candidate, ec);
return ec ? candidate.string() : canonical.string();
}
}
return {};
}
void sleep_ms(int ms) {
std::this_thread::sleep_for(std::chrono::milliseconds(ms));
}
void render_for(Tui::Screen &screen, int milliseconds, const std::string &title, const std::string &message,
bool error = false) {
auto end = std::chrono::steady_clock::now() + std::chrono::milliseconds(milliseconds);
do {
screen.Render(title, message, error);
sleep_ms(16);
} while (std::chrono::steady_clock::now() < end);
}
void print_list(const std::vector<Injector::Process> &processes) {
std::cout << " PID NAME COMMAND\n";
for (const auto &process : processes) {
std::string command = process.command_line;
if (command.size() > 100) command = command.substr(0, 97) + "...";
std::cout << std::setw(6) << process.pid << " " << std::setw(20) << std::left << process.name << " "
<< command << "\n";
}
}
} // namespace
int main(int argc, char **argv) {
Options options;
std::string error;
if (!parse_options(argc, argv, options, error)) {
std::cerr << "slinky-inject: " << error << "\n\n";
usage(argv[0]);
return 1;
}
if (options.help) {
usage(argv[0]);
return 0;
}
if (options.list) {
print_list(Injector::ListProcesses());
return 0;
}
const bool interactive = Tui::EnableAnsi() && Tui::StdoutIsTty() && !options.no_anim;
Tui::Screen screen(interactive);
if (!options.library.empty()) {
std::error_code ec;
if (!std::filesystem::exists(options.library, ec)) {
std::cerr << "slinky-inject: library not found: " << options.library << "\n";
return 1;
}
} else {
options.library = default_library();
if (options.library.empty()) {
if (interactive) {
screen.Render("ERROR",
"Library not found. Put libslinky.so/slinky.dll beside the injector, "
"or pass its path as the second argument.",
true);
sleep_ms(3000);
} else {
std::cerr << "slinky-inject: library not found (pass its path as the second argument)\n";
}
return 1;
}
}
// ---- find the target ----
Injector::Process target;
if (interactive) {
render_for(screen, 450, "FINDING MINECRAFT", "Looking for the game process...");
} else {
std::cout << "Finding Minecraft...\n";
}
bool found = false;
if (options.pid_given) {
target.pid = options.pid;
found = true;
} else if (!options.target.empty()) {
found = Injector::FindTarget(options.target, target, error);
} else {
found = Injector::FindMinecraft(target, error);
}
if (!found) {
std::string hint = Injector::EnvironmentHint();
std::string message = error;
if (!hint.empty()) message += "\n\n" + hint;
if (interactive) {
screen.Render("ERROR", message, true);
sleep_ms(4000);
} else {
std::cerr << "slinky-inject: " << message << "\n";
}
return 1;
}
// ---- inject, with the animation still running ----
std::string last_log = "Injecting into Minecraft...";
std::mutex log_mutex;
std::atomic<bool> done{false};
std::atomic<bool> injected{false};
auto started = std::chrono::steady_clock::now();
std::thread worker([&]() {
injected.store(Injector::Inject(
target.pid, options.library,
[&](const std::string &line) {
std::lock_guard<std::mutex> lock(log_mutex);
last_log = line;
}));
done.store(true);
});
if (interactive) {
for (;;) {
{
std::lock_guard<std::mutex> lock(log_mutex);
screen.Render("INJECTING", last_log);
}
sleep_ms(16);
bool finished = done.load();
auto elapsed = std::chrono::steady_clock::now() - started;
if (finished && elapsed > std::chrono::milliseconds(1200)) break;
}
}
worker.join();
if (!injected.load()) {
std::string hint = Injector::EnvironmentHint();
std::string message = "Injection failed. " + last_log;
if (!hint.empty()) message += "\n\n" + hint;
if (interactive) {
screen.Render("ERROR", message, true);
sleep_ms(5000);
} else {
std::cerr << "slinky-inject: " << message << "\n";
}
return 1;
}
// ---- success countdown, exactly the Go loader's five seconds ----
if (interactive) {
for (int i = 5; i > 0; --i) {
std::string title = "Injected successfully. Loader will close in " + std::to_string(i) + "s.";
render_for(screen, 1000, title, "DONE");
}
} else {
std::cout << "Injected successfully into pid " << target.pid << ".\n";
}
return 0;
}
+193
View File
@@ -0,0 +1,193 @@
#include "tui.hpp"
#include <chrono>
#include <cstdio>
#include <cstdlib>
#include <random>
#include <string>
#include <thread>
#ifdef _WIN32
#include <io.h>
#include <windows.h>
#else
#include <unistd.h>
#endif
namespace Tui {
namespace {
// Exact frames from the Go loader. The trailing spaces are part of the art:
// they keep the right edge of the box aligned between frames.
const char *kLogo[4] = {
" _ \n"
"(( ____ \n"
" \\\\________________( ( e\\____o\n"
" ( _____/ \n"
" \\___________________/ \n"
" / \\ \\ / ",
" _ \n"
" (( ____ \n"
" ||________________( ( e\\____o\n"
" ( _____/ \n"
" \\___________________/ \n"
" ) | | ) ",
" _ \n"
" // ____ \n"
" //________________( ( e\\____o\n"
" ( _____/ \n"
" \\___________________/ \n"
" \\ / / \\ ",
" _ \n"
" (( ____ \n"
" ||________________( ( e\\____o\n"
" ( _____/ \n"
" \\___________________/ \n"
" | ) ) | ",
};
const char *kErrorLogo =
"\n"
" ____ \n"
" _________________( ( x\\____o\n"
"// _____/ \n"
"||____________________/ \n"
" | | | | ";
const char *kOrange = "\x1b[38;5;208m";
const char *kRed = "\x1b[91m";
const char *kReset = "\x1b[0m";
std::string make_line() {
std::random_device device;
std::mt19937 rng(device());
std::string line;
line.reserve(25);
for (int i = 0; i < 25; i++) line.push_back(rng() & 1 ? '_' : '-');
return line;
}
const std::string &base_line() {
static const std::string line = make_line();
return line;
}
} // namespace
bool EnableAnsi() {
#ifdef _WIN32
HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD mode = 0;
if (!out || out == INVALID_HANDLE_VALUE || !GetConsoleMode(out, &mode)) return false;
return SetConsoleMode(out, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING) != 0;
#else
return true;
#endif
}
bool StdoutIsTty() {
#ifdef _WIN32
return _isatty(_fileno(stdout)) != 0;
#else
return isatty(STDOUT_FILENO) != 0;
#endif
}
std::string Logo(int frame) {
return kLogo[((frame % 4) + 4) % 4];
}
std::string ErrorLogo() { return kErrorLogo; }
// The line is the base pattern shifted one character per frame, exactly as the
// Go loader's init() builds its 30 frames.
std::string SlinkyLine(int frame) {
const std::string &base = base_line();
if (base.empty()) return {};
int shift = ((frame % (int)base.size()) + (int)base.size()) % (int)base.size();
return base.substr(shift) + base.substr(0, shift);
}
Screen::Screen(bool animate) : animate_(animate && StdoutIsTty()) {
if (animate_) {
std::fputs("\x1b[?1049h\x1b[?25l", stdout);
std::fflush(stdout);
}
}
Screen::~Screen() {
if (animate_) {
std::fputs("\x1b[?25h\x1b[?1049l", stdout);
std::fflush(stdout);
}
}
void Screen::Render(const std::string &title, const std::string &message, bool error) {
if (!animate_) {
// Non-interactive output: one line per state change is enough.
std::fputs(error ? "ERROR: " : "", stdout);
std::fputs(title.c_str(), stdout);
if (!message.empty()) {
std::fputs(" - ", stdout);
std::fputs(message.c_str(), stdout);
}
std::fputc('\n', stdout);
std::fflush(stdout);
return;
}
// Build the whole frame first. Clearing the screen before every 16 ms redraw made the
// console blank between frames (the flicker); instead the cursor goes home and each line is
// overwritten in one write, with erase-to-end-of-line cleaning up shorter content.
const int frame_index = Frame();
std::string body;
body.reserve(1024);
if (error) {
body += kRed;
body += kErrorLogo;
} else {
body += kLogo[frame_index % 4];
}
body += '\n';
body += SlinkyLine(frame_index);
body += "\n\n> ";
body += (error ? kRed : kOrange);
body += title;
body += kReset;
body += "\n\n";
body += message;
body += '\n';
// Nothing changed since the last call: leave the terminal alone.
if (body == last_body_) return;
last_body_ = body;
std::string out;
out.reserve(body.size() + 64);
// Synchronized output: supporting terminals swap the completed frame atomically.
out += "\x1b[?2026h";
out += "\x1b[H";
for (char c : body) {
if (c == '\n') {
out += "\x1b[K\n";
} else {
out += c;
}
}
out += "\x1b[K\x1b[J"; // clear the rest of the last line and anything below the frame
out += "\x1b[?2026l";
std::fwrite(out.data(), 1, out.size(), stdout);
std::fflush(stdout);
}
int Screen::Frame() const {
auto elapsed = std::chrono::steady_clock::now() - start_;
return (int)(std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count() / kFrameMs);
}
} // namespace Tui
+40
View File
@@ -0,0 +1,40 @@
// The loader's terminal UI: the four-frame Slinky logo and the sliding
// underscore line, ported from the Go loader (animation/animation.go) so the
// injector looks and moves exactly like it.
#pragma once
#include <chrono>
#include <string>
namespace Tui {
bool EnableAnsi(); // Windows: turn on VT processing; Linux: no-op
bool StdoutIsTty();
std::string Logo(int frame);
std::string ErrorLogo();
std::string SlinkyLine(int frame);
class Screen {
public:
explicit Screen(bool animate);
~Screen();
Screen(const Screen&) = delete;
Screen& operator=(const Screen&) = delete;
void Render(const std::string& title, const std::string& message, bool error = false);
private:
// The animation advances with time, not with redraws: the caller redraws every ~16 ms so status
// messages show up at once, which made a frame-per-redraw animation run at 60 fps.
static constexpr int kFrameMs = 100;
int Frame() const;
bool animate_;
std::chrono::steady_clock::time_point start_ = std::chrono::steady_clock::now();
// Last rendered content: the caller redraws at 60 Hz, but the frame only changes every
// kFrameMs (or on a status change), so identical redraws are skipped.
std::string last_body_;
};
} // namespace Tui
+10450
View File
File diff suppressed because it is too large. Load diff
+3
View File
@@ -0,0 +1,3 @@
# generated by tools/gen_data.py: import-table slots used by code that calls the image's thunks
.section .data,"dw"
.balign 8
+29
View File
@@ -0,0 +1,29 @@
// Aliases for logic functions the engine refers to by their address name.
#include "slinky/engine.h"
#pragma comment(linker, "/alternatename:R_bool=visit_bool_setting")
#pragma comment(linker, "/alternatename:R_enum=visit_choice_setting")
#pragma comment(linker, "/alternatename:R_key=visit_color_setting")
#pragma comment(linker, "/alternatename:R_kind2=visit_list_setting")
#pragma comment(linker, "/alternatename:R_num=visit_float_setting")
#pragma comment(linker, "/alternatename:R_rng=visit_range_setting")
#pragma comment(linker, "/alternatename:W_bool=widget_bool")
#pragma comment(linker, "/alternatename:W_color=widget_color")
#pragma comment(linker, "/alternatename:W_enumw=widget_enum")
#pragma comment(linker, "/alternatename:W_items=widget_items")
#pragma comment(linker, "/alternatename:W_label=draw_section_label")
#pragma comment(linker, "/alternatename:W_range=widget_range")
#pragma comment(linker, "/alternatename:W_slider=widget_slider")
#pragma comment(linker, "/alternatename:velocity_shared_conditions_entry=velocity_shared_conditions")
#pragma comment(linker, "/alternatename:gui_get_scale_entry=gui_get_scale")
#pragma comment(linker, "/alternatename:gui_handle_input_event_entry=gui_handle_input_event")
#pragma comment(linker, "/alternatename:mod_overlay_rebuild_visibility_entry=mod_overlay_rebuild_visibility")
#pragma comment(linker, "/alternatename:mod_overlay_append_entry_entry=mod_overlay_append_entry")
#pragma comment(linker, "/alternatename:is_held_item_accepted_weapon=weapons_check_held_item_cached")
#pragma comment(linker, "/alternatename:weapons_is_item_allowed_by_class_entry=weapons_is_item_allowed_by_class")
#pragma comment(linker, "/alternatename:weapons_find_first_weapon_slot_entry=weapons_find_first_weapon_slot")
#pragma comment(linker, "/alternatename:friends_friends_friend_list=friends_on_middle_click_add")
#pragma comment(linker, "/alternatename:chams_render_living_entity_entry=chams_render_living_entity")
#pragma comment(linker, "/alternatename:piercing_decide_which_entity_entry=piercing_decide_which_entity")
#pragma comment(linker, "/alternatename:piercing_should_pierce_blocks_entry=piercing_should_pierce_blocks")
#pragma comment(linker, "/alternatename:ghost_hand_should_ignore_entity_entry=ghost_hand_should_ignore_entity")
#pragma comment(linker, "/alternatename:log_fmt=libc_snprintf")
+428
View File
@@ -0,0 +1,428 @@
// 0x7000: Java native entry points (SlinkyHooks13): wrap the JNIEnv in a Jvm handle and forward
#include "slinky/engine.h"
#include "slinky/jids.h"
extern "C" JIds *java_natives_world_cached_jni_ids;
extern "C" void slinky_logf(const char *fmt, ...);
extern "C" char *ptr_to_frame_module_settings; // -> module settings block
FN(void, render_world_events, (JNIEnv *env, int key, bool down));
FN(void, tick_rotation, (Jvm *jvm));
FN(void, set_local_player_max_hurt_time, (Jvm *jvm));
FN(unsigned char, world_is_loaded, (Jvm *jvm));
FN(void, slinky_gui_init_once, ()); // one-time NanoVG/GUI setup (protected/hooks.cpp)
FN(void, slinky_try_pending_class_hooks, ()); // Java 9+: deferred WorldClient retransform (protected/init.cpp)
// The unload sequence sets java_natives_frame_client_shutting_down_natives_check ("client is shutting down") before slinky_teardown frees every
// global about a second later. The JVM can still dispatch any of these natives in that window (the
// retransform is not guaranteed to have unwired every patched call site: see the java_set_render_manager_player_view_y/serialize_settings
// unload crashes in hs_err pid6708/pid2260), so every entry reachable through the hooks checks the flag
// first and does nothing.
extern "C" unsigned char java_natives_frame_client_shutting_down_natives_check;
extern "C" void hook_post_world_clear_1_7(JNIEnv *env, jclass, int key) { if (!java_natives_frame_client_shutting_down_natives_check) render_world_events(env, key, false); }
extern "C" void hook_post_world_clear_1_8(JNIEnv *env, jclass, int key) { if (!java_natives_frame_client_shutting_down_natives_check) render_world_events(env, key, true); }
extern "C" void hook_is_display_active_prerender(JNIEnv *env, jclass) {
if (java_natives_frame_client_shutting_down_natives_check) return;
Jvm jvm = {java_natives_world_cached_jni_ids, env};
tick_rotation(&jvm);
if (ptr_to_frame_module_settings[0x14] == 1) set_local_player_max_hurt_time(&jvm);
world_is_loaded(&jvm);
}
struct Rot { float yaw, pitch; };
FN(unsigned char, is_silent_rotation_active, ());
FN(Rot, get_silent_rotation, ());
FN(void *, get_minecraft, (Jvm *jvm));
FN(void *, get_local_player, (Jvm *jvm, void *minecraft));
FN(void *, get_minecraft_render_global, (Jvm *jvm, void *minecraft));
FN(void, set_render_manager_boolean, (Jvm *jvm, void *render_manager, bool on));
FN(void, apply_rotation_to_player, (Jvm *jvm, void *player));
FN(void, set_silent_rotation_active, (Jvm *jvm, void *player, bool on));
FN(void, call_orient_camera, (Jvm *jvm, void *object, float t));
FN(Rot, get_entity_rotation, (Jvm *jvm, void *entity));
FN(Rot, get_entity_prev_rotation, (Jvm *jvm, void *entity));
FN(void, set_entity_rotation, (Jvm *jvm, void *entity, Rot rotation));
FN(void, set_entity_prev_rotation, (Jvm *jvm, void *entity, Rot rotation));
FN(void, set_active_render_info, (Jvm *jvm, void *active_render_info, bool b));
// camera orientation hook: with silent rotations active, orient using the server rotation
extern "C" void hook_orient_camera(JNIEnv *env, jclass, void *receiver, float t) {
if (java_natives_frame_client_shutting_down_natives_check) return;
Jvm jvm = {java_natives_world_cached_jni_ids, env};
if (is_silent_rotation_active()) {
void *minecraft = get_minecraft(&jvm);
void *player = get_local_player(&jvm, minecraft);
set_render_manager_boolean(&jvm, get_minecraft_render_global(&jvm, minecraft), true);
apply_rotation_to_player(&jvm, player);
set_silent_rotation_active(&jvm, player, true);
call_orient_camera(&jvm, receiver, t);
set_silent_rotation_active(&jvm, player, false);
} else {
call_orient_camera(&jvm, receiver, t);
}
}
// same for the other hook: temporarily swap in the silent rotation around the call
extern "C" void hook_update_active_render_info(JNIEnv *env, jclass, void *receiver, unsigned char b) {
if (java_natives_frame_client_shutting_down_natives_check) return;
Jvm jvm = {java_natives_world_cached_jni_ids, env};
if (is_silent_rotation_active()) {
void *minecraft = get_minecraft(&jvm);
void *player = get_local_player(&jvm, minecraft);
Rot rotation = get_entity_rotation(&jvm, player);
Rot entity_prev_rotation = get_entity_prev_rotation(&jvm, player);
Rot silent_rotation = get_silent_rotation();
set_entity_rotation(&jvm, player, silent_rotation);
set_entity_prev_rotation(&jvm, player, silent_rotation);
set_active_render_info(&jvm, receiver, b != 0);
set_entity_rotation(&jvm, player, rotation);
set_entity_prev_rotation(&jvm, player, entity_prev_rotation);
} else {
set_active_render_info(&jvm, receiver, b != 0);
}
}
// ---- tick hooks: build the module Ctx, dispatch pre-event (0xe), run the game method, dispatch post (0xf) ----
extern "C" char java_natives_world_module_manager[], java_natives_frame_module_manager_target_state[];
extern "C" void *init_profile_list, *java_natives_world_key_state_table_one_byte;
extern "C" char *ptr_to_gui_module_descriptor;
struct HCtx { Jvm *jvm; void *module_manager; void *profile_list; void *minecraft; void *world; void *player; void *key_state_table; void *target;
unsigned char in_game; unsigned char padding_41[3]; int padding_44; };
struct HEvent { int type; int padding_04; HCtx *ctx; unsigned char flag, cancel; char rest[0x36]; };
FN(void *, get_world, (Jvm *jvm, void *minecraft));
FN(unsigned char, is_in_game, (Jvm *jvm, void *minecraft));
FN(void, tick_pre_click, (HCtx *ctx, void *state));
FN(void, dispatch_event, (void *mgr, HEvent *e));
FN(void, java_call_minecraft_click_mouse, (Jvm *jvm, void *minecraft));
FN(void, java_call_minecraft_right_click_mouse, (Jvm *jvm, void *minecraft));
FN(void, tick_click, (HCtx *ctx));
FN(void, tick_click_done, (HCtx *ctx));
static inline __attribute__((always_inline)) void make_ctx(HCtx *ctx, Jvm *jvm, void *minecraft) {
void *world = get_world(jvm, minecraft);
void *player = get_local_player(jvm, minecraft);
ctx->jvm = jvm;
ctx->module_manager = java_natives_world_module_manager;
ctx->profile_list = init_profile_list;
ctx->minecraft = minecraft;
ctx->world = world;
ctx->player = player;
ctx->key_state_table = java_natives_world_key_state_table_one_byte;
ctx->target = java_natives_frame_module_manager_target_state;
ctx->in_game = ptr_to_gui_module_descriptor[0x14] ? 0 : is_in_game(jvm, minecraft);
ctx->padding_41[0] = 0; ctx->padding_41[1] = 0; ctx->padding_41[2] = 0;
*(int *)&ctx->padding_41[0] = 0;
ctx->padding_44 = 0;
}
extern "C" void hook_click_mouse(JNIEnv *env, jclass, void *minecraft) {
if (java_natives_frame_client_shutting_down_natives_check) return;
Jvm jvm = {java_natives_world_cached_jni_ids, env};
HCtx ctx;
make_ctx(&ctx, &jvm, minecraft);
tick_pre_click(&ctx, java_natives_frame_module_manager_target_state);
HEvent e = {};
e.type = 0xe;
e.ctx = &ctx;
e.flag = 1;
dispatch_event(java_natives_world_module_manager, &e);
if (e.cancel) return;
java_call_minecraft_click_mouse(&jvm, minecraft);
e.type = 0xf;
dispatch_event(java_natives_world_module_manager, &e);
}
extern "C" void hook_right_click_mouse(JNIEnv *env, jclass, void *minecraft) {
if (java_natives_frame_client_shutting_down_natives_check) return;
Jvm jvm = {java_natives_world_cached_jni_ids, env};
HCtx ctx;
make_ctx(&ctx, &jvm, minecraft);
HEvent e = {};
e.type = 0xe;
e.ctx = &ctx;
e.cancel = 0;
dispatch_event(java_natives_world_module_manager, &e);
tick_click(&ctx);
if (e.cancel) return;
java_call_minecraft_right_click_mouse(&jvm, minecraft);
e.type = 0xf;
dispatch_event(java_natives_world_module_manager, &e);
tick_click_done(&ctx);
}
// ---- main per-frame hook: context, GUI frame (events 9/10), module ticks, unload ----
FN(void, hook_game_window, ());
FN(void *, get_timer, (Jvm *jvm, void *minecraft));
FN(void, invoke_callback, (void *x));
FN(void, apply_module_state, (void *mgr, void *mod, int v));
FN(double, time_now, ());
FN(int, display_width, (Jvm *jvm));
FN(int, display_height, (Jvm *jvm));
FN(unsigned char, is_event_handled, (void *mgr, int type));
FN(unsigned char, resize_render_target, (void *gui, int w, int h));
FN(double, gui_get_scale_entry, ());
FN(void, set_render_scale, (void *gui, double t));
FN(void, begin_frame_framebuffer, (void *gui, double t));
FN(void, clear_frame, (void *gui));
FN(void, begin_frame, (void *gui, double t));
FN(void, draw_framebuffer_texture_over_screen, (void *gui));
FN(void, end_frame, (void *gui));
FN(unsigned char, has_queued_modules, (void *mgr));
FN(void, apply_module_toggles, (HCtx *ctx));
FN(void, tick_modules, (HCtx *ctx, void *timer, double now));
FN(void, tick_netty, (Jvm *jvm));
FN(unsigned char, is_unload_requested_flag, ());
FN(int, save_settings, (void *module_manager, void *profile_list));
FN(void, queue_module_toggle, (void *module_manager, void *module, unsigned char enabled));
struct Pair16 { long long a, b; };
FN(int, unregister_channel_class, (Jvm *jvm, Pair16 *p));
FN(void, tick_post_frame, (HCtx *ctx, void *state));
FN(int, set_java_hooks, (JNIEnv *env, int x));
extern "C" char gui_state_block[]; // GUI state; [0x4cb] = unload requested
FN(void, restore_game_window, ());
FN(void, start_unload_thread, (void *x));
typedef void (*GlPushAttrib)(unsigned);
typedef void (*GlPopAttrib)();
extern "C" GlPushAttrib *ptr_to_pGlPushAttrib;
extern "C" GlPopAttrib *ptr_to_pGlPopAttrib;
extern "C" const double threshold_0_00833333;
extern "C" double frame_start_time;
extern "C" unsigned char frame_prev_module_state, frame_input_flag, frame_repass_needed, frame_spinlock, java_natives_frame_client_shutting_down_natives_check;
extern "C" int frame_resize_cooldown, render_target_width, render_target_height;
extern "C" void *java_natives_frame_module_key_binds_key_module, *frame_render_target, *init_our_module_handle;
extern "C" Pair16 init_slinkychannelhandler_class_constructor;
struct ModEnt { char padding_00[0x1e8]; void (*on_shutdown)(HEvent *); };
struct Mgr { ModEnt **data; unsigned long long count; };
struct GuiEvent { int type; int padding_04; HCtx *ctx; int flags; float dt; char rest[0x30]; };
struct GuiEvent2 { long long type; HCtx *ctx; void *gui; float dt; char rest[0x2c]; };
static inline __attribute__((always_inline)) void spin_lock() {
while (__atomic_exchange_n(&frame_spinlock, 1, __ATOMIC_SEQ_CST)) __builtin_ia32_pause();
}
extern "C" void hook_update_display(JNIEnv *env) {
if (java_natives_frame_client_shutting_down_natives_check) return; // the client is shutting down; this frame must not touch anything the
// unload thread is about to free (save_settings of a second pass crashed here)
hook_game_window();
slinky_gui_init_once(); // updateDisplay runs on the client thread with the GL context current, also in the menu
slinky_try_pending_class_hooks();
Jvm jvm = {java_natives_world_cached_jni_ids, env};
void *minecraft = get_minecraft(&jvm);
void *player = get_local_player(&jvm, minecraft);
void *world = get_world(&jvm, minecraft);
void *timer = get_timer(&jvm, minecraft);
HCtx ctx;
ctx.jvm = &jvm; ctx.module_manager = java_natives_world_module_manager; ctx.profile_list = init_profile_list; ctx.minecraft = minecraft; ctx.world = world; ctx.player = player;
ctx.key_state_table = java_natives_world_key_state_table_one_byte; ctx.target = java_natives_frame_module_manager_target_state;
char *cfg = ptr_to_gui_module_descriptor;
ctx.in_game = cfg[0x14] ? 0 : is_in_game(&jvm, minecraft);
*(int *)&ctx.padding_41[0] = 0;
ctx.padding_44 = 0;
if (!ctx.in_game && frame_prev_module_state) {
invoke_callback(java_natives_frame_module_key_binds_key_module);
if (!world_is_loaded(&jvm)) __builtin_memset(java_natives_world_key_state_table_one_byte, 0, 0x68);
}
frame_prev_module_state = ctx.in_game;
if (*(int *)(cfg + 0x1c) == 0) apply_module_state(java_natives_world_module_manager, ptr_to_gui_module_descriptor, 0x63);
double now = time_now();
int W = display_width(&jvm);
int H = display_height(&jvm);
if (0.0 > frame_start_time) frame_start_time = now;
if (frame_render_target && !is_event_handled(java_natives_world_module_manager, 0xa)) {
render_target_width = W;
render_target_height = H;
if (resize_render_target(frame_render_target, W, H)) {
frame_resize_cooldown = 0xa;
} else if (frame_resize_cooldown > 0) {
frame_resize_cooldown = frame_resize_cooldown - 1;
}
void *gui = frame_render_target;
set_render_scale(gui, gui_get_scale_entry());
double dt = now - frame_start_time;
if (dt > threshold_0_00833333) {
float fdt = (float)dt;
GuiEvent e = {};
e.type = 9;
e.ctx = &ctx;
e.dt = fdt;
dispatch_event(java_natives_world_module_manager, (HEvent *)&e);
int flags = e.flags;
frame_input_flag = flags & 1;
if (flags & 1) {
if ((flags & 2) || frame_repass_needed || frame_resize_cooldown > 0) {
unsigned char again = (unsigned)(flags & 2) >> 1;
(*ptr_to_pGlPushAttrib)(0xffffffff);
begin_frame_framebuffer(frame_render_target, now);
GuiEvent2 e2 = {0xa, &ctx, frame_render_target, fdt, {}};
dispatch_event(java_natives_world_module_manager, (HEvent *)&e2);
clear_frame(frame_render_target);
(*ptr_to_pGlPopAttrib)();
frame_repass_needed = again;
}
}
frame_start_time = now;
}
if (frame_input_flag) {
(*ptr_to_pGlPushAttrib)(0xffffffff);
begin_frame(frame_render_target, now);
draw_framebuffer_texture_over_screen(frame_render_target);
end_frame(frame_render_target);
(*ptr_to_pGlPopAttrib)();
} else {
frame_repass_needed = 1;
}
}
if (has_queued_modules(java_natives_world_module_manager)) {
spin_lock();
apply_module_toggles(&ctx);
frame_spinlock = 0;
}
tick_modules(&ctx, timer, now);
tick_netty(&jvm);
if (!is_unload_requested_flag()) return;
gui_state_block[0x4cb] = 0; // consume the click immediately: one sequence per click, no repass if
// the user clicks again while this one is still running
// Take the Java hooks out before touching any state. If removal fails the game will keep calling
// these natives; the old sequence still freed the module manager from the unload thread and the
// next frame crashed in save_settings -> serialize_settings on the freed module list. In that case keep the client
// resident (everything stays allocated) and cancel the unload request instead. The native entries
// check java_natives_frame_client_shutting_down_natives_check, so a partial removal followed by the real teardown can no longer fault
// either: everything the game still reaches goes inert the moment this branch commits.
Pair16 p = init_slinkychannelhandler_class_constructor;
int channel_off = p.a ? unregister_channel_class(&jvm, &p) : 0;
slinky_logf("unload: channel-off=%d", channel_off);
// 0x3fd means there is no live network pipeline; there is nothing to remove in that case.
if (channel_off && channel_off != 0x3fd) return;
int hoff = set_java_hooks(env, 0);
slinky_logf("unload: hooks-off=%d", hoff);
if (hoff) return;
// unload: save settings, notify and disable every module, tear down
int saved = save_settings(java_natives_world_module_manager, init_profile_list);
slinky_logf("unload: settings saved=%d", saved);
HEvent e3 = {};
e3.type = 0x1a;
e3.ctx = &ctx;
Mgr *mgr = (Mgr *)java_natives_world_module_manager;
for (unsigned long long i = 0; i < mgr->count; i++)
if (mgr->data[i]->on_shutdown) mgr->data[i]->on_shutdown(&e3);
for (unsigned long long i = 0; i < mgr->count; i++) queue_module_toggle(java_natives_world_module_manager, mgr->data[i], 0);
spin_lock();
java_natives_frame_client_shutting_down_natives_check = 1;
frame_spinlock = 0;
apply_module_toggles(&ctx);
tick_post_frame(&ctx, java_natives_frame_module_manager_target_state);
restore_game_window();
slinky_logf("unload: starting unload thread");
start_unload_thread(init_our_module_handle);
}
// ---- set the key bind of a module: re-register it in the bind map, then tell the modules (event 0x18) ----
FN(void, key_map_remove, (void *map, int key, void *mod)); // unregister
FN(void, key_map_add, (void *map, int key, void *mod)); // register
struct BindEvent { int type; int padding_04; void *mgr; void *mod; char rest[0x30]; int tail; };
extern "C" void set_module_bind(void *mod, int key) {
int *slot = (int *)((char *)mod + 0x1c);
key_map_remove(java_natives_frame_module_key_binds_key_module, *slot, mod);
*slot = key;
key_map_add(java_natives_frame_module_key_binds_key_module, key, mod);
BindEvent ev = {};
ev.type = 0x18;
ev.mgr = java_natives_world_module_manager;
ev.mod = mod;
dispatch_event(java_natives_world_module_manager, (HEvent *)&ev);
}
// ---- key handlers of the two key maps (java_natives_frame_profile_key_binds_key_profile: key -> profile name, java_natives_frame_module_key_binds_key_module: key -> module) ----
// Both are called from the input handler on_input_event through key_map_dispatch as handler(item, key, down).
FN(int, load_profile_name, (const char *name, void *mgr)); // load profile `name`; 0 = ok
FN(void *, find_profile, (void *profiles, const char *name)); // profile entry by name (+0x38: key bind)
FN(void *, key_map_get, (void *map, int key, void *val)); // item registered under `key`
FN(void *, libc_calloc, (unsigned long long n, unsigned long long size));
FN(unsigned long long, libc_strlen, (const char *str));
FN(void *, libc_memcpy, (void *dest, const void *src, unsigned long long n));
extern "C" void *java_natives_frame_profile_key_binds_key_profile;
extern "C" unsigned char frame_spinlock, java_natives_frame_client_shutting_down_natives_check;
extern "C" const char str_failed_to_load_profile[]; // "Failed to load profile"
struct Ev48 { int type; int padding_04; union { const char *name; struct { int err; int pad2; const char *msg; }; }; char rest[0x38]; };
extern "C" const char ptr_to_str_failed_to_set_bind[]; // prepared "Failed to set bind" event (0x48 bytes)
// profile bind pressed: load the profile; a failure unbinds it and is reported (event 0xc), success is event 0xb
extern "C" unsigned char on_profile_bind(const char *name, int key, bool down) {
if (!down) return 0;
int err = load_profile_name(name, java_natives_world_module_manager);
if (!err) {
Ev48 e = {};
e.type = 0xb;
e.name = name;
dispatch_event(java_natives_world_module_manager, (HEvent *)&e);
return 1;
}
char *prof = (char *)find_profile(init_profile_list, name);
if (prof) *(int *)(prof + 0x38) = 0;
Ev48 e = {};
e.type = 0xc;
e.err = err;
e.msg = str_failed_to_load_profile;
dispatch_event(java_natives_world_module_manager, (HEvent *)&e);
key_map_remove(java_natives_frame_profile_key_binds_key_profile, key, 0);
return 0;
}
// module bind pressed (hold mode: enabled while the key is down; otherwise toggles on press)
extern "C" unsigned char on_module_bind(char *mod, int key, bool down) {
if (mod[0x20] == 1) {
queue_module_toggle(java_natives_world_module_manager, mod, down);
mod[0x21] = down;
} else if (down) {
queue_module_toggle(java_natives_world_module_manager, mod, mod[0x14] ^ 1);
}
return 1;
}
// bind profile `p` to `key` (0 = unbind): a key serves one profile, the previous owner loses its bind
extern "C" void bind_profile_to_key(char *p, int key) {
void *map = java_natives_frame_profile_key_binds_key_profile;
key_map_remove(map, *(int *)(p + 0x38), 0);
void *other = key_map_get(java_natives_frame_profile_key_binds_key_profile, key, 0);
if (other) {
char *o = (char *)find_profile(init_profile_list, (const char *)other);
if (o) {
*(int *)(o + 0x38) = 0;
key_map_remove(java_natives_frame_profile_key_binds_key_profile, key, 0);
}
}
if (key) {
const char *name = *(const char **)p;
unsigned long long n = libc_strlen(name);
char *copy = (char *)libc_calloc(n + 1, 1);
if (!copy) {
Ev48 e;
__builtin_memcpy(&e, ptr_to_str_failed_to_set_bind, 0x48);
dispatch_event(java_natives_world_module_manager, (HEvent *)&e);
return;
}
libc_memcpy(copy, name, n);
key_map_add(java_natives_frame_profile_key_binds_key_profile, key, copy);
}
*(int *)(p + 0x38) = key;
}
// ---- SlinkyChannelHandler.onPacket(Object packet, boolean outbound, long time, int id): int ----
// Netty thread -> client: serialised with the frame code through the lock at frame_spinlock, ignored once the
// client is shutting down (java_natives_frame_client_shutting_down_natives_check). The modules see it as event 2 (inbound) / 3 (outbound) and may change
// `result` (checked at +0x24 by the handler).
struct PktEv { int type; int padding_04; Jvm *jvm; void *pkt; long long time; int id; int result; char rest[0x20]; int tail; };
extern "C" int on_packet(JNIEnv *env, jobject, void *pkt, unsigned char outbound, long long time, int id) {
bool busy;
for (;;) {
busy = __atomic_exchange_n(&frame_spinlock, 1, __ATOMIC_SEQ_CST) != 0;
if (!busy) break;
__builtin_ia32_pause();
}
int result = 0;
if (!java_natives_frame_client_shutting_down_natives_check) {
Jvm jvm = {java_natives_world_cached_jni_ids, env};
PktEv e = {};
e.type = outbound ? 3 : 2;
e.jvm = &jvm;
e.pkt = pkt;
e.time = time;
e.id = id;
dispatch_event(java_natives_world_module_manager, (HEvent *)&e);
result = e.result;
}
frame_spinlock = 0;
return result;
}
+424
View File
@@ -0,0 +1,424 @@
// 0x8000: Java native entry points (SlinkyHooks13): wrap the JNIEnv in a Jvm handle and forward
#include "slinky/engine.h"
#include "slinky/jids.h"
extern "C" JIds *java_natives_world_cached_jni_ids;
extern "C" unsigned char java_natives_frame_client_shutting_down_natives_check; // set by the unload sequence: every entry below is inert once it is 1
extern "C" void slinky_logf(const char *fmt, ...);
struct Rot { float yaw, pitch; };
FN(unsigned char, is_silent_rotation_active, ());
FN(Rot, get_silent_rotation, ());
FN(void *, should_render_player_name, (Jvm *jvm, void *render_player, void *other_object));
FN(void, java_set_render_manager_player_view_x, (Jvm *jvm, void *render_manager, float value));
FN(void, java_set_render_manager_player_view_y, (Jvm *jvm, void *render_manager, float value));
FN(unsigned char, handle_chat_packet, (Jvm *jvm, void *object, void *other_object));
FN(void, send_packet_with_id, (Jvm *jvm, void *object, void *other_object, void *c));
FN(void, send_packet, (Jvm *jvm, void *object, void *other_object));
// renderPlayer is a static native with the class-file descriptor (Object,Object,DDDFF)V: the JVM passes
// the three doubles and two floats in registers. The module receives them unchanged and forwards them
// to RendererLivingEntity.doRender.
FN(void, chams_render_living_entity_entry, (Jvm *jvm, void *entity, void *other_object, double x, double d0, double d1, float f0, float f1));
extern "C" void *hook_can_render_name(JNIEnv *env, jclass, void *receiver, void *b) {
if (java_natives_frame_client_shutting_down_natives_check) return 0;
Jvm jvm = {java_natives_world_cached_jni_ids, env};
return should_render_player_name(&jvm, receiver, b);
}
extern "C" void hook_render_player(JNIEnv *env, jclass, void *receiver, void *b, double x, double d0, double d1, float f0, float f1) {
if (java_natives_frame_client_shutting_down_natives_check) return;
static int logged;
if (!logged) {
logged = 1;
slinky_logf("frame: first render_player");
}
Jvm jvm = {java_natives_world_cached_jni_ids, env};
chams_render_living_entity_entry(&jvm, receiver, b, x, d0, d1, f0, f1);
}
// silent-rotation aware pitch / yaw setters
extern "C" void hook_set_render_x(JNIEnv *env, jclass, void *receiver, float pitch) {
if (java_natives_frame_client_shutting_down_natives_check) return;
if (is_silent_rotation_active()) pitch = get_silent_rotation().pitch;
Jvm jvm = {java_natives_world_cached_jni_ids, env};
java_set_render_manager_player_view_x(&jvm, receiver, pitch);
}
extern "C" void hook_set_render_y(JNIEnv *env, jclass, void *receiver, float yaw) {
if (java_natives_frame_client_shutting_down_natives_check) return;
if (is_silent_rotation_active()) yaw = get_silent_rotation().yaw;
Jvm jvm = {java_natives_world_cached_jni_ids, env};
java_set_render_manager_player_view_y(&jvm, receiver, yaw);
}
extern "C" void hook_send_packet_1_7(JNIEnv *env, jclass, void *receiver, void *b, void *c) {
if (java_natives_frame_client_shutting_down_natives_check) return;
Jvm jvm = {java_natives_world_cached_jni_ids, env};
if (!handle_chat_packet(&jvm, receiver, b)) send_packet_with_id(&jvm, receiver, b, c);
}
extern "C" void hook_send_packet_1_8(JNIEnv *env, jclass, void *receiver, void *b) {
if (java_natives_frame_client_shutting_down_natives_check) return;
Jvm jvm = {java_natives_world_cached_jni_ids, env};
if (!handle_chat_packet(&jvm, receiver, b)) send_packet(&jvm, receiver, b);
}
FN(void *, get_minecraft, (Jvm *jvm));
FN(void *, get_world, (Jvm *jvm, void *minecraft));
FN(void, set_block_meta, (Jvm *jvm, void *block, int a, int b));
struct Event { long long type; Jvm *jvm; void *world; int z; int a; int b; int zz[9]; };
extern "C" char java_natives_world_module_manager[];
FN(void, dispatch_event, (void *mgr, Event *e));
extern "C" void hook_unload_chunk(JNIEnv *env, jclass, void *receiver, int a, int b) {
if (java_natives_frame_client_shutting_down_natives_check) return;
Jvm jvm = {java_natives_world_cached_jni_ids, env};
void *world = get_world(&jvm, get_minecraft(&jvm));
set_block_meta(&jvm, receiver, a, b);
Event e = {0x14, &jvm, world, 0, a, b, {}};
dispatch_event(java_natives_world_module_manager, &e);
}
// ---- world-change hooks: report the change to the modules (event 0x14), then call the game method ----
FN(int, get_block_id, (Jvm *jvm, void *blockOrState));
FN(void *, set_block_state, (Jvm *jvm, void *block, int a, int b, int c, void *blk, int d, int e));
struct Pos3 { int x, y, z; };
FN(Pos3, get_block_position, (Jvm *jvm, void *pos));
FN(void *, get_block_from_pos, (Jvm *jvm, void *blk));
FN(void *, set_block_at_pos, (Jvm *jvm, void *block, void *pos, void *blk, int f));
FN(void, update_chunk, (Jvm *jvm, void *chunk, int p4, int p5, int p6, int p7, int p8, int p9));
FN(int, chunk_coordinate, (int v));
struct Ev14 { long long type; Jvm *jvm; void *o; int kind; int v[4]; int z[7]; };
extern "C" void *hook_set_block_1_7(JNIEnv *env, jclass, void *receiver, int a, int b, int c, void *blk, int d, int e) {
if (java_natives_frame_client_shutting_down_natives_check) return 0;
Jvm jvm = {java_natives_world_cached_jni_ids, env};
int block_id = get_block_id(&jvm, blk);
Ev14 ev = {0x14, &jvm, receiver, 2, {a, b, c, block_id}, {}};
dispatch_event(java_natives_world_module_manager, (Event *)&ev);
return set_block_state(&jvm, receiver, a, b, c, blk, d, e);
}
extern "C" void *hook_set_block_1_8(JNIEnv *env, jclass, void *receiver, void *pos, void *blk, int f) {
if (java_natives_frame_client_shutting_down_natives_check) return 0;
Jvm jvm = {java_natives_world_cached_jni_ids, env};
Pos3 block_position = get_block_position(&jvm, pos);
int block_id = get_block_id(&jvm, get_block_from_pos(&jvm, blk));
Ev14 ev = {0x14, &jvm, receiver, 2, {block_position.x, block_position.y, block_position.z, block_id}, {}};
dispatch_event(java_natives_world_module_manager, (Event *)&ev);
return set_block_at_pos(&jvm, receiver, pos, blk, f);
}
extern "C" void hook_invalidate_chunk(JNIEnv *env, jclass, void *receiver, int p4, int p5, int p6, int p7, int p8, int p9) {
if (java_natives_frame_client_shutting_down_natives_check) return;
Jvm jvm = {java_natives_world_cached_jni_ids, env};
update_chunk(&jvm, receiver, p4, p5, p6, p7, p8, p9);
int cx = chunk_coordinate(p4);
int cz = chunk_coordinate(p6);
Ev14 ev = {0x14, &jvm, receiver, 1, {cx, cz, 0, 0}, {}};
dispatch_event(java_natives_world_module_manager, (Event *)&ev);
}
extern "C" char *ptr_to_safe_walk_module_descriptor;
FN(void *, get_local_player, (Jvm *jvm, void *minecraft));
FN(unsigned char, get_render_flag, ());
FN(unsigned char, is_sneaking, (Jvm *jvm, void *object));
// isSneaking hook: report true for the local player while the "sneak" module asks for it
extern "C" unsigned char hook_should_clip_ledge(JNIEnv *env, jclass, void *receiver) {
if (java_natives_frame_client_shutting_down_natives_check) return 0;
Jvm jvm = {java_natives_world_cached_jni_ids, env};
if (ptr_to_safe_walk_module_descriptor[0x14] == 1) {
void *player = get_local_player(&jvm, get_minecraft(&jvm));
if (player && (*jvm.env)->IsSameObject(jvm.env, (jobject)player, (jobject)receiver) && get_render_flag()) return 1;
}
return is_sneaking(&jvm, receiver);
}
// ---- player update / velocity hooks ----
extern "C" char java_natives_frame_module_manager_target_state[];
extern "C" void *init_profile_list, *java_natives_world_key_state_table_one_byte;
extern "C" char *ptr_to_gui_module_descriptor;
struct HCtx { Jvm *jvm; void *module_manager; void *profile_list; void *minecraft; void *world; void *player; void *key_state_table; void *target;
unsigned char in_game; unsigned char padding_41[3]; int padding_44; };
struct HEvent { int type; int padding_04; HCtx *ctx; char rest[0x38]; };
struct RotEvent { int type; int padding_04; unsigned char a, b, d, e; Rot rot; char rest[0x34]; }; // event 0xd
FN(unsigned char, is_in_game, (Jvm *jvm, void *minecraft));
FN(void, request_rotation_reset_if_flagged, (HCtx *ctx));
FN(void, update_entities, (Jvm *jvm, void *object));
FN(Rot, get_entity_rotation, (Jvm *jvm, void *entity));
FN(unsigned char, is_event_handled, (void *mgr, int type));
FN(void, apply_player_rotation, (HCtx *ctx, unsigned char a, unsigned char b, unsigned char d, Rot rotation));
FN(void, set_rotation_active, (Jvm *jvm, void *player, bool on));
extern "C" void hook_update_entities(JNIEnv *env, jclass, void *receiver) {
if (java_natives_frame_client_shutting_down_natives_check) return;
Jvm jvm = {java_natives_world_cached_jni_ids, env};
void *minecraft = get_minecraft(&jvm);
void *player = get_local_player(&jvm, minecraft);
if (!player) {
// updateEntities also runs in the menu / after disconnect. Keep the game's world tick intact;
// only the module/rotation part below needs a player.
if (receiver) update_entities(&jvm, receiver);
return;
}
HCtx ctx;
ctx.jvm = &jvm; ctx.module_manager = java_natives_world_module_manager; ctx.profile_list = init_profile_list; ctx.minecraft = minecraft; ctx.world = receiver; ctx.player = player;
ctx.key_state_table = java_natives_world_key_state_table_one_byte; ctx.target = java_natives_frame_module_manager_target_state;
ctx.in_game = ptr_to_gui_module_descriptor[0x14] ? 0 : is_in_game(&jvm, minecraft);
*(int *)&ctx.padding_41[0] = 0;
ctx.padding_44 = 0;
static int entities_logged;
if (!entities_logged) {
entities_logged = 1;
slinky_logf("frame: first update_entities in_game=%d gui_open=%d", (int)ctx.in_game, (int)ptr_to_gui_module_descriptor[0x14]);
}
HEvent e = {};
e.type = 0x10;
e.ctx = &ctx;
dispatch_event(java_natives_world_module_manager, (Event *)&e);
request_rotation_reset_if_flagged(&ctx);
e.type = 0x11;
dispatch_event(java_natives_world_module_manager, (Event *)&e);
update_entities(&jvm, ctx.world);
if ((*env)->ExceptionCheck(env)) {
(*env)->ExceptionDescribe(env);
(*env)->ExceptionClear(env);
return; // do not call more JNI with an exception pending
}
e.type = 0x12;
dispatch_event(java_natives_world_module_manager, (Event *)&e);
Rot rotation = is_silent_rotation_active() ? get_silent_rotation() : get_entity_rotation(&jvm, player);
unsigned char a, b, d;
if (is_event_handled(java_natives_world_module_manager, 0xd)) {
a = 0; b = 0; d = 0;
} else {
RotEvent m;
__builtin_memset(&m, 0, sizeof m);
m.type = 0xd;
m.rot = rotation;
dispatch_event(java_natives_world_module_manager, (Event *)&m);
a = m.a; b = m.b; d = m.d;
rotation = m.rot;
}
apply_player_rotation(&ctx, a, b, d, rotation);
set_rotation_active(&jvm, player, true);
}
FN(void, set_motion, (Jvm *jvm, void *object, float x, float y, float z));
FN(void *, get_game_settings, (Jvm *jvm, void *minecraft));
FN(void *, get_held_item, (Jvm *jvm, void *item));
FN(int, get_hurt_time, (Jvm *jvm, void *player));
FN(unsigned char, is_key_down, (Jvm *jvm, void *object));
struct VelEvent { int type; int padding_04; HCtx *ctx; float x, y, z; unsigned char flag; char rest[0x2b]; };
extern "C" void hook_move_flying(JNIEnv *env, jclass, void *receiver, float x, float y, float z) {
if (java_natives_frame_client_shutting_down_natives_check) return;
Jvm jvm = {java_natives_world_cached_jni_ids, env};
if (!is_event_handled(java_natives_world_module_manager, 0x15) || !is_event_handled(java_natives_world_module_manager, 0x16)) {
void *minecraft = get_minecraft(&jvm);
void *player = get_local_player(&jvm, minecraft);
if ((*jvm.env)->IsSameObject(jvm.env, (jobject)receiver, (jobject)player)) {
void *world = get_world(&jvm, minecraft);
HCtx ctx;
ctx.jvm = &jvm; ctx.module_manager = java_natives_world_module_manager; ctx.profile_list = init_profile_list; ctx.minecraft = minecraft; ctx.world = world; ctx.player = player;
ctx.key_state_table = java_natives_world_key_state_table_one_byte; ctx.target = java_natives_frame_module_manager_target_state;
ctx.in_game = ptr_to_gui_module_descriptor[0x14] ? 0 : is_in_game(&jvm, minecraft);
*(int *)&ctx.padding_41[0] = 0;
ctx.padding_44 = 0;
void *held_item = get_held_item(&jvm, get_game_settings(&jvm, minecraft));
unsigned char flag = get_hurt_time(&jvm, player) > 0 ? is_key_down(&jvm, held_item) : 0;
VelEvent e = {0x15, 0, &ctx, x, y, z, flag, {}};
dispatch_event(java_natives_world_module_manager, (Event *)&e);
set_motion(&jvm, player, e.x, e.y, e.z);
e.type = 0x16;
dispatch_event(java_natives_world_module_manager, (Event *)&e);
return;
}
}
set_motion(&jvm, receiver, x, y, z);
}
// GUI tick: let modules ask for key pass-through, then forward movement keys into the open screen
FN(void, gui_key_passthrough, (Jvm *jvm, void *object));
FN(void *, get_current_screen, (Jvm *jvm, void *minecraft));
FN(void *, get_game_screen, (Jvm *jvm, void *object));
FN(int, get_event_key, (Jvm *jvm, void *object));
FN(unsigned char, keyboard_has_events, (Jvm *jvm));
FN(unsigned char, keyboard_has_next, (Jvm *jvm));
FN(unsigned char, keyboard_can_repeat, (Jvm *jvm));
FN(int, keyboard_next_key, (Jvm *jvm));
FN(void, keyboard_send_key, (Jvm *jvm, void *scr, int x, int y));
struct FlagEvent { int type; int padding_04; HCtx *ctx; unsigned char flag; char rest[0x37]; };
extern "C" void hook_update_controller(JNIEnv *env, jclass, void *receiver) {
if (java_natives_frame_client_shutting_down_natives_check) return;
Jvm jvm = {java_natives_world_cached_jni_ids, env};
gui_key_passthrough(&jvm, receiver);
void *minecraft = get_minecraft(&jvm);
void *player = get_local_player(&jvm, minecraft);
void *world = get_world(&jvm, minecraft);
HCtx ctx;
ctx.jvm = &jvm; ctx.module_manager = java_natives_world_module_manager; ctx.profile_list = init_profile_list; ctx.minecraft = minecraft; ctx.world = world; ctx.player = player;
ctx.key_state_table = java_natives_world_key_state_table_one_byte; ctx.target = java_natives_frame_module_manager_target_state;
ctx.in_game = ptr_to_gui_module_descriptor[0x14] ? 0 : is_in_game(&jvm, minecraft);
*(int *)&ctx.padding_41[0] = 0;
ctx.padding_44 = 0;
FlagEvent e = {};
e.type = 0x19;
e.ctx = &ctx;
dispatch_event(java_natives_world_module_manager, (Event *)&e);
if (!(e.flag & 1)) return;
void *scr = get_current_screen(&jvm, minecraft);
int key = get_event_key(&jvm, get_game_screen(&jvm, get_game_settings(&jvm, minecraft)));
while (keyboard_has_events(&jvm)) {
}
while (keyboard_has_next(&jvm)) {
if (!scr || !keyboard_can_repeat(&jvm)) continue;
int k = keyboard_next_key(&jvm);
if (k == 1 || k == key) keyboard_send_key(&jvm, scr, 0, k);
}
}
// ---- glClear(GL_DEPTH_BUFFER_BIT) hook: fire the 3D (6) and 2D (7) render events with GL state set up ----
struct Vec3 { double x, y, z; };
FN(void *, get_render_view_entity, (Jvm *jvm, void *minecraft));
FN(void *, get_timer, (Jvm *jvm, void *minecraft));
FN(float, get_partial_ticks, (Jvm *jvm, void *timer));
FN(Vec3, java_get_entity_pos_x, (Jvm *jvm, void *entity));
FN(Vec3, java_get_entity_prev_pos_x, (Jvm *jvm, void *entity));
FN(Vec3, vec3_lerp, (Vec3 a, Vec3 b, double t));
FN(void, gl_push_state, (Jvm *jvm));
FN(void, gl_clear_color, (Jvm *jvm));
FN(void, gl_disable_lighting, (Jvm *jvm));
FN(void, gl_pop_state, (Jvm *jvm));
FN(void, gl_blend_mode, (Jvm *jvm, unsigned src, unsigned dst));
FN(void, gl_bind_texture, (Jvm *jvm));
FN(void, gl_enable_depth, (Jvm *jvm));
FN(void, gl_begin_2d, (Jvm *jvm));
FN(void, gl_depth_mask, (Jvm *jvm));
FN(void, gl_set_color, (Jvm *jvm, float r, float g, float b, float a));
FN(void, set_silent_rotation_active, (Jvm *jvm, void *player, bool on));
typedef void (*GlCap)(unsigned);
typedef void (*GlBlend)(unsigned, unsigned);
typedef void (*GlGetD)(unsigned, double *);
typedef void (*GlVoid)();
typedef void (*GlOrtho)(double, double, double, double, double, double);
typedef void (*GlColor4)(float, float, float, float);
extern "C" GlCap *ptr_to_pGlDisable, *ptr_to_pGlEnable, *ptr_to_pGlClear, *ptr_to_pGlMatrixMode;
extern "C" GlBlend *ptr_to_pGlBlendFunc;
extern "C" GlGetD *ptr_to_pGlGetFloatv;
extern "C" GlVoid *ptr_to_pGlLoadIdentity;
extern "C" GlOrtho *ptr_to_pGlOrtho;
extern "C" GlColor4 *ptr_to_pGlColor4f;
extern "C" const float one_b;
extern "C" void *frame_render_target;
extern "C" int render_target_width, render_target_height;
extern "C" double gl_modelview_matrix[16], gl_projection_matrix[16];
struct R3D { long long type; Vec3 cam; HCtx *ctx; float pt; unsigned char flag; char rest[0x1b]; };
struct R2D { long long type; Vec3 cam; HCtx *ctx; float w, h; double *proj, *mv; float pt; unsigned char flag, padding_5, padding_6, padding_7; };
extern "C" void render_world_events(JNIEnv *env, unsigned mask, unsigned char flag) {
if (java_natives_frame_client_shutting_down_natives_check) return;
if (!frame_render_target) return;
static int events_logged;
if (!events_logged) {
events_logged = 1;
slinky_logf("frame: first post_world_clear mask=%x flag=%d target=%p", mask, (int)flag, frame_render_target);
}
if (mask == 0x100) {
Jvm jvm = {java_natives_world_cached_jni_ids, env};
void *minecraft = get_minecraft(&jvm);
void *player = get_local_player(&jvm, minecraft);
unsigned char silent = is_silent_rotation_active();
if (silent) set_silent_rotation_active(&jvm, player, true);
unsigned char no3d = is_event_handled(java_natives_world_module_manager, 6);
unsigned char no2d = is_event_handled(java_natives_world_module_manager, 7);
if (!(no3d & no2d)) {
void *world = get_world(&jvm, minecraft);
void *view = get_render_view_entity(&jvm, minecraft);
static int worldrender_logged;
if (!worldrender_logged) {
worldrender_logged = 1;
slinky_logf("worldrender: no3d=%d no2d=%d player=%p world=%p view=%p", (int)no3d, (int)no2d, player, world, view);
}
if (player && world && view) {
HCtx ctx;
ctx.jvm = &jvm; ctx.module_manager = java_natives_world_module_manager; ctx.profile_list = init_profile_list; ctx.minecraft = minecraft; ctx.world = world; ctx.player = player;
ctx.key_state_table = java_natives_world_key_state_table_one_byte; ctx.target = java_natives_frame_module_manager_target_state;
ctx.in_game = ptr_to_gui_module_descriptor[0x14] ? 0 : is_in_game(&jvm, minecraft);
*(int *)&ctx.padding_41[0] = 0;
ctx.padding_44 = 0;
float partial_ticks = get_partial_ticks(&jvm, get_timer(&jvm, minecraft));
Vec3 cur = java_get_entity_pos_x(&jvm, view);
Vec3 prev = java_get_entity_prev_pos_x(&jvm, view);
Vec3 cam = vec3_lerp(prev, cur, (double)partial_ticks);
if (flag) {
gl_push_state(&jvm); gl_clear_color(&jvm); gl_disable_lighting(&jvm); gl_pop_state(&jvm);
gl_blend_mode(&jvm, 0x302, 0x303);
}
GlCap *dis = ptr_to_pGlDisable;
(*dis)(0xb71); (*dis)(0xde1); (*dis)(0xb50);
GlCap *en = ptr_to_pGlEnable;
(*en)(0xbe2);
(*ptr_to_pGlBlendFunc)(0x302, 0x303);
(*en)(0xb20);
if (!no3d) {
R3D e = {6, cam, &ctx, partial_ticks, flag, {}};
dispatch_event(java_natives_world_module_manager, (Event *)&e);
}
if (!(no2d & 1)) {
GlGetD *get = ptr_to_pGlGetFloatv;
(*get)(0xba6, gl_modelview_matrix);
(*get)(0xba7, gl_projection_matrix);
GlCap *mode = ptr_to_pGlMatrixMode;
(*mode)(0x1701);
GlVoid *push = ptr_to_pGlLoadIdentity;
(*push)();
(*ptr_to_pGlOrtho)(0.0, (double)render_target_width, (double)render_target_height, 0.0, -3000.0, 3000.0);
(*mode)(0x1700);
(*push)();
R2D e = {7, cam, &ctx, (float)render_target_width, (float)render_target_height, gl_modelview_matrix, gl_projection_matrix, partial_ticks, flag, 0, 0, 0};
dispatch_event(java_natives_world_module_manager, (Event *)&e);
}
if (flag) {
gl_bind_texture(&jvm); gl_enable_depth(&jvm); gl_begin_2d(&jvm); gl_depth_mask(&jvm);
float one = one_b;
gl_set_color(&jvm, one, one, one, 1.0f);
}
(*en)(0xde1); (*en)(0xb71); (*en)(0xb44); (*en)(0xbc0);
float one = one_b;
(*ptr_to_pGlColor4f)(one, one, one, one);
}
}
if (silent) set_silent_rotation_active(&jvm, player, false);
}
(*ptr_to_pGlClear)(mask);
}
// ---- the client's input handler (installed in input_input_handler_hooked_into_game for the game-window hook, see protected/input.cpp) ----
// Keeps the key-state table (byte per key: bit0 = down, bit1 = toggled by synthetic events), attaches to the JVM,
// lets the key-bind logic handle the event, and passes keys/buttons on to the registered key listeners.
// Returns true when the event was consumed.
struct InEvt { int type; int code; int padding_08; unsigned char down, synthetic; };
struct VmFns { void *fn[8]; };
extern "C" VmFns **init_javavm; // JavaVM*
extern "C" void *java_natives_world_key_state_table_one_byte, *java_natives_frame_profile_key_binds_key_profile, *java_natives_frame_module_key_binds_key_module; // key states, listener maps (typed / always)
extern "C" char java_natives_world_module_manager[]; // module manager
extern "C" char *ptr_to_gui_module_descriptor, *ptr_to_friends_module_descriptor; // settings blocks (+0x14: flag)
FN(unsigned char, gui_handle_input_event_entry, (InEvt *e, unsigned char unchanged, void *mgr));
FN(void *, get_minecraft, (Jvm *j));
FN(void, friends_friends_friend_list, (Jvm *jvm, void *mgr));
FN(bool, key_map_dispatch, (void *map, int id, int arg));
extern "C" bool on_input_event(InEvt *e) {
bool unchanged = true;
if (e->type == 2) {
unsigned char *keys = (unsigned char *)java_natives_world_key_state_table_one_byte;
unsigned code = e->code;
unsigned char cur = keys[code];
unsigned char was = cur & 1;
unsigned char down = e->down;
if (!(e->synthetic == 1 && was == down)) {
if (e->synthetic == 1) down = (down | (cur & 2)) ^ 2;
keys[code] = down;
unchanged = was == e->down;
}
}
void *env;
if (((int (*)(void *, void **, int))(*init_javavm)->fn[6])(init_javavm, &env, 0x10008)) return false;
Jvm jvm = {java_natives_world_cached_jni_ids, (JNIEnv *)env};
bool r = gui_handle_input_event_entry(e, unchanged, java_natives_world_module_manager);
if (r) return r;
if (e->type != 2 || unchanged) return r;
if (e->code == 0) return r;
if (!is_in_game(&jvm, get_minecraft(&jvm))) return r;
if (ptr_to_gui_module_descriptor[0x14] != 0) return r;
if (ptr_to_friends_module_descriptor[0x14] == 1 && e->type == 2 && e->down == 1 && e->code == 3) friends_friends_friend_list(&jvm, java_natives_world_module_manager);
if (key_map_dispatch(java_natives_frame_profile_key_binds_key_profile, e->code, e->down)) return r;
key_map_dispatch(java_natives_frame_module_key_binds_key_module, e->code, e->down);
return r;
}
+33
View File
@@ -0,0 +1,33 @@
// 0x6000: per-frame tick: rotation-delta tracking and the timed "tick" event (type 1)
#include "slinky/engine.h"
struct Rot { float yaw, pitch; };
extern "C" double tick_rotation_last_time;
extern "C" Rot tick_rotation_previous;
extern "C" const double threshold_0_002;
extern "C" char java_natives_world_module_manager[];
FN(void, update_frame_time, ());
FN(Rot, get_entity_rotation, (void *jvm, void *entity));
FN(Rot, rot_sub, (Rot a, Rot b));
FN(void, add_rotation_delta, (Rot rotation));
FN(float, get_partial_ticks, (void *jvm, void *object));
FN(void, set_rotation_active, (void *jvm, void *player, bool on));
struct TickEvent { long long type; Ctx *ctx; float pt; int z; double dt; char rest[0x28]; };
FN(void, dispatch_event, (void *mgr, TickEvent *e));
extern "C" void tick_modules(Ctx *ctx, void *object, double now) {
if (tick_rotation_last_time == 0.0) tick_rotation_last_time = now;
update_frame_time();
if (!ctx->world || !ctx->player) {
tick_rotation_last_time = now;
return;
}
Rot d = rot_sub(get_entity_rotation(ctx->jvm, ctx->player), tick_rotation_previous);
if (__builtin_bit_cast(unsigned long long, d) & 0x7fffffff7fffffffull) add_rotation_delta(d);
double dt = now - tick_rotation_last_time;
if (threshold_0_002 > dt) return;
float partial_ticks = get_partial_ticks(ctx->jvm, object);
TickEvent e = {1, ctx, partial_ticks, 0, dt, {}};
dispatch_event(java_natives_world_module_manager, &e);
set_rotation_active(ctx->jvm, ctx->player, false);
tick_rotation_last_time = now;
tick_rotation_previous = get_entity_rotation(ctx->jvm, ctx->player);
}
+278
View File
@@ -0,0 +1,278 @@
// 0xf000: chat command check, GUI font/style setup
#include "slinky/engine.h"
#include "slinky/jids.h"
FN(jstring, java_get_c14_packet_tab_complete, (Jvm *jvm, void *c14_packet_tab_complete));
// does the text of obj start with '!'?
extern "C" bool text_starts_with_bang(Jvm **c, void *obj) {
JNIEnv *env = (*c)->env;
jstring c14_packet_tab_complete = java_get_c14_packet_tab_complete(*c, obj);
bool r;
if ((*env)->GetStringLength(env, c14_packet_tab_complete)) {
const jchar *p = (const jchar *)(*env)->GetStringCritical(env, c14_packet_tab_complete, 0);
r = *p == '!';
(*env)->ReleaseStringCritical(env, c14_packet_tab_complete, p);
} else {
r = false;
}
return r;
}
// ---- chat commands: "!word word arg arg" walks the command tree, parses up to 3 typed arguments and runs the
// node's handler; prints help (sub-commands) or usage on errors. "!!text" sends "!text" as a normal message. ----
struct CBuf { char *p; unsigned long long cap, len; };
union CVal { struct { const char *p; unsigned long long n; } s; unsigned long long i; unsigned char b; };
struct CArg { const char *name; int type; unsigned char opt; char padding_0d[3]; CVal dflt; }; // type 1 str, 2 uint, 3 bool
struct CNode { const char *name, *desc; CArg args[3]; void (*run)(Jvm **c, CVal *v); CNode *sub; };
extern "C" CNode chat_fonts_root; // root
FN(void *, java_get_c01_packet_chat_message, (Jvm *jvm, void *msg));
FN(void, java_set_c01_packet_chat_message, (Jvm *jvm, void *msg, jstring s));
FN(void, buffer_init, (CBuf *b, char *p, unsigned long long cap));
FN(bool, buffer_printf, (CBuf *b, const char *fmt, ...));
FN(bool, buffer_push_char, (CBuf *b, char character));
FN(bool, buffer_terminate, (CBuf *b));
FN(unsigned long long, libc_strlen, (const char *str));
FN(void *, libc_memcpy_alt, (void *dest, const void *src, unsigned long long n));
FN(int, libc_strncmp_alt, (const void *left, const void *right, unsigned long long n));
extern "C" void jni_throw_format(Jvm *jvm, const char *fmt, ...);
extern "C" const char str_s_in_chat_fonts[], str_sect_7_s[], str_sect_f_s_s_sect_7_s[], str_sect_f_message_sect_7_send_sec[], str_sect_cmissing_argument_sect_o[], str_sect_cargument_sect_o_ssect_c[],
str_sect_cargument_sect_o_ssect_c_in_chat_fonts[], str_sect_ctoo_many_arguments_provi[], str_sect_c_s[], str_csect_o_ssect_c_s_c[];
extern "C" const char *const ptr_to_str_string[]; // type names
extern "C" bool handle_chat_message(Jvm **c, void *msg) {
JNIEnv *env = (*c)->env;
jstring c01_packet_chat_message = (jstring)java_get_c01_packet_chat_message(*c, msg);
if (!c01_packet_chat_message) return false;
const char *utf = (*env)->GetStringUTFChars(env, c01_packet_chat_message, 0);
if (!utf) return false;
char buf[0x1a0];
unsigned long long ulen = libc_strlen(utf);
if (ulen >= sizeof buf) ulen = sizeof buf - 1;
libc_memcpy_alt(buf, utf, ulen);
buf[ulen] = 0;
(*env)->ReleaseStringUTFChars(env, c01_packet_chat_message, utf);
if (!buf[0] || buf[0] != '!') return false;
char *p = buf + 1;
if (buf[1] == '!') {
java_set_c01_packet_chat_message(*c, msg, (*env)->NewStringUTF(env, p));
return false;
}
char pb[0x100], ubuf[0x100];
CBuf path;
buffer_init(&path, pb, 0x100);
buffer_push_char(&path, '!');
unsigned long long L = libc_strlen(p), pos = 0;
CNode *node = &chat_fonts_root;
Jvm *jvm;
const char *err;
CVal v[3];
unsigned long long i;
// command words
while (node->sub) {
if (pos == L) goto help;
unsigned long long we = pos;
while (we < L && p[we] != ' ') we++;
unsigned long long nx = we;
while (nx < L && p[nx] == ' ') nx++;
unsigned long long wl = we - pos;
CNode *sub = node->sub;
for (; sub->name; sub++)
if (libc_strlen(sub->name) == wl && libc_strncmp_alt(sub->name, p + pos, wl) == 0) break;
if (!sub->name) goto help;
buffer_printf(&path, str_s_in_chat_fonts /* "%s " */, sub->name);
pos = nx;
node = sub;
}
// arguments
jvm = *c;
for (i = 0; i != 3; i++) {
CArg *a = &node->args[i];
if (!a->name) break;
if (pos == L) {
if (!(a->opt & 1)) {
err = str_sect_cmissing_argument_sect_o; // missing argument
goto usage;
}
if (a->type == 3) v[i].b = a->dflt.b & 1;
else if (a->type == 2) v[i].i = a->dflt.i;
else if (a->type == 1) v[i] = a->dflt;
continue;
}
unsigned long long we = pos;
while (we < L && p[we] != ' ') we++;
unsigned long long nx = we;
while (nx < L && p[nx] == ' ') nx++;
const char *w = p + pos;
unsigned long long wl = we - pos;
if (a->type == 3) {
unsigned d = (unsigned char)w[0] - 0x46u;
if (d > 0x33) goto badbool;
if ((0x10100000101ull >> d) & 1) v[i].b = 0; // F N f n
else if ((0x8400000084000ull >> d) & 1) v[i].b = 1; // T Y t y
else goto badbool;
} else if (a->type == 2) {
unsigned long long x = 0;
for (unsigned long long k = 0; k < wl; k++) {
unsigned char ch = (unsigned char)w[k];
if ((unsigned char)(ch - ':') < 0xf6) goto badint;
unsigned long long y = x * 10 + (ch & 0xf);
if (y < x) goto badint;
x = y;
}
v[i].i = x;
} else if (a->type == 1) {
v[i].s.p = w;
v[i].s.n = wl;
}
pos = nx;
}
if (pos < L) {
jni_throw_format(jvm, str_sect_ctoo_many_arguments_provi); // too many arguments
goto usage_line;
}
node->run(c, v);
return true;
badbool:
err = str_sect_cargument_sect_o_ssect_c_in_chat_fonts;
goto usage;
badint:
err = str_sect_cargument_sect_o_ssect_c;
usage:
jni_throw_format(jvm, err);
usage_line : {
buffer_terminate(&path);
CBuf u;
buffer_init(&u, ubuf, 0x100);
buffer_printf(&u, str_sect_c_s, path.p);
for (int k = 0; k < 3; k++) {
CArg *a = &node->args[k];
if (!a->name) continue;
bool o = a->opt & 1;
buffer_printf(&u, str_csect_o_ssect_c_s_c, o ? '[' : '<', a->name, ptr_to_str_string[(unsigned)a->type], o ? ']' : '>');
}
buffer_terminate(&u);
jni_throw_format(jvm, ubuf);
return true;
}
help : {
buffer_terminate(&path);
jvm = *c;
jni_throw_format(jvm, str_sect_7_s, node->desc);
for (CNode *ch = node->sub; ch->name; ch++) jni_throw_format(jvm, str_sect_f_s_s_sect_7_s, path.p, ch->name, ch->desc);
if (node == &chat_fonts_root) jni_throw_format(jvm, str_sect_f_message_sect_7_send_sec);
return true;
}
}
// ---- GLFW key code -> client key code (0 = unmapped). Counterpart of windows_key_to_client_key (Windows virtual keys). ----
extern "C" int glfw_key_to_client_key(int key) {
switch (key) {
case 0x20: return 0x58; // space
case 0x27: return 0x3e; // '
case 0x2c: return 0x36; // ,
case 0x2d: case 0x14d: return 0x37; // - (also keypad -)
case 0x2e: case 0x14a: return 0x38; // . (also keypad .)
case 0x2f: case 0x14b: return 0x39; // / (also keypad /)
case 0x30 ... 0x39: return key - 0x2a; // 0-9
case 0x3b: return 0x34; // ;
case 0x3d: case 0x14e: case 0x150: return 0x35; // = (also keypad)
case 0x41 ... 0x5a: return key - 0x27; // A-Z
case 0x5b: return 0x3b;
case 0x5c: return 0x3c;
case 0x5d: return 0x3d;
case 0x60: return 0x3a;
case 0x100: return 0x57; // escape
case 0x101: case 0x14f: return 0x55; // enter (also keypad)
case 0x102: return 0x54; // tab
case 0x103: return 0x53; // backspace
case 0x104: return 0x5e; // insert
case 0x105: return 0x5f; // delete
case 0x106: return 0x41; // right
case 0x107: return 0x3f; // left
case 0x108: return 0x42; // down
case 0x109: return 0x40; // up
case 0x10a: return 0x59; // page up
case 0x10b: return 0x5a; // page down
case 0x10c: return 0x5c; // home
case 0x10d: return 0x5b; // end
case 0x118: return 0x56; // caps lock
case 0x119: return 0x61; // scroll lock
case 0x11a: return 0x60; // num lock
case 0x11b: return 0x5d; // print screen
case 0x122 ... 0x131: return key - 0xdf; // F1-F16
case 0x140 ... 0x149: return key - 0x130; // keypad 0-9
case 0x154: return 0x62; // left shift
case 0x155: return 0x64; // left control
case 0x156: return 0x66; // left alt
case 0x158: return 0x63; // right shift
case 0x159: return 0x65; // right control
case 0x15a: return 0x67; // right alt
default: return 0;
}
}
// copy the two built-in chat commands (descriptors in .rdata) into the root command's sub-command array
extern "C" const CNode *ptr_to_rdata_1531c0, *ptr_to_rdata_151518;
FN(void *, libc_calloc, (unsigned long long n, unsigned long long size));
extern "C" int init_chat_commands() {
CNode *cmds = (CNode *)libc_calloc(3, sizeof(CNode)); // two commands + a zero terminator
if (!cmds) return 0x3ed;
cmds[0] = *ptr_to_rdata_1531c0;
cmds[1] = *ptr_to_rdata_151518;
chat_fonts_root.sub = cmds;
return 0;
}
// next token of a text reader: returns a pointer to it and its length in *len, then skips the delimiters after it
struct TextReader { const char *data; unsigned long long end, pos; };
extern "C" const char *text_reader_next(TextReader *r, char delim, unsigned long long *len) {
if (r->pos == r->end) return 0;
unsigned long long start = r->pos, p = start;
const char *data = r->data;
while (p < r->end && data[p] != delim) r->pos = ++p;
*len = p - start;
while (r->pos < r->end && data[r->pos] == delim) r->pos++;
return data + start;
}
// load a font file into slot `slot` of the font table (0x1772 on failure)
FN(int, nvg_create_font_mem, (void *nvg, const char *path, const char *name, int size, int flags));
extern "C" int find_glfw_entrypoint(void **ctx, unsigned slot, const char *path, const char *name, int size) {
int id = nvg_create_font_mem(ctx[0], path, name, size, 0);
if (id == -1) return 0x1772;
((int *)ctx)[0x10 + slot] = id;
return 0;
}
// ---- small helpers the first gap scan missed ----
extern "C" void *chat_font_context;
FN(void, libc_free, (void *ptr));
FN(unsigned long long, libc_strlen, (const char *text));
FN(void *, libc_memcpy_alt, (void *dest, const void *src, unsigned long long n));
extern "C" void destroy_chat_commands() {
libc_free(chat_fonts_root.sub);
chat_fonts_root.sub = 0;
}
// bounded memory cursor {data, size, pos}: the sink of the output helpers (fwrite-like mem_cursor_write)
struct MemCursor { char *data; unsigned long long size, pos; };
extern "C" MemCursor *mem_cursor_init(MemCursor *c, char *data, unsigned long long size) {
c->data = data;
c->size = size;
c->pos = 0;
return c;
}
extern "C" MemCursor *text_reader_init(MemCursor *c, char *text) {
unsigned long long n = libc_strlen(text);
c->data = text;
c->size = n;
c->pos = 0;
return c;
}
extern "C" bool text_reader_has_more(const MemCursor *c) { return c->pos < c->size; }
// fwrite onto the cursor: append `count` records of `size` bytes if they all fit, returns the bytes written
extern "C" unsigned long long mem_cursor_write(const void *src, unsigned long long size, unsigned long long count, MemCursor *c) {
unsigned long long n = size * count;
if (c->pos + n > c->size) return 0;
libc_memcpy_alt(c->data + c->pos, src, n);
c->pos += n;
return n;
}
+344
View File
@@ -0,0 +1,344 @@
// 0x1f000: class-file constant pool getters (single exit, like the original)
#include "slinky/engine.h"
#include "slinky/classfile.h"
// CONSTANT_Utf8
extern "C" __attribute__((noinline)) int constant_utf8(CP *constant_pool, unsigned short idx, Utf8 *out) {
const unsigned char *end, *lim;
int error_code = 0x2afd;
CP_ENTRY(constant_pool, idx, end, lim, out_);
if (end + 1 > lim) goto trunc;
error_code = 0x2afe;
if (*end != 1) goto out_;
{
const unsigned char *cursor = end + 3;
if (cursor > lim) goto trunc;
unsigned short len = be16(end + 1);
error_code = 0x2af8;
if (cursor + len > lim) goto out_;
error_code = 0;
if (out) {
out->len = len;
out->p = cursor;
}
}
goto out_;
trunc:
error_code = 0x2af8;
out_:
return error_code;
}
// CONSTANT_Class -> its name
extern "C" __attribute__((noinline)) int constant_class_name(CP *constant_pool, unsigned short idx, Utf8 *out) {
const unsigned char *end, *lim, *u, *ulim;
int error_code = 0x2afd;
CP_ENTRY(constant_pool, idx, end, lim, out_);
if (end + 1 > lim) goto trunc;
if (*end != 7) goto tag;
if (end + 3 > lim) goto trunc;
{
unsigned short ni = be16(end + 1);
CP_ENTRY(constant_pool, ni, u, ulim, out_);
if (u + 1 > ulim) goto trunc;
error_code = 0x2afe;
if (*u != 1) goto out_;
const unsigned char *cursor = u + 3;
if (cursor > ulim) goto trunc;
unsigned short len = be16(u + 1);
error_code = 0x2af8;
if (cursor + len > ulim) goto out_;
error_code = 0;
if (out) {
out->len = len;
out->p = cursor;
}
}
goto out_;
tag:
error_code = 0x2afe;
goto out_;
trunc:
error_code = 0x2af8;
out_:
return error_code;
}
// CONSTANT_NameAndType -> name + descriptor
extern "C" __attribute__((noinline)) int constant_name_and_type(CP *constant_pool, unsigned short idx, NameType *out) {
const unsigned char *end, *lim, *u, *ulim;
int error_code = 0x2afd;
CP_ENTRY(constant_pool, idx, end, lim, out_);
if (end + 1 > lim) goto trunc;
if (*end != 0xc) goto tag;
if (end + 3 > lim || end + 5 > lim) goto trunc;
{
unsigned short ni = be16(end + 1);
CP_ENTRY(constant_pool, ni, u, ulim, out_);
if (u + 1 > ulim) goto trunc;
error_code = 0x2afe;
if (*u != 1) goto out_;
const unsigned char *cursor = u + 3;
if (cursor > ulim) goto trunc;
unsigned short len = be16(u + 1);
error_code = 0x2af8;
if (cursor + len > ulim) goto out_;
unsigned short di = be16(end + 3);
if (out) {
out->name.len = len;
out->name.p = cursor;
}
return constant_utf8(constant_pool, di, &out->desc);
}
tag:
error_code = 0x2afe;
goto out_;
trunc:
error_code = 0x2af8;
out_:
return error_code;
}
struct WBuf { unsigned char *p, *end; };
extern "C" int classfile_write_u1(WBuf *w, unsigned char b) {
unsigned char *cursor = w->p;
if (cursor + 1 > w->end) return 0x2af9;
*cursor = b;
w->p = cursor + 1;
return 0;
}
struct RBuf { const unsigned char *p, *end; };
extern "C" int classfile_read_u1(RBuf *r, unsigned char *out) {
const unsigned char *cursor = r->p;
if (cursor + 1 > r->end) return 0x2af8;
if (out) *out = *cursor;
r->p = cursor + 1;
return 0;
}
extern "C" int classfile_read_u2(RBuf *r, unsigned short *out) {
const unsigned char *cursor = r->p;
if (cursor + 2 > r->end) return 0x2af8;
if (out) *out = be16(cursor);
r->p = cursor + 2;
return 0;
}
extern "C" int classfile_write_u2(WBuf *w, unsigned short v) {
unsigned char *cursor = w->p;
if (cursor + 2 > w->end) return 0x2af9;
cursor[0] = (unsigned char)(v >> 8);
w->p[1] = (unsigned char)v;
w->p = cursor + 2;
return 0;
}
#include "slinky/jnienv.h"
extern "C" const char str_loadclass[], str_ljava_lang_string_ljava_lang_c[]; // "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"
struct LoaderRef { jobject loader; jmethodID loadClass; };
extern "C" int make_secure_class_loader(LoaderRef *o, JNIEnv *env, jobject loader) {
jclass cls = (*env)->GetObjectClass(env, loader);
jmethodID mid = (*env)->GetMethodID(env, cls, str_loadclass, str_ljava_lang_string_ljava_lang_c);
if (!mid) return 0x7d4;
jobject ref = (*env)->NewGlobalRef(env, loader);
if (!ref) return 0x7d5;
o->loader = ref;
o->loadClass = mid;
return 0;
}
FN(int, constant_class_name, (CP *cp, unsigned short idx, Utf8 *out));
// CONSTANT_Methodref / InterfaceMethodref (tags 10, 11) -> class, name, descriptor
extern "C" int constant_pool_member_ref2(CP *constant_pool, unsigned short idx, MemberRef *out) {
const unsigned char *end, *lim;
int error_code = 0x2afd;
CP_ENTRY(constant_pool, idx, end, lim, out_);
if (end + 1 > lim) goto trunc;
error_code = 0x2afe;
if ((*end & 0xfe) != 10) goto out_;
if (end + 3 > lim) goto trunc;
error_code = 0x2af8;
if (end + 5 > lim) goto out_;
{
unsigned short ci = be16(end + 1);
unsigned char hi = end[3], lo = end[4];
error_code = constant_class_name(constant_pool, ci, &out->cls);
if (error_code) goto out_;
return constant_name_and_type(constant_pool, (unsigned short)((hi << 8) | lo), &out->nt);
}
trunc:
error_code = 0x2af8;
out_:
return error_code;
}
FN(jclass, find_class, (jobject loader, JNIEnv *env, const char *name));
extern "C" const char str_net_minecraft_client_minecraft_in_classfile_constants[], str_field_71439_g_in_classfile_constants[], str_lnet_minecraft_client_entity_e_in_classfile_constants[], str_lnet_minecraft_client_entity_e_in_classfile_constants_a[], str_theplayer_in_classfile_constants[],
str_bao_in_classfile_constants[], str_i_in_classfile_constants[], str_ljava_io_file[], str_ave_in_classfile_constants[], str_n_in_classfile_constants[];
struct Mapping { int player; int names; unsigned char padding_08; }; // player: 0 = ClientPlayerMP, 1 = PlayerSP; names: 0/1 obf, 1 MCP, 2 SRG
// detect which mapping set the game uses (SRG / MCP / obfuscated 1.7 / 1.8)
extern "C" int fill_jni_ids(Mapping *out, jobject loader, JNIEnv *env) {
jclass mc = find_class(loader, env, str_net_minecraft_client_minecraft_in_classfile_constants); // net.minecraft.client.Minecraft
if (mc) {
if ((*env)->GetFieldID(env, mc, str_field_71439_g_in_classfile_constants, str_lnet_minecraft_client_entity_e_in_classfile_constants)) { *(long long *)out = 0x200000000ll; return 0; }
(*env)->ExceptionClear(env);
if ((*env)->GetFieldID(env, mc, str_field_71439_g_in_classfile_constants, str_lnet_minecraft_client_entity_e_in_classfile_constants_a)) { *(long long *)out = 0x200000001ll; return 0; }
(*env)->ExceptionClear(env);
if ((*env)->GetFieldID(env, mc, str_theplayer_in_classfile_constants, str_lnet_minecraft_client_entity_e_in_classfile_constants)) { *(long long *)out = 0x100000000ll; out->padding_08 = 0; return 0; }
(*env)->ExceptionClear(env);
if ((*env)->GetFieldID(env, mc, str_theplayer_in_classfile_constants, str_lnet_minecraft_client_entity_e_in_classfile_constants_a)) { *(long long *)out = 0x100000001ll; out->padding_08 = 0; return 0; }
(*env)->ExceptionClear(env);
return 0x3eb;
}
(*env)->ExceptionClear(env);
jclass c = find_class(loader, env, str_bao_in_classfile_constants); // "bao" (1.7 obf)
if (c && (*env)->GetFieldID(env, c, str_i_in_classfile_constants, str_ljava_io_file)) { *(long long *)out = 0; return 0; }
(*env)->ExceptionClear(env);
c = find_class(loader, env, str_ave_in_classfile_constants); // "ave" (1.8 obf)
if (c && (*env)->GetFieldID(env, c, str_n_in_classfile_constants, str_ljava_io_file)) { *(long long *)out = 1; return 0; }
(*env)->ExceptionClear(env);
return 0x3eb;
}
extern "C" const char str_java_lang_thread[], str_getallstacktraces[], str_ljava_util_map_in_classfile_constants[], str_java_util_map_in_classfile_constants[], str_keyset[], str_ljava_util_set[],
str_java_util_collection[], str_toarray[], str_ljava_lang_object[], str_getname_in_classfile_constants[], str_ljava_lang_string_in_classfile_constants[], str_client_thread[], str_getcontextclassloader[], str_ljava_lang_classloader[];
extern "C" int libc_strcmp(const char *left, const char *right); // strcmp
// find the game's "Client thread" and keep a global ref to its context class loader (+ loadClass)
extern "C" int find_game_class_loader(LoaderRef *out, JNIEnv *env) {
jclass T = (*env)->FindClass(env, str_java_lang_thread);
if (!T) return 0x7d0;
jmethodID all = (*env)->GetStaticMethodID(env, T, str_getallstacktraces, str_ljava_util_map_in_classfile_constants);
if (!all) return 0x7d0;
jclass M = (*env)->FindClass(env, str_java_util_map_in_classfile_constants);
if (!M) return 0x7d0;
jmethodID keySet = (*env)->GetMethodID(env, M, str_keyset, str_ljava_util_set);
if (!keySet) return 0x7d0;
jobject map = (*env)->CallStaticObjectMethod(env, T, all);
jobject set = (*env)->CallObjectMethod(env, map, keySet);
jclass C = (*env)->FindClass(env, str_java_util_collection);
if (!C) return 0x7d0;
jmethodID toArray = (*env)->GetMethodID(env, C, str_toarray, str_ljava_lang_object);
if (!toArray) return 0x7d0;
jmethodID getName = (*env)->GetMethodID(env, T, str_getname_in_classfile_constants, str_ljava_lang_string_in_classfile_constants);
if (!getName) return 0x7d0;
jobjectArray arr = (jobjectArray)(*env)->CallObjectMethod(env, set, toArray);
jsize n = (*env)->GetArrayLength(env, arr);
if (n <= 0) return 0x3ea;
jobject th;
for (int i = 0;; i++) {
th = (*env)->GetObjectArrayElement(env, arr, i);
jstring name = (jstring)(*env)->CallObjectMethod(env, th, getName);
const char *s = (*env)->GetStringUTFChars(env, name, 0);
int c = libc_strcmp(s, str_client_thread); // "Client thread"
(*env)->ReleaseStringUTFChars(env, name, s);
if (c == 0) break;
if (n == i + 1) return 0x3ea;
}
jmethodID gccl = (*env)->GetMethodID(env, T, str_getcontextclassloader, str_ljava_lang_classloader);
if (!gccl) return 0x7d0;
jobject loader = (*env)->CallObjectMethod(env, th, gccl);
jclass lc = (*env)->GetObjectClass(env, loader);
jmethodID load = (*env)->GetMethodID(env, lc, str_loadclass, str_ljava_lang_string_ljava_lang_c);
if (!load) return 0x7d4;
jobject ref = (*env)->NewGlobalRef(env, loader);
if (!ref) return 0x7d5;
out->loader = ref;
out->loadClass = load;
return 0;
}
extern "C" void *class_loader_ref(void **p) { return *p; }
// load class `name` (slashes or dots) through the game's class loader: loader.loadClass(name with dots)
FN(unsigned long long, libc_strlen, (const char *str));
FN(void *, libc_malloc, (unsigned long long n)); // malloc
FN(void, libc_free, (void *ptr)); // free
extern "C" __attribute__((noinline)) jclass find_class(jobject lr, JNIEnv *env, const char *name) {
LoaderRef *o = (LoaderRef *)lr;
unsigned long long n = libc_strlen(name);
char *buf = (char *)libc_malloc(n + 1);
if (!buf) return 0;
#pragma clang loop vectorize(disable) interleave(disable)
for (unsigned long long i = 0; i < n; i++) buf[i] = name[i] == '/' ? '.' : name[i];
buf[n] = 0;
jstring s = (*env)->NewStringUTF(env, buf);
libc_free(buf);
if (!s) return 0;
jclass c = (jclass)(*env)->CallObjectMethod(env, o->loader, o->loadClass, s);
(*env)->DeleteLocalRef(env, s);
if ((*env)->ExceptionCheck(env)) {
(*env)->ExceptionClear(env); // class not found: the caller decides what to do
return 0;
}
return c;
}
// ---- gap-scan functions of the class-file reader block ----
// release a global ref: DeleteGlobalRef(env, *ref); *ref = 0
extern "C" void class_file_scan(jobject *ref, JNIEnv *env) {
if (*ref) {
(*env)->DeleteGlobalRef(env, *ref);
*ref = 0;
}
}
// which Minecraft is it? the obfuscated main class "bao" (field I: File) or "ave" (field N: File) -> *kind 0 / 1
// (0x3eb: neither found)
extern "C" const char str_bao_in_classfile_constants[], str_i_in_classfile_constants[], str_ljava_io_file[], str_ave_in_classfile_constants[], str_n_in_classfile_constants[];
extern "C" int class_define(long long *kind, jobject loader, JNIEnv *env) {
jclass c = find_class(loader, env, str_bao_in_classfile_constants /* "bao" */);
if (c && (*env)->GetFieldID(env, c, str_i_in_classfile_constants /* "I" */, str_ljava_io_file /* "Ljava/io/File;" */)) {
*kind = 0;
return 0;
}
(*env)->ExceptionClear(env);
c = find_class(loader, env, str_ave_in_classfile_constants /* "ave" */);
if (c && (*env)->GetFieldID(env, c, str_n_in_classfile_constants /* "N" */, str_ljava_io_file)) {
*kind = 1;
return 0;
}
(*env)->ExceptionClear(env);
return 0x3eb;
}
FN(void *, libc_memcpy_alt, (void *dest, const void *src, unsigned long long n));
// read n bytes into dst (may be NULL to skip) / a big-endian u32
extern "C" int classfile_read_bytes(RBuf *r, unsigned n, void *dst) {
const unsigned char *cursor = r->p, *end = cursor + n;
if (end > r->end) return 0x2af8;
if (dst) libc_memcpy_alt(dst, cursor, n);
r->p = end;
return 0;
}
extern "C" int classfile_read_constant(RBuf *r, unsigned *out) {
const unsigned char *cursor = r->p, *end = cursor + 4;
if (end > r->end) return 0x2af8;
if (out) *out = __builtin_bswap32(*(const unsigned *)cursor);
r->p = end;
return 0;
}
// Fieldref/Methodref-style entry whose tag satisfies `pred` -> class, name, descriptor (general form of constant_pool_member_ref/constant_pool_member_ref2)
FN(int, constant_name_and_type, (CP *cp, unsigned short idx, NameType *out));
extern "C" int constant_pool_find_if(CP *constant_pool, bool (*pred)(unsigned char), unsigned short idx, MemberRef *out) {
const unsigned char *end, *lim;
int error_code = 0x2afd;
CP_ENTRY(constant_pool, idx, end, lim, out_);
if (end + 1 > lim) goto trunc;
{
unsigned char tag = *end;
error_code = 0x2afe;
if (!pred(tag)) goto out_;
}
if (end + 3 > lim) goto trunc;
error_code = 0x2af8;
if (end + 5 > lim) goto out_;
{
unsigned short ci = be16(end + 1);
unsigned char hi = end[3], lo = end[4];
error_code = constant_class_name(constant_pool, ci, &out->cls);
if (error_code) goto out_;
return constant_name_and_type(constant_pool, (unsigned short)((hi << 8) | lo), &out->nt);
}
trunc:
error_code = 0x2af8;
out_:
return error_code;
}
@@ -0,0 +1,452 @@
// 0xe000: comparators, static tables
#include "slinky/engine.h"
extern "C" int libc_strcmp(const char *left, const char *right);
extern "C" int compare_cstr_ptr(const char **left, const char **right) {
const char *x = *left;
return libc_strcmp(x, *right);
}
extern "C" int compare_int_field(const int *left, const char *right) { return *left - *(const int *)(right + 8); }
extern "C" int compare_int(const int *left, const int *right) { return *left - *right; }
extern "C" int compare_cstr_desc(const char **left, const char **right) {
const char *x = *left;
return libc_strcmp(x, *right);
}
extern "C" char default_name_list_data[], default_key_list_data[];
extern "C" void default_name_list(void **p, unsigned long long *n) { *p = default_name_list_data; *n = 0x181; }
extern "C" void default_key_list(void **p, unsigned long long *n) { *p = default_key_list_data; *n = 0xc6; }
extern "C" int compare_long_field(const char **left, const char **right) {
long long y = *(const long long *)((const char *)right + 0x30);
if (*(const long long *)((const char *)left + 0x30) < y) return -1;
if (*(const long long *)((const char *)left + 0x30) > y) return 1;
const char *x = *left;
return libc_strcmp(x, *right);
}
FN(void *, libc_bsearch, (const void *key, const void *base, unsigned long long n, unsigned long long size, int (*cmp)(const void *, const void *)));
struct MEnt { int id; int padding_04; unsigned long long n; void *capacity; void **list; };
struct IdMap { char padding_00[0x10]; unsigned long long count; char padding_18[8]; MEnt *data; unsigned char multi; };
// lookup id -> value (multi-maps: test membership of val)
extern "C" void *key_map_get(IdMap *m, int id, void *val) {
if (!id) return 0;
MEnt k = {};
k.id = id;
if (!m->data) return 0;
MEnt *map_entry = (MEnt *)libc_bsearch(&k, m->data, m->count, 0x20, (int (*)(const void *, const void *))compare_int);
if (!map_entry) return 0;
unsigned long long n = map_entry->n;
if (m->multi == 1) {
for (unsigned long long i = 0; i < n; i++) {
void *x = map_entry->list[i];
if (x == val) return x;
}
return 0;
}
if (!n) return 0;
return map_entry->list[0];
}
struct CbMap { unsigned char (*fn)(void *item, int id, int arg); char padding_08[8]; unsigned long long count; char padding_18[8]; MEnt *data; };
// call fn on every item registered under id; true if any returned true
extern "C" bool key_map_dispatch(CbMap *m, int id, int arg) {
MEnt k = {};
k.id = id;
unsigned char r = 0;
if (m->data) {
MEnt *map_entry = (MEnt *)libc_bsearch(&k, m->data, m->count, 0x20, (int (*)(const void *, const void *))compare_int);
if (map_entry && map_entry->n) {
for (unsigned long long i = 0; i < map_entry->n; i++) r |= m->fn(map_entry->list[i], id, arg);
}
}
return r & 1;
}
// ---- id -> value(s) map (sorted 32-byte entries; "multi" maps keep a list per id) ----
FN(unsigned char, vector_grow, (void *data, unsigned long long elem, unsigned long long n, void *cap));
FN(void, libc_free, (void *ptr));
FN(void *, libc_memmove, (void *dest, const void *src, unsigned long long n));
struct IdMap2 { void *fn; void (*release)(void *v); unsigned long long count, cap; MEnt *data; unsigned char multi; };
extern "C" bool key_map_add(IdMap2 *m, int id, void *val) {
if (!id) return true;
MEnt k = {};
k.id = id;
MEnt *map_entry = m->data ? (MEnt *)libc_bsearch(&k, m->data, m->count, 0x20, (int (*)(const void *, const void *))compare_int) : 0;
if (!map_entry) {
unsigned long long idx = 0;
if (m->count) {
for (idx = 0; idx < m->count; idx++)
if (id - m->data[idx].id <= 0) break;
}
if (!vector_grow(&m->data, 0x20, m->count, &m->cap)) return false;
MEnt *base = m->data;
libc_memmove(base + idx + 1, base + idx, (m->count - idx) * 0x20);
base[idx] = MEnt{};
base[idx].id = id;
m->count++;
if (!m->data) return false;
map_entry = m->data + idx;
}
unsigned long long n = map_entry->n;
if (m->multi == 1) {
for (unsigned long long i = 0; i < n; i++)
if (map_entry->list[i] == val) return true;
} else if (n) {
if (m->release) m->release(map_entry->list[0]);
map_entry->n = 0;
map_entry->capacity = 0;
libc_free(map_entry->list);
map_entry->list = 0;
n = map_entry->n;
}
if (!vector_grow(&map_entry->list, 8, n, &map_entry->capacity)) return false;
map_entry->list[map_entry->n] = val;
map_entry->n++;
return true;
}
extern "C" void key_map_remove(IdMap2 *m, int id, void *val) {
if (!id) return;
MEnt k = {};
k.id = id;
if (!m->data) return;
MEnt *map_entry = (MEnt *)libc_bsearch(&k, m->data, m->count, 0x20, (int (*)(const void *, const void *))compare_int);
if (!map_entry) return;
unsigned long long n0 = map_entry->n;
if (!n0) return;
if (m->multi == 1) {
void **list = map_entry->list;
unsigned long long i;
for (i = 0; i < n0; i++)
if (list[i] == val) break;
if (i == n0) return;
unsigned long long n = n0;
if (m->release) {
m->release(list[i]);
n = map_entry->n;
list = map_entry->list;
}
map_entry->n = --n;
libc_memmove(&list[i], &list[i + 1], (n - i) * 8);
if (n0 != 1) return;
} else {
if (m->release) m->release(map_entry->list[0]);
}
map_entry->n = 0;
map_entry->capacity = 0;
libc_free(map_entry->list);
map_entry->list = 0;
unsigned long long idx = (unsigned long long)(map_entry - m->data);
unsigned long long c = m->count;
m->count = c - 1;
libc_memmove(map_entry, map_entry + 1, (c - idx - 1) * 0x20);
}
// ---- profile list (names + "modified" label + bind), refreshed from the profiles folder ----
FN(void *, libc_calloc, (unsigned long long n, unsigned long long size)); // calloc
FN(void *, libc_memcpy, (void *dest, const void *src, unsigned long long n));
FN(unsigned long long, libc_strlen, (const char *str));
FN(int, libc_snprintf, (char *buf, unsigned long long n, const char *fmt, ...)); // snprintf
FN(void, libc_qsort, (void *base, unsigned long long n, unsigned long long size, int (*cmp)(const void *, const void *)));
FN(int, list_profiles, (void *ctx, int (*callback)(void *fi, void *ctx)));
extern "C" const char str_unknown_modification_date[], str_modified_1_day_ago[], str_modified_today[], str_modified_lld_days_ago[];
struct ProfEnt { char *name; char *end; char desc[0x20]; long long age; int bind; int padding_3c; };
struct ProfList { unsigned long long count, cap; ProfEnt *data; };
struct OldEnt { char *name; int bind; int padding_0c; };
struct OldList { unsigned long long count, cap; OldEnt *data; };
struct FileInfo { const char *name; long long age; };
struct ScanCtx { ProfList *list; OldList *old; };
extern "C" int scan_next_file(FileInfo *fi, ScanCtx *ctx) {
const char *name = fi->name;
unsigned long long n = libc_strlen(name);
char *s = (char *)libc_calloc(n + 1, 1);
if (!s) return 0x3ed;
libc_memcpy(s, name, n);
ProfEnt e = {};
e.name = s;
e.end = s + (n < 0xf ? n : 0xf);
e.age = fi->age;
OldEnt key = {s, 0, 0};
OldList *old = ctx->old;
if (old->data) {
OldEnt *f = (OldEnt *)libc_bsearch(&key, old->data, old->count, 0x10, (int (*)(const void *, const void *))compare_cstr_desc);
if (f) e.bind = f->bind;
}
long long age = fi->age;
if (age == -1) {
__builtin_memcpy(e.desc, str_unknown_modification_date, 0x18);
} else {
long long days = age / 86400;
if (days == 1) __builtin_memcpy(e.desc, str_modified_1_day_ago, 0x18);
else if (days == 0) { __builtin_memcpy(e.desc, str_modified_today, 0x10); *(long long *)(e.desc + 0x10) = 0; }
else libc_snprintf(e.desc, 0x19, str_modified_lld_days_ago, days);
}
ProfList *l = ctx->list;
if (!vector_grow(&l->data, 0x40, l->count, &l->cap)) {
libc_free(s);
return 0x3ed;
}
l->data[l->count] = e;
l->count++;
return 0;
}
extern "C" int load_profiles(ProfList *l) {
OldList old = {0, 0, 0};
int error_code;
if (l->count) {
unsigned long long left = l->count;
for (ProfEnt *e = l->data; ; ) {
if (e->bind) {
const char *nm = e->name;
unsigned long long n = libc_strlen(nm);
char *s = (char *)libc_calloc(n + 1, 1);
if (!s) { error_code = 0x3ed; goto cleanup; }
libc_memcpy(s, nm, n);
int bind = e->bind;
if (vector_grow(&old.data, 0x10, old.count, &old.cap)) {
old.data[old.count].name = s;
old.data[old.count].bind = bind;
old.data[old.count].padding_0c = 0;
old.count++;
}
}
if (!--left) break;
e = l->data + (l->count - left);
}
if (old.data) libc_qsort(old.data, old.count, 0x10, (int (*)(const void *, const void *))compare_cstr_desc);
for (unsigned long long i = 0; i < l->count; i++) libc_free(l->data[i].name);
l->count = 0;
l->cap = 0;
libc_free(l->data);
l->data = 0;
}
{
ScanCtx ctx = {l, &old};
error_code = list_profiles(&ctx, (int (*)(void *, void *))scan_next_file);
if (l->count && l->data) libc_qsort(l->data, l->count, 0x40, (int (*)(const void *, const void *))compare_long_field);
}
cleanup:
for (unsigned long long i = 0; i < old.count; i++) libc_free(old.data[i].name);
old.count = 0;
old.cap = 0;
libc_free(old.data);
return error_code;
}
// ---- generic dynamic arrays (as 0xdd0..0xdf90): {u32 cap, u32 count} header, element size passed in ----
struct AHdr { unsigned cap, count; };
typedef int (*ACmp)(void *ctx, const void *key, const void *elem);
FN(void *, libc_realloc, (void *ptr, unsigned long long n)); // realloc
extern "C" __attribute__((noreturn)) void libc_abort(); // abort
FN(void *, libc_memcpy_alt, (void *dest, const void *src, unsigned long long n)); // memcpy
// sorted find-or-insert: the existing element equal to key, or key inserted in order
extern "C" void *vector_find_insert(void **data, unsigned elem, AHdr *header, const void *key, ACmp cmp) {
unsigned hi = header->count, idx = 0, cnt = 0;
if (hi) {
char *base = (char *)*data;
unsigned lo = 0;
for (;;) {
unsigned mid = lo + ((hi - lo) >> 1);
int result = cmp(0, key, base + mid * elem);
if (result < 0) {
hi = mid;
if (lo >= hi) break;
} else if (result == 0) {
return (char *)*data + mid * elem;
} else {
lo = mid + 1;
if (lo >= hi) break;
}
}
cnt = header->count;
idx = hi;
}
char *base = (char *)*data;
if (cnt >= header->cap) {
unsigned cap = header->cap ? header->cap * 2 : 8;
header->cap = cap;
void *p = libc_realloc(base, cap * elem);
if (!p) libc_abort();
*data = p;
base = (char *)p;
cnt = header->count;
}
char *at = base + idx * elem;
libc_memmove(base + (idx + 1) * elem, at, (cnt - idx) * elem);
libc_memcpy_alt(at, key, elem);
header->count++;
return at;
}
// erase index i
extern "C" void *vector_erase(void *base, unsigned elem, AHdr *header, unsigned i) {
unsigned n = --header->count;
return libc_memmove((char *)base + i * elem, (char *)base + (i + 1) * elem, (n - i) * elem);
}
// erase the element at p
extern "C" void *vector_erase_value(void *base, unsigned elem, AHdr *header, void *p) {
unsigned i = (unsigned)((unsigned long long)((char *)p - (char *)base) / elem);
unsigned n = --header->count;
return libc_memmove((char *)base + i * elem, (char *)base + (i + 1) * elem, (n - i) * elem);
}
// make room for element n (64-bit sizes): starts at 8, doubles
extern "C" unsigned char vector_grow(void *datav, unsigned long long elem, unsigned long long n, void *capv) {
void **data = (void **)datav;
unsigned long long *cap = (unsigned long long *)capv;
unsigned long long c = *cap;
if (n < c) return true;
void *p;
if (*data) {
c += c;
p = libc_realloc(*data, elem * c);
} else {
c = 8;
p = libc_calloc(8, elem);
}
if (!p) return false;
*data = p;
*cap = c;
return true;
}
// ---- block name <-> id tables (sorted by name / by id, 0x181 entries) ----
struct NameId { const char *name; int id; int padding_0c; };
extern "C" NameId name_id_table_sorted_by_name[], name_id_table_sorted_by_id[];
extern "C" int key_name_to_code(const char *name) {
const char *k = name;
NameId *e = (NameId *)libc_bsearch(&k, name_id_table_sorted_by_name, 0x181, 0x10, (int (*)(const void *, const void *))compare_cstr_ptr);
int result;
if (e) result = e->id;
else result = -1;
SHARED_EPILOG();
return result;
}
extern "C" const char *key_code_to_index(int id) {
int k = id;
NameId *e = (NameId *)libc_bsearch(&k, name_id_table_sorted_by_id, 0x181, 0x10, (int (*)(const void *, const void *))compare_int_field);
const char *r;
if (e) r = e->name;
else r = 0;
SHARED_EPILOG();
return r;
}
// call fn(item, id, 0) for every registered item
extern "C" void invoke_callback(CbMap *m) {
for (unsigned long long i = 0; i < m->count; i++) {
MEnt *map_entry = &m->data[i];
if (!map_entry->n) continue;
unsigned long long j = 0;
do {
void *it = map_entry->list[j];
m->fn(it, map_entry->id, 0);
} while (++j < map_entry->n);
}
}
// ---- profile list accessors ----
struct Profs { unsigned long long count, cap; ProfEnt *data; void *bind; };
extern "C" void *profile_set_bind(Profs *p) { return p->bind; }
extern "C" ProfEnt *profile_array(Profs *p, unsigned long long *n) {
*n = p->count;
return p->count ? p->data : 0;
}
extern "C" ProfEnt *find_profile(Profs *p, const char *name) {
unsigned long long n = p->count;
if (!n) return 0;
ProfEnt *e = p->data;
for (; n; n--, e++)
if (libc_strcmp(name, e->name) == 0) return e;
return 0;
}
// ---- gap-scan functions of the 0xe000 block ----
// grow a ring buffer when full: capacity doubles (16 first); if the contents wrap around, the wrapped part
// [0, tail) is copied behind the old end. Indices are free-running, taken modulo the (power of two) capacity.
struct Ring { unsigned long long cap, head, tail; };
extern "C" void vector_reserve(void **data, unsigned long long elem, Ring *r) {
unsigned long long cap = r->cap;
if (r->tail - r->head < cap) return;
unsigned long long ncap = cap ? cap * 2 : 16;
r->cap = ncap;
void *p = libc_realloc(*data, ncap * elem);
if (!p) libc_abort();
*data = p;
unsigned long long mask = cap - 1;
unsigned long long h = r->head & mask, t = r->tail & mask;
if (h < t) return;
libc_memcpy_alt((char *)p + cap * elem, p, t * elem);
}
// small zeroed records
struct Node30 { void *a, *b; char padding_10[0x18]; unsigned char flag; char padding_29[7]; }; // 0x30
extern "C" Node30 *find_entry_index(void *jvm, void *object, bool flag) {
Node30 *n = (Node30 *)libc_calloc(1, sizeof(Node30));
if (n) {
n->a = jvm;
n->b = object;
n->flag = flag;
}
return n;
}
struct Node20 { char padding_00[0x18]; void *p; }; // 0x20
extern "C" Node20 *count_matching(void *p) {
Node20 *n = (Node20 *)libc_calloc(1, sizeof(Node20));
n->p = p; // (the original does not check the allocation)
return n;
}
// destroy a callback map (see CbMap): run dtor on every registered item, free the lists, the entry array and the map
struct DtorMap { void *fn; void (*dtor)(void *item); unsigned long long count; unsigned long long cap; MEnt *data; };
extern "C" void key_map_destroy(DtorMap *m) {
if (!m) return;
if (m->count) {
void (*dtor)(void *) = m->dtor;
for (unsigned long long i = 0; i < m->count; i++) {
MEnt *map_entry = &m->data[i];
if (dtor && map_entry->n)
#pragma clang loop unroll(disable)
for (unsigned long long j = 0; j < map_entry->n; j++) dtor(map_entry->list[j]);
map_entry->n = 0;
map_entry->capacity = 0;
libc_free(map_entry->list);
map_entry->list = 0;
}
}
m->count = 0;
m->cap = 0;
libc_free(m->data);
libc_free(m);
}
// free a profile list: every entry's name, the entry array and the list itself
extern "C" void destroy_profiles(ProfList *l) {
if (!l) return;
#pragma clang loop unroll(disable)
for (unsigned long long i = 0; i < l->count; i++) libc_free(l->data[i].name);
l->count = 0;
l->cap = 0;
libc_free(l->data);
libc_free(l);
}
// look up the GLFW entry points the client hooks from the already loaded "glfw" module
// (0x3e8: module not loaded, 0x3e9: a function is missing)
FN(void *, kernel32_lookups, (const char *module));
FN(void *, get_proc_address, (void *module, const char *name));
extern "C" void *pfn_glfwgetcurrentcontext, *pfn_glfwgetframebuffersize, *pfn_glfwsetframebuffersizecallback, *pfn_glfwsetmousebuttoncallback, *pfn_glfwsetkeycallback, *pfn_glfwsetcharcallback, *pfn_glfwsetcursorposcallback, *pfn_glfwsetscrollcallback;
extern "C" const char str_glfw[], str_glfwgetcurrentcontext[], str_glfwgetframebuffersize[], str_glfwsetframebuffersizecallback[], str_glfwsetmousebuttoncallback[], str_glfwsetkeycallback[],
str_glfwsetcharcallback[], str_glfwsetcursorposcallback[], str_glfwsetscrollcallback[];
extern "C" int init_glfw_entrypoints() {
void *glfw = kernel32_lookups(str_glfw /* "glfw" */);
if (!glfw) return 0x3e8;
if (!(pfn_glfwgetcurrentcontext = get_proc_address(glfw, str_glfwgetcurrentcontext /* glfwGetCurrentContext */))) return 0x3e9;
if (!(pfn_glfwgetframebuffersize = get_proc_address(glfw, str_glfwgetframebuffersize /* glfwGetFramebufferSize */))) return 0x3e9;
if (!(pfn_glfwsetframebuffersizecallback = get_proc_address(glfw, str_glfwsetframebuffersizecallback /* glfwSetFramebufferSizeCallback */))) return 0x3e9;
if (!(pfn_glfwsetmousebuttoncallback = get_proc_address(glfw, str_glfwsetmousebuttoncallback /* glfwSetMouseButtonCallback */))) return 0x3e9;
if (!(pfn_glfwsetkeycallback = get_proc_address(glfw, str_glfwsetkeycallback /* glfwSetKeyCallback */))) return 0x3e9;
if (!(pfn_glfwsetcharcallback = get_proc_address(glfw, str_glfwsetcharcallback /* glfwSetCharCallback */))) return 0x3e9;
if (!(pfn_glfwsetcursorposcallback = get_proc_address(glfw, str_glfwsetcursorposcallback /* glfwSetCursorPosCallback */))) return 0x3e9;
if (!(pfn_glfwsetscrollcallback = get_proc_address(glfw, str_glfwsetscrollcallback /* glfwSetScrollCallback */))) return 0x3e9;
return 0;
}
+129
View File
@@ -0,0 +1,129 @@
// GUI: module cards - header, settings body, per-card open/close animation.
#include "slinky/engine.h"
#include "slinky/gui_state.h"
FN(void, close_popup, (void *x));
extern "C" void close_popup_now(void *x) { close_popup(x); }
extern "C" void reset_drag_state(GuiState *gui) { gui->input_mode = 0; gui->click_capture = 0; }
struct R16f { float x, y, w, h; };
struct V4f { float x, y, z, w; };
FN(float, libc_powf, (float x, float y)); // powf
FN(void, nvg_GlobalAlpha, (void *nvg, float alpha));
FN(V4f, nvg_rgba_f, (float r, float g, float b, float a));
FN(V4f, nvg_trans_rgba_f, (V4f c, float k));
FN(void, nvg_FillColor, (void *nvg, V4f *c));
FN(R16f, make_rect, (float x, float y, float w, float h));
FN(void, draw_rect_filled, (void *window, R16f *r));
FN(void, nvg_Translate, (void *nvg, float a, float b));
extern "C" const float one_in_gui_cards, five_in_gui_cards, gui_card_header_grey_level, colour_alpha_0_75, gui_card_slide_offset, factor_50;
struct WinA { void *ig; char padding_08[0x10]; float w, h; };
// open/close animation: ease-out alpha, dimmed backdrop, slide offset
extern "C" void card_open_animation(WinA *window, float t) {
float one = one_in_gui_cards;
float alpha = one - libc_powf(one - t, five_in_gui_cards);
void *nvg = window->ig;
nvg_GlobalAlpha(nvg, alpha);
float k = gui_card_header_grey_level;
V4f c = nvg_rgba_f(k, k, k, 1.0f);
V4f c2 = nvg_trans_rgba_f(c, colour_alpha_0_75);
nvg_FillColor(nvg, &c2);
R16f r = make_rect(0.0f, 0.0f, window->w, window->h);
draw_rect_filled(window, &r);
nvg_Translate(nvg, 0.0f, gui_card_slide_offset);
nvg_Translate(nvg, 0.0f, alpha * factor_50);
}
// ---- module cards of one category: header (name, bind button, toggle), description, settings ----
struct Reg2 { void **list; unsigned long long n; };
struct Mgr2 { void *mods; unsigned long long count; Reg2 cat[5]; char padding_60[0x308 - 0x60]; void *set308; };
struct ModC { const char *name; const char *desc; char padding_10[4]; unsigned char enabled; char padding_15[3]; unsigned flags;
int bind; unsigned char hold; char padding_21[7]; char anim[0x10]; float height; char padding_3c[4];
unsigned char expanded; char padding_41[0x1b7]; void (*settings)(GuiState *gui, void *w); };
FN(void, choose_columns, (GuiState *gui, float w));
FN(R16f, add_cell, (GuiState *gui, float h));
FN(bool, rects_intersect, (const R16f *a, const R16f *b));
FN(void, draw_rect_rounded, (void *window, const R16f *r, float round));
FN(void, nvg_Scissor, (void *nvg, float x, float y, float w, float h));
FN(void, begin_content, (void *window, GuiState *gui, R16f *r, float pad, float f));
FN(void, pop_layout_row, (GuiState *gui, int n));
FN(float, draw_module_header, (GuiState *gui, void *object, void *other_object, const R16f *r, float fsz, const V4f *col, const V4f *hcol, const char *text));
FN(void, nvg_FontSize, (void *nvg, float size));
FN(float, draw_text, (void *window, R16f r, int align, int col, const char *text));
FN(void, draw_module_description, (GuiState *gui, void *object, void *cbArg, void *target, int key, unsigned char *hold, const R16f *r));
FN(bool, draw_animated_description, (GuiState *gui, void *object, R16f *r, void *anim, unsigned char *val));
FN(void, queue_module_toggle, (void *module_manager, void *module, unsigned char enabled));
FN(float, end_layout_row, (void *window, GuiState *gui));
FN(float, lerp_clamped_float, (float a, float b, float t));
extern "C" const float gui_cards_column_width, gui_card_background_grey_level, row_height_20_in_gui_cards, content_padding_8_in_gui_cards, font_size_16_in_gui_cards, colour_level_0_866667_in_gui_cards, colour_level_1_in_gui_cards_a,
rect_extent_5_in_gui_cards_a, rect_extent_neg_40, rect_extent_40, colour_level_0_6_in_gui_cards, colour_level_0_733333, offset_9_22337e18;
extern "C" const double divisor_0_05_in_gui_cards;
static inline __attribute__((always_inline)) V4f grey12(float k) { return nvg_rgba_f(k, k, k, 1.0f); }
extern "C" void draw_module_cards(GuiState *gui, WinA *window, Mgr2 *mgr, unsigned cat, float dt) {
void *nvg = window->ig;
choose_columns(gui, gui_cards_column_width);
Reg2 *list = &mgr->cat[cat];
if (!list->n) return;
float speed = (float)((double)dt / divisor_0_05_in_gui_cards);
for (unsigned long long i = 0; i < list->n; i++) {
ModC *module = (ModC *)list->list[i];
R16f card = add_cell(gui, module->height);
// the content height grows through the per-card end_layout_row finalizers alone, exactly like the
// original: a card outside the viewport is only measured once it scrolls into view
R16f view = make_rect(0.0f, gui->scroll, window->w, window->h);
if (!rects_intersect(&view, &card)) continue;
V4f cc;
volatile float ck = gui_card_background_grey_level;
cc.x = cc.y = cc.z = ck;
cc.w = 1.0f;
nvg_FillColor(nvg, &cc);
draw_rect_rounded(window, &card, row_height_20_in_gui_cards);
R16f inner = card;
begin_content(window, gui, &inner, content_padding_8_in_gui_cards, 12.0f);
pop_layout_row(gui, 1);
void *ig2 = window->ig;
R16f hdr = add_cell(gui, row_height_20_in_gui_cards);
float x;
if (gui->card_layout_mode == 1) {
V4f c1 = grey12(colour_level_0_866667_in_gui_cards), c2 = grey12(colour_level_1_in_gui_cards_a);
x = draw_module_header(gui, window, module, &hdr, 16.0f, &c1, &c2, module->name);
} else {
nvg_FontSize(ig2, font_size_16_in_gui_cards);
V4f c = grey12(colour_level_0_866667_in_gui_cards);
nvg_FillColor(ig2, &c);
x = draw_text(window, hdr, 0, 0, module->name);
}
unsigned f = module->flags;
if (!(f & 1)) {
R16f kb = make_rect(x + rect_extent_5_in_gui_cards_a, hdr.y, hdr.w - x + hdr.x, hdr.h);
draw_module_description(gui, window, mgr->set308, module, module->bind, (module->flags & 8) ? 0 : &module->hold, &kb);
f = module->flags;
}
if (!(f & 2)) {
R16f tg = make_rect(hdr.x + hdr.w + rect_extent_neg_40, hdr.y, rect_extent_40, hdr.h);
unsigned char on = module->enabled;
draw_animated_description(gui, window, &tg, module->anim, &on);
if (on != module->enabled) queue_module_toggle(mgr, module, on);
}
if (gui->card_layout_mode == 0) {
R16f d = add_cell(gui, row_height_20_in_gui_cards);
V4f c1 = grey12(colour_level_0_6_in_gui_cards), c2 = grey12(colour_level_0_733333);
draw_module_header(gui, window, module, &d, 14.0f, &c1, &c2, module->desc);
}
if (module->expanded == 1 && module->settings) module->settings(gui, window);
float used = end_layout_row(window, gui);
long long ia = (long long)used;
long long ib = (long long)(used - offset_9_22337e18); // float -> uint64 like the original (no constant pool)
unsigned long long u = (unsigned long long)((ib & (ia >> 63)) | ia);
float target = (float)u;
if (module->height != 0.0f) target = lerp_clamped_float(module->height, target, speed);
module->height = target;
}
}
extern "C" unsigned char is_unload_requested(GuiState *gui) { return (unsigned char)gui->unload_requested; }
// is rect r inside the scrolled viewport (0, scroll, width, height)?
extern "C" bool rect_in_viewport(GuiState *gui, WinA *w, const R16f *r) {
R16f view = make_rect(0.0f, gui->scroll, w->w, w->h);
R16f rr = *r;
return rects_intersect(&view, &rr);
}
+304
View File
@@ -0,0 +1,304 @@
// GUI: controls - sliders, ranges, numeric fields, key pickers.
#include "slinky/engine.h"
#include "slinky/gui_state.h"
FN(void *, libc_memcpy, (void *dest, const void *src, unsigned long long n));
FN(unsigned long long, libc_strlen, (const char *str));
extern "C" void set_slider_text(char *o, void *, const char *src, int a, int b) {
o[0x268] = 1;
unsigned long long n = libc_strlen(src);
if (n > 0x65) n = 0x65;
libc_memcpy(o + 0x26a, src, n);
o[0x26a + n] = 0;
float *f = (float *)(o + 0x2d0);
f[0] = (float)a;
f[1] = (float)b;
}
// ---- slider: drag along the track or type a value (after a double click), rounded to step, clamped ----
struct R16g { float x, y, w, h; };
FN(unsigned char, clicked_inside_r_frame, (GuiState *gui, R16g r));
FN(void, start_dragging_item_b, (GuiState *gui, void *item, void *other_object));
FN(bool, dragged_item_is, (GuiState *gui, void *item));
FN(const char *, take_dragged_item, (GuiState *gui));
FN(void, numeric_text_filter, (char *buf, char character));
FN(float, libc_roundf, (float x)); // roundf
extern "C" const float tenth_b;
extern "C" const double factor_0_1_in_gui_controls;
extern "C" unsigned char draw_slider_track(GuiState *gui, const R16g *track, const R16g *hit, float lo, float hi, float step,
float scale, unsigned char *active, float *val) {
if (clicked_inside_r_frame(gui, *hit)) {
*active = 1;
if (gui->numeric_drag_enabled == 1) start_dragging_item_b(gui, (void *)numeric_text_filter, val);
} else if (gui->click_capture == 0 && !dragged_item_is(gui, val)) {
*active = 0;
}
unsigned char a = *active;
if (a != 1) return a;
float v;
if (dragged_item_is(gui, val)) {
const char *s = take_dragged_item(gui);
if (!s) return a;
int neg = *s == '-';
const char *p = s + neg;
int sign = -neg | 1;
int ip = 0;
char c = *p;
while ((unsigned)(c - '0') <= 9) {
ip = ip * 10 + (c - '0');
c = *++p;
}
if ((unsigned char)(c | 2) == '.') {
float f = 0.0f;
float m = tenth_b;
++p;
for (int d; (unsigned)(d = *p - '0') < 10; p++) {
f += (float)d * m;
m = (float)((double)m * factor_0_1_in_gui_controls);
}
v = ((float)ip + f) * (float)sign;
} else {
v = (float)(ip * sign);
}
if (scale != 0.0f) v /= scale;
} else {
float t = (gui->mouse_scene.x - track->x) / track->w;
v = (hi - lo) * t + lo;
}
if (step != 0.0f) v = libc_roundf(v / step) * step;
*val = v;
if (lo >= v) *val = lo;
else if (!(v < hi)) *val = hi;
return a;
}
// ---- range slider: two knobs (each grabs on its side of the midpoint), they push each other ----
struct V4g { float x, y, z, w; };
struct GW14 { void *ig; double t; };
struct RAnim { unsigned char low_knob_active, high_knob_active; char padding_02[2]; float p0, p1; int padding_0c; double last; };
FN(R16g, add_cell, (GuiState *gui, float h));
FN(V4g, nvg_rgba_f, (float r, float g, float b, float a));
FN(void, nvg_FillColor, (void *nvg, V4g *c));
FN(void, nvg_FontSize, (void *nvg, float size));
FN(float, draw_text, (GW14 *window, R16g r, int align, int col, const char *text));
FN(R16g, make_rect, (float x, float y, float w, float h));
FN(bool, mouse_over_rect_r, (GuiState *gui, R16g r));
FN(void, draw_rect_rounded, (GW14 *window, R16g r, float round));
FN(float, limit_rotation_delta, (float lo, float hi, float x));
FN(float, lerp_clamped_float, (float a, float b, float t));
FN(float, lerp_unclamped_float, (float a, float b, float t));
FN(void, nvg_BeginPath, (void *nvg));
FN(void, nvg_Circle, (void *nvg, float x, float y, float rad));
FN(void, nvg_Fill, (void *nvg));
FN(unsigned char, draw_slider_track_local, (GuiState *gui, R16g track, R16g hit, float lo, float hi, float step, float scale,
unsigned char *active, float *val)) __asm__("draw_slider_track");
FN(int, libc_snprintf, (char *buf, unsigned long long n, const char *fmt, ...));
extern "C" const float row_height_20_in_gui_controls, colour_level_0_866667_in_gui_controls, font_size_15_in_gui_controls, offset_10_in_gui_controls, offset_neg_2_in_gui_controls_b, gui_control_idle_grey_level,
gui_control_hover_grey_level, two_in_gui_controls, clamp_bound_1_in_gui_controls, gui_range_slider_knob_radius, rect_extent_neg_6, gui_range_slider_padding;
extern "C" const double factor_0_5_in_gui_controls, offset_neg_2_in_gui_controls, divisor_0_1_in_gui_controls_a;
extern "C" const char fmt_float_2dp[], fmt_float_2dp_float_2dp[]; // "%0.2f", "%0.2f - %0.2f"
extern "C" char value_label_buffer[], range_label_buffer[];
static inline __attribute__((always_inline)) const char *val_text(GuiState *gui, float *item, const char *fmt, float scale) {
if (dragged_item_is(gui, item)) return gui->edit_text;
float x = *item;
float s = scale != 0.0f ? scale * x : x;
libc_snprintf(value_label_buffer, 0x40, fmt ? fmt : fmt_float_2dp, (double)s);
return value_label_buffer;
}
extern "C" bool draw_range_slider_row(GuiState *gui, GW14 *window, const char *label, const char *fmt, const char *rfmt, RAnim *an, float lo,
float hi, float step, float scale, float *vlo, float *vhi) {
void *nvg = window->ig;
R16g r = add_cell(gui, row_height_20_in_gui_controls);
V4g tc = nvg_rgba_f(colour_level_0_866667_in_gui_controls, colour_level_0_866667_in_gui_controls, colour_level_0_866667_in_gui_controls, 1.0f);
nvg_FillColor(nvg, &tc);
nvg_FontSize(nvg, font_size_15_in_gui_controls);
draw_text(window, r, 0, 0, label);
float y = r.y + offset_10_in_gui_controls + offset_neg_2_in_gui_controls_b;
double half = (double)r.w * factor_0_5_in_gui_controls;
float x0 = (float)((double)r.x + half);
float tw = (float)(half + offset_neg_2_in_gui_controls);
R16g track = make_rect(x0, y, tw, 4.0f);
float k = mouse_over_rect_r(gui, r) ? gui_control_hover_grey_level : gui_control_idle_grey_level;
V4g kc = nvg_rgba_f(k, k, k, 1.0f);
nvg_FillColor(nvg, &kc);
float rnd = two_in_gui_controls;
draw_rect_rounded(window, track, rnd);
float range = hi - lo;
float t0 = limit_rotation_delta(0.0f, clamp_bound_1_in_gui_controls, (*vlo - lo) / range);
float t1 = limit_rotation_delta(0.0f, clamp_bound_1_in_gui_controls, (*vhi - lo) / range);
float dt = (float)((double)(float)(window->t - an->last) / divisor_0_1_in_gui_controls_a);
an->p0 = lerp_clamped_float(an->p0, t0, dt);
an->p1 = lerp_clamped_float(an->p1, t1, dt);
an->last = window->t;
float x1 = x0 + tw;
float a = lerp_unclamped_float(x0, x1, an->p0);
float b = lerp_unclamped_float(x0, x1, an->p1);
R16g fill = make_rect(a, y, b - a, 4.0f);
V4g acc = *(V4g *)&gui->accent_color;
nvg_FillColor(nvg, &acc);
draw_rect_rounded(window, fill, rnd);
float ky = rnd + y, rad = gui_range_slider_knob_radius;
nvg_BeginPath(nvg);
nvg_Circle(nvg, a, ky, rad);
nvg_Fill(nvg);
nvg_BeginPath(nvg);
nvg_Circle(nvg, b, ky, rad);
nvg_Fill(nvg);
float mid = (float)(int)((double)(a + b) * factor_0_5_in_gui_controls);
float d = mid - x0;
float pad = gui_range_slider_padding;
R16g hl = make_rect(rect_extent_neg_6 + x0, r.y, d + pad, r.h);
R16g hh = make_rect(mid, r.y, tw - d + pad, r.h);
const char *text;
int tx;
bool changed;
if (draw_slider_track_local(gui, track, hl, lo, hi, step, scale, &an->low_knob_active, vlo)) {
if (*vlo > *vhi) *vhi = *vlo;
text = val_text(gui, vlo, fmt, scale);
tx = (int)a;
changed = true;
} else if (draw_slider_track_local(gui, track, hh, lo, hi, step, scale, &an->high_knob_active, vhi)) {
if (*vlo > *vhi) *vlo = *vhi;
text = val_text(gui, vhi, fmt, scale);
tx = (int)b;
changed = true;
} else {
if (!mouse_over_rect_r(gui, r)) return false;
if (gui->click_capture == 1 && !an->low_knob_active && an->high_knob_active != 1) return false;
float l = *vlo, h = *vhi;
float ls = scale == 0.0f ? l : scale * l;
float hs = scale == 0.0f ? h : scale * h;
libc_snprintf(range_label_buffer, 0x40, rfmt ? rfmt : fmt_float_2dp_float_2dp, (double)ls, (double)hs);
text = range_label_buffer;
tx = (int)((double)(hl.x + hl.w + hh.x) * factor_0_5_in_gui_controls);
changed = false;
}
gui->toast_visible = 1;
unsigned long long tn = libc_strlen(text);
if (tn > 0x65) tn = 0x65;
libc_memcpy(gui->toast_text, text, tn);
gui->toast_text[tn] = 0;
gui->toast_x = (float)tx;
gui->toast_y = (float)(int)y;
return changed;
}
// ---- dropdown: expands in place (< 6 items) or opens the "Select item" search popup; optional dropdowns
// (index 0 = none) get a clear cross ----
struct DDrop { char padding_00[8]; double last; float h; unsigned char open; };
FN(void, nvg_StrokeColor, (void *nvg, V4g *c));
FN(void, nvg_StrokeWidth, (void *nvg, float w));
FN(void, draw_rect_stroked, (GW14 *window, R16g r, float round));
FN(V4g, nvg_trans_rgba_f, (V4g c, float k));
FN(void, nvg_MoveTo, (void *nvg, float x, float y));
FN(void, nvg_LineTo, (void *nvg, float x, float y));
FN(void, nvg_Stroke, (void *nvg));
FN(void, open_list_popup, (GuiState *gui, const char *title, void *sel, void *filter, void *pick, void *items, unsigned long long n));
FN(void, text_filter_alnum, (char *buf, char character));
FN(void, select_index, (int *out, int i, void *sel));
extern "C" const float twenty_in_gui_controls_a, colour_level_0_866667_in_gui_controls_a, font_size_15_in_gui_controls_a, colour_alpha_0_5_in_gui_controls, colour_level_0_25098_in_gui_controls_a, stroke_width_2_in_gui_controls_a,
corner_radius_10_in_gui_controls_a, colour_level_0_933333_in_gui_controls, colour_level_0_345098_in_gui_controls, colour_level_1_in_gui_controls_a, colour_level_0_6_in_gui_controls, colour_level_0_733333_in_gui_controls;
extern "C" const double divisor_0_1_in_gui_controls_b, factor_0_5_in_gui_controls_a, factor_0_3_in_gui_controls;
extern "C" const char str_select_item[]; // "Select item"
static inline __attribute__((always_inline)) V4g grey14(float k) { return nvg_rgba_f(k, k, k, 1.0f); }
extern "C" unsigned char draw_dropdown_row(GuiState *gui, GW14 *window, const char *label, DDrop *dd, unsigned n, const char **items,
int *sel, bool opt) {
void *nvg = window->ig;
float dt = (float)(window->t - dd->last);
float target = dd->open ? (float)(28 * n) : twenty_in_gui_controls_a;
float h = lerp_clamped_float(dd->h, target, (float)((double)dt / divisor_0_1_in_gui_controls_b));
dd->h = h;
dd->last = window->t;
R16g r = add_cell(gui, h);
R16g lr = r;
lr.h = 20.0f;
V4g tc = grey14(colour_level_0_866667_in_gui_controls_a);
nvg_FillColor(nvg, &tc);
nvg_FontSize(nvg, font_size_15_in_gui_controls_a);
draw_text(window, lr, 0, 0, label);
R16g box = make_rect((float)((double)r.w * factor_0_5_in_gui_controls_a + (double)r.x), r.y, r.w * colour_alpha_0_5_in_gui_controls, r.h);
V4g oc = grey14(colour_level_0_25098_in_gui_controls_a);
nvg_StrokeColor(nvg, &oc);
nvg_StrokeWidth(nvg, stroke_width_2_in_gui_controls_a);
draw_rect_stroked(window, box, corner_radius_10_in_gui_controls_a);
if (dd->open) {
float rh = box.h;
V4g sc = nvg_trans_rgba_f(grey14(colour_level_0_25098_in_gui_controls_a), colour_alpha_0_5_in_gui_controls);
nvg_StrokeColor(nvg, &sc);
if (!n) return 0;
rh /= (float)n;
bool notopt = !opt;
unsigned char done = 0;
for (unsigned long long i = 0; i != n; i++) {
R16g row = make_rect(box.x, (float)(unsigned)i * rh + box.y, box.w, rh);
const char *name = items[i];
bool hov = mouse_over_rect_r(gui, row);
unsigned s = (unsigned)*sel;
V4g c;
if (i == s) c = *(V4g *)&gui->accent_color;
else if (hov) c = grey14(i == 0 || notopt ? colour_level_1_in_gui_controls_a : colour_level_0_733333_in_gui_controls);
else c = grey14(i != 0 || notopt ? colour_level_0_866667_in_gui_controls_a : colour_level_0_6_in_gui_controls);
nvg_FillColor(nvg, &c);
draw_text(window, row, 1, 0, name);
if (!(done & 1)) {
if (clicked_inside_r_frame(gui, row)) {
*sel = (int)i;
dd->open = 0;
done = 1;
} else {
done = 0;
}
} else {
done = 1;
}
if (i) {
nvg_BeginPath(nvg);
nvg_MoveTo(nvg, row.x, row.y);
nvg_LineTo(nvg, row.x + row.w, row.y);
nvg_Stroke(nvg);
}
}
return done;
}
bool small = n < 6;
bool hov = mouse_over_rect_r(gui, box);
int s = *sel;
if (opt && !small) {
if (s) {
float y = box.y, xh = box.h, x = box.w - xh + box.x;
R16g xr = {x, y, xh, xh};
bool xhov = mouse_over_rect_r(gui, xr);
V4g xc = xhov ? nvg_rgba_f(colour_level_0_933333_in_gui_controls, colour_level_0_345098_in_gui_controls, colour_level_0_345098_in_gui_controls, 1.0f) : grey14(colour_level_0_25098_in_gui_controls_a);
nvg_StrokeColor(nvg, &xc);
float in = (float)((double)xh * factor_0_3_in_gui_controls);
nvg_BeginPath(nvg);
float x0 = x + in, y0 = y + in, x1 = xh + x - in, y1 = y + xh - in;
nvg_MoveTo(nvg, x0, y0);
nvg_LineTo(nvg, x1, y1);
nvg_MoveTo(nvg, x1, y0);
nvg_LineTo(nvg, x0, y1);
nvg_Stroke(nvg);
if (clicked_inside_r_frame(gui, xr)) {
*sel = 0;
s = 0;
} else {
s = *sel;
}
hov = hov & !xhov;
} else {
s = 0;
}
}
if (!n) return 0;
V4g c;
if (opt && s == 0) c = grey14(hov ? colour_level_0_733333_in_gui_controls : colour_level_0_6_in_gui_controls);
else c = grey14(hov ? colour_level_1_in_gui_controls_a : colour_level_0_866667_in_gui_controls_a);
nvg_FillColor(nvg, &c);
if ((unsigned)s >= n) s = 0;
draw_text(window, box, 1, 0, items[(unsigned)s]);
if (clicked_inside_r_frame(gui, box)) {
if (n >= 6) open_list_popup(gui, str_select_item, sel, (void *)text_filter_alnum, (void *)select_index, items + 1, n - 1);
else dd->open = 1;
}
return 0;
}
+182
View File
@@ -0,0 +1,182 @@
// GUI: small helpers - text buffers and formatting.
#include "slinky/engine.h"
#include "slinky/gui_state.h"
extern "C" void select_index(int *out, int i, void *sel) { if (sel) *out = i + 1; }
struct C12 { long long a; int b; };
extern "C" void copy_pair12(C12 *o, const C12 *i) { o->b = i->b; o->a = i->a; }
FN(unsigned long long, libc_strlen, (const char *str));
extern "C" void text_filter_alnum(char *buf, char character) {
unsigned long long n = libc_strlen(buf);
if (n > 99) return;
unsigned char u = (unsigned char)((character & 0xdf) - 0x41);
asm("" : "+r"(u));
if (u >= 0x1a) {
unsigned char d = (unsigned char)(character - 0x30);
asm("" : "+r"(d));
unsigned char notdigit = d >= 10;
asm("" : "+r"(notdigit));
unsigned char notspace = character != 0x20;
asm("" : "+r"(notspace));
if (notdigit & notspace) return;
}
buf[n] = character;
}
// ---- colour setting row: label, swatch (colour fading to 25 % alpha), click opens the colour picker ----
struct R15 { float x, y, w, h; };
struct V15 { float x, y, z, w; };
struct Grad15 { float v[19]; };
struct Col15 { float r, g, b; };
struct GW15 { void *ig; double t; };
FN(R15, add_cell, (GuiState *gui, float h));
FN(V15, nvg_rgba_f, (float r, float g, float b, float a));
FN(V15, nvg_rgb_f, (float r, float g, float b));
FN(void, nvg_FillColor, (void *nvg, V15 *c));
FN(void, nvg_StrokeColor, (void *nvg, V15 *c));
FN(void, nvg_StrokeWidth, (void *nvg, float w));
FN(void, nvg_FontSize, (void *nvg, float size));
FN(float, draw_text, (GW15 *window, R15 r, int align, int col, const char *text));
FN(R15, make_rect, (float x, float y, float w, float h));
FN(void, draw_rect_stroked, (GW15 *window, R15 r, float round));
FN(void, draw_rect_rounded, (GW15 *window, R15 r, float round));
FN(Grad15, nvg_linear_gradient, (void *nvg, float x0, float y0, float x1, float y1, V15 c0, V15 c1));
FN(void, nvg_FillPaint, (void *nvg, Grad15 g));
FN(unsigned char, clicked_inside_r_frame, (GuiState *gui, R15 r));
FN(void, open_colour_picker_colour_c, (GuiState *gui, const char *label, Col15 *col, const Col15 *c, void *cb));
extern "C" const float row_height_20_in_gui_helpers, colour_level_0_866667_in_gui_helpers, font_size_15_in_gui_helpers, rect_extent_neg_40_in_gui_helpers, rect_extent_40_in_gui_helpers, colour_level_0_25098_in_gui_helpers,
stroke_width_2_in_gui_helpers, ten_in_gui_helpers, factor_0_5_in_gui_helpers_b;
extern "C" const double factor_0_5_in_gui_helpers_a;
extern "C" unsigned char draw_color_swatch_row(GuiState *gui, GW15 *window, const char *label, Col15 *col) {
void *nvg = window->ig;
R15 r = add_cell(gui, row_height_20_in_gui_helpers);
R15 lr = r;
lr.h = 20.0f;
V15 tc = nvg_rgba_f(colour_level_0_866667_in_gui_helpers, colour_level_0_866667_in_gui_helpers, colour_level_0_866667_in_gui_helpers, 1.0f);
nvg_FillColor(nvg, &tc);
nvg_FontSize(nvg, font_size_15_in_gui_helpers);
draw_text(window, lr, 0, 0, label);
R15 sw = make_rect(r.x + r.w + rect_extent_neg_40_in_gui_helpers, r.y, rect_extent_40_in_gui_helpers, 20.0f);
V15 oc = nvg_rgba_f(colour_level_0_25098_in_gui_helpers, colour_level_0_25098_in_gui_helpers, colour_level_0_25098_in_gui_helpers, 1.0f);
nvg_StrokeColor(nvg, &oc);
nvg_StrokeWidth(nvg, stroke_width_2_in_gui_helpers);
float rnd = ten_in_gui_helpers;
draw_rect_stroked(window, sw, rnd);
float x0 = sw.x, y0 = sw.y, x1 = sw.w + sw.x;
V15 c0 = nvg_rgb_f(col->r, col->g, col->b);
V15 c1 = nvg_rgba_f(col->r, col->g, col->b, 0.25f);
nvg_FillPaint(nvg, nvg_linear_gradient(nvg, x0, y0, x1, y0, c0, c1));
draw_rect_rounded(window, sw, rnd);
R15 cr = make_rect((float)((double)r.w * factor_0_5_in_gui_helpers_a + (double)r.x), r.y, r.w * factor_0_5_in_gui_helpers_b, r.h);
unsigned char clicked = clicked_inside_r_frame(gui, cr);
if (clicked) {
Col15 c = *col;
open_colour_picker_colour_c(gui, label, col, &c, (void *)copy_pair12);
}
return clicked;
}
// ---- list setting: one row per entry (name, colour swatch for colour lists, remove cross), "Click to add" ----
struct ListOps {
void *ctx;
unsigned long long (*count)(void *ctx);
const char *(*name)(void *ctx, unsigned long long i);
Col15 *(*color)(void *ctx, unsigned long long i);
void (*add)(void *ctx, const char *name, Col15 *color);
void (*remove)(void *ctx, unsigned long long i);
};
FN(float, draw_text_sized, (GW15 *window, R15 r, unsigned align, unsigned col, const char *text, const char *end));
FN(R15, inset_rect, (R15 r, float d));
FN(V15, nvg_trans_rgba_f, (V15 c, float k));
FN(bool, mouse_over_rect_r, (GuiState *gui, R15 r));
FN(void, nvg_BeginPath, (void *nvg));
FN(void, nvg_MoveTo, (void *nvg, float x, float y));
FN(void, nvg_LineTo, (void *nvg, float x, float y));
FN(void, nvg_Stroke, (void *nvg));
FN(void, pick_add_highlight, (void *p, void *, void *key));
FN(void, pick_add_shadow, (void *p, void *key));
FN(void, open_list_popup, (GuiState *gui, void *object, void *other_object, void *c, void *d0, void *d1, void *e));
FN(void, start_timed_popup_drag_payload, (GuiState *gui, void *object, void *other_object, void *c, void *d));
extern "C" const float colour_level_0_866667_in_gui_helpers_a, font_size_15_in_gui_helpers_a, colour_alpha_0_5_in_gui_helpers_a, colour_level_0_25098_in_gui_helpers_a, stroke_width_2_in_gui_helpers_a, corner_radius_10_in_gui_helpers_a,
inset_4, corner_radius_5_in_gui_helpers, colour_level_0_933333_in_gui_helpers, colour_level_0_345098_in_gui_helpers, gui_list_add_idle_grey_level, gui_list_add_hover_grey_level;
extern "C" const double factor_0_5_in_gui_helpers, factor_0_3_in_gui_helpers;
extern "C" const char str_click_to_add[]; // "Click to add"
extern "C" bool draw_list_editor_row(GuiState *gui, GW15 *window, const char *label, void *title, void *filter, int kind, ListOps *sel, void *items,
void *p9) {
void *nvg = window->ig;
unsigned long long n = sel->count(sel->ctx);
R15 r = add_cell(gui, (float)(n + 27 * (n + 1) + 1));
R15 lr = r;
lr.h = 20.0f;
V15 tc = nvg_rgba_f(colour_level_0_866667_in_gui_helpers_a, colour_level_0_866667_in_gui_helpers_a, colour_level_0_866667_in_gui_helpers_a, 1.0f);
nvg_FillColor(nvg, &tc);
nvg_FontSize(nvg, font_size_15_in_gui_helpers_a);
draw_text(window, lr, 0, 0, label);
R15 box = make_rect((float)((double)r.w * factor_0_5_in_gui_helpers + (double)r.x), r.y, r.w * colour_alpha_0_5_in_gui_helpers_a, r.h);
V15 oc = nvg_rgba_f(colour_level_0_25098_in_gui_helpers_a, colour_level_0_25098_in_gui_helpers_a, colour_level_0_25098_in_gui_helpers_a, 1.0f);
nvg_StrokeColor(nvg, &oc);
nvg_StrokeWidth(nvg, stroke_width_2_in_gui_helpers_a);
draw_rect_stroked(window, box, corner_radius_10_in_gui_helpers_a);
float rh = box.h / (float)(n + 1);
bool changed = false;
if (n) {
unsigned long long del = ~0ull;
float dim = colour_level_0_25098_in_gui_helpers_a;
for (unsigned long long i = 0; i != n; i++) {
R15 row = make_rect(box.x, (float)i * rh + box.y, box.w, rh);
const char *name = sel->name(sel->ctx, i);
unsigned long long len = libc_strlen(name);
if (len >= 0x14) len = 0x14;
V15 nc = nvg_rgba_f(colour_level_0_866667_in_gui_helpers_a, colour_level_0_866667_in_gui_helpers_a, colour_level_0_866667_in_gui_helpers_a, 1.0f);
nvg_FillColor(nvg, &nc);
draw_text_sized(window, row, 1, 0, name, name + len);
if (kind == 1) {
R15 sw = inset_rect(R15{row.x, row.y, row.h, row.h}, inset_4);
Col15 *col = sel->color(sel->ctx, i);
V15 cc = nvg_rgb_f(col->r, col->g, col->b);
nvg_FillColor(nvg, &cc);
draw_rect_rounded(window, sw, corner_radius_5_in_gui_helpers);
if (clicked_inside_r_frame(gui, sw)) {
Col15 c = *col;
open_colour_picker_colour_c(gui, name, col, &c, (void *)copy_pair12);
changed = true;
}
}
float y = row.y, h = row.h, x = row.w - h + row.x;
R15 xr = {x, y, h, h};
V15 xc = mouse_over_rect_r(gui, xr) ? nvg_rgba_f(colour_level_0_933333_in_gui_helpers, colour_level_0_345098_in_gui_helpers, colour_level_0_345098_in_gui_helpers, 1.0f)
: nvg_rgba_f(dim, dim, dim, 1.0f);
nvg_StrokeColor(nvg, &xc);
float in = (float)((double)h * factor_0_3_in_gui_helpers);
nvg_BeginPath(nvg);
float x0 = x + in, y0 = y + in, x1 = h + x - in, y1 = y + h - in;
nvg_MoveTo(nvg, x0, y0);
nvg_LineTo(nvg, x1, y1);
nvg_MoveTo(nvg, x1, y0);
nvg_LineTo(nvg, x0, y1);
nvg_Stroke(nvg);
if (clicked_inside_r_frame(gui, xr)) del = i;
V15 sc = nvg_trans_rgba_f(nvg_rgba_f(dim, dim, dim, 1.0f), colour_alpha_0_5_in_gui_helpers_a);
nvg_StrokeColor(nvg, &sc);
nvg_BeginPath(nvg);
nvg_MoveTo(nvg, row.x, row.y + row.h);
nvg_LineTo(nvg, row.x + row.w, row.y + row.h);
nvg_Stroke(nvg);
}
if (del != ~0ull) {
sel->remove(sel->ctx, del);
changed = true;
}
}
R15 add = make_rect(box.x, (float)n * rh + box.y, box.w, rh);
float k = mouse_over_rect_r(gui, add) ? gui_list_add_hover_grey_level : gui_list_add_idle_grey_level;
V15 ac = nvg_rgba_f(k, k, k, 1.0f);
nvg_FillColor(nvg, &ac);
draw_text(window, add, 1, 0, str_click_to_add);
if (clicked_inside_r_frame(gui, add)) {
if (items) open_list_popup(gui, title, sel, filter, (void *)pick_add_highlight, items, p9);
else start_timed_popup_drag_payload(gui, title, sel, filter, (void *)pick_add_shadow);
changed = true;
}
return changed;
}
+434
View File
@@ -0,0 +1,434 @@
// 0x10000: GUI window helpers
#include "slinky/engine.h"
#include "slinky/gui_state.h"
FN(void, nvg_FontFaceId, (void *imgui, unsigned col));
struct GCol { void *imgui; char padding_08[0x38]; unsigned col[1]; };
extern "C" void set_draw_color(GCol *g, unsigned i) {
void *nvg = g->imgui;
nvg_FontFaceId(nvg, g->col[i]);
}
extern "C" void reset_layout_columns(GuiState *gui) { gui->layout_row_depth = 0; gui->layout_column_count = 0; }
FN(void, nvg_BeginPath, (void *nvg));
FN(void, nvg_Rect, (void *nvg, float x, float y, float w, float h));
FN(void, nvg_Fill, (void *nvg));
extern "C" void draw_rect_filled(void **w, const float *r) {
void *nvg = w[0];
nvg_BeginPath(nvg);
float x = r[0];
float y = r[1];
float w2 = r[2];
float h = r[3];
nvg_Rect(nvg, x, y, w2, h);
nvg_Fill(nvg);
}
FN(void, nvg_RoundedRect, (void *nvg, float x, float y, float w, float h, float round));
FN(void, nvg_Stroke, (void *nvg));
extern "C" void draw_rect_rounded(void **w, const float *r, float round) {
float x = r[0], y = r[1], ww = r[2], h = r[3];
void *nvg = w[0];
nvg_BeginPath(nvg);
nvg_RoundedRect(nvg, x, y, ww, h, round);
nvg_Fill(nvg);
}
extern "C" void draw_rect_stroked(void **w, const float *r, float round) {
float x = r[0], y = r[1], ww = r[2], h = r[3];
void *nvg = w[0];
nvg_BeginPath(nvg);
nvg_RoundedRect(nvg, x, y, ww, h, round);
nvg_Stroke(nvg);
}
struct R16e { float x, y, w, h; };
struct V4e { float x, y, z, w; };
FN(void, push_layout_row, (GuiState *gui, R16e *r, float sp, float pad));
FN(R16e, round_rect, (R16e r));
FN(void, nvg_Save, (void *nvg));
FN(void, nvg_Scissor, (void *nvg, float x, float y, float w, float h));
typedef int v4ie __attribute__((vector_size(16)));
typedef float v4fe __attribute__((vector_size(16)));
extern "C" const v4ie gui_layout_sign_mask; // sign mask
// begin a child region: push it, round the rect to whole pixels and clip to its padded inside
extern "C" void begin_content(void **w, GuiState *gui, R16e *r, float pad, float f) {
void *nvg = w[0];
// the original round-trips a stack copy of the rect: push_layout_row clips and insets the copy to build
// the pushed row while the caller's rect stays raw, then the raw rect is rounded and reused for
// the scissor - whose left/top edge sits at the card edge, not at the already-inset row. Scissoring
// the inset row shaved the first glyph off every label ("mode" -> "ode").
R16e local = *r;
push_layout_row(gui, &local, pad, f);
gui->rows[gui->layout_row_depth - 1].clip = 1;
*r = round_rect(*r);
nvg_Save(nvg);
v4fe v = {pad, 0, 0, 0};
float np = ((v4fe)((v4ie)v ^ gui_layout_sign_mask))[0] - pad;
nvg_Scissor(nvg, r->x + pad, r->y + pad, r->w + np, np + r->h);
}
FN(float, libc_sinf, (float x)); // sinf
FN(V4e, nvg_trans_rgba_f, (V4e c, float k));
FN(void, nvg_StrokeColor, (void *nvg, V4e *c));
FN(void, nvg_StrokeWidth, (void *nvg, float w));
FN(void, nvg_MoveTo, (void *nvg, float x, float y));
FN(void, nvg_LineTo, (void *nvg, float x, float y));
extern "C" const float half_constant;
struct WT { void *ig; double t; };
// pulsing vertical accent line at the left edge of r
extern "C" void draw_accent_line(WT *w, R16e *r, V4e *col, float speed, float width) {
void *nvg = w->ig;
float s = libc_sinf((float)((double)speed * w->t));
float k = half_constant;
k = s * k + k;
nvg_BeginPath(nvg);
V4e c = nvg_trans_rgba_f(*col, k);
nvg_StrokeColor(nvg, &c);
nvg_StrokeWidth(nvg, width);
nvg_MoveTo(nvg, r->x, r->y);
nvg_LineTo(nvg, r->x, r->y + r->h);
nvg_Stroke(nvg);
}
extern "C" const float minus_one_in_gui_layout_a;
// set the column count of the current layout row (finishing the previous row's height)
typedef GuiCell Cell;
typedef GuiLayoutRow Row;
extern "C" void pop_layout_row(GuiState *gui, int n) {
Row *row = &gui->rows[gui->layout_row_depth - 1];
unsigned long long k = row->items;
if (k) {
k--;
// the compiled binary here reloads only one cell, (items-1)&3 i.e. the last added one: its
// "max over the last visual row" loop re-maxes that same value into a register constant and
// moves it - dead straight-lining. Two columns therefore never cross-balance their bottoms.
Cell *c = &row->cell[k & 3];
float m = c->y + c->h;
m = m > minus_one_in_gui_layout_a ? m : minus_one_in_gui_layout_a;
if (m != minus_one_in_gui_layout_a) row->bottom = m + row->y0;
}
int cols = n < 4 ? n : 4;
if (!n) cols = 1;
row->cols = cols;
row->items = 0;
}
FN(void, nvg_TextAlign, (void *nvg, unsigned font));
FN(float, nvg_text, (void *nvg, float x, float y, void *text, void *end));
extern "C" const unsigned text_align_flags_table[];
extern "C" const float offset_1_in_gui_layout_c;
extern "C" const double factor_0_5_in_gui_layout_a; // 0.5
// text in rect r: align 0 = left, 1 = centre, 2 = right; vertically centred
extern "C" void draw_text_sized(GCol *window, R16e *r, unsigned align, unsigned col, void *text, void *end) {
set_draw_color(window, col);
void *nvg = window->imgui;
nvg_TextAlign(nvg, text_align_flags_table[align]);
float x = r->x;
float y = r->y + offset_1_in_gui_layout_c;
r->y = y;
if (align != 0) {
if (align == 2) x = x + r->w;
else x = (float)((double)r->w * factor_0_5_in_gui_layout_a + (double)x);
}
float yy = (float)((double)r->h * factor_0_5_in_gui_layout_a + (double)y);
nvg_text(nvg, x, yy, text, end);
}
extern "C" const float minus_one_in_gui_layout_b;
// pick the column count so that cells of width w fit the row
extern "C" void choose_columns(GuiState *gui, float w) {
Row *row = &gui->rows[gui->layout_row_depth - 1];
float avail = row->width, sp = row->y0;
unsigned long long k = row->items;
if (k) {
k--;
// same single-cell finalizer as end_layout_row/pop_layout_row, see the note there
Cell *c = &row->cell[k & 3];
float m = c->y + c->h;
m = m > minus_one_in_gui_layout_b ? m : minus_one_in_gui_layout_b;
if (m != minus_one_in_gui_layout_b) row->bottom = m + sp;
}
int n = (int)((avail + sp) / (w + sp));
int cols = n < 4 ? n : 4;
if (!n) cols = 1;
row->cols = cols;
row->items = 0;
}
extern "C" const float one_in_gui_layout_b;
extern "C" const double factor_0_5_in_gui_layout;
// text in rect r (read-only), returns its width
extern "C" float draw_text(GCol *window, const R16e *r, unsigned align, unsigned col, void *text) {
float x = r->x, y = r->y, w = r->w, h = r->h;
set_draw_color(window, col);
void *nvg = window->imgui;
nvg_TextAlign(nvg, text_align_flags_table[align]);
if (align != 0) {
if (align == 2) x = x + w;
else x = (float)((double)w * factor_0_5_in_gui_layout + (double)x);
}
y += one_in_gui_layout_b;
float yy = (float)((double)h * factor_0_5_in_gui_layout + (double)y);
return nvg_text(nvg, x, yy, text, 0);
}
extern "C" const float minus_one_in_gui_layout;
FN(void, nvg_Restore, (void *nvg));
// end the current layout row: finish its height, pop its clip, grow the window height; returns the used height
extern "C" float end_layout_row(GCol *w, GuiState *gui) {
Row *row = &gui->rows[gui->layout_row_depth - 1];
unsigned long long k = row->items;
if (k) {
k--;
// only the last added cell ((items-1)&3) reaches the finalizer in the compiled original
Cell *c = &row->cell[k & 3];
float m = c->y + c->h;
m = m > minus_one_in_gui_layout ? m : minus_one_in_gui_layout;
if (m != minus_one_in_gui_layout) row->bottom = m + row->y0;
}
if (row->clip == 1) nvg_Restore(w->imgui);
float bottom = row->bottom;
if (bottom > (float)gui->layout_column_count) gui->layout_column_count = (int)bottom;
gui->layout_row_depth -= 1;
return (row->y0 + row->y0) + (bottom - row->top);
}
FN(R16e, inset_rect, (R16e r, float d));
FN(unsigned long long, offset_rect, (R16e r));
// push a layout row: clip r to the parent row, inset it by pad, start the cursor at its corner
extern "C" void push_layout_row(GuiState *gui, R16e *r, float sp, float pad) {
long long n = gui->layout_row_depth;
if (n < 0) n = 0;
if (n >= 5) n = 4; // rows[5] is the fixed stack size
if (n) {
float *p = (float *)&gui->rows[n - 1];
float px = p[0], py = p[1], pw = p[2], ph = p[3];
float x = r->x;
if (px > x) { r->x = px; x = px; }
float y = r->y;
if (py > y) { r->y = py; y = py; }
float right = px + pw;
if (r->w + x > right) {
r->w = right - x;
if (0.0f > r->w) r->w = 0.0f;
}
float bottom = py + ph;
if (r->h + y > bottom) {
r->h = bottom - y;
if (0.0f > r->h) r->h = 0.0f;
}
}
char *row = (char *)&gui->rows[n];
gui->layout_row_depth = n + 1;
*r = inset_rect(*r, pad);
__builtin_memset(row, 0, 0x78);
*(R16e *)row = *r;
*(float *)(row + 0x10) = sp;
*(float *)(row + 0x14) = pad;
*(unsigned long long *)(row + 0x1c) = offset_rect(*r);
}
struct Img { char b[0x4c]; };
struct Off2 { float x, y; };
FN(Img, nvg_image_pattern, (void *nvg, float x, float y, float w, float h, int z, int tex, float f));
FN(void, nvg_FillPaint, (void *nvg, Img im));
extern "C" const float twenty;
extern "C" Off2 *ptr_to_unreferenced_rodata;
struct GImg { void *imgui; char padding_08[0x40]; int tex; float tw, th; };
// draw texture tile `idx` scaled into r, clipped to r
extern "C" void draw_texture(GImg *w, R16e *r, unsigned idx, float alpha) {
void *nvg = w->imgui;
float k = twenty;
float sx = r->w / k, sy = r->h / k;
Off2 *t = ptr_to_unreferenced_rodata;
float ox = r->x - t[idx].x * sx;
float oy = r->y - t[idx].y * sy;
Img im = nvg_image_pattern(nvg, ox, oy, sx * w->tw, sy * w->th, 0, w->tex, alpha);
nvg_BeginPath(nvg);
nvg_Rect(nvg, r->x, r->y, r->w, r->h);
nvg_FillPaint(nvg, im);
nvg_Fill(nvg);
}
// next cell of the current layout row (height h), rounded to whole pixels
extern "C" R16e add_cell(GuiState *gui, float h) {
long long top = gui->layout_row_depth;
if (top <= 0) return R16e{0.0f, 0.0f, 0.0f, h};
Row *row = &gui->rows[top - 1];
unsigned long long cols = (unsigned long long)row->cols;
if (!cols) {
row->cols = 1;
cols = 1;
}
unsigned long long n = row->items;
row->items = n + 1;
Cell *above = &row->cell[(n - cols) & 3];
Cell *c = &row->cell[n & 3];
if (n % cols) {
Cell *prev = &row->cell[(n - 1) & 3];
float pw = prev->w;
c->x = prev->x + pw + row->y0;
c->w = pw;
c->y = n < cols ? row->bottom : above->y + above->h + row->y0;
} else {
c->x = *(float *)((char *)row + 0x1c);
if (n < cols) {
c->y = row->bottom;
c->w = (row->width - (float)(cols - 1) * row->y0) / (float)cols;
} else {
c->y = above->y + above->h + row->y0;
c->w = above->w;
}
}
c->h = h;
return round_rect(*(R16e *)c);
}
// ---- input events: 1 mouse move, 2 key/button (+ up to 4 typed chars), 3 scroll; returns "consumed" ----
FN(bool, popup_is_open, (GuiState *gui));
FN(unsigned long long, libc_strlen, (const char *str));
FN(void, close_popup, (GuiState *gui));
extern "C" const float factor_neg_30;
struct InEv { int type; union { int key; float wheel; }; union { int y; unsigned char ch[4]; }; unsigned char down; };
typedef void (*CharFilter)(char *buf, char c, void *arg);
extern "C" bool handle_gui_input(GuiState *gui, InEv *in) {
int st;
switch (in->type) {
case 1:
gui->mouse_raw.x = (float)in->key;
gui->mouse_raw.y = (float)in->y;
return false;
case 3:
if (!popup_is_open(gui)) gui->scroll += in->wheel * factor_neg_30;
return false;
case 2:
if (in->down != 1) break;
st = gui->input_mode;
if ((unsigned)(st - 2) < 2) { // text field active
int k = in->key;
if (k > 0x54) {
if (k == 0x55) { // enter
if (gui->edit_text[0]) gui->edit_confirmed = 1;
return true;
}
if (k == 0x57) { // escape
gui->input_mode = 0;
return true;
}
} else if (k == 1) {
if (st != 3) {
gui->input_mode = 0;
return true;
}
break;
} else if (k == 0x53) { // backspace
unsigned long long n = libc_strlen(gui->edit_text);
if (n) gui->edit_text[n - 1] = 0;
return true;
}
CharFilter edit_char_filter = ((CharFilter)gui->edit_char_filter);
for (int i = 0; i < 4; i++) {
char c = (char)in->ch[i];
if (!c) return true;
edit_char_filter = ((CharFilter)gui->edit_char_filter);
edit_char_filter(gui->edit_text, c, gui->edit_filter_arg);
}
return true;
}
if (st == 1) { // key-bind capture
int k = in->key;
int key = (k == 1 || k == 0x57) ? 0 : k;
((void (*)(void *, int))gui->keybind_callback)(gui->keybind_target, key);
gui->input_mode = 0;
return true;
}
if (st == 0 && popup_is_open(gui) && in->key == 0x57) {
close_popup(gui);
return true;
}
break;
default:
return false;
}
{
int k = in->key;
if ((unsigned)(k - 100) < 2) gui->numeric_drag_enabled = in->down;
else if (k == 1) gui->click_capture = in->down;
}
return false;
}
// ---- GUI render target: ImGui frame into an offscreen framebuffer scaled by `scale` ----
struct FBuf { char padding_00[0x14]; int tex; };
struct Rend { void *ig; double t; int w, h; float sw, sh; float scale; int padding_24; FBuf *fb; float clear[4]; };
FN(void, nvg_wrap_delete_framebuffer, (FBuf *fb)); // destroy
FN(FBuf *, nvglu_create_framebuffer, (void *nvg, int w, int h, int flags)); // create
FN(void, nvg_wrap_bind_framebuffer, (FBuf *fb)); // bind (0 = default)
FN(void, nvg_BeginFrame, (void *nvg, float w, float h, float dpi)); // begin frame
FN(void, nvg_EndFrame, (void *nvg)); // end frame
FN(void, nvg_Scale, (void *nvg, float x, float y));
extern "C" void (**const ptr_to_pGlGetFloatv)(unsigned pname, float *out); // glGetFloatv
extern "C" void (**const ptr_to_pGlClearColor)(float r, float g, float b, float a); // glClearColor
extern "C" void (**const ptr_to_pGlClear)(unsigned mask); // glClear
extern "C" float frame_ratio_1_in_gui_layout, frame_ratio_1_in_gui_layout_a;
extern "C" void set_render_scale(Rend *renderer, float scale) {
renderer->scale = scale;
renderer->sw = (float)renderer->w / scale;
renderer->sh = (float)renderer->h / scale;
}
// (re)create the framebuffer when the window size changed
extern "C" bool resize_render_target(Rend *renderer, int w, int h) {
bool changed;
if (w == renderer->w && h == renderer->h) {
changed = false;
} else {
if (renderer->fb) nvg_wrap_delete_framebuffer(renderer->fb);
renderer->fb = nvglu_create_framebuffer(renderer->ig, w, h, 0);
changed = true;
}
renderer->w = w;
renderer->h = h;
return changed;
}
extern "C" void begin_frame(Rend *renderer, double t) {
float fw = (float)renderer->w;
void *nvg = renderer->ig;
float fh = (float)renderer->h;
nvg_BeginFrame(nvg, fw, fh, frame_ratio_1_in_gui_layout);
nvg_Scale(renderer->ig, renderer->scale, renderer->scale);
renderer->t = t;
}
// begin a frame into the framebuffer (saves and clears the GL clear colour)
extern "C" void begin_frame_framebuffer(Rend *renderer, double t) {
nvg_wrap_bind_framebuffer(renderer->fb);
(*ptr_to_pGlGetFloatv)(0xc22, renderer->clear); // GL_COLOR_CLEAR_VALUE
(*ptr_to_pGlClearColor)(0.0f, 0.0f, 0.0f, 0.0f);
(*ptr_to_pGlClear)(0x4400); // colour | stencil
float fw = (float)renderer->w;
void *nvg = renderer->ig;
float fh = (float)renderer->h;
nvg_BeginFrame(nvg, fw, fh, frame_ratio_1_in_gui_layout_a);
nvg_Scale(renderer->ig, renderer->scale, renderer->scale);
renderer->t = t;
}
// draw the framebuffer texture over the screen
extern "C" void draw_framebuffer_texture_over_screen(Rend *renderer) {
void *nvg = renderer->ig;
float sw = renderer->sw, sh = renderer->sh;
Img g = nvg_image_pattern(nvg, 0.0f, 0.0f, sw, sh, 0, renderer->fb->tex, 1.0f);
nvg_BeginPath(nvg);
nvg_Rect(nvg, 0.0f, 0.0f, renderer->sw, renderer->sh);
nvg_FillPaint(nvg, g);
nvg_Fill(nvg);
}
extern "C" void end_frame(Rend *renderer) { nvg_EndFrame(renderer->ig); }
extern "C" void clear_frame(Rend *renderer) {
nvg_EndFrame(renderer->ig);
nvg_wrap_bind_framebuffer(0);
void (*cc)(float, float, float, float) = *ptr_to_pGlClearColor;
float c0 = renderer->clear[0], c1 = renderer->clear[1], c2 = renderer->clear[2], c3 = renderer->clear[3];
cc(c0, c1, c2, c3);
}
+631
View File
@@ -0,0 +1,631 @@
// 0x16000
#include "slinky/engine.h"
#include "slinky/gui_state.h"
FN(void, set_popup_open, (void *x, bool b));
extern "C" void close_popup(void *x) { set_popup_open(x, true); }
extern "C" bool popup_is_open(GuiState *gui) { return gui->popup_kind_i != 0; }
extern "C" unsigned char popup_is_animating(GuiState *gui) { return (unsigned char)gui->popup_animating; }
struct Col3 { float r, g, b; };
struct Pick { void *ctx; char padding_08[0x18]; void (*add)(void *ctx, void *key, Col3 *c); };
extern "C" const float one_in_gui_popups, one_in_gui_popups_a;
extern "C" void pick_add_highlight(Pick *p, void *, void *key) {
if (!key) return;
void *ctx = p->ctx;
auto add = p->add;
alignas(16) Col3 c = {one_in_gui_popups, 0, 0};
add(ctx, key, &c);
}
extern "C" void pick_add_shadow(Pick *p, void *key) {
if (!key) return;
void *ctx = p->ctx;
auto add = p->add;
alignas(16) Col3 c = {one_in_gui_popups_a, 0, 0};
add(ctx, key, &c);
}
FN(unsigned long long, libc_strlen, (const char *str));
// numeric text filter: digits and , - .
extern "C" void numeric_text_filter(char *buf, char character) {
unsigned long long n = libc_strlen(buf);
if (n == 100) return;
unsigned char d = (unsigned char)(character - 0x30);
unsigned char nd = d >= 10;
unsigned char e = (unsigned char)(character - 0x2c);
unsigned char np = e >= 3;
if (np & nd) return;
buf[n] = character;
}
struct R16 { long long a, b; };
FN(unsigned char, clicked_inside_r_frame, (void *gui, R16 r));
FN(void, start_dragging_item_b, (void *gui, void *item, void *other_object));
FN(bool, dragged_item_is, (GuiState *gui, void *item));
extern "C" __attribute__((noinline)) bool drag_item(GuiState *gui, void *, R16 *r, void *item, void *b) {
if (clicked_inside_r_frame(gui, *r)) start_dragging_item_b(gui, item, b);
return dragged_item_is(gui, b);
}
struct R16d { float x, y, w, h; };
struct V4b { float x, y, z, w; };
FN(float, draw_text, (void *window, R16d r, int a, int b, void *x));
FN(V4b, nvg_rgba_f, (float r, float g, float b, float a));
FN(void, draw_accent_line, (void *window, R16d r, V4b c, float a, float b));
extern "C" const float offset_1_in_gui_popups_b, gui_popup_title_grey_level, gui_popup_title_inset_a;
extern "C" __attribute__((noinline)) void draw_tracked_text(GuiState *gui, void *object, const R16d *rp) {
R16d r = *rp;
float f = draw_text(object, *rp, 1, 0, gui->edit_text) + offset_1_in_gui_popups_b;
float k = gui_popup_title_grey_level;
V4b c = nvg_rgba_f(k, k, k, 1.0f);
R16d n = {f, r.y, r.w, r.h};
draw_accent_line(object, n, c, gui_popup_title_inset_a, 1.0f);
}
extern "C" const float one_in_gui_popups_c, gui_popup_text_grey_level, gui_popup_title_inset_b;
extern "C" __attribute__((noinline)) void draw_clipped_text(GuiState *gui, void *object, const R16d *rp, int align) {
float f = draw_text(object, *rp, align, 0, gui->edit_text);
R16d r = *rp;
f += one_in_gui_popups_c;
float k = gui_popup_text_grey_level;
V4b c = nvg_rgba_f(k, k, k, 1.0f);
R16d n = {f, r.y, r.w, r.h};
draw_accent_line(object, n, c, gui_popup_title_inset_b, 1.0f);
}
FN(void, set_popup_open, (void *x, bool b));
FN(double, time_now, ());
FN(void, start_dragging_item_b, (void *g, void *a, void *b));
// start a timed popup/drag payload
extern "C" void start_timed_popup_drag_payload(GuiState *gui, void *object, void *other_object, void *item, void *d) {
set_popup_open(gui, true);
double now = time_now();
gui->popup_kind = 1;
gui->popup_context = other_object;
gui->popup_title = object;
gui->popup_open_time = now;
__builtin_memset(&gui->popup_opacity, 0, 0x10);
gui->popup_callback = d;
gui->popup_list_callback = item;
__builtin_memset(&gui->popup_rgb.b, 0, 0x60);
start_dragging_item_b(gui, item, &gui->popup_kind);
}
#include <stdarg.h>
FN(int, libc_vsnprintf, (char *buf, unsigned long long n, const char *fmt, va_list ap));
FN(void, end_drag, (void *gui));
extern "C" const char str_are_you_sure[]; // "Are you sure?"
// confirmation popup with a formatted message
extern "C" void confirmation_popup_formatted_message(GuiState *gui, void *object, void *other_object, const char *fmt, ...) {
set_popup_open(gui, true);
double now = time_now();
gui->popup_kind = 3;
gui->popup_context = object;
gui->popup_title_text = str_are_you_sure;
gui->popup_open_time = now;
__builtin_memset(&gui->popup_opacity, 0, 0x10);
gui->popup_callback = other_object;
__builtin_memset(gui->popup_message, 0, 0x68);
va_list ap;
va_start(ap, fmt);
libc_vsnprintf(gui->popup_message, 100, fmt, ap);
va_end(ap);
}
extern "C" void open_list_popup(GuiState *gui, void *object, void *other_object, void *item, void *d0, void *d1, void *e) {
set_popup_open(gui, true);
double now = time_now();
gui->popup_kind = 4;
gui->popup_context = other_object;
gui->popup_title = object;
gui->popup_open_time = now;
__builtin_memset(&gui->popup_opacity, 0, 0x10);
gui->popup_callback = item;
gui->popup_list_callback = d0;
gui->popup_list_items = d1;
gui->popup_list_extra = e;
__builtin_memset(&gui->popup_list_state, 0, 0x50);
start_dragging_item_b(gui, item, &gui->popup_kind);
end_drag(gui);
}
// ---- popups: 1 = callback, 2 = colour picker, 3 = confirm, 4 = list picker ----
struct Col3b { float r, g, b; };
extern "C" unsigned long long gui_popups_recent_colour_ring_position; // recent-colour ring position
extern "C" Col3b recent_colors_ring[8];
struct ListV { unsigned long long n, cap; void *data; };
static inline __attribute__((always_inline)) bool same(const Col3b &a, const Col3b &b) {
return a.r == b.r && a.g == b.g && a.b == b.b;
}
// close the open popup (apply = run its callback with the result)
FN(void, libc_free, (void *ptr));
extern "C" __attribute__((noinline)) void set_popup_open(void *gv, bool apply) {
GuiState *gui = (GuiState *)gv;
gui->input_mode = 0;
switch (gui->popup_kind_u) {
case 0:
return;
case 1:
if (apply) ((void (*)(void *, void *))gui->popup_callback)(gui->popup_context, 0);
break;
case 2: {
if (apply) {
Col3b c = *(Col3b *)&gui->popup_rgb;
((void (*)(void *, Col3b *))gui->popup_callback)(gui->popup_context, &c);
}
Col3b c = *(Col3b *)&gui->popup_rgb;
unsigned long long p = gui_popups_recent_colour_ring_position;
for (unsigned j = 0; j < 8; j++)
if (same(c, recent_colors_ring[(p + 8 - j) & 7])) goto out;
p = p ? p - 1 : 7;
gui_popups_recent_colour_ring_position = p;
recent_colors_ring[p] = c;
break;
}
case 3:
break;
case 4: {
if (apply) ((void (*)(void *, void *, void *))gui->popup_list_callback)(gui->popup_context, 0, 0);
ListV *popup_list_view = ((ListV *)gui->popup_list_view);
if (popup_list_view) {
popup_list_view->n = 0;
popup_list_view->cap = 0;
libc_free(popup_list_view->data);
popup_list_view->data = 0;
libc_free(gui->popup_list_view);
gui->popup_list_view = 0;
}
break;
}
}
out:
gui->popup_kind = 0;
}
extern "C" const float half_in_gui_popups, offset_2_in_gui_popups, offset_4_in_gui_popups, divisor_6_in_gui_popups; // 0.5, 2, 4, 6
// open the colour picker for colour c (keeps the RGB and its HSL)
extern "C" void open_colour_picker_colour_c(GuiState *gui, void *object, void *other_object, const Col3b *c, void *cb) {
set_popup_open(gui, true);
double now = time_now();
Col3b in = *c;
float r = c->r, gg = c->g, bb = c->b;
float mx = __builtin_fmaxf(r, __builtin_fmaxf(gg, bb));
float mn = __builtin_fminf(r, __builtin_fminf(gg, bb));
float sum = mx + mn;
float l = half_in_gui_popups * sum;
float h = 0.0f, s = 0.0f;
if (mx != mn) {
float d = mx - mn;
float denom = half_in_gui_popups < l ? offset_2_in_gui_popups - mx - mn : sum;
if (mx == r) h = (gg - bb) / d + (gg < bb ? divisor_6_in_gui_popups : 0.0f);
else if (mx == gg) h = (bb - r) / d + offset_2_in_gui_popups;
else if (mx == bb) h = (r - gg) / d + offset_4_in_gui_popups;
else h = l;
s = d / denom;
h = h / divisor_6_in_gui_popups;
}
gui->popup_kind = 2;
gui->popup_context = other_object;
gui->popup_title = object;
gui->popup_open_time = now;
__builtin_memset(&gui->popup_opacity, 0, 0x10);
gui->popup_callback = cb;
*(Col3b *)&gui->popup_rgb = in;
gui->popup_hsl.h = h;
gui->popup_hsl.s = s;
gui->popup_hsl.l = l;
__builtin_memset(&gui->popup_list_state, 0, 0x50);
}
// ---- draw the open popup: title bar with close cross, then by kind (popup_kind): 1 text prompt, 2 colour
// picker (hex field, HSL sliders, recent colours), 3 confirm, 4 searchable list. Closes it when done. ----
struct GW16 { void *ig; double t; };
struct Grad { float v[19]; };
struct Hit { unsigned long long idx; const char *name; };
FN(void, pop_layout_row, (GuiState *gui, int n));
FN(R16d, add_cell, (GuiState *gui, float h));
FN(void, nvg_FillColor, (void *nvg, V4b *c));
FN(void, nvg_StrokeColor, (void *nvg, V4b *c));
FN(void, nvg_StrokeWidth, (void *nvg, float w));
FN(void, nvg_FontSize, (void *nvg, float size));
FN(void, draw_rect_rounded, (GW16 *window, R16d r, float round));
FN(void, draw_rect_stroked, (GW16 *window, R16d r, float round));
FN(void, begin_content, (GW16 *window, GuiState *gui, R16d r, float pad, float f));
FN(bool, mouse_over_rect_r, (GuiState *gui, R16d r));
FN(unsigned char, point_in_rect_local, (void *gui, R16d r)) __asm__("clicked_inside_r_frame");
FN(void, nvg_BeginPath, (void *nvg));
FN(void, nvg_MoveTo, (void *nvg, float x, float y));
FN(void, nvg_LineTo, (void *nvg, float x, float y));
FN(void, nvg_Stroke, (void *nvg));
FN(void, nvg_Fill, (void *nvg));
FN(R16d, inset_rect, (R16d r, float d));
FN(void *, take_dragged_item, (GuiState *gui));
FN(char *, dragged_payload, (GuiState *gui));
FN(R16d, make_rect, (float x, float y, float w, float h));
FN(V4b, nvg_rgb_f, (float r, float g, float b));
FN(V4b, nvg_hsl, (float h, float s, float l));
FN(V4b, nvg_lerp_rgba, (V4b a, V4b b, float t));
FN(Grad, nvg_linear_gradient, (void *nvg, float x0, float y0, float x1, float y1, V4b c0, V4b c1));
FN(void, nvg_FillPaint, (void *nvg, Grad g));
FN(void, nvg_RoundedRectVarying, (void *nvg, float x, float y, float w, float h, float r0, float r1, float r2, float r3));
FN(void, nvg_Rect, (void *nvg, float x, float y, float w, float h));
FN(void, nvg_RoundedRect, (void *nvg, float x, float y, float w, float h, float r));
FN(float, libc_ceilf, (float x));
FN(float, libc_floorf, (float x));
FN(float, draw_section_label, (GuiState *gui, GW16 *window, const char *label));
FN(bool, is_packet_cancelled, (GuiState *gui, GW16 *window, R16d r, unsigned idx, V4b *col));
FN(void, hex_colour_text_filter, (char *buf, char character));
FN(float, end_layout_row, (GW16 *window, GuiState *gui));
FN(void *, libc_calloc, (unsigned long long n, unsigned long long size));
FN(unsigned char, vector_grow, (void *vec, unsigned long long elem, unsigned long long n, void *cap));
FN(int, libc_snprintf, (char *buf, unsigned long long n, const char *fmt, ...));
extern "C" const float colour_level_1_in_gui_popups_d, colour_level_0_0784314_in_gui_popups, twenty_in_gui_popups, corner_radius_8_in_gui_popups_c, font_size_16_in_gui_popups, colour_level_0_866667_in_gui_popups_c, colour_level_0_6_in_gui_popups_a,
colour_level_0_933333_in_gui_popups, colour_level_0_345098_in_gui_popups, stroke_width_2_in_gui_popups_a, row_height_30, font_size_15_in_gui_popups, colour_level_0_25098_in_gui_popups_a, inset_4_in_gui_popups_a, row_height_25,
factor_0_5_in_gui_popups_a, colour_level_0_278431_in_gui_popups, corner_radius_5_in_gui_popups, row_height_24, colour_byte_max, gui_popup_button_hover_grey_level, divisor_6_in_gui_popups_a, offset_neg_56,
factor_0_125_b, gui_popup_hover_grey_level;
extern "C" const double divisor_0_3_in_gui_popups, factor_0_2_in_gui_popups, threshold_0_25, divisor_255;
extern "C" const char str_hue[], str_saturation[], str_lightness[], str_recently_used[], str_str[], fmt_hex2_hex2_hex2[],
str_continue[], str_d_more[];
extern "C" char color_hex_string_buffer[];
static inline __attribute__((always_inline)) unsigned char lower16(unsigned char c) {
return (unsigned char)(c - 'A') < 26 ? (unsigned char)(c | 0x20) : c;
}
static inline __attribute__((always_inline)) unsigned char hexval(signed char c) {
return (unsigned char)(c + (c >= 'a' ? 0xa9 : 0xd0));
}
// popup_rgb -> popup_hsl
static inline __attribute__((always_inline)) void rgb_to_hsl(GuiState *gui) {
float r = gui->popup_rgb.r, gg = gui->popup_rgb.g, bb = gui->popup_rgb.b;
float mx = __builtin_fmaxf(__builtin_fmaxf(bb, gg), r);
float mn = __builtin_fminf(__builtin_fminf(bb, gg), r);
float sum = mx + mn;
float l = factor_0_5_in_gui_popups_a * sum;
float h = 0.0f, s = 0.0f;
if (mx != mn) {
float d = mx - mn;
float denom = factor_0_5_in_gui_popups_a < l ? stroke_width_2_in_gui_popups_a - mx - mn : sum;
if (mx == r) h = (gg - bb) / d + (gg < bb ? divisor_6_in_gui_popups_a : 0.0f);
else if (mx == gg) h = (bb - r) / d + stroke_width_2_in_gui_popups_a;
else if (mx == bb) h = (r - gg) / d + inset_4_in_gui_popups_a;
else h = l;
s = d / denom;
h = h / divisor_6_in_gui_popups_a;
}
gui->popup_hsl.h = h;
gui->popup_hsl.s = s;
gui->popup_hsl.l = l;
}
// one HSL slider: gradient from c(t=0) to c(t=1) with the knob at the current value
static inline __attribute__((always_inline)) bool hsl_bar(GuiState *gui, GW16 *window, unsigned idx, V4b c0, V4b c1, float t) {
V4b mid = nvg_lerp_rgba(c0, c1, t);
void *nvg = window->ig;
R16d r = add_cell(gui, font_size_15_in_gui_popups);
nvg_FillPaint(nvg, nvg_linear_gradient(nvg, r.x, r.y, r.w + r.x, r.h + r.y, c0, c1));
draw_rect_rounded(window, r, corner_radius_5_in_gui_popups);
return is_packet_cancelled(gui, window, r, idx, &mid);
}
extern "C" void close_popup_animated(GuiState *gui, GW16 *window) {
gui->popup_animating = 1;
float op = (float)((double)gui->popup_opacity + (window->t - gui->popup_open_time) / divisor_0_3_in_gui_popups);
gui->popup_opacity = colour_level_1_in_gui_popups_d < op ? colour_level_1_in_gui_popups_d : op;
pop_layout_row(gui, 1);
R16d box = add_cell(gui, gui->popup_height);
void *nvg = window->ig;
V4b bg = nvg_rgba_f(colour_level_0_0784314_in_gui_popups, colour_level_0_0784314_in_gui_popups, colour_level_0_0784314_in_gui_popups, 1.0f);
nvg_FillColor(nvg, &bg);
draw_rect_rounded(window, box, twenty_in_gui_popups);
begin_content(window, gui, box, corner_radius_8_in_gui_popups_c, 12.0f);
pop_layout_row(gui, 1);
void *tig = window->ig;
R16d t = add_cell(gui, twenty_in_gui_popups);
nvg_FontSize(tig, font_size_16_in_gui_popups);
V4b tc = nvg_rgba_f(colour_level_0_866667_in_gui_popups_c, colour_level_0_866667_in_gui_popups_c, colour_level_0_866667_in_gui_popups_c, 1.0f);
nvg_FillColor(tig, &tc);
draw_text(window, t, 0, 0, gui->popup_title);
// close cross in the title bar's right square
float cx = t.w - t.h + t.x;
R16d xr = {cx, t.y, t.h, t.h};
V4b xc = mouse_over_rect_r(gui, xr) ? nvg_rgba_f(colour_level_0_933333_in_gui_popups, colour_level_0_345098_in_gui_popups, colour_level_0_345098_in_gui_popups, 1.0f)
: nvg_rgba_f(colour_level_0_6_in_gui_popups_a, colour_level_0_6_in_gui_popups_a, colour_level_0_6_in_gui_popups_a, 1.0f);
nvg_StrokeColor(tig, &xc);
nvg_StrokeWidth(tig, stroke_width_2_in_gui_popups_a);
float in = (float)((double)t.h * factor_0_2_in_gui_popups);
nvg_BeginPath(tig);
float x0 = cx + in, y0 = t.y + in;
nvg_MoveTo(tig, x0, y0);
float x1 = t.h + cx - in, y1 = t.y + t.h - in;
nvg_LineTo(tig, x1, y1);
nvg_MoveTo(tig, x1, y0);
nvg_LineTo(tig, x0, y1);
nvg_Stroke(tig);
bool closed = point_in_rect_local(gui, R16d{cx, t.y, t.h, t.h});
int res = 1; // 0 keep open, 1 close (cancel), 2 close (done)
void *key = &gui->popup_kind;
switch (gui->popup_kind_i) {
case 1: { // text prompt -> cb(ctx, text)
void *fig = window->ig;
R16d f = add_cell(gui, row_height_30);
nvg_FontSize(fig, font_size_15_in_gui_popups);
V4b oc = nvg_rgba_f(colour_level_0_25098_in_gui_popups_a, colour_level_0_25098_in_gui_popups_a, colour_level_0_25098_in_gui_popups_a, 1.0f);
nvg_StrokeColor(fig, &oc);
nvg_StrokeWidth(fig, stroke_width_2_in_gui_popups_a);
draw_rect_stroked(window, f, corner_radius_8_in_gui_popups_c);
res = 1;
if (!dragged_item_is(gui, key)) break;
f = inset_rect(f, inset_4_in_gui_popups_a);
draw_tracked_text(gui, window, &f);
void *s = take_dragged_item(gui);
if (!s) {
res = 0;
break;
}
((void (*)(void *, void *))gui->popup_callback)(gui->popup_context, s);
res = 2;
break;
}
case 2: { // colour picker
void *pig = window->ig;
R16d f = add_cell(gui, row_height_24);
bool hov = mouse_over_rect_r(gui, f);
float fh = f.h;
V4b sw = nvg_rgb_f(gui->popup_rgb.r, gui->popup_rgb.g, gui->popup_rgb.b);
nvg_FillColor(pig, &sw);
draw_rect_rounded(window, R16d{f.x, f.y, fh, fh}, corner_radius_5_in_gui_popups);
V4b oc = nvg_rgba_f(colour_level_0_25098_in_gui_popups_a, colour_level_0_25098_in_gui_popups_a, colour_level_0_25098_in_gui_popups_a, 1.0f);
nvg_StrokeColor(pig, &oc);
nvg_StrokeWidth(pig, stroke_width_2_in_gui_popups_a);
nvg_Stroke(pig);
nvg_FontSize(pig, twenty_in_gui_popups);
V4b hc = nvg_rgba_f(colour_level_0_6_in_gui_popups_a, colour_level_0_6_in_gui_popups_a, colour_level_0_6_in_gui_popups_a, 1.0f);
nvg_FillColor(pig, &hc);
float hx = f.x + fh + corner_radius_8_in_gui_popups_c;
float hw = f.w - (f.x - hx);
float ex = draw_text(window, R16d{hx, f.y, hw, f.h}, 0, 0, (void *)str_str /* "#" */);
hx -= ex;
hw -= hx;
R16d hr = {ex, f.y, hw, f.h};
R16 ed;
__builtin_memcpy(&ed, &f, sizeof ed);
if (drag_item(gui, window, &ed, (void *)hex_colour_text_filter, &gui->popup_callback)) {
V4b wc = nvg_rgba_f(colour_level_1_in_gui_popups_d, colour_level_1_in_gui_popups_d, colour_level_1_in_gui_popups_d, 1.0f);
nvg_FillColor(pig, &wc);
draw_clipped_text(gui, window, &hr, 0);
const char *s = (const char *)take_dragged_item(gui);
if (s) {
if (libc_strlen(s) > 5) {
gui->popup_rgb.r =
(float)((double)(hexval(s[0]) << 4 | hexval(s[1])) / divisor_255);
gui->popup_rgb.g =
(float)((double)(hexval(s[2]) << 4 | hexval(s[3])) / divisor_255);
gui->popup_rgb.b =
(float)((double)(hexval(s[4]) << 4 | hexval(s[5])) / divisor_255);
}
rgb_to_hsl(gui);
}
} else {
float k = colour_byte_max;
libc_snprintf(color_hex_string_buffer, 7, fmt_hex2_hex2_hex2 /* "%02x%02x%02x" */,
(unsigned char)(long long)(gui->popup_rgb.r * k),
(unsigned char)(long long)(gui->popup_rgb.g * k),
(unsigned char)(long long)(k * gui->popup_rgb.b));
float c = hov ? gui_popup_button_hover_grey_level : colour_level_0_6_in_gui_popups_a;
V4b tc2 = nvg_rgba_f(c, c, c, 1.0f);
nvg_FillColor(pig, &tc2);
draw_text(window, hr, 0, 0, color_hex_string_buffer);
}
// hue: six gradient segments, rounded at the ends
draw_section_label(gui, window, str_hue /* "Hue" */);
void *hig = window->ig;
R16d hb = add_cell(gui, font_size_15_in_gui_popups);
float seg = hb.w / divisor_6_in_gui_popups_a;
float sw2 = libc_ceilf(seg);
for (unsigned long long k = 0; k != 6; k++) {
float sx = libc_floorf((float)k * seg + hb.x);
V4b c0 = nvg_hsl((float)k / divisor_6_in_gui_popups_a, gui->popup_hsl.s, gui->popup_hsl.l);
V4b c1 = nvg_hsl((float)(k + 1) / divisor_6_in_gui_popups_a, gui->popup_hsl.s, gui->popup_hsl.l);
Grad gr = nvg_linear_gradient(hig, sx, hb.y, seg + sx, hb.y, c0, c1);
nvg_BeginPath(hig);
if (k == 5) nvg_RoundedRectVarying(hig, sx, hb.y, sw2, hb.h, 0.0f, 5.0f, 5.0f, 0.0f);
else if (k == 0) nvg_RoundedRectVarying(hig, sx, hb.y, sw2, hb.h, 5.0f, 0.0f, 0.0f, 5.0f);
else nvg_Rect(hig, sx, hb.y, sw2, hb.h);
nvg_FillPaint(hig, gr);
nvg_Fill(hig);
}
V4b cur = nvg_hsl(gui->popup_hsl.h, gui->popup_hsl.s, gui->popup_hsl.l);
bool ch = is_packet_cancelled(gui, window, hb, 0, &cur);
draw_section_label(gui, window, str_saturation /* "Saturation" */);
bool cs = hsl_bar(gui, window, 1, nvg_hsl(gui->popup_hsl.h, 0.0f, gui->popup_hsl.l),
nvg_hsl(gui->popup_hsl.h, colour_level_1_in_gui_popups_d, gui->popup_hsl.l), gui->popup_hsl.s);
draw_section_label(gui, window, str_lightness /* "Lightness" */);
bool cl = hsl_bar(gui, window, 2, nvg_hsl(gui->popup_hsl.h, gui->popup_hsl.s, 0.0f),
nvg_hsl(gui->popup_hsl.h, gui->popup_hsl.s, colour_level_1_in_gui_popups_d), gui->popup_hsl.l);
if (ch || cs || cl) {
V4b c = nvg_hsl(gui->popup_hsl.h, gui->popup_hsl.s, gui->popup_hsl.l);
__builtin_memcpy(gui->popup_message, &c, 8);
gui->popup_rgb.b = c.z;
}
// recently used colours (ring buffer, newest first)
draw_section_label(gui, window, str_recently_used /* "Recently used" */);
void *rig = window->ig;
R16d rr = add_cell(gui, row_height_25);
float x = rr.x;
float sz = (rr.w + offset_neg_56) * factor_0_125_b;
nvg_StrokeWidth(rig, stroke_width_2_in_gui_popups_a);
float step = corner_radius_8_in_gui_popups_c + sz;
unsigned long long pick = 8;
for (long long i = -8; i; i++) {
unsigned long long p = gui_popups_recent_colour_ring_position + i + 8;
unsigned long long idx = p < 8 ? p : gui_popups_recent_colour_ring_position + i;
R16d cell = make_rect(x, rr.y, sz, rr.h);
float k = mouse_over_rect_r(gui, cell) ? gui_popup_hover_grey_level : colour_level_0_25098_in_gui_popups_a;
V4b oc2 = nvg_rgba_f(k, k, k, 1.0f);
nvg_StrokeColor(rig, &oc2);
Col3b *e = &recent_colors_ring[idx];
if (point_in_rect_local(gui, cell)) pick = idx;
nvg_BeginPath(rig);
nvg_RoundedRect(rig, cell.x, cell.y, cell.w, cell.h, 5.0f);
V4b fc = nvg_rgb_f(e->r, e->g, e->b);
nvg_FillColor(rig, &fc);
nvg_Fill(rig);
nvg_Stroke(rig);
x += step;
}
if (pick != 8) {
*(Col3b *)&gui->popup_rgb = recent_colors_ring[pick];
rgb_to_hsl(gui);
}
res = 0;
break;
}
case 3: { // confirm -> cb(ctx)
void *cig = window->ig;
R16d f = add_cell(gui, row_height_30);
nvg_FontSize(cig, font_size_15_in_gui_popups);
V4b mc = nvg_rgba_f(colour_level_0_866667_in_gui_popups_c, colour_level_0_866667_in_gui_popups_c, colour_level_0_866667_in_gui_popups_c, 1.0f);
nvg_FillColor(cig, &mc);
draw_text(window, f, 0, 0, gui->popup_message);
R16d b = add_cell(gui, row_height_30);
V4b bc = mouse_over_rect_r(gui, b) ? nvg_rgba_f(colour_level_0_933333_in_gui_popups, colour_level_0_345098_in_gui_popups, colour_level_0_345098_in_gui_popups, 1.0f)
: nvg_rgba_f(colour_level_0_866667_in_gui_popups_c, colour_level_0_278431_in_gui_popups, colour_level_0_278431_in_gui_popups, 1.0f);
nvg_FillColor(cig, &bc);
draw_rect_rounded(window, b, corner_radius_5_in_gui_popups);
V4b wc = nvg_rgba_f(colour_level_1_in_gui_popups_d, colour_level_1_in_gui_popups_d, colour_level_1_in_gui_popups_d, 1.0f);
nvg_FillColor(cig, &wc);
draw_text(window, b, 1, 0, (void *)str_continue /* "Continue" */);
res = 0;
if (!point_in_rect_local(gui, b)) break;
((void (*)(void *))gui->popup_callback)(gui->popup_context);
res = 2;
break;
}
case 4: { // searchable list -> cb(ctx, index, name)
R16d f = add_cell(gui, row_height_30);
void *lig = window->ig;
nvg_FontSize(lig, font_size_15_in_gui_popups);
V4b oc = nvg_rgba_f(colour_level_0_25098_in_gui_popups_a, colour_level_0_25098_in_gui_popups_a, colour_level_0_25098_in_gui_popups_a, 1.0f);
nvg_StrokeColor(lig, &oc);
nvg_StrokeWidth(lig, stroke_width_2_in_gui_popups_a);
draw_rect_stroked(window, f, corner_radius_8_in_gui_popups_c);
if (!dragged_item_is(gui, key)) break;
f = inset_rect(f, inset_4_in_gui_popups_a);
draw_tracked_text(gui, window, &f);
const char *q = dragged_payload(gui);
double now = window->t;
if (!(threshold_0_25 > now - gui->popup_list_state)) { // rebuild the matches at most every 0.25 s
ListV *popup_list_view = ((ListV *)gui->popup_list_view);
if (!popup_list_view) {
popup_list_view = (ListV *)libc_calloc(1, 0x18);
gui->popup_list_view = popup_list_view;
if (!popup_list_view) goto shown;
}
popup_list_view->n = 0;
popup_list_view->cap = 0;
libc_free(popup_list_view->data);
popup_list_view->data = 0;
gui->popup_list_count = 0;
for (unsigned long long k = 0; k < gui->popup_list_extra_u; k++) {
const char *it = (((const char **)gui->popup_list_items))[k];
unsigned long long il = libc_strlen(it), ql = libc_strlen(q);
if (il < ql) continue;
if (ql) { // case-insensitive substring
for (unsigned long long st = 0;; st++) {
unsigned long long p = 0;
while (lower16(it[st + p]) == lower16(q[p]))
if (++p == ql) goto match;
if (st + 1 > il - ql) goto next;
}
}
match : {
ListV *m = ((ListV *)gui->popup_list_view);
if (m->n >= 0x1e) {
(gui->popup_list_count)++;
continue;
}
if (!vector_grow(&m->data, 0x10, m->n, &m->cap)) continue;
Hit *h = &((Hit *)m->data)[m->n];
h->idx = k;
h->name = it;
m->n++;
}
next:;
}
gui->popup_list_state = now;
}
shown : {
if (!gui->popup_list_view) {
res = 0;
break;
}
unsigned long long cnt = (((ListV *)gui->popup_list_view))->n;
if (!cnt) {
res = 0;
break;
}
void *tig2 = window->ig;
nvg_FontSize(tig2, font_size_16_in_gui_popups);
const char *chosen = 0;
unsigned long long cidx = 0;
R16d row = {};
for (unsigned long long i = 0; i != cnt; i++) { // two columns
float cx2, cw;
if (i & 1) {
cw = row.w * factor_0_5_in_gui_popups_a;
cx2 = row.x + cw;
} else {
row = add_cell(gui, row_height_25);
cw = row.w * factor_0_5_in_gui_popups_a;
cx2 = row.x;
}
R16d cell = {cx2, row.y, cw, row.h};
float k = mouse_over_rect_r(gui, cell) ? colour_level_1_in_gui_popups_d : colour_level_0_866667_in_gui_popups_c;
V4b ic = nvg_rgba_f(k, k, k, 1.0f);
nvg_FillColor(tig2, &ic);
Hit *h = &((Hit *)(((ListV *)gui->popup_list_view))->data)[i];
unsigned long long hi = h->idx;
const char *hn = h->name;
draw_text(window, cell, 0, 0, (void *)hn);
if (point_in_rect_local(gui, cell)) {
chosen = hn;
cidx = hi;
}
}
unsigned long long more = gui->popup_list_count;
if (more) {
char buf[0xe];
libc_snprintf(buf, 0xe, str_d_more /* "+%d more..." */, more < 0x270f ? more : 0x270f);
R16d mr = add_cell(gui, row_height_25);
V4b mc = nvg_rgba_f(colour_level_0_6_in_gui_popups_a, colour_level_0_6_in_gui_popups_a, colour_level_0_6_in_gui_popups_a, 1.0f);
nvg_FillColor(tig2, &mc);
nvg_FontSize(tig2, font_size_15_in_gui_popups);
draw_text(window, mr, 0, 0, buf);
}
res = 0;
if (!chosen) break;
((void (*)(void *, unsigned long long, const char *))gui->popup_list_callback)(gui->popup_context, cidx, chosen);
res = 2;
break;
}
}
}
gui->popup_height = end_layout_row(window, gui);
gui->popup_animating = 0;
gui->popup_open_time = window->t;
if (res) set_popup_open(gui, res == 1);
else if (closed) set_popup_open(gui, true);
}
// ---- selectable label: rounded box (highlighted when `hot`), text centred; returns whether it was clicked ----
extern "C" const float colour_level_0_25098_in_gui_popups, corner_radius_8_in_gui_popups, gui_popup_disabled_grey_level, gui_popup_enabled_grey_level, font_size_15_in_gui_popups_a;
extern "C" bool draw_icon_button(GuiState *gui, GW16 *window, const char *text, bool selectable, const R16d *rect) {
void *nvg = window->ig;
bool clicked = selectable ? point_in_rect_local(gui, *rect) : false;
V4b bg = nvg_rgba_f(colour_level_0_25098_in_gui_popups, colour_level_0_25098_in_gui_popups, colour_level_0_25098_in_gui_popups, 1.0f);
nvg_FillColor(nvg, &bg);
draw_rect_rounded(window, *rect, corner_radius_8_in_gui_popups);
float k = selectable ? gui_popup_enabled_grey_level : gui_popup_disabled_grey_level;
V4b fg = nvg_rgba_f(k, k, k, 1.0f);
nvg_FillColor(nvg, &fg);
nvg_FontSize(nvg, font_size_15_in_gui_popups_a);
draw_text(window, *rect, 1, 0, (void *)text);
return clicked;
}
+441
View File
@@ -0,0 +1,441 @@
// 0x11000: GUI state helpers (drag state at +0x370, flags at +0x4c8..)
#include "slinky/engine.h"
#include "slinky/gui_state.h"
extern "C" __attribute__((noinline)) void mark_click_consumed(GuiState *gui) {
if (gui->click_capture == 1 && gui->click_consumed == 0) { gui->click_consumed = 1; }
SHARED_EPILOG();
}
extern "C" void begin_drag(GuiState *gui, void *object, void *other_object) {
gui->input_mode = 1;
gui->keybind_callback = object;
gui->keybind_target = other_object;
}
extern "C" bool drag_owner_is(GuiState *gui, void *object) {
if (gui->input_mode != 1) return false;
return gui->keybind_target == object;
}
extern "C" void end_drag(GuiState *gui) {
if (gui->input_mode == 2) gui->input_mode = 3;
}
extern "C" void *dragged_payload(GuiState *gui) { return (gui->input_mode_u & ~1u) == 2 ? gui->edit_text : 0; }
extern "C" void request_unload(GuiState *gui) { gui->unload_requested = 1; }
extern "C" void set_tab_animation(GuiState *gui, double t) {
gui->card_open_progress_i = 0;
gui->card_anim_start_time = t;
}
extern "C" bool dragged_item_is(GuiState *gui, void *item) {
if ((gui->input_mode_u & ~1u) != 2) return false;
return gui->edit_filter_arg == item;
}
extern "C" __attribute__((noinline)) void *take_dragged_item(GuiState *gui) {
if ((gui->input_mode_u & ~1u) != 2 || gui->edit_confirmed != 1) return 0;
void *edit_text = gui->edit_text;
gui->input_mode = 0;
gui->edit_confirmed = 0;
return edit_text;
}
struct R16b { long long a, b; };
FN(bool, popup_is_open, (GuiState *gui));
FN(unsigned char, popup_is_animating, (GuiState *gui));
FN(bool, point_in_rect, (R16b r, unsigned long long mouse));
// is the mouse over rect r (and no drag in progress)?
extern "C" __attribute__((noinline)) bool mouse_over_rect_r(GuiState *gui, const R16b *r) {
if (popup_is_open(gui) && !popup_is_animating(gui)) return false;
if (!point_in_rect(*r, *(unsigned long long *)&gui->mouse_scene)) return false;
int st = gui->input_mode;
return (st == 0) | (st == 3);
}
// clicked inside r this frame (consumes the click)
extern "C" bool clicked_inside_r_frame(GuiState *gui, R16b r) {
R16b c = r;
if (popup_is_open(gui) && !popup_is_animating(gui)) return false;
if (!point_in_rect(c, *(unsigned long long *)&gui->mouse_scene)) return false;
int st = gui->input_mode;
if (st != 3 && st != 0) return false;
if (gui->click_capture != 1 || gui->click_consumed != 0) return false;
gui->click_consumed = 1;
return true;
}
// start dragging item b (owner a)
extern "C" void start_dragging_item_b(GuiState *gui, void *item, void *other_object) {
if ((gui->input_mode_u & ~1u) == 2 && gui->edit_filter_arg == other_object) return;
gui->input_mode = 2;
__builtin_memset(gui->edit_text, 0, 0x80);
gui->edit_char_filter = item;
gui->edit_filter_arg = other_object;
}
// ---- category tab bar: 7 tabs with a sliding highlight; tab kind 6 = "Unload Slinky?" ----
struct R16t { float x, y, w, h; };
struct V4t { float x, y, z, w; };
struct WT3 { void *ig; double t; };
FN(R16t, add_cell, (GuiState *gui, float h));
FN(V4t, nvg_rgba_f, (float r, float g, float b, float a));
FN(void, nvg_FillColor, (void *nvg, V4t *c));
FN(void, draw_rect_rounded, (WT3 *w, const R16t *r, float round));
FN(float, lerp_clamped_float, (float a, float b, float t));
FN(R16t, make_rect, (float x, float y, float w, float h));
FN(R16t, inset_rect, (R16t r, float d));
FN(void, nvg_FontSize, (void *nvg, float size));
FN(unsigned char, clicked_inside_r_frame_local, (GuiState *gui, R16t r)) __asm__("clicked_inside_r_frame");
FN(float, draw_text, (WT3 *w, R16t r, int align, int col, void *text));
FN(void, confirmation_popup_formatted_message, (void *gui, void *object, void *other_object, const char *fmt, ...));
FN(void, request_unload, (GuiState *gui));
extern "C" const float row_height_42, gui_panel_grey_level, corner_radius_20_in_gui_state, divisor_7, gui_selection_inset, gui_tab_highlight_grey_level, corner_radius_14,
font_size_16, colour_level_0_866667, colour_level_1_in_gui_state, colour_level_0_278431, colour_level_0_933333, colour_level_0_345098;
extern "C" const double divisor_0_05;
extern "C" const char str_unload_slinky[]; // "Unload Slinky?"
struct Tab { int kind; int padding_04; const char *label; };
extern "C" void draw_tab_bar(GuiState *gui, WT3 *window, float dt) {
void *nvg = window->ig;
R16t bar = add_cell(gui, row_height_42);
float k = gui_panel_grey_level;
V4t c = nvg_rgba_f(k, k, k, 1.0f);
nvg_FillColor(nvg, &c);
draw_rect_rounded(window, &bar, corner_radius_20_in_gui_state);
float tw = bar.w / divisor_7;
float pos = lerp_clamped_float(gui->tab_highlight_pos, (float)gui->selected_tab,
(float)((double)dt / divisor_0_05));
gui->tab_highlight_pos = pos;
float inset = gui_selection_inset;
R16t sel = inset_rect(make_rect(bar.x + pos * tw, bar.y, tw, bar.h), inset);
float ks = gui_tab_highlight_grey_level;
V4t sc = nvg_rgba_f(ks, ks, ks, 1.0f);
nvg_FillColor(window->ig, &sc);
draw_rect_rounded(window, &sel, corner_radius_14);
nvg_FontSize(nvg, font_size_16);
Tab *tab = (Tab *)(gui->tabs);
for (unsigned long long i = 0; i != 7; i++, tab++) {
float cw = bar.w / divisor_7;
R16t tr = make_rect((float)(long long)i * cw + bar.x, bar.y, cw, bar.h);
if (clicked_inside_r_frame_local(gui, tr)) {
if (tab->kind == 6) {
confirmation_popup_formatted_message(gui, gui, (void *)request_unload, str_unload_slinky);
} else {
gui->selected_tab = i;
gui->input_mode = 0;
}
}
R16t in = inset_rect(tr, inset);
V4t tc;
bool hov = mouse_over_rect_r(gui, (const R16b *)&in);
if (tab->kind == 6) tc = hov ? nvg_rgba_f(colour_level_0_933333, colour_level_0_345098, colour_level_0_345098, 1.0f)
: nvg_rgba_f(colour_level_0_866667, colour_level_0_278431, colour_level_0_278431, 1.0f);
else tc = hov ? nvg_rgba_f(colour_level_1_in_gui_state, colour_level_1_in_gui_state, colour_level_1_in_gui_state, 1.0f)
: nvg_rgba_f(colour_level_0_866667, colour_level_0_866667, colour_level_0_866667, 1.0f);
nvg_FillColor(window->ig, &tc);
draw_text(window, in, 1, 0, (void *)tab->label);
}
}
// ---- click GUI frame: window rect + open animation, scroll, tab bar, category page (module cards or the
// profile list), toast, modal popup ----
struct GWin { void *ig; double t; void *padding_10; float w, h, scale; };
struct R2f { float x, y; };
struct Prof { const char *name, *end; char desc[0x28]; int key; int padding_3c; };
struct GEv { int type, padding_04; union { void *p; int i; } a, b; char rest[0x30]; };
typedef void (*SetBindF)(void *mod, int key);
typedef int v4i11 __attribute__((vector_size(16)));
typedef float v4f11 __attribute__((vector_size(16)));
FN(void, nvg_Save, (void *nvg));
FN(void, nvg_Restore, (void *nvg));
FN(void, nvg_GlobalAlpha, (void *nvg, float a));
FN(void, nvg_Translate, (void *nvg, float x, float y));
FN(void, nvg_Scale, (void *nvg, float x, float y));
FN(R2f, rot_scale, (R2f a, float s));
FN(void, card_open_animation, (GWin *window, float t));
FN(void, reset_layout_columns, (GuiState *gui));
FN(void, push_layout_row, (GuiState *gui, R16t r, float sp, float pad));
FN(void, pop_layout_row, (GuiState *gui, int n));
FN(void, draw_module_cards, (GuiState *gui, GWin *window, void *mgr, unsigned cat, float dt));
FN(int, load_profiles, (void *profiles));
FN(void, dispatch_event, (void *mgr, GEv *e));
FN(void, choose_columns, (GuiState *gui, float w));
FN(Prof *, profile_array, (void *profiles, unsigned long long *n));
FN(bool, rects_intersect, (R16t a, R16t b));
FN(void, begin_content, (GWin *window, GuiState *gui, R16t r, float pad, float f));
FN(float, draw_text_sized, (GWin *window, R16t r, unsigned align, unsigned col, const char *text, const char *end));
FN(SetBindF, profile_set_bind, (void *profiles));
FN(void, draw_module_description, (GuiState *gui, GWin *window, SetBindF cb, void *target, int key, unsigned char *hold, R16t r));
FN(void, ask_sure_before_running_action, (GuiState *gui, void *mgr, void *profiles, const char *name, int kind));
FN(void, draw_texture, (GWin *window, R16t r, unsigned icon, float alpha));
FN(void, set_slider_text, (GuiState *gui, GWin *window, const char *tip, int x, int y));
FN(bool, get_profile_dirty, ());
FN(const char *, get_saved_string, ());
FN(int, libc_strcmp, (const char *left, const char *right)); // strcmp
FN(int, load_profile_name, (const char *name, void *mgr)); // load profile
FN(int, save_profile, (const char *name, bool text, void *mgr)); // save profile
FN(V4t, nvg_trans_rgba_f, (V4t c, float k));
FN(void, file_name_text_filter, (char *buf, char character));
FN(bool, drag_item, (GuiState *gui, GWin *window, R16t r, void (*filter)(char *, char), void *b));
FN(void, draw_tracked_text, (GuiState *gui, GWin *window, const R16t *r));
FN(float, end_layout_row, (GWin *window, GuiState *gui));
FN(float, ease_curve, (float t));
FN(float, nvg_text_bounds, (void *nvg, float x, float y, const char *text, const char *end, const float *bounds)); // nvgTextBounds
FN(void, close_popup_animated, (GuiState *gui, GWin *window));
FN(bool, mouse_over_rect_r_local, (GuiState *gui, R16t r)) __asm__("mouse_over_rect_r");
FN(void, draw_rect_rounded_local, (WT3 *w, R16t r, float round)) __asm__("draw_rect_rounded");
extern "C" const float offset_neg_16, gui_wide_layout_max_width, gui_narrow_layout_max_width, half_in_gui_state, one_in_gui_state_a, row_padding_8,
row_padding_12, gui_profiles_column_width, colour_level_0_0784314_in_gui_state, row_height_20_in_gui_state_a, font_size_16_in_gui_state, colour_level_0_866667_in_gui_state, rect_extent_5, font_size_14_in_gui_state,
colour_level_0_6, font_size_15, gui_tab_font_size, offset_10, gui_tab_bar_vertical_offset, colour_level_0_25098_in_gui_state, threshold_600, offset_neg_600;
extern "C" const double divisor_0_3, factor_neg_0_1, offset_2, factor_0_5_in_gui_state, offset_neg_8, divisor_0_1_in_gui_state,
factor_neg_0_5, offset_neg_5;
extern "C" const v4i11 gui_state_sign_abs_masks, gui_state_sign_abs_masks_second; // sign / abs masks
extern "C" const char str_default[], str_contains_the_default_configura[], str_default_profile_cannot_be_dele[], str_default_profile_cannot_be_over[], str_profile_name_is_taken[], str_create_new_profile[];
extern "C" const int gui_state_tooltip_strings_as_offsets_from[]; // tooltip strings as offsets from the table ("Click to load", ...)
extern "C" const unsigned gui_state_button_icons[]; // button icons
static inline __attribute__((always_inline)) float mask11(float x, v4i11 m, bool x_or) {
v4f11 v = {x, 0, 0, 0};
return ((v4f11)(x_or ? ((v4i11)v ^ m) : ((v4i11)v & m)))[0];
}
static inline __attribute__((always_inline)) void prof_event(void *mgr, int type, GEv *ev) {
ev->type = type;
dispatch_event(mgr, ev);
}
FN(void, nvg_Scissor, (void *nvg, float x, float y, float w, float h));
extern "C" void draw_gui_frame(GuiState *gui, GWin *window, void *mgr, void *profiles) {
void *nvg = window->ig;
nvg_Save(nvg);
card_open_animation(window, gui->card_open_progress);
float ww = window->w;
float pw = offset_neg_16 + ww;
float lim = gui->wide_layout ? gui_wide_layout_max_width : gui_narrow_layout_max_width;
pw = pw < lim ? pw : lim;
float px = (ww - pw) * half_in_gui_state;
float *scroll = (float *)(&gui->scroll);
float s = *scroll;
if (0.0f > s) *scroll = 0.0f;
else if (s > gui->scroll_max) *scroll = gui->scroll_max;
float one = one_in_gui_state_a;
R2f m = rot_scale(*(R2f *)&gui->mouse_raw, one / window->scale);
*(R2f *)&gui->mouse_scene = m;
gui->mouse_scene.y = m.y + *scroll;
nvg_Translate(window->ig, 0.0f, mask11(*scroll, gui_state_sign_abs_masks, true));
reset_layout_columns(gui);
R16t win = make_rect(px, 0.0f, pw, window->h);
float dt = (float)(window->t - gui->card_anim_start_time);
double dtd = dt;
float op = (float)((double)gui->card_open_progress + dtd / divisor_0_3);
gui->card_open_progress = one < op ? one : op;
push_layout_row(gui, win, row_padding_8, row_padding_12);
pop_layout_row(gui, 1);
draw_tab_bar(gui, (WT3 *)window, dt);
unsigned long long cat = gui->selected_tab;
float off = (float)cat - gui->tab_highlight_pos;
if (off != 0.0f) { // page slides while the tab highlight moves
void *sig = window->ig;
float d = mask11(off, gui_state_sign_abs_masks_second, false);
nvg_GlobalAlpha(sig, half_in_gui_state < d ? 0.0f : one - (d + d));
nvg_Translate(sig, (float)((double)off * ((double)window->w * factor_neg_0_1)), 0.0f);
cat = gui->selected_tab;
}
if (cat >= 7) cat = 0; // tabs[7] would run into selected_tab
switch ((unsigned)gui->tabs[cat].kind) {
case 0: draw_module_cards(gui, window, mgr, 0, dt); break;
case 4: draw_module_cards(gui, window, mgr, 4, dt); break;
case 2: draw_module_cards(gui, window, mgr, 2, dt); break;
case 3: draw_module_cards(gui, window, mgr, 3, dt); break;
case 1: draw_module_cards(gui, window, mgr, 1, dt); break;
case 5: { // profiles
if (gui->input_mode == 0 && window->t > gui->profile_refresh_time + offset_2) {
int e = load_profiles(profiles);
if (e) {
GEv ev = {};
ev.a.i = e;
prof_event(mgr, 0xc, &ev);
}
gui->profile_refresh_time = window->t;
}
void *pig = window->ig;
choose_columns(gui, gui_profiles_column_width);
unsigned long long n;
Prof *list = profile_array(profiles, &n);
for (unsigned long long i = 0; i <= n; i++) {
R16t row = add_cell(gui, gui->row_height);
if (!rects_intersect(make_rect(0.0f, gui->scroll, window->w, window->h), row)) continue;
Prof *ent = i ? &list[i - 1] : 0;
V4t bg = nvg_rgba_f(colour_level_0_0784314_in_gui_state, colour_level_0_0784314_in_gui_state, colour_level_0_0784314_in_gui_state, 1.0f);
nvg_FillColor(pig, &bg);
draw_rect_rounded_local((WT3 *)window, row, row_height_20_in_gui_state_a);
begin_content(window, gui, row, row_padding_8, 12.0f);
pop_layout_row(gui, 1);
void *tig = window->ig;
R16t t = add_cell(gui, row_height_20_in_gui_state_a);
nvg_FontSize(tig, font_size_16_in_gui_state);
V4t tc = nvg_rgba_f(colour_level_0_866667_in_gui_state, colour_level_0_866667_in_gui_state, colour_level_0_866667_in_gui_state, 1.0f);
nvg_FillColor(tig, &tc);
if (ent) {
float tw = draw_text_sized(window, t, 0, 0, ent->name, ent->end);
R16t br = make_rect(tw + rect_extent_5, t.y, t.w - tw + t.x, t.h);
draw_module_description(gui, window, profile_set_bind(profiles), ent, ent->key, 0, br);
} else {
draw_text_sized(window, t, 0, 0, str_default /* "Default" */, 0);
}
for (unsigned j = 0; j != 4; j++) { // load, save, open folder, delete
R16t b = make_rect(t.x + t.w - (float)(int)(20 * (4 - j)) - (float)(int)(12 * (3 - j)), t.y,
row_height_20_in_gui_state_a, 20.0f);
float alpha = half_in_gui_state;
if (ent || !j) {
alpha = one;
if (mouse_over_rect_r_local(gui, b)) {
set_slider_text(gui, window, (const char *)gui_state_tooltip_strings_as_offsets_from + gui_state_tooltip_strings_as_offsets_from[j],
(int)((double)b.x + (double)b.w * factor_0_5_in_gui_state), (int)b.y);
if (clicked_inside_r_frame_local(gui, b)) {
const char *name = ent ? ent->name : 0;
int error_code;
const char *msg;
switch (j) {
case 0:
if (get_profile_dirty()) {
ask_sure_before_running_action(gui, mgr, profiles, name, 0);
goto icon;
}
error_code = load_profile_name(name, mgr);
goto result;
case 1:
if (!ent) {
error_code = 0x1773;
msg = str_default_profile_cannot_be_over; // "Default profile cannot be overwritten"
goto error;
}
if (get_saved_string() && libc_strcmp(name, get_saved_string()) == 0) {
error_code = save_profile(name, true, mgr);
goto result;
}
ask_sure_before_running_action(gui, mgr, profiles, name, 1);
goto icon;
case 2:
ask_sure_before_running_action(gui, mgr, profiles, name, 2);
goto icon;
case 3:
if (ent) {
ask_sure_before_running_action(gui, mgr, profiles, name, 3);
goto icon;
}
error_code = 0x1774;
msg = str_default_profile_cannot_be_dele; // "Default profile cannot be deleted"
goto error;
}
result:
if (!error_code) {
GEv ev = {};
ev.a.p = (void *)name;
ev.b.i = (int)j;
prof_event(mgr, 0xb, &ev);
gui->profile_refresh_time = -1.0;
goto icon;
}
msg = error_code == 0x1774 ? str_default_profile_cannot_be_dele : error_code == 0x1773 ? str_default_profile_cannot_be_over : 0;
error : {
GEv ev = {};
ev.a.i = error_code;
ev.b.p = (void *)msg;
prof_event(mgr, 0xc, &ev);
}
}
}
}
icon:
draw_texture(window, b, gui_state_button_icons[j], alpha);
}
if (!gui->card_layout_mode) { // description line
R16t d = add_cell(gui, row_height_20_in_gui_state_a);
nvg_FontSize(tig, font_size_14_in_gui_state);
V4t dc = nvg_rgba_f(colour_level_0_6, colour_level_0_6, colour_level_0_6, 1.0f);
nvg_FillColor(tig, &dc);
draw_text((WT3 *)window, d, 0, 0, ent ? (void *)ent->desc : (void *)str_contains_the_default_configura);
}
gui->row_height = end_layout_row(window, gui);
}
// "CREATE NEW PROFILE": a text field once clicked
R16t cr = add_cell(gui, gui->row_height);
void *cig = window->ig;
V4t c0 = nvg_rgba_f(colour_level_0_0784314_in_gui_state, colour_level_0_0784314_in_gui_state, colour_level_0_0784314_in_gui_state, 1.0f);
V4t c1 = nvg_trans_rgba_f(c0, half_in_gui_state);
nvg_FillColor(cig, &c1);
draw_rect_rounded_local((WT3 *)window, cr, row_height_20_in_gui_state_a);
if (drag_item(gui, window, cr, file_name_text_filter, 0)) {
nvg_FontSize(cig, font_size_16_in_gui_state);
V4t wc = nvg_rgba_f(one, one, one, 1.0f);
nvg_FillColor(cig, &wc);
R16t ir = {cr.x, (float)((double)cr.h * factor_0_5_in_gui_state + (double)cr.y + offset_neg_8), cr.w, gui_tab_font_size};
draw_tracked_text(gui, window, &ir);
const char *nm = (const char *)take_dragged_item(gui);
if (nm) {
int r = save_profile(nm, false, mgr);
GEv ev = {};
if (!r) {
ev.a.p = (void *)nm;
ev.b.i = 1;
prof_event(mgr, 0xb, &ev);
gui->profile_refresh_time = -1.0;
} else {
ev.a.i = r;
ev.b.p = r == 0x3f4 ? (void *)str_profile_name_is_taken : 0; // "Profile name is taken."
prof_event(mgr, 0xc, &ev);
}
}
} else {
nvg_FontSize(cig, font_size_15);
float k = mouse_over_rect_r_local(gui, cr) ? one : colour_level_0_866667_in_gui_state;
V4t tc2 = nvg_rgba_f(k, k, k, 1.0f);
nvg_FillColor(cig, &tc2);
draw_text((WT3 *)window, cr, 1, 1, (void *)str_create_new_profile); // "CREATE NEW PROFILE"
}
break;
}
}
end_layout_row(window, gui);
if (gui->toast_visible) { // toast at the top, fades in
gui->toast_fade = (float)((double)gui->toast_fade + dtd / divisor_0_1_in_gui_state);
void *tig = window->ig;
nvg_Save(tig);
nvg_Translate(tig, gui->toast_x, gui->toast_y);
if (gui->toast_fade > one) {
gui->toast_fade = 1.0f;
} else {
float e = ease_curve(gui->toast_fade);
nvg_GlobalAlpha(tig, e);
nvg_Scale(tig, e, e);
}
nvg_FontSize(tig, font_size_14_in_gui_state);
float tw = nvg_text_bounds(tig, 0.0f, 0.0f, gui->toast_text, 0, (const float *)0);
R16t tr = make_rect((float)((double)tw * factor_neg_0_5 + offset_neg_5), gui_tab_bar_vertical_offset, tw + offset_10, 20.0f);
V4t bc = nvg_rgba_f(colour_level_0_25098_in_gui_state, colour_level_0_25098_in_gui_state, colour_level_0_25098_in_gui_state, 1.0f);
nvg_FillColor(tig, &bc);
draw_rect_rounded_local((WT3 *)window, tr, rect_extent_5);
V4t fc = nvg_rgba_f(colour_level_0_866667_in_gui_state, colour_level_0_866667_in_gui_state, colour_level_0_866667_in_gui_state, 1.0f);
nvg_FillColor(tig, &fc);
draw_text((WT3 *)window, tr, 1, 0, gui->toast_text);
nvg_Restore(tig);
gui->toast_visible = 0;
} else {
gui->toast_fade = 0.0f;
}
nvg_Restore(nvg);
float mx = (float)gui->layout_column_count - window->h + row_padding_12;
gui->scroll_max = 0.0f > mx ? 0.0f : mx;
if (popup_is_open(gui)) { // modal popup over everything (unscrolled)
nvg_Save(nvg);
gui->mouse_scene.y -= gui->scroll;
card_open_animation(window, gui->popup_opacity);
reset_layout_columns(gui);
R16t pr = win;
if (pr.w > threshold_600) {
pr.x = (float)((double)(pr.w + offset_neg_600) * factor_0_5_in_gui_state + (double)pr.x);
pr.w = threshold_600;
}
push_layout_row(gui, pr, row_padding_8, row_padding_12);
close_popup_animated(gui, window);
end_layout_row(window, gui);
nvg_Restore(nvg);
}
mark_click_consumed(gui);
gui->click_consumed = gui->click_capture;
gui->card_anim_start_time = window->t;
}
+356
View File
@@ -0,0 +1,356 @@
// GUI: text rendering helpers - labels, wrapped descriptions, numeric filters.
#include "slinky/engine.h"
#include "slinky/gui_state.h"
FN(unsigned long long, libc_strlen, (const char *str));
// file-name text filter: printable, no slashes, max 15 chars
extern "C" void file_name_text_filter(char *buf, char character) {
unsigned long long n = libc_strlen(buf);
unsigned char lng = n >= 0xf;
unsigned char np = (unsigned char)(character - 0x20) >= 0x5f;
if (np | lng) return;
if (character == 0x2f || character == 0x5c) return;
buf[n] = character;
}
struct R16c { float x, y, w, h; };
struct V4 { float x, y, z, w; };
FN(R16c, add_cell, (const void *src, float f));
FN(V4, nvg_rgba_f, (float r, float g, float b, float a));
FN(void, nvg_FillColor, (void *nvg, V4 *c));
FN(void, nvg_FontSize, (void *nvg, float f));
FN(float, draw_text, (void *window, R16c r, int a, int b, void *x));
extern "C" const float row_height_19, gui_section_label_top_margin, gui_section_label_grey_level, font_size_15_in_gui_text_a;
extern "C" float draw_section_label(const void *src, void **window, void *other_object) {
void *nvg = window[0];
R16c r = add_cell(src, row_height_19);
r.y += gui_section_label_top_margin;
float g = gui_section_label_grey_level;
V4 c = nvg_rgba_f(g, g, g, 1.0f);
nvg_FillColor(nvg, &c);
nvg_FontSize(nvg, font_size_15_in_gui_text_a);
return draw_text(window, r, 0, 0, other_object);
}
FN(void *, libc_malloc, (unsigned long long n));
FN(void, libc_free, (void *ptr));
FN(void *, libc_memcpy, (void *dest, const void *src, unsigned long long n));
FN(void, confirmation_popup_formatted_message, (void *gui, void *object, void *other_object, const char *fmt, ...));
FN(void, run_confirmed_profile_action, (void *p));
extern "C" const int format_string_offsets[];
extern "C" const char str_default[]; // "Default"
struct Confirm { GuiState *gui; void *module_manager; void *profile_list; char *name; int kind; };
// ask "Are you sure?" before running action `kind` on profile `name`
extern "C" void ask_sure_before_running_action(GuiState *gui, void *module_manager, void *profile_list, const char *name, int kind) {
Confirm *c = (Confirm *)libc_malloc(0x28);
if (!c) return;
c->gui = gui; c->module_manager = module_manager; c->profile_list = profile_list; c->name = 0; c->kind = kind;
if (name) {
unsigned long long n = libc_strlen(name);
char *s = (char *)libc_malloc(n + 1);
if (!s) { libc_free(c); return; }
libc_memcpy(s, name, n);
s[n] = 0;
c->name = s;
} else {
name = str_default;
}
const char *fmt = (const char *)format_string_offsets + format_string_offsets[(unsigned)kind];
confirmation_popup_formatted_message(gui, c, (void *)run_confirmed_profile_action, fmt, name);
}
FN(int, load_profile_name, (const char *name, void *mods));
FN(int, save_profile, (const char *name, bool text, void *mods));
FN(int, open_profiles_folder, ());
FN(int, delete_profile_file, (const char *name));
FN(void, dispatch_event, (void *mgr, void *object));
struct ProfEvent { int type; int padding_04; union { int code; const char *name; }; int kind; char rest[0x2c]; };
// run the confirmed profile action (0 load, 1 save, 2 open folder, 3 delete) and report it
extern "C" void run_confirmed_profile_action(void *cv) {
Confirm *c = (Confirm *)cv;
int error_code;
switch ((unsigned)c->kind) {
case 0: error_code = load_profile_name(c->name, c->module_manager); break;
case 1: error_code = save_profile(c->name, true, c->module_manager); break;
case 2: error_code = open_profiles_folder(); break;
case 3: error_code = delete_profile_file(c->name); break;
default: error_code = 0; goto ok;
}
if (error_code) {
ProfEvent e = {};
e.type = 0xc;
e.code = error_code;
dispatch_event(c->module_manager, &e);
goto done;
}
ok:
{
ProfEvent e = {};
e.type = 0xb;
e.name = c->name;
e.kind = c->kind;
dispatch_event(c->module_manager, &e);
}
done:
c->gui->profile_refresh_time = -1.0;
libc_free(c->name);
libc_free(c);
}
// ---- animated toggle switch; returns true when clicked ----
struct Anim { float pos; int padding_04; double last; };
struct WT2 { void *ig; double t; };
FN(float, ease_curve, (float t)); // smoothstep
FN(float, lerp_unclamped_float, (float a, float b, float t));
FN(bool, mouse_over_rect_r, (GuiState *gui, const R16c *r));
FN(unsigned char, clicked_inside_r_frame, (GuiState *gui, R16c r));
FN(V4, nvg_lerp_rgba, (V4 a, V4 b, float t));
FN(void, draw_rect_rounded, (WT2 *window, const R16c *r, float round));
FN(R16c, inset_rect, (R16c r, float d));
FN(void, nvg_BeginPath, (void *nvg));
FN(void, nvg_Circle, (void *nvg, float x, float y, float rad));
FN(void, nvg_Fill, (void *nvg));
extern "C" const double tenth_in_gui_text, divisor_neg_0_1_in_gui_text;
extern "C" const float threshold_1_in_gui_text_b, gui_toggle_idle_grey_level, gui_toggle_hover_grey_level, corner_radius_10_in_gui_text, inset_6_in_gui_text_a, gui_text_background_grey_level, factor_0_5_in_gui_text_a;
extern "C" bool draw_animated_description(GuiState *gui, WT2 *window, R16c *r, Anim *an, unsigned char *val) {
double t = window->t;
double dt = (double)(float)(t - an->last);
unsigned char v = *val;
float p;
if (dt > tenth_in_gui_text) {
p = (float)v;
an->pos = p;
} else {
p = an->pos;
if (v) {
if (threshold_1_in_gui_text_b > p) {
p = (float)(dt / tenth_in_gui_text + (double)p);
p = p < threshold_1_in_gui_text_b ? p : threshold_1_in_gui_text_b;
an->pos = p;
}
} else if (p > 0.0f) {
p = (float)(dt / divisor_neg_0_1_in_gui_text + (double)p);
p = p > 0.0f ? p : 0.0f;
an->pos = p;
}
}
an->last = t;
void *nvg = window->ig;
float e = ease_curve(p);
float k = mouse_over_rect_r(gui, r) ? gui_toggle_hover_grey_level : gui_toggle_idle_grey_level;
V4 col = nvg_rgba_f(k, k, k, 1.0f);
V4 track = nvg_lerp_rgba(col, *(V4 *)&gui->accent_color, e);
nvg_FillColor(nvg, &track);
draw_rect_rounded(window, r, corner_radius_10_in_gui_text);
bool clicked = clicked_inside_r_frame(gui, *r);
if (clicked) *val ^= 1;
*r = inset_rect(*r, inset_6_in_gui_text_a);
float h = r->h;
float kk = gui_text_background_grey_level;
V4 knob = nvg_rgba_f(kk, kk, kk, 1.0f);
nvg_FillColor(nvg, &knob);
nvg_BeginPath(nvg);
float rad = (float)(int)h;
float off = rad * factor_0_5_in_gui_text_a;
float x = lerp_unclamped_float(off + r->x, r->x + r->w - off, e);
nvg_Circle(nvg, x, off + r->y, rad);
nvg_Fill(nvg);
return clicked;
}
// ---- module header row: [+]/[-] expander box and name; returns the right edge of the clickable area ----
FN(R16c, make_rect, (float x, float y, float w, float h));
FN(float, nvg_text_bounds, (void *nvg, float x, float y, const char *text, const char *end, const float *bounds)); // nvgTextBounds
FN(void, nvg_StrokeColor, (void *nvg, V4 *c));
FN(void, nvg_MoveTo, (void *nvg, float x, float y));
FN(void, nvg_LineTo, (void *nvg, float x, float y));
FN(void, nvg_StrokeWidth, (void *nvg, float w));
FN(void, nvg_Stroke, (void *nvg));
extern "C" const float gui_text_gap, gui_text_idle_grey_level, gui_text_hover_grey_level, corner_radius_5_in_gui_text, inset_6_in_gui_text, gui_toggle_text_grey_level, one_in_gui_text,
half_in_gui_text, stroke_width_2;
struct ModHdr { char padding_00[0x40]; unsigned char expanded; char padding_41[0x1b7]; void *settings; };
extern "C" float draw_module_header(GuiState *gui, WT2 *window, ModHdr *m, const R16c *r, float fsz, const V4 *col, const V4 *hcol,
const char *text) {
void *nvg = window->ig;
if (!m->settings) {
nvg_FontSize(nvg, fsz);
V4 c = *col;
nvg_FillColor(nvg, &c);
return draw_text(window, *r, 0, 0, (void *)text);
}
R16c box = make_rect(r->x, r->y, r->h, r->h);
float gap = gui_text_gap;
float tx = box.x + box.w + gap;
float tw = nvg_text_bounds(nvg, tx, r->y, text, 0, (const float *)0);
R16c lbl = make_rect(tx, r->y, tw, r->h);
R16c hit = make_rect(r->x, r->y, box.w + lbl.w + gap, r->h);
if (clicked_inside_r_frame(gui, hit)) m->expanded ^= 1;
bool hov = mouse_over_rect_r(gui, &hit);
nvg_FontSize(nvg, fsz);
V4 c = *(hov ? hcol : col);
nvg_FillColor(nvg, &c);
draw_text(window, lbl, 0, 0, (void *)text);
float k = hov ? gui_text_hover_grey_level : gui_text_idle_grey_level;
V4 bc = nvg_rgba_f(k, k, k, 1.0f);
nvg_FillColor(nvg, &bc);
draw_rect_rounded(window, &box, corner_radius_5_in_gui_text);
R16c in = inset_rect(box, inset_6_in_gui_text);
float k2 = hov ? one_in_gui_text : gui_toggle_text_grey_level;
V4 sc = nvg_rgba_f(k2, k2, k2, 1.0f);
nvg_StrokeColor(nvg, &sc);
nvg_BeginPath(nvg);
float half = half_in_gui_text;
nvg_MoveTo(nvg, in.x, in.h * half + in.y);
nvg_LineTo(nvg, in.x + in.w, in.h * half + in.y);
if (!m->expanded) {
nvg_MoveTo(nvg, in.w * half + in.x, in.y);
nvg_LineTo(nvg, half * in.w + in.x, in.y + in.h);
}
nvg_StrokeWidth(nvg, stroke_width_2);
nvg_Stroke(nvg);
return hit.x + hit.w;
}
// ---- key-bind button ("PRESS A KEY..." while capturing) and optional "HOLD" toggle ----
FN(void, set_draw_color, (void *window, unsigned col));
FN(bool, drag_owner_is, (GuiState *gui, void *object));
FN(void, begin_drag, (GuiState *gui, void *object, void *other_object));
FN(float, libc_ceilf, (float x)); // ceilf
FN(const char *, mouse_button_name, (int key));
FN(V4, nvg_trans_rgba_f, (V4 c, float k));
extern "C" float text_width_cache[];
extern "C" const char str_press_a_key[], str_hold_in_gui_text[]; // "PRESS A KEY...", "HOLD"
extern "C" const float rect_extent_12_in_gui_text, colour_alpha_0_15, colour_level_0_6_in_gui_text, corner_radius_5_in_gui_text_a, colour_level_1_in_gui_text_a, colour_level_0_866667_in_gui_text_a;
static inline __attribute__((always_inline)) V4 grey(float k) { return nvg_rgba_f(k, k, k, 1.0f); }
extern "C" void draw_module_description(GuiState *gui, WT2 *window, void *cbArg, void *target, int key, unsigned char *hold, const R16c *r) {
void *nvg = window->ig;
nvg_FontSize(nvg, rect_extent_12_in_gui_text);
set_draw_color(window, 1);
bool capturing = drag_owner_is(gui, target);
float tw;
if (capturing) {
tw = libc_ceilf(nvg_text_bounds(nvg, 0.0f, 0.0f, str_press_a_key, 0, (const float *)0));
} else {
tw = text_width_cache[(unsigned)key];
if (tw == 0.0f) {
tw = libc_ceilf(nvg_text_bounds(nvg, 0.0f, 0.0f, mouse_button_name(key), 0, (const float *)0));
text_width_cache[(unsigned)key] = tw;
}
}
R16c btn = make_rect(r->x, r->y, tw + rect_extent_12_in_gui_text, r->h);
V4 bg = key ? nvg_trans_rgba_f(*(V4 *)&gui->accent_color, colour_alpha_0_15) : nvg_trans_rgba_f(grey(colour_level_0_6_in_gui_text), colour_alpha_0_15);
nvg_FillColor(nvg, &bg);
draw_rect_rounded(window, &btn, corner_radius_5_in_gui_text_a);
V4 tc;
if (mouse_over_rect_r(gui, &btn)) tc = grey(colour_level_1_in_gui_text_a);
else if (key) tc = *(V4 *)&gui->accent_color;
else tc = grey(colour_level_0_6_in_gui_text);
nvg_FillColor(nvg, &tc);
draw_text(window, btn, 1, 1, (void *)(capturing ? str_press_a_key : mouse_button_name(key)));
if (clicked_inside_r_frame(gui, btn)) begin_drag(gui, cbArg, target);
if (!key) {
if (hold) *hold = 0;
return;
}
if (!hold) return;
float hw = libc_ceilf(nvg_text_bounds(nvg, 0.0f, 0.0f, str_hold_in_gui_text, 0, (const float *)0));
R16c hb = make_rect(btn.x + btn.w + corner_radius_5_in_gui_text_a, btn.y, hw + rect_extent_12_in_gui_text, btn.h);
V4 hc;
if (*hold) {
hc = nvg_trans_rgba_f(grey(colour_level_0_866667_in_gui_text_a), colour_alpha_0_15);
} else {
if (!mouse_over_rect_r(gui, r)) return;
hc = *hold == 1 ? nvg_trans_rgba_f(grey(colour_level_0_866667_in_gui_text_a), colour_alpha_0_15) : nvg_trans_rgba_f(grey(colour_level_0_6_in_gui_text), colour_alpha_0_15);
}
nvg_FillColor(nvg, &hc);
draw_rect_rounded(window, &hb, corner_radius_5_in_gui_text_a);
V4 ht = mouse_over_rect_r(gui, &hb) ? grey(colour_level_0_866667_in_gui_text_a) : grey(colour_level_0_6_in_gui_text);
nvg_FillColor(nvg, &ht);
draw_text(window, hb, 1, 1, (void *)str_hold_in_gui_text);
if (clicked_inside_r_frame(gui, hb)) *hold ^= 1;
}
// labelled toggle row: label text, then the switch right of it
extern "C" const float row_height_20_in_gui_text, gui_label_text_grey_level, font_size_15_in_gui_text, rect_extent_neg_40_in_gui_text, rect_extent_40_in_gui_text;
FN(R16c, make_rect, (float x, float y, float w, float h));
FN(bool, draw_animated_description_local, (GuiState *gui, WT2 *window, R16c r, Anim *an, unsigned char *val)) __asm__("draw_animated_description");
extern "C" bool draw_toggle_row(GuiState *gui, WT2 *window, const char *label, Anim *an, unsigned char *val) {
void *nvg = window->ig;
R16c r = add_cell(gui, row_height_20_in_gui_text);
float k = gui_label_text_grey_level;
V4 c = nvg_rgba_f(k, k, k, 1.0f);
nvg_FillColor(nvg, &c);
nvg_FontSize(nvg, font_size_15_in_gui_text);
draw_text(window, r, 0, 0, (void *)label);
return draw_animated_description_local(gui, window, make_rect(r.x + r.w + rect_extent_neg_40_in_gui_text, r.y, rect_extent_40_in_gui_text, r.h), an, val);
}
// ---- float slider: label, track, animated fill + knob, drag/typing via draw_slider_track, value tooltip ----
struct SAnim { unsigned char active; char padding_01[3]; float pos; double last; };
FN(unsigned char, draw_slider_track, (GuiState *gui, R16c track, R16c hit, float lo, float hi, float step, float scale, SAnim *an,
float *val));
FN(float, limit_rotation_delta, (float lo, float hi, float x)); // clamp
FN(float, lerp_clamped_float, (float a, float b, float t));
FN(bool, dragged_item_is, (GuiState *gui, void *item));
FN(int, libc_snprintf, (char *buf, unsigned long long n, const char *fmt, ...)); // snprintf
extern "C" const float row_height_20_in_gui_text_a, colour_level_0_866667_in_gui_text_c, font_size_15_in_gui_text_b, offset_10_in_gui_text_a, offset_neg_2_in_gui_text, gui_description_idle_grey_level,
gui_slider_hover_grey_level, two_in_gui_text, clamp_bound_1_in_gui_text_c, gui_slider_knob_radius;
extern "C" const double factor_0_5_in_gui_text, offset_neg_2, divisor_0_1_in_gui_text_a;
extern "C" const char fmt_float_2dp[]; // "%0.2f"
extern "C" char value_label_buffer[];
extern "C" unsigned char draw_slider_row(GuiState *gui, WT2 *window, const char *label, const char *fmt, SAnim *active, float lo, float hi,
float step, float scale, float *val) {
void *nvg = window->ig;
R16c r = add_cell(gui, row_height_20_in_gui_text_a);
V4 tc = nvg_rgba_f(colour_level_0_866667_in_gui_text_c, colour_level_0_866667_in_gui_text_c, colour_level_0_866667_in_gui_text_c, 1.0f);
nvg_FillColor(nvg, &tc);
nvg_FontSize(nvg, font_size_15_in_gui_text_b);
draw_text(window, r, 0, 0, (void *)label);
float y = r.y + offset_10_in_gui_text_a + offset_neg_2_in_gui_text;
double half = (double)r.w * factor_0_5_in_gui_text;
float x0 = (float)((double)r.x + half);
float tw = (float)(half + offset_neg_2);
R16c track = make_rect(x0, y, tw, 4.0f);
R16c hr = r;
float k = mouse_over_rect_r(gui, &hr) ? gui_slider_hover_grey_level : gui_description_idle_grey_level;
V4 kc = nvg_rgba_f(k, k, k, 1.0f);
nvg_FillColor(nvg, &kc);
float rnd = two_in_gui_text;
R16c tr = track;
draw_rect_rounded(window, &tr, rnd);
float t = limit_rotation_delta(0.0f, clamp_bound_1_in_gui_text_c, (*val - lo) / (hi - lo));
float pos = lerp_clamped_float(active->pos, t, (float)((double)(float)(window->t - active->last) / divisor_0_1_in_gui_text_a));
active->pos = pos;
active->last = window->t;
float fx = lerp_unclamped_float(x0, tw + x0, pos);
R16c fill = make_rect(x0, y, fx - x0, 4.0f);
V4 acc = *(V4 *)&gui->accent_color;
nvg_FillColor(nvg, &acc);
draw_rect_rounded(window, &fill, rnd);
nvg_BeginPath(nvg);
nvg_Circle(nvg, fx, rnd + y, gui_slider_knob_radius);
nvg_Fill(nvg);
unsigned char res = draw_slider_track(gui, track, r, lo, hi, step, scale, active, val);
if (!active->active) {
R16c h2 = r;
if (!mouse_over_rect_r(gui, &h2) || gui->click_capture) return res;
}
const char *text;
if (dragged_item_is(gui, val)) {
text = gui->edit_text;
} else {
float v = *val;
float shown = scale != 0.0f ? scale * v : v;
text = value_label_buffer;
libc_snprintf(value_label_buffer, 0x40, fmt ? fmt : fmt_float_2dp, (double)shown);
}
gui->toast_visible = 1;
unsigned long long tn = libc_strlen(text);
if (tn > 0x65) tn = 0x65;
libc_memcpy(gui->toast_text, text, tn);
gui->toast_text[tn] = 0;
gui->toast_x = (float)(int)fx;
gui->toast_y = (float)(int)y;
return res;
}
+380
View File
@@ -0,0 +1,380 @@
// 0xd000: ImGui widget wrappers (mark profile dirty on change), bounded string builder
#include "slinky/engine.h"
#include <stdarg.h>
FN(void, set_profile_dirty, (unsigned char v));
FN(unsigned char, draw_dropdown_row, (void *gui, void *window, void *label, void *setting, int count, void *names, void *value, unsigned char optional));
FN(unsigned char, draw_color_swatch_row, (void *gui, void *window, void *label, void *color));
FN(unsigned char, draw_list_editor_row, (void *gui, void *window, void *label, void *popup_title, void *char_filter, int kind, void *list_ops,
void *candidates, void *candidate_count));
// enum setting: label, option count, option names, current value (+ saved copy); `optional` adds a "none" entry with a clear cross
struct DropdownSetting { char padding_00[0x18]; void *label; int count; int padding_24; void *names; char value_and_saved[8]; unsigned char optional; };
extern "C" void widget_enum(void *window, void *gui, DropdownSetting *setting) {
if (draw_dropdown_row(gui, window, setting->label, setting, setting->count, setting->names, setting->value_and_saved, setting->optional)) set_profile_dirty(1);
}
// colour setting: label, then the colour (current + saved)
struct ColorSetting { void *label; char color[8]; };
extern "C" void widget_color(void *window, void *gui, ColorSetting *setting) {
if (draw_color_swatch_row(gui, window, setting->label, setting->color)) set_profile_dirty(1);
}
// list setting: label, "add" popup title and character filter, entry kind (0 names, 1 names + colour), list callbacks,
// and an optional provider of the candidate items offered by the "add" popup
struct ListSetting {
void *label, *popup_title, *char_filter; int kind; int padding_1c; char list_ops[0x38];
void (*get_candidates)(void **items, void **count); unsigned char changed;
};
extern "C" void widget_items(void *window, void *gui, ListSetting *setting) {
void *candidates = 0, *candidate_count = 0;
if (setting->get_candidates) setting->get_candidates(&candidates, &candidate_count);
if (draw_list_editor_row(gui, window, setting->label, setting->popup_title, setting->char_filter, setting->kind, setting->list_ops, candidates, candidate_count)) {
setting->changed = 1;
set_profile_dirty(1);
}
}
struct Buf { char *p; unsigned long long cap, len; };
FN(unsigned long long, libc_strlen, (const char *str));
FN(void *, libc_memcpy_alt, (void *dest, const void *src, unsigned long long n));
FN(int, libc_vsnprintf, (char *buf, unsigned long long n, const char *fmt, va_list ap)); // vsnprintf
extern "C" void buffer_init(Buf *b, char *p, unsigned long long cap) { b->p = p; b->cap = cap; b->len = 0; }
extern "C" bool buffer_append(Buf *b, const char *s) {
unsigned long long len = b->len;
if (b->cap <= len) return false;
unsigned long long avail = b->cap - len;
unsigned long long n = libc_strlen(s);
if (n >= avail) return false;
libc_memcpy_alt(b->p + len, s, n);
b->len += n;
return true;
}
extern "C" bool buffer_printf(Buf *b, const char *fmt, ...) {
if (b->cap <= b->len) return false;
unsigned long long avail = b->cap - b->len;
va_list ap;
va_start(ap, fmt);
int result = libc_vsnprintf(b->p + b->len, avail, fmt, ap);
unsigned long long nl;
bool ok;
if (result < 0 || avail <= (unsigned)result) { nl = b->cap; ok = false; }
else { nl = (unsigned)result + b->len; ok = true; }
b->len = nl;
return ok;
}
extern "C" bool buffer_push_char(Buf *b, char character) {
unsigned long long len = b->len, cap = b->cap;
if (len < cap) {
char *p = b->p;
b->len = len + 1;
p[len] = character;
}
return len < cap;
}
extern "C" bool buffer_terminate(Buf *b) {
unsigned long long len = b->len, cap = b->cap;
if (len < cap) {
b->p[len] = 0;
b->len++;
}
return len < cap;
}
// ---- generic dynamic array: data pointer + {u32 cap, u32 count} header, element size passed in ----
struct AHdr { unsigned cap, count; };
typedef int (*ACmp)(void *ctx, const void *key, const void *elem);
FN(void *, libc_realloc, (void *ptr, unsigned long long n)); // realloc
extern "C" __attribute__((noreturn)) void libc_abort(); // abort
FN(void *, libc_memmove, (void *dest, const void *src, unsigned long long n)); // memmove
extern "C" void *element_at(void *base, unsigned elem, AHdr *, unsigned i) { return (char *)base + elem * i; }
static inline __attribute__((always_inline)) void agrow(void **data, unsigned elem, AHdr *header) {
if (header->cap > 0x7fffffffu) libc_abort();
unsigned cap = header->cap ? header->cap * 2 : 8;
if (elem && cap > 0xffffffffu / elem) libc_abort();
header->cap = cap;
void *p = libc_realloc(*data, (unsigned long long)cap * elem);
if (!p) libc_abort();
*data = p;
}
// push_back
extern "C" void *vector_push_back(void **data, unsigned elem, AHdr *header, const void *src) {
if (header->count >= header->cap) agrow(data, elem, header);
unsigned i = header->count;
header->count = i + 1;
return libc_memcpy_alt((char *)*data + i * elem, src, elem);
}
// insert at index (shares the manifest range of vector_push_back)
extern "C" void *insert_index(void **data, unsigned elem, AHdr *header, const void *src, unsigned idx) {
if (idx > header->count) return 0;
if (header->count >= header->cap) agrow(data, elem, header);
char *base = (char *)*data;
char *at = base + idx * elem;
libc_memmove(base + (idx + 1) * elem, at, (header->count - idx) * elem);
libc_memcpy_alt(at, src, elem);
header->count++;
return at;
}
// binary search
extern "C" void *binary_search(void *base, unsigned elem, AHdr *header, const void *key, ACmp cmp) {
unsigned hi = header->count;
if (!hi) return 0;
unsigned lo = 0;
do {
unsigned mid = lo + ((hi - lo) >> 1);
char *p = (char *)base + mid * elem;
int result = cmp(0, key, p);
if (result < 0) hi = mid;
else if (result == 0) return p;
else lo = mid + 1;
} while (lo < hi);
return 0;
}
// sorted insert (returns the new element)
extern "C" void *sorted_insert(void **data, unsigned elem, AHdr *header, const void *key, ACmp cmp) {
unsigned hi = header->count, idx;
if (!hi) {
idx = 0;
} else {
char *base = (char *)*data;
unsigned lo = 0;
for (;;) {
unsigned mid = lo + ((hi - lo) >> 1);
int result = cmp(0, key, base + mid * elem);
if (result < 0) {
hi = mid;
if (lo >= hi) break;
} else if (result == 0) {
hi = mid;
break;
} else {
lo = mid + 1;
if (lo >= hi) break;
}
}
idx = hi;
}
if (header->count >= header->cap) agrow(data, elem, header);
char *base = (char *)*data;
char *at = base + idx * elem;
libc_memmove(base + (idx + 1) * elem, at, (header->count - idx) * elem);
libc_memcpy_alt(at, key, elem);
header->count++;
return at;
}
// ---- install / remove "slinky_handler" in the client's netty pipeline ----
#include "slinky/jids.h"
FN(void *, get_minecraft, (Jvm *jvm));
FN(void *, java_call_minecraft_get_net_handler, (Jvm *jvm, void *minecraft));
FN(void *, java_get_net_handler_play_client_net_manager, (Jvm *jvm, void *net_handler_play_client));
FN(void *, java_get_network_manager, (Jvm *jvm, void *network_manager));
FN(void *, java_call_channel_pipeline, (Jvm *jvm, void *channel_pipeline));
FN(void *, java_call_channel_pipeline_get, (Jvm *jvm, void *pipe, void *cls));
FN(void, java_call_channel_pipeline_add_before, (Jvm *jvm, void *pipe, const char *before, const char *name, jobject h));
FN(void, java_call_channel_pipeline_remove, (Jvm *jvm, void *pipe, void *cls));
extern "C" const char str_packet_handler[], str_slinky_handler[];
struct HClass { jclass cls; jmethodID ctor; };
static inline __attribute__((always_inline)) int pipeline(Jvm *jvm, void **out) {
void *minecraft = get_minecraft(jvm);
if (!minecraft) return 0x3fc;
void *minecraft_get_net_handler = java_call_minecraft_get_net_handler(jvm, minecraft);
if (!minecraft_get_net_handler) return 0x3fd;
void *network_manager = java_get_network_manager(jvm, java_get_net_handler_play_client_net_manager(jvm, minecraft_get_net_handler));
if (!network_manager) return 0x3fd;
void *pipe = java_call_channel_pipeline(jvm, network_manager);
if (!pipe) return 0x3fd;
*out = pipe;
return 0;
}
extern "C" int register_channel_class(Jvm *jvm, HClass *h) {
void *pipe;
int error_code = pipeline(jvm, &pipe);
if (error_code) return error_code;
if (java_call_channel_pipeline_get(jvm, pipe, h->cls)) return 0;
JNIEnv *env = jvm->env;
jobject object = (*env)->NewObject(env, h->cls, h->ctor);
if (!object) return 0x7d5;
java_call_channel_pipeline_add_before(jvm, pipe, str_packet_handler, str_slinky_handler, object);
return (*env)->ExceptionCheck(env) ? 0x7d4 : 0;
}
extern "C" int unregister_channel_class(Jvm *jvm, HClass *h) {
void *pipe;
int error_code = pipeline(jvm, &pipe);
if (error_code) return error_code;
if (!java_call_channel_pipeline_get(jvm, pipe, h->cls)) return 0;
JNIEnv *env = jvm->env;
java_call_channel_pipeline_remove(jvm, pipe, h->cls);
if ((*env)->ExceptionCheck(env)) {
(*env)->ExceptionClear(env);
return 0x7d4;
}
return 0;
}
// ---- reach: entity mouse-over with the module's reach / hitbox expansion (piercing, ghost hand aware) ----
struct V3d { double x, y, z; };
struct Box6 { double v[6]; };
struct ReachA { Ctx *c; void *padding_08; double reach, expand; };
FN(bool, piercing_should_pierce_blocks_entry, (Ctx *ctx)); // piercing: blocks
FN(bool, ghost_hand_should_ignore_entity_entry, (Ctx *ctx, jobject entity)); // ghost hand: may hit through e
FN(int, piercing_decide_which_entity_entry, (Ctx *ctx, jobject e1, jobject e2, bool inside)); // piercing: 0/1 preferred, 2 none
FN(jobject, java_call_entity_ray_trace, (Jvm *jvm, jobject object, double d, float pt)); // rayTrace
FN(jobject, java_call_entity_living_base_get_position, (Jvm *jvm, jobject entity_living_base)); // eye position
FN(V3d, java_get_vec3_x_coord, (Jvm *jvm, jobject vec3));
FN(jobject, java_get_moving_object_position_hit_vec, (Jvm *jvm, jobject mop)); // hitVec
FN(double, vec3_distance, (V3d a, V3d b));
FN(V3d, java_get_entity_rotation_pitch, (Jvm *jvm, jobject entity)); // look vector
FN(V3d, vec3_scale, (V3d a, double s));
FN(V3d, vec3_add, (V3d a, V3d b));
FN(jobject, java_new_vec3_init, (Jvm *jvm, V3d v));
FN(jobject, java_get_world_loaded_entity_list, (Jvm *jvm, void *world)); // loaded entity list
FN(int, java_call_list_size, (Jvm *jvm, jobject l));
FN(int, java_get_moving_object_position_type_ordinal, (Jvm *jvm, jobject mop)); // typeOfHit ordinal
FN(jobject, java_call_list_get, (Jvm *jvm, jobject l, int i));
FN(bool, java_call_entity_can_be_collided_with, (Jvm *jvm, jobject entity)); // canBeCollidedWith
FN(jobject, java_get_entity_bounding_box, (Jvm *jvm, jobject entity)); // bounding box
FN(Box6, java_get_axis_aligned_bb_min_x, (Jvm *jvm, jobject bb));
FN(Box6, aabb_expand, (Box6 b, double d)); // expand
FN(jobject, java_new_axis_aligned_bb_init, (Jvm *jvm, Box6 b));
FN(jobject, java_axis_aligned_bb_calculate_intercept, (Jvm *jvm, jobject bb, jobject from, jobject to)); // calculateIntercept
FN(bool, aabb_contains_point, (Box6 b, V3d p)); // isVecInside
FN(V3d, aabb_clamp_point, (Box6 b, V3d p)); // clamp into box
FN(jobject, java_moving_object_position_init, (Jvm *jvm, jobject moving_object_position, jobject v)); // new MovingObjectPosition
FN(void, java_set_minecraft, (Jvm *jvm, void *minecraft, jobject mop)); // objectMouseOver =
extern "C" const double offset_1_5, tenth;
extern "C" const float one_in_imgui_widgets;
#define RJ ((Jvm *)ctx->jvm)
extern "C" void reach_distance_check(ReachA *a) {
Ctx *ctx = a->c;
double reach = a->reach;
double d = offset_1_5 + reach;
bool pierce = piercing_should_pierce_blocks_entry(ctx);
jobject mop = java_call_entity_ray_trace(RJ, ctx->player, d, one_in_imgui_widgets);
jobject eyes = java_call_entity_living_base_get_position(RJ, ctx->player);
V3d eye = java_get_vec3_x_coord(RJ, eyes);
if (!pierce && mop) d = vec3_distance(eye, java_get_vec3_x_coord(RJ, java_get_moving_object_position_hit_vec(RJ, mop)));
V3d endv = vec3_add(eye, vec3_scale(java_get_entity_rotation_pitch(RJ, ctx->player), d));
jobject endo = java_new_vec3_init(RJ, endv);
jobject list = java_get_world_loaded_entity_list(RJ, ctx->world);
int n = java_call_list_size(RJ, list);
bool blockHit = mop && java_get_moving_object_position_type_ordinal(RJ, mop) == 1;
if (n <= 0) {
if (blockHit) java_set_minecraft(RJ, ctx->minecraft, mop);
return;
}
jobject pointed = 0, hitv = 0;
double closest = reach;
bool found = false;
for (int i = 0; i != n; i++) {
jobject element = java_call_list_get(RJ, list, i);
JNIEnv *env = RJ->env;
if ((*env)->IsSameObject(env, (jobject)ctx->player, element)) continue;
if (!java_call_entity_can_be_collided_with(RJ, element)) continue;
Box6 bb = aabb_expand(java_get_axis_aligned_bb_min_x(RJ, java_get_entity_bounding_box(RJ, element)), a->expand);
jobject ic = java_axis_aligned_bb_calculate_intercept(RJ, java_new_axis_aligned_bb_init(RJ, bb), eyes, endo);
jobject entity;
double nd;
if (aabb_contains_point(bb, eye)) { // eye inside the (expanded) box
found = true;
entity = pointed;
nd = closest;
if (closest >= 0.0 && !(pointed && piercing_decide_which_entity_entry(ctx, pointed, element, true) == 1)) {
hitv = ic ? java_get_moving_object_position_hit_vec(RJ, ic) : eyes;
entity = element;
nd = 0.0;
}
} else {
if (!ic) continue;
jobject moving_object_position = java_get_moving_object_position_hit_vec(RJ, ic);
double dist = vec3_distance(eye, java_get_vec3_x_coord(RJ, moving_object_position));
int result = piercing_decide_which_entity_entry(ctx, pointed, element, found);
bool take = result == 2 ? closest == 0.0 || closest > dist : result == 0 && reach > dist;
entity = pointed;
nd = closest;
if (take) {
hitv = moving_object_position;
entity = element;
nd = dist;
}
}
bool through = ghost_hand_should_ignore_entity_entry(ctx, entity);
blockHit = blockHit & through;
closest = nd;
pointed = entity;
}
if (blockHit) {
java_set_minecraft(RJ, ctx->minecraft, mop);
return;
}
if (!pointed) return;
if (!(pierce | !mop | !(closest > d))) return;
Box6 bb = aabb_expand(java_get_axis_aligned_bb_min_x(RJ, java_get_entity_bounding_box(RJ, pointed)), tenth);
V3d p = aabb_clamp_point(bb, java_get_vec3_x_coord(RJ, hitv));
java_set_minecraft(RJ, ctx->minecraft, java_moving_object_position_init(RJ, pointed, java_new_vec3_init(RJ, p)));
}
#undef RJ
// ---- counting allocator (the allocation counter at allocation_counter is checked for leaks on unload) ----
FN(void *, libc_malloc, (unsigned long long n)); // malloc
FN(void, libc_free, (void *ptr)); // free
FN(void *, libc_memset, (void *ptr, int c, unsigned long long n)); // memset
extern "C" long long allocation_counter;
extern "C" void *debug_alloc(unsigned long long n) { // allocate n bytes filled with 0xaa
void *p = libc_malloc(n);
if (!p) return 0;
libc_memset(p, 0xaa, n);
__atomic_fetch_add(&allocation_counter, 1, __ATOMIC_SEQ_CST);
return p;
}
extern "C" void debug_free(void *p) {
if (p) {
__atomic_fetch_sub(&allocation_counter, 1, __ATOMIC_SEQ_CST);
libc_free(p);
}
}
// ---- define the netty channel handler class "SlinkyChannelHandler" from the embedded class bytes ----
// registers its native `onPacket(Object, boolean, long, int)` (implemented by `onPacket`), looks up the
// constructor and keeps a global ref of the class. 0x7d0: no constructor, 0x7d5: no global ref.
FN(int, define_class, (JNIEnv *env, const char *name, jobject loader, const jbyte *buf, jint len, const JNINativeMethod *methods,
jclass *out));
extern "C" const jbyte *ptr_to_str_str; // class file bytes of SlinkyChannelHandler
extern "C" const jint *ptr_to_rdata_2b4bf8; // their length
extern "C" const char str_onpacket[], str_ljava_lang_object_zji_i[], str_slinkychannelhandler[], str_init[], str_v_in_imgui_widgets[];
extern "C" int install_packet_channel(Jvm *jvm, jobject loader, HClass *out, void *onPacket) {
JNINativeMethod native = {(char *)str_onpacket /* "onPacket" */, (char *)str_ljava_lang_object_zji_i /* "(Ljava/lang/Object;ZJI)I" */,
onPacket};
JNIEnv *env = jvm->env;
jclass cls;
int error_code = define_class(env, str_slinkychannelhandler /* "SlinkyChannelHandler" */, loader, ptr_to_str_str, *ptr_to_rdata_2b4bf8, &native, &cls);
if (error_code) return error_code;
out->ctor = (*env)->GetMethodID(env, cls, str_init /* "<init>" */, str_v_in_imgui_widgets /* "()V" */);
if (!out->ctor) return 0x7d0;
out->cls = (jclass)(*env)->NewGlobalRef(env, cls);
return out->cls ? 0 : 0x7d5;
}
// drop the class again: unregister its natives and release the global ref
extern "C" void release_packet_channel(JNIEnv *env, jclass *cls) {
if (!*cls) return;
(*env)->UnregisterNatives(env, *cls);
(*env)->DeleteGlobalRef(env, *cls);
*cls = 0;
}
// grow a dynamic array when it is full (the capacity step of vector_push_back)
extern "C" void grow_dynamic_array_full(void **data, unsigned elem, AHdr *header) {
if (__builtin_expect(header->count >= header->cap, 0)) agrow(data, elem, header);
}
// calloc / realloc with the same allocation counter
FN(void *, libc_calloc, (unsigned long long n, unsigned long long size));
FN(void *, libc_realloc, (void *p, unsigned long long n));
extern "C" void *debug_calloc(unsigned long long n, unsigned long long size) {
void *p = libc_calloc(n, size);
if (p) __atomic_fetch_add(&allocation_counter, 1, __ATOMIC_SEQ_CST);
return p;
}
extern "C" void *debug_realloc(void *old, unsigned long long n) {
bool had = old != 0;
void *p = libc_realloc(old, n);
if (!(p == 0 || had)) __atomic_fetch_add(&allocation_counter, 1, __ATOMIC_SEQ_CST);
return p;
}
File diff suppressed because it is too large. Load diff
+115
View File
@@ -0,0 +1,115 @@
// 0x1e000: JNI ID table teardown
#include "slinky/engine.h"
#include "slinky/jids.h"
// Release the global ref of every class cached by load_all_jni_ids. (The original skips S27PacketExplosion and EntityArrow.)
#define RELEASE(f) \
do { \
if (ids->f) { \
(*env)->DeleteGlobalRef(env, ids->f); \
ids->f = 0; \
} \
} while (0)
extern "C" void release_jni_ids(JIds *ids, JNIEnv *env) {
if (ids->gameClassLoader) {
(*env)->DeleteGlobalRef(env, (jobject)ids->gameClassLoader);
ids->gameClassLoader = 0;
}
RELEASE(List);
RELEASE(Map);
RELEASE(Keyboard);
RELEASE(Mouse);
RELEASE(Display);
RELEASE(Object);
RELEASE(SecureClassLoader);
RELEASE(Channel);
RELEASE(ChannelPipeline);
RELEASE(Enum);
RELEASE(Minecraft);
RELEASE(Entity);
RELEASE(World);
RELEASE(Timer);
RELEASE(GameSettings);
RELEASE(MovingObjectPosition);
RELEASE(_init_);
RELEASE(Block);
RELEASE(BlockPos);
RELEASE(KeyBinding);
RELEASE(AxisAlignedBB);
RELEASE(GuiContainer);
RELEASE(EntityLivingBase);
RELEASE(EntityPlayer);
RELEASE(InventoryPlayer);
RELEASE(ItemStack);
RELEASE(Item);
RELEASE(Vec3);
RELEASE(ScorePlayerTeam);
RELEASE(NetHandlerPlayClient);
RELEASE(IChatComponent);
RELEASE(IChatComponent_Serializer);
RELEASE(Slot);
RELEASE(IInventory);
RELEASE(NetworkManager);
RELEASE(C03PacketPlayer);
RELEASE(C07PacketPlayerDigging);
RELEASE(C08PacketPlayerBlockPlacement);
RELEASE(TileEntity);
RELEASE(S12PacketEntityVelocity);
RELEASE(RenderManager);
RELEASE(S14PacketEntity);
RELEASE(S18PacketEntityTeleport);
RELEASE(S19PacketEntityStatus);
RELEASE(S06PacketUpdateHealth);
RELEASE(S29PacketSoundEffect);
RELEASE(EntityOtherPlayerMP);
RELEASE(GuiInventory);
RELEASE(EntityPlayerSP);
RELEASE(EntityRenderer);
RELEASE(Framebuffer);
RELEASE(RenderGlobal);
RELEASE(ActiveRenderInfo);
RELEASE(RendererLivingEntity);
RELEASE(FontRenderer);
RELEASE(GlStateManager);
RELEASE(C02PacketUseEntity);
RELEASE(C0BPacketEntityAction);
RELEASE(S08PacketPlayerPosLook);
RELEASE(S00PacketKeepAlive);
RELEASE(C00PacketKeepAlive);
RELEASE(Potion);
RELEASE(RenderPlayer);
RELEASE(RenderItem);
RELEASE(RenderHelper);
RELEASE(EntityList);
RELEASE(Chunk);
RELEASE(ExtendedBlockStorage);
RELEASE(WorldClient);
RELEASE(ChunkProviderClient);
RELEASE(EntityItem);
RELEASE(S3FPacketCustomPayload);
RELEASE(PacketBuffer);
RELEASE(UUID);
RELEASE(ChatComponentText);
RELEASE(PlayerControllerMP);
RELEASE(C0EPacketClickWindow);
RELEASE(NBTTagList);
RELEASE(NBTTagCompound);
RELEASE(PotionEffect);
RELEASE(NetworkPlayerInfo);
RELEASE(GenericFutureListener);
RELEASE(C16PacketClientStatus);
RELEASE(C16PacketClientStatus_EnumState);
RELEASE(c);
RELEASE(ContainerPlayer);
RELEASE(GuiScreen);
RELEASE(GuiChest);
RELEASE(InventoryBasic);
RELEASE(StatCollector);
RELEASE(C01PacketChatMessage);
RELEASE(C14PacketTabComplete);
if (ids->S2APacketParticles) { // last one is a tail call, function pointer loaded first
auto f = (*env)->DeleteGlobalRef;
f(env, ids->S2APacketParticles);
}
}
+456
View File
@@ -0,0 +1,456 @@
// 0xc000: named profiles (non-global modules) load/save via cJSON
#include "slinky/engine.h"
struct cJSON;
struct Mod { char padding_00[0x18]; unsigned char flags; };
struct Mods { Mod **data; unsigned long long count; };
FN(cJSON *, cjson_parse, (const char *text)); // cJSON_Parse
FN(void, cjson_delete, (cJSON *object)); // cJSON_Delete
FN(void, cjson_free_string, (void *p)); // cJSON_free
FN(cJSON *, cjson_create_object, ()); // cJSON_CreateObject
FN(char *, cjson_print_unformatted, (const cJSON *object)); // cJSON_PrintUnformatted
FN(unsigned char, load_module_settings, (cJSON *root, Mods *mods, Mod *module));
FN(unsigned char, save_module_settings, (cJSON *root, Mod *module));
FN(int, settings_path, (char **path));
FN(int, settings_path_append, (char **path, const char *suffix));
FN(int, settings_path_ensure, (char **path));
FN(void, free_path, (char **path));
FN(int, write_text_file, (char **path, const char *data, bool text));
FN(void, set_saved_string, (const char *s));
extern "C" const char str_slinky_in_profiles[], str_profiles[];
extern "C" unsigned char settings_dirty_flag;
extern "C" bool load_module_states(const char *text, Mods *mods) {
cJSON *root = cjson_parse(text);
if (!root) return false;
bool ok = true;
for (unsigned long long i = 0; i < mods->count; i++) {
Mod *module = mods->data[i];
if (module->flags & 0x10) continue;
if (!load_module_settings(root, mods, module)) { ok = false; break; }
}
cjson_delete(root);
return ok;
}
extern "C" char *serialize_module_states(Mods *mods) {
cJSON *root = cjson_create_object();
char *out = 0;
if (!root) goto done;
for (unsigned long long i = 0; i < mods->count; i++) {
Mod *module = mods->data[i];
if (module->flags & 0x10) continue;
if (!save_module_settings(root, module)) goto done;
}
out = cjson_print_unformatted(root);
done:
cjson_delete(root);
return out;
}
extern "C" int save_profile(const char *name, bool text, Mods *mods) {
char *path;
int error_code = settings_path(&path);
if (error_code) return error_code;
if ((error_code = settings_path_append(&path, str_slinky_in_profiles)) != 0) goto clean;
if ((error_code = settings_path_ensure(&path)) != 0) goto clean;
if ((error_code = settings_path_append(&path, str_profiles)) != 0) goto clean;
if ((error_code = settings_path_ensure(&path)) != 0) goto clean;
if ((error_code = settings_path_append(&path, name)) != 0) goto clean;
{
char *json = serialize_module_states(mods);
if (!json) {
error_code = 0x1388;
} else {
error_code = write_text_file(&path, json, text);
if (!error_code) {
set_saved_string(name);
settings_dirty_flag = 0;
}
cjson_free_string(json);
}
}
clean:
free_path(&path);
return error_code;
}
// ---- setting visitors: mode 0 = load from json, 1 = save to json, 2 = remember, 3 = restore ----
// cflags-free: the original used a 4-entry jump table for the mode switch
struct Visit { cJSON *json; int mode; unsigned char failed; };
FN(cJSON *, cjson_get_object_item, (const cJSON *object, const char *name)); // cJSON_GetObjectItemCaseSensitive
FN(int, cjson_is_bool, (const cJSON *object)); // cJSON_IsBool
FN(int, cjson_is_true, (const cJSON *object)); // cJSON_IsTrue
FN(int, cjson_is_number, (const cJSON *object)); // cJSON_IsNumber
FN(double, cjson_get_number_value, (const cJSON *object)); // cJSON_GetNumberValue
FN(int, cjson_is_object, (const cJSON *object)); // cJSON_IsObject
FN(cJSON *, cjson_add_item_to_object, (cJSON *object, const char *name, int b)); // cJSON_AddBoolToObject
FN(cJSON *, cjson_add_number_to_object, (cJSON *object, const char *name, double v)); // cJSON_AddNumberToObject
FN(cJSON *, cjson_add_bool_to_object, (cJSON *object, const char *name)); // cJSON_AddObjectToObject
extern "C" const char str_min[], str_max[]; // "min", "max"
struct BoolSet { char padding_00[0x18]; unsigned char value, saved; };
extern "C" void visit_bool_setting(Visit *visitor, const char *name, BoolSet *s) {
if (visitor->failed) return;
switch (visitor->mode) {
case 0: {
unsigned char b = s->saved;
cJSON *json = cjson_get_object_item(visitor->json, name);
if (cjson_is_bool(json)) b = cjson_is_true(json) != 0;
s->value = b;
break;
}
case 2: s->saved = s->value; break;
case 3: s->value = s->saved; break;
case 1: {
cJSON *j = visitor->json;
visitor->failed |= cjson_add_item_to_object(j, name, s->value) == 0;
break;
}
}
}
struct FloatSet { char padding_00[0x28]; float min, max, value, saved; };
extern "C" void visit_float_setting(Visit *visitor, const char *name, FloatSet *s) {
if (visitor->failed) return;
int mode = visitor->mode;
if (mode == 0) {
cJSON *json = cjson_get_object_item(visitor->json, name);
float r = s->saved, lo = s->min, hi = s->max;
if (cjson_is_number(json)) {
float d = (float)cjson_get_number_value(json);
if (d == d) {
r = lo;
if (!(lo > d)) {
r = d;
if (d > hi) r = hi;
}
}
}
s->value = r;
} else {
if (mode == 1) {
cJSON *j = visitor->json;
visitor->failed |= cjson_add_number_to_object(j, name, (double)s->value) == 0;
} else if (mode == 2) {
s->saved = s->value;
} else if (mode == 3) {
s->value = s->saved;
}
}
}
struct Range { float min, max; };
struct RangeSet { char padding_00[0x38]; float lo, hi; Range cur, saved; };
static inline __attribute__((always_inline)) float clampf(float x, float lo, float hi) {
float r = lo;
if (!(lo > x)) {
r = x;
if (x > hi) r = hi;
}
return r;
}
extern "C" void visit_range_setting(Visit *visitor, const char *name, RangeSet *s) {
if (visitor->failed) return;
int mode = visitor->mode;
if (mode == 0) {
cJSON *json = cjson_get_object_item(visitor->json, name);
Range r = s->saved;
float lo = s->lo, hi = s->hi;
if (cjson_is_object(json)) {
float mn = (float)cjson_get_number_value(cjson_get_object_item(json, str_min));
if (mn == mn) {
float mx = (float)cjson_get_number_value(cjson_get_object_item(json, str_max));
if (mx == mx) {
float a = mx < mn ? mx : mn;
r.min = clampf(a, lo, hi);
r.max = clampf(mx, lo, hi);
}
}
}
s->cur = r;
} else {
if (mode == 1) {
cJSON *o = cjson_add_bool_to_object(visitor->json, name);
if (!o) { visitor->failed = 1; return; }
visitor->failed |= cjson_add_number_to_object(o, str_min, (double)s->cur.min) == 0;
visitor->failed |= cjson_add_number_to_object(o, str_max, (double)s->cur.max) == 0;
} else if (mode == 2) {
s->saved = s->cur;
} else if (mode == 3) {
s->cur = s->saved;
}
}
}
// ---- current profile name / dirty flag, profile file helpers ----
extern "C" char *settings_buffer;
extern "C" char *get_saved_string() { return settings_buffer; }
extern "C" unsigned char get_profile_dirty() { return settings_dirty_flag; }
extern "C" __attribute__((noinline)) void set_profile_dirty(unsigned char v) { settings_dirty_flag = v; }
FN(int, delete_file_path, (char **path));
FN(int, open_path_explorer, (char **path));
FN(int, find_files, (char **path, void *object, void *other_object));
extern "C" int delete_profile_file(const char *name) { // delete profile file
char *path;
int error_code = settings_path(&path);
if (error_code) return error_code;
if ((error_code = settings_path_append(&path, str_slinky_in_profiles)) == 0 && (error_code = settings_path_append(&path, str_profiles)) == 0 &&
(error_code = settings_path_append(&path, name)) == 0)
error_code = delete_file_path(&path);
free_path(&path);
return error_code;
}
extern "C" int open_profiles_folder() { // open the profiles folder
char *path;
int error_code = settings_path(&path);
if (error_code) return error_code;
if ((error_code = settings_path_append(&path, str_slinky_in_profiles)) == 0 && (error_code = settings_path_append(&path, str_profiles)) == 0)
error_code = open_path_explorer(&path);
free_path(&path);
return error_code;
}
extern "C" int list_profiles(void *ctx, void *callback) { // list profiles
char *path;
int error_code = settings_path(&path);
if (error_code) return error_code;
if ((error_code = settings_path_append(&path, str_slinky_in_profiles)) == 0 && (error_code = settings_path_ensure(&path)) == 0 &&
(error_code = settings_path_append(&path, str_profiles)) == 0 && (error_code = settings_path_ensure(&path)) == 0)
error_code = find_files(&path, ctx, callback);
free_path(&path);
return error_code;
}
FN(int, cjson_is_string, (const cJSON *object)); // cJSON_IsString
FN(cJSON *, cjson_add_string_to_object, (cJSON *object, const char *name, const char *s));
extern "C" int libc_strcmp(const char *left, const char *right); // strcmp
struct JStr { char padding_00[0x20]; const char *valuestring; };
struct ChoiceSet { char padding_00[0x20]; unsigned count; char padding_24[4]; const char **names; unsigned value, saved; };
extern "C" void visit_choice_setting(Visit *visitor, const char *name, ChoiceSet *s) {
if (visitor->failed) return;
int mode = visitor->mode;
if (mode == 0) {
unsigned r = s->saved;
cJSON *json = cjson_get_object_item(visitor->json, name);
if (cjson_is_string(json)) {
unsigned long long n = s->count;
for (unsigned long long i = 0; i < n; i++)
if (libc_strcmp(s->names[i], ((JStr *)json)->valuestring) == 0) { r = (unsigned)i; break; }
}
s->value = r;
} else {
if (mode == 1) {
if (!s->count) {
visitor->failed = 1;
return;
}
unsigned v = s->value < s->count ? s->value : 0;
visitor->failed |= cjson_add_string_to_object(visitor->json, name, s->names[v]) == 0;
} else if (mode == 2) s->saved = s->value;
else if (mode == 3) s->value = s->saved;
}
}
struct Color { float r, g, b; };
extern "C" const float threshold_1_in_profiles; // 1.0
extern "C" const char str_r_in_profiles[], str_g_in_profiles[], str_b_in_profiles[];
static inline __attribute__((always_inline)) bool unit(float x) {
return x >= 0.0f && !(x > threshold_1_in_profiles);
}
extern "C" Color parse_color(const cJSON *json, const Color *def) {
if (cjson_is_object(json)) {
float r = (float)cjson_get_number_value(cjson_get_object_item(json, str_r_in_profiles));
if (unit(r)) {
float g = (float)cjson_get_number_value(cjson_get_object_item(json, str_g_in_profiles));
if (unit(g)) {
float b = (float)cjson_get_number_value(cjson_get_object_item(json, str_b_in_profiles));
if (unit(b)) return Color{r, g, b};
}
}
}
return *def;
}
struct ColorSet { char padding_00[8]; Color cur, saved; };
extern "C" void visit_color_setting(Visit *visitor, const char *name, ColorSet *s) {
if (visitor->failed) return;
int mode = visitor->mode;
if (mode == 0) {
cJSON *json = cjson_get_object_item(visitor->json, name);
Color def = s->saved;
s->cur = parse_color(json, &def);
} else {
if (mode == 1) {
cJSON *o = cjson_add_bool_to_object(visitor->json, name);
if (!o) { visitor->failed = 1; return; }
visitor->failed |= cjson_add_number_to_object(o, str_r_in_profiles, (double)s->cur.r) == 0;
visitor->failed |= cjson_add_number_to_object(o, str_g_in_profiles, (double)s->cur.g) == 0;
visitor->failed |= cjson_add_number_to_object(o, str_b_in_profiles, (double)s->cur.b) == 0;
} else if (mode == 2) {
s->saved = s->cur;
} else if (mode == 3) {
s->cur = s->saved;
}
}
}
// ---- per-module json: "__on", "__bind", "__hold" and the module's own settings visitor ----
struct MCfg { const char *name; char padding_08[0xc]; unsigned char enabled; char padding_15[3]; unsigned flags; unsigned bind;
unsigned char hold; char padding_21[0x1df]; void (*visit)(Visit *); };
FN(void, queue_module_toggle, (Mods *module_manager, MCfg *module, unsigned char enabled));
FN(void, apply_module_state, (Mods *mods, MCfg *m, int key));
extern "C" const char mc_obf_name_on[], str_bind_in_profiles[], str_hold[];
extern "C" const double offset_0_5_in_profiles; // 0.5
struct JNum { char padding_00[0x30]; double vd; };
extern "C" unsigned char load_module_settings(cJSON *root, Mods *mods, Mod *mm) {
MCfg *m = (MCfg *)mm;
cJSON *json = cjson_get_object_item(root, m->name);
unsigned f = m->flags;
if (!(f & 2)) {
queue_module_toggle(mods, m, cjson_is_true(cjson_get_object_item(json, mc_obf_name_on)) != 0);
f = m->flags;
}
if (!(f & 1)) {
cJSON *item = cjson_get_object_item(json, str_bind_in_profiles);
int key;
if (cjson_is_number(item)) {
int k = (int)(((JNum *)item)->vd + offset_0_5_in_profiles);
key = 0;
if ((unsigned)(k - 0x68) >= (unsigned)-0x67) key = k;
} else {
key = 0;
}
apply_module_state(mods, m, key);
m->hold = (m->flags & 8) ? 0 : cjson_is_true(cjson_get_object_item(json, str_hold)) != 0;
}
if (!m->visit) return true;
Visit vv = {json, 0, 0};
m->visit(&vv);
return !vv.failed;
}
extern "C" unsigned char save_module_settings(cJSON *root, Mod *mm) {
MCfg *m = (MCfg *)mm;
cJSON *json = cjson_add_bool_to_object(root, m->name);
unsigned f = m->flags;
if (!(f & 2) && m->enabled == 1) {
if (!cjson_add_item_to_object(json, mc_obf_name_on, 1)) return false;
f = m->flags;
}
if (!(f & 1)) {
if (m->bind) {
if (!cjson_add_number_to_object(json, str_bind_in_profiles, (double)m->bind)) return false;
f = m->flags;
}
if (!(f & 8) && m->hold == 1 && !cjson_add_item_to_object(json, str_hold, 1)) return false;
}
if (!m->visit) return true;
Visit vv = {json, 1, 0};
m->visit(&vv);
return !vv.failed;
}
// ---- ImGui widget wrappers: mark the profile dirty when a widget changed its setting ----
FN(unsigned char, draw_toggle_row, (void *gui, void *window, const char *label, void *setting, void *value));
FN(unsigned char, draw_slider_row, (void *gui, void *window, void *label, void *format, void *setting, float min, float max, float step, float scale,
void *value));
FN(unsigned char, draw_range_slider_row, (void *gui, void *window, void *label, void *format, void *range_format, void *setting, float min, float max,
float step, float scale, void *low, void *high));
// bool setting: label, then the switch value
struct ToggleSetting { char padding_00[0x10]; void *label; unsigned char value; };
extern "C" void widget_bool(void *window, void *gui, ToggleSetting *setting) {
if (draw_toggle_row(gui, window, (const char *)setting->label, setting, &setting->value)) set_profile_dirty(1);
}
// float setting: label, display format, step, display scale, bounds, current value
struct SliderSetting { char padding_00[0x10]; void *label, *format; float step, scale, min, max, value; };
extern "C" void widget_slider(void *window, void *gui, SliderSetting *setting) {
if (draw_slider_row(gui, window, setting->label, setting->format, setting, setting->min, setting->max, setting->step, setting->scale, &setting->value)) set_profile_dirty(1);
}
// range setting: label, display formats, step, display scale, bounds, current low / high
struct RangeSetting { char padding_00[0x18]; void *label, *format, *range_format; float step, scale, min, max, low, high; };
extern "C" void widget_range(void *window, void *gui, RangeSetting *setting) {
if (draw_range_slider_row(gui, window, setting->label, setting->format, setting->range_format, setting, setting->min, setting->max, setting->step, setting->scale, &setting->low, &setting->high)) set_profile_dirty(1);
}
// ---- list setting (names, optionally with a colour each) ----
FN(cJSON *, cjson_add_array_to_object, (cJSON *object, const char *name)); // cJSON_AddArrayToObject
FN(cJSON *, cjson_create_string, (const char *text)); // cJSON_CreateString
FN(int, cjson_add_item_to_array, (cJSON *arr, cJSON *item)); // cJSON_AddItemToArray
FN(int, cjson_is_array, (const cJSON *object)); // cJSON_IsArray
extern "C" const char str_name[], str_color[]; // "name", "color"
extern "C" const double default_colour_rg_one_as_double; // {1.0f, 1.0f}
struct JNode2 { JNode2 *next, *prev, *child; int type; const char *vs; };
struct ListSet {
char padding_00[0x18]; int kind; int padding_1c; void *ctx;
unsigned long long (*count)(void *ctx);
const char *(*name)(void *ctx, unsigned long long i);
const float *(*color)(void *ctx, unsigned long long i);
void (*add)(void *ctx, const char *name, Color *c);
void (*remove)(void *ctx, unsigned long long i);
void (*clear)(void *ctx);
};
extern "C" void visit_list_setting(Visit *visitor, const char *name, ListSet *s) {
if (visitor->failed) return;
int mode = visitor->mode;
if (mode == 0) {
s->clear(s->ctx);
JNode2 *arr = (JNode2 *)cjson_get_object_item(visitor->json, name);
unsigned char notarr = !cjson_is_array((cJSON *)arr);
if ((arr == 0) | notarr) return;
for (JNode2 *e = arr->child; e; e = e->next) {
if (s->kind == 1) {
if (!cjson_is_object((cJSON *)e)) continue;
JNode2 *item = (JNode2 *)cjson_get_object_item((cJSON *)e, str_name);
if (!cjson_is_string((cJSON *)item)) continue;
cJSON *col = cjson_get_object_item((cJSON *)e, str_color);
void *ctx = s->ctx;
auto add = s->add;
const char *str = item->vs;
Color def;
*(double *)&def = default_colour_rg_one_as_double;
def.b = 1.0f;
Color c = parse_color(col, &def);
add(ctx, str, &c);
} else if (s->kind == 0) {
if (!cjson_is_string((cJSON *)e)) continue;
Color c = {0, 0, 0};
s->add(s->ctx, e->vs, &c);
}
}
} else if (mode == 1) {
cJSON *arr = cjson_add_array_to_object(visitor->json, name);
if (!arr) { visitor->failed = 1; return; }
if (!s->count(s->ctx)) return;
for (unsigned long long i = 0; i < s->count(s->ctx); i++) {
unsigned char bad;
if (s->kind == 1) {
bad = 1;
cJSON *json = cjson_create_object();
if (!json) goto fail;
if (!cjson_add_string_to_object(json, str_name, s->name(s->ctx, i))) goto fail;
{
cJSON *c = cjson_add_bool_to_object(json, str_color);
if (!c) goto fail;
const float *col = s->color(s->ctx, i);
if (!cjson_add_number_to_object(c, str_r_in_profiles, (double)col[0]) || !cjson_add_number_to_object(c, str_g_in_profiles, (double)col[1]) ||
!cjson_add_number_to_object(c, str_b_in_profiles, (double)col[2]))
goto fail;
bad = cjson_add_item_to_array(arr, json) == 0;
}
} else if (s->kind == 0) {
cJSON *str = cjson_create_string(s->name(s->ctx, i));
bad = str ? cjson_add_item_to_array(arr, str) == 0 : 1;
} else {
continue;
}
fail:
visitor->failed |= bad;
}
} else if (mode == 3) {
s->clear(s->ctx);
}
}
FN(void, libc_free, (void *ptr)); // free
extern "C" void clear_saved_string() {
if (settings_buffer) libc_free(settings_buffer);
settings_buffer = 0;
}
+409
View File
@@ -0,0 +1,409 @@
// JNI bridge 0x22000
// cflags: /Ob1 /clang:-fno-slp-vectorize
#include "slinky/engine.h"
#include "slinky/jcall.h"
struct Rot { float yaw, pitch; };
struct Vec3 { double x, y, z; };
extern "C" jobject get_minecraft(Jvm *jvm) {
return JCALL(jvm, GetStaticObjectField, ids->Minecraft, ids->Minecraft_theMinecraft);
}
// Lunar and other modern remappers keep the MCP class names but rename the
// members (Mojang names such as f_71439_g). The generated JIds tables only
// know the vanilla MCP/SRG spellings, so resolve the two fields the frame hooks
// need on first use instead of passing a null jfieldID to GetObjectField.
static jfieldID resolve_minecraft_field(Jvm *jvm, jfieldID *slot,
const char *type1, const char *type2,
const char *const *names, int count) {
if (*slot) return *slot;
JNIEnv *env = jvm->env;
jclass cls = (jclass)jvm->ids->Minecraft;
for (int t = 0; t < 2; t++) {
const char *sig = t == 0 ? type1 : type2;
if (!sig) continue;
for (int i = 0; i < count; i++) {
jfieldID f = (*env)->GetFieldID(env, cls, names[i], sig);
if (f) {
*slot = f;
return f;
}
(*env)->ExceptionClear(env);
}
}
return nullptr;
}
extern "C" jobject get_local_player(Jvm *jvm, void *minecraft) {
static const char *const names[] = { "thePlayer", "field_71439_g", "f_71439_g", "player" };
jfieldID id = resolve_minecraft_field(jvm, &jvm->ids->Minecraft_thePlayer,
"Lnet/minecraft/client/entity/EntityPlayerSP;",
"Lnet/minecraft/client/entity/EntityClientPlayerMP;",
names, 4);
if (!id) return nullptr;
return JCALL(jvm, GetObjectField, minecraft, id);
}
extern "C" jobject get_world(Jvm *jvm, void *minecraft) {
static const char *const names[] = { "theWorld", "field_71441_e", "f_71441_e", "world" };
jfieldID id = resolve_minecraft_field(jvm, &jvm->ids->Minecraft_theWorld,
"Lnet/minecraft/world/World;",
"Lnet/minecraft/client/multiplayer/WorldClient;",
names, 4);
if (!id) return nullptr;
return JCALL(jvm, GetObjectField, minecraft, id);
}
extern "C" jobject get_timer(Jvm *jvm, void *minecraft) {
return JCALL(jvm, GetObjectField, minecraft, ids->Minecraft_timer);
}
extern "C" jobject get_game_settings(Jvm *jvm, void *minecraft) {
return JCALL(jvm, GetObjectField, minecraft, ids->Minecraft_gameSettings);
}
extern "C" jobject get_minecraft_render_global(Jvm *jvm, void *minecraft) {
return JCALL(jvm, GetObjectField, minecraft, ids->Minecraft_renderGlobal);
}
extern "C" jobject java_get_minecraft(Jvm *jvm, void *minecraft) {
return JCALL(jvm, GetObjectField, minecraft, ids->Minecraft_entityRenderer);
}
extern "C" jobject java_get_minecraft_render_item(Jvm *jvm, void *minecraft) {
return JCALL(jvm, GetObjectField, minecraft, ids->Minecraft_renderItem);
}
extern "C" jobject java_get_minecraft_render_engine(Jvm *jvm, void *minecraft) {
return JCALL(jvm, GetObjectField, minecraft, ids->Minecraft_renderEngine);
}
extern "C" jobject java_get_minecraft_player_controller(Jvm *jvm, void *minecraft) {
return JCALL(jvm, GetObjectField, minecraft, ids->Minecraft_playerController);
}
extern "C" jobject java_get_minecraft_object_mouse_over(Jvm *jvm, void *minecraft) {
return JCALL(jvm, GetObjectField, minecraft, ids->Minecraft_objectMouseOver);
}
extern "C" void java_set_minecraft(Jvm *jvm, void *minecraft, void *value) {
JCALL(jvm, SetObjectField, minecraft, ids->Minecraft_objectMouseOver, value);
}
extern "C" bool is_in_game(Jvm *jvm, void *minecraft) {
return JCALLN(jvm, GetBooleanField, minecraft, ids->Minecraft_inGameHasFocus) != 0;
}
extern "C" jint java_get_minecraft_left_click_counter(Jvm *jvm, void *minecraft) {
return JCALL(jvm, GetIntField, minecraft, ids->Minecraft_leftClickCounter);
}
extern "C" void java_set_minecraft_left_click_counter(Jvm *jvm, void *minecraft, int value) {
JCALL(jvm, SetIntField, minecraft, ids->Minecraft_leftClickCounter, value);
}
extern "C" jobject get_render_view_entity(Jvm *jvm, void *minecraft) {
return JCALL(jvm, GetObjectField, minecraft, ids->Minecraft_renderViewEntity);
}
extern "C" jobject java_call_minecraft_get_net_handler(Jvm *jvm, void *minecraft) {
return JCALLN(jvm, CallObjectMethod, minecraft, ids->Minecraft_getNetHandler);
}
extern "C" void java_call_minecraft_click_mouse(Jvm *jvm, void *minecraft) {
JCALLN(jvm, CallVoidMethod, minecraft, ids->Minecraft_clickMouse);
}
extern "C" void java_call_minecraft_right_click_mouse(Jvm *jvm, void *minecraft) {
JCALLN(jvm, CallVoidMethod, minecraft, ids->Minecraft_rightClickMouse);
}
// ---- hand-written
FN(jobject, java_get_render_manager_instance, (Jvm *jvm, jobject render_manager));
extern "C" jobject get_current_screen(Jvm *jvm, jobject object) {
jobject x = JCALLN(jvm, GetObjectField, object, (jfieldID)ids->Minecraft_currentScreen);
return JCALLN(jvm, IsInstanceOf, x, (jclass)ids->GuiContainer) ? x : nullptr;
}
extern "C" jobject java_get_minecraft_render_manager(Jvm *jvm, jobject minecraft) {
if (jvm->ids->variant != 0)
return JCALL(jvm, GetObjectField, minecraft, (jfieldID)ids->Minecraft_renderManager);
return java_get_render_manager_instance(jvm, minecraft);
}
// ---- ClassFileLoadHook: rewrite the classes listed in the hook table, injecting "SlinkyHooks13"
struct MethodKey { char b[32]; };
struct MethodPat { long long a, b; };
struct HookTab { char padding_00[0x18]; MethodPat *pats; unsigned long long n; };
struct HookCls { jobject cls; void *data; int len; char padding_14[0x14]; }; // 0x28 bytes each
extern "C" volatile int classfile_hooks_first_error; // first error (sticky)
extern "C" volatile long classfile_hooks_sites_rewritten = 0; // instructions replaced by the class currently being rewritten (diagnostics)
extern "C" unsigned char classfile_hooks_1_while_hooks_are_installed; // 1 while the hooks are installed
extern "C" unsigned char classfile_hooks_world_client_pending = 0; // WorldClient retransform skipped at install on Java 9+
extern "C" unsigned char classfile_hooks_world_client_patched = 0; // ... and has since been applied
extern "C" unsigned char java_natives_frame_client_shutting_down_natives_check; // the client is unloading: the transformer must go inert
extern "C" void slinky_logf(const char *fmt, ...);
static unsigned char g_disarming; // 1 while remove_class_hooks restores the original bytecode (atomic)
extern "C" HookCls hooked_class_minecraft, hooked_class_entity_renderer, hooked_class_render_manager, hooked_class_renderer_living_entity, hooked_class_render_player,
hooked_class_net_handler_play_client, hooked_class_world_client, hooked_class_entity, hooked_class_entity_living_base;
extern "C" const char str_slinkyhooks13[]; // "SlinkyHooks13"
FN(bool, compare_member_name, (MethodKey left, MethodPat right));
struct HookTable; struct CP; struct RBuf22; struct WBuf22;
FN(int, rewrite_instruction, (HookTable *t, CP *constant_pool, RBuf22 *in, WBuf22 *out)); // instruction patcher (below)
FN(int, rewrite_class_file, (const unsigned char *data, int len, unsigned char **out, int *outLen, const char *hookCls,
void *hookData, int hookLen, void *match, HookCls *h));
FN(void, libc_free, (void *ptr)); // free
FN(void *, libc_memcpy_alt, (void *dest, const void *src, long long n)); // memcpy
struct JvmtiEnv_ { void *fn[64]; };
typedef int (*JvmtiAllocate)(JvmtiEnv_ **, long long, unsigned char **);
// method matcher passed to the class rewriter: (-1,-1) entries match everything
extern "C" int match_method(HookTab *t, MethodKey *k, void **outFn, MethodPat **outPat) {
static int pat_logs;
for (unsigned long long i = 0; i < t->n; i++) {
MethodPat *p = &t->pats[i * 2];
if (p->a == -1 && p->b == -1) {
*outFn = (void *)rewrite_instruction;
*outPat = p;
break;
}
unsigned long long na = (unsigned long long)p->a, da = (unsigned long long)p->b;
if (pat_logs < 12) { pat_logs++; slinky_logf("patcmp: i=%llu name=%p desc=%p", i, p->a, p->b); }
if (na < 0x10000 || da < 0x10000) {
slinky_logf("patcmp: bad pattern name=%llx desc=%llx (skipped)", na, da);
continue;
}
if (compare_member_name(*k, *p)) {
*outFn = (void *)rewrite_instruction;
*outPat = p;
break;
}
}
return 0;
}
extern "C" void class_file_load_hook(JvmtiEnv_ **jvmti, JNIEnv *env, jclass redefined, jobject loader, const char *name,
jobject pd, int len, const unsigned char *data, int *newLen, unsigned char **newData) {
if (classfile_hooks_first_error != 0) return;
// remove_class_hooks sets g_disarming before it retransforms: during that call the JVM may replay this hook
// for the classes being restored, and re-patching them would silently undo the disarm. Handing the
// bytes back untouched makes the retransform a true restore. (The install also runs with the hook
// active and g_disarming clear, so it still patches.)
if (__atomic_load_n(&g_disarming, __ATOMIC_SEQ_CST) || java_natives_frame_client_shutting_down_natives_check) return;
// JVMTI passes class_being_redefined = NULL when a class is loaded for the first time. IsSameObject(NULL,
// NULL) is true, so an empty table entry (all of them before build_records ran, or one whose class did
// not resolve) would match every class load and rewrite it with the wrong hook record. The nine hooked
// classes are all loaded by init_jni_ids; install patches them with RetransformClasses.
if (!redefined) return;
HookCls *pctx;
#define TRY(x) if (x.cls && (*env)->IsSameObject(env, x.cls, redefined)) { pctx = &x; goto found; }
TRY(hooked_class_minecraft) TRY(hooked_class_entity_renderer) TRY(hooked_class_render_manager) TRY(hooked_class_renderer_living_entity) TRY(hooked_class_render_player)
TRY(hooked_class_net_handler_play_client) TRY(hooked_class_world_client) TRY(hooked_class_entity) TRY(hooked_class_entity_living_base)
#undef TRY
return;
found:
slinky_logf("classhook-seen: %s", name ? name : "?");
unsigned char *buf = nullptr;
int outLen;
__atomic_store_n(&classfile_hooks_sites_rewritten, 0, __ATOMIC_RELAXED);
int err = rewrite_class_file(data, len, &buf, &outLen, str_slinkyhooks13, pctx->data, pctx->len, (void *)match_method, pctx);
long sites = __atomic_load_n(&classfile_hooks_sites_rewritten, __ATOMIC_RELAXED);
if (err == 0)
slinky_logf("classhook: %s sites=%ld %d->%d", name ? name : "?", sites, len, outLen);
else
slinky_logf("classhook: %s sites=%ld err=%d", name ? name : "?", sites, err);
if (err == 0) {
unsigned char *mem = nullptr;
JvmtiAllocate alloc = (JvmtiAllocate)(*jvmti)->fn[45];
int alloc_error = alloc(jvmti, outLen, &mem);
if (alloc_error != 0 || !mem) {
err = (alloc_error ? alloc_error : 100) + 12000; // JVMTI_ERROR_NULL_POINTER
libc_free(buf);
__sync_val_compare_and_swap(&classfile_hooks_first_error, 0, err);
return;
}
libc_memcpy_alt(mem, buf, outLen);
*newData = mem;
*newLen = outLen;
libc_free(buf);
return;
}
__sync_val_compare_and_swap(&classfile_hooks_first_error, 0, err);
}
// ---- JNI bridge functions found by the gap scan (no caller outside the virtualised init) ----
extern "C" jint java_get_minecraft_right_click_delay_timer(Jvm *jvm, void *minecraft) {
return JCALL(jvm, GetIntField, minecraft, ids->Minecraft_rightClickDelayTimer);
}
extern "C" jobject java_call_minecraft_add_scheduled_task(Jvm *jvm, void *minecraft, void *value) {
return JCALLN(jvm, CallObjectMethod, minecraft, ids->Minecraft_addScheduledTask, value);
}
// ---- JVMTI: switch the class hooks off again (undo of the install) ----
// Disables the ClassFileLoadHook event, retransforms the 9 hooked classes so the game gets its original bytecode
// back, gives the capabilities up and drops the global ref of the transformer class. Errors are JVMTI error + 12000.
struct JvmtiTable { void *fn[160]; };
typedef int (*JvmtiSetEventMode)(JvmtiTable **, int mode, int event, void *thread, ...);
typedef int (*JvmtiRetransform)(JvmtiTable **, int count, jclass *classes);
typedef int (*JvmtiRelinquish)(JvmtiTable **, const void *caps);
#define JVMTI(env, idx, T) ((T)(*(env))->fn[idx])
extern "C" unsigned char classfile_hooks_1_while_hooks_are_installed; // 1 while the hooks are installed
extern "C" jobject classfile_hooks_global_ref_transformer_class; // global ref of the transformer class
extern "C" const char ptr_to_hook_installer_class_file_slinkyhooks13[]; // JVMTI capabilities that were added
#define JVMTI_CHECK(call) \
do { \
int check_error = (call); \
if (check_error) { \
err = check_error + 0x2ee0; \
goto out; \
} \
} while (0)
// Deferred half of the Java 9+ install: the installer skips RetransformClasses(WorldClient) because
// Lunar/Badlion transformers can deadlock in it. Retry from the game thread after the installer has
// completed; if it succeeds the class is patched and must also be restored by remove_class_hooks.
extern "C" int retransform_pending_world_client(void *jvmti_env) {
if (!__atomic_load_n(&classfile_hooks_world_client_pending, __ATOMIC_SEQ_CST)) return 0;
__atomic_store_n(&classfile_hooks_world_client_pending, 0, __ATOMIC_SEQ_CST);
jclass one = hooked_class_world_client.cls;
if (!one) return 12000 + 49 /* JVMTI_ERROR_NOT_AVAILABLE */;
JvmtiTable **jvmti = (JvmtiTable **)jvmti_env;
int transform_error = JVMTI(jvmti, 151, JvmtiRetransform)(jvmti, 1, &one);
if (transform_error) {
slinky_logf("classhooks: deferred WorldClient retransform err=%d", transform_error);
} else {
__atomic_store_n(&classfile_hooks_world_client_patched, 1, __ATOMIC_SEQ_CST);
slinky_logf("classhooks: deferred WorldClient retransform ok");
}
return transform_error;
}
extern "C" int remove_class_hooks(JNIEnv *env, JvmtiTable **jvmti) {
int err = 0;
// Not armed = the hooks were never installed or another pass already consumed the flag (a failed
// pass left the game's patched calls live). Reporting success here made the unload sequence tear
// everything down while the JVM was still dispatching natives into this DLL: hs_err pid6708
// (java_set_render_manager_player_view_y via setRenderY) and pid2260 (serialize_settings via a second hook_update_display pass) look exactly like that.
// Set the callback guard before clearing the armed flag: a class-load callback racing with this
// CAS must not re-patch a class between the two operations.
__atomic_store_n(&g_disarming, 1, __ATOMIC_SEQ_CST);
if (!__sync_bool_compare_and_swap(&classfile_hooks_1_while_hooks_are_installed, 1, 0)) {
__atomic_store_n(&g_disarming, 0, __ATOMIC_SEQ_CST);
// A failed install may still have defined SlinkyHooks13 and kept its global ref.
if (env && classfile_hooks_global_ref_transformer_class) {
(*env)->DeleteGlobalRef(env, classfile_hooks_global_ref_transformer_class);
classfile_hooks_global_ref_transformer_class = 0;
}
return 12000 + 49 /* JVMTI_ERROR_NOT_AVAILABLE */;
}
JVMTI_CHECK(JVMTI(jvmti, 1, JvmtiSetEventMode)(jvmti, 0 /* JVMTI_DISABLE */, 0x36 /* CLASS_FILE_LOAD_HOOK */, 0));
{
jclass all[9] = {hooked_class_minecraft.cls, hooked_class_entity_renderer.cls, hooked_class_render_manager.cls, hooked_class_renderer_living_entity.cls, hooked_class_render_player.cls,
hooked_class_net_handler_play_client.cls, hooked_class_world_client.cls, hooked_class_entity.cls, hooked_class_entity_living_base.cls};
// Same Java 9+ deadlock as install: WorldClient is index 6. Skip it only if the
// deferred retransform never patched it; a patched WorldClient must be restored
// before the DLL is unloaded.
bool java9plus = false;
if (env) java9plus = ((unsigned)(*env)->GetVersion(env) >> 16) >= 9;
bool restore_world_client = !java9plus || __atomic_load_n(&classfile_hooks_world_client_patched, __ATOMIC_SEQ_CST);
jclass classes[9];
int n = 0;
for (int i = 0; i < 9; i++) {
if (i == 6 && !restore_world_client) continue;
if (all[i]) classes[n++] = all[i];
}
if (n) JVMTI_CHECK(JVMTI(jvmti, 151, JvmtiRetransform)(jvmti, n, classes));
if (restore_world_client) __atomic_store_n(&classfile_hooks_world_client_patched, 0, __ATOMIC_SEQ_CST);
}
__atomic_store_n(&g_disarming, 0, __ATOMIC_SEQ_CST);
JVMTI_CHECK(JVMTI(jvmti, 142, JvmtiRelinquish)(jvmti, ptr_to_hook_installer_class_file_slinkyhooks13));
err = 0;
out:
__atomic_store_n(&g_disarming, 0, __ATOMIC_SEQ_CST);
if (classfile_hooks_global_ref_transformer_class) {
if (env) {
if (!err) (*env)->UnregisterNatives(env, classfile_hooks_global_ref_transformer_class);
(*env)->DeleteGlobalRef(env, classfile_hooks_global_ref_transformer_class);
}
classfile_hooks_global_ref_transformer_class = 0;
}
if (!err) __atomic_store_n(&classfile_hooks_world_client_pending, 0, __ATOMIC_SEQ_CST);
// A failed step leaves the client resident (the unload sequence cancels on errors): re-arm the flag
// so the next click gets a whole new pass. Retransform is idempotent, so resuming is safe.
slinky_logf("disarm: err=%d", err);
if (err) __sync_bool_compare_and_swap(&classfile_hooks_1_while_hooks_are_installed, 0, 1);
return err;
}
// ---- class-file patcher: rewrites a call/field instruction that matches a hook entry into `invokestatic hook` ----
// Instruction callback of the class transformer (rewrite_span/copy_methods_table_constant_pool `pick`). `t` lists the hooks; each has the
// owner class, member name + descriptor to catch, the kind of instruction and where its new Methodref index is.
#include "slinky/classfile.h"
struct RBuf22 { const unsigned char *p, *end; };
struct WBuf22 { unsigned char *p, *end; };
struct HookEnt { const char *cls; const char *name, *desc; int kind; int padding_1c; unsigned short *ref; }; // 0x28
struct HookTable { char padding_00[0x10]; HookEnt *e; unsigned long long n; };
struct MemberName { const char *name, *desc; };
FN(int, classfile_read_u1, (RBuf22 *r, unsigned char *out));
FN(int, classfile_read_u2, (RBuf22 *r, unsigned short *out));
FN(int, classfile_write_u1, (WBuf22 *w, unsigned char b));
FN(int, classfile_write_u2, (WBuf22 *w, unsigned short v));
FN(int, constant_pool_member_ref, (CP *constant_pool, unsigned short idx, MemberRef *out));
FN(int, constant_pool_member_ref2, (CP *constant_pool, unsigned short idx, MemberRef *out));
extern "C" int compare_utf8_local(Utf8 left, const char *right) __asm__("compare_utf8");
extern "C" bool compare_member_name_local(NameType left, MemberName right) __asm__("compare_member_name");
extern "C" int rewrite_instruction(HookTable *t, CP *cp, RBuf22 *in, WBuf22 *out) {
unsigned char op;
int error_code = classfile_read_u1(in, &op);
if (error_code) return error_code;
int kind;
bool field = true;
switch (op) {
case 0xb2: kind = 4; break; // getstatic
case 0xb3: kind = 6; break; // putstatic
case 0xb4: kind = 3; break; // getfield
case 0xb5: kind = 5; break; // putfield
case 0xb6: case 0xb7: kind = 1; field = false; break; // invokevirtual / invokespecial
case 0xb8: kind = 2; field = false; break; // invokestatic
default: return 0;
}
unsigned short idx;
error_code = classfile_read_u2(in, &idx);
if (error_code) return error_code;
MemberRef ref;
error_code = field ? constant_pool_member_ref(cp, idx, &ref) : constant_pool_member_ref2(cp, idx, &ref);
if (error_code) return error_code;
for (unsigned long long i = 0; i < t->n; i++) {
HookEnt *h = &t->e[i];
if (h->kind != kind) continue;
if (compare_utf8_local(ref.cls, h->cls) != 0) continue;
unsigned long long np = (unsigned long long)h->name, dp = (unsigned long long)h->desc;
static int site_logs;
if (site_logs < 30) { site_logs++; slinky_logf("sitecmp: cls=%s kind=%d name=%p desc=%p", h->cls, h->kind, h->name, h->desc); }
if (np < 0x10000 || dp < 0x10000) {
slinky_logf("sitecmp: bad site name=%llx desc=%llx (skipped)", np, dp);
continue;
}
if (!compare_member_name_local(ref.nt, MemberName{h->name, h->desc})) continue;
error_code = classfile_write_u1(out, 0xb8); // invokestatic
if (error_code) return error_code;
error_code = classfile_write_u2(out, *h->ref);
if (error_code == 0) __atomic_add_fetch(&classfile_hooks_sites_rewritten, 1, __ATOMIC_RELAXED);
return error_code;
}
return 0;
}
// ---- functions the first gap scan missed (padding / tail-jump neighbours) ----
extern "C" void java_set_minecraft_right_click_delay_timer(Jvm *jvm, void *minecraft, int value) {
JCALL(jvm, SetIntField, minecraft, ids->Minecraft_rightClickDelayTimer, value);
}
extern "C" jint java_get_minecraft_display_width(Jvm *jvm, void *minecraft) {
return JCALL(jvm, GetIntField, minecraft, ids->Minecraft_displayWidth);
}
extern "C" jint java_get_minecraft_display_height(Jvm *jvm, void *minecraft) {
return JCALL(jvm, GetIntField, minecraft, ids->Minecraft_displayHeight);
}
+542
View File
@@ -0,0 +1,542 @@
// 0x20000: class-file patching helpers (member refs, name compares)
// cflags: /Ob1 /clang:-fno-slp-vectorize -mllvm -switch-to-lookup=false
#include "slinky/engine.h"
#include "slinky/classfile.h"
FN(int, constant_class_name, (CP *constant_pool, unsigned short idx, Utf8 *out));
FN(int, constant_name_and_type, (CP *constant_pool, unsigned short idx, NameType *out));
FN(int, libc_strncmp_alt, (const void *left, const void *right, unsigned long long n));
// CONSTANT_Fieldref/Methodref (tag 9) -> class, name, descriptor
extern "C" int constant_pool_member_ref(CP *constant_pool, unsigned short idx, MemberRef *out) {
const unsigned char *end, *lim;
int error_code = 0x2afd;
CP_ENTRY(constant_pool, idx, end, lim, out_);
if (end + 1 > lim) goto trunc;
error_code = 0x2afe;
if (*end != 9) goto out_;
if (end + 3 > lim) goto trunc;
error_code = 0x2af8;
if (end + 5 > lim) goto out_;
{
unsigned short ci = be16(end + 1);
unsigned char hi = end[3], lo = end[4];
error_code = constant_class_name(constant_pool, ci, &out->cls);
if (error_code) goto out_;
return constant_name_and_type(constant_pool, (unsigned short)((hi << 8) | lo), &out->nt);
}
trunc:
error_code = 0x2af8;
out_:
return error_code;
}
// compare a constant-pool string with a C string (strncmp semantics, bounded by len + 1);
// `k` is the compile-time copy of `s` used for the length (lets a literal fold like the original)
static inline __attribute__((always_inline)) int utf8_cmpk(const Utf8 *u, const char *s, const char *k) {
unsigned long long n;
if (!k[0]) {
n = 0;
} else {
unsigned long long lim = (unsigned long long)u->len + 1;
unsigned long long i = 0;
for (;;) {
if (lim == i) { n = lim; break; }
if (!k[i + 1]) { n = i + 1; break; }
i++;
}
}
return libc_strncmp_alt(u->p, s, n);
}
static inline __attribute__((always_inline)) int utf8_cmp(const Utf8 *u, const char *s) {
unsigned long long n;
if (!s[0]) {
n = 0;
} else {
unsigned long long lim = (unsigned long long)u->len + 1;
unsigned long long i = 0;
for (;;) {
if (lim == i) { n = lim; break; }
if (!s[i + 1]) { n = i + 1; break; }
i++;
}
}
return libc_strncmp_alt(u->p, s, n);
}
extern "C" int compare_utf8(const Utf8 *left, const char *right) { return utf8_cmp(left, right); }
// does the NameAndType match {name, desc}?
extern "C" bool compare_member_name(const NameType *left, const char *const *right) {
if (utf8_cmp(&left->name, right[0]) != 0) return false;
return utf8_cmp(&left->desc, right[1]) == 0;
}
// ---- class-file writer: bounded output cursor, every put fails with 0x2af9 when it would overflow ----
struct W { unsigned char *p, *end; };
extern "C" __attribute__((pure)) unsigned long long libc_strlen(const char *str); // strlen
FN(void *, libc_memcpy_alt, (void *dest, const void *src, unsigned long long n));
#define W_PUT8(w, v) \
do { \
unsigned char *q_ = (w)->p + 1; \
if (q_ > (w)->end) return 0x2af9; \
*(w)->p = (unsigned char)(v); \
(w)->p = q_; \
} while (0)
#define W_PUT16(w, v) \
do { \
unsigned char *q_ = (w)->p + 2; \
if (q_ > (w)->end) return 0x2af9; \
*(w)->p = (unsigned char)((v) >> 8); \
(w)->p[1] = (unsigned char)(v); \
(w)->p = q_; \
} while (0)
// append CONSTANT_Utf8 `s` and a CONSTANT_Class naming it; *cls = the class entry's index
extern "C" int append_constant_utf8_s_constant(W *w, const char *s, unsigned short *count, unsigned short *cls) {
unsigned short u = (*count)++;
W_PUT8(w, 1);
unsigned char *q = w->p + 2;
if (q > w->end) return 0x2af9;
unsigned short n = (unsigned short)libc_strlen(s);
*w->p = (unsigned char)(n >> 8);
w->p[1] = (unsigned char)n;
w->p = q;
unsigned char *e = q + n;
if (e > w->end) return 0x2af9;
libc_memcpy_alt(q, s, n);
w->p = e;
unsigned c = *count;
*count = (unsigned short)(c + 1);
*cls = (unsigned short)c;
unsigned char *t = e + 1;
if (t > w->end) return 0x2af9;
*e = 7;
w->p = t;
q = e + 3;
if (q > w->end) return 0x2af9;
*t = (unsigned char)(u >> 8);
w->p[1] = (unsigned char)u;
w->p = q;
return 0;
}
// resolve two CONSTANT_Utf8 entries into {name, desc} and hand them to cb(a, &nt, b, c)
typedef int (*NtCb)(void *a, NameType *nt, void *b, void *c);
extern "C" int constant_pool_name_and_type(CP *constant_pool, unsigned short ni, unsigned short di, NtCb cb, void *a, void *b, void *c) {
const unsigned char *end, *lim, *u, *ulim;
int error_code = 0x2afd;
CP_ENTRY(constant_pool, ni, end, lim, out_);
if (end + 1 > lim) goto trunc;
if (*end != 1) goto tag;
{
const unsigned char *cursor = end + 3;
if (cursor > lim) goto trunc;
unsigned short len = be16(end + 1);
if (cursor + len > lim) goto trunc;
CP_ENTRY(constant_pool, di, u, ulim, out_);
if (u + 1 > ulim) goto trunc;
error_code = 0x2afe;
if (*u != 1) goto out_;
const unsigned char *q = u + 3;
if (q > ulim) goto trunc;
unsigned short dl = be16(u + 1);
error_code = 0x2af8;
if (q + dl > ulim) goto out_;
NameType nt = {{cursor, len}, {q, dl}};
return cb(a, &nt, b, c);
}
tag:
error_code = 0x2afe;
goto out_;
trunc:
error_code = 0x2af8;
out_:
return error_code;
}
// is constant-pool entry idx the Utf8 "Code"?
extern "C" const char str_code[]; // "Code"
extern "C" int constant_pool_utf8_member(CP *constant_pool, unsigned short idx, bool *out) {
const unsigned char *end, *lim;
int error_code = 0x2afd;
CP_ENTRY(constant_pool, idx, end, lim, out_);
if (end + 1 > lim) goto trunc;
error_code = 0x2afe;
if (*end != 1) goto out_;
{
const unsigned char *cursor = end + 3;
if (cursor > lim) goto trunc;
unsigned short len = be16(end + 1);
error_code = 0x2af8;
if (cursor + len > lim) goto out_;
Utf8 u = {cursor, len};
*out = utf8_cmpk(&u, str_code, "Code") == 0;
error_code = 0;
goto out_;
}
trunc:
error_code = 0x2af8;
out_:
return error_code;
}
// ---- bytecode walker: copies each instruction's span to `out` and hands both spans to cb ----
struct Span { const unsigned char *p, *end; };
typedef int (*InsCb)(void *ctx, void *a, Span *ins, Span *dst);
extern "C" const unsigned char classfile_patch_helpers_opcode_instruction_length[]; // opcode -> instruction length (0 = invalid)
#define IN_SKIP(in, n) \
do { \
const unsigned char *q_ = (in)->p + (n); \
if (q_ > (in)->end) return 0x2af8; \
(in)->p = q_; \
} while (0)
static inline __attribute__((always_inline)) unsigned be32(const unsigned char *p) {
return __builtin_bswap32(*(const unsigned *)p);
}
extern "C" int rewrite_span(Span *in, W *out, const unsigned char *code, void *a, InsCb cb, void *ctx) {
for (;;) {
const unsigned char *cursor = in->p;
if (cursor >= in->end) return 0;
unsigned char *dst = out->p;
unsigned char op = *cursor;
in->p = cursor + 1;
const unsigned char *cur, *nx;
switch (op) {
case 0xc4: { // wide: opcode + u16 index (+ s16 constant for iinc)
const unsigned char *s = in->p;
cur = s + 1;
if (cur > in->end) return 0x2af8;
nx = cur + (*s == 0x84 ? 4 : 2);
goto skip;
}
case 0xab: { // lookupswitch: pad, default, npairs, npairs * {match, offset}
unsigned pad = (unsigned)(in->p - code) & 3;
if (pad) IN_SKIP(in, 4 - pad);
IN_SKIP(in, 4);
const unsigned char *l = in->p;
cur = l + 4;
if (cur > in->end) return 0x2af8;
nx = cur + (unsigned)(be32(l) << 3);
goto skip;
}
case 0xaa: { // tableswitch: pad, default, low, high, (high - low + 1) offsets
unsigned pad = (unsigned)(in->p - code) & 3;
if (pad) IN_SKIP(in, 4 - pad);
IN_SKIP(in, 4);
const unsigned char *l = in->p;
if (l + 4 > in->end) return 0x2af8;
unsigned lo = (unsigned)l[0] << 24 | (unsigned)l[1] << 16 | (unsigned)l[2] << 8 | l[3];
in->p = l + 4;
cur = l + 8;
if (cur > in->end) return 0x2af8;
nx = cur + (unsigned)((be32(l + 4) - lo) * 4 + 4);
goto skip;
}
default:
if (op > 0xc9) return 0x2aff;
IN_SKIP(in, (unsigned)(classfile_patch_helpers_opcode_instruction_length[op] - 1));
break;
skip:
in->p = nx <= in->end ? nx : cur;
if (nx > in->end) return 0x2af8;
break;
}
const unsigned char *end = in->p;
unsigned n = (unsigned)(end - cursor);
unsigned char *o = out->p + n;
if (o > out->end) return 0x2af9;
out->p = o;
Span ins = {cursor, end}, os = {dst, o};
int error_code = cb(ctx, a, &ins, &os);
if (error_code) return error_code;
}
}
// copy a Code attribute's u4 code_length + code through the walker (the length word is copied verbatim)
extern "C" int copy_code_attribute_s_u4(Span *in, W *out, void *other_object, InsCb cb, void *ctx) {
const unsigned char *cursor = in->p, *iend = in->end;
const unsigned char *l = cursor + 4;
if (l > iend) return 0x2af8;
in->p = l;
const unsigned char *c = cursor + 8;
if (c > iend) return 0x2af8;
unsigned char b0 = l[0], b1 = l[1], b2 = l[2], b3 = l[3];
in->p = c;
unsigned char *op = out->p, *oend = out->end;
unsigned char *o = op + 8;
if (o > oend) return 0x2af9;
unsigned long long n = (unsigned)(b1 << 16 | b0 << 24 | b2 << 8) | (unsigned long long)b3;
out->p = o;
Span src = {c, c + n};
W dst = {o, o + n};
if (src.end > iend) return 0x2af8;
in->p = src.end;
if (dst.end > oend) return 0x2af9;
out->p = dst.end;
return rewrite_span(&src, &dst, c, other_object, cb, ctx);
}
// copy the methods table; when a constant pool is given, `pick` chooses per method an instruction
// callback (+ ctx) and the method's Code attribute is rewritten through it
extern "C" int copy_methods_table_constant_pool(Span *in, W *out, CP *constant_pool, NtCb pick, void *pctx) {
const unsigned char *cursor = in->p;
if (cursor + 2 > in->end) return 0x2af8;
unsigned char c0 = cursor[0], c1 = cursor[1];
in->p = cursor + 2;
unsigned char *o = out->p;
if (o + 2 > out->end) return 0x2af9;
*o = c0;
out->p[1] = c1;
out->p = o + 2;
unsigned short cnt = (unsigned short)(c0 << 8 | c1);
if (!cnt) return 0;
unsigned short m = 0;
do {
const unsigned char *h = in->p, *hend = in->end;
if (h + 2 > hend) return 0x2af8;
in->p = h + 2;
if (h + 4 > hend) return 0x2af8;
unsigned short ni = *(const unsigned short *)(h + 2);
in->p = h + 4;
if (h + 6 > hend) return 0x2af8;
unsigned short di = *(const unsigned short *)(h + 4);
in->p = h + 6;
if (h + 8 > hend) return 0x2af8;
unsigned char high_byte = h[6], low_byte = h[7];
in->p = h + 8;
unsigned char *w = out->p;
if (w + 8 > out->end) return 0x2af9;
__builtin_memcpy(w, h, 8);
out->p = w + 8;
InsCb cb = 0;
void *cbctx = 0;
if (constant_pool) {
int error_code = constant_pool_name_and_type(constant_pool, (unsigned short)(ni << 8 | ni >> 8), (unsigned short)(di << 8 | di >> 8), pick,
pctx, &cb, &cbctx);
if (error_code) return error_code;
}
unsigned short na = (unsigned short)((high_byte << 8) + low_byte);
for (; na; na--) {
const unsigned char *q = in->p, *qend = in->end;
if (q + 2 > qend) return 0x2af8;
InsCb acb = cb;
void *actx = cbctx;
unsigned short an = *(const unsigned short *)q;
in->p = q + 2;
if (q + 6 > qend) return 0x2af8;
unsigned long long len = (unsigned)((unsigned)q[2] << 24 | (unsigned)q[3] << 16 | (unsigned)q[4] << 8) | (unsigned long long)q[5];
in->p = q + 6;
unsigned char *ah = out->p;
unsigned char *body = ah + 6;
if (body > out->end) return 0x2af9;
__builtin_memcpy(ah, q, 6);
out->p = body;
const unsigned char *src = in->p;
if (src + len > in->end) return 0x2af8;
in->p = src + len;
unsigned char *bend = body + len;
if (bend > out->end) return 0x2af9;
if (src) libc_memcpy_alt(body, src, len);
out->p = bend;
if (!acb) continue;
bool code;
int r = constant_pool_utf8_member(constant_pool, (unsigned short)(an << 8 | an >> 8), &code);
if (!r && code) {
Span s2 = {src, in->p};
W w2 = {body, bend};
r = copy_code_attribute_s_u4(&s2, &w2, constant_pool, acb, actx);
}
if (r) return r;
}
} while (++m != cnt);
return 0;
}
// ---- class transformer: rewrite a class file, adding per hook a Utf8 name/desc, NameAndType and a
// Methodref on the class itself (hooks[i].ref = its index), and rewriting methods through `pick` ----
struct MHook { const char *name, *desc; unsigned short ref; };
FN(void *, libc_malloc, (long long n)); // malloc
FN(void *, libc_calloc, (unsigned long long n, unsigned long long size)); // calloc
FN(void, libc_free, (void *ptr)); // free
extern "C" int rewrite_class_file(const unsigned char *cls, int len, unsigned char **outBuf, int *outLen, const char *clsName,
MHook *hooks, int nh, NtCb pick, void *pctx) {
long long cap = len + 0x1000;
unsigned char *buf = (unsigned char *)libc_malloc(cap);
if (!buf) return 0x3ed;
const unsigned char *end = cls + len;
unsigned char *oend = buf + cap;
const unsigned char **ents;
int error_code = 0x2af8;
if (len < 4) goto free_buf;
error_code = 0x2afa;
if (be32(cls) != 0xcafebabe) goto free_buf;
error_code = 0x2af8;
if ((unsigned)len < 8) goto free_buf;
error_code = 0x2afb;
if (be16(cls + 6) > 0x45) goto free_buf; // newer than Java 1 (major 69)
__builtin_memcpy(buf, cls, 8);
error_code = 0x2af8;
if ((unsigned)len < 10) goto free_buf;
{
unsigned short cpn = be16(cls + 8);
Span in = {cls + 10, end};
unsigned nc = cpn + nh * 4 + 2;
buf[8] = (unsigned char)(nc >> 8);
buf[9] = (unsigned char)nc;
W out = {buf + 10, oend};
unsigned short n1 = cpn - 1;
ents = (const unsigned char **)libc_calloc(n1, 8);
if (!ents) {
error_code = 0x3ed;
goto free_buf;
}
// index the constant pool (long/double take two slots)
const unsigned char *cursor = cls + 10;
for (unsigned short i = 0; i < n1;) {
ents[i] = cursor;
const unsigned char *q = cursor + 1;
if (q > end) { error_code = 0x2af8; goto free_both; }
unsigned char tag = *cursor;
in.p = q;
error_code = 0x2afc;
unsigned long long sz;
switch (tag) {
case 3: case 4: case 9: case 10: case 11: case 12: case 17: case 18: sz = 4; break;
case 7: case 8: case 16: case 19: case 20: sz = 2; break;
case 5: case 6: sz = 8; break;
case 15: sz = 3; break;
case 1:
q = cursor + 3;
if (q > end) { error_code = 0x2af8; goto free_both; }
q += (cursor[1] << 8) + cursor[2];
error_code = 0x2af8;
if (q > end) goto free_both;
in.p = q;
i++;
cursor = q;
continue;
default: goto free_both;
}
q += sz;
error_code = 0x2af8;
if (q > end) goto free_both;
in.p = q;
i += (unsigned char)(tag - 5) < 2;
i++;
cursor = q;
}
unsigned cpl = (unsigned)(cursor - (cls + 10));
unsigned char *ce = buf + 10 + cpl;
if (ce > oend) { error_code = 0x2af9; goto free_both; }
libc_memcpy_alt(buf + 10, cls + 10, cpl);
out.p = ce;
unsigned short count = cpn, clsIdx;
error_code = append_constant_utf8_s_constant(&out, clsName, &count, &clsIdx);
if (error_code) goto free_both;
if (nh > 0) {
unsigned char *w = out.p;
if (w + 1 > oend) { error_code = 0x2af9; goto free_both; }
unsigned c = count;
for (unsigned long long k = 0;;) {
const char *nm = hooks[k].name;
w[0] = 1;
unsigned char *s = w + 3;
if (s > oend) { error_code = 0x2af9; goto free_both; }
unsigned short l = (unsigned short)libc_strlen(nm);
w[1] = (unsigned char)(l >> 8);
w[2] = (unsigned char)l;
unsigned char *t = s + l;
if (t > oend) { error_code = 0x2af9; goto free_both; }
libc_memcpy_alt(s, nm, l);
if (t + 1 > oend) { error_code = 0x2af9; goto free_both; }
const char *desc = hooks[k].desc;
t[0] = 1;
s = t + 3;
if (s > oend) { error_code = 0x2af9; goto free_both; }
l = (unsigned short)libc_strlen(desc);
*(unsigned short *)(t + 1) = __builtin_bswap16(l);
unsigned char *u = s + l;
if (u > oend) { error_code = 0x2af9; goto free_both; }
libc_memcpy_alt(s, desc, l);
if (u + 1 > oend) { error_code = 0x2af9; goto free_both; }
u[0] = 0xc; // NameAndType {c, c + 1}
if (u + 3 > oend) { error_code = 0x2af9; goto free_both; }
*(unsigned short *)(u + 1) = __builtin_bswap16((unsigned short)c);
if (u + 5 > oend) { error_code = 0x2af9; goto free_both; }
u[3] = (unsigned char)((c + 1) >> 8);
u[4] = (unsigned char)(c + 1);
hooks[k].ref = (unsigned short)(c + 3);
if (u + 6 > oend) { error_code = 0x2af9; goto free_both; }
u[5] = 0xa; // Methodref {clsIdx, c + 2}
if (u + 8 > oend) { error_code = 0x2af9; goto free_both; }
u[6] = (unsigned char)(clsIdx >> 8);
u[7] = (unsigned char)clsIdx;
w = u + 10;
if (w > oend) { error_code = 0x2af9; goto free_both; }
u[8] = (unsigned char)((c + 2) >> 8);
u[9] = (unsigned char)(c + 2);
if (++k == (unsigned)nh) break;
c += 4;
if (w + 1 > oend) { error_code = 0x2af9; goto free_both; }
}
out.p = w;
}
CP cp;
cp.e = ents;
cp.n = n1;
cp.end = cursor;
// access, this, super, interfaces
const unsigned char *h = cursor;
error_code = 0x2af8;
if (h + 6 > end) goto free_cp;
in.p = h + 6;
if (h + 8 > end) goto free_cp;
{
unsigned long long ic = (unsigned long long)(h[6] << 9) + (unsigned long long)(h[7] * 2);
const unsigned char *ie = h + 8 + ic;
in.p = ie > end ? h + 8 : ie;
if (ie > end) goto free_cp;
unsigned char *we = out.p + ic + 8;
error_code = 0x2af9;
if (we > oend) goto free_cp;
libc_memcpy_alt(out.p, h, ic + 8);
out.p = we;
}
{
int e2 = copy_methods_table_constant_pool(&in, &out, 0, 0, 0); // fields
if (e2) { error_code = e2; goto free_cp; }
e2 = copy_methods_table_constant_pool(&in, &out, &cp, pick, pctx); // methods
if (e2) { error_code = e2; goto free_cp; }
}
{
const unsigned char *rest = in.p;
unsigned rn = (unsigned)(end - rest);
unsigned char *fe = out.p + rn;
if (fe > oend) {
error_code = 0x2af9;
*outBuf = nullptr;
*outLen = 0;
} else {
error_code = 0;
if (rest) libc_memcpy_alt(out.p, rest, rn);
*outBuf = buf;
*outLen = (int)(fe - buf);
}
}
free_cp:
libc_free(cp.e);
if (!error_code) return 0;
goto free_buf;
}
free_both:
libc_free(ents);
free_buf:
libc_free(buf);
return error_code;
}
// ---- gap-scan functions of the class transformer ----
// size of what rewrite_class_file appends to the constant pool: the class entries plus, per hook, a Utf8 name, a Utf8 descriptor,
// a NameAndType and a Methodref
extern "C" int gap_scan_functions_class_transformer(const char *clsName, MHook *hooks, int nh) {
int n = (int)libc_strlen(clsName) + 6;
for (int i = 0; i < nh; i++) n += (int)libc_strlen(hooks[i].name) + (int)libc_strlen(hooks[i].desc) + 0x10;
return n;
}
+176
View File
@@ -0,0 +1,176 @@
// 0x21000: class-hook installer (JVMTI) and the table of native hook bodies
// Defines SlinkyHooks13 in the game's class loader, registers its natives and rewrites the nine hooked game classes
// through the ClassFileLoadHook (class_file_load_hook, see classfile_hooks.cpp). The tables of what gets rewritten are data
// (hook_tables.inc, generated from the image by tools/gen_hooks.py): the original assembled them on its stack.
#include "slinky/engine.h"
#include "slinky/jnienv.h"
#include "slinky/jids.h"
FN(void *, libc_memcpy_alt, (void *dest, const void *src, unsigned long long n));
extern "C" void slinky_logf(const char *fmt, ...);
static inline const char *cs(const void *p) { return p ? (const char *)p : "?"; }
extern "C" char hook_native_pointer_table[];
// Copies the 23 native pointers to hook_native_pointer_table (the original installer read them back from there; this
// implementation takes them from kNatives, so nothing calls this any more)
extern "C" void *copies_23_native_pointers_data(const void *natives) { return libc_memcpy_alt(hook_native_pointer_table, natives, 0xb8); }
// ---- run-time layouts shared with the ClassFileLoadHook code (classfile_hooks.cpp, classfile_patch_helpers.cpp) ----
struct MHook { const char *name, *desc; unsigned short ref; }; // a native of SlinkyHooks13; ref = its Methodref
struct HookEnt { const char *owner, *name, *desc; long long kind; unsigned short *ref; }; // instruction to replace
struct MethodPat { const char *name, *desc; HookEnt *ents; unsigned long long n; }; // method to rewrite (-1, -1 = all)
struct HookRec { jclass cls; MHook *natives; unsigned long long nnat; MethodPat *pats; unsigned long long npats; };
// ---- compile-time description of the hooks ----
enum { INVOKE_VIRTUAL = 1, INVOKE_STATIC = 2, GETFIELD = 3, GETSTATIC = 4, PUTFIELD = 5, PUTSTATIC = 6 };
#define ANY nullptr
struct NativeRef { const char *name, *desc; };
struct Site { const char *owner, *name, *desc; int kind; int native; }; // `native` indexes the class's NativeRef list
struct Method { const char *name, *desc; const Site *sites; int nsites; };
struct HookedClass { jclass JIds::*cls; const NativeRef *natives; int nnatives; const Method *methods; int nmethods; };
#include "hook_tables.inc"
// ---- JVMTI (function table indices) ----
struct JvmtiTable { void *fn[160]; };
typedef int (*SetEventMode)(JvmtiTable **, int mode, int event, void *thread);
typedef int (*AddCaps)(JvmtiTable **, const void *caps);
typedef int (*SetCallbacks)(JvmtiTable **, const void *callbacks, int size);
typedef int (*Retransform)(JvmtiTable **, int count, jclass *classes);
#define JVMTI(env, idx, T) ((T)(*(env))->fn[idx])
enum { JVMTI_DISABLE = 0, JVMTI_ENABLE = 1, EV_CLASS_FILE_LOAD_HOOK = 0x36 };
enum { SET_EVENT_NOTIFICATION_MODE = 1, SET_EVENT_CALLBACKS = 121, ADD_CAPABILITIES = 141, RETRANSFORM_CLASSES = 151 };
enum { JVMTI_ERROR_BASE = 12000 }; // client error code = 12000 + jvmtiError
extern "C" unsigned char classfile_hooks_1_while_hooks_are_installed; // 1 while the hooks are installed
extern "C" unsigned char classfile_hooks_world_client_pending; // WorldClient retransform still to do (Java 9+)
extern "C" jobject classfile_hooks_global_ref_transformer_class; // global ref of SlinkyHooks13
extern "C" int classfile_hooks_first_error; // first error seen by the ClassFileLoadHook (sticky)
extern "C" HookRec hooked_class_minecraft[9]; // the hooked classes, read by the ClassFileLoadHook
extern "C" const char str_slinkyhooks13[]; // "SlinkyHooks13"
extern "C" const char ptr_to_hook_installer_class_file_slinkyhooks13[]; // jvmtiCapabilities: can_retransform_classes
extern "C" const char ptr_to_class_file_load_hook[]; // jvmtiEventCallbacks (0x138 bytes): ClassFileLoadHook = class_file_load_hook
extern "C" const jbyte hook_installer_class_file_slinkyhooks13[]; // class file of SlinkyHooks13
extern "C" const int hook_installer_its_length; // its length
FN(int, define_class_with_natives, (JNIEnv *env, const char *name, jobject loader, const jbyte *buf, jsize len, const JNINativeMethod *methods, jint n,
jclass *out));
// static storage for the records of the chosen mapping (the ClassFileLoadHook reads them for as long as the hooks
// are installed)
static MHook g_natives[9][6];
static MethodPat g_pats[9][3];
static HookEnt g_ents[32];
static unsigned long long g_nents;
// The original installer assembled parts of this table at run time from the JIds method name /
// signature slots (RendererLivingEntity/RenderPlayer doRender + canRenderName): tools/gen_hooks.py
// could not recover those values statically, because dump_hooks.py had filled every JIds pointer
// with a fake 0x10000000+offset when it decoded the table - the generated table therefore carries
// "BAD@<ids offset>" placeholders. Resolve them against the live JIds exactly like the original.
static const char *hook_string(JIds *ids, const char *s) {
if (!s || s[0] != 'B' || s[1] != 'A' || s[2] != 'D' || s[3] != '@') return s;
unsigned long long off = 0;
for (const char *p = s + 4; *p; p++)
off = off * 16 + (unsigned long long)(*p <= '9' ? *p - '0' : (*p | 0x20) - 'a' + 10);
// The generator wrote the dumper's fake pointer base into the placeholder: "BAD@10000968" is
// JIds+0x968, not JIds+0x10000968. Using the raw value read 256 MB past the table, which crashed
// wherever that address was unmapped (vanilla 1.8.9: access violation in this function) and
// silently produced garbage pointers where it happened to be mapped.
if (off >= 0x10000000) off -= 0x10000000;
if (off + sizeof(const char *) > sizeof(JIds)) return 0; // malformed placeholder
return *(const char **)((char *)ids + off);
}
static void build_records(JIds *ids, const HookedClass *tab) {
g_nents = 0;
for (int c = 0; c < 9; c++) {
const HookedClass &hc = tab[c];
HookRec &rec = hooked_class_minecraft[c];
for (int i = 0; i < hc.nnatives; i++) g_natives[c][i] = MHook{hook_string(ids, hc.natives[i].name), hook_string(ids, hc.natives[i].desc), 0};
for (int m = 0; m < hc.nmethods; m++) {
const Method &meth = hc.methods[m];
HookEnt *first = &g_ents[g_nents];
for (int s = 0; s < meth.nsites; s++) {
const Site &site = meth.sites[s];
g_ents[g_nents++] = HookEnt{hook_string(ids, site.owner), hook_string(ids, site.name), hook_string(ids, site.desc), site.kind, &g_natives[c][site.native].ref};
}
const char *any = (const char *)-1;
g_pats[c][m] = MethodPat{meth.name ? hook_string(ids, meth.name) : any, meth.desc ? hook_string(ids, meth.desc) : any, first,
(unsigned long long)meth.nsites};
}
rec = HookRec{ids->*hc.cls, g_natives[c], (unsigned long long)hc.nnatives, g_pats[c], (unsigned long long)hc.nmethods};
}
}
static bool java9_or_newer(JNIEnv *env) {
if (!env) return false;
return ((unsigned)(*env)->GetVersion(env) >> 16) >= 9;
}
// Install the hooks: 0 or an error (JNI/loader errors as the client defines them, 12000 + jvmtiError)
extern "C" int install_class_hooks(Jvm *jvm, JvmtiTable **jvmti) {
if (!jvm || !jvm->ids) {
slinky_logf("classhooks: install called with bad jvm=%p ids=%p", jvm, jvm ? jvm->ids : 0);
return 0x7d0;
}
slinky_logf("classhooks: install jvm=%p ids=%p env=%p jvmti=%p installed=%d", jvm, jvm->ids, jvm->env, jvmti, (int)classfile_hooks_1_while_hooks_are_installed);
if (classfile_hooks_1_while_hooks_are_installed) return 0;
JNIEnv *env = jvm->env;
JIds *ids = jvm->ids;
// Build the per-class hook records before the ClassFileLoadHook is enabled: the callback matches a
// load against these entries, and an entry that is still zero (or a class that failed to resolve)
// would match JVMTI's class_being_redefined == NULL for a first-time load and rewrite every class.
build_records(ids, kHooks[ids->variant][ids->names]);
JNINativeMethod natives[23];
for (int i = 0; i < 23; i++) natives[i] = JNINativeMethod{(char *)kNatives[i].name, (char *)kNatives[i].desc, kNatives[i].fn};
jclass cls;
int err = define_class_with_natives(env, str_slinkyhooks13, (jobject)((char *)ids + 0x10 /* game loader + loadClass */), hook_installer_class_file_slinkyhooks13,
hook_installer_its_length, natives, 23, &cls);
if (err) return err;
classfile_hooks_global_ref_transformer_class = (*env)->NewGlobalRef(env, cls);
if ((err = JVMTI(jvmti, ADD_CAPABILITIES, AddCaps)(jvmti, ptr_to_hook_installer_class_file_slinkyhooks13))) return JVMTI_ERROR_BASE + err;
char callbacks[0x138];
__builtin_memcpy(callbacks, ptr_to_class_file_load_hook, sizeof callbacks);
if ((err = JVMTI(jvmti, SET_EVENT_CALLBACKS, SetCallbacks)(jvmti, callbacks, sizeof callbacks)))
return JVMTI_ERROR_BASE + err;
if ((err = JVMTI(jvmti, SET_EVENT_NOTIFICATION_MODE, SetEventMode)(jvmti, JVMTI_ENABLE, EV_CLASS_FILE_LOAD_HOOK, 0)))
return JVMTI_ERROR_BASE + err;
slinky_logf("classhooks: variant=%d names=%d canRenderName=%s%s doRender=%s%s playerDoRender=%s%s",
(int)ids->variant, (int)ids->names,
cs(ids->RendererLivingEntity_canRenderNameMethodName), cs(ids->RendererLivingEntity_canRenderNameMethodSignature),
cs(ids->RendererLivingEntity_doRenderMethodName), cs(ids->RendererLivingEntity_doRenderMethodSignature),
cs(ids->RenderArrow_doRenderMethodName), cs(ids->RenderArrow_doRenderMethodSignature));
for (unsigned long long k = 0; k < g_nents; k++)
slinky_logf("ent[%llu] owner=%s kind=%d name=%p desc=%p", k, g_ents[k].owner, (int)g_ents[k].kind, (void *)g_ents[k].name, (void *)g_ents[k].desc);
__atomic_exchange_n(&classfile_hooks_first_error, 0, __ATOMIC_SEQ_CST);
// Retransform one class at a time: classes the game has not loaded yet make the
// all-at-once call fail, and they will be patched later by the still-enabled
// ClassFileLoadHook when the game loads them (RenderPlayer/WorldClient on world join).
// Java 9+ class transformers (Lunar/Badlion) can deadlock inside
// RetransformClasses(WorldClient) while their transformer is active. The
// world hooks are the only ones affected; skip that one retransform so
// the installer completes and the client stays responsive. The
// ClassFileLoadHook remains enabled for classes loaded later.
const bool java9plus = java9_or_newer(env);
if (java9plus)
slinky_logf("classhooks: Java 9+ detected; skipping WorldClient retransform");
for (int i = 0; i < 9; i++) {
jclass one = hooked_class_minecraft[i].cls;
slinky_logf("classhooks: retransform[%d]=%p", i, one);
if (!one) continue;
if (java9plus && i == 6) {
slinky_logf("classhooks: retransform[%d] skipped on Java 9+ (deferred)", i);
__atomic_store_n(&classfile_hooks_world_client_pending, 1, __ATOMIC_SEQ_CST);
continue;
}
err = JVMTI(jvmti, RETRANSFORM_CLASSES, Retransform)(jvmti, 1, &one);
slinky_logf("classhooks: retransform[%d] err=%d first_error=%d", i, err, classfile_hooks_first_error);
if (err) __sync_val_compare_and_swap(&classfile_hooks_first_error, 0, JVMTI_ERROR_BASE + err);
}
if (classfile_hooks_first_error) __atomic_store_n(&classfile_hooks_world_client_pending, 0, __ATOMIC_SEQ_CST);
__atomic_exchange_n(&classfile_hooks_1_while_hooks_are_installed, 1, __ATOMIC_SEQ_CST);
// Keep the ClassFileLoadHook enabled: classes that were not loaded yet (RenderPlayer,
// WorldClient, ...) are only retransformed when the game loads them after joining a world.
slinky_logf("classhooks: installed, hook stays enabled, first_error=%d", classfile_hooks_first_error);
return classfile_hooks_first_error;
}
+688
View File
@@ -0,0 +1,688 @@
// generated by tools/gen_hooks.py from the image's class-hook installer - do not edit
// Which instructions of the game are rewritten into calls of SlinkyHooks13 natives, per Minecraft mapping.
extern "C" void hook_get_mouse_over(), hook_is_display_active_prerender(), hook_update_display(), hook_post_world_clear_1_7(), hook_post_world_clear_1_8(), hook_click_mouse(), hook_right_click_mouse(), hook_orient_camera(), hook_update_active_render_info(), hook_set_render_x(), hook_set_render_y(), hook_update_entities(), hook_can_render_name(), hook_render_player(), hook_unload_chunk(), hook_set_block_1_7(), hook_set_block_1_8(), hook_invalidate_chunk(), hook_should_clip_ledge(), hook_move_flying(), hook_send_packet_1_7(), hook_send_packet_1_8(), hook_update_controller(); // bodies: base/e_7.cpp, e_8.cpp, protected/hooks.cpp
static const struct { const char *name, *desc; void *fn; } kNatives[23] = {
{"getMouseOver", "(Ljava/lang/Object;F)V", (void *)hook_get_mouse_over},
{"isDisplayActivePreRender", "()Z", (void *)hook_is_display_active_prerender},
{"updateDisplay", "()V", (void *)hook_update_display},
{"postWorldClear_1_7", "(I)V", (void *)hook_post_world_clear_1_7},
{"postWorldClear_1_8", "(I)V", (void *)hook_post_world_clear_1_8},
{"clickMouse", "(Ljava/lang/Object;)V", (void *)hook_click_mouse},
{"rightClickMouse", "(Ljava/lang/Object;)V", (void *)hook_right_click_mouse},
{"orientCamera", "(Ljava/lang/Object;F)V", (void *)hook_orient_camera},
{"updateActiveRenderInfo", "(Ljava/lang/Object;Z)V", (void *)hook_update_active_render_info},
{"setRenderX", "(Ljava/lang/Object;F)V", (void *)hook_set_render_x},
{"setRenderY", "(Ljava/lang/Object;F)V", (void *)hook_set_render_y},
{"updateEntities", "(Ljava/lang/Object;)V", (void *)hook_update_entities},
{"canRenderName", "(Ljava/lang/Object;Ljava/lang/Object;)Z", (void *)hook_can_render_name},
{"renderPlayer", "(Ljava/lang/Object;Ljava/lang/Object;DDDFF)V", (void *)hook_render_player},
{"unloadChunk", "(Ljava/lang/Object;II)V", (void *)hook_unload_chunk},
{"setBlock_1_7", "(Ljava/lang/Object;IIILjava/lang/Object;II)Z", (void *)hook_set_block_1_7},
{"setBlock_1_8", "(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;I)Z", (void *)hook_set_block_1_8},
{"invalidateChunk", "(Ljava/lang/Object;IIIIII)V", (void *)hook_invalidate_chunk},
{"shouldClipLedge", "(Ljava/lang/Object;)Z", (void *)hook_should_clip_ledge},
{"moveFlying", "(Ljava/lang/Object;FFF)V", (void *)hook_move_flying},
{"sendPacket_1_7", "(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V", (void *)hook_send_packet_1_7},
{"sendPacket_1_8", "(Ljava/lang/Object;Ljava/lang/Object;)V", (void *)hook_send_packet_1_8},
{"updateController", "(Ljava/lang/Object;)V", (void *)hook_update_controller},
};
static const NativeRef natives_Minecraft[] = {
{"getMouseOver", "(Ljava/lang/Object;F)V"},
{"updateDisplay", "()V"},
{"clickMouse", "(Ljava/lang/Object;)V"},
{"rightClickMouse", "(Ljava/lang/Object;)V"},
{"updateEntities", "(Ljava/lang/Object;)V"},
{"updateController", "(Ljava/lang/Object;)V"},
};
static const NativeRef natives_EntityRenderer[] = {
{"postWorldClear_1_7", "(I)V"},
{"postWorldClear_1_8", "(I)V"},
{"orientCamera", "(Ljava/lang/Object;F)V"},
{"updateActiveRenderInfo", "(Ljava/lang/Object;Z)V"},
{"isDisplayActivePreRender", "()Z"},
};
static const NativeRef natives_RenderManager[] = {
{"setRenderX", "(Ljava/lang/Object;F)V"},
{"setRenderY", "(Ljava/lang/Object;F)V"},
};
static const NativeRef natives_RendererLivingEntity[] = {
{"canRenderName", "(Ljava/lang/Object;Ljava/lang/Object;)Z"},
};
static const NativeRef natives_RenderPlayer[] = {
{"renderPlayer", "(Ljava/lang/Object;Ljava/lang/Object;DDDFF)V"},
};
static const NativeRef natives_NetHandlerPlayClient[] = {
{"invalidateChunk", "(Ljava/lang/Object;IIIIII)V"},
{"sendPacket_1_7", "(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V"},
{"sendPacket_1_8", "(Ljava/lang/Object;Ljava/lang/Object;)V"},
};
static const NativeRef natives_WorldClient[] = {
{"unloadChunk", "(Ljava/lang/Object;II)V"},
{"setBlock_1_7", "(Ljava/lang/Object;IIILjava/lang/Object;II)Z"},
{"setBlock_1_8", "(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;I)Z"},
};
static const NativeRef natives_Entity[] = {
{"shouldClipLedge", "(Ljava/lang/Object;)Z"},
};
static const NativeRef natives_EntityLivingBase[] = {
{"moveFlying", "(Ljava/lang/Object;FFF)V"},
};
// ---- Minecraft 1.7.10, obfuscated names ----
static const Site v0_0_Minecraft_m0[] = { // in Minecraft.p()V
{"blt", "a", "(F)V", INVOKE_VIRTUAL, 0}, // -> getMouseOver
{"bao", "al", "()V", INVOKE_VIRTUAL, 2}, // -> clickMouse
{"bao", "am", "()V", INVOKE_VIRTUAL, 3}, // -> rightClickMouse
{"bjf", "h", "()V", INVOKE_VIRTUAL, 4}, // -> updateEntities
{"bje", "e", "()V", INVOKE_VIRTUAL, 5}, // -> updateController
};
static const Site v0_0_Minecraft_m1[] = { // in Minecraft.runInputTick()V
{"bao", "al", "()V", INVOKE_VIRTUAL, 2}, // -> clickMouse
{"bao", "am", "()V", INVOKE_VIRTUAL, 3}, // -> rightClickMouse
};
static const Site v0_0_Minecraft_m2[] = { // in Minecraft.g()V
{"org/lwjgl/opengl/Display", "update", "()V", INVOKE_STATIC, 1}, // -> updateDisplay
};
static const Method v0_0_Minecraft[] = {
{"p", "()V", v0_0_Minecraft_m0, 5},
{"runInputTick", "()V", v0_0_Minecraft_m1, 2},
{"g", "()V", v0_0_Minecraft_m2, 1},
};
static const Site v0_0_EntityRenderer_m0[] = { // in EntityRenderer.a(FJ)V
{"org/lwjgl/opengl/GL11", "glClear", "(I)V", INVOKE_STATIC, 0}, // -> postWorldClear_1_7
{"baj", "a", "(Lyz;Z)V", INVOKE_STATIC, 3}, // -> updateActiveRenderInfo
};
static const Site v0_0_EntityRenderer_m1[] = { // in EntityRenderer.a(FI)V
{"blt", "h", "(F)V", INVOKE_VIRTUAL, 2}, // -> orientCamera
};
static const Site v0_0_EntityRenderer_m2[] = { // in EntityRenderer.b(F)V
{"org/lwjgl/opengl/Display", "isActive", "()Z", INVOKE_STATIC, 4}, // -> isDisplayActivePreRender
};
static const Method v0_0_EntityRenderer[] = {
{"a", "(FJ)V", v0_0_EntityRenderer_m0, 2},
{"a", "(FI)V", v0_0_EntityRenderer_m1, 1},
{"b", "(F)V", v0_0_EntityRenderer_m2, 1},
};
static const Site v0_0_RenderManager_m0[] = { // in RenderManager.a(Lahb;Lbqf;Lbbu;Lsv;Lsa;Lbbj;F)V
{"bnn", "k", "F", PUTFIELD, 0}, // -> setRenderX
{"bnn", "j", "F", PUTFIELD, 1}, // -> setRenderY
};
static const Method v0_0_RenderManager[] = {
{"a", "(Lahb;Lbqf;Lbbu;Lsv;Lsa;Lbbj;F)V", v0_0_RenderManager_m0, 2},
};
static const Site v0_0_RendererLivingEntity_m0[] = { // in RendererLivingEntity.*
{"boh", "BAD@10000968", "BAD@10000970", INVOKE_VIRTUAL, 0}, // -> canRenderName
};
static const Method v0_0_RendererLivingEntity[] = {
{ANY, ANY, v0_0_RendererLivingEntity_m0, 1},
};
static const Site v0_0_RenderPlayer_m0[] = { // in RenderPlayer.BAD@10000a90BAD@10000a98
{"boh", "BAD@10000980", "BAD@10000988", INVOKE_VIRTUAL, 0}, // -> renderPlayer
};
static const Method v0_0_RenderPlayer[] = {
{"BAD@10000a90", "BAD@10000a98", v0_0_RenderPlayer_m0, 1},
};
static const Site v0_0_NetHandlerPlayClient_m0[] = { // in NetHandlerPlayClient.a(Lgx;)V
{"bjf", "c", "(IIIIII)V", INVOKE_VIRTUAL, 0}, // -> invalidateChunk
};
static const Site v0_0_NetHandlerPlayClient_m1[] = { // in NetHandlerPlayClient.a(Lgz;)V
{"bjf", "c", "(IIIIII)V", INVOKE_VIRTUAL, 0}, // -> invalidateChunk
};
static const Site v0_0_NetHandlerPlayClient_m2[] = { // in NetHandlerPlayClient.a(Lft;)V
{"ej", "a", "(Lft;[Lio/netty/util/concurrent/GenericFutureListener;)V", INVOKE_VIRTUAL, 1}, // -> sendPacket_1_7
};
static const Method v0_0_NetHandlerPlayClient[] = {
{"a", "(Lgx;)V", v0_0_NetHandlerPlayClient_m0, 1},
{"a", "(Lgz;)V", v0_0_NetHandlerPlayClient_m1, 1},
{"a", "(Lft;)V", v0_0_NetHandlerPlayClient_m2, 1},
};
static const Site v0_0_WorldClient_m0[] = { // in WorldClient.c(IIILaji;I)Z
{"ahb", "d", "(IIILaji;II)Z", INVOKE_VIRTUAL, 1}, // -> setBlock_1_7
};
static const Site v0_0_WorldClient_m1[] = { // in WorldClient.a(IIZ)V
{"bjd", "b", "(II)V", INVOKE_VIRTUAL, 0}, // -> unloadChunk
};
static const Method v0_0_WorldClient[] = {
{"c", "(IIILaji;I)Z", v0_0_WorldClient_m0, 1},
{"a", "(IIZ)V", v0_0_WorldClient_m1, 1},
};
static const Site v0_0_Entity_m0[] = { // in Entity.d(DDD)V
{"sa", "an", "()Z", INVOKE_VIRTUAL, 0}, // -> shouldClipLedge
};
static const Method v0_0_Entity[] = {
{"d", "(DDD)V", v0_0_Entity_m0, 1},
};
static const Site v0_0_EntityLivingBase_m0[] = { // in EntityLivingBase.e(FF)V
{"sv", "a", "(FFF)V", INVOKE_VIRTUAL, 0}, // -> moveFlying
};
static const Method v0_0_EntityLivingBase[] = {
{"e", "(FF)V", v0_0_EntityLivingBase_m0, 1},
};
static const HookedClass v0_0[9] = {
{&JIds::Minecraft, natives_Minecraft, 6, v0_0_Minecraft, 3},
{&JIds::EntityRenderer, natives_EntityRenderer, 5, v0_0_EntityRenderer, 3},
{&JIds::RenderManager, natives_RenderManager, 2, v0_0_RenderManager, 1},
{&JIds::RendererLivingEntity, natives_RendererLivingEntity, 1, v0_0_RendererLivingEntity, 1},
{&JIds::RenderPlayer, natives_RenderPlayer, 1, v0_0_RenderPlayer, 1},
{&JIds::NetHandlerPlayClient, natives_NetHandlerPlayClient, 3, v0_0_NetHandlerPlayClient, 3},
{&JIds::WorldClient, natives_WorldClient, 3, v0_0_WorldClient, 2},
{&JIds::Entity, natives_Entity, 1, v0_0_Entity, 1},
{&JIds::EntityLivingBase, natives_EntityLivingBase, 1, v0_0_EntityLivingBase, 1},
};
// ---- Minecraft 1.7.10, MCP names ----
static const Site v0_1_Minecraft_m0[] = { // in Minecraft.runTick()V
{"net/minecraft/client/renderer/EntityRenderer", "getMouseOver", "(F)V", INVOKE_VIRTUAL, 0}, // -> getMouseOver
{"net/minecraft/client/Minecraft", "clickMouse", "()V", INVOKE_VIRTUAL, 2}, // -> clickMouse
{"net/minecraft/client/Minecraft", "rightClickMouse", "()V", INVOKE_VIRTUAL, 3}, // -> rightClickMouse
{"net/minecraft/client/multiplayer/WorldClient", "updateEntities", "()V", INVOKE_VIRTUAL, 4}, // -> updateEntities
{"net/minecraft/client/multiplayer/PlayerControllerMP", "updateController", "()V", INVOKE_VIRTUAL, 5}, // -> updateController
};
static const Site v0_1_Minecraft_m1[] = { // in Minecraft.runInputTick()V
{"net/minecraft/client/Minecraft", "clickMouse", "()V", INVOKE_VIRTUAL, 2}, // -> clickMouse
{"net/minecraft/client/Minecraft", "rightClickMouse", "()V", INVOKE_VIRTUAL, 3}, // -> rightClickMouse
};
static const Site v0_1_Minecraft_m2[] = { // in Minecraft.resetSize()V
{"org/lwjgl/opengl/Display", "update", "()V", INVOKE_STATIC, 1}, // -> updateDisplay
};
static const Method v0_1_Minecraft[] = {
{"runTick", "()V", v0_1_Minecraft_m0, 5},
{"runInputTick", "()V", v0_1_Minecraft_m1, 2},
{"resetSize", "()V", v0_1_Minecraft_m2, 1},
};
static const Site v0_1_EntityRenderer_m0[] = { // in EntityRenderer.renderWorld(FJ)V
{"org/lwjgl/opengl/GL11", "glClear", "(I)V", INVOKE_STATIC, 0}, // -> postWorldClear_1_7
{"net/minecraft/client/renderer/ActiveRenderInfo", "updateRenderInfo", "(Lnet/minecraft/entity/player/EntityPlayer;Z)V", INVOKE_STATIC, 3}, // -> updateActiveRenderInfo
};
static const Site v0_1_EntityRenderer_m1[] = { // in EntityRenderer.setupCameraTransform(FI)V
{"net/minecraft/client/renderer/EntityRenderer", "orientCamera", "(F)V", INVOKE_VIRTUAL, 2}, // -> orientCamera
};
static const Site v0_1_EntityRenderer_m2[] = { // in EntityRenderer.updateCameraAndRender(F)V
{"org/lwjgl/opengl/Display", "isActive", "()Z", INVOKE_STATIC, 4}, // -> isDisplayActivePreRender
};
static const Method v0_1_EntityRenderer[] = {
{"renderWorld", "(FJ)V", v0_1_EntityRenderer_m0, 2},
{"setupCameraTransform", "(FI)V", v0_1_EntityRenderer_m1, 1},
{"updateCameraAndRender", "(F)V", v0_1_EntityRenderer_m2, 1},
};
static const Site v0_1_RenderManager_m0[] = { // in RenderManager.cacheActiveRenderInfo(Lnet/minecraft/world/World;Lnet/minecraft/client/renderer/texture/TextureManager;Lnet/minecraft/client/gui/FontRenderer;Lnet/minecraft/entity/EntityLivingBase;Lnet/minecraft/entity/Entity;Lnet/minecr
{"net/minecraft/client/renderer/entity/RenderManager", "playerViewX", "F", PUTFIELD, 0}, // -> setRenderX
{"net/minecraft/client/renderer/entity/RenderManager", "playerViewY", "F", PUTFIELD, 1}, // -> setRenderY
};
static const Method v0_1_RenderManager[] = {
{"cacheActiveRenderInfo", "(Lnet/minecraft/world/World;Lnet/minecraft/client/renderer/texture/TextureManager;Lnet/minecraft/client/gui/FontRenderer;Lnet/minecraft/entity/EntityLivingBase;Lnet/minecraft/entity/Entity;Lnet/minecr", v0_1_RenderManager_m0, 2},
};
static const Site v0_1_RendererLivingEntity_m0[] = { // in RendererLivingEntity.*
{"net/minecraft/client/renderer/entity/RendererLivingEntity", "BAD@10000968", "BAD@10000970", INVOKE_VIRTUAL, 0}, // -> canRenderName
};
static const Method v0_1_RendererLivingEntity[] = {
{ANY, ANY, v0_1_RendererLivingEntity_m0, 1},
};
static const Site v0_1_RenderPlayer_m0[] = { // in RenderPlayer.BAD@10000a90BAD@10000a98
{"net/minecraft/client/renderer/entity/RendererLivingEntity", "BAD@10000980", "BAD@10000988", INVOKE_VIRTUAL, 0}, // -> renderPlayer
};
static const Method v0_1_RenderPlayer[] = {
{"BAD@10000a90", "BAD@10000a98", v0_1_RenderPlayer_m0, 1},
};
static const Site v0_1_NetHandlerPlayClient_m0[] = { // in NetHandlerPlayClient.handleChunkData(Lnet/minecraft/network/play/server/S21PacketChunkData;)V
{"net/minecraft/client/multiplayer/WorldClient", "markBlockRangeForRenderUpdate", "(IIIIII)V", INVOKE_VIRTUAL, 0}, // -> invalidateChunk
};
static const Site v0_1_NetHandlerPlayClient_m1[] = { // in NetHandlerPlayClient.handleMapChunkBulk(Lnet/minecraft/network/play/server/S26PacketMapChunkBulk;)V
{"net/minecraft/client/multiplayer/WorldClient", "markBlockRangeForRenderUpdate", "(IIIIII)V", INVOKE_VIRTUAL, 0}, // -> invalidateChunk
};
static const Site v0_1_NetHandlerPlayClient_m2[] = { // in NetHandlerPlayClient.addToSendQueue(Lnet/minecraft/network/Packet;)V
{"net/minecraft/network/NetworkManager", "scheduleOutboundPacket", "(Lnet/minecraft/network/Packet;[Lio/netty/util/concurrent/GenericFutureListener;)V", INVOKE_VIRTUAL, 1}, // -> sendPacket_1_7
};
static const Method v0_1_NetHandlerPlayClient[] = {
{"handleChunkData", "(Lnet/minecraft/network/play/server/S21PacketChunkData;)V", v0_1_NetHandlerPlayClient_m0, 1},
{"handleMapChunkBulk", "(Lnet/minecraft/network/play/server/S26PacketMapChunkBulk;)V", v0_1_NetHandlerPlayClient_m1, 1},
{"addToSendQueue", "(Lnet/minecraft/network/Packet;)V", v0_1_NetHandlerPlayClient_m2, 1},
};
static const Site v0_1_WorldClient_m0[] = { // in WorldClient.func_147492_c(IIILnet/minecraft/block/Block;I)Z
{"net/minecraft/world/World", "setBlock", "(IIILnet/minecraft/block/Block;II)Z", INVOKE_VIRTUAL, 1}, // -> setBlock_1_7
};
static const Site v0_1_WorldClient_m1[] = { // in WorldClient.doPreChunk(IIZ)V
{"net/minecraft/client/multiplayer/ChunkProviderClient", "unloadChunk", "(II)V", INVOKE_VIRTUAL, 0}, // -> unloadChunk
};
static const Method v0_1_WorldClient[] = {
{"func_147492_c", "(IIILnet/minecraft/block/Block;I)Z", v0_1_WorldClient_m0, 1},
{"doPreChunk", "(IIZ)V", v0_1_WorldClient_m1, 1},
};
static const Site v0_1_Entity_m0[] = { // in Entity.moveEntity(DDD)V
{"net/minecraft/entity/Entity", "isSneaking", "()Z", INVOKE_VIRTUAL, 0}, // -> shouldClipLedge
};
static const Method v0_1_Entity[] = {
{"moveEntity", "(DDD)V", v0_1_Entity_m0, 1},
};
static const Site v0_1_EntityLivingBase_m0[] = { // in EntityLivingBase.moveEntityWithHeading(FF)V
{"net/minecraft/entity/EntityLivingBase", "moveFlying", "(FFF)V", INVOKE_VIRTUAL, 0}, // -> moveFlying
};
static const Method v0_1_EntityLivingBase[] = {
{"moveEntityWithHeading", "(FF)V", v0_1_EntityLivingBase_m0, 1},
};
static const HookedClass v0_1[9] = {
{&JIds::Minecraft, natives_Minecraft, 6, v0_1_Minecraft, 3},
{&JIds::EntityRenderer, natives_EntityRenderer, 5, v0_1_EntityRenderer, 3},
{&JIds::RenderManager, natives_RenderManager, 2, v0_1_RenderManager, 1},
{&JIds::RendererLivingEntity, natives_RendererLivingEntity, 1, v0_1_RendererLivingEntity, 1},
{&JIds::RenderPlayer, natives_RenderPlayer, 1, v0_1_RenderPlayer, 1},
{&JIds::NetHandlerPlayClient, natives_NetHandlerPlayClient, 3, v0_1_NetHandlerPlayClient, 3},
{&JIds::WorldClient, natives_WorldClient, 3, v0_1_WorldClient, 2},
{&JIds::Entity, natives_Entity, 1, v0_1_Entity, 1},
{&JIds::EntityLivingBase, natives_EntityLivingBase, 1, v0_1_EntityLivingBase, 1},
};
// ---- Minecraft 1.7.10, SRG names ----
static const Site v0_2_Minecraft_m0[] = { // in Minecraft.func_71407_l()V
{"net/minecraft/client/renderer/EntityRenderer", "func_78473_a", "(F)V", INVOKE_VIRTUAL, 0}, // -> getMouseOver
{"net/minecraft/client/Minecraft", "func_147116_af", "()V", INVOKE_VIRTUAL, 2}, // -> clickMouse
{"net/minecraft/client/Minecraft", "func_147121_ag", "()V", INVOKE_VIRTUAL, 3}, // -> rightClickMouse
{"net/minecraft/client/multiplayer/WorldClient", "func_72939_s", "()V", INVOKE_VIRTUAL, 4}, // -> updateEntities
{"net/minecraft/client/multiplayer/PlayerControllerMP", "func_78765_e", "()V", INVOKE_VIRTUAL, 5}, // -> updateController
};
static const Site v0_2_Minecraft_m1[] = { // in Minecraft.runInputTick()V
{"net/minecraft/client/Minecraft", "func_147116_af", "()V", INVOKE_VIRTUAL, 2}, // -> clickMouse
{"net/minecraft/client/Minecraft", "func_147121_ag", "()V", INVOKE_VIRTUAL, 3}, // -> rightClickMouse
};
static const Site v0_2_Minecraft_m2[] = { // in Minecraft.func_147120_f()V
{"org/lwjgl/opengl/Display", "update", "()V", INVOKE_STATIC, 1}, // -> updateDisplay
};
static const Method v0_2_Minecraft[] = {
{"func_71407_l", "()V", v0_2_Minecraft_m0, 5},
{"runInputTick", "()V", v0_2_Minecraft_m1, 2},
{"func_147120_f", "()V", v0_2_Minecraft_m2, 1},
};
static const Site v0_2_EntityRenderer_m0[] = { // in EntityRenderer.func_78471_a(FJ)V
{"org/lwjgl/opengl/GL11", "glClear", "(I)V", INVOKE_STATIC, 0}, // -> postWorldClear_1_7
{"net/minecraft/client/renderer/ActiveRenderInfo", "func_74583_a", "(Lnet/minecraft/entity/player/EntityPlayer;Z)V", INVOKE_STATIC, 3}, // -> updateActiveRenderInfo
};
static const Site v0_2_EntityRenderer_m1[] = { // in EntityRenderer.func_78479_a(FI)V
{"net/minecraft/client/renderer/EntityRenderer", "func_78467_g", "(F)V", INVOKE_VIRTUAL, 2}, // -> orientCamera
};
static const Site v0_2_EntityRenderer_m2[] = { // in EntityRenderer.func_78480_b(F)V
{"org/lwjgl/opengl/Display", "isActive", "()Z", INVOKE_STATIC, 4}, // -> isDisplayActivePreRender
};
static const Method v0_2_EntityRenderer[] = {
{"func_78471_a", "(FJ)V", v0_2_EntityRenderer_m0, 2},
{"func_78479_a", "(FI)V", v0_2_EntityRenderer_m1, 1},
{"func_78480_b", "(F)V", v0_2_EntityRenderer_m2, 1},
};
static const Site v0_2_RenderManager_m0[] = { // in RenderManager.func_147938_a(Lnet/minecraft/world/World;Lnet/minecraft/client/renderer/texture/TextureManager;Lnet/minecraft/client/gui/FontRenderer;Lnet/minecraft/entity/EntityLivingBase;Lnet/minecraft/entity/Entity;Lnet/minecr
{"net/minecraft/client/renderer/entity/RenderManager", "field_78732_j", "F", PUTFIELD, 0}, // -> setRenderX
{"net/minecraft/client/renderer/entity/RenderManager", "field_78735_i", "F", PUTFIELD, 1}, // -> setRenderY
};
static const Method v0_2_RenderManager[] = {
{"func_147938_a", "(Lnet/minecraft/world/World;Lnet/minecraft/client/renderer/texture/TextureManager;Lnet/minecraft/client/gui/FontRenderer;Lnet/minecraft/entity/EntityLivingBase;Lnet/minecraft/entity/Entity;Lnet/minecr", v0_2_RenderManager_m0, 2},
};
static const Site v0_2_RendererLivingEntity_m0[] = { // in RendererLivingEntity.*
{"net/minecraft/client/renderer/entity/RendererLivingEntity", "BAD@10000968", "BAD@10000970", INVOKE_VIRTUAL, 0}, // -> canRenderName
};
static const Method v0_2_RendererLivingEntity[] = {
{ANY, ANY, v0_2_RendererLivingEntity_m0, 1},
};
static const Site v0_2_RenderPlayer_m0[] = { // in RenderPlayer.BAD@10000a90BAD@10000a98
{"net/minecraft/client/renderer/entity/RendererLivingEntity", "BAD@10000980", "BAD@10000988", INVOKE_VIRTUAL, 0}, // -> renderPlayer
};
static const Method v0_2_RenderPlayer[] = {
{"BAD@10000a90", "BAD@10000a98", v0_2_RenderPlayer_m0, 1},
};
static const Site v0_2_NetHandlerPlayClient_m0[] = { // in NetHandlerPlayClient.func_147263_a(Lnet/minecraft/network/play/server/S21PacketChunkData;)V
{"net/minecraft/client/multiplayer/WorldClient", "func_147458_c", "(IIIIII)V", INVOKE_VIRTUAL, 0}, // -> invalidateChunk
};
static const Site v0_2_NetHandlerPlayClient_m1[] = { // in NetHandlerPlayClient.func_147269_a(Lnet/minecraft/network/play/server/S26PacketMapChunkBulk;)V
{"net/minecraft/client/multiplayer/WorldClient", "func_147458_c", "(IIIIII)V", INVOKE_VIRTUAL, 0}, // -> invalidateChunk
};
static const Site v0_2_NetHandlerPlayClient_m2[] = { // in NetHandlerPlayClient.func_147297_a(Lnet/minecraft/network/Packet;)V
{"net/minecraft/network/NetworkManager", "func_150725_a", "(Lnet/minecraft/network/Packet;[Lio/netty/util/concurrent/GenericFutureListener;)V", INVOKE_VIRTUAL, 1}, // -> sendPacket_1_7
};
static const Method v0_2_NetHandlerPlayClient[] = {
{"func_147263_a", "(Lnet/minecraft/network/play/server/S21PacketChunkData;)V", v0_2_NetHandlerPlayClient_m0, 1},
{"func_147269_a", "(Lnet/minecraft/network/play/server/S26PacketMapChunkBulk;)V", v0_2_NetHandlerPlayClient_m1, 1},
{"func_147297_a", "(Lnet/minecraft/network/Packet;)V", v0_2_NetHandlerPlayClient_m2, 1},
};
static const Site v0_2_WorldClient_m0[] = { // in WorldClient.func_147492_c(IIILnet/minecraft/block/Block;I)Z
{"net/minecraft/world/World", "func_147465_d", "(IIILnet/minecraft/block/Block;II)Z", INVOKE_VIRTUAL, 1}, // -> setBlock_1_7
};
static const Site v0_2_WorldClient_m1[] = { // in WorldClient.func_73025_a(IIZ)V
{"net/minecraft/client/multiplayer/ChunkProviderClient", "func_73234_b", "(II)V", INVOKE_VIRTUAL, 0}, // -> unloadChunk
};
static const Method v0_2_WorldClient[] = {
{"func_147492_c", "(IIILnet/minecraft/block/Block;I)Z", v0_2_WorldClient_m0, 1},
{"func_73025_a", "(IIZ)V", v0_2_WorldClient_m1, 1},
};
static const Site v0_2_Entity_m0[] = { // in Entity.func_70091_d(DDD)V
{"net/minecraft/entity/Entity", "func_70093_af", "()Z", INVOKE_VIRTUAL, 0}, // -> shouldClipLedge
};
static const Method v0_2_Entity[] = {
{"func_70091_d", "(DDD)V", v0_2_Entity_m0, 1},
};
static const Site v0_2_EntityLivingBase_m0[] = { // in EntityLivingBase.func_70612_e(FF)V
{"net/minecraft/entity/EntityLivingBase", "func_70060_a", "(FFF)V", INVOKE_VIRTUAL, 0}, // -> moveFlying
};
static const Method v0_2_EntityLivingBase[] = {
{"func_70612_e", "(FF)V", v0_2_EntityLivingBase_m0, 1},
};
static const HookedClass v0_2[9] = {
{&JIds::Minecraft, natives_Minecraft, 6, v0_2_Minecraft, 3},
{&JIds::EntityRenderer, natives_EntityRenderer, 5, v0_2_EntityRenderer, 3},
{&JIds::RenderManager, natives_RenderManager, 2, v0_2_RenderManager, 1},
{&JIds::RendererLivingEntity, natives_RendererLivingEntity, 1, v0_2_RendererLivingEntity, 1},
{&JIds::RenderPlayer, natives_RenderPlayer, 1, v0_2_RenderPlayer, 1},
{&JIds::NetHandlerPlayClient, natives_NetHandlerPlayClient, 3, v0_2_NetHandlerPlayClient, 3},
{&JIds::WorldClient, natives_WorldClient, 3, v0_2_WorldClient, 2},
{&JIds::Entity, natives_Entity, 1, v0_2_Entity, 1},
{&JIds::EntityLivingBase, natives_EntityLivingBase, 1, v0_2_EntityLivingBase, 1},
};
// ---- Minecraft 1.8.9, obfuscated names ----
static const Site v1_0_Minecraft_m0[] = { // in Minecraft.s()V
{"bfk", "a", "(F)V", INVOKE_VIRTUAL, 0}, // -> getMouseOver
{"ave", "aw", "()V", INVOKE_VIRTUAL, 2}, // -> clickMouse
{"ave", "ax", "()V", INVOKE_VIRTUAL, 3}, // -> rightClickMouse
{"bdb", "i", "()V", INVOKE_VIRTUAL, 4}, // -> updateEntities
{"bda", "e", "()V", INVOKE_VIRTUAL, 5}, // -> updateController
};
static const Site v1_0_Minecraft_m1[] = { // in Minecraft.runInputTick()V
{"ave", "aw", "()V", INVOKE_VIRTUAL, 2}, // -> clickMouse
{"ave", "ax", "()V", INVOKE_VIRTUAL, 3}, // -> rightClickMouse
};
static const Site v1_0_Minecraft_m2[] = { // in Minecraft.h()V
{"org/lwjgl/opengl/Display", "update", "()V", INVOKE_STATIC, 1}, // -> updateDisplay
};
static const Method v1_0_Minecraft[] = {
{"s", "()V", v1_0_Minecraft_m0, 5},
{"runInputTick", "()V", v1_0_Minecraft_m1, 2},
{"h", "()V", v1_0_Minecraft_m2, 1},
};
static const Site v1_0_EntityRenderer_m0[] = { // in EntityRenderer.a(IFJ)V
{"bfl", "m", "(I)V", INVOKE_STATIC, 1}, // -> postWorldClear_1_8
{"auz", "a", "(Lwn;Z)V", INVOKE_STATIC, 3}, // -> updateActiveRenderInfo
};
static const Site v1_0_EntityRenderer_m1[] = { // in EntityRenderer.a(FI)V
{"bfk", "f", "(F)V", INVOKE_VIRTUAL, 2}, // -> orientCamera
};
static const Site v1_0_EntityRenderer_m2[] = { // in EntityRenderer.a(FJ)V
{"org/lwjgl/opengl/Display", "isActive", "()Z", INVOKE_STATIC, 4}, // -> isDisplayActivePreRender
};
static const Method v1_0_EntityRenderer[] = {
{"a", "(IFJ)V", v1_0_EntityRenderer_m0, 2},
{"a", "(FI)V", v1_0_EntityRenderer_m1, 1},
{"a", "(FJ)V", v1_0_EntityRenderer_m2, 1},
};
static const Site v1_0_RenderManager_m0[] = { // in RenderManager.a(Ladm;Lavn;Lpk;Lpk;Lavh;F)V
{"biu", "f", "F", PUTFIELD, 0}, // -> setRenderX
{"biu", "e", "F", PUTFIELD, 1}, // -> setRenderY
};
static const Method v1_0_RenderManager[] = {
{"a", "(Ladm;Lavn;Lpk;Lpk;Lavh;F)V", v1_0_RenderManager_m0, 2},
};
static const Site v1_0_RendererLivingEntity_m0[] = { // in RendererLivingEntity.*
{"bjl", "BAD@10000968", "BAD@10000970", INVOKE_VIRTUAL, 0}, // -> canRenderName
};
static const Method v1_0_RendererLivingEntity[] = {
{ANY, ANY, v1_0_RendererLivingEntity_m0, 1},
};
static const Site v1_0_RenderPlayer_m0[] = { // in RenderPlayer.BAD@10000a90BAD@10000a98
{"bjl", "BAD@10000980", "BAD@10000988", INVOKE_VIRTUAL, 0}, // -> renderPlayer
};
static const Method v1_0_RenderPlayer[] = {
{"BAD@10000a90", "BAD@10000a98", v1_0_RenderPlayer_m0, 1},
};
static const Site v1_0_NetHandlerPlayClient_m0[] = { // in NetHandlerPlayClient.a(Lgo;)V
{"bdb", "b", "(IIIIII)V", INVOKE_VIRTUAL, 0}, // -> invalidateChunk
};
static const Site v1_0_NetHandlerPlayClient_m1[] = { // in NetHandlerPlayClient.a(Lgp;)V
{"bdb", "b", "(IIIIII)V", INVOKE_VIRTUAL, 0}, // -> invalidateChunk
};
static const Site v1_0_NetHandlerPlayClient_m2[] = { // in NetHandlerPlayClient.a(Lff;)V
{"ek", "a", "(Lff;)V", INVOKE_VIRTUAL, 2}, // -> sendPacket_1_8
};
static const Method v1_0_NetHandlerPlayClient[] = {
{"a", "(Lgo;)V", v1_0_NetHandlerPlayClient_m0, 1},
{"a", "(Lgp;)V", v1_0_NetHandlerPlayClient_m1, 1},
{"a", "(Lff;)V", v1_0_NetHandlerPlayClient_m2, 1},
};
static const Site v1_0_WorldClient_m0[] = { // in WorldClient.b(Lcj;Lalz;)Z
{"adm", "a", "(Lcj;Lalz;I)Z", INVOKE_VIRTUAL, 2}, // -> setBlock_1_8
};
static const Site v1_0_WorldClient_m1[] = { // in WorldClient.b(IIZ)V
{"bcz", "b", "(II)V", INVOKE_VIRTUAL, 0}, // -> unloadChunk
};
static const Method v1_0_WorldClient[] = {
{"b", "(Lcj;Lalz;)Z", v1_0_WorldClient_m0, 1},
{"b", "(IIZ)V", v1_0_WorldClient_m1, 1},
};
static const Site v1_0_Entity_m0[] = { // in Entity.d(DDD)V
{"pk", "av", "()Z", INVOKE_VIRTUAL, 0}, // -> shouldClipLedge
};
static const Method v1_0_Entity[] = {
{"d", "(DDD)V", v1_0_Entity_m0, 1},
};
static const Site v1_0_EntityLivingBase_m0[] = { // in EntityLivingBase.g(FF)V
{"pr", "a", "(FFF)V", INVOKE_VIRTUAL, 0}, // -> moveFlying
};
static const Method v1_0_EntityLivingBase[] = {
{"g", "(FF)V", v1_0_EntityLivingBase_m0, 1},
};
static const HookedClass v1_0[9] = {
{&JIds::Minecraft, natives_Minecraft, 6, v1_0_Minecraft, 3},
{&JIds::EntityRenderer, natives_EntityRenderer, 5, v1_0_EntityRenderer, 3},
{&JIds::RenderManager, natives_RenderManager, 2, v1_0_RenderManager, 1},
{&JIds::RendererLivingEntity, natives_RendererLivingEntity, 1, v1_0_RendererLivingEntity, 1},
{&JIds::RenderPlayer, natives_RenderPlayer, 1, v1_0_RenderPlayer, 1},
{&JIds::NetHandlerPlayClient, natives_NetHandlerPlayClient, 3, v1_0_NetHandlerPlayClient, 3},
{&JIds::WorldClient, natives_WorldClient, 3, v1_0_WorldClient, 2},
{&JIds::Entity, natives_Entity, 1, v1_0_Entity, 1},
{&JIds::EntityLivingBase, natives_EntityLivingBase, 1, v1_0_EntityLivingBase, 1},
};
// ---- Minecraft 1.8.9, MCP names ----
static const Site v1_1_Minecraft_m0[] = { // in Minecraft.runTick()V
{"net/minecraft/client/renderer/EntityRenderer", "getMouseOver", "(F)V", INVOKE_VIRTUAL, 0}, // -> getMouseOver
{"net/minecraft/client/Minecraft", "clickMouse", "()V", INVOKE_VIRTUAL, 2}, // -> clickMouse
{"net/minecraft/client/Minecraft", "rightClickMouse", "()V", INVOKE_VIRTUAL, 3}, // -> rightClickMouse
{"net/minecraft/client/multiplayer/WorldClient", "updateEntities", "()V", INVOKE_VIRTUAL, 4}, // -> updateEntities
{"net/minecraft/client/multiplayer/PlayerControllerMP", "updateController", "()V", INVOKE_VIRTUAL, 5}, // -> updateController
};
static const Site v1_1_Minecraft_m1[] = { // in Minecraft.runInputTick()V
{"net/minecraft/client/Minecraft", "clickMouse", "()V", INVOKE_VIRTUAL, 2}, // -> clickMouse
{"net/minecraft/client/Minecraft", "rightClickMouse", "()V", INVOKE_VIRTUAL, 3}, // -> rightClickMouse
};
static const Site v1_1_Minecraft_m2[] = { // in Minecraft.updateDisplay()V
{"org/lwjgl/opengl/Display", "update", "()V", INVOKE_STATIC, 1}, // -> updateDisplay
};
static const Method v1_1_Minecraft[] = {
{"runTick", "()V", v1_1_Minecraft_m0, 5},
{"runInputTick", "()V", v1_1_Minecraft_m1, 2},
{"updateDisplay", "()V", v1_1_Minecraft_m2, 1},
};
static const Site v1_1_EntityRenderer_m0[] = { // in EntityRenderer.renderWorldPass(IFJ)V
{"net/minecraft/client/renderer/GlStateManager", "clear", "(I)V", INVOKE_STATIC, 1}, // -> postWorldClear_1_8
{"net/minecraft/client/renderer/ActiveRenderInfo", "updateRenderInfo", "(Lnet/minecraft/entity/player/EntityPlayer;Z)V", INVOKE_STATIC, 3}, // -> updateActiveRenderInfo
};
static const Site v1_1_EntityRenderer_m1[] = { // in EntityRenderer.setupCameraTransform(FI)V
{"net/minecraft/client/renderer/EntityRenderer", "orientCamera", "(F)V", INVOKE_VIRTUAL, 2}, // -> orientCamera
};
static const Site v1_1_EntityRenderer_m2[] = { // in EntityRenderer.updateCameraAndRender(FJ)V
{"org/lwjgl/opengl/Display", "isActive", "()Z", INVOKE_STATIC, 4}, // -> isDisplayActivePreRender
};
static const Method v1_1_EntityRenderer[] = {
{"renderWorldPass", "(IFJ)V", v1_1_EntityRenderer_m0, 2},
{"setupCameraTransform", "(FI)V", v1_1_EntityRenderer_m1, 1},
{"updateCameraAndRender", "(FJ)V", v1_1_EntityRenderer_m2, 1},
};
static const Site v1_1_RenderManager_m0[] = { // in RenderManager.cacheActiveRenderInfo(Lnet/minecraft/world/World;Lnet/minecraft/client/gui/FontRenderer;Lnet/minecraft/entity/Entity;Lnet/minecraft/entity/Entity;Lnet/minecraft/client/settings/GameSettings;F)V
{"net/minecraft/client/renderer/entity/RenderManager", "playerViewX", "F", PUTFIELD, 0}, // -> setRenderX
{"net/minecraft/client/renderer/entity/RenderManager", "playerViewY", "F", PUTFIELD, 1}, // -> setRenderY
};
static const Method v1_1_RenderManager[] = {
{"cacheActiveRenderInfo", "(Lnet/minecraft/world/World;Lnet/minecraft/client/gui/FontRenderer;Lnet/minecraft/entity/Entity;Lnet/minecraft/entity/Entity;Lnet/minecraft/client/settings/GameSettings;F)V", v1_1_RenderManager_m0, 2},
};
static const Site v1_1_RendererLivingEntity_m0[] = { // in RendererLivingEntity.*
{"net/minecraft/client/renderer/entity/RendererLivingEntity", "BAD@10000968", "BAD@10000970", INVOKE_VIRTUAL, 0}, // -> canRenderName
};
static const Method v1_1_RendererLivingEntity[] = {
{ANY, ANY, v1_1_RendererLivingEntity_m0, 1},
};
static const Site v1_1_RenderPlayer_m0[] = { // in RenderPlayer.BAD@10000a90BAD@10000a98
{"net/minecraft/client/renderer/entity/RendererLivingEntity", "BAD@10000980", "BAD@10000988", INVOKE_VIRTUAL, 0}, // -> renderPlayer
};
static const Method v1_1_RenderPlayer[] = {
{"BAD@10000a90", "BAD@10000a98", v1_1_RenderPlayer_m0, 1},
};
static const Site v1_1_NetHandlerPlayClient_m0[] = { // in NetHandlerPlayClient.handleChunkData(Lnet/minecraft/network/play/server/S21PacketChunkData;)V
{"net/minecraft/client/multiplayer/WorldClient", "markBlockRangeForRenderUpdate", "(IIIIII)V", INVOKE_VIRTUAL, 0}, // -> invalidateChunk
};
static const Site v1_1_NetHandlerPlayClient_m1[] = { // in NetHandlerPlayClient.handleMapChunkBulk(Lnet/minecraft/network/play/server/S26PacketMapChunkBulk;)V
{"net/minecraft/client/multiplayer/WorldClient", "markBlockRangeForRenderUpdate", "(IIIIII)V", INVOKE_VIRTUAL, 0}, // -> invalidateChunk
};
static const Site v1_1_NetHandlerPlayClient_m2[] = { // in NetHandlerPlayClient.addToSendQueue(Lnet/minecraft/network/Packet;)V
{"net/minecraft/network/NetworkManager", "sendPacket", "(Lnet/minecraft/network/Packet;)V", INVOKE_VIRTUAL, 2}, // -> sendPacket_1_8
};
static const Method v1_1_NetHandlerPlayClient[] = {
{"handleChunkData", "(Lnet/minecraft/network/play/server/S21PacketChunkData;)V", v1_1_NetHandlerPlayClient_m0, 1},
{"handleMapChunkBulk", "(Lnet/minecraft/network/play/server/S26PacketMapChunkBulk;)V", v1_1_NetHandlerPlayClient_m1, 1},
{"addToSendQueue", "(Lnet/minecraft/network/Packet;)V", v1_1_NetHandlerPlayClient_m2, 1},
};
static const Site v1_1_WorldClient_m0[] = { // in WorldClient.invalidateRegionAndSetBlock(Lnet/minecraft/util/BlockPos;Lnet/minecraft/block/state/IBlockState;)Z
{"net/minecraft/world/World", "setBlockState", "(Lnet/minecraft/util/BlockPos;Lnet/minecraft/block/state/IBlockState;I)Z", INVOKE_VIRTUAL, 2}, // -> setBlock_1_8
};
static const Site v1_1_WorldClient_m1[] = { // in WorldClient.doPreChunk(IIZ)V
{"net/minecraft/client/multiplayer/ChunkProviderClient", "unloadChunk", "(II)V", INVOKE_VIRTUAL, 0}, // -> unloadChunk
};
static const Method v1_1_WorldClient[] = {
{"invalidateRegionAndSetBlock", "(Lnet/minecraft/util/BlockPos;Lnet/minecraft/block/state/IBlockState;)Z", v1_1_WorldClient_m0, 1},
{"doPreChunk", "(IIZ)V", v1_1_WorldClient_m1, 1},
};
static const Site v1_1_Entity_m0[] = { // in Entity.moveEntity(DDD)V
{"net/minecraft/entity/Entity", "isSneaking", "()Z", INVOKE_VIRTUAL, 0}, // -> shouldClipLedge
};
static const Method v1_1_Entity[] = {
{"moveEntity", "(DDD)V", v1_1_Entity_m0, 1},
};
static const Site v1_1_EntityLivingBase_m0[] = { // in EntityLivingBase.moveEntityWithHeading(FF)V
{"net/minecraft/entity/EntityLivingBase", "moveFlying", "(FFF)V", INVOKE_VIRTUAL, 0}, // -> moveFlying
};
static const Method v1_1_EntityLivingBase[] = {
{"moveEntityWithHeading", "(FF)V", v1_1_EntityLivingBase_m0, 1},
};
static const HookedClass v1_1[9] = {
{&JIds::Minecraft, natives_Minecraft, 6, v1_1_Minecraft, 3},
{&JIds::EntityRenderer, natives_EntityRenderer, 5, v1_1_EntityRenderer, 3},
{&JIds::RenderManager, natives_RenderManager, 2, v1_1_RenderManager, 1},
{&JIds::RendererLivingEntity, natives_RendererLivingEntity, 1, v1_1_RendererLivingEntity, 1},
{&JIds::RenderPlayer, natives_RenderPlayer, 1, v1_1_RenderPlayer, 1},
{&JIds::NetHandlerPlayClient, natives_NetHandlerPlayClient, 3, v1_1_NetHandlerPlayClient, 3},
{&JIds::WorldClient, natives_WorldClient, 3, v1_1_WorldClient, 2},
{&JIds::Entity, natives_Entity, 1, v1_1_Entity, 1},
{&JIds::EntityLivingBase, natives_EntityLivingBase, 1, v1_1_EntityLivingBase, 1},
};
// ---- Minecraft 1.8.9, SRG names ----
static const Site v1_2_Minecraft_m0[] = { // in Minecraft.func_71407_l()V
{"net/minecraft/client/renderer/EntityRenderer", "func_78473_a", "(F)V", INVOKE_VIRTUAL, 0}, // -> getMouseOver
{"net/minecraft/client/Minecraft", "func_147116_af", "()V", INVOKE_VIRTUAL, 2}, // -> clickMouse
{"net/minecraft/client/Minecraft", "func_147121_ag", "()V", INVOKE_VIRTUAL, 3}, // -> rightClickMouse
{"net/minecraft/client/multiplayer/WorldClient", "func_72939_s", "()V", INVOKE_VIRTUAL, 4}, // -> updateEntities
{"net/minecraft/client/multiplayer/PlayerControllerMP", "func_78765_e", "()V", INVOKE_VIRTUAL, 5}, // -> updateController
};
static const Site v1_2_Minecraft_m1[] = { // in Minecraft.runInputTick()V
{"net/minecraft/client/Minecraft", "func_147116_af", "()V", INVOKE_VIRTUAL, 2}, // -> clickMouse
{"net/minecraft/client/Minecraft", "func_147121_ag", "()V", INVOKE_VIRTUAL, 3}, // -> rightClickMouse
};
static const Site v1_2_Minecraft_m2[] = { // in Minecraft.func_175601_h()V
{"org/lwjgl/opengl/Display", "update", "()V", INVOKE_STATIC, 1}, // -> updateDisplay
};
static const Method v1_2_Minecraft[] = {
{"func_71407_l", "()V", v1_2_Minecraft_m0, 5},
{"runInputTick", "()V", v1_2_Minecraft_m1, 2},
{"func_175601_h", "()V", v1_2_Minecraft_m2, 1},
};
static const Site v1_2_EntityRenderer_m0[] = { // in EntityRenderer.func_175068_a(IFJ)V
{"net/minecraft/client/renderer/GlStateManager", "func_179086_m", "(I)V", INVOKE_STATIC, 1}, // -> postWorldClear_1_8
{"net/minecraft/client/renderer/ActiveRenderInfo", "func_74583_a", "(Lnet/minecraft/entity/player/EntityPlayer;Z)V", INVOKE_STATIC, 3}, // -> updateActiveRenderInfo
};
static const Site v1_2_EntityRenderer_m1[] = { // in EntityRenderer.func_78479_a(FI)V
{"net/minecraft/client/renderer/EntityRenderer", "func_78467_g", "(F)V", INVOKE_VIRTUAL, 2}, // -> orientCamera
};
static const Site v1_2_EntityRenderer_m2[] = { // in EntityRenderer.func_181560_a(FJ)V
{"org/lwjgl/opengl/Display", "isActive", "()Z", INVOKE_STATIC, 4}, // -> isDisplayActivePreRender
};
static const Method v1_2_EntityRenderer[] = {
{"func_175068_a", "(IFJ)V", v1_2_EntityRenderer_m0, 2},
{"func_78479_a", "(FI)V", v1_2_EntityRenderer_m1, 1},
{"func_181560_a", "(FJ)V", v1_2_EntityRenderer_m2, 1},
};
static const Site v1_2_RenderManager_m0[] = { // in RenderManager.func_180597_a(Lnet/minecraft/world/World;Lnet/minecraft/client/gui/FontRenderer;Lnet/minecraft/entity/Entity;Lnet/minecraft/entity/Entity;Lnet/minecraft/client/settings/GameSettings;F)V
{"net/minecraft/client/renderer/entity/RenderManager", "field_78732_j", "F", PUTFIELD, 0}, // -> setRenderX
{"net/minecraft/client/renderer/entity/RenderManager", "field_78735_i", "F", PUTFIELD, 1}, // -> setRenderY
};
static const Method v1_2_RenderManager[] = {
{"func_180597_a", "(Lnet/minecraft/world/World;Lnet/minecraft/client/gui/FontRenderer;Lnet/minecraft/entity/Entity;Lnet/minecraft/entity/Entity;Lnet/minecraft/client/settings/GameSettings;F)V", v1_2_RenderManager_m0, 2},
};
static const Site v1_2_RendererLivingEntity_m0[] = { // in RendererLivingEntity.*
{"net/minecraft/client/renderer/entity/RendererLivingEntity", "BAD@10000968", "BAD@10000970", INVOKE_VIRTUAL, 0}, // -> canRenderName
};
static const Method v1_2_RendererLivingEntity[] = {
{ANY, ANY, v1_2_RendererLivingEntity_m0, 1},
};
static const Site v1_2_RenderPlayer_m0[] = { // in RenderPlayer.BAD@10000a90BAD@10000a98
{"net/minecraft/client/renderer/entity/RendererLivingEntity", "BAD@10000980", "BAD@10000988", INVOKE_VIRTUAL, 0}, // -> renderPlayer
};
static const Method v1_2_RenderPlayer[] = {
{"BAD@10000a90", "BAD@10000a98", v1_2_RenderPlayer_m0, 1},
};
static const Site v1_2_NetHandlerPlayClient_m0[] = { // in NetHandlerPlayClient.func_147263_a(Lnet/minecraft/network/play/server/S21PacketChunkData;)V
{"net/minecraft/client/multiplayer/WorldClient", "func_147458_c", "(IIIIII)V", INVOKE_VIRTUAL, 0}, // -> invalidateChunk
};
static const Site v1_2_NetHandlerPlayClient_m1[] = { // in NetHandlerPlayClient.func_147269_a(Lnet/minecraft/network/play/server/S26PacketMapChunkBulk;)V
{"net/minecraft/client/multiplayer/WorldClient", "func_147458_c", "(IIIIII)V", INVOKE_VIRTUAL, 0}, // -> invalidateChunk
};
static const Site v1_2_NetHandlerPlayClient_m2[] = { // in NetHandlerPlayClient.func_147297_a(Lnet/minecraft/network/Packet;)V
{"net/minecraft/network/NetworkManager", "func_179290_a", "(Lnet/minecraft/network/Packet;)V", INVOKE_VIRTUAL, 2}, // -> sendPacket_1_8
};
static const Method v1_2_NetHandlerPlayClient[] = {
{"func_147263_a", "(Lnet/minecraft/network/play/server/S21PacketChunkData;)V", v1_2_NetHandlerPlayClient_m0, 1},
{"func_147269_a", "(Lnet/minecraft/network/play/server/S26PacketMapChunkBulk;)V", v1_2_NetHandlerPlayClient_m1, 1},
{"func_147297_a", "(Lnet/minecraft/network/Packet;)V", v1_2_NetHandlerPlayClient_m2, 1},
};
static const Site v1_2_WorldClient_m0[] = { // in WorldClient.func_180503_b(Lnet/minecraft/util/BlockPos;Lnet/minecraft/block/state/IBlockState;)Z
{"net/minecraft/world/World", "func_180501_a", "(Lnet/minecraft/util/BlockPos;Lnet/minecraft/block/state/IBlockState;I)Z", INVOKE_VIRTUAL, 2}, // -> setBlock_1_8
};
static const Site v1_2_WorldClient_m1[] = { // in WorldClient.func_73025_a(IIZ)V
{"net/minecraft/client/multiplayer/ChunkProviderClient", "func_73234_b", "(II)V", INVOKE_VIRTUAL, 0}, // -> unloadChunk
};
static const Method v1_2_WorldClient[] = {
{"func_180503_b", "(Lnet/minecraft/util/BlockPos;Lnet/minecraft/block/state/IBlockState;)Z", v1_2_WorldClient_m0, 1},
{"func_73025_a", "(IIZ)V", v1_2_WorldClient_m1, 1},
};
static const Site v1_2_Entity_m0[] = { // in Entity.func_70091_d(DDD)V
{"net/minecraft/entity/Entity", "func_70093_af", "()Z", INVOKE_VIRTUAL, 0}, // -> shouldClipLedge
};
static const Method v1_2_Entity[] = {
{"func_70091_d", "(DDD)V", v1_2_Entity_m0, 1},
};
static const Site v1_2_EntityLivingBase_m0[] = { // in EntityLivingBase.func_70612_e(FF)V
{"net/minecraft/entity/EntityLivingBase", "func_70060_a", "(FFF)V", INVOKE_VIRTUAL, 0}, // -> moveFlying
};
static const Method v1_2_EntityLivingBase[] = {
{"func_70612_e", "(FF)V", v1_2_EntityLivingBase_m0, 1},
};
static const HookedClass v1_2[9] = {
{&JIds::Minecraft, natives_Minecraft, 6, v1_2_Minecraft, 3},
{&JIds::EntityRenderer, natives_EntityRenderer, 5, v1_2_EntityRenderer, 3},
{&JIds::RenderManager, natives_RenderManager, 2, v1_2_RenderManager, 1},
{&JIds::RendererLivingEntity, natives_RendererLivingEntity, 1, v1_2_RendererLivingEntity, 1},
{&JIds::RenderPlayer, natives_RenderPlayer, 1, v1_2_RenderPlayer, 1},
{&JIds::NetHandlerPlayClient, natives_NetHandlerPlayClient, 3, v1_2_NetHandlerPlayClient, 3},
{&JIds::WorldClient, natives_WorldClient, 3, v1_2_WorldClient, 2},
{&JIds::Entity, natives_Entity, 1, v1_2_Entity, 1},
{&JIds::EntityLivingBase, natives_EntityLivingBase, 1, v1_2_EntityLivingBase, 1},
};
static const HookedClass *const kHooks[2][3] = {
{v0_0, v0_1, v0_2}, // 1.7.10: obfuscated, MCP, SRG
{v1_0, v1_1, v1_2}, // 1.8.9
};
+299
View File
@@ -0,0 +1,299 @@
// JNI: entity field accessors.
// cflags: /Ob1 /clang:-fno-slp-vectorize
#include "slinky/engine.h"
#include "slinky/jcall.h"
struct Rot { float yaw, pitch; };
struct Vec3 { double x, y, z; };
FN(Vec3, vec3_make, (double x, double y, double z));
extern "C" void java_set_entity_player_item_in_use_count(Jvm *jvm, void *entity_player, int value) {
JCALL(jvm, SetIntField, entity_player, ids->EntityPlayer_itemInUseCount, value);
}
extern "C" jobject java_call_entity_player_get_equipment_in_slot(Jvm *jvm, void *entity_player, int value) {
return JCALLN(jvm, CallObjectMethod, entity_player, ids->EntityPlayer_getEquipmentInSlot, value);
}
extern "C" jobject java_get_entity_player_inventory_container(Jvm *jvm, void *entity_player) {
return JCALL(jvm, GetObjectField, entity_player, ids->EntityPlayer_inventoryContainer);
}
extern "C" bool java_get_entity_player_sleeping(Jvm *jvm, void *entity_player) {
return JCALLN(jvm, GetBooleanField, entity_player, ids->EntityPlayer_sleeping) != 0;
}
extern "C" jfloat java_get_entity_player_speed_on_ground(Jvm *jvm, void *entity_player) {
return JCALL(jvm, GetFloatField, entity_player, ids->EntityPlayer_speedOnGround);
}
extern "C" void java_set_entity_player_speed_on_ground(Jvm *jvm, void *entity_player, float value) {
JCALL(jvm, SetFloatField, entity_player, ids->EntityPlayer_speedOnGround, value);
}
extern "C" jint java_get_entity_other_player_mp_other_player_mppos_rotation_increments(Jvm *jvm, void *entity_other_player_mp) {
return JCALL(jvm, GetIntField, entity_other_player_mp, ids->EntityOtherPlayerMP_otherPlayerMPPosRotationIncrements);
}
extern "C" jdouble java_get_entity_other_player_mp_other_player_mpyaw(Jvm *jvm, void *entity_other_player_mp) {
return JCALL(jvm, GetDoubleField, entity_other_player_mp, ids->EntityOtherPlayerMP_otherPlayerMPYaw);
}
extern "C" jobject get_block_state_id(Jvm *jvm, void *block) {
return JCALLN(jvm, CallStaticObjectMethod, ids->Block, ids->Block_getBlockFromItem, block);
}
extern "C" jint get_block_id(Jvm *jvm, void *block) {
return JCALLN(jvm, CallStaticIntMethod, ids->Block, ids->Block_getIdFromBlock, block);
}
extern "C" jobject java_get_tile_entity_block_type(Jvm *jvm, void *tile_entity) {
return JCALL(jvm, GetObjectField, tile_entity, ids->TileEntity_blockType);
}
extern "C" jobject java_get_world_player_entities(Jvm *jvm, void *world) {
return JCALL(jvm, GetObjectField, world, ids->World_playerEntities);
}
extern "C" jobject java_get_world_loaded_entity_list(Jvm *jvm, void *world) {
return JCALL(jvm, GetObjectField, world, ids->World_loadedEntityList);
}
extern "C" jobject java_get_world_loaded_tile_entity_list(Jvm *jvm, void *world) {
return JCALL(jvm, GetObjectField, world, ids->World_loadedTileEntityList);
}
extern "C" jobject java_get_chunk_storage_arrays(Jvm *jvm, void *chunk) {
return JCALL(jvm, GetObjectField, chunk, ids->Chunk_storageArrays);
}
extern "C" jint java_get_chunk_x_position(Jvm *jvm, void *chunk) {
return JCALL(jvm, GetIntField, chunk, ids->Chunk_xPosition);
}
extern "C" jint java_get_chunk_z_position(Jvm *jvm, void *chunk) {
return JCALL(jvm, GetIntField, chunk, ids->Chunk_zPosition);
}
extern "C" jobject java_get_id_b20(Jvm *jvm, void *object) {
return JCALL(jvm, GetObjectField, object, ids->ExtendedBlockStorage_blockLSBArray);
}
extern "C" jobject java_get_chunk_provider_client(Jvm *jvm, void *chunk_provider_client) {
return JCALL(jvm, GetObjectField, chunk_provider_client, ids->ChunkProviderClient_chunkMapping);
}
extern "C" jobject java_get_moving_object_position_hit_vec(Jvm *jvm, void *moving_object_position) {
return JCALL(jvm, GetObjectField, moving_object_position, ids->MovingObjectPosition_hitVec);
}
extern "C" jobject java_get_moving_object_position_entity_hit(Jvm *jvm, void *moving_object_position) {
return JCALL(jvm, GetObjectField, moving_object_position, ids->MovingObjectPosition_entityHit);
}
extern "C" jobject java_call_list_get(Jvm *jvm, void *list, int value) {
return JCALLN(jvm, CallObjectMethod, list, ids->List_get, value);
}
extern "C" jobject java_call_map_get(Jvm *jvm, void *map, void *value) {
return JCALLN(jvm, CallObjectMethod, map, ids->Map_get, value);
}
extern "C" jfloat java_get_game_settings_mouse_sensitivity(Jvm *jvm, void *game_settings) {
return JCALL(jvm, GetFloatField, game_settings, ids->GameSettings_mouseSensitivity);
}
extern "C" jobject get_block_from_pos(Jvm *jvm, void *a1) {
return JCALLN(jvm, CallObjectMethod, a1, ids->_init__getBlock);
}
extern "C" void update_entities(Jvm *jvm, void *world) {
JCALLN(jvm, CallVoidMethod, world, ids->World_updateEntities);
}
extern "C" jint java_call_list_size(Jvm *jvm, void *list) {
return JCALLN(jvm, CallIntMethod, list, ids->List_size);
}
extern "C" jobject java_jni_is_instance_of_entity_other_player_mp(Jvm *jvm, jobject candidate) {
return JCALLN(jvm, IsInstanceOf, candidate, (jclass)ids->EntityOtherPlayerMP) ? candidate : nullptr;
}
extern "C" Vec3 java_get_entity_other_player_mp_other_player_mpx(Jvm *jvm, jobject entity_other_player_mp) {
double x = JCALLN(jvm, GetDoubleField, entity_other_player_mp, ids->EntityOtherPlayerMP_otherPlayerMPX);
double y = JCALLN(jvm, GetDoubleField, entity_other_player_mp, ids->EntityOtherPlayerMP_otherPlayerMPY);
double z = JCALLN(jvm, GetDoubleField, entity_other_player_mp, ids->EntityOtherPlayerMP_otherPlayerMPZ);
Vec3 r = vec3_make(x, y, z);
asm volatile("");
return r;
}
// ---- hand-written (BlockPos / Vec3i glue, multi-argument calls)
struct I3 { int x, y, z; };
FN(jobject, get_block_from_pos, (Jvm *j, void *block_pos));
extern "C" __attribute__((noinline)) jobject java_block_pos_init(Jvm *jvm, int x, int y, int z) {
return JNI(jvm)->NewObject(jvm->env, (jclass)jvm->ids->BlockPos, (jmethodID)jvm->ids->BlockPos__init_, x, y, z);
}
extern "C" Vec3 java_get_block_pos_a(Jvm *jvm, jobject block_pos) {
double x = JCALLN(jvm, GetIntField, block_pos, (jfieldID)ids->BlockPos_a);
double y = JCALLN(jvm, GetIntField, block_pos, (jfieldID)ids->BlockPos_c);
double z = JCALLN(jvm, GetIntField, block_pos, (jfieldID)ids->BlockPos_d);
Vec3 r = vec3_make(x, y, z);
asm volatile("");
return r;
}
extern "C" __attribute__((noinline)) void java_set_block_pos_a(Jvm *jvm, jobject block_pos, int value, int y, int z) {
JCALLN(jvm, SetIntField, block_pos, (jfieldID)ids->BlockPos_a, value);
JCALLN(jvm, SetIntField, block_pos, (jfieldID)ids->BlockPos_c, y);
JCALL(jvm, SetIntField, block_pos, (jfieldID)ids->BlockPos_d, z);
}
extern "C" I3 get_block_position(Jvm *jvm, jobject object) {
I3 r;
r.x = JCALLN(jvm, GetIntField, object, (jfieldID)ids->BlockPos_a);
r.y = JCALLN(jvm, GetIntField, object, (jfieldID)ids->BlockPos_c);
r.z = JCALLN(jvm, GetIntField, object, (jfieldID)ids->BlockPos_d);
return r;
}
// block position of an entity/object; the mapping set decides whether it goes through a BlockPos
extern "C" Vec3 java_get_block_pos_as_vec3(Jvm *jvm, jobject block_pos) {
int x;
long yo, zo;
if (jvm->ids->variant != 0) {
block_pos = JCALLN(jvm, GetObjectField, block_pos, (jfieldID)ids->TileEntity_pos);
x = JCALLN(jvm, GetIntField, block_pos, (jfieldID)ids->BlockPos_a);
zo = 0x468; yo = 0x460;
} else {
x = JCALLN(jvm, GetIntField, block_pos, (jfieldID)ids->TileEntity_xCoord);
zo = 0x780; yo = 0x778;
}
double dx = x;
double dy = JCALLN(jvm, GetIntField, block_pos, *(jfieldID *)((char *)ids + yo));
double dz = JCALLN(jvm, GetIntField, block_pos, *(jfieldID *)((char *)ids + zo));
Vec3 r = vec3_make(dx, dy, dz);
asm volatile("");
return r;
}
extern "C" jobject java_call_variant(Jvm *jvm, jobject object, int x, int y, int z) {
if (jvm->ids->variant != 0) {
jobject p = java_block_pos_init(jvm, x, y, z);
jobject r = JCALLN(jvm, CallObjectMethod, object, (jmethodID)ids->World_getBlockState, p);
return get_block_from_pos(jvm, r);
}
return JNI(jvm)->CallObjectMethod(jvm->env, object, (jmethodID)jvm->ids->World_getBlock, x, y, z);
}
extern "C" jobject java_call_world_get_block_state(Jvm *jvm, jobject object, jobject pos, int x, int y, int z) {
if (pos) {
java_set_block_pos_a(jvm, pos, x, y, z);
jobject r = JCALLN(jvm, CallObjectMethod, object, (jmethodID)ids->World_getBlockState, pos);
return get_block_from_pos(jvm, r);
}
return JNI(jvm)->CallObjectMethod(jvm->env, object, (jmethodID)jvm->ids->World_getBlock, x, y, z);
}
extern "C" jboolean set_block_state(Jvm *jvm, jobject object, int a, int b, int c, void *d, int e, int f) {
return JNI(jvm)->CallBooleanMethod(jvm->env, object, (jmethodID)jvm->ids->World_setBlock, a, b, c, d, e, f);
}
extern "C" jboolean set_block_at_pos(Jvm *jvm, jobject object, void *other_object, void *b, int c) {
return JNI(jvm)->CallBooleanMethod(jvm->env, object, (jmethodID)jvm->ids->World_setBlockState, other_object, b, c);
}
extern "C" jobject java_world_ray_trace_blocks(Jvm *jvm, jobject object, void *other_object, void *b, bool c, bool d, bool e) {
int c_ = c, d_ = d, e_ = e;
return JNI(jvm)->CallObjectMethod(jvm->env, object, (jmethodID)jvm->ids->World_rayTraceBlocks, other_object, b, c_, d_, e_);
}
extern "C" jobject java_world_get_chunk_from_chunk_coords(Jvm *jvm, jobject object, int a, int b) {
return JNI(jvm)->CallObjectMethod(jvm->env, object, (jmethodID)jvm->ids->World_getChunkFromChunkCoords, a, b);
}
extern "C" void update_chunk(Jvm *jvm, jobject object, int a, int b, int c, int d, int e, int f) {
JNI(jvm)->CallVoidMethod(jvm->env, object, (jmethodID)jvm->ids->World_markBlockRangeForRenderUpdate, a, b, c, d, e, f);
}
extern "C" jobject java_get_world_chunk_provider_client(Jvm *jvm, jobject chunk_provider_client) {
jobject x = JCALLN(jvm, GetObjectField, chunk_provider_client, (jfieldID)ids->World_chunkProvider);
return JCALLN(jvm, IsInstanceOf, x, (jclass)ids->ChunkProviderClient) ? x : nullptr;
}
extern "C" void set_block_meta(Jvm *jvm, jobject object, int a, int b) {
JNI(jvm)->CallVoidMethod(jvm->env, object, (jmethodID)jvm->ids->ChunkProviderClient_unloadChunk, a, b);
}
extern "C" jobject java_moving_object_position_init(Jvm *jvm, void *moving_object_position, void *other_object) {
return JNI(jvm)->NewObject(jvm->env, (jclass)jvm->ids->MovingObjectPosition, (jmethodID)jvm->ids->MovingObjectPosition__init_, moving_object_position, other_object);
}
FN(jint, java_call_enum_ordinal, (Jvm *jvm, void *axis_aligned_bb)); // enum ordinal
FN(Vec3, vec3_add, (Vec3 a, Vec3 b)); // a + b
FN(Vec3, java_get_block_pos_a, (Jvm *j, jobject o));
FN(I3, get_block_position, (Jvm *j, jobject block_pos));
extern "C" const Vec3 jni_entity_accessors_facing_unit_offset[]; // facing -> unit offset
extern "C" jobject java_axis_aligned_bb_calculate_intercept(Jvm *jvm, jobject object, void *other_object, void *b) {
return JNI(jvm)->CallObjectMethod(jvm->env, object, (jmethodID)jvm->ids->AxisAlignedBB_calculateIntercept, other_object, b);
}
extern "C" jint java_get_moving_object_position_type_ordinal(Jvm *jvm, jobject moving_object_position) {
jobject x = JCALLN(jvm, GetObjectField, moving_object_position, (jfieldID)ids->MovingObjectPosition_typeOfHit);
return java_call_enum_ordinal(jvm, x);
}
// side hit of a MovingObjectPosition (EnumFacing ordinal)
extern "C" jint java_get_variant(Jvm *jvm, jobject object) {
if (jvm->ids->variant != 0) {
jobject x = JCALLN(jvm, GetObjectField, object, (jfieldID)ids->MovingObjectPosition_sideHit);
return java_call_enum_ordinal(jvm, x);
}
return JCALL(jvm, GetIntField, object, (jfieldID)ids->MovingObjectPosition_sideHitIndex);
}
// block position of a MovingObjectPosition
extern "C" Vec3 java_get_moving_object_position_block_x(Jvm *jvm, jobject moving_object_position) {
if (jvm->ids->variant != 0) {
jobject block_pos = JCALLN(jvm, GetObjectField, moving_object_position, (jfieldID)ids->MovingObjectPosition_blockPos);
return java_get_block_pos_a(jvm, block_pos);
}
double x = JCALLN(jvm, GetIntField, moving_object_position, (jfieldID)ids->MovingObjectPosition_blockX);
double y = JCALLN(jvm, GetIntField, moving_object_position, (jfieldID)ids->MovingObjectPosition_blockY);
double z = JCALLN(jvm, GetIntField, moving_object_position, (jfieldID)ids->MovingObjectPosition_blockZ);
return vec3_make(x, y, z);
}
extern "C" I3 java_get_moving_object_position_block_pos(Jvm *jvm, jobject moving_object_position) {
if (jvm->ids->variant != 0) {
jobject p = JCALLN(jvm, GetObjectField, moving_object_position, (jfieldID)ids->MovingObjectPosition_blockPos);
return get_block_position(jvm, p);
}
I3 r;
r.x = JCALLN(jvm, GetIntField, moving_object_position, (jfieldID)ids->MovingObjectPosition_blockX);
r.y = JCALLN(jvm, GetIntField, moving_object_position, (jfieldID)ids->MovingObjectPosition_blockY);
r.z = JCALLN(jvm, GetIntField, moving_object_position, (jfieldID)ids->MovingObjectPosition_blockZ);
return r;
}
// block position + facing offset (the block the hit side points to)
extern "C" Vec3 java_get_adjacent_block_position(Jvm *jvm, jobject object) {
Vec3 p = java_get_moving_object_position_block_x(jvm, object);
unsigned side;
if (jvm->ids->variant != 0) {
jobject x = JCALLN(jvm, GetObjectField, object, (jfieldID)ids->MovingObjectPosition_sideHit);
side = java_call_enum_ordinal(jvm, x);
} else {
side = JCALLN(jvm, GetIntField, object, (jfieldID)ids->MovingObjectPosition_sideHitIndex);
}
return vec3_add(p, jni_entity_accessors_facing_unit_offset[side]);
}
extern "C" jobject java_get_game_settings_use_item_key(Jvm *jvm, jobject game_settings) {
jobject object = JCALLN(jvm, GetObjectField, game_settings, (jfieldID)ids->GameSettings_keyBindings);
return JCALL(jvm, GetObjectArrayElement, (jobjectArray)object, 1);
}
extern "C" jobject java_get_game_settings_key_8_or_9(Jvm *jvm, jobject game_settings) {
jobject object = JCALLN(jvm, GetObjectField, game_settings, (jfieldID)ids->GameSettings_keyBindings);
return JCALL(jvm, GetObjectArrayElement, (jobjectArray)object, ids->variant == 0 ? 8 : 9);
}
// ---- JNI bridge functions found by the gap scan (no caller outside the virtualised init) ----
extern "C" jobject java_call_world_get_entity_by_id(Jvm *jvm, void *world, int value) {
return JCALLN(jvm, CallObjectMethod, world, ids->World_getEntityByID, value);
}
extern "C" jboolean java_call_list_add(Jvm *jvm, void *list, void *value) {
return JCALLN(jvm, CallBooleanMethod, list, ids->List_add, value);
}
// ---- functions the first gap scan missed (padding / tail-jump neighbours) ----
extern "C" jboolean java_call_list_remove(Jvm *jvm, void *list, void *value) {
return JCALLN(jvm, CallBooleanMethod, list, ids->List_remove, value);
}
extern "C" bool java_call_map_contains_key(Jvm *jvm, void *map, void *value) {
return JCALLN(jvm, CallBooleanMethod, map, ids->Map_containsKey, value) != 0;
}
+310
View File
@@ -0,0 +1,310 @@
// JNI: entity rotation and angle accessors.
// cflags: /Ob1 /clang:-fno-slp-vectorize
#include "slinky/engine.h"
#include "slinky/jcall.h"
struct Rot { float yaw, pitch; };
struct Vec3 { double x, y, z; };
FN(Vec3, vec3_make, (double x, double y, double z));
FN(Rot, rot_make, (float yaw, float pitch));
FN(float, limit_rotation_delta, (float lo, float hi, float v));
FN(float, libc_sinf, (float x)); // sinf
FN(float, libc_cosf, (float x)); // cosf
typedef int v4i __attribute__((vector_size(16), aligned(16)));
typedef float v4f __attribute__((vector_size(16), aligned(16)));
extern "C" const float clamp_bound_neg_90, clamp_bound_90, clamp_bound_neg_90_in_jni_entity_rotation, clamp_bound_90_in_jni_entity_rotation, twenty_in_jni_entity_rotation, twenty_in_jni_entity_rotation_a;
extern "C" const double neg_pi, half_turn_degrees_in_jni_entity_rotation, one_in_jni_entity_rotation;
extern "C" const v4i jni_entity_rotation_sign_mask; // sign mask
extern "C" bool java_variant(Jvm *jvm) { return jvm->ids->variant == 0; }
// rotationYaw/rotationPitch packed into a Rot
extern "C" Rot get_entity_rotation(Jvm *jvm, jobject object) {
if (!object) return Rot{0.0f, 0.0f};
float y = JCALLN(jvm, GetFloatField, object, ids->Entity_rotationYaw);
float pitch = JCALLN(jvm, GetFloatField, object, ids->Entity_rotationPitch);
return rot_make(y, pitch);
}
extern "C" Rot get_entity_prev_rotation(Jvm *jvm, jobject object) {
if (!object) return Rot{0.0f, 0.0f};
float y = JCALLN(jvm, GetFloatField, object, ids->Entity_prevRotationYaw);
float pitch = JCALLN(jvm, GetFloatField, object, ids->Entity_prevRotationPitch);
return rot_make(y, pitch);
}
// set yaw, pitch clamped to [lo, hi]
extern "C" void set_entity_rotation(Jvm *jvm, jobject object, Rot rotation) {
if (!object) return;
JCALLN(jvm, SetFloatField, object, ids->Entity_rotationYaw, rotation.yaw);
JIds *ids = jvm->ids; JNIEnv *env = jvm->env;
auto f = (*env)->SetFloatField; jfieldID id = ids->Entity_rotationPitch;
f(env, object, id, limit_rotation_delta(clamp_bound_neg_90, clamp_bound_90, rotation.pitch));
}
extern "C" void set_entity_prev_rotation(Jvm *jvm, jobject object, Rot rotation) {
if (!object) return;
JCALLN(jvm, SetFloatField, object, ids->Entity_prevRotationYaw, rotation.yaw);
JIds *ids = jvm->ids; JNIEnv *env = jvm->env;
auto f = (*env)->SetFloatField; jfieldID id = ids->Entity_prevRotationPitch;
f(env, object, id, limit_rotation_delta(clamp_bound_neg_90_in_jni_entity_rotation, clamp_bound_90_in_jni_entity_rotation, rotation.pitch));
}
// look vector from the entity rotation
extern "C" Vec3 java_get_entity_rotation_pitch(Jvm *jvm, jobject entity) {
float y = JCALLN(jvm, GetFloatField, entity, ids->Entity_rotationYaw);
float pitch = JCALLN(jvm, GetFloatField, entity, ids->Entity_rotationPitch);
Rot rotation = rot_make(y, pitch);
float a = (float)((double)rotation.yaw * neg_pi / half_turn_degrees_in_jni_entity_rotation + neg_pi);
float ca = libc_cosf(a);
float sa = libc_sinf(a);
float b = (float)((double)rotation.pitch * neg_pi / half_turn_degrees_in_jni_entity_rotation);
float cb = ((v4f)((v4i)(v4f){libc_cosf(b)} ^ jni_entity_rotation_sign_mask))[0];
float sb = libc_sinf(b);
float x = sa * cb;
Vec3 v = vec3_make(x, sb, cb * ca);
return v;
}
extern "C" void java_set_entity_motion_x(Jvm *jvm, jobject entity, Vec3 value) {
JCALLN(jvm, SetDoubleField, entity, ids->Entity_motionX, value.x);
JCALLN(jvm, SetDoubleField, entity, ids->Entity_motionY, value.y);
JCALL(jvm, SetDoubleField, entity, ids->Entity_motionZ, value.z);
}
extern "C" void set_motion(Jvm *jvm, jobject object, float x, float y, float z) {
JNI(jvm)->CallVoidMethod(jvm->env, object, jvm->ids->Entity_moveFlying, (double)x, (double)y, (double)z);
}
extern "C" jobject java_call_entity_living_base_get_position(Jvm *jvm, jobject entity_living_base) {
return JCALL1(jvm, CallObjectMethod, entity_living_base, ids->EntityLivingBase_getPosition, one_in_jni_entity_rotation);
}
static inline __attribute__((always_inline)) bool isnan_bits(float f) {
return (__builtin_bit_cast(unsigned, f) & 0x7fffffff) >= 0x7f800001;
}
extern "C" float java_call_entity_living_base_get_health(Jvm *jvm, jobject entity_living_base) {
float f = JCALLN(jvm, CallFloatMethod, entity_living_base, ids->EntityLivingBase_getHealth);
return isnan_bits(f) ? twenty_in_jni_entity_rotation : f;
}
extern "C" float java_call_entity_living_base_get_max_health(Jvm *jvm, jobject entity_living_base) {
float f = JCALLN(jvm, CallFloatMethod, entity_living_base, ids->EntityLivingBase_getMaxHealth);
return isnan_bits(f) ? twenty_in_jni_entity_rotation_a : f;
}
extern "C" float java_call_entity_player_get_absorption_amount(Jvm *jvm, jobject entity_player) {
float f = JCALLN(jvm, CallFloatMethod, entity_player, ids->EntityPlayer_getAbsorptionAmount);
if (__builtin_expect(isnan_bits(f), 0)) f = 0.0f;
return f;
}
extern "C" jobject java_get_entity_bounding_box(Jvm *jvm, void *entity) {
return JCALL(jvm, GetObjectField, entity, ids->Entity_boundingBox);
}
extern "C" void java_call_entity_set_sprinting(Jvm *jvm, void *entity, unsigned char value) {
JCALL1(jvm, CallVoidMethod, entity, ids->Entity_setSprinting, value);
}
extern "C" jint java_get_entity_entity_id(Jvm *jvm, void *entity) {
return JCALL(jvm, GetIntField, entity, ids->Entity_entityId);
}
extern "C" jobject java_get_entity_uuid(Jvm *jvm, void *entity) {
return JCALL(jvm, GetObjectField, entity, ids->Entity_uniqueId);
}
extern "C" jint java_get_entity_ticks_existed(Jvm *jvm, void *entity) {
return JCALL(jvm, GetIntField, entity, ids->Entity_ticksExisted);
}
extern "C" bool java_get_entity_on_ground(Jvm *jvm, void *entity) {
return JCALLN(jvm, GetBooleanField, entity, ids->Entity_onGround) != 0;
}
extern "C" bool java_get_entity_is_dead(Jvm *jvm, void *entity) {
return JCALLN(jvm, GetBooleanField, entity, ids->Entity_isDead) != 0;
}
extern "C" jfloat java_get_entity_fall_distance(Jvm *jvm, void *entity) {
return JCALL(jvm, GetFloatField, entity, ids->Entity_fallDistance);
}
extern "C" jint java_call_static_entity_list_get_entity_id(Jvm *jvm, void *entity_list) {
return JCALLN(jvm, CallStaticIntMethod, ids->EntityList, ids->EntityList_getEntityID, entity_list);
}
extern "C" jobject java_get_entity_riding_entity(Jvm *jvm, void *entity) {
return JCALL(jvm, GetObjectField, entity, ids->Entity_ridingEntity);
}
extern "C" jint java_get_entity_living_base_last_attacker_time(Jvm *jvm, void *entity_living_base) {
return JCALL(jvm, GetIntField, entity_living_base, ids->EntityLivingBase_lastAttackerTime);
}
extern "C" jint java_get_entity_living_base_hurt_time(Jvm *jvm, void *entity_living_base) {
return JCALL(jvm, GetIntField, entity_living_base, ids->EntityLivingBase_hurtTime);
}
extern "C" void java_set_entity_living_base_max_hurt_time(Jvm *jvm, void *entity_living_base, int value) {
JCALL(jvm, SetIntField, entity_living_base, ids->EntityLivingBase_maxHurtTime, value);
}
extern "C" jfloat java_get_entity_living_base_move_forward(Jvm *jvm, void *entity_living_base) {
return JCALL(jvm, GetFloatField, entity_living_base, ids->EntityLivingBase_moveForward);
}
extern "C" jfloat java_get_entity_living_base_move_strafing(Jvm *jvm, void *entity_living_base) {
return JCALL(jvm, GetFloatField, entity_living_base, ids->EntityLivingBase_moveStrafing);
}
extern "C" jobject java_call_entity_ray_trace(Jvm *jvm, void *object, double value, float a3) {
return JCALL1(jvm, CallObjectMethod, object, ids->EntityLivingBase_rayTrace, value, (double)a3);
}
extern "C" jfloat java_get_entity_living_base_land_movement_factor(Jvm *jvm, void *entity_living_base) {
return JCALL(jvm, GetFloatField, entity_living_base, ids->EntityLivingBase_landMovementFactor);
}
extern "C" jint get_hurt_time(Jvm *jvm, void *entity_living_base) {
return JCALL(jvm, GetIntField, entity_living_base, ids->EntityLivingBase_jumpTicks);
}
extern "C" void java_set_entity_living_base_jump_ticks(Jvm *jvm, void *entity_living_base, int value) {
JCALL(jvm, SetIntField, entity_living_base, ids->EntityLivingBase_jumpTicks, value);
}
extern "C" void java_jni_call_nonvirtual_void_method_entity_living_base_swing_item(Jvm *jvm, void *entity_living_base) {
JCALLN(jvm, CallNonvirtualVoidMethod, entity_living_base, ids->EntityLivingBase, ids->EntityLivingBase_swingItem);
}
extern "C" jobject java_call_entity_living_base_get_active_potion_effect(Jvm *jvm, void *entity_living_base, void *value) {
return JCALLN(jvm, CallObjectMethod, entity_living_base, ids->EntityLivingBase_getActivePotionEffect, value);
}
extern "C" void java_entity_player_sp_render_arm_pitch(Jvm *jvm, void *entity_player_sp, Rot value) {
JNI(jvm)->SetFloatField(jvm->env, entity_player_sp, jvm->ids->EntityPlayerSP_renderArmYaw, value.yaw);
JNI(jvm)->SetFloatField(jvm->env, entity_player_sp, jvm->ids->EntityPlayerSP_renderArmPitch, value.pitch);
}
extern "C" void java_entity_player_sp_prev_render_arm_pitch(Jvm *jvm, void *entity_player_sp, Rot value) {
JNI(jvm)->SetFloatField(jvm->env, entity_player_sp, jvm->ids->EntityPlayerSP_prevRenderArmYaw, value.yaw);
JNI(jvm)->SetFloatField(jvm->env, entity_player_sp, jvm->ids->EntityPlayerSP_prevRenderArmPitch, value.pitch);
}
extern "C" void java_call_entity_player_sp_add_chat_message(Jvm *jvm, void *entity_player_sp, void *value) {
JCALLN(jvm, CallVoidMethod, entity_player_sp, ids->EntityPlayerSP_addChatMessage, value);
}
extern "C" void java_set_entity_player_sp_time_in_portal(Jvm *jvm, void *entity_player_sp, float value) {
JCALL(jvm, SetFloatField, entity_player_sp, ids->EntityPlayerSP_timeInPortal, value);
}
extern "C" void java_set_entity_player_sp_prev_time_in_portal(Jvm *jvm, void *entity_player_sp, float value) {
JCALL(jvm, SetFloatField, entity_player_sp, ids->EntityPlayerSP_prevTimeInPortal, value);
}
extern "C" jobject java_get_entity_player_inventory(Jvm *jvm, void *entity_player) {
return JCALL(jvm, GetObjectField, entity_player, ids->EntityPlayer_inventory);
}
extern "C" bool java_call_entity_player_is_invisible_to_player(Jvm *jvm, void *entity_player, void *value) {
return JCALLN(jvm, CallBooleanMethod, entity_player, ids->EntityPlayer_isInvisibleToPlayer, value) != 0;
}
extern "C" jint java_get_entity_player_item_in_use_count(Jvm *jvm, void *entity_player) {
return JCALL(jvm, GetIntField, entity_player, ids->EntityPlayer_itemInUseCount);
}
extern "C" bool java_call_entity_is_sprinting(Jvm *jvm, void *entity) {
return JCALLN(jvm, CallBooleanMethod, entity, ids->Entity_isSprinting) != 0;
}
extern "C" bool is_sneaking(Jvm *jvm, void *entity) {
return JCALLN(jvm, CallBooleanMethod, entity, ids->Entity_isSneaking) != 0;
}
extern "C" jobject java_call_entity_get_formatted_command_sender_name(Jvm *jvm, void *entity) {
return JCALLN(jvm, CallObjectMethod, entity, ids->Entity_getFormattedCommandSenderName);
}
extern "C" jobject java_call_entity_get_command_sender_name(Jvm *jvm, void *entity) {
return JCALLN(jvm, CallObjectMethod, entity, ids->Entity_getCommandSenderName);
}
extern "C" jboolean java_call_entity_can_be_collided_with(Jvm *jvm, void *entity) {
return JCALLN(jvm, CallBooleanMethod, entity, ids->Entity_canBeCollidedWith);
}
extern "C" jobject java_call_entity_living_base_get_team(Jvm *jvm, void *entity_living_base) {
return JCALLN(jvm, CallObjectMethod, entity_living_base, ids->EntityLivingBase_getTeam);
}
extern "C" bool java_call_entity_player_is_using_item(Jvm *jvm, void *entity_player) {
return JCALLN(jvm, CallBooleanMethod, entity_player, ids->EntityPlayer_isUsingItem) != 0;
}
extern "C" Vec3 java_get_entity_pos_x(Jvm *jvm, jobject entity) {
double x = JCALLN(jvm, GetDoubleField, entity, ids->Entity_posX);
double y = JCALLN(jvm, GetDoubleField, entity, ids->Entity_posY);
double z = JCALLN(jvm, GetDoubleField, entity, ids->Entity_posZ);
Vec3 r = vec3_make(x, y, z);
asm volatile("");
return r;
}
extern "C" Vec3 java_get_entity_prev_pos_x(Jvm *jvm, jobject entity) {
double x = JCALLN(jvm, GetDoubleField, entity, ids->Entity_prevPosX);
double y = JCALLN(jvm, GetDoubleField, entity, ids->Entity_prevPosY);
double z = JCALLN(jvm, GetDoubleField, entity, ids->Entity_prevPosZ);
Vec3 r = vec3_make(x, y, z);
asm volatile("");
return r;
}
extern "C" Vec3 java_get_entity_motion_x(Jvm *jvm, jobject entity) {
double x = JCALLN(jvm, GetDoubleField, entity, ids->Entity_motionX);
double y = JCALLN(jvm, GetDoubleField, entity, ids->Entity_motionY);
double z = JCALLN(jvm, GetDoubleField, entity, ids->Entity_motionZ);
Vec3 r = vec3_make(x, y, z);
asm volatile("");
return r;
}
extern "C" jobject java_jni_is_instance_of_entity_living_base(Jvm *jvm, jobject candidate) {
return JCALLN(jvm, IsInstanceOf, candidate, (jclass)ids->EntityLivingBase) ? candidate : nullptr;
}
extern "C" jobject java_jni_is_instance_of_entity_player(Jvm *jvm, jobject candidate) {
return JCALLN(jvm, IsInstanceOf, candidate, (jclass)ids->EntityPlayer) ? candidate : nullptr;
}
// ---- JNI bridge functions found by the gap scan (no caller outside the virtualised init) ----
extern "C" void java_call_entity_set_angles(Jvm *jvm, void *entity, float value, float a3) {
JCALL1(jvm, CallVoidMethod, entity, ids->Entity_setAngles, ({ double y = value; y; }), (double)a3);
}
extern "C" jdouble java_get_entity_motion_x_value(Jvm *jvm, void *entity) {
return JCALL(jvm, GetDoubleField, entity, ids->Entity_motionX);
}
extern "C" void java_call_entity_living_base_jump(Jvm *jvm, void *entity_living_base) {
JCALLN(jvm, CallVoidMethod, entity_living_base, ids->EntityLivingBase_jump);
}
// ---- gap-scan functions: render-arm rotation of the local player, casts ----
extern "C" Rot java_get_entity_player_sp_render_arm_pitch(Jvm *jvm, jobject entity_player_sp) { // renderArmYaw / renderArmPitch
float y = JCALLN(jvm, GetFloatField, entity_player_sp, ids->EntityPlayerSP_renderArmYaw);
float p = JCALLN(jvm, GetFloatField, entity_player_sp, ids->EntityPlayerSP_renderArmPitch);
return Rot{y, p};
}
extern "C" Rot java_get_entity_player_sp_prev_render_arm_pitch(Jvm *jvm, jobject entity_player_sp) { // prevRenderArmYaw / prevRenderArmPitch
float y = JCALLN(jvm, GetFloatField, entity_player_sp, ids->EntityPlayerSP_prevRenderArmYaw);
float p = JCALLN(jvm, GetFloatField, entity_player_sp, ids->EntityPlayerSP_prevRenderArmPitch);
return Rot{y, p};
}
extern "C" jobject java_jni_is_instance_of_entity_player_sp(Jvm *jvm, jobject candidate) {
return JCALLN(jvm, IsInstanceOf, candidate, (jclass)ids->EntityPlayerSP) ? candidate : nullptr;
}
// ---- functions the first gap scan missed (padding / tail-jump neighbours) ----
extern "C" jdouble java_get_entity_motion_y(Jvm *jvm, void *entity) {
return JCALL(jvm, GetDoubleField, entity, ids->Entity_motionY);
}
extern "C" jdouble java_get_entity_motion_z(Jvm *jvm, void *entity) {
return JCALL(jvm, GetDoubleField, entity, ids->Entity_motionZ);
}
@@ -0,0 +1,318 @@
// JNI: render-manager accessors (camera fields).
// cflags: /Ob1 /clang:-fno-slp-vectorize
#include "slinky/engine.h"
#include "slinky/jcall.h"
struct Rot { float yaw, pitch; };
struct Vec3 { double x, y, z; };
extern "C" jobject java_new_c_init(Jvm *jvm, int a1) {
return JCALLN(jvm, NewObject, ids->c, ids->c__init_, a1);
}
extern "C" jobject java_get_c01_packet_chat_message(Jvm *jvm, void *c01_packet_chat_message) {
return JCALL(jvm, GetObjectField, c01_packet_chat_message, ids->C01PacketChatMessage_Ljava_lang_String_);
}
extern "C" void java_set_c01_packet_chat_message(Jvm *jvm, void *c01_packet_chat_message, void *value) {
JCALL(jvm, SetObjectField, c01_packet_chat_message, ids->C01PacketChatMessage_Ljava_lang_String_, value);
}
extern "C" jobject java_get_c14_packet_tab_complete(Jvm *jvm, void *c14_packet_tab_complete) {
return JCALL(jvm, GetObjectField, c14_packet_tab_complete, ids->C14PacketTabComplete_Ljava_lang_String_);
}
extern "C" jobject java_get_render_manager_instance(Jvm *jvm) {
return JCALL(jvm, GetStaticObjectField, ids->RenderManager, ids->RenderManager_instance);
}
extern "C" jfloat java_get_render_manager_player_view_x(Jvm *jvm, void *render_manager) {
return JCALL(jvm, GetFloatField, render_manager, ids->RenderManager_playerViewX);
}
extern "C" void java_set_render_manager_player_view_x(Jvm *jvm, void *render_manager, float value) {
JCALL(jvm, SetFloatField, render_manager, ids->RenderManager_playerViewX, value);
}
extern "C" jfloat java_get_render_manager_player_view_y(Jvm *jvm, void *render_manager) {
return JCALL(jvm, GetFloatField, render_manager, ids->RenderManager_playerViewY);
}
extern "C" void java_set_render_manager_player_view_y(Jvm *jvm, void *render_manager, float value) {
JCALL(jvm, SetFloatField, render_manager, ids->RenderManager_playerViewY, value);
}
extern "C" jobject java_get_render_manager_text_renderer(Jvm *jvm, void *render_manager) {
return JCALL(jvm, GetObjectField, render_manager, ids->RenderManager_textRenderer);
}
extern "C" jobject java_get_score_player_team(Jvm *jvm, void *score_player_team) {
return JCALL(jvm, GetObjectField, score_player_team, ids->ScorePlayerTeam_Ljava_lang_String_);
}
extern "C" void call_get_mouse_over(Jvm *jvm, void *entity_renderer, float value) {
JCALL1(jvm, CallVoidMethod, entity_renderer, ids->EntityRenderer_getMouseOver, (double)value);
}
extern "C" void call_orient_camera(Jvm *jvm, void *entity_renderer, float value) {
JCALL1(jvm, CallVoidMethod, entity_renderer, ids->EntityRenderer_orientCamera, (double)value);
}
extern "C" void set_render_manager_boolean(Jvm *jvm, void *render_global, unsigned char value) {
JCALL(jvm, SetBooleanField, render_global, ids->RenderGlobal_displayListEntitiesDirty, value);
}
extern "C" jboolean java_call_renderer_living_entity_can_render_name(Jvm *jvm, void *renderer_living_entity, void *value) {
return JCALLN(jvm, CallBooleanMethod, renderer_living_entity, ids->RendererLivingEntity_canRenderName, value);
}
extern "C" jint java_call_font_renderer_get_string_width(Jvm *jvm, void *font_renderer, void *value) {
return JCALLN(jvm, CallIntMethod, font_renderer, ids->FontRenderer_getStringWidth, value);
}
extern "C" void java_set_potion_effect_duration(Jvm *jvm, void *potion_effect, int value) {
JCALL(jvm, SetIntField, potion_effect, ids->PotionEffect_duration, value);
}
extern "C" jint java_get_static_display_width(Jvm *jvm) {
return JCALL(jvm, GetStaticIntField, ids->Display, ids->Display_width);
}
extern "C" jint java_get_static_display_height(Jvm *jvm) {
return JCALL(jvm, GetStaticIntField, ids->Display, ids->Display_height);
}
extern "C" jint display_width(Jvm *jvm) {
return JCALL(jvm, GetStaticIntField, ids->Display, ids->Display_framebufferWidth);
}
extern "C" jint display_height(Jvm *jvm) {
return JCALL(jvm, GetStaticIntField, ids->Display, ids->Display_framebufferHeight);
}
extern "C" jfloat java_get_render_item_z_level(Jvm *jvm, void *render_item) {
return JCALL(jvm, GetFloatField, render_item, ids->RenderItem_zLevel);
}
extern "C" void java_set_render_item_z_level(Jvm *jvm, void *render_item, float value) {
JCALL(jvm, SetFloatField, render_item, ids->RenderItem_zLevel, value);
}
extern "C" jfloat java_get_player_controller_mp_cur_block_damage_mp(Jvm *jvm, void *player_controller_mp) {
return JCALL(jvm, GetFloatField, player_controller_mp, ids->PlayerControllerMP_curBlockDamageMP);
}
extern "C" void java_set_player_controller_mp_cur_block_damage_mp(Jvm *jvm, void *player_controller_mp, float value) {
JCALL(jvm, SetFloatField, player_controller_mp, ids->PlayerControllerMP_curBlockDamageMP, value);
}
extern "C" jint java_get_player_controller_mp_block_hit_delay(Jvm *jvm, void *player_controller_mp) {
return JCALL(jvm, GetIntField, player_controller_mp, ids->PlayerControllerMP_blockHitDelay);
}
extern "C" void java_set_player_controller_mp_block_hit_delay(Jvm *jvm, void *player_controller_mp, int value) {
JCALL(jvm, SetIntField, player_controller_mp, ids->PlayerControllerMP_blockHitDelay, value);
}
extern "C" jboolean java_get_player_controller_mp_is_hitting_block(Jvm *jvm, void *player_controller_mp) {
return JCALL(jvm, GetBooleanField, player_controller_mp, ids->PlayerControllerMP_isHittingBlock);
}
extern "C" void java_set_player_controller_mp_is_hitting_block(Jvm *jvm, void *player_controller_mp, unsigned char value) {
JCALL(jvm, SetBooleanField, player_controller_mp, ids->PlayerControllerMP_isHittingBlock, value);
}
extern "C" jobject java_call_nbttag_list_get_compound_tag_at(Jvm *jvm, void *nbt_tag_list, int value) {
return JCALLN(jvm, CallObjectMethod, nbt_tag_list, ids->NBTTagList_getCompoundTagAt, value);
}
extern "C" jshort java_jni_call_short_method_nbttag_compound_get_short(Jvm *jvm, void *nbt_tag_compound, void *value) {
return JCALLN(jvm, CallShortMethod, nbt_tag_compound, ids->NBTTagCompound_getShort, value);
}
extern "C" jboolean java_get_entity_arrow_in_ground(Jvm *jvm, void *entity_arrow) {
return JCALL(jvm, GetBooleanField, entity_arrow, ids->EntityArrow_inGround);
}
extern "C" jobject java_call_static_stat_collector_translate_to_local(Jvm *jvm, void *stat_collector) {
return JCALLN(jvm, CallStaticObjectMethod, ids->StatCollector, ids->StatCollector_translateToLocal, stat_collector);
}
extern "C" jint java_call_enum_ordinal(Jvm *jvm, void *enum_object) {
return JCALLN(jvm, CallIntMethod, enum_object, ids->Enum_ordinal);
}
extern "C" void gl_begin_2d(Jvm *jvm) {
JCALLN(jvm, CallStaticVoidMethod, ids->GlStateManager, ids->GlStateManager_enableCull);
}
extern "C" void gl_disable_lighting(Jvm *jvm) {
JCALLN(jvm, CallStaticVoidMethod, ids->GlStateManager, ids->GlStateManager_disableLighting);
}
extern "C" void gl_enable_depth(Jvm *jvm) {
JCALLN(jvm, CallStaticVoidMethod, ids->GlStateManager, ids->GlStateManager_enableDepth);
}
extern "C" void gl_push_state(Jvm *jvm) {
JCALLN(jvm, CallStaticVoidMethod, ids->GlStateManager, ids->GlStateManager_disableDepth);
}
extern "C" void gl_pop_state(Jvm *jvm) {
JCALLN(jvm, CallStaticVoidMethod, ids->GlStateManager, ids->GlStateManager_enableBlend);
}
extern "C" void gl_bind_texture(Jvm *jvm) {
JCALLN(jvm, CallStaticVoidMethod, ids->GlStateManager, ids->GlStateManager_enableTexture2D);
}
extern "C" void gl_clear_color(Jvm *jvm) {
JCALLN(jvm, CallStaticVoidMethod, ids->GlStateManager, ids->GlStateManager_disableTexture2D);
}
extern "C" void gl_depth_mask(Jvm *jvm) {
JCALLN(jvm, CallStaticVoidMethod, ids->GlStateManager, ids->GlStateManager_enableAlpha);
}
extern "C" void java_call_static_gl_state_manager_disable_alpha(Jvm *jvm) {
JCALLN(jvm, CallStaticVoidMethod, ids->GlStateManager, ids->GlStateManager_disableAlpha);
}
extern "C" void tick_netty(Jvm *jvm) {
JCALLN(jvm, CallStaticVoidMethod, ids->Display, ids->Display_update);
}
extern "C" bool world_is_loaded(Jvm *jvm) {
return JCALLN(jvm, CallStaticBooleanMethod, ids->Display, ids->Display_isActive) != 0;
}
extern "C" jobject java_new_render_item(Jvm *jvm) {
return JCALLN(jvm, NewObject, ids->RenderItem, ids->RenderItem_init);
}
extern "C" void java_call_static_render_helper_enable_standard_item_lighting(Jvm *jvm) {
JCALLN(jvm, CallStaticVoidMethod, ids->RenderHelper, ids->RenderHelper_enableStandardItemLighting);
}
extern "C" void java_call_static_render_helper_disable_standard_item_lighting(Jvm *jvm) {
JCALLN(jvm, CallStaticVoidMethod, ids->RenderHelper, ids->RenderHelper_disableStandardItemLighting);
}
extern "C" jobject java_call_entity_item_get_entity_item(Jvm *jvm, void *entity_item) {
return JCALLN(jvm, CallObjectMethod, entity_item, ids->EntityItem_getEntityItem);
}
extern "C" jlong java_jni_call_long_method_uuid_get_least_significant_bits(Jvm *jvm, void *uuid) {
return JCALLN(jvm, CallLongMethod, uuid, ids->UUID_getLeastSignificantBits);
}
extern "C" jlong java_jni_call_long_method_uuid_get_most_significant_bits(Jvm *jvm, void *uuid) {
return JCALLN(jvm, CallLongMethod, uuid, ids->UUID_getMostSignificantBits);
}
extern "C" void gui_key_passthrough(Jvm *jvm, void *player_controller_mp) {
JCALLN(jvm, CallVoidMethod, player_controller_mp, ids->PlayerControllerMP_updateController);
}
extern "C" jint java_call_nbttag_list_tag_count(Jvm *jvm, void *nbt_tag_list) {
return JCALLN(jvm, CallIntMethod, nbt_tag_list, ids->NBTTagList_tagCount);
}
extern "C" jobject java_jni_is_instance_of_c01_packet_chat_message(Jvm *jvm, jobject candidate) {
return JCALLN(jvm, IsInstanceOf, candidate, (jclass)ids->C01PacketChatMessage) ? candidate : nullptr;
}
extern "C" jobject java_jni_is_instance_of_c14_packet_tab_complete(Jvm *jvm, jobject candidate) {
return JCALLN(jvm, IsInstanceOf, candidate, (jclass)ids->C14PacketTabComplete) ? candidate : nullptr;
}
extern "C" jobject java_jni_is_instance_of_s2_apacket_particles(Jvm *jvm, jobject candidate) {
return JCALLN(jvm, IsInstanceOf, candidate, (jclass)ids->S2APacketParticles) ? candidate : nullptr;
}
extern "C" jobject java_jni_is_instance_of_score_player_team(Jvm *jvm, jobject candidate) {
return JCALLN(jvm, IsInstanceOf, candidate, (jclass)ids->ScorePlayerTeam) ? candidate : nullptr;
}
extern "C" jobject java_jni_is_instance_of_entity_item(Jvm *jvm, jobject candidate) {
return JCALLN(jvm, IsInstanceOf, candidate, (jclass)ids->EntityItem) ? candidate : nullptr;
}
// ---- hand-written
FN(float, libc_roundf, (float v));
extern "C" float java_call_entity_renderer_get_fovmodifier(Jvm *jvm, jobject entity_renderer, float partial) {
return JCALL1(jvm, CallFloatMethod, entity_renderer, (jmethodID)ids->EntityRenderer_getFOVModifier, (double)partial, 1);
}
extern "C" void set_active_render_info(Jvm *jvm, void *active_render_info, bool b) {
JCALL2(jvm, CallStaticVoidMethod, (jclass)ids->ActiveRenderInfo, (jmethodID)ids->ActiveRenderInfo_updateRenderInfo, active_render_info, (int)b);
}
// MS x64 passes a {double,double} struct by reference, so declaring this as one D2 while every caller
// (chams_render_living_entity, hook_render_player) passes three separate doubles made the callee read the
// stale R9 as a pointer (fault at 0). Keep the three doubles in the signature: they are the method's
// three D arguments and go to the variadic JNI call as such.
extern "C" void java_jni_call_nonvirtual_renderer_living_entity_do_render(Jvm *jvm, jobject object, void *other_object, double x, double d0, double d1, float f0, float f1) {
JCALL2(jvm, CallNonvirtualVoidMethod, object, (jclass)ids->RendererLivingEntity, (jmethodID)ids->RendererLivingEntity___2, other_object, x, d0, d1,
(double)f0, (double)f1);
}
// FontRenderer.drawString: float coordinates in the newer mappings, rounded ints in the old ones
extern "C" jint java_font_draw_string(Jvm *jvm, jobject font, jobject str, float x, float y, unsigned color, unsigned char shadow) {
if (jvm->ids->variant != 0)
return JNI(jvm)->CallIntMethod(jvm->env, font, (jmethodID)jvm->ids->FontRenderer_drawString, str, (double)x, (double)y, color, (int)shadow);
int ix = (int)libc_roundf(x);
int iy = (int)libc_roundf(y);
return JNI(jvm)->CallIntMethod(jvm->env, font, (jmethodID)jvm->ids->FontRenderer_drawString, str, ix, iy, color, (int)shadow);
}
extern "C" void gl_blend_mode(Jvm *jvm, int a, int b) {
JCALL2(jvm, CallStaticVoidMethod, (jclass)ids->GlStateManager, (jmethodID)ids->GlStateManager_blendFunc, a, b);
}
extern "C" void gl_set_color(Jvm *jvm, float a, float b, float c, float d) {
JCALL2(jvm, CallStaticVoidMethod, (jclass)ids->GlStateManager, (jmethodID)ids->GlStateManager_color, (double)a, (double)b, (double)c, (double)d);
}
// element k (< 256) of a static object array
extern "C" jobject java_potion_potion_types(Jvm *jvm, unsigned k) {
if (k > 0xff) return nullptr;
JIds *ids = jvm->ids;
JNIEnv *env = jvm->env;
jobject object = (*env)->GetStaticObjectField(env, (jclass)ids->Potion, (jfieldID)ids->Potion_potionTypes);
if (!object) return nullptr;
jobject value = (*env)->GetObjectArrayElement(env, (jobjectArray)object, k);
(*env)->DeleteLocalRef(env, object);
return value;
}
extern "C" void java_render_item_init(Jvm *jvm, jobject object, void *other_object, void *b, void *c, int d, int e) {
if (jvm->ids->variant != 0)
JNI(jvm)->CallVoidMethod(jvm->env, object, (jmethodID)jvm->ids->RenderItem__init_, c, d, e);
else
JNI(jvm)->CallVoidMethod(jvm->env, object, (jmethodID)jvm->ids->RenderItem__init_, other_object, b, c, d, e);
}
extern "C" void java_render_item_render_item_overlay_into_gui(Jvm *jvm, jobject object, void *other_object, void *b, void *c, int d, int e, void *f) {
if (jvm->ids->variant != 0)
JNI(jvm)->CallVoidMethod(jvm->env, object, (jmethodID)jvm->ids->RenderItem_renderItemOverlayIntoGUI, other_object, c, d, e, f);
else
JNI(jvm)->CallVoidMethod(jvm->env, object, (jmethodID)jvm->ids->RenderItem_renderItemOverlayIntoGUI, other_object, b, c, d, e, f);
}
// ---- JNI bridge functions found by the gap scan (no caller outside the virtualised init) ----
extern "C" void java_call_framebuffer_bind_framebuffer(Jvm *jvm, void *framebuffer, unsigned char value) {
JCALL1(jvm, CallVoidMethod, framebuffer, ids->Framebuffer_bindFramebuffer, value);
}
extern "C" void java_call_static_gl_state_manager_disable_cull(Jvm *jvm) {
JCALLN(jvm, CallStaticVoidMethod, ids->GlStateManager, ids->GlStateManager_disableCull);
}
extern "C" void java_call_static_gl_state_manager_enable_lighting(Jvm *jvm) {
JCALLN(jvm, CallStaticVoidMethod, ids->GlStateManager, ids->GlStateManager_enableLighting);
}
extern "C" void java_call_static_gl_state_manager_disable_blend(Jvm *jvm) {
JCALLN(jvm, CallStaticVoidMethod, ids->GlStateManager, ids->GlStateManager_disableBlend);
}
extern "C" void java_call_entity_item_search_for_other_items_nearby(Jvm *jvm, void *entity_item) {
JCALLN(jvm, CallVoidMethod, entity_item, ids->EntityItem_searchForOtherItemsNearby);
}
// EntityRenderer.renderHand(partialTicks, pass)
extern "C" void java_call_entity_renderer_render_hand(Jvm *jvm, jobject renderer, float partial_ticks, int pass) {
JCALL1(jvm, CallVoidMethod, renderer, ids->EntityRenderer_renderHand, (double)partial_ticks, pass);
}
+302
View File
@@ -0,0 +1,302 @@
// JNI: local-player accessors.
// cflags: /Ob1 /clang:-fno-slp-vectorize
#include "slinky/engine.h"
#include "slinky/jcall.h"
struct Rot { float yaw, pitch; };
struct Vec3 { double x, y, z; };
FN(Vec3, vec3_make, (double x, double y, double z));
extern "C" jint java_get_game_settings_third_person_view(Jvm *jvm, void *game_settings) {
return JCALL(jvm, GetIntField, game_settings, ids->GameSettings_thirdPersonView);
}
extern "C" void java_set_game_settings_third_person_view(Jvm *jvm, void *game_settings, int value) {
JCALL(jvm, SetIntField, game_settings, ids->GameSettings_thirdPersonView, value);
}
extern "C" jint java_get_key_binding_press_time(Jvm *jvm, void *key_binding) {
return JCALL(jvm, GetIntField, key_binding, ids->KeyBinding_pressTime);
}
extern "C" void java_set_key_binding_press_time(Jvm *jvm, void *key_binding, int value) {
JCALL(jvm, SetIntField, key_binding, ids->KeyBinding_pressTime, value);
}
extern "C" jint get_event_key(Jvm *jvm, void *key_binding) {
return JCALL(jvm, GetIntField, key_binding, ids->KeyBinding_keyCode);
}
extern "C" bool is_key_down(Jvm *jvm, void *key_binding) {
return JCALLN(jvm, GetBooleanField, key_binding, ids->KeyBinding_pressed) != 0;
}
extern "C" void java_set_key_binding_pressed(Jvm *jvm, void *key_binding, unsigned char value) {
JCALL(jvm, SetBooleanField, key_binding, ids->KeyBinding_pressed, value);
}
extern "C" bool java_call_static_keyboard_is_key_down(Jvm *jvm, int keyboard) {
return JCALLN(jvm, CallStaticBooleanMethod, ids->Keyboard, ids->Keyboard_isKeyDown, keyboard) != 0;
}
extern "C" bool java_call_static_mouse_is_button_down(Jvm *jvm, int mouse) {
return JCALLN(jvm, CallStaticBooleanMethod, ids->Mouse, ids->Mouse_isButtonDown, mouse) != 0;
}
extern "C" jfloat get_partial_ticks(Jvm *jvm, void *timer) {
return JCALL(jvm, GetFloatField, timer, ids->Timer_renderPartialTicks);
}
extern "C" jfloat java_get_timer_timer_speed(Jvm *jvm, void *timer) {
return JCALL(jvm, GetFloatField, timer, ids->Timer_timerSpeed);
}
extern "C" void java_set_timer_timer_speed(Jvm *jvm, void *timer, float value) {
JCALL(jvm, SetFloatField, timer, ids->Timer_timerSpeed, value);
}
extern "C" jfloat java_get_timer_elapsed_partial_ticks(Jvm *jvm, void *timer) {
return JCALL(jvm, GetFloatField, timer, ids->Timer_elapsedPartialTicks);
}
extern "C" void java_set_timer_elapsed_partial_ticks(Jvm *jvm, void *timer, float value) {
JCALL(jvm, SetFloatField, timer, ids->Timer_elapsedPartialTicks, value);
}
extern "C" jint java_get_inventory_player_current_item(Jvm *jvm, void *inventory_player) {
return JCALL(jvm, GetIntField, inventory_player, ids->InventoryPlayer_currentItem);
}
extern "C" void java_set_inventory_player_current_item(Jvm *jvm, void *inventory_player, int value) {
JCALL(jvm, SetIntField, inventory_player, ids->InventoryPlayer_currentItem, value);
}
extern "C" jobject java_get_inventory_player_main_inventory(Jvm *jvm, void *inventory_player) {
return JCALL(jvm, GetObjectField, inventory_player, ids->InventoryPlayer_mainInventory);
}
extern "C" jobject java_get_inventory_player_item_stack(Jvm *jvm, void *inventory_player) {
return JCALL(jvm, GetObjectField, inventory_player, ids->InventoryPlayer_itemStack);
}
extern "C" jobject java_get_gui_container_the_slot(Jvm *jvm, void *gui_container) {
return JCALL(jvm, GetObjectField, gui_container, ids->GuiContainer_theSlot);
}
extern "C" bool java_jni_is_instance_of_gui_inventory(Jvm *jvm, void *candidate) {
return JCALLN(jvm, IsInstanceOf, candidate, ids->GuiInventory) != 0;
}
extern "C" bool java_jni_is_instance_of_gui_chest(Jvm *jvm, void *candidate) {
return JCALLN(jvm, IsInstanceOf, candidate, ids->GuiChest) != 0;
}
extern "C" jobject java_get_gui_chest_lower_chest_inventory(Jvm *jvm, void *gui_chest) {
return JCALL(jvm, GetObjectField, gui_chest, ids->GuiChest_lowerChestInventory);
}
extern "C" jint java_call_static_item_get_id_from_item(Jvm *jvm, void *item) {
return JCALLN(jvm, CallStaticIntMethod, ids->Item, ids->Item_getIdFromItem, item);
}
extern "C" jfloat java_call_item_stack_get_str_vs_block(Jvm *jvm, void *item_stack, void *value) {
return JCALLN(jvm, CallFloatMethod, item_stack, ids->ItemStack_getStrVsBlock, value);
}
extern "C" jint java_get_item_stack_stack_size(Jvm *jvm, void *item_stack) {
return JCALL(jvm, GetIntField, item_stack, ids->ItemStack_stackSize);
}
extern "C" jint java_get_item_stack_metadata(Jvm *jvm, void *item_stack) {
return JCALL(jvm, GetIntField, item_stack, ids->ItemStack_metadata);
}
extern "C" jobject java_get_net_handler_play_client(Jvm *jvm, void *net_handler_play_client) {
return JCALL(jvm, GetObjectField, net_handler_play_client, ids->NetHandlerPlayClient_playerInfoMap);
}
extern "C" jobject java_get_net_handler_play_client_net_manager(Jvm *jvm, void *net_handler_play_client) {
return JCALL(jvm, GetObjectField, net_handler_play_client, ids->NetHandlerPlayClient_netManager);
}
extern "C" jobject java_get_network_manager(Jvm *jvm, void *network_manager) {
return JCALL(jvm, GetObjectField, network_manager, ids->NetworkManager_Lio_netty_channel_Channel_);
}
extern "C" void send_packet(Jvm *jvm, void *a1, void *value) {
JCALLN(jvm, CallVoidMethod, a1, ids->NetworkManager_sendPacket, value);
}
extern "C" bool keyboard_has_next(Jvm *jvm) {
return JCALLN(jvm, CallStaticBooleanMethod, ids->Keyboard, ids->Keyboard_next) != 0;
}
extern "C" bool keyboard_can_repeat(Jvm *jvm) {
return JCALLN(jvm, CallStaticBooleanMethod, ids->Keyboard, ids->Keyboard_getEventKeyState) != 0;
}
extern "C" jint keyboard_next_key(Jvm *jvm) {
return JCALLN(jvm, CallStaticIntMethod, ids->Keyboard, ids->Keyboard_getEventKey);
}
extern "C" bool java_call_static_mouse_is_grabbed(Jvm *jvm) {
return JCALLN(jvm, CallStaticBooleanMethod, ids->Mouse, ids->Mouse_isGrabbed) != 0;
}
extern "C" bool keyboard_has_events(Jvm *jvm) {
return JCALLN(jvm, CallStaticBooleanMethod, ids->Mouse, ids->Mouse_next) != 0;
}
extern "C" jobject java_call_item_stack_get_item(Jvm *jvm, void *item_stack) {
return JCALLN(jvm, CallObjectMethod, item_stack, ids->ItemStack_getItem);
}
extern "C" jobject java_call_item_stack_get_enchantment_tag_list(Jvm *jvm, void *item_stack) {
return JCALLN(jvm, CallObjectMethod, item_stack, ids->ItemStack_getEnchantmentTagList);
}
extern "C" jint java_call_item_stack_get_max_stack_size(Jvm *jvm, void *item_stack) {
return JCALLN(jvm, CallIntMethod, item_stack, ids->ItemStack_getMaxStackSize);
}
extern "C" bool java_call_item_stack_has_display_name(Jvm *jvm, void *item_stack) {
return JCALLN(jvm, CallBooleanMethod, item_stack, ids->ItemStack_hasDisplayName) != 0;
}
extern "C" Vec3 java_get_vec3_x_coord(Jvm *jvm, jobject vec3) {
double x = JCALLN(jvm, GetDoubleField, vec3, ids->Vec3_xCoord);
double y = JCALLN(jvm, GetDoubleField, vec3, ids->Vec3_yCoord);
double z = JCALLN(jvm, GetDoubleField, vec3, ids->Vec3_zCoord);
Vec3 r = vec3_make(x, y, z);
asm volatile("");
return r;
}
extern "C" jobject java_get_game_settings_sneak_key(Jvm *jvm, jobject game_settings) {
jobject object = JCALLN(jvm, GetObjectField, game_settings, ids->GameSettings_keyBindings);
return JCALL(jvm, GetObjectArrayElement, (jobjectArray)object, 7);
}
extern "C" jobject get_held_item(Jvm *jvm, jobject object) {
jobject a = JCALLN(jvm, GetObjectField, object, ids->GameSettings_keyBindings);
return JCALL(jvm, GetObjectArrayElement, (jobjectArray)a, 6);
}
extern "C" jobject java_get_game_settings_forward_key(Jvm *jvm, jobject game_settings) {
jobject object = JCALLN(jvm, GetObjectField, game_settings, ids->GameSettings_keyBindings);
return JCALL(jvm, GetObjectArrayElement, (jobjectArray)object, 2);
}
extern "C" jobject java_get_game_settings_back_key(Jvm *jvm, jobject game_settings) {
jobject object = JCALLN(jvm, GetObjectField, game_settings, ids->GameSettings_keyBindings);
return JCALL(jvm, GetObjectArrayElement, (jobjectArray)object, 4);
}
extern "C" jobject java_get_game_settings_left_key(Jvm *jvm, jobject game_settings) {
jobject object = JCALLN(jvm, GetObjectField, game_settings, ids->GameSettings_keyBindings);
return JCALL(jvm, GetObjectArrayElement, (jobjectArray)object, 3);
}
extern "C" jobject java_get_game_settings_right_key(Jvm *jvm, jobject game_settings) {
jobject object = JCALLN(jvm, GetObjectField, game_settings, ids->GameSettings_keyBindings);
return JCALL(jvm, GetObjectArrayElement, (jobjectArray)object, 5);
}
extern "C" jobject java_get_game_settings_attack_key(Jvm *jvm, jobject game_settings) {
jobject object = JCALLN(jvm, GetObjectField, game_settings, ids->GameSettings_keyBindings);
return JCALL(jvm, GetObjectArrayElement, (jobjectArray)object, 0);
}
// ---- hand-written
struct Box { Vec3 lo, hi; };
FN(Box, aabb_make, (const Vec3 *lo, const Vec3 *hi));
FN(bool, java_call_static_keyboard_is_key_down, (Jvm *j, int key)); // Keyboard.isKeyDown
FN(bool, java_call_static_mouse_is_button_down, (Jvm *j, int button)); // Mouse.isButtonDown
extern "C" jobject java_get_game_settings_key_17_or_8(Jvm *jvm, jobject game_settings) {
jobject object = JCALLN(jvm, GetObjectField, game_settings, (jfieldID)ids->GameSettings_keyBindings);
return JCALL(jvm, GetObjectArrayElement, (jobjectArray)object, (ids->variant == 0) * 9 + 8);
}
extern "C" jobject get_game_screen(Jvm *jvm, jobject object) {
jobject a = JCALLN(jvm, GetObjectField, object, (jfieldID)ids->GameSettings_keyBindings);
return JCALL(jvm, GetObjectArrayElement, (jobjectArray)a, ids->variant == 0 ? 9 : 10);
}
extern "C" void java_increment_key_binding_press_time(Jvm *jvm, jobject key_binding) {
jint v = JCALLN(jvm, GetIntField, key_binding, (jfieldID)ids->KeyBinding_pressTime);
JCALL(jvm, SetIntField, key_binding, (jfieldID)ids->KeyBinding_pressTime, v + 1);
}
// key binding pressed: negative codes are mouse buttons (code + 100)
extern "C" bool java_get_key_binding_key_code(Jvm *jvm, jobject key_binding) {
jint keyboard = JCALLN(jvm, GetIntField, key_binding, (jfieldID)ids->KeyBinding_keyCode);
if (keyboard < 0) return java_call_static_mouse_is_button_down(jvm, keyboard + 100);
if ((unsigned)keyboard <= 0x100) return java_call_static_keyboard_is_key_down(jvm, keyboard);
return false;
}
extern "C" jobject java_new_vec3_init(Jvm *jvm, Vec3 v) {
return JCALL2(jvm, NewObject, (jclass)ids->Vec3, (jmethodID)ids->Vec3__init_, v.x, v.y, v.z);
}
extern "C" jobject java_new_axis_aligned_bb_init(Jvm *jvm, Box b) {
return JCALL2(jvm, NewObject, (jclass)ids->AxisAlignedBB, (jmethodID)ids->AxisAlignedBB__init_,
b.lo.x, b.lo.y, b.lo.z, b.hi.x, b.hi.y, b.hi.z);
}
extern "C" Box java_get_axis_aligned_bb_min_x(Jvm *jvm, jobject axis_aligned_bb) {
double x = JCALLN(jvm, GetDoubleField, axis_aligned_bb, (jfieldID)ids->AxisAlignedBB_minX);
double y = JCALLN(jvm, GetDoubleField, axis_aligned_bb, (jfieldID)ids->AxisAlignedBB_minY);
double z = JCALLN(jvm, GetDoubleField, axis_aligned_bb, (jfieldID)ids->AxisAlignedBB_minZ);
Vec3 lo = vec3_make(x, y, z);
x = JCALLN(jvm, GetDoubleField, axis_aligned_bb, (jfieldID)ids->AxisAlignedBB_maxX);
y = JCALLN(jvm, GetDoubleField, axis_aligned_bb, (jfieldID)ids->AxisAlignedBB_maxY);
z = JCALLN(jvm, GetDoubleField, axis_aligned_bb, (jfieldID)ids->AxisAlignedBB_maxZ);
Vec3 hi = vec3_make(x, y, z);
Box r = aabb_make(&lo, &hi);
return r;
}
extern "C" void java_call_static_mouse_set_grabbed(Jvm *jvm, bool grab) {
JCALL2(jvm, CallStaticVoidMethod, (jclass)ids->Mouse, (jmethodID)ids->Mouse_setGrabbed, grab);
}
extern "C" void java_call_static_mouse_set_cursor_position(Jvm *jvm, int x, int y) {
JCALL2(jvm, CallStaticVoidMethod, (jclass)ids->Mouse, (jmethodID)ids->Mouse_setCursorPosition, x, y);
}
extern "C" void keyboard_send_key(Jvm *jvm, jobject object, unsigned short c, int k) {
JCALL1(jvm, CallVoidMethod, object, (jmethodID)ids->GuiScreen_keyTyped, (int)c, k);
}
extern "C" void java_gui_container_handle_mouse_click(Jvm *jvm, jobject object, int a, int b, int c) {
JNI(jvm)->CallVoidMethod(jvm->env, object, (jmethodID)jvm->ids->GuiContainer_handleMouseClick, 0, a, b, c);
}
extern "C" bool java_call_static_item_stack_are_item_stack_tags_equal(Jvm *jvm, void *item_stack, void *other_object) {
return JCALL2(jvm, CallStaticBooleanMethod, (jclass)ids->ItemStack, (jmethodID)ids->ItemStack_areItemStackTagsEqual, item_stack, other_object) != 0;
}
extern "C" void send_packet_with_id(Jvm *jvm, jobject object, void *other_object, void *b) {
JNI(jvm)->CallVoidMethod(jvm->env, object, (jmethodID)jvm->ids->NetworkManager_scheduleOutboundPacket, other_object, b);
}
// newer mappings take an extra (empty) array argument
extern "C" void java_call_generic_future_listener(Jvm *jvm, jobject generic_future_listener, void *other_object) {
if (jvm->ids->NetworkManager_scheduleOutboundPacket) {
jobjectArray arr = JNI(jvm)->NewObjectArray(jvm->env, 0, (jclass)jvm->ids->GenericFutureListener, nullptr);
JCALL1(jvm, CallVoidMethod, generic_future_listener, (jmethodID)ids->NetworkManager_scheduleOutboundPacket, other_object, arr);
} else {
JCALL1(jvm, CallVoidMethod, generic_future_listener, (jmethodID)ids->NetworkManager_sendPacket, other_object);
}
}
// ---- JNI bridge functions found by the gap scan (no caller outside the virtualised init) ----
extern "C" jint java_get_timer_elapsed_ticks(Jvm *jvm, void *timer) {
return JCALL(jvm, GetIntField, timer, ids->Timer_elapsedTicks);
}
extern "C" void java_jni_set_long_field_gui_container_last_click_time(Jvm *jvm, void *gui_container, long long value) {
JCALL(jvm, SetLongField, gui_container, ids->GuiContainer_lastClickTime, (jlong)value);
}
extern "C" jobject java_call_static_item_get_item_by_id(Jvm *jvm, int item) {
return JCALLN(jvm, CallStaticObjectMethod, ids->Item, ids->Item_getItemById, item);
}
extern "C" void java_set_item_stack_stack_size(Jvm *jvm, void *item_stack, int value) {
JCALL(jvm, SetIntField, item_stack, ids->ItemStack_stackSize, value);
}
// ---- functions the first gap scan missed (padding / tail-jump neighbours) ----
extern "C" jobject java_new_item_stack_init(Jvm *jvm, void *item_stack) {
return JCALLN(jvm, NewObject, ids->ItemStack, ids->ItemStack__init_, item_stack);
}
+351
View File
@@ -0,0 +1,351 @@
// JNI: world and chunk accessors.
// cflags: /Ob1 /clang:-fno-slp-vectorize
#include "slinky/engine.h"
#include "slinky/jcall.h"
struct Rot { float yaw, pitch; };
struct Vec3 { double x, y, z; };
FN(Vec3, vec3_make, (double x, double y, double z));
extern "C" jobject java_get_network_player_info_display_name(Jvm *jvm, void *network_player_info) {
return JCALL(jvm, GetObjectField, network_player_info, ids->NetworkPlayerInfo_displayName);
}
extern "C" jobject java_new_chat_component_text_init(Jvm *jvm, void *chat_component_text) {
return JCALLN(jvm, NewObject, ids->ChatComponentText, ids->ChatComponentText__init_, chat_component_text);
}
extern "C" jobject java_call_iinventory_get_stack_in_slot(Jvm *jvm, void *i_inventory, int value) {
return JCALLN(jvm, CallObjectMethod, i_inventory, ids->IInventory_getStackInSlot, value);
}
extern "C" jobject java_get_container_player_craft_matrix(Jvm *jvm, void *container_player) {
return JCALL(jvm, GetObjectField, container_player, ids->ContainerPlayer_craftMatrix);
}
extern "C" jint java_get_slot_slot_number(Jvm *jvm, void *slot) {
return JCALL(jvm, GetIntField, slot, ids->Slot_slotNumber);
}
extern "C" jobject java_get_inventory_basic(Jvm *jvm, void *inventory_basic) {
return JCALL(jvm, GetObjectField, inventory_basic, ids->InventoryBasic_Ljava_lang_String_);
}
extern "C" jobject java_call_channel_pipeline_get(Jvm *jvm, void *channel_pipeline, void *value) {
return JCALLN(jvm, CallObjectMethod, channel_pipeline, ids->ChannelPipeline_get, value);
}
extern "C" jobject java_call_channel_pipeline_remove(Jvm *jvm, void *channel_pipeline, void *value) {
return JCALLN(jvm, CallObjectMethod, channel_pipeline, ids->ChannelPipeline_remove, value);
}
extern "C" bool java_get_c03_packet_player_moving(Jvm *jvm, void *c03_packet_player) {
return JCALLN(jvm, GetBooleanField, c03_packet_player, ids->C03PacketPlayer_moving) != 0;
}
extern "C" bool java_get_c03_packet_player_on_ground(Jvm *jvm, void *c03_packet_player) {
return JCALLN(jvm, GetBooleanField, c03_packet_player, ids->C03PacketPlayer_onGround) != 0;
}
extern "C" jdouble java_get_c03_packet_player_b(Jvm *jvm, void *c03_packet_player) {
return JCALL(jvm, GetDoubleField, c03_packet_player, ids->C03PacketPlayer_b);
}
extern "C" jobject java_get_c08_packet_player_block_placement_stack(Jvm *jvm, void *c08_packet_player_block_placement) {
return JCALL(jvm, GetObjectField, c08_packet_player_block_placement, ids->C08PacketPlayerBlockPlacement_stack);
}
extern "C" jint java_get_s12_packet_entity_velocity_entity_id(Jvm *jvm, void *s12_packet_entity_velocity) {
return JCALL(jvm, GetIntField, s12_packet_entity_velocity, ids->S12PacketEntityVelocity_entityID);
}
extern "C" jint java_get_s14_packet_entity_entity_id(Jvm *jvm, void *s14_packet_entity) {
return JCALL(jvm, GetIntField, s14_packet_entity, ids->S14PacketEntity_entityId);
}
extern "C" jbyte java_jni_get_byte_field_s14_packet_entity_pos_x(Jvm *jvm, void *s14_packet_entity) {
return JCALL(jvm, GetByteField, s14_packet_entity, ids->S14PacketEntity_posX);
}
extern "C" jbyte java_jni_get_byte_field_s14_packet_entity_pos_y(Jvm *jvm, void *s14_packet_entity) {
return JCALL(jvm, GetByteField, s14_packet_entity, ids->S14PacketEntity_posY);
}
extern "C" jbyte java_jni_get_byte_field_s14_packet_entity_pos_z(Jvm *jvm, void *s14_packet_entity) {
return JCALL(jvm, GetByteField, s14_packet_entity, ids->S14PacketEntity_posZ);
}
extern "C" jbyte java_jni_get_byte_field_s14_packet_entity_yaw(Jvm *jvm, void *s14_packet_entity) {
return JCALL(jvm, GetByteField, s14_packet_entity, ids->S14PacketEntity_yaw);
}
extern "C" bool java_get_s14_packet_entity_g(Jvm *jvm, void *s14_packet_entity) {
return JCALLN(jvm, GetBooleanField, s14_packet_entity, ids->S14PacketEntity_g) != 0;
}
extern "C" jint java_get_s18_packet_entity_teleport_entity_id(Jvm *jvm, void *s18_packet_entity_teleport) {
return JCALL(jvm, GetIntField, s18_packet_entity_teleport, ids->S18PacketEntityTeleport_entityId);
}
extern "C" jint java_get_s18_packet_entity_teleport_pos_x(Jvm *jvm, void *s18_packet_entity_teleport) {
return JCALL(jvm, GetIntField, s18_packet_entity_teleport, ids->S18PacketEntityTeleport_posX);
}
extern "C" jint java_get_s18_packet_entity_teleport_pos_y(Jvm *jvm, void *s18_packet_entity_teleport) {
return JCALL(jvm, GetIntField, s18_packet_entity_teleport, ids->S18PacketEntityTeleport_posY);
}
extern "C" jint java_get_s18_packet_entity_teleport_pos_z(Jvm *jvm, void *s18_packet_entity_teleport) {
return JCALL(jvm, GetIntField, s18_packet_entity_teleport, ids->S18PacketEntityTeleport_posZ);
}
extern "C" jbyte java_jni_get_byte_field_s18_packet_entity_teleport_yaw(Jvm *jvm, void *s18_packet_entity_teleport) {
return JCALL(jvm, GetByteField, s18_packet_entity_teleport, ids->S18PacketEntityTeleport_yaw);
}
extern "C" jint java_get_s19_packet_entity_status_entity_id(Jvm *jvm, void *s19_packet_entity_status) {
return JCALL(jvm, GetIntField, s19_packet_entity_status, ids->S19PacketEntityStatus_entityId);
}
extern "C" jfloat java_get_s27_packet_explosion_f(Jvm *jvm, void *s27_packet_explosion) {
return JCALL(jvm, GetFloatField, s27_packet_explosion, ids->S27PacketExplosion_f);
}
extern "C" jfloat java_get_s27_packet_explosion_h(Jvm *jvm, void *s27_packet_explosion) {
return JCALL(jvm, GetFloatField, s27_packet_explosion, ids->S27PacketExplosion_h);
}
extern "C" jobject java_get_c16_enum_state_open_inventory(Jvm *jvm) {
return JCALL(jvm, GetStaticObjectField, ids->C16PacketClientStatus_EnumState, ids->C16PacketClientStatus_EnumState_openInventory);
}
extern "C" jobject java_new_c16_packet_client_status(Jvm *jvm, void *object) {
return JCALLN(jvm, NewObject, ids->C16PacketClientStatus, ids->C16PacketClientStatus_init, object);
}
extern "C" jobject java_call_network_player_info_get_player_team(Jvm *jvm, void *network_player_info) {
return JCALLN(jvm, CallObjectMethod, network_player_info, ids->NetworkPlayerInfo_getPlayerTeam);
}
extern "C" jint java_call_object_hash_code(Jvm *jvm, void *object) {
return JCALLN(jvm, CallIntMethod, object, ids->Object_hashCode);
}
extern "C" jobject java_call_ichat_component_get_formatted_text(Jvm *jvm, void *i_chat_component) {
return JCALLN(jvm, CallObjectMethod, i_chat_component, ids->IChatComponent_getFormattedText);
}
extern "C" jobject java_call_slot_get_stack(Jvm *jvm, void *slot) {
return JCALLN(jvm, CallObjectMethod, slot, ids->Slot_getStack);
}
extern "C" jobject java_call_channel_pipeline(Jvm *jvm, void *channel) {
return JCALLN(jvm, CallObjectMethod, channel, ids->Channel_pipeline);
}
extern "C" jobject java_jni_is_instance_of_inventory_basic(Jvm *jvm, jobject candidate) {
return JCALLN(jvm, IsInstanceOf, candidate, (jclass)ids->InventoryBasic) ? candidate : nullptr;
}
extern "C" jobject java_jni_is_instance_of_c03_packet_player(Jvm *jvm, jobject candidate) {
return JCALLN(jvm, IsInstanceOf, candidate, (jclass)ids->C03PacketPlayer) ? candidate : nullptr;
}
extern "C" jobject java_jni_is_instance_of_c07_packet_player_digging(Jvm *jvm, jobject candidate) {
return JCALLN(jvm, IsInstanceOf, candidate, (jclass)ids->C07PacketPlayerDigging) ? candidate : nullptr;
}
extern "C" jobject java_jni_is_instance_of_c08_packet_player_block_placement(Jvm *jvm, jobject candidate) {
return JCALLN(jvm, IsInstanceOf, candidate, (jclass)ids->C08PacketPlayerBlockPlacement) ? candidate : nullptr;
}
extern "C" jobject java_jni_is_instance_of_c02_packet_use_entity(Jvm *jvm, jobject candidate) {
return JCALLN(jvm, IsInstanceOf, candidate, (jclass)ids->C02PacketUseEntity) ? candidate : nullptr;
}
extern "C" jobject java_jni_is_instance_of_c0_bpacket_entity_action(Jvm *jvm, jobject candidate) {
return JCALLN(jvm, IsInstanceOf, candidate, (jclass)ids->C0BPacketEntityAction) ? candidate : nullptr;
}
extern "C" jobject java_jni_is_instance_of_s12_packet_entity_velocity(Jvm *jvm, jobject candidate) {
return JCALLN(jvm, IsInstanceOf, candidate, (jclass)ids->S12PacketEntityVelocity) ? candidate : nullptr;
}
extern "C" jobject java_jni_is_instance_of_s14_packet_entity(Jvm *jvm, jobject candidate) {
return JCALLN(jvm, IsInstanceOf, candidate, (jclass)ids->S14PacketEntity) ? candidate : nullptr;
}
extern "C" jobject java_jni_is_instance_of_s18_packet_entity_teleport(Jvm *jvm, jobject candidate) {
return JCALLN(jvm, IsInstanceOf, candidate, (jclass)ids->S18PacketEntityTeleport) ? candidate : nullptr;
}
extern "C" jobject java_jni_is_instance_of_s19_packet_entity_status(Jvm *jvm, jobject candidate) {
return JCALLN(jvm, IsInstanceOf, candidate, (jclass)ids->S19PacketEntityStatus) ? candidate : nullptr;
}
extern "C" jobject java_jni_is_instance_of_s29_packet_sound_effect(Jvm *jvm, jobject candidate) {
return JCALLN(jvm, IsInstanceOf, candidate, (jclass)ids->S29PacketSoundEffect) ? candidate : nullptr;
}
extern "C" jobject java_jni_is_instance_of_s06_packet_update_health(Jvm *jvm, jobject candidate) {
return JCALLN(jvm, IsInstanceOf, candidate, (jclass)ids->S06PacketUpdateHealth) ? candidate : nullptr;
}
extern "C" jobject java_jni_is_instance_of_s08_packet_player_pos_look(Jvm *jvm, jobject candidate) {
return JCALLN(jvm, IsInstanceOf, candidate, (jclass)ids->S08PacketPlayerPosLook) ? candidate : nullptr;
}
extern "C" jobject java_jni_is_instance_of_s27_packet_explosion(Jvm *jvm, jobject candidate) {
return JCALLN(jvm, IsInstanceOf, candidate, (jclass)ids->S27PacketExplosion) ? candidate : nullptr;
}
extern "C" jobject java_jni_is_instance_of_s00_packet_keep_alive(Jvm *jvm, jobject candidate) {
return JCALLN(jvm, IsInstanceOf, candidate, (jclass)ids->S00PacketKeepAlive) ? candidate : nullptr;
}
extern "C" jobject java_jni_is_instance_of_c00_packet_keep_alive(Jvm *jvm, jobject candidate) {
return JCALLN(jvm, IsInstanceOf, candidate, (jclass)ids->C00PacketKeepAlive) ? candidate : nullptr;
}
extern "C" jobject java_jni_is_instance_of_c0_epacket_click_window(Jvm *jvm, jobject candidate) {
return JCALLN(jvm, IsInstanceOf, candidate, (jclass)ids->C0EPacketClickWindow) ? candidate : nullptr;
}
extern "C" jobject java_jni_is_instance_of_c16_packet_client_status(Jvm *jvm, jobject candidate) {
return JCALLN(jvm, IsInstanceOf, candidate, (jclass)ids->C16PacketClientStatus) ? candidate : nullptr;
}
extern "C" jobject java_jni_is_instance_of_c(Jvm *jvm, jobject candidate) {
return JCALLN(jvm, IsInstanceOf, candidate, (jclass)ids->c) ? candidate : nullptr;
}
extern "C" Vec3 java_get_c03_packet_player_a(Jvm *jvm, jobject c03_packet_player) {
double x = JCALLN(jvm, GetDoubleField, c03_packet_player, ids->C03PacketPlayer_a);
double y = JCALLN(jvm, GetDoubleField, c03_packet_player, ids->C03PacketPlayer_b);
double z = JCALLN(jvm, GetDoubleField, c03_packet_player, ids->C03PacketPlayer_c);
Vec3 r = vec3_make(x, y, z);
asm volatile("");
return r;
}
// ---- hand-written
FN(jint, java_call_enum_ordinal, (Jvm *jvm, void *c07_packet_player_digging)); // enum ordinal
// enum field: object (ordinal via java_call_enum_ordinal) in the newer mappings, plain int in the old ones
extern "C" jint java_c07_packet_player_digging_status(Jvm *jvm, jobject object) {
JIds *ids = jvm->ids; JNIEnv *env = jvm->env;
auto vt = *env; jfieldID id = (jfieldID)ids->C07PacketPlayerDigging_status;
if (ids->variant != 0)
return java_call_enum_ordinal(jvm, vt->GetObjectField(env, object, id));
return vt->GetIntField(env, object, id);
}
extern "C" jint java_c0_bpacket_entity_action(Jvm *jvm, jobject object) {
JIds *ids = jvm->ids; JNIEnv *env = jvm->env;
auto vt = *env; jfieldID id = (jfieldID)ids->C0BPacketEntityAction_Lis_a_;
if (ids->variant != 0)
return java_call_enum_ordinal(jvm, vt->GetObjectField(env, object, id));
return vt->GetIntField(env, object, id);
}
extern "C" jobject java_call_channel_pipeline_add_before(Jvm *jvm, jobject channel_pipeline, const char *a, const char *b, void *c) {
JNIEnv *env = jvm->env;
jstring sa = (*env)->NewStringUTF(env, a);
jstring sb = (*env)->NewStringUTF(env, b);
return JCALL1(jvm, CallObjectMethod, channel_pipeline, (jmethodID)ids->ChannelPipeline_addBefore, sa, sb, c);
}
extern "C" jint java_get_c16_packet_client_status_status(Jvm *jvm, jobject c16_packet_client_status) {
jobject e = JCALLN(jvm, GetObjectField, c16_packet_client_status, (jfieldID)ids->C16PacketClientStatus_status);
return java_call_enum_ordinal(jvm, e);
}
// ---- JNI bridge functions found by the gap scan (no caller outside the virtualised init) ----
extern "C" jobject java_call_static_ichat_component_serializer_json_to_component(Jvm *jvm, void *i_chat_component) {
return JCALLN(jvm, CallStaticObjectMethod, ids->IChatComponent_Serializer, ids->IChatComponent_Serializer_jsonToComponent, i_chat_component);
}
extern "C" jdouble java_get_c03_packet_player_x(Jvm *jvm, void *c03_packet_player) {
return JCALL(jvm, GetDoubleField, c03_packet_player, ids->C03PacketPlayer_a);
}
extern "C" jdouble java_get_c03_packet_player_c(Jvm *jvm, void *c03_packet_player) {
return JCALL(jvm, GetDoubleField, c03_packet_player, ids->C03PacketPlayer_c);
}
extern "C" jint java_get_s12_packet_entity_velocity_motion_x(Jvm *jvm, void *s12_packet_entity_velocity) {
return JCALL(jvm, GetIntField, s12_packet_entity_velocity, ids->S12PacketEntityVelocity_motionX);
}
extern "C" jbyte java_jni_get_byte_field_s14_packet_entity_pitch(Jvm *jvm, void *s14_packet_entity) {
return JCALL(jvm, GetByteField, s14_packet_entity, ids->S14PacketEntity_pitch);
}
extern "C" jbyte java_jni_get_byte_field_s18_packet_entity_teleport_pitch(Jvm *jvm, void *s18_packet_entity_teleport) {
return JCALL(jvm, GetByteField, s18_packet_entity_teleport, ids->S18PacketEntityTeleport_pitch);
}
extern "C" void java_set_s27_packet_explosion_f(Jvm *jvm, void *s27_packet_explosion, float value) {
JCALL(jvm, SetFloatField, s27_packet_explosion, ids->S27PacketExplosion_f, value);
}
extern "C" void java_set_s27_packet_explosion_h(Jvm *jvm, void *s27_packet_explosion, float value) {
JCALL(jvm, SetFloatField, s27_packet_explosion, ids->S27PacketExplosion_h, value);
}
extern "C" jobject java_jni_is_instance_of_s3_fpacket_custom_payload(Jvm *jvm, jobject candidate) {
return JCALLN(jvm, IsInstanceOf, candidate, (jclass)ids->S3FPacketCustomPayload) ? candidate : nullptr;
}
// ---- functions the first gap scan missed (padding / tail-jump neighbours) ----
extern "C" void java_set_s12_packet_entity_velocity_motion_x(Jvm *jvm, void *s12_packet_entity_velocity, int value) {
JCALL(jvm, SetIntField, s12_packet_entity_velocity, ids->S12PacketEntityVelocity_motionX, value);
}
extern "C" jint java_get_s12_packet_entity_velocity_motion_y(Jvm *jvm, void *s12_packet_entity_velocity) {
return JCALL(jvm, GetIntField, s12_packet_entity_velocity, ids->S12PacketEntityVelocity_motionY);
}
extern "C" void java_set_s12_packet_entity_velocity_motion_y(Jvm *jvm, void *s12_packet_entity_velocity, int value) {
JCALL(jvm, SetIntField, s12_packet_entity_velocity, ids->S12PacketEntityVelocity_motionY, value);
}
extern "C" jint java_get_s12_packet_entity_velocity_motion_z(Jvm *jvm, void *s12_packet_entity_velocity) {
return JCALL(jvm, GetIntField, s12_packet_entity_velocity, ids->S12PacketEntityVelocity_motionZ);
}
extern "C" void java_set_s12_packet_entity_velocity_motion_z(Jvm *jvm, void *s12_packet_entity_velocity, int value) {
JCALL(jvm, SetIntField, s12_packet_entity_velocity, ids->S12PacketEntityVelocity_motionZ, value);
}
extern "C" jfloat java_get_s27_packet_explosion_g(Jvm *jvm, void *s27_packet_explosion) {
return JCALL(jvm, GetFloatField, s27_packet_explosion, ids->S27PacketExplosion_g);
}
extern "C" void java_set_s27_packet_explosion_g(Jvm *jvm, void *s27_packet_explosion, float value) {
JCALL(jvm, SetFloatField, s27_packet_explosion, ids->S27PacketExplosion_g, value);
}
extern "C" jobject java_get_s3_fpacket_custom_payload(Jvm *jvm, void *s3_f_packet_custom_payload) {
return JCALL(jvm, GetObjectField, s3_f_packet_custom_payload, ids->S3FPacketCustomPayload_Ljava_lang_String_);
}
extern "C" jint java_call_packet_buffer_readable_bytes(Jvm *jvm, void *packet_buffer) {
return JCALLN(jvm, CallIntMethod, packet_buffer, ids->PacketBuffer_readableBytes);
}
extern "C" jint java_call_packet_buffer_reader_index(Jvm *jvm, void *packet_buffer) {
return JCALLN(jvm, CallIntMethod, packet_buffer, ids->PacketBuffer_readerIndex);
}
// S3FPacketCustomPayload payload as a byte[]: 1.7 keeps a byte[] field; 1.8 keeps a PacketBuffer whose readable
// bytes are copied into a new array (getBytes(readerIndex, array))
extern "C" jobject java_get_s3_fpacket_custom_payload_data(Jvm *jvm, void *pkt) {
jobject data = JCALLN(jvm, GetObjectField, pkt, ids->S3FPacketCustomPayload_data);
if (jvm->ids->variant == 0) return data;
jint len = JCALLN(jvm, CallIntMethod, data, ids->PacketBuffer_readableBytes);
jint idx = JCALLN(jvm, CallIntMethod, data, ids->PacketBuffer_readerIndex);
jobject arr = JCALLN(jvm, NewByteArray, len);
if (!arr) return nullptr;
JCALLN(jvm, CallObjectMethod, data, ids->PacketBuffer_getBytes, idx, arr);
return arr;
}
extern "C" jobject java_call_packet_buffer_get_bytes(Jvm *jvm, void *buf, int idx, jobject arr) {
return JCALL1(jvm, CallObjectMethod, buf, ids->PacketBuffer_getBytes, idx, arr);
}
+400
View File
@@ -0,0 +1,400 @@
// 0x28000: module manager - per-event handler lists, dispatch, small item helpers
// cflags: /Ob1 /clang:-fno-slp-vectorize -mllvm -switch-to-lookup=false
#include "slinky/engine.h"
#include "slinky/jcall.h"
extern "C" void slinky_logf(const char *fmt, ...);
typedef void (*Handler)(void *event);
struct Module;
struct Reg { Module **list; unsigned long long n; };
struct Mgr {
Module **mods; unsigned long long count; Reg cat[5]; // +0x10: modules per category
Reg reg[27]; // +0x060: modules that implement each event type
Handler *lists[27]; // +0x210: NULL-terminated handler list per event type
Module **en_list; unsigned long long en_count; // +0x2e8 pending enables
Module **dis_list; unsigned long long dis_count; // +0x2f8 pending disables
void (*set308)(void *mod, int v); // +0x308
};
struct HSlot { Handler f; void *x; };
struct Module { char padding_00[0x10]; unsigned cat; unsigned char enabled; char padding_15[0x33]; HSlot h[27]; };
// dispatch: call every handler registered for the event's type (first dword of the event)
extern "C" void dispatch_event(Mgr *manager, unsigned *e) {
unsigned type = *e;
if (type >= 27) return;
Handler *h = manager->lists[type];
for (Handler f = *h; f; f = *++h) f(e);
}
extern "C" bool is_event_handled(Mgr *manager, unsigned type) {
if (type >= 27) return true;
return *manager->lists[type] == 0;
}
extern "C" bool has_queued_modules(Mgr *manager) { return manager->dis_count != 0 || manager->en_count != 0; }
extern "C" void apply_module_state(Mgr *manager, void *mod, int v) {
auto set308 = manager->set308;
asm("" : "+r"(set308));
set308(mod, v);
}
FN(void *, java_get_entity_player_inventory, (void *jvm, void *entity_player));
FN(int, java_get_inventory_player_current_item, (void *jvm, void *inventory_player));
FN(void *, java_get_inventory_player_main_inventory, (void *jvm, void *inventory_player));
FN(void *, java_call_item_stack_get_item, (void *jvm, void *item_stack));
FN(int, java_call_static_item_get_id_from_item, (void *jvm, void *item));
// item held in a hotbar slot (0..8) of the entity's inventory
extern "C" void *item_held_hotbar_slot(Jvm *jvm, void *ent) {
void *inv = java_get_entity_player_inventory(jvm, ent);
unsigned slot = java_get_inventory_player_current_item(jvm, inv);
if (slot > 8) return 0;
void *arr = java_get_inventory_player_main_inventory(jvm, inv);
jobject st = (*jvm->env)->GetObjectArrayElement(jvm->env, (jobjectArray)arr, slot);
if (!st) return 0;
return java_call_item_stack_get_item(jvm, st);
}
// is the item a sword (ids 267, 268, 272, 276, 283)?
extern "C" bool item_sword(Jvm *jvm, void *item) {
unsigned char r;
if (!item) { r = 0; goto out; }
{
unsigned k = java_call_static_item_get_id_from_item(jvm, item) - 0x10b;
if (k <= 0x10) { asm volatile(""); r = (unsigned char)(0x10223u >> k); }
else r = 0;
}
out:
SHARED_EPILOG();
return r & 1;
}
static inline __attribute__((always_inline)) bool is_code(unsigned short c) {
return (unsigned short)(c - 0x61) < 6 | (unsigned short)(c - 0x30) < 10 | c == 0x72;
}
// colour code (0-9, a-f, r) in effect before the first name character of a display string, 0 if none
static inline __attribute__((always_inline)) unsigned prefix_code(Jvm *jvm, jstring s) {
JNIEnv *env = jvm->env;
if (!s) return 0;
jsize len = (*env)->GetStringLength(env, s);
if (len <= 0) return 0;
const jchar *ch = (const jchar *)(*env)->GetStringCritical(env, s, 0);
if (!ch) return 0;
unsigned r = 0;
bool prev = false;
long long i = 0;
for (;;) {
bool p = prev;
unsigned short c = ch[i];
prev = c == 0xa7;
if (!prev) {
if (p) {
if ((unsigned short)(c - 0x30) < 10) r = c;
if ((unsigned short)(c - 0x61) < 6) r = c;
if (c == 0x72) r = c;
} else {
if (c == 0x5f || (unsigned short)(c - 0x30) < 10 || (unsigned short)((c & 0xffdf) - 0x41) < 0x1a)
break;
}
}
i++;
if (i >= (long long)len) { r = 0; break; }
}
(*env)->ReleaseStringCritical(env, s, ch);
return r;
}
// last colour code (0-9, a-f, r) of a display string, 'f' if none
static inline __attribute__((always_inline)) unsigned short last_code(Jvm *jvm, jstring s) {
unsigned short res = 0x66;
JNIEnv *env = jvm->env;
jsize len = (*env)->GetStringLength(env, s);
if (!len) return res;
const jchar *ch = (const jchar *)(*env)->GetStringCritical(env, s, 0);
if (!ch) return res;
if (len > 0) {
unsigned cand = 0;
unsigned char valid = 0;
for (unsigned long long k = (unsigned)len + 1; ; ) {
unsigned short c = ch[k - 2];
if (c != 0xa7) {
valid = is_code(c);
if (valid) cand = c;
} else {
if (valid & 1) { res = (unsigned short)cand; break; }
valid = 0;
}
k--;
if (k <= 1) break;
}
}
(*env)->ReleaseStringCritical(env, s, ch);
return res;
}
extern "C" unsigned name_prefix_code(Jvm *jvm, jstring s) { return prefix_code(jvm, s); }
FN(jstring, java_get_score_player_team, (Jvm *jvm, void *score_player_team));
extern "C" unsigned short name_last_code(Jvm *jvm, void *object) {
jstring score_player_team = java_get_score_player_team(jvm, object);
if (!score_player_team) return 0x66;
return last_code(jvm, score_player_team);
}
FN(jstring, java_call_entity_get_command_sender_name, (Jvm *jvm, void *ent));
FN(void *, java_jni_is_instance_of_entity_living_base, (Jvm *jvm, void *ent));
FN(void *, java_call_entity_living_base_get_team, (Jvm *jvm, void *entity_living_base));
FN(void *, java_jni_is_instance_of_score_player_team, (Jvm *jvm, void *candidate));
// entity name colour: prefix code of the display name, else the team colour, else 'f'
extern "C" unsigned short entity_name_colour(Jvm *jvm, void *ent) {
unsigned short r = (unsigned short)prefix_code(jvm, java_call_entity_get_command_sender_name(jvm, ent));
if (r) return r;
void *entity_living_base = java_jni_is_instance_of_entity_living_base(jvm, ent);
if (!entity_living_base) return 0x66;
void *entity_living_base_get_team = java_call_entity_living_base_get_team(jvm, entity_living_base);
if (!entity_living_base_get_team) return 0x66;
void *score_player_team = java_jni_is_instance_of_score_player_team(jvm, entity_living_base_get_team);
if (!score_player_team) return 0x66;
jstring s = java_get_score_player_team(jvm, score_player_team);
if (!s) return 0x66;
return last_code(jvm, s);
}
// apply pending enables/disables: rebuild the handler lists of the affected event types, fire event 5
// Same layout as the context the frame code builds (java_natives_frame.cpp): toggle handlers read the game
// objects, so these first fields have to line up.
struct HCtx { void *jvm; Mgr *module_manager; void *profile_list; void *minecraft; void *world; void *player; };
struct ToggleEvent { long long type; HCtx *ctx; Module *mod; unsigned char on; char rest[0x2f]; };
FN(void, release_owner, (void *timer));
static inline __attribute__((always_inline)) unsigned long long handler_mask(Module *module) {
unsigned long long mask = 0;
#pragma clang loop unroll_count(3)
for (unsigned t = 0; t < 27; t++)
if (module->h[t].f) mask |= 1u << t;
return mask;
}
static inline __attribute__((always_inline)) void rebuild(Mgr *manager, unsigned long long mask) {
#pragma clang loop unroll(disable)
for (unsigned long long t = 0; t != 27; t++) {
if (!((mask >> t) & 1)) continue;
Handler *out = manager->lists[t];
Reg *r = &manager->reg[t];
#pragma clang loop unroll_count(2)
for (unsigned long long i = 0; i < r->n; i++) {
Module *mod = r->list[i];
if (mod->enabled == 1 && mod->h[t].f) *out++ = mod->h[t].f;
}
*out = 0;
}
}
extern "C" void apply_module_toggles(HCtx *ctx) {
Mgr *manager = ctx->module_manager;
// The handlers can touch the game objects: Block In's enable reads the local player's rotation, and the
// start-up queue (modules with flags bit 1) is applied on the first frame, which on a fresh launch is
// the main menu, where Minecraft.thePlayer/theWorld are NULL. Keeping the queue until the first frame
// that has both is what the tick path already does (tick_modules has the same check); applying it
// against a NULL player faulted in jni_fast_GetFloatField.
if (!ctx->world || !ctx->player) return;
if (manager->en_count) {
unsigned long long mask = 0;
for (unsigned long long i = 0; i != manager->en_count; i++) {
Module *mod = manager->en_list[i];
mod->enabled = 1;
mask |= handler_mask(mod);
}
rebuild(manager, mask);
slinky_logf("toggles: enable n=%llu mask=%llx first=%s", manager->en_count, mask, *(const char **)manager->en_list[0]);
for (unsigned long long i = 0; i < manager->en_count; i++) {
ToggleEvent e = {5, ctx, manager->en_list[i], 1, {}};
Handler *h = ctx->module_manager->lists[5];
for (Handler f = *h; f; f = *++h) f(&e);
}
manager->en_count = 0;
manager = ctx->module_manager;
}
if (manager->dis_count) {
unsigned long long mask = 0;
for (unsigned long long i = 0; i != manager->dis_count; i++) {
Module *mod = manager->dis_list[i];
mod->enabled = 0;
mask |= handler_mask(mod);
}
for (unsigned long long i = 0; i < manager->dis_count; i++) {
Module *mod = manager->dis_list[i];
ToggleEvent e = {5, ctx, mod, 0, {}};
Handler *h = ctx->module_manager->lists[5];
for (Handler f = *h; f; f = *++h) f(&e);
release_owner(mod);
}
rebuild(manager, mask);
slinky_logf("toggles: disable n=%llu mask=%llx first=%s", manager->dis_count, mask, *(const char **)manager->dis_list[0]);
manager->dis_count = 0;
}
}
// manager constructor: copy the static module table, bucket modules by category and by implemented event
FN(void *, libc_memset, (void *dest, int c, unsigned long long n)); // memset
FN(void *, libc_calloc, (unsigned long long n, unsigned long long size)); // calloc
FN(void *, libc_memcpy_alt, (void *dest, const void *src, unsigned long long n)); // memcpy
FN(void, libc_qsort, (void *base, unsigned long long n, unsigned long long size, int (*cmp)(const void *, const void *))); // qsort
extern "C" Module *const ptr_to_aim_assist_module_descriptor[0x43];
extern "C" unsigned module_manager_count;
FN(int, compare_module_slots, (const void *left, const void *right));
extern "C" int create_module_manager(Mgr *manager, void (*setter)(void *mod, int v)) {
libc_memset(manager->cat, 0, 0x300);
manager->count = 0x43;
manager->mods = (Module **)libc_calloc(0x43, 8);
if (!manager->mods) return 0x3ed;
libc_memcpy_alt(manager->mods, ptr_to_aim_assist_module_descriptor, 0x218);
#pragma clang loop unroll(disable)
for (unsigned long long i = 0; i != 0x43; i++) {
Module *mod = ptr_to_aim_assist_module_descriptor[i];
*(unsigned long long *)((char *)manager + 0x18 + ((unsigned long long)mod->cat << 4)) += 1;
#pragma clang loop unroll_count(2)
for (unsigned t = 0; t < 27; t++)
if (mod->h[t].f) manager->reg[t].n++;
}
for (int c = 0; c < 5; c++) {
unsigned long long n = manager->cat[c].n;
if (n) {
manager->cat[c].list = (Module **)libc_calloc(n, 8);
if (!manager->cat[c].list) return 0x3ed;
} else {
manager->cat[c].list = 0; // empty category: calloc(0) is implementation-defined
}
manager->cat[c].n = 0;
}
#pragma clang loop unroll(disable)
for (int t = 0; t < 27; t++) {
unsigned long long n = manager->reg[t].n;
if (n) {
manager->reg[t].list = (Module **)libc_calloc(n, 8);
if (!manager->reg[t].list) return 0x3ed;
} else {
manager->reg[t].list = 0;
}
manager->lists[t] = (Handler *)libc_calloc(n + 1, 8);
if (!manager->lists[t]) return 0x3ed;
manager->reg[t].n = 0;
}
manager->en_list = (Module **)libc_calloc(0x43, 8);
if (!manager->en_list) return 0x3ed;
manager->dis_list = (Module **)libc_calloc(0x43, 8);
if (!manager->dis_list) return 0x3ed;
#pragma clang loop unroll(disable)
for (unsigned long long i = 0; i != 0x43; i++) {
Module *mod = ptr_to_aim_assist_module_descriptor[i];
Reg *cr = &manager->cat[mod->cat];
unsigned long long cn = cr->n;
cr->list[cn] = mod;
cr->n = cn + 1;
#pragma clang loop unroll_count(2)
for (unsigned t = 0; t < 27; t++)
if (mod->h[t].f) {
unsigned long long rn = manager->reg[t].n;
manager->reg[t].list[rn] = mod;
manager->reg[t].n = rn + 1;
}
}
#pragma clang loop unroll(disable)
for (unsigned t = 0; t != 27; t++) {
module_manager_count = t;
libc_qsort(manager->reg[t].list, manager->reg[t].n, 8, compare_module_slots);
}
manager->set308 = setter;
return 0;
}
// queue a module enable/disable (applied by apply_module_toggles); a disable is announced first (event 4) and can be
// cancelled by a handler setting the event's flag
struct QEvent { int type; int padding_04; Module *mod; unsigned char cancel; char rest[0x37]; };
extern "C" bool queue_module_toggle(Mgr *module_manager, Module *module, unsigned char enabled) {
if (module->enabled == enabled) return true;
Module ***list;
if (enabled) {
list = &module_manager->en_list;
} else {
QEvent ev = {};
ev.type = 4;
ev.mod = module;
Handler *h = module_manager->lists[4];
if (*h) {
for (Handler f = *h; f; f = *++h) f((unsigned *)&ev);
if (ev.cancel & 1) return false;
}
list = &module_manager->dis_list;
}
unsigned long long *n = (unsigned long long *)(list + 1);
if (*n >= 0x43) return false; // en_list/dis_list capacity
(*list)[(*n)++] = module;
return true;
}
FN(void *, get_block_state_id, (Jvm *jvm, void *a1));
FN(jint, get_block_id, (Jvm *jvm, void *a1));
extern "C" bool block_entity_id(Jvm *jvm, void *ent) {
bool r = false;
if (ent) {
void *block_state_id = get_block_state_id(jvm, ent);
if (block_state_id) r = get_block_id(jvm, block_state_id) != 0;
}
SHARED_EPILOG();
return r;
}
// ---- gap-scan functions of the manager block ----
// sort comparator: descending by the int in slot `module_manager_count` (slots are 16 bytes, starting at +0x50)
struct SortSlot { int v; int padding_04[3]; };
struct SortRec { char padding_00[0x50]; SortSlot k[1]; };
extern "C" int compare_module_slots(const void *pa, const void *pb) {
SortRec **a = (SortRec **)pa, **b = (SortRec **)pb;
SortRec *rb = *b;
unsigned i = module_manager_count;
return rb->k[i].v - (*a)->k[i].v;
}
// module manager destructor: free every list the constructor (create_module_manager) allocated
FN(void, libc_free, (void *ptr));
#define RELEASE(p) \
do { \
if (p) { \
libc_free(p); \
p = 0; \
} \
} while (0)
extern "C" void destroy_module_manager(Mgr *manager) {
RELEASE(manager->mods);
for (int i = 0; i < 5; i++) RELEASE(manager->cat[i].list);
#pragma clang loop unroll(disable)
for (int i = 0; i < 27; i++) RELEASE(manager->reg[i].list);
#pragma clang loop unroll(disable)
for (int i = 0; i < 27; i++) RELEASE(manager->lists[i]);
RELEASE(manager->en_list);
RELEASE(manager->dis_list);
}
// Minecraft formatting codes in a (modified UTF-8) name: returns the last colour code (0-9, a-f; 'r' resets to 'f')
// seen before the first visible character (letter, digit or '_'); *hasText tells whether there was one.
// "§" arrives as the byte pair C2 A7.
FN(unsigned long long, libc_strlen, (const char *str));
extern "C" int last_text_color_code(const char *s, int colour, bool *hasText) {
*hasText = false;
unsigned long long n = libc_strlen(s);
bool sawC2 = false, afterSign = false;
for (unsigned long long i = 0; i != n; i++) {
signed char c = s[i];
if (afterSign) {
signed char code = c == 'r' ? 'f' : c;
if ((unsigned short)(c - '0') < 10) colour = code;
if ((unsigned short)(c - 'a') < 6) colour = code;
if (c == 'r') colour = code;
afterSign = false;
} else if (sawC2) {
if ((unsigned char)c == 0xa7) {
afterSign = true;
sawC2 = false;
} else {
sawC2 = (unsigned char)c == 0xc2;
}
} else {
if ((unsigned char)c == 0xc2) {
sawC2 = true;
continue;
}
if (c == '_' || (unsigned short)(c - '0') < 10 || (unsigned short)((c & 0xdf) - 'A') <= 25) {
*hasText = true;
return colour;
}
}
}
return colour;
}
+59
View File
@@ -0,0 +1,59 @@
// 0x2b000: refill-stack tracking for the held block, accumulated rotation delta
#include "slinky/engine.h"
#include "slinky/jcall.h"
struct Rot { float yaw, pitch; };
FN(void *, java_get_entity_player_inventory, (void *jvm, void *entity_player));
FN(int, java_get_inventory_player_current_item, (void *jvm, void *inv));
FN(void *, java_get_inventory_player_main_inventory, (void *jvm, void *inv));
FN(void *, java_call_item_stack_get_item, (void *jvm, void *stack));
FN(int, java_call_static_item_get_id_from_item, (void *jvm, void *item));
FN(int, java_get_item_stack_stack_size, (void *jvm, void *stack)); // stack size
FN(bool, block_entity_id, (void *jvm, void *item)); // is a block item
FN(Rot, rot_add, (Rot a, Rot b));
extern "C" int rotation_hotbar_item_id, refill_state_ticks_since_swap, refill_state_item_id, refill_state_target_stack_size, refill_state_last_slot, refill_state_countdown;
extern "C" unsigned char rotation_hotbar_item_pending;
extern "C" unsigned long long aim_assist_cached_target_ref;
extern "C" Rot aim_assist_mouse_movement_since_last_tick;
// remember the held block (id, count, slot) before placing
extern "C" void tick_click(Ctx *ctx) {
if (refill_state_item_id != 0) return;
void *inv = java_get_entity_player_inventory(ctx->jvm, ctx->player);
int slot = java_get_inventory_player_current_item(ctx->jvm, inv);
void *items = java_get_inventory_player_main_inventory(ctx->jvm, inv);
JNIEnv *env = ((Jvm *)ctx->jvm)->env;
jobject st = (*env)->GetObjectArrayElement(env, (jobjectArray)items, slot);
if (!st) return;
void *item = java_call_item_stack_get_item(ctx->jvm, st);
if (!block_entity_id(ctx->jvm, item)) return;
refill_state_item_id = java_call_static_item_get_id_from_item(ctx->jvm, item);
int n = java_get_item_stack_stack_size(ctx->jvm, st);
refill_state_target_stack_size = n;
if ((unsigned)refill_state_ticks_since_swap > 20 || refill_state_last_slot != slot) refill_state_countdown = n;
refill_state_ticks_since_swap = 0;
refill_state_last_slot = slot;
}
// after placing: if the stack ran out, request a switch to the same block id
extern "C" void tick_click_done(Ctx *ctx) {
if (refill_state_item_id == 0) return;
void *inv = java_get_entity_player_inventory(ctx->jvm, ctx->player);
int slot = java_get_inventory_player_current_item(ctx->jvm, inv);
void *items = java_get_inventory_player_main_inventory(ctx->jvm, inv);
JNIEnv *env = ((Jvm *)ctx->jvm)->env;
jobject st = (*env)->GetObjectArrayElement(env, (jobjectArray)items, slot);
if (st) {
void *item = java_call_item_stack_get_item(ctx->jvm, st);
if (item && java_call_static_item_get_id_from_item(ctx->jvm, item) != 0) {
if (java_get_item_stack_stack_size(ctx->jvm, st) >= refill_state_target_stack_size) goto clear;
if (--refill_state_countdown != 0) goto clear;
}
}
rotation_hotbar_item_id = refill_state_item_id;
rotation_hotbar_item_pending = 1;
clear:
refill_state_item_id = 0;
}
extern "C" unsigned long long get_refill_state() { return aim_assist_cached_target_ref; }
extern "C" void add_rotation_delta(Rot rotation) { aim_assist_mouse_movement_since_last_tick = rot_add(aim_assist_mouse_movement_since_last_tick, rotation); }
@@ -0,0 +1,324 @@
// 0x2a000: rotation save/restore, hotbar slot switching, tagged-float entity flags, small id sets
// cflags: /Ob1 /clang:-fno-slp-vectorize
#include "slinky/engine.h"
#include "slinky/jcall.h"
struct F2 { float a, b; };
FN(void *, get_minecraft, (void *jvm));
FN(void *, get_local_player, (void *jvm, void *minecraft));
FN(unsigned long long, get_entity_rotation, (void *jvm, void *entity)); // packed Rot
FN(void, set_entity_rotation, (void *jvm, void *entity, unsigned long long rot));
FN(void, set_entity_prev_rotation, (void *jvm, void *entity, unsigned long long rot));
FN(float, java_get_entity_player_speed_on_ground, (void *jvm, void *entity_player)); // tagged flag float
FN(void, java_set_entity_player_speed_on_ground, (void *jvm, void *entity_player, float value));
FN(void *, java_get_entity_player_inventory, (void *jvm, void *entity_player));
FN(int, java_get_inventory_player_current_item, (void *jvm, void *inv));
FN(void *, java_get_inventory_player_main_inventory, (void *jvm, void *inv));
FN(void *, java_call_item_stack_get_item, (void *jvm, void *stack));
FN(int, java_call_static_item_get_id_from_item, (void *jvm, void *item));
FN(void, java_set_inventory_player_current_item, (void *jvm, void *inv, int slot));
FN(unsigned long long, libc_strlen, (const char *str)); // strlen
FN(void **, element_at, (void *vec, int elem, void *hdr, void *key));
FN(int, key_name_to_code, (void *key));
FN(void *, key_code_to_index, (int idx));
FN(void *, vector_find_insert, (void *t, int elem, void *hdr, void *e, void *cmp));
FN(void *, vector_erase, (void *vec, int elem, void *hdr, void *key));
FN(void *, binary_search, (void *vec, int elem, void *hdr, void *e, void *cmp));
extern "C" unsigned long long silent_rotation_current, rotation_hotbar_saved_current_rotation; // saved / current rotation
extern "C" unsigned char silent_rotation_active_flag, render_rotation_dirty, rotation_hotbar_swap_pending;
extern "C" void *rotation_hotbar_slot_owner; // slot owner
extern "C" void *rotation_hotbar_module_that_switched_slot; // module that switched the slot
extern "C" int rotation_hotbar_item_id, refill_state_ticks_since_swap;
extern "C" unsigned char rotation_hotbar_item_pending;
// apply the saved rotation to the local player (silent rotations)
extern "C" void tick_rotation(void *jvm) {
void *minecraft = get_minecraft(jvm);
void *player = get_local_player(jvm, minecraft);
if (!player) return;
rotation_hotbar_saved_current_rotation = get_entity_rotation(jvm, player);
if (render_rotation_dirty == 1) {
render_rotation_dirty = 0;
set_entity_rotation(jvm, player, silent_rotation_current);
set_entity_prev_rotation(jvm, player, silent_rotation_current);
}
if (silent_rotation_active_flag == 1) {
rotation_hotbar_swap_pending = 1;
set_entity_rotation(jvm, player, silent_rotation_current);
}
}
// restore the real rotation
extern "C" void apply_rotation_to_player(void *jvm, void *object) {
if (rotation_hotbar_swap_pending != 1) return;
silent_rotation_current = get_entity_rotation(jvm, object);
rotation_hotbar_swap_pending = 0;
set_entity_rotation(jvm, object, rotation_hotbar_saved_current_rotation);
set_entity_prev_rotation(jvm, object, rotation_hotbar_saved_current_rotation);
}
struct GRef { void *padding_00; jobject ref; };
extern "C" void tick_post_frame(Jvm **jj, GRef *g) {
if (g->ref) JNI(*jj)->DeleteGlobalRef((*jj)->env, g->ref);
g->ref = nullptr;
}
// claim / release the hotbar for a module
extern "C" bool claim_release_hotbar_module(void *owner) {
void *cur = rotation_hotbar_slot_owner;
if (cur == owner) return true;
if (cur) return false;
rotation_hotbar_slot_owner = owner;
return true;
}
extern "C" void release_owner(void *owner) {
if (rotation_hotbar_slot_owner == owner) rotation_hotbar_slot_owner = nullptr;
}
// slot index -> (column, row) of the inventory grid; hotbar slots get row 1
extern "C" F2 slot_index(unsigned slot) {
F2 r;
if (slot <= 8) {
r.b = 1.0f;
r.a = (float)((int)slot - 5);
} else {
unsigned q = slot / 9;
r.b = (float)(int)(slot - q * 9);
r.a = (float)(q + 3);
}
return r;
}
// append a letter/digit/space to a 100-byte text buffer
extern "C" void key_name_append(char *buf, char character) {
unsigned long long n = libc_strlen(buf);
if (n > 99) return;
if ((unsigned char)((character & 0xdf) + 0xbf) >= 0x1a && (unsigned char)(character - '0') >= 10 && character != ' ') return;
buf[n] = character;
}
struct IdVec { void *v; char hdr[4]; int count; };
struct IdEnt { void *p; int id; int z; };
extern "C" int id_vector_count(IdVec *id_vector) { return id_vector->count; }
extern "C" void *id_vector_get(IdVec *id_vector, void *key) { return *element_at(id_vector->v, 0x10, id_vector->hdr, key); }
extern "C" int compare_id_entry(void *left, IdEnt *right, IdEnt *b) { return right->id - b->id; }
extern "C" bool id_vector_insert(IdVec *id_vector, void *key) {
int id = key_name_to_code(key);
if (id != -1) {
IdEnt e = {key_code_to_index(id), id, 0};
vector_find_insert(id_vector, 0x10, id_vector->hdr, &e, (void *)compare_id_entry);
}
return id != -1;
}
extern "C" void *id_vector_remove(IdVec *id_vector, void *key) { return vector_erase(id_vector->v, 0x10, id_vector->hdr, key); }
extern "C" void id_vector_clear(IdVec *id_vector) { id_vector->count = 0; }
extern "C" bool id_vector_contains(IdVec *id_vector, int id) {
IdEnt e = {nullptr, id, 0};
return binary_search(id_vector->v, 0x10, id_vector->hdr, &e, (void *)compare_id_entry) != nullptr;
}
// entity flags kept in a float field as a NaN tagged 0xb315xxxx
#define TAG 0xb3150000u
static inline __attribute__((always_inline)) unsigned bits(float f) { return __builtin_bit_cast(unsigned, f); }
extern "C" unsigned entity_flags_get(void *jvm, void *entity) {
unsigned v = bits(java_get_entity_player_speed_on_ground(jvm, entity));
return (v & 0xffff0000) == TAG ? (v & 0xffff) : 0;
}
extern "C" bool entity_flags_has(void *jvm, void *entity, int mask) {
unsigned v = bits(java_get_entity_player_speed_on_ground(jvm, entity));
unsigned f = (v & 0xffff0000) == TAG ? (v & 0xffff) : 0;
return (f & mask) != 0;
}
extern "C" void entity_flags_set(void *jvm, void *entity, int mask) {
unsigned v = bits(java_get_entity_player_speed_on_ground(jvm, entity));
unsigned cur = (v & 0xffff0000) == TAG ? v : TAG;
java_set_entity_player_speed_on_ground(jvm, entity, __builtin_bit_cast(float, ((cur | mask) & 0xffff) | TAG));
}
extern "C" void entity_flags_clear(void *jvm, void *entity, int mask) {
unsigned v = bits(java_get_entity_player_speed_on_ground(jvm, entity));
unsigned cur = (v & 0xffff0000) == TAG ? (v & 0xffff) : TAG;
java_set_entity_player_speed_on_ground(jvm, entity, __builtin_bit_cast(float, (~mask & cur) | TAG));
}
// switch to the best hotbar slot (exact item id first, else best score), once per module
extern "C" void switch_best_hotbar_slot(Ctx *ctx, void *owner, unsigned char force, int (*score)(int)) {
if (rotation_hotbar_module_that_switched_slot != nullptr) return;
if (!force && rotation_hotbar_item_id == 0) return;
void *inv = java_get_entity_player_inventory(ctx->jvm, ctx->player);
int want = rotation_hotbar_item_id;
int start = java_get_inventory_player_current_item(ctx->jvm, inv) + rotation_hotbar_item_pending;
void *items = java_get_inventory_player_main_inventory(ctx->jvm, inv);
JNIEnv *env = ((Jvm *)ctx->jvm)->env;
int best = -1, bestScore = -1;
#pragma clang loop unroll(disable)
for (int i = 0; i != 9; i++) {
int slot = (start + i) % 9;
jobject st = (*env)->GetObjectArrayElement(env, (jobjectArray)items, slot);
if (!st) continue;
void *item = java_call_item_stack_get_item(ctx->jvm, st);
if (!item) continue;
int item_id = java_call_static_item_get_id_from_item(ctx->jvm, item);
if (want != 0 && item_id == want) { best = slot; break; }
int s = score(item_id);
if (s > bestScore) { bestScore = s; best = slot; }
}
if (best == -1) return;
if (!claim_release_hotbar_module(owner)) return;
java_set_inventory_player_current_item(ctx->jvm, inv, best);
rotation_hotbar_module_that_switched_slot = owner;
}
// block-id preference for scaffolding: 2 = solid building block, 1 = acceptable, -1 = no
extern "C" int block_id_preference_scaffolding(int id) {
switch (id) {
case 1: case 2: case 3: case 35: case 45: return 2;
case 5: case 17: case 20: case 24: case 87: case 121: case 159: case 162: return 1;
default: return -1;
}
}
extern "C" void track_owner(Ctx *ctx, void *owner, unsigned char force) {
switch_best_hotbar_slot(ctx, owner, force, block_id_preference_scaffolding);
}
extern "C" void release_tracked_owner() {
if (rotation_hotbar_module_that_switched_slot) {
release_owner(rotation_hotbar_module_that_switched_slot);
rotation_hotbar_module_that_switched_slot = nullptr;
}
refill_state_ticks_since_swap++;
rotation_hotbar_item_id = 0;
rotation_hotbar_item_pending = 0;
}
// ---- "moving toward the target?" test, computed once per tick (state bit 0) ----
struct V3a { double x, y, z; };
FN(void *, java_get_minecraft_object_mouse_over, (void *jvm, void *minecraft));
FN(int, java_get_moving_object_position_type_ordinal, (void *jvm, void *moving_object_position));
FN(void *, java_get_moving_object_position_hit_vec, (void *jvm, void *moving_object_position));
FN(V3a, java_get_vec3_x_coord, (void *jvm, void *vec3));
FN(V3a, java_get_entity_pos_x, (void *jvm, void *entity));
FN(unsigned char, java_variant, (void *jvm));
FN(V3a, vec3_add_eye_height, (V3a v));
FN(unsigned char, java_get_entity_on_ground, (void *jvm, void *entity));
FN(float, java_get_entity_living_base_land_movement_factor, (void *jvm, void *entity_living_base));
FN(void *, get_game_settings, (void *jvm, void *object));
FN(void *, java_get_game_settings_forward_key, (void *jvm, void *game_settings));
FN(void *, java_get_game_settings_back_key, (void *jvm, void *game_settings));
FN(void *, java_get_game_settings_left_key, (void *jvm, void *game_settings));
FN(void *, java_get_game_settings_right_key, (void *jvm, void *game_settings));
FN(unsigned char, is_key_down, (void *jvm, void *object));
FN(float, java_get_entity_living_base_move_forward, (void *jvm, void *entity_living_base));
FN(float, java_get_entity_living_base_move_strafing, (void *jvm, void *entity_living_base));
FN(V3a, horizontal_motion, (unsigned long long rot, float fwd, float strafe, float speed));
FN(V3a, java_get_entity_motion_x, (void *jvm, void *entity));
FN(V3a, vec3_add, (V3a a, V3a b));
FN(double, vec3_distance_sq, (V3a a, V3a b));
extern "C" const float airborne_movement_speed;
extern "C" const double attack_motion_slowdown_factor;
struct TickState { unsigned char done; char padding_01[0x17]; unsigned char result; };
extern "C" void tick_pre_click(Ctx *ctx, TickState *st) {
if (st->done & 1) return;
unsigned char r = 1;
void *hit = java_get_minecraft_object_mouse_over(ctx->jvm, ctx->minecraft);
if (hit && java_get_moving_object_position_type_ordinal(ctx->jvm, hit) == 2) {
V3a vec3_x_coord = java_get_vec3_x_coord(ctx->jvm, java_get_moving_object_position_hit_vec(ctx->jvm, hit));
V3a eye = java_get_entity_pos_x(ctx->jvm, ctx->player);
if (!java_variant(ctx->jvm)) eye = vec3_add_eye_height(eye);
float speed = java_get_entity_on_ground(ctx->jvm, ctx->player) ? java_get_entity_living_base_land_movement_factor(ctx->jvm, ctx->player) : airborne_movement_speed;
void *jvm = ctx->jvm;
void *inv = get_game_settings(jvm, ctx->minecraft);
void *game_settings_key_bindings = java_get_game_settings_forward_key(jvm, inv), *key_binding = java_get_game_settings_back_key(jvm, inv), *i2 = java_get_game_settings_left_key(jvm, inv), *i3 = java_get_game_settings_right_key(jvm, inv);
is_key_down(jvm, game_settings_key_bindings); is_key_down(jvm, key_binding); is_key_down(jvm, i2); is_key_down(jvm, i3);
unsigned long long rot = get_entity_rotation(ctx->jvm, ctx->player);
float fwd = java_get_entity_living_base_move_forward(ctx->jvm, ctx->player);
V3a mv = horizontal_motion(rot, fwd, java_get_entity_living_base_move_strafing(ctx->jvm, ctx->player), speed);
V3a motion = java_get_entity_motion_x(ctx->jvm, ctx->player);
V3a entity_motion_x = java_get_entity_motion_x(ctx->jvm, ctx->player);
entity_motion_x.x *= attack_motion_slowdown_factor;
entity_motion_x.z *= attack_motion_slowdown_factor;
V3a da = vec3_add(motion, mv);
V3a db = vec3_add(entity_motion_x, mv);
V3a pa = vec3_add(eye, da);
V3a pb = vec3_add(eye, db);
double dB = vec3_distance_sq(pb, vec3_x_coord);
r = vec3_distance_sq(pa, vec3_x_coord) > dB;
}
st->result = r;
st->done |= 1;
}
// ---- pick the aim target: crosshair entity, forced target, else nearest valid entity in FOV ----
struct AB { V3a min, max; };
struct Rot2 { float yaw, pitch; };
FN(void *, java_get_moving_object_position_entity_hit, (void *jvm, void *moving_object_position));
FN(void *, java_jni_is_instance_of_entity_other_player_mp, (void *jvm, void *candidate));
FN(float, java_call_entity_living_base_get_health, (void *jvm, void *entity_living_base));
FN(void *, get_refill_state, ());
FN(void *, java_get_world_player_entities, (void *jvm, void *world));
FN(int, java_call_list_size, (void *jvm, void *list));
FN(void *, java_call_list_get, (void *jvm, void *list, int i));
FN(unsigned char, java_get_entity_player_sleeping, (void *jvm, void *entity_player));
FN(void *, java_get_entity_bounding_box, (void *jvm, void *entity));
FN(AB, java_get_axis_aligned_bb_min_x, (void *jvm, void *bounding_box));
FN(AB, aabb_expand, (AB b, double d));
FN(V3a, aabb_clamp_point, (AB b, V3a p));
FN(Rot2, rot_look_at, (V3a from, V3a to));
FN(float, wrap_angle, (float x));
extern "C" const double tenth_in_rotation_and_hotbar_ticks, search_range_squared;
extern "C" const float threshold_4900;
struct Target { int z0; int padding_04; jobject ref; double dist; };
extern "C" bool target_clear(Ctx *ctx, Target *target) {
target->z0 = 0;
if (!ctx->world) {
if (target->ref) { JNIEnv *env = ((Jvm *)ctx->jvm)->env; (*env)->DeleteGlobalRef(env, target->ref); }
target->ref = 0;
return true;
}
V3a eye = java_get_entity_pos_x(ctx->jvm, ctx->player);
if (!java_variant(ctx->jvm)) eye = vec3_add_eye_height(eye);
void *ent;
JNIEnv *env;
void *hit = java_get_minecraft_object_mouse_over(ctx->jvm, ctx->minecraft);
if (hit && java_get_moving_object_position_type_ordinal(ctx->jvm, hit) == 2 && (ent = java_jni_is_instance_of_entity_other_player_mp(ctx->jvm, java_get_moving_object_position_entity_hit(ctx->jvm, hit))) != 0 &&
!entity_flags_has(ctx->jvm, ent, 7) && java_call_entity_living_base_get_health(ctx->jvm, ent) > 0.0f) {
env = ((Jvm *)ctx->jvm)->env;
} else {
ent = get_refill_state();
env = ((Jvm *)ctx->jvm)->env;
if (!ent) {
Rot2 rot = __builtin_bit_cast(Rot2, get_entity_rotation(ctx->jvm, ctx->player));
void *list = java_get_world_player_entities(ctx->jvm, ctx->world);
void *best = 0;
double bestD = search_range_squared;
for (int i = 0; i < java_call_list_size(ctx->jvm, list); i++) {
void *element = java_call_list_get(ctx->jvm, list, i);
JNIEnv *e2 = ((Jvm *)ctx->jvm)->env;
if ((*e2)->IsSameObject(e2, (jobject)ctx->player, (jobject)element)) continue;
if (entity_flags_has(ctx->jvm, element, 7)) continue;
if (!(java_call_entity_living_base_get_health(ctx->jvm, element) > 0.0f)) continue;
if (java_get_entity_player_sleeping(ctx->jvm, element)) continue;
AB bb = aabb_expand(java_get_axis_aligned_bb_min_x(ctx->jvm, java_get_entity_bounding_box(ctx->jvm, element)), tenth_in_rotation_and_hotbar_ticks);
double d = vec3_distance_sq(eye, aabb_clamp_point(bb, eye));
if (d > bestD) continue;
Rot2 ang = rot_look_at(eye, java_get_entity_pos_x(ctx->jvm, element));
float dp = ang.pitch - rot.pitch;
float dy = wrap_angle(ang.yaw - rot.yaw);
if (dy * dy + dp * dp > threshold_4900) continue;
bestD = d;
best = element;
}
if (best) target->dist = bestD;
env = ((Jvm *)ctx->jvm)->env;
if (target->ref) (*env)->DeleteGlobalRef(env, target->ref);
if (!best) {
target->ref = 0;
return true;
}
jobject r = (*env)->NewGlobalRef(env, (jobject)best);
if (!r) return false;
target->ref = r;
return true;
}
}
if (target->ref) (*env)->DeleteGlobalRef(env, target->ref);
jobject r = (*env)->NewGlobalRef(env, (jobject)ent);
if (r) target->ref = r;
AB bb = aabb_expand(java_get_axis_aligned_bb_min_x(ctx->jvm, java_get_entity_bounding_box(ctx->jvm, ent)), tenth_in_rotation_and_hotbar_ticks);
target->dist = vec3_distance_sq(eye, aabb_clamp_point(bb, eye));
return true;
}
@@ -0,0 +1,345 @@
// 0x29000: silent rotation state, mouse step, Minecraft colour codes and GL vertex-array helpers.
// cflags: /Ob1 /clang:-fno-slp-vectorize
#include "slinky/engine.h"
#include "slinky/jcall.h"
typedef void (*GlPtrFn)(int size, int type, int stride, const void *p); // glVertexPointer-style
typedef void (*GlEnableFn)(int cap);
extern "C" GlPtrFn *ptr_to_pGlVertexPointer; // cell -> fn ptr (size,type,stride,ptr)
extern "C" GlEnableFn *ptr_to_pGlEnableClientState; // cell -> glEnableClientState
extern "C" GlEnableFn *ptr_to_pGlDisableClientState;
extern "C" void (**ptr_to_pGlDrawElements)(int, int, int, const void *);
extern "C" char gl_array_xyz_float_data[], gl_array_color1_indices[], gl_array_color2_indices[], gl_array_xy_float_data[], gl_array_color3_indices[], gl_array_color4_indices[],
gl_array_xy_float2_data[], gl_array_color5_indices[], gl_array_color6_indices[];
#define GL_VEC(name, tbl, n, cell, ptr) \
extern "C" void name() { (*tbl)(n, 0x1406, 0, ptr); (*cell)(0x8074); }
#define GL_COL(name, n, size, ptr) \
extern "C" void name() { auto f = *ptr_to_pGlDrawElements; asm("" : "+r"(f)); f(n, size, 0x1401, ptr); }
GL_VEC(gl_array_xyz_float, ptr_to_pGlVertexPointer, 3, ptr_to_pGlEnableClientState, gl_array_xyz_float_data)
GL_COL(gl_array_color1, 5, 0xe, gl_array_color1_indices)
GL_COL(gl_array_color2, 1, 0x18, gl_array_color2_indices)
GL_VEC(gl_array_xy_float, ptr_to_pGlVertexPointer, 2, ptr_to_pGlEnableClientState, gl_array_xy_float_data)
GL_COL(gl_array_color3, 5, 4, gl_array_color3_indices)
GL_COL(gl_array_color4, 2, 4, gl_array_color4_indices)
GL_VEC(gl_array_xy_float2, ptr_to_pGlVertexPointer, 2, ptr_to_pGlEnableClientState, gl_array_xy_float2_data)
GL_COL(gl_array_color5, 6, 4, gl_array_color5_indices)
GL_COL(gl_array_color6, 2, 4, gl_array_color6_indices)
extern "C" void disable_vertex_array_state() { (*ptr_to_pGlDisableClientState)(0x8074); }
struct Vec3 { double x, y, z; };
FN(void *, java_new_vec3_init, (void *jvm, Vec3 v));
FN(void *, java_world_ray_trace_blocks, (void *jvm, void *object, void *other_object, void *d, bool e, bool f, bool g));
FN(void *, get_game_settings, (void *jvm, void *object));
FN(void *, java_get_game_settings_attack_key, (void *jvm, void *game_settings));
FN(int, get_event_key, (void *jvm, void *object));
FN(Vec3, vec3_make, (double x, double y, double z));
extern "C" bool set_vertex_pointer(void *jvm, void *object, Vec3 *p, Vec3 *q) {
void *from = java_new_vec3_init(jvm, *p);
void *s = java_new_vec3_init(jvm, *q);
return java_world_ray_trace_blocks(jvm, object, from, s, false, true, true) == nullptr;
}
struct Ctx3 { void *jvm; char padding_08[0x10]; void *minecraft; char padding_20[0x10]; char *key_state_table; };
extern "C" bool vertex_pointer_current(Ctx3 *c) {
void *game_settings = get_game_settings(c->jvm, c->minecraft);
game_settings = java_get_game_settings_attack_key(c->jvm, game_settings);
int event_key = get_event_key(c->jvm, game_settings);
bool f = event_key == -0x63;
return c->key_state_table[f + 1] != 0;
}
// ---- rotation state (silent aim / mouse emulation)
struct Rot { float yaw, pitch; };
FN(Rot, rot_add, (Rot a, Rot b));
FN(Rot, rot_make, (float yaw, float pitch));
FN(float, wrap_angle, (float a)); // wrap to [-180, 180)
FN(void *, get_game_settings, (void *j, void *x));
FN(float, java_get_game_settings_mouse_sensitivity, (void *jvm, void *settings)); // mouse sensitivity
FN(void, pack_flags, (void *jvm, bool on));
FN(Rot, get_entity_rotation, (void *jvm, void *entity));
FN(Rot, get_entity_prev_rotation, (void *jvm, void *entity));
FN(void, set_entity_rotation, (void *jvm, void *entity, Rot rotation));
FN(void, set_entity_prev_rotation, (void *jvm, void *entity, Rot rotation));
extern "C" unsigned rotation_priority_peak;
extern "C" Rot silent_rotation_offset, saved_entity_rotation, saved_entity_prev_rotation, silent_rotation_current;
extern "C" int silent_rotation_age_ticks;
extern "C" float mouse_sensitivity_step;
extern "C" unsigned char rotation_reset_requested, rotation_reset_done, silent_rotation_active_flag, render_rotation_dirty;
extern "C" Rot render_rotation;
extern "C" const float factor_0_6_in_silent_rotation_and_chat_colors, offset_0_2, factor_8_in_silent_rotation_and_chat_colors, factor_0_15_in_silent_rotation_and_chat_colors;
extern "C" bool update_rotation_priority_peak(unsigned v) {
unsigned old = rotation_priority_peak;
if (v >= old) rotation_priority_peak = v;
return v >= old;
}
extern "C" void apply_silent_rotation(Rot rotation) {
silent_rotation_offset = rot_add(silent_rotation_offset, rotation);
silent_rotation_age_ticks = 0;
}
extern "C" Rot get_silent_rotation_offset() { return silent_rotation_offset; }
// per-tick: recompute the mouse step from the sensitivity (f = s*0.6+0.2; step = f^3*8*0.15)
extern "C" void update_mouse_sensitivity_step(void *jvm, void *object) {
float f = java_get_game_settings_mouse_sensitivity(jvm, get_game_settings(jvm, object)) * factor_0_6_in_silent_rotation_and_chat_colors + offset_0_2;
mouse_sensitivity_step = f * f * f * factor_8_in_silent_rotation_and_chat_colors * factor_0_15_in_silent_rotation_and_chat_colors;
if (silent_rotation_age_ticks++ > 0) silent_rotation_offset = rot_make(0.0f, 0.0f);
rotation_priority_peak = 0;
}
extern "C" void request_rotation_reset_if_flagged(void **j) {
if (rotation_reset_requested == 1) {
pack_flags(*j, true);
rotation_reset_done = 1;
}
}
extern "C" void set_silent_rotation_active(void *jvm, void *object, bool save) {
Rot *a, *b;
if (save) {
saved_entity_rotation = get_entity_rotation(jvm, object);
saved_entity_prev_rotation = get_entity_prev_rotation(jvm, object);
a = b = &silent_rotation_current;
} else {
b = &saved_entity_prev_rotation;
a = &saved_entity_rotation;
}
set_entity_rotation(jvm, object, *a);
set_entity_prev_rotation(jvm, object, *b);
}
extern "C" unsigned char is_silent_rotation_active() { return silent_rotation_active_flag; }
extern "C" Rot get_silent_rotation() { return silent_rotation_current; }
extern "C" void set_render_rotation(Rot rotation) {
float d = wrap_angle(rotation.yaw - silent_rotation_current.yaw);
render_rotation_dirty = 1;
silent_rotation_current.yaw = d + silent_rotation_current.yaw;
silent_rotation_current.pitch = rotation.pitch;
}
extern "C" Rot get_render_rotation() { return render_rotation; }
// Minecraft formatting colour code ('0'-'9', 'a'-'e', anything else white) -> RGB
struct F2 { float a, b; };
struct Col { F2 rg; float b; };
extern "C" const F2 color_rg_1_1, color_rg_0p33_0p33, color_rg_0p67_0p67, color_rg_1_0p33, color_rg_0p33_1, color_rg_0_0p67, color_rg_1_0p67;
extern "C" const float minecraft_colour_dark_component, one_in_silent_rotation_and_chat_colors, minecraft_colour_bright_component; // 0.33, 1.0, 0.67
extern "C" Col minecraft_color_code_to_rgb(unsigned short ch) {
Col c;
F2 z = {0.0f, 0.0f};
switch (ch) {
case '0': c.rg = z; c.b = 0.0f; break;
case '1': c.rg = z; c.b = minecraft_colour_bright_component; break;
case '2': c.rg = color_rg_0_0p67; c.b = 0.0f; break;
case '3': c.rg = color_rg_0_0p67; c.b = minecraft_colour_bright_component; break;
case '4': c.rg.a = minecraft_colour_bright_component; c.rg.b = 0.0f; c.b = 0.0f; break;
case '5': c.rg.a = minecraft_colour_bright_component; c.rg.b = 0.0f; c.b = minecraft_colour_bright_component; break;
case '6': c.rg = color_rg_1_0p67; c.b = 0.0f; break;
case '7': c.rg = color_rg_0p67_0p67; c.b = minecraft_colour_bright_component; break;
case '8': c.rg = color_rg_0p33_0p33; c.b = minecraft_colour_dark_component; break;
case '9': c.rg = color_rg_0p33_0p33; c.b = one_in_silent_rotation_and_chat_colors; break;
case 'a': c.rg = color_rg_0p33_1; c.b = minecraft_colour_dark_component; break;
case 'b': c.rg = color_rg_0p33_1; c.b = one_in_silent_rotation_and_chat_colors; break;
case 'c': c.rg = color_rg_1_0p33; c.b = minecraft_colour_dark_component; break;
case 'd': c.rg = color_rg_1_0p33; c.b = one_in_silent_rotation_and_chat_colors; break;
case 'e': c.rg = color_rg_1_1; c.b = minecraft_colour_dark_component; break;
default: c.rg = color_rg_1_1; c.b = one_in_silent_rotation_and_chat_colors; break;
}
return c;
}
// ---- chat output
FN(void *, get_minecraft, (void *jvm));
FN(void *, get_local_player, (void *jvm, void *minecraft));
FN(int, libc_vsnprintf, (char *buf, unsigned long long n, const char *fmt, char *va)); // vsnprintf
FN(void *, java_new_chat_component_text_init, (void *jvm, void *str)); // new ChatComponentText
FN(void, java_call_entity_player_sp_add_chat_message, (void *jvm, void *entity_player_sp, void *msg));
extern "C" char log_message_buffer[0x100];
extern "C" void jni_throw_format(Jvm *jvm, const char *fmt, ...) {
void *minecraft = get_minecraft(jvm);
void *player = get_local_player(jvm, minecraft);
if (!player) return;
__builtin_va_list va;
__builtin_va_start(va, fmt);
libc_vsnprintf(log_message_buffer, 0x100, fmt, va);
JNIEnv *env = jvm->env;
jstring chat_component_text = (*env)->NewStringUTF(env, log_message_buffer);
if (!chat_component_text) return;
void *t = java_new_chat_component_text_init(jvm, chat_component_text);
if (!t) return;
java_call_entity_player_sp_add_chat_message(jvm, player, t);
}
// ---- movement helpers
FN(float, libc_sinf, (float x)); // sinf
FN(float, libc_cosf, (float x)); // cosf
extern "C" const float movement_input_friction, threshold_0_0001, one_in_silent_rotation_and_chat_colors_a, threshold_1_in_silent_rotation_and_chat_colors_b;
extern "C" const double pi_in_silent_rotation_and_chat_colors, half_turn_degrees_in_silent_rotation_and_chat_colors;
// horizontal motion for (forward, strafe) input at the given yaw (EntityLivingBase.moveFlying)
extern "C" Vec3 horizontal_motion(Rot rotation, float fwd, float strafe, float speed) {
fwd *= movement_input_friction;
strafe *= movement_input_friction;
float m = strafe * strafe + fwd * fwd;
if (threshold_0_0001 > m) {
Vec3 z = vec3_make(0.0, 0.0, 0.0);
return z;
}
float d = __builtin_sqrtf(m);
float k = speed * one_in_silent_rotation_and_chat_colors_a / (threshold_1_in_silent_rotation_and_chat_colors_b > d ? threshold_1_in_silent_rotation_and_chat_colors_b : d);
strafe *= k;
fwd *= k;
float a = (float)((double)rotation.yaw * pi_in_silent_rotation_and_chat_colors / half_turn_degrees_in_silent_rotation_and_chat_colors);
float sn = libc_sinf(a);
float cs = libc_cosf(a);
Vec3 v;
float t = fwd * sn;
v.x = (double)(strafe * cs - t);
v.y = 0.0;
v.z = (double)(fwd * cs + strafe * sn);
return v;
}
// ---- world -> screen projection with the captured GL matrices
struct View { char padding_00[0x20]; float w, h; const float *mv, *proj; };
extern "C" const float one_in_silent_rotation_and_chat_colors_c, offset_0_5_in_silent_rotation_and_chat_colors; // 1.0, 0.5
extern "C" bool project_point_ex(View *v, Vec3 p, F2 *out, float *depth) {
float x = (float)p.x, y = (float)p.y, z = (float)p.z;
const float *m = v->mv, *proj = v->proj;
float e0 = m[0] * x + m[4] * y + m[8] * z + m[12];
float e1 = m[1] * x + m[5] * y + m[9] * z + m[13];
float e2 = m[2] * x + m[6] * y + m[10] * z + m[14];
float e3 = m[3] * x + m[7] * y + m[11] * z + m[15];
float w = proj[3] * e0 + proj[7] * e1 + proj[11] * e2 + proj[15] * e3;
if (w == 0.0f) return false;
float k = one_in_silent_rotation_and_chat_colors_c / w * offset_0_5_in_silent_rotation_and_chat_colors;
float zz = (proj[2] * e0 + proj[6] * e1 + proj[10] * e2 + proj[14] * e3) * k + offset_0_5_in_silent_rotation_and_chat_colors;
if (0.0f >= zz || zz >= one_in_silent_rotation_and_chat_colors_c) return false;
float yy = (proj[1] * e0 + proj[5] * e1 + proj[9] * e2 + proj[13] * e3) * k + offset_0_5_in_silent_rotation_and_chat_colors;
float xx = (proj[0] * e0 + proj[4] * e1 + proj[8] * e2 + proj[12] * e3) * k + offset_0_5_in_silent_rotation_and_chat_colors;
out->a = xx * v->w;
out->b = v->h - yy * v->h;
if (depth) *depth = k;
return true;
}
extern "C" bool project_point(View *v, Vec3 *p, F2 *out) { return project_point_ex(v, *p, out, nullptr); }
// ---- apply the pending mouse delta in whole sensitivity steps
FN(float, libc_truncf, (float v)); // truncf
FN(void, java_entity_player_sp_render_arm_pitch, (void *jvm, void *object, Rot rotation));
FN(void, java_entity_player_sp_prev_render_arm_pitch, (void *jvm, void *object, Rot rotation));
typedef int v4i __attribute__((vector_size(16), aligned(16)));
typedef float v4f __attribute__((vector_size(16), aligned(16)));
extern "C" const v4i gl_vertex_state_sign_mask; // sign mask
extern "C" void set_rotation_active(void *jvm, void *object, bool render) {
if (!object) return;
float step = mouse_sensitivity_step;
if (step == 0.0f) return;
float neg = -step;
float dx = 0.0f, dy = 0.0f;
float yaw = silent_rotation_offset.yaw;
if (neg >= yaw || yaw >= step) dx = libc_truncf(yaw / step) * step;
silent_rotation_offset.yaw = yaw - dx;
float pitch = silent_rotation_offset.pitch;
if (neg >= pitch || pitch >= step) dy = libc_truncf(pitch / step) * step;
silent_rotation_offset.pitch = pitch - dy;
if (dx == 0.0f && dy == 0.0f) return;
Rot rotation = rot_add(get_entity_rotation(jvm, object), Rot{dx, dy});
set_entity_rotation(jvm, object, rotation);
set_entity_prev_rotation(jvm, object, rotation);
if (!render) return;
java_entity_player_sp_prev_render_arm_pitch(jvm, object, rotation);
java_entity_player_sp_render_arm_pitch(jvm, object, rotation);
}
// ---- movement fix: remap the pressed WASD keys to the server-side yaw
FN(bool, is_in_game, (void *jvm, void *minecraft));
FN(void *, java_get_game_settings_forward_key, (void *jvm, void *game_settings)); // keyBindForward
FN(void *, java_get_game_settings_back_key, (void *jvm, void *game_settings)); // keyBindBack
FN(void *, java_get_game_settings_left_key, (void *jvm, void *game_settings)); // keyBindLeft
FN(void *, java_get_game_settings_right_key, (void *jvm, void *game_settings)); // keyBindRight
FN(bool, is_key_down, (void *jvm, void *kb)); // pressed
FN(void, java_set_key_binding_pressed, (void *jvm, void *kb, bool pressed));
FN(float, libc_roundf, (float v)); // roundf
extern "C" unsigned char gl_vertex_state_captured_key_bits_fwd_back; // captured key bits: fwd, back, left, right
extern "C" const float divisor_45; // 45
extern "C" const unsigned short gl_vertex_state_octant_bytes[8]; // octant -> (fwd, strafe) bytes
extern "C" void pack_flags(void *j, bool apply) {
void *minecraft = get_minecraft(j);
if (!is_in_game(j, minecraft)) return;
void *game_settings = get_game_settings(j, minecraft);
void *game_settings_key_bindings = java_get_game_settings_forward_key(j, game_settings), *key_binding = java_get_game_settings_back_key(j, game_settings), *kl = java_get_game_settings_left_key(j, game_settings), *kr = java_get_game_settings_right_key(j, game_settings);
if (!apply) {
java_set_key_binding_pressed(j, game_settings_key_bindings, gl_vertex_state_captured_key_bits_fwd_back & 1);
java_set_key_binding_pressed(j, key_binding, (gl_vertex_state_captured_key_bits_fwd_back >> 1) & 1);
java_set_key_binding_pressed(j, kl, (gl_vertex_state_captured_key_bits_fwd_back >> 2) & 1);
java_set_key_binding_pressed(j, kr, (gl_vertex_state_captured_key_bits_fwd_back >> 3) & 1);
return;
}
unsigned char a = is_key_down(j, game_settings_key_bindings);
unsigned char b = is_key_down(j, key_binding);
unsigned char bits = (unsigned char)(b + b) | a;
unsigned char c = is_key_down(j, kl);
bits = (unsigned char)(c << 2) | bits;
unsigned char d = is_key_down(j, kr);
gl_vertex_state_captured_key_bits_fwd_back = (unsigned char)(d << 3) | bits;
void *player = get_local_player(j, minecraft);
float delta = wrap_angle(get_entity_rotation(j, player).yaw - render_rotation.yaw);
unsigned f = gl_vertex_state_captured_key_bits_fwd_back;
signed char fwd = (signed char)((f & 1) + ((signed char)(f << 6) >> 7));
signed char str = (signed char)(((signed char)(f << 4) >> 7) + ((f >> 2) & 1));
unsigned short mv;
if (fwd == 0 && str == 0) {
mv = 0;
} else {
int dir;
if (fwd == 0) dir = str == 1 ? 2 : (str == -1 ? 6 : 0);
else if (fwd == -1) dir = 5 - (unsigned char)(str + 1);
else if (str == 0) dir = 0;
else dir = str == 1 ? 1 : (str == -1 ? 7 : 0);
mv = gl_vertex_state_octant_bytes[((int)libc_roundf(delta / divisor_45) + dir) & 7];
}
unsigned hi = mv >> 8;
java_set_key_binding_pressed(j, game_settings_key_bindings, (mv & 0xff) == 1);
java_set_key_binding_pressed(j, key_binding, (mv & 0xff) == 0xff);
java_set_key_binding_pressed(j, kl, hi == 1);
java_set_key_binding_pressed(j, kr, hi == 0xff);
}
// ---- silent-rotation controller: start (save real rotation) / stop (restore smoothly)
extern "C" unsigned char use_target_rotation;
extern "C" void apply_player_rotation(Ctx *ctx, bool on, unsigned char moveFix, unsigned char useTarget, Rot target) {
if (rotation_reset_done == 1) {
pack_flags(ctx->jvm, false);
rotation_reset_done = 0;
rotation_reset_requested = 0;
}
if (on) {
if (!(silent_rotation_active_flag & 1) && ctx->player) {
silent_rotation_active_flag = 1;
silent_rotation_current = get_entity_rotation(ctx->jvm, ctx->player);
}
use_target_rotation = useTarget;
render_rotation = target;
rotation_reset_requested = moveFix;
return;
}
if (!(silent_rotation_active_flag & 1)) return;
silent_rotation_active_flag = 0;
Rot *src = use_target_rotation ? &render_rotation : &silent_rotation_current;
float yaw = src->yaw, pitch = src->pitch;
float cur = get_entity_rotation(ctx->jvm, ctx->player).yaw;
Rot rotation = {wrap_angle(yaw - cur) + cur, pitch};
set_entity_rotation(ctx->jvm, ctx->player, rotation);
set_entity_prev_rotation(ctx->jvm, ctx->player, rotation);
java_entity_player_sp_prev_render_arm_pitch(ctx->jvm, ctx->player, rotation);
java_entity_player_sp_render_arm_pitch(ctx->jvm, ctx->player, rotation);
}
// wrap an angle into range when it lies outside (-limit, limit); limit = mouse_sensitivity_step
FN(float, libc_truncf, (float x)); // floorf
extern "C" void clamp_angle(float *angle) {
float v = *angle, limit = mouse_sensitivity_step;
float wrap = 0.0f;
if (v <= -limit || v >= limit) wrap = libc_truncf(v / limit) * limit;
*angle = v - wrap;
}
+21
View File
@@ -0,0 +1,21 @@
// 0x97c70: 1D gradient noise (Perlin style) on a 256-entry permutation table; used by the aim assist's humanising jitter.
// Library code of the image (just past the NanoVG range); translated from the listing, not byte-matched.
extern "C" const unsigned char noise_permutation_table[256]; // permutation table
extern "C" const float offset_neg_1_in_noise, factor_6_in_noise, offset_neg_15_in_noise, offset_10_in_noise, factor_0_188; // -1, 6, -15, 10, output scale
// gradient at lattice point with hash h: magnitude 1..8, sign from bit 3, times the distance x
static inline float grad(unsigned char h, float x) {
float g = (float)((h & 7) + 1);
if (h & 8) g = -g;
return g * x;
}
extern "C" float noise_1d(float x) {
int i = (int)x;
if ((float)i > x) i--; // floor
float f = x - (float)i;
float fade = f * f * f * ((factor_6_in_noise * f + offset_neg_15_in_noise) * f + offset_10_in_noise); // 6f^5 - 15f^4 + 10f^3
float g0 = grad(noise_permutation_table[i & 255], f);
float g1 = grad(noise_permutation_table[(i + 1) & 255], f + offset_neg_1_in_noise);
return (fade * (g1 - g0) + g0) * factor_0_188;
}
+228
View File
@@ -0,0 +1,228 @@
// 0xa000: Rot / Rect / float helpers, random numbers
#include "slinky/engine.h"
struct Rot { float yaw, pitch; };
struct Rect { float x, y, w, h; };
FN(float, libc_roundf, (float x));
FN(float, libc_fmodf, (float x, float y));
FN(double, time_now, ());
extern "C" float full_turn_degrees, half_turn_degrees_b, offset_neg_360, threshold_neg_180, threshold_1_in_math_helpers_a, three_constant;
extern "C" double current_time_seconds;
extern "C" __attribute__((noinline)) Rot rot_make_floats(float a, float b) { return Rot{a, b}; }
extern "C" Rect make_rect(float x, float y, float w, float h) { return Rect{x, y, w, h}; }
extern "C" bool point_in_rect(Rect *r, Rot p) {
return p.yaw >= r->x && p.pitch >= r->y && r->x + r->w > p.yaw && r->y + r->h > p.pitch;
}
extern "C" bool rects_intersect(Rect *a, Rect *b) {
if (a->x > b->x + b->w || b->x > a->x + a->w) return false;
if (a->y > b->y + b->h) return false;
return !(b->y > a->y + a->h);
}
typedef float v2f __attribute__((ext_vector_type(2)));
extern "C" Rect inset_rect(Rect r, float d) {
v2f pos = {r.x, r.y}, size = {r.w, r.h};
v2f dd = {d, d};
float d2 = d + d;
v2f d22 = {d2, d2};
pos += dd;
size -= d22;
return Rect{pos.x, pos.y, size.x, size.y};
}
extern "C" Rect round_rect(Rect *r) {
float x0 = r->x, y0 = r->y;
float x = libc_roundf(x0), y = libc_roundf(y0), w = libc_roundf(r->w), h = libc_roundf(r->h);
return Rect{x, y, w, h};
}
extern "C" Rot offset_rect(Rot *p) { return *p; }
extern "C" float limit_rotation_delta(float x, float a, float b) { return (b < x) ? x : (a < b ? a : b); }
extern "C" float wrap_angle(float x) {
float r = libc_fmodf(x, full_turn_degrees);
if (r >= half_turn_degrees_b) return r + offset_neg_360;
if (threshold_neg_180 > r) { r += full_turn_degrees; }
return r;
}
extern "C" float lerp_clamped_float(float a, float b, float t) {
float m = threshold_1_in_math_helpers_a < t ? threshold_1_in_math_helpers_a : t;
return a + m * (b - a);
}
extern "C" float ease_curve(float t) { float sq = t * t; float d = t + t; return (three_constant - d) * sq; }
extern "C" float lerp_unclamped_float(float a, float b, float t) { return a + (b - a) * t; }
extern "C" double lerp_double(double a, double b, double t) { return a + (b - a) * t; }
extern "C" int chunk_coordinate(int x) { return x >> 4; }
extern "C" int block_coordinate(int x) { return x << 4; }
extern "C" void update_frame_time() { current_time_seconds = time_now(); }
// ---- xoshiro128+ random numbers ----
extern "C" unsigned init_table[4];
extern "C" const float float_epsilon_unused_a, one_in_math_helpers_b, float_epsilon_unused_b, random_range_bound_1_in_math_helpers_c, random_walk_range_epsilon_a, random_range_bound_1_in_math_helpers_d, random_walk_range_epsilon_b;
extern "C" const double threshold_0_2, threshold_0_2_in_math_helpers;
typedef int v4i __attribute__((vector_size(16)));
typedef float v4f __attribute__((vector_size(16)));
extern "C" const v4i math_helpers_fabs_masks, math_helpers_fabs_masks_second; // fabs masks
static inline __attribute__((always_inline)) unsigned rng_next() {
unsigned *s = init_table;
unsigned result = s[0] + s[3];
unsigned t = s[1] << 9;
s[2] ^= s[0];
s[3] ^= s[1];
s[1] ^= s[2];
s[0] ^= s[3];
s[2] ^= t;
s[3] = (s[3] << 11) | (s[3] >> 21);
return result;
}
static inline __attribute__((always_inline)) float rng_range(Rot rotation, float scale) {
float d = rotation.pitch - rotation.yaw;
return (float)(rng_next() >> 8) * scale * d + rotation.yaw;
}
static inline __attribute__((always_inline)) float fabs_m(float x, v4i m) {
v4f v = {x, 0, 0, 0};
return ((v4f)((v4i)v & m))[0];
}
extern "C" float random_rotation(Rot rotation) { return rng_range(rotation, float_epsilon_unused_a); }
extern "C" bool random_chance(float p) { return p > rng_range(rot_make_floats(0.0f, one_in_math_helpers_b), float_epsilon_unused_b); }
struct Walk { double t0; float a, b; };
extern "C" __attribute__((noinline)) void walk_randomize(Walk *w, bool both) {
do {
if (both) w->a = rng_range(rot_make_floats(0.0f, random_range_bound_1_in_math_helpers_d), random_walk_range_epsilon_b);
w->b = rng_range(rot_make_floats(0.0f, random_range_bound_1_in_math_helpers_d), random_walk_range_epsilon_b);
} while (threshold_0_2_in_math_helpers > (double)fabs_m(w->a - w->b, math_helpers_fabs_masks_second));
}
// smooth random value in [r.yaw, r.pitch], re-targeted every `period` seconds
extern "C" __attribute__((noinline)) float walk_update(Walk *w, Rot rotation, double period) {
if (current_time_seconds > w->t0 + period) {
w->t0 = current_time_seconds;
if (w->a == 0.0f && w->b == 0.0f) {
walk_randomize(w, true);
} else {
w->a = w->b;
do {
w->b = rng_range(rot_make_floats(0.0f, random_range_bound_1_in_math_helpers_c), random_walk_range_epsilon_a);
} while (threshold_0_2 > (double)fabs_m(w->a - w->b, math_helpers_fabs_masks));
}
}
double t = (current_time_seconds - w->t0) / period;
double u = (double)(w->b - w->a) * t + (double)w->a;
return (float)((double)(rotation.pitch - rotation.yaw) * u + (double)rotation.yaw);
}
extern "C" const float random_range_bound_1_in_math_helpers_e, random_walk_range_epsilon_c, random_range_bound_1_in_math_helpers_f, random_walk_range_epsilon_d;
extern "C" const double threshold_0_2_in_math_helpers_a;
extern "C" const v4i abs_mask;
// same walk driven by an accumulated time step instead of the clock
extern "C" float walk_update_timed(Walk *w, Rot rotation, double period, double dt) {
w->t0 += dt;
if (w->t0 > period) {
w->t0 = 0.0;
if (w->a == 0.0f && w->b == 0.0f) {
walk_randomize(w, true);
} else {
w->a = w->b;
do {
w->b = rng_range(rot_make_floats(0.0f, random_range_bound_1_in_math_helpers_e), random_walk_range_epsilon_c);
} while (threshold_0_2_in_math_helpers_a > (double)fabs_m(w->a - w->b, abs_mask));
}
}
double t = w->t0 / period;
double u = (double)(w->b - w->a) * t + (double)w->a;
return (float)((double)(rotation.pitch - rotation.yaw) * u + (double)rotation.yaw);
}
extern "C" bool walk_should_change(Walk *w, Rot rotation, double period) {
float x = rng_range(rot_make_floats(0.0f, random_range_bound_1_in_math_helpers_f), random_walk_range_epsilon_d);
return walk_update(w, rotation, period) > x;
}
// Gaussian random number (Marsaglia polar method, second value cached)
FN(float, libc_hypotf_alt, (float a, float b));
FN(float, libc_logf, (float x)); // logf
FN(float, libc_sqrtf, (float x)); // sqrtf (errno path)
extern "C" const float factor_5_96046eneg_08_in_math_helpers_e, offset_neg_1, threshold_1_in_math_helpers, minus_two;
struct Gauss { float spare; bool has; };
extern "C" float gauss_random(Gauss *g) {
if (g->has) {
g->has = false;
return g->spare;
}
float u, v, s;
do {
u = (float)(rng_next() >> 8) * factor_5_96046eneg_08_in_math_helpers_e;
u = u + u + offset_neg_1;
v = (float)(rng_next() >> 8) * factor_5_96046eneg_08_in_math_helpers_e;
v = v + v + offset_neg_1;
s = libc_hypotf_alt(u, v);
} while (s >= threshold_1_in_math_helpers || s == 0.0f);
float x = libc_logf(s) * minus_two / s;
float m = x >= 0.0f ? __builtin_elementwise_sqrt(x) : libc_sqrtf(x);
g->spare = v * m;
g->has = true;
return u * m;
}
// define (or find) a class in `loader` and register its native methods
#include "slinky/jnienv.h"
FN(jclass, find_class, (jobject loader, JNIEnv *env, const char *name));
FN(jobject, class_loader_ref, (jobject loader));
extern "C" int define_class(JNIEnv *env, const char *name, jobject loader, const jbyte *buf, jsize len,
const JNINativeMethod *methods, jclass *out) {
jclass cls = find_class(loader, env, name);
if (!cls) {
(*env)->ExceptionClear(env);
auto define = (*env)->DefineClass;
cls = define(env, name, class_loader_ref(loader), buf, len);
if (!cls) return 0x7d6;
}
if ((*env)->RegisterNatives(env, cls, methods, 1) != 0) return 0x7d1;
*out = cls;
return 0;
}
extern "C" int define_class_with_natives(JNIEnv *env, const char *name, jobject loader, const jbyte *buf, jsize len,
const JNINativeMethod *methods, jint n, jclass *out) {
jclass cls = find_class(loader, env, name);
if (!cls) {
(*env)->ExceptionClear(env);
auto define = (*env)->DefineClass;
cls = define(env, name, class_loader_ref(loader), buf, len);
if (!cls) return 0x7d6;
}
if ((*env)->RegisterNatives(env, cls, methods, n) != 0) return 0x7d1;
*out = cls;
return 0;
}
// one xoshiro128+ step of the shared generator (out-of-line copy of rng_next)
extern "C" unsigned xoshiro128_step_shared_generator() { return rng_next(); }
// ---- JavaVM lookup: JNI_GetCreatedJavaVMs from the already loaded "jvm" module (one VM) ----
// 0x3e8: no jvm module, 0x3e9: no such export, 0x7d2: the call failed
extern "C" const char str_jvm[], str_jni_getcreatedjavavms[]; // "jvm", "JNI_GetCreatedJavaVMs"
FN(void *, kernel32_lookups, (const char *module));
FN(void *, get_proc_address, (void *module, const char *name));
typedef int (*GetCreatedJavaVMs)(void **vms, int n, int *count);
extern "C" int get_java_vm(void **vm) {
int err;
if (!vm) return 0x7d2;
*vm = nullptr;
void *jvm = kernel32_lookups(str_jvm);
if (!jvm) {
err = 0x3e8;
} else {
GetCreatedJavaVMs get = (GetCreatedJavaVMs)get_proc_address(jvm, str_jni_getcreatedjavavms);
if (!get) {
err = 0x3e9;
} else {
int count = 0;
void *created = nullptr;
int result = get(&created, 1, &count);
if (result == 0 && count >= 1 && created) {
*vm = created;
err = 0;
} else {
err = 0x7d2; // call failed or no VM has been created yet
}
}
}
SHARED_EPILOG();
return err;
}
+212
View File
@@ -0,0 +1,212 @@
// 0xb000: settings profile load/save (cJSON), saved-string helper
#include "slinky/engine.h"
struct cJSON;
FN(cJSON *, cjson_parse, (const char *text)); // cJSON_Parse
FN(cJSON *, cjson_get_object_item, (const cJSON *object, const char *name)); // cJSON_GetObjectItemCaseSensitive
FN(int, cjson_is_array, (const cJSON *object)); // cJSON_IsArray
FN(char *, cjson_get_string_value, (const cJSON *object)); // cJSON_GetStringValue
FN(int, cjson_is_number, (const cJSON *object)); // cJSON_IsNumber
FN(void, cjson_delete, (cJSON *object)); // cJSON_Delete
FN(void, cjson_free_string, (void *p)); // cJSON_free
struct JNode { JNode *next, *prev, *child; int type; char *vs; int vi; double vd; char *key; };
struct Mod { char padding_00[0x18]; unsigned char flags; };
struct Mods { Mod **data; unsigned long long count; };
FN(unsigned char, load_module_settings, (cJSON *root, Mods *mods, Mod *module));
FN(void *, find_profile, (void *ctx, const char *name));
typedef void (*SetBind)(void *mod, int key);
FN(SetBind, profile_set_bind, (void *ctx));
extern "C" const char str_profile_binds[], str_name[], str_bind[], str_slinky_in_profiles[], str_settings[];
extern "C" const double offset_0_5;
extern "C" bool load_module_states_from_text(const char *text, Mods *mods, void *ctx) {
cJSON *root = cjson_parse(text);
if (!root) return false;
bool ok;
for (unsigned long long i = 0; i < mods->count; i++) {
Mod *module = mods->data[i];
if (!(module->flags & 0x10)) continue;
if (!load_module_settings(root, mods, module)) { ok = false; goto out; }
}
{
cJSON *binds = cjson_get_object_item(root, str_profile_binds);
unsigned char notarr = !cjson_is_array(binds);
unsigned char nul = binds == 0;
ok = true;
if (nul | notarr) goto out;
for (JNode *child = ((JNode *)binds)->child; child; child = child->next) {
cJSON *json = cjson_get_object_item((cJSON *)child, str_name);
if (!json) continue;
char *name = cjson_get_string_value(json);
if (!name) continue;
void *mod = find_profile(ctx, name);
if (!mod) continue;
JNode *item = (JNode *)cjson_get_object_item((cJSON *)child, str_bind);
int key = 0;
if (cjson_is_number((cJSON *)item)) {
int v = (int)(item->vd + offset_0_5);
key = 0;
if ((unsigned)(v - 0x68) >= (unsigned)-0x67) { key = v; }
}
profile_set_bind(ctx)(mod, key);
}
}
out:
cjson_delete(root);
return ok;
}
FN(int, settings_path, (char **path));
FN(int, settings_path_append, (char **path, const char *suffix));
FN(int, settings_path_ensure, (char **path));
FN(void, free_path, (char **path));
FN(int, write_text_file, (char **path, const char *data, bool text));
FN(char *, serialize_settings, (Mods *mods, void *ctx));
extern "C" int save_settings(void *module_manager, void *profile_list) {
char *path;
int error_code = settings_path(&path);
if (error_code) return error_code;
error_code = settings_path_append(&path, str_slinky_in_profiles);
if (!error_code) error_code = settings_path_ensure(&path);
if (!error_code) error_code = settings_path_append(&path, str_settings);
if (!error_code) {
char *json = serialize_settings((Mods *)module_manager, profile_list);
if (!json) {
error_code = 0x1388;
} else {
error_code = write_text_file(&path, json, true);
cjson_free_string(json);
}
}
free_path(&path);
return error_code;
}
extern "C" char *settings_buffer;
FN(void *, libc_malloc, (unsigned long long n)); // malloc
FN(void *, libc_memcpy, (void *dest, const void *src, unsigned long long n)); // memcpy
FN(unsigned long long, libc_strlen, (const char *str));
FN(void, libc_free, (void *ptr));
extern "C" void set_saved_string(const char *s) {
if (settings_buffer) libc_free(settings_buffer);
if (!s) {
settings_buffer = 0;
return;
}
unsigned long long n = libc_strlen(s);
char *p = (char *)libc_malloc(n + 1);
settings_buffer = p;
if (!p) return;
libc_memcpy(p, s, n);
p[n] = 0;
}
FN(cJSON *, cjson_create_object, ()); // cJSON_CreateObject
FN(cJSON *, cjson_add_array_to_object, (cJSON *object, const char *name)); // cJSON_AddArrayToObject
FN(cJSON *, cjson_add_string_to_object, (cJSON *object, const char *name, const char *s)); // cJSON_AddStringToObject
FN(cJSON *, cjson_add_number_to_object, (cJSON *object, const char *name, double v)); // cJSON_AddNumberToObject
FN(int, cjson_add_item_to_array, (cJSON *arr, cJSON *item)); // cJSON_AddItemToArray
FN(char *, cjson_print_unformatted, (const cJSON *object)); // cJSON_PrintUnformatted
FN(unsigned char, save_module_settings, (cJSON *root, Mod *module));
struct BindEnt { const char *name; char padding_08[0x30]; unsigned bind; int padding_3c; };
FN(BindEnt *, profile_array, (void *ctx, unsigned long long *n));
// serialize all savable modules plus the key binds
extern "C" char *serialize_settings(Mods *mods, void *ctx) {
cJSON *root = cjson_create_object();
char *out = 0;
if (!root) goto done;
for (unsigned long long i = 0; i < mods->count; i++) {
Mod *module = mods->data[i];
if (!(module->flags & 0x10)) continue;
if (!save_module_settings(root, module)) goto done;
}
{
cJSON *binds = cjson_add_array_to_object(root, str_profile_binds);
if (!binds) { out = 0; goto done; }
unsigned long long n;
BindEnt *e = profile_array(ctx, &n);
for (unsigned long long i = 0; i < n; i++, e++) {
if (e->bind == 0) continue;
cJSON *json = cjson_create_object();
if (!json) goto done;
if (!cjson_add_string_to_object(json, str_name, e->name)) goto done;
if (!cjson_add_number_to_object(json, str_bind, (double)e->bind)) goto done;
if (!cjson_add_item_to_array(binds, json)) goto done;
}
out = cjson_print_unformatted(root);
}
done:
cjson_delete(root);
return out;
}
FN(int, read_text_file, (char **path, char **data));
FN(unsigned char, load_module_states, (const char *data, Mods *mods));
FN(void, apply_module_state, (Mods *mods, Mod *module, int v));
FN(void, queue_module_toggle, (Mods *module_manager, Mod *module, unsigned char enabled));
extern "C" const char str_profiles[];
extern "C" unsigned char settings_dirty_flag;
struct Pair16 { long long a, b; };
extern "C" const Pair16 settings_saved_event;
struct ModX { char padding_00[0x18]; unsigned char flags; char padding_19[7]; unsigned char padding_20; char padding_21[0x1df]; void (*register_settings)(Pair16 *); };
// load profile `name` (NULL = reset every savable module to its defaults)
extern "C" int load_profile_name(const char *name, Mods *mods) {
char *data;
char *path;
Pair16 p;
if (!name) {
for (unsigned long long i = 0; i < mods->count; i++) {
ModX *module = (ModX *)mods->data[i];
if (module->flags & 0x10) continue;
apply_module_state(mods, (Mod *)module, 0);
module->padding_20 = 0;
if (!(module->flags & 2)) queue_module_toggle(mods, (Mod *)module, 0);
if (module->register_settings) {
p = settings_saved_event;
module->register_settings(&p);
}
}
if (settings_buffer) libc_free(settings_buffer);
settings_buffer = 0;
settings_dirty_flag = 0;
return 0;
}
int error_code = settings_path(&path);
if (error_code) return error_code;
if ((error_code = settings_path_append(&path, str_slinky_in_profiles)) != 0) goto clean;
if ((error_code = settings_path_ensure(&path)) != 0) goto clean;
if ((error_code = settings_path_append(&path, str_profiles)) != 0) goto clean;
if ((error_code = settings_path_ensure(&path)) != 0) goto clean;
if ((error_code = settings_path_append(&path, name)) != 0) goto clean;
if ((error_code = read_text_file(&path, &data)) != 0) goto clean;
{
unsigned char ok = load_module_states(data, mods);
error_code = 0x1389;
if (ok) {
set_saved_string(name);
settings_dirty_flag = 0;
error_code = 0;
}
}
libc_free(data);
clean:
free_path(&path);
return error_code;
}
// ---- run the settings visitor (descriptor +0x200) of every module in "remember" mode ----
// Used at start-up to copy the static setting values into each setting's saved slot, which is the
// configuration the Default profile restores and the fallback for fields missing from a profile.
struct ModList { struct ModCb { char padding_00[0x200]; void (*register_settings)(void *ev); } **data; unsigned long long count; };
struct Ev16 { long long a, b; };
extern "C" const Ev16 settings_pending_event;
extern "C" void save_module_states(ModList *l) {
for (unsigned long long i = 0; i < l->count; i++) {
auto register_settings = l->data[i]->register_settings;
if (register_settings) {
Ev16 ev = settings_pending_event;
register_settings(&ev);
}
}
}
+314
View File
@@ -0,0 +1,314 @@
// 0x9000: Vec3 / AABB helpers (MS ABI: 24/48-byte structs travel by pointer, results via sret)
#include "slinky/engine.h"
struct Vec3 { double x, y, z; };
struct AABB { Vec3 min, max; };
extern "C" double unused_constant_1_62, unused_constant_neg_1_62_b;
extern "C" Vec3 vec3_make(double x, double y, double z) { return Vec3{x, y, z}; }
extern "C" Vec3 vec3_sub(Vec3 a, Vec3 b) { return Vec3{a.x - b.x, a.y - b.y, a.z - b.z}; }
extern "C" Vec3 vec3_add(Vec3 a, Vec3 b) { return Vec3{b.x + a.x, b.y + a.y, b.z + a.z}; }
extern "C" Vec3 vec3_scale(Vec3 a, double s) { return Vec3{s * a.x, s * a.y, s * a.z}; }
extern "C" Vec3 vec3_lerp(Vec3 a, Vec3 b, double t) {
return Vec3{a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t, a.z + (b.z - a.z) * t};
}
extern "C" double vec3_distance_sq(Vec3 *a, Vec3 *b) {
double dx = a->x - b->x, dy = a->y - b->y, dz = a->z - b->z;
return dz * dz + (dx * dx + dy * dy);
}
extern "C" double vec3_distance(Vec3 *a, Vec3 *b) {
double ax = a->x, ay = a->y, az = a->z;
double dx = ax - b->x, dy = ay - b->y, dz = az - b->z;
return __builtin_sqrt(dz * dz + (dx * dx + dy * dy));
}
extern "C" Vec3 vec3_add_eye_height(Vec3 a) { return Vec3{a.x, a.y + unused_constant_1_62, a.z}; }
extern "C" Vec3 vec3_add_eye_height_sneak(Vec3 a) { return Vec3{a.x, a.y + unused_constant_neg_1_62_b, a.z}; }
extern "C" Vec3 vec3_reciprocal(Vec3 a) { return Vec3{1.0 / a.x, 1.0 / a.y, 1.0 / a.z}; }
extern "C" AABB aabb_make(Vec3 a, Vec3 b) { return AABB{a, b}; }
extern "C" AABB *aabb_expand(AABB *out, AABB *b, double d) {
b->min.x -= d; b->min.y -= d; b->min.z -= d;
b->max.x += d; b->max.y += d; b->max.z += d;
*out = *b;
return out;
}
extern "C" bool aabb_intersects(AABB *a, AABB *b) {
return b->max.x > a->min.x && a->max.x > b->min.x && b->max.y > a->min.y &&
a->max.y > b->min.y && b->max.z > a->min.z && a->max.z > b->min.z;
}
extern "C" bool aabb_contains_point(AABB *a, Vec3 *p) {
return p->x > a->min.x && a->max.x > p->x && p->y > a->min.y && a->max.y > p->y &&
p->z > a->min.z && a->max.z > p->z;
}
// ---- Rot (yaw, pitch) packed in one register ----
struct Rot { float yaw, pitch; };
FN(float, libc_hypotf_alt, (float a, float b));
FN(float, libc_hypotf, (float a, float b));
extern "C" double libc_atan2(double y, double x);
extern "C" double half_turn_degrees, pi_constant, offset_neg_90, minus_half_turn_degrees;
extern "C" Rot rot_make(float a, float b) { return Rot{a, b}; }
extern "C" Rot rot_add(Rot a, Rot b) { return Rot{b.yaw + a.yaw, b.pitch + a.pitch}; }
extern "C" Rot rot_sub(Rot a, Rot b) { return Rot{a.yaw - b.yaw, a.pitch - b.pitch}; }
extern "C" Rot rot_scale(Rot a, float s) { return Rot{a.yaw * s, a.pitch * s}; }
extern "C" Rot rot_lerp(Rot a, Rot b, float t) {
float y = (b.yaw - a.yaw) * t + a.yaw;
asm("" : "+x"(y));
return Rot{y, (b.pitch - a.pitch) * t + a.pitch};
}
extern "C" float rot_delta_length(Rot a, Rot b) { return libc_hypotf_alt(a.yaw - b.yaw, a.pitch - b.pitch); }
extern "C" float rot_distance(Rot a, Rot b) { return libc_hypotf(b.pitch - a.pitch, b.yaw - a.yaw); }
extern "C" float rot_length(Rot a) { return libc_hypotf_alt(a.yaw, a.pitch); }
extern "C" Rot rot_look_at(Vec3 *from, Vec3 *to) {
double dx = to->x - from->x;
double dy = to->y - from->y;
double dz = to->z - from->z;
double hor = __builtin_sqrt(dx * dx + dz * dz);
float yaw = (float)(libc_atan2(dz, dx) * half_turn_degrees / pi_constant + offset_neg_90);
float pitch = (float)(libc_atan2(dy, hor) * minus_half_turn_degrees / pi_constant);
return Rot{yaw, pitch};
}
// key code -> name (clang turns the switch into a relative string table)
extern "C" const char str_none[], str_mouse1[], str_mouse2[], str_mouse3[], str_mouse4[], str_mouse5[], str_backspace[], str_tab[], str_enter[], str_caps[], str_esc[], str_space[], str_prev[], str_next[], str_end[], str_home[], str_left[], mc_obf_name_up[], str_right[], str_down[], str_prntscr[], str_ins[], str_del[], str_numlock[], str_scroll[], str_lshift[], str_rshift[], str_lcontrol[], str_rcontrol[], str_lmenu[], str_rmenu[], str_colon[], str_plus[], str_comma[], str_minus[], str_period[], str_slash[], str_grave[], str_lbracket[], str_backslash[], str_rbracket[], str_quote[], text_number_0[], text_number_1[], text_number_2[], text_number_3[], text_number_4[], text_number_5[], text_number_6[], text_number_7[], text_number_8[], text_number_9[], mc_obf_name_a[], mc_obf_name_b[], mc_obf_name_c[], mc_obf_name_d[], mc_obf_name_e[], mc_obf_name_f[], mc_obf_name_g[], mc_obf_name_h[], mc_obf_name_i[], mc_obf_name_j[], mc_obf_name_k[], mc_obf_name_l[], mc_obf_name_m[], mc_obf_name_n[], mc_obf_name_o[], mc_obf_name_p[], mc_obf_name_q[], mc_obf_name_r[], mc_obf_name_s[], mc_obf_name_t[], mc_obf_name_u[], mc_obf_name_v[], mc_obf_name_w[], mc_obf_name_x[], mc_obf_name_y[], mc_obf_name_z[], str_numpad0[], str_numpad1[], str_numpad2[], str_numpad3[], str_numpad4[], str_numpad5[], str_numpad6[], str_numpad7[], str_numpad8[], str_numpad9[], mc_obf_name_f1[], mc_obf_name_f2[], mc_obf_name_f3[], mc_obf_name_f4[], mc_obf_name_f5[], mc_obf_name_f6[], mc_obf_name_f7[], mc_obf_name_f8[], mc_obf_name_f9[], text_f10[], text_f11[], text_f12[], text_f13[], text_f14[], text_f15[], text_f16[];
extern "C" const char *mouse_button_name(int v) {
switch (v) {
case 1: return str_mouse1; // "MOUSE1"
case 2: return str_mouse2; // "MOUSE2"
case 3: return str_mouse3; // "MOUSE3"
case 4: return str_mouse4; // "MOUSE4"
case 5: return str_mouse5; // "MOUSE5"
case 6: return text_number_0; // "0"
case 7: return text_number_1; // "1"
case 8: return text_number_2; // "2"
case 9: return text_number_3; // "3"
case 10: return text_number_4; // "4"
case 11: return text_number_5; // "5"
case 12: return text_number_6; // "6"
case 13: return text_number_7; // "7"
case 14: return text_number_8; // "8"
case 15: return text_number_9; // "9"
case 16: return str_numpad0; // "NUMPAD0"
case 17: return str_numpad1; // "NUMPAD1"
case 18: return str_numpad2; // "NUMPAD2"
case 19: return str_numpad3; // "NUMPAD3"
case 20: return str_numpad4; // "NUMPAD4"
case 21: return str_numpad5; // "NUMPAD5"
case 22: return str_numpad6; // "NUMPAD6"
case 23: return str_numpad7; // "NUMPAD7"
case 24: return str_numpad8; // "NUMPAD8"
case 25: return str_numpad9; // "NUMPAD9"
case 26: return mc_obf_name_a; // "A"
case 27: return mc_obf_name_b; // "B"
case 28: return mc_obf_name_c; // "C"
case 29: return mc_obf_name_d; // "D"
case 30: return mc_obf_name_e; // "E"
case 31: return mc_obf_name_f; // "F"
case 32: return mc_obf_name_g; // "G"
case 33: return mc_obf_name_h; // "H"
case 34: return mc_obf_name_i; // "I"
case 35: return mc_obf_name_j; // "J"
case 36: return mc_obf_name_k; // "K"
case 37: return mc_obf_name_l; // "L"
case 38: return mc_obf_name_m; // "M"
case 39: return mc_obf_name_n; // "N"
case 40: return mc_obf_name_o; // "O"
case 41: return mc_obf_name_p; // "P"
case 42: return mc_obf_name_q; // "Q"
case 43: return mc_obf_name_r; // "R"
case 44: return mc_obf_name_s; // "S"
case 45: return mc_obf_name_t; // "T"
case 46: return mc_obf_name_u; // "U"
case 47: return mc_obf_name_v; // "V"
case 48: return mc_obf_name_w; // "W"
case 49: return mc_obf_name_x; // "X"
case 50: return mc_obf_name_y; // "Y"
case 51: return mc_obf_name_z; // "Z"
case 52: return str_colon; // "COLON"
case 53: return str_plus; // "PLUS"
case 54: return str_comma; // "COMMA"
case 55: return str_minus; // "MINUS"
case 56: return str_period; // "PERIOD"
case 57: return str_slash; // "SLASH"
case 58: return str_grave; // "GRAVE"
case 59: return str_lbracket; // "LBRACKET"
case 60: return str_backslash; // "BACKSLASH"
case 61: return str_rbracket; // "RBRACKET"
case 62: return str_quote; // "QUOTE"
case 63: return str_left; // "LEFT"
case 64: return mc_obf_name_up; // "UP"
case 65: return str_right; // "RIGHT"
case 66: return str_down; // "DOWN"
case 67: return mc_obf_name_f1; // "F1"
case 68: return mc_obf_name_f2; // "F2"
case 69: return mc_obf_name_f3; // "F3"
case 70: return mc_obf_name_f4; // "F4"
case 71: return mc_obf_name_f5; // "F5"
case 72: return mc_obf_name_f6; // "F6"
case 73: return mc_obf_name_f7; // "F7"
case 74: return mc_obf_name_f8; // "F8"
case 75: return mc_obf_name_f9; // "F9"
case 76: return text_f10; // "F10"
case 77: return text_f11; // "F11"
case 78: return text_f12; // "F12"
case 79: return text_f13; // "F13"
case 80: return text_f14; // "F14"
case 81: return text_f15; // "F15"
case 82: return text_f16; // "F16"
case 83: return str_backspace; // "BACKSPACE"
case 84: return str_tab; // "TAB"
case 85: return str_enter; // "ENTER"
case 86: return str_caps; // "CAPS"
case 87: return str_esc; // "ESC"
case 88: return str_space; // "SPACE"
case 89: return str_prev; // "PREV"
case 90: return str_next; // "NEXT"
case 91: return str_end; // "END"
case 92: return str_home; // "HOME"
case 93: return str_prntscr; // "PRNTSCR"
case 94: return str_ins; // "INS"
case 95: return str_del; // "DEL"
case 96: return str_numlock; // "NUMLOCK"
case 97: return str_scroll; // "SCROLL"
case 98: return str_lshift; // "LSHIFT"
case 99: return str_rshift; // "RSHIFT"
case 100: return str_lcontrol; // "LCONTROL"
case 101: return str_rcontrol; // "RCONTROL"
case 102: return str_lmenu; // "LMENU"
case 103: return str_rmenu; // "RMENU"
default: return str_none; // "NONE"
}
}
// ---- AABB union / clamp / ray slab tests (double fmin/fmax on float slab values, like the C source) ----
extern "C" AABB aabb_union(Vec3 a, Vec3 b) {
AABB r;
r.min.z = __builtin_fmin(a.z, b.z);
r.max.z = __builtin_fmax(a.z, b.z);
r.min.x = __builtin_fmin(a.x, b.x); r.min.y = __builtin_fmin(a.y, b.y);
r.max.x = __builtin_fmax(a.x, b.x); r.max.y = __builtin_fmax(a.y, b.y);
return r;
}
extern "C" Vec3 aabb_clamp_point(AABB b, Vec3 p) {
Vec3 r;
r.x = p.x < b.min.x ? b.min.x : (b.max.x < p.x ? b.max.x : p.x);
r.y = p.y < b.min.y ? b.min.y : (b.max.y < p.y ? b.max.y : p.y);
r.z = p.z < b.min.z ? b.min.z : (b.max.z < p.z ? b.max.z : p.z);
return r;
}
extern "C" const double positive_infinity, one_constant, positive_infinity_for_ray_test;
extern "C" const float threshold_1_in_vec_aabb, threshold_1_in_vec_aabb_a;
extern "C" bool aabb_ray_slab_test(const AABB *b, const Vec3 *o, const Vec3 *inv) {
const double *mn = &b->min.x, *mx = &b->max.x, *po = &o->x, *pi = &inv->x;
float tmin = 0.0f, tmax = (float)positive_infinity;
#pragma clang loop unroll(full)
for (int i = 0; i < 3; i++) {
float t1 = (float)((mn[i] - po[i]) * pi[i]);
float t2 = (float)((mx[i] - po[i]) * pi[i]);
tmin = (float)__builtin_fmin(__builtin_fmax(t1, tmin), __builtin_fmax(t2, tmin));
tmax = (float)__builtin_fmax(__builtin_fmin(t1, tmax), __builtin_fmin(t2, tmax));
}
return tmin < tmax && tmin <= threshold_1_in_vec_aabb;
}
extern "C" bool aabb_ray_hit(const AABB *b, const Vec3 *o, const Vec3 *dir, Vec3 *hit) {
double inv[3] = {one_constant / dir->x, one_constant / dir->y, one_constant / dir->z};
const double *mn = &b->min.x, *mx = &b->max.x, *po = &o->x;
float tmin = 0.0f, tmax = (float)positive_infinity_for_ray_test;
#pragma clang loop unroll(full)
for (int i = 0; i < 3; i++) {
float t1 = (float)((mn[i] - po[i]) * inv[i]);
float t2 = (float)((mx[i] - po[i]) * inv[i]);
tmin = (float)__builtin_fmin(__builtin_fmax(t1, tmin), __builtin_fmax(t2, tmin));
tmax = (float)__builtin_fmax(__builtin_fmin(t1, tmax), __builtin_fmin(t2, tmax));
}
bool ok = !(tmax <= tmin) & !(threshold_1_in_vec_aabb_a < tmin);
if (ok) {
hit->x = o->x + dir->x * tmin;
hit->y = o->y + dir->y * tmin;
hit->z = o->z + dir->z * tmin;
}
return ok;
}
// ---- packet hook: GUI-chat commands first, then the packet event (0x17); returns "cancel" ----
#include "slinky/jids.h"
struct HCtx { Jvm *jvm; void *module_manager; void *profile_list; void *minecraft; void *world; void *player; void *key_state_table; void *target;
unsigned char in_game; unsigned char padding_41[3]; int padding_44; };
extern "C" char java_natives_world_module_manager[], java_natives_frame_module_manager_target_state[];
extern "C" void *init_profile_list, *java_natives_world_key_state_table_one_byte;
extern "C" char *ptr_to_gui_module_descriptor;
FN(void *, get_minecraft, (Jvm *jvm));
FN(void *, get_local_player, (Jvm *jvm, void *minecraft));
FN(void *, get_world, (Jvm *jvm, void *minecraft));
FN(unsigned char, is_in_game, (Jvm *jvm, void *minecraft));
FN(void *, java_jni_is_instance_of_c01_packet_chat_message, (Jvm *jvm, void *pkt));
FN(void *, java_jni_is_instance_of_c14_packet_tab_complete, (Jvm *jvm, void *pkt));
FN(unsigned char, handle_chat_message, (HCtx *ctx, void *object));
FN(unsigned char, text_starts_with_bang, (HCtx *ctx, void *object));
struct PktEvent { long long type; Jvm *j; void *a; void *pkt; unsigned char cancel; char rest[0x27]; };
FN(void, dispatch_event, (void *mgr, PktEvent *e));
extern "C" unsigned char handle_chat_packet(Jvm *jvm, void *object, void *pkt) {
void *minecraft = get_minecraft(jvm);
void *player = get_local_player(jvm, minecraft);
void *world = get_world(jvm, minecraft);
HCtx ctx;
ctx.jvm = jvm; ctx.module_manager = java_natives_world_module_manager; ctx.profile_list = init_profile_list; ctx.minecraft = minecraft; ctx.world = world; ctx.player = player;
ctx.key_state_table = java_natives_world_key_state_table_one_byte; ctx.target = java_natives_frame_module_manager_target_state;
ctx.in_game = ptr_to_gui_module_descriptor[0x14] ? 0 : is_in_game(jvm, minecraft);
*(int *)&ctx.padding_41[0] = 0;
ctx.padding_44 = 0;
void *c01_packet_chat_message = java_jni_is_instance_of_c01_packet_chat_message(jvm, pkt);
unsigned char r;
if (c01_packet_chat_message) {
r = handle_chat_message(&ctx, c01_packet_chat_message);
} else {
void *c14_packet_tab_complete = java_jni_is_instance_of_c14_packet_tab_complete(jvm, pkt);
if (!c14_packet_tab_complete) goto send;
r = text_starts_with_bang(&ctx, c14_packet_tab_complete);
}
if (r) return 1;
send:
PktEvent e = {0x17, jvm, object, pkt, 0, {}};
dispatch_event(java_natives_world_module_manager, &e);
return e.cancel;
}
// ---- gap-scan functions ----
// client key code -> ASCII for text fields (a-z, 0-9, space, '.', '-'); 0 = not printable. Inverse of windows_key_to_client_key.
extern "C" char key_code_to_char(int key) {
if ((unsigned)(key - 0x1a) <= 0x19) return (char)(key + 0x47);
if ((unsigned)(key - 6) <= 9) return (char)(key + 0x2a);
switch (key) {
case 0x58: return ' ';
case 0x38: return '.';
case 0x37: return '-';
default: return 0;
}
}
// horizontal distance (*horiz) and absolute height difference (*vert) between two points
typedef long long v2l9 __attribute__((vector_size(16)));
typedef double v2d9 __attribute__((vector_size(16)));
extern "C" const v2l9 vec_aabb_fabs_mask; // fabs mask
extern "C" double libc_hypot(double x, double z); // hypot
extern "C" void vec3_horizontal_distance(const Vec3 *a, const Vec3 *b, double *horiz, double *vert) {
double ax = a->x, az = a->z;
*horiz = libc_hypot(ax - b->x, az - b->z);
v2d9 d = {a->y - b->y, 0};
*vert = ((v2d9)((v2l9)d & vec_aabb_fabs_mask))[0];
}
extern "C" Vec3 vec3_copy(Vec3 v) { // normalize
double len = __builtin_sqrt((v.x * v.x + v.y * v.y) + v.z * v.z);
v.x /= len;
v.y /= len;
v.z /= len;
asm("" : "+m"(v)); // the original normalises its by-value copy in memory and copies it out
return v;
}
extern "C" double vec3_length(const Vec3 *v) { return __builtin_sqrt((v->x * v->x + v->y * v->y) + v->z * v->z); }
struct V2f9 { float x, y; };
extern "C" float vec2_length_sq(V2f9 v) { return v.x * v.x + v.y * v.y; }
+21
View File
@@ -0,0 +1,21 @@
// helper functions (formerly address block; names inferred from behaviour).
#include "slinky/engine.h"
FN(void *, java_potion_potion_types, (void *jvm, int id));
FN(void *, java_call_entity_living_base_get_active_potion_effect, (void *jvm, void *entity_living_base, void *effect));
FN(void, java_set_potion_effect_duration, (void *jvm, void *effect, int value));
FN(void, java_set_entity_player_sp_time_in_portal, (void *jvm, void *entity_player_sp, float value));
FN(void, java_set_entity_player_sp_prev_time_in_portal, (void *jvm, void *entity_player_sp, float value));
extern "C" unsigned char anti_debuff_blindness_value, anti_debuff_nausea_value;
struct Ev { char padding_00[8]; Ctx *ctx; };
extern "C" void anti_debuff_tick(Ev *event) {
Ctx *ctx = event->ctx;
if (anti_debuff_blindness_value == 1) {
void *jvm = java_potion_potion_types(ctx->jvm, 0xf);
void *effect = java_call_entity_living_base_get_active_potion_effect(ctx->jvm, ctx->player, jvm);
if (effect) java_set_potion_effect_duration(ctx->jvm, effect, 0);
}
if (anti_debuff_nausea_value == 1) {
java_set_entity_player_sp_time_in_portal(ctx->jvm, ctx->player, 0.0f);
java_set_entity_player_sp_prev_time_in_portal(ctx->jvm, ctx->player, 0.0f);
}
}
@@ -0,0 +1,116 @@
// helper functions (formerly address block; names inferred from behaviour).
#include "slinky/engine.h"
FN(void, libc_free, (void *ptr));
FN(unsigned long long, libc_strlen, (const char *str));
struct Vec { unsigned long long size, cap; void *data; };
struct Lists { char padding_00[8]; Vec a, b, c; };
extern "C" void free_block_search_section_lists(Lists *l) {
l->a.size = 0; l->a.cap = 0; libc_free(l->a.data); l->a.data = 0;
l->b.size = 0; l->b.cap = 0; libc_free(l->b.data); l->b.data = 0;
l->c.size = 0; l->c.cap = 0; libc_free(l->c.data); l->c.data = 0;
}
extern "C" void search_box_append_char(char *buf, char character) {
unsigned long long n = libc_strlen(buf);
if (n > 99) return;
unsigned char u = (unsigned char)((character & 0xdf) - 0x41);
asm("" : "+r"(u));
if (u >= 0x1a) {
unsigned char d = (unsigned char)(character - 0x30);
asm("" : "+r"(d));
unsigned char notdigit = d >= 10;
asm("" : "+r"(notdigit));
unsigned char notspace = character != 0x20;
asm("" : "+r"(notspace));
if (notdigit & notspace) return;
}
buf[n] = character;
}
FN(void *, java_get_world_chunk_provider_client, (void *jvm, void *chunk_provider_client));
FN(void *, java_get_chunk_provider_client, (void *jvm, void *chunk_provider_client));
FN(int, java_call_list_size, (void *jvm, void *list));
FN(void *, java_call_list_get, (void *jvm, void *list, int i));
FN(int, java_get_chunk_x_position, (void *jvm, void *chunk));
FN(int, java_get_chunk_z_position, (void *jvm, void *chunk));
struct Rec { void *jvm; void *world; int one; int id; int lvl; int padding_1c, padding_20, padding_24; };
FN(void, equipment_event, (Rec *r));
struct S3f { void *jvm; char padding_08[0x18]; void *world; };
extern "C" void block_esp_queue_loaded_chunks(S3f *s) {
void *chunk_provider_client = java_get_world_chunk_provider_client(s->jvm, s->world);
if (!chunk_provider_client) return;
void *list = java_get_chunk_provider_client(s->jvm, chunk_provider_client);
int n = java_call_list_size(s->jvm, list);
if (n <= 0) return;
for (int i = 0; i != n; i++) {
void *element = java_call_list_get(s->jvm, list, i);
int chunk_x_position = java_get_chunk_x_position(s->jvm, element);
int lvl = java_get_chunk_z_position(s->jvm, element);
Rec r;
r.jvm = s->jvm;
r.world = s->world;
r.one = 1;
r.id = chunk_x_position;
r.lvl = lvl;
r.padding_1c = 0; r.padding_20 = 0; r.padding_24 = 0;
equipment_event(&r);
}
}
// rebuild a coloured box mesh: 8 corner vertices + 24 line indices per block
FN(unsigned char, vector_grow, (void *data, unsigned long long elem, unsigned long long size, void *cap));
struct Vtx { float x, y, z; unsigned char r, g, b, a; };
struct Corner { float x, y, z; };
struct Blk { unsigned char x, y, z, padding_03; char padding_04[4]; float *color; };
struct Mesh {
char padding_00[8]; unsigned long long nblocks; char padding_10[8]; Blk *blocks;
unsigned long long nv, capv; Vtx *v;
unsigned long long ni, capi; unsigned short *idx;
};
extern "C" const float factor_255_in_block_esp_mesh_and_search_box;
extern "C" const Corner cube_corner_offsets[8];
extern "C" const unsigned short cube_edge_indices[24];
extern "C" void rebuild_block_esp_mesh(Mesh *m) {
m->nv = 0;
m->ni = 0;
if (!m->nblocks) return;
for (unsigned long long b = 0; b < m->nblocks; b++) {
Blk *bk = &m->blocks[b];
float *col = bk->color;
unsigned char r = (unsigned char)(int)(col[2] * factor_255_in_block_esp_mesh_and_search_box);
unsigned char g = (unsigned char)(int)(col[3] * factor_255_in_block_esp_mesh_and_search_box);
unsigned char bl = (unsigned char)(int)(col[4] * factor_255_in_block_esp_mesh_and_search_box);
unsigned long long base = m->nv;
#pragma clang loop unroll(disable)
for (int k = 0; k < 8; k++) {
if (!vector_grow(&m->v, 0x10, m->nv, &m->capv)) return;
Vtx *v = &m->v[m->nv];
v->x = cube_corner_offsets[k].x + (float)bk->x;
v->y = cube_corner_offsets[k].y + (float)bk->y;
v->z = cube_corner_offsets[k].z + (float)bk->z;
v->r = r; v->g = g; v->b = bl; v->a = 0xff;
m->nv++;
}
#pragma clang loop unroll(disable)
for (int k = 0; k < 24; k++) {
if (!vector_grow(&m->idx, 2, m->ni, &m->capi)) return;
m->idx[m->ni] = (unsigned short)(cube_edge_indices[k] + base);
m->ni++;
}
}
}
// ---- free the whole block-search mesh cache: every section's three lists, then the section array ----
struct LVec3f { unsigned long long size, cap; Lists *data; };
extern "C" LVec3f block_esp_mesh_count_capacity;
extern "C" void free_block_search_cache() {
for (unsigned long long i = 0; i < block_esp_mesh_count_capacity.size; i++) {
Lists *l = &block_esp_mesh_count_capacity.data[i];
l->a.size = 0; l->a.cap = 0; libc_free(l->a.data); l->a.data = 0;
l->b.size = 0; l->b.cap = 0; libc_free(l->b.data); l->b.data = 0;
l->c.size = 0; l->c.cap = 0; libc_free(l->c.data); l->c.data = 0;
}
block_esp_mesh_count_capacity.size = 0;
block_esp_mesh_count_capacity.cap = 0;
libc_free(block_esp_mesh_count_capacity.data);
block_esp_mesh_count_capacity.data = 0;
}
+79
View File
@@ -0,0 +1,79 @@
// 0x41000: sorted name table (24-byte entries) helpers
#include "slinky/engine.h"
struct Ent { void *name; long long q; int d; int key; };
struct Vec { unsigned long long size, cap; Ent *data; };
extern "C" unsigned long long name_table_count, name_table_capacity;
extern "C" Ent *name_table_entries;
extern "C" unsigned char safe_walk_safe_walk_active;
FN(unsigned long long, libc_strlen, (const char *str));
FN(int, key_name_to_code, (void *key));
FN(void *, key_code_to_index, (int idx));
FN(unsigned char, vector_grow, (void *vec, unsigned long long elem, unsigned long long n, void *cap));
FN(void *, libc_bsearch, (const void *key, const void *base, unsigned long long n, unsigned long long size, int (*cmp)(const void *, const void *)));
FN(void, libc_memmove, (void *dst, const void *src, unsigned long long n));
FN(void, libc_free, (void *ptr));
FN(void *, get_minecraft, (void *jvm));
FN(void *, get_local_player, (void *jvm, void *minecraft));
FN(void, java_set_entity_living_base_max_hurt_time, (void *jvm, void *entity_living_base, int value));
extern "C" int compare_name_key(const int *left, const Ent *right) { return *left - right->key; }
extern "C" void text_filter_name(char *buf, char character) {
unsigned long long n = libc_strlen(buf);
if (n > 99) return;
unsigned char u = (unsigned char)((character & 0xdf) - 0x41);
asm("" : "+r"(u));
if (u >= 0x1a) {
unsigned char d = (unsigned char)(character - 0x30);
asm("" : "+r"(d));
unsigned char notdigit = d >= 10;
asm("" : "+r"(notdigit));
unsigned char notspace = character != 0x20;
asm("" : "+r"(notspace));
if (notdigit & notspace) return;
}
buf[n] = character;
}
extern "C" unsigned long long deref_u64(unsigned long long *p) { return *p; }
extern "C" unsigned long long vec24_key(Vec *vector, long long i) { return *(unsigned long long *)((char *)vector->data + i * 24); }
extern "C" void *vec24_name(Vec *vector, long long i) { return (char *)vector->data + i * 24 + 8; }
extern "C" unsigned char name_table_find(Vec *vector, void *name, long long *src) {
int id = key_name_to_code(name);
if (id == -1) return 0;
if (name_table_entries && libc_bsearch(&id, name_table_entries, name_table_count, 0x18, (int (*)(const void *, const void *))compare_name_key))
return 0;
unsigned long long idx = 0;
if (vector->size) {
for (idx = 0; idx < vector->size; idx++)
if (id <= vector->data[idx].key) break;
}
void *nm = key_code_to_index(id);
long long q = src[0];
int d = (int)src[1];
int k = id;
unsigned char ok = vector_grow(&name_table_entries, 0x18, name_table_count, &name_table_capacity);
if (!ok) return ok;
Ent *base = name_table_entries;
libc_memmove(base + idx + 1, base + idx, (name_table_count - idx) * 0x18);
base[idx].name = nm;
base[idx].q = q;
base[idx].d = d;
base[idx].key = k;
name_table_count++;
return ok;
}
extern "C" void vec24_erase(Vec *vector, long long i) {
vector->size--;
libc_memmove(&vector->data[i], &vector->data[i + 1], (vector->size - i) * sizeof(Ent));
}
extern "C" void vec24_clear(Vec *vector) {
vector->size = 0; vector->cap = 0;
libc_free(vector->data);
vector->data = 0;
}
extern "C" unsigned char get_render_flag() { return safe_walk_safe_walk_active; }
extern "C" void set_local_player_max_hurt_time(void *jvm) {
void *player = get_local_player(jvm, get_minecraft(jvm));
if (player) java_set_entity_living_base_max_hurt_time(jvm, player, 0x7fffffff);
}
+296
View File
@@ -0,0 +1,296 @@
// 0x40000: 24-byte entry vector accessors, comparators
#include "slinky/engine.h"
struct Ent { void *name; long long q; int d; int key; };
struct Vec { unsigned long long size, cap; Ent *data; };
extern "C" unsigned long long search_list_read_u64(unsigned long long *p) { return *p; }
extern "C" unsigned long long search_entry_key(Vec *vector, long long i) { return *(unsigned long long *)((char *)vector->data + i * 24); }
extern "C" void *search_entry_name(Vec *vector, long long i) { return (char *)vector->data + i * 24 + 8; }
extern "C" int compare_vec24_key(const int *left, const Ent *right) { return *left - right->key; }
extern "C" int compare_int_ids(const int *left, const int *right) {
int result = left[0] - right[0];
if (result == 0) result = left[1] - right[1];
SHARED_EPILOG();
return result;
}
FN(void, libc_memmove, (void *dst, const void *src, unsigned long long n));
FN(void, libc_free, (void *ptr));
struct V3 { unsigned long long size, cap; void *data; };
struct Lists { char padding_00[8]; V3 a, b, c; };
struct LVec { unsigned long long size, cap; Lists *data; };
extern "C" LVec block_esp_mesh_count_capacity;
extern "C" unsigned char block_esp_rebuild_requested;
static inline __attribute__((always_inline)) void clear_lists(Lists *l) {
l->a.size = 0; l->a.cap = 0; libc_free(l->a.data); l->a.data = 0;
l->b.size = 0; l->b.cap = 0; libc_free(l->b.data); l->b.data = 0;
l->c.size = 0; l->c.cap = 0; libc_free(l->c.data); l->c.data = 0;
}
static inline __attribute__((always_inline)) void clear_all() {
for (unsigned long long i = 0; i < block_esp_mesh_count_capacity.size; i++) clear_lists(&block_esp_mesh_count_capacity.data[i]);
block_esp_mesh_count_capacity.size = 0; block_esp_mesh_count_capacity.cap = 0;
libc_free(block_esp_mesh_count_capacity.data);
block_esp_mesh_count_capacity.data = 0;
block_esp_rebuild_requested = 1;
}
extern "C" void search_entries_erase(Vec *vector, long long i) {
vector->size--;
libc_memmove(&vector->data[i], &vector->data[i + 1], (vector->size - i) * sizeof(Ent));
clear_all();
}
extern "C" void search_entries_clear(Vec *vector) {
vector->size = 0; vector->cap = 0;
libc_free(vector->data);
vector->data = 0;
clear_all();
}
typedef char v16c __attribute__((vector_size(16)));
typedef char v16cu __attribute__((vector_size(16), aligned(1)));
FN(int, key_name_to_code, (void *key));
FN(void *, key_code_to_index, (int idx));
FN(unsigned char, vector_grow, (void *vec, unsigned long long elem, unsigned long long n, void *cap));
FN(void *, libc_bsearch, (const void *key, const void *base, unsigned long long n, unsigned long long size, int (*cmp)(const void *, const void *)));
extern "C" unsigned long long id_table_count, id_table_capacity;
extern "C" Ent *id_table_entries;
// insert a block-name entry into the sorted search list (and drop all cached meshes)
extern "C" unsigned char insert_block_name_entry_sorted(Vec *vector, void *name, long long *src) {
int id = key_name_to_code(name);
if (id == -1) return 0;
if (id_table_entries && libc_bsearch(&id, id_table_entries, id_table_count, 0x18, (int (*)(const void *, const void *))compare_vec24_key))
return 0;
unsigned long long idx = 0;
if (vector->size) {
for (idx = 0; idx < vector->size; idx++)
if (id <= vector->data[idx].key) break;
}
void *nm = key_code_to_index(id);
long long q = src[0];
int d = (int)src[1];
int k = id;
clear_all();
unsigned char ok = vector_grow(&id_table_entries, 0x18, id_table_count, &id_table_capacity);
if (!ok) return ok;
Ent *base = id_table_entries;
libc_memmove(base + idx + 1, base + idx, (id_table_count - idx) * 0x18);
base[idx].name = nm;
base[idx].q = q;
base[idx].d = d;
base[idx].key = k;
id_table_count++;
return ok;
}
struct Blk { unsigned char x, y, z, padding_03, padding_04, padding_05, padding_06, padding_07; Ent *ent; };
struct BMesh { char padding_00[8]; unsigned long long n, cap; Blk *b; };
FN(BMesh *, find_insert_mesh_record_chunk, (unsigned long long key));
// scan a 16x16x16 chunk section for every searched block id and append the hits to the section mesh
extern "C" void scan_16x16x16_chunk_section_searched(int cx, int sy, unsigned cz, const unsigned char *ids, BMesh **out) {
if (!id_table_count) return;
unsigned long long key = (unsigned)cx | ((unsigned long long)cz << 32);
int ybase = sy << 4;
for (unsigned long long e = 0; e < id_table_count; e++) {
Ent *ent = &id_table_entries[e];
char kb = (char)ent->key;
v16c want = {kb, kb, kb, kb, kb, kb, kb, kb, kb, kb, kb, kb, kb, kb, kb, kb};
for (unsigned long long r = 0; r != 0x100; r++) {
v16c row = *(const v16cu *)(ids + r * 16);
unsigned mask = __builtin_ia32_pmovmskb128((v16c)(row == want));
if (!mask) continue;
unsigned char y = (unsigned char)(((unsigned)(r << 4) >> 8) + ybase);
unsigned char z = (unsigned char)(r & 0xf);
for (int x = 0; x != 0x10; x++) {
if (!((mask >> x) & 1)) continue;
BMesh *m = *out;
if (!m) {
m = find_insert_mesh_record_chunk(key);
*out = m;
if (!m) return;
}
if (!vector_grow(&m->b, 0x10, m->n, &m->cap)) return;
Blk *b = &m->b[m->n];
b->x = (unsigned char)x; b->y = y; b->z = z;
b->padding_03 = 0; b->padding_04 = 0; b->padding_05 = 0; b->padding_06 = 0; b->padding_07 = 0;
b->ent = ent;
m->n++;
}
}
}
}
typedef short v8s __attribute__((vector_size(16)));
typedef short v8su __attribute__((vector_size(16), aligned(1)));
// same scan for 16-bit block data (id << 4 | meta)
extern "C" void scan_16_bit_block_data(int cx, int sy, unsigned cz, const unsigned short *ids, BMesh **out) {
if (!id_table_count) return;
unsigned long long key = (unsigned)cx | ((unsigned long long)cz << 32);
int ybase = sy << 4;
for (unsigned long long e = 0; e < id_table_count; e++) {
Ent *ent = &id_table_entries[e];
short kw = (short)ent->key;
v8s want = {kw, kw, kw, kw, kw, kw, kw, kw};
#pragma clang loop unroll(disable) vectorize(disable)
for (unsigned long long r = 0; r != 0x200; r++) {
v8s row = *(const v8su *)(ids + r * 8);
unsigned mask = __builtin_ia32_pmovmskb128((v16c)((row >> 4) == want));
if (!mask) continue;
unsigned char x = (unsigned char)((r & 1) << 3);
unsigned char y = (unsigned char)(((unsigned)(r * 8) >> 8) + ybase);
unsigned char z = (unsigned char)((r >> 1) & 0xf);
#pragma clang loop unroll(disable) vectorize(disable)
for (int w = 0; w != 0x10; w += 2, x++) {
if (!((mask >> w) & 3)) continue;
BMesh *m = *out;
if (!m) {
m = find_insert_mesh_record_chunk(key);
*out = m;
if (!m) return;
}
if (!vector_grow(&m->b, 0x10, m->n, &m->cap)) return;
Blk *b = &m->b[m->n];
b->x = x; b->y = y; b->z = z;
b->padding_03 = 0; b->padding_04 = 0; b->padding_05 = 0; b->padding_06 = 0; b->padding_07 = 0;
b->ent = ent;
m->n++;
}
}
}
}
extern "C" unsigned long long mesh_vector_capacity;
// find or insert the mesh record of chunk section `key` (sorted by x, then z)
extern "C" BMesh *find_insert_mesh_record_chunk(unsigned long long key) {
Lists tmp = {};
*(unsigned long long *)&tmp = key;
if (block_esp_mesh_count_capacity.data) {
void *hit = libc_bsearch(&tmp, block_esp_mesh_count_capacity.data, block_esp_mesh_count_capacity.size, 0x50,
(int (*)(const void *, const void *))compare_int_ids);
if (hit) return (BMesh *)hit;
}
unsigned long long idx = 0;
if (block_esp_mesh_count_capacity.size) {
for (idx = 0; idx < block_esp_mesh_count_capacity.size; idx++) {
const int *e = (const int *)&block_esp_mesh_count_capacity.data[idx];
int c = (int)key - e[0];
if (c == 0) c = (int)(key >> 32) - e[1];
if (c <= 0) break;
}
}
if (!vector_grow(&block_esp_mesh_count_capacity.data, 0x50, block_esp_mesh_count_capacity.size, &mesh_vector_capacity)) return 0;
Lists *base = block_esp_mesh_count_capacity.data;
libc_memmove(base + idx + 1, base + idx, (block_esp_mesh_count_capacity.size - idx) * 0x50);
*(unsigned long long *)&base[idx] = key;
base[idx].a = V3{}; base[idx].b = V3{}; base[idx].c = V3{};
block_esp_mesh_count_capacity.size++;
return (BMesh *)&block_esp_mesh_count_capacity.data[idx];
}
// ---- world events: chunk unload (0) / load (1) rebuild the chunk's meshes, block change (2) edits one ----
#include "slinky/jids.h"
struct WEv { Jvm *jvm; jobject world; unsigned type; int a, b, c, id; };
FN(jobject, java_world_get_chunk_from_chunk_coords, (Jvm *jvm, jobject chunk, int cx, int cz));
FN(jobject, java_get_chunk_storage_arrays, (Jvm *jvm, jobject chunk));
FN(jobject, java_get_id_b20, (Jvm *jvm, jobject sec));
FN(int, chunk_coordinate, (int x));
FN(int, block_coordinate, (int x));
FN(void, rebuild_block_esp_mesh, (BMesh *m));
FN(void, free_block_search_section_lists, (Lists *l));
extern "C" void (*block_esp_block_scan_callback)(int cx, int sy, int cz, void *ids, BMesh **out);
static inline __attribute__((always_inline)) void drop_rec(Lists *dst) {
unsigned long long i = dst - block_esp_mesh_count_capacity.data;
block_esp_mesh_count_capacity.size--;
libc_memmove(dst, dst + 1, (block_esp_mesh_count_capacity.size - i) * 0x50);
}
extern "C" void equipment_event(WEv *ev) {
if (ev->type >= 2) {
if (ev->type != 2) return;
int id = ev->id;
Ent *ent = id_table_entries ? (Ent *)libc_bsearch(&id, id_table_entries, id_table_count, 0x18,
(int (*)(const void *, const void *))compare_vec24_key)
: 0;
int cx = chunk_coordinate(ev->a), cz = chunk_coordinate(ev->c);
unsigned long long key = (unsigned)cx | (unsigned long long)(unsigned)cz << 32;
Lists tmp = {};
*(unsigned long long *)&tmp = key;
BMesh *m = block_esp_mesh_count_capacity.data ? (BMesh *)libc_bsearch(&tmp, block_esp_mesh_count_capacity.data, block_esp_mesh_count_capacity.size, 0x50,
(int (*)(const void *, const void *))compare_int_ids)
: 0;
unsigned char lx = ev->a - block_coordinate(cx), lz = ev->c - block_coordinate(cz), y = (unsigned char)ev->b;
if (m) {
unsigned long long n = m->n;
for (unsigned long long i = 0; i < n; i++) {
Blk *dst = &m->b[i];
if (dst->x != lx || dst->y != y || dst->z != lz) continue;
if (ent) {
dst->ent = ent;
} else {
m->n = --n;
libc_memmove(dst, dst + 1, (n - i) * sizeof(Blk));
if (!m->n) {
free_block_search_section_lists((Lists *)m);
drop_rec((Lists *)m);
return;
}
}
rebuild_block_esp_mesh(m);
return;
}
if (!ent) return;
} else {
if (!ent) return;
unsigned long long idx = 0;
if (block_esp_mesh_count_capacity.size) {
for (idx = 0; idx < block_esp_mesh_count_capacity.size; idx++) {
const int *e = (const int *)&block_esp_mesh_count_capacity.data[idx];
int c = cx - e[0];
if (c == 0) c = cz - e[1];
if (c <= 0) break;
}
}
if (!vector_grow(&block_esp_mesh_count_capacity.data, 0x50, block_esp_mesh_count_capacity.size, &mesh_vector_capacity)) return;
Lists *base = block_esp_mesh_count_capacity.data;
libc_memmove(base + idx + 1, base + idx, (block_esp_mesh_count_capacity.size - idx) * 0x50);
*(unsigned long long *)&base[idx] = key;
base[idx].a = V3{}; base[idx].b = V3{}; base[idx].c = V3{};
block_esp_mesh_count_capacity.size++;
if (!block_esp_mesh_count_capacity.data) return;
m = (BMesh *)&block_esp_mesh_count_capacity.data[idx];
}
Blk nb = {lx, y, lz, 0, 0, 0, 0, 0, ent};
if (!vector_grow(&m->b, 0x10, m->n, &m->cap)) return;
m->b[m->n] = nb;
m->n++;
rebuild_block_esp_mesh(m);
return;
}
Lists tmp = {};
*(unsigned long long *)&tmp = *(unsigned long long *)&ev->a;
if (block_esp_mesh_count_capacity.data) {
Lists *r = (Lists *)libc_bsearch(&tmp, block_esp_mesh_count_capacity.data, block_esp_mesh_count_capacity.size, 0x50,
(int (*)(const void *, const void *))compare_int_ids);
if (r) {
clear_lists(r);
drop_rec(r);
}
}
if (ev->type != 1) return;
Jvm *jvm = ev->jvm;
int cx = ev->a, cz = ev->b;
jobject chunk = java_world_get_chunk_from_chunk_coords(jvm, ev->world, cx, cz);
if (!chunk) return;
BMesh *out = 0;
JNIEnv *env = jvm->env;
jobjectArray secs = (jobjectArray)java_get_chunk_storage_arrays(jvm, chunk);
if (!secs) return;
for (int i = 0; i != 16; i++) {
jobject sec = (*env)->GetObjectArrayElement(env, secs, i);
if (!sec) continue;
jarray ids = (jarray)java_get_id_b20(jvm, sec);
if (!ids) continue;
void *p = (*env)->GetPrimitiveArrayCritical(env, ids, 0);
if (!p) continue;
block_esp_block_scan_callback(cx, i, cz, p, &out);
(*env)->ReleasePrimitiveArrayCritical(env, ids, p, 0);
}
if (out) rebuild_block_esp_mesh(out);
return;
}
+206
View File
@@ -0,0 +1,206 @@
// 0x48000: chest-GUI check (localized title compare) and randomized click delay
#include "slinky/engine.h"
#include "slinky/jids.h"
struct Rot { float yaw, pitch; };
FN(void *, get_current_screen, (void *jvm, void *object));
FN(unsigned char, java_jni_is_instance_of_gui_chest, (void *jvm, void *candidate));
FN(void *, java_get_gui_chest_lower_chest_inventory, (void *jvm, void *gui_chest));
FN(void *, java_jni_is_instance_of_inventory_basic, (void *jvm, void *candidate));
FN(void *, java_get_inventory_basic, (void *jvm, void *inventory_basic));
FN(void *, java_call_static_stat_collector_translate_to_local, (void *jvm, void *stat_collector));
FN(Rot, rot_make_floats, (float a, float b));
FN(float, random_rotation, (Rot rotation));
FN(void, slot_index, (int id));
FN(int, libc_strncmp_alt, (const void *left, const void *right, unsigned long long n));
extern "C" unsigned char chest_stealer_require_vanilla_title_value;
extern "C" float chest_stealer_delay_after_open_value, chest_stealer_steal_delay, range_low_1_in_chest_gui, random_range_bound_1_5_in_chest_gui;
extern "C" const char str_container_chest[], str_container_chestdouble[];
// does the Java string equal the translation of `key`?
extern "C" bool java_string_equal_translation_key(Jvm *jvm, jstring s, const char *key) {
JNIEnv *env = jvm->env;
if (!s || !key) return false;
jstring key_string = (*env)->NewStringUTF(env, key);
jstring t = key_string ? (jstring)java_call_static_stat_collector_translate_to_local(jvm, key_string) : 0;
if (key_string) (*env)->DeleteLocalRef(env, key_string);
if (!t) return false;
jsize len = (*env)->GetStringLength(env, s);
if ((*env)->GetStringLength(env, t) != len) {
(*env)->DeleteLocalRef(env, t);
return false;
}
const jchar *a = (const jchar *)(*env)->GetStringChars(env, s, 0);
const jchar *b = a ? (const jchar *)(*env)->GetStringChars(env, t, 0) : 0;
bool eq = a && b && libc_strncmp_alt(a, b, (long long)len * 2) == 0;
if (a) (*env)->ReleaseStringChars(env, s, a);
if (b) (*env)->ReleaseStringChars(env, t, b);
(*env)->DeleteLocalRef(env, t);
return eq;
}
struct Ev { char padding_00[8]; Ctx *ctx; };
extern "C" void chest_gui_tick(Ev *event) {
Ctx *ctx = event->ctx;
void *screen = get_current_screen(ctx->jvm, ctx->minecraft);
if (screen && java_jni_is_instance_of_gui_chest(ctx->jvm, screen)) {
if (chest_stealer_require_vanilla_title_value != 1) return;
void *jvm = ctx->jvm;
void *inventory_basic = java_jni_is_instance_of_inventory_basic(jvm, java_get_gui_chest_lower_chest_inventory(jvm, screen));
if (inventory_basic) {
void *name = java_get_inventory_basic(ctx->jvm, inventory_basic);
if (java_string_equal_translation_key((Jvm *)ctx->jvm, (jstring)name, str_container_chest)) return;
if (java_string_equal_translation_key((Jvm *)ctx->jvm, (jstring)name, str_container_chestdouble)) return;
}
}
chest_stealer_steal_delay = chest_stealer_delay_after_open_value;
float lo = range_low_1_in_chest_gui;
chest_stealer_steal_delay = random_rotation(rot_make_floats(lo, random_range_bound_1_5_in_chest_gui)) * chest_stealer_steal_delay;
slot_index(0xd);
}
// item id -> {tool kind, material tier} (kind 1/14/16/22 = axe/shovel/pickaxe/sword, 15 = shears)
struct ToolInfo { int kind, tier; };
extern "C" ToolInfo chest_gui_title_id(int id) {
switch (id) {
case 0x100: return ToolInfo{16, 3};
case 0x101: return ToolInfo{14, 3};
case 0x102: return ToolInfo{1, 3};
case 0x10b: return ToolInfo{22, 3};
case 0x10c: return ToolInfo{22, 1};
case 0x10d: return ToolInfo{16, 1};
case 0x10e: return ToolInfo{14, 1};
case 0x10f: return ToolInfo{1, 1};
case 0x110: return ToolInfo{22, 2};
case 0x111: return ToolInfo{16, 2};
case 0x112: return ToolInfo{14, 2};
case 0x113: return ToolInfo{1, 2};
case 0x114: return ToolInfo{22, 5};
case 0x115: return ToolInfo{16, 5};
case 0x116: return ToolInfo{14, 5};
case 0x117: return ToolInfo{1, 5};
case 0x11b: return ToolInfo{22, 4};
case 0x11c: return ToolInfo{16, 4};
case 0x11d: return ToolInfo{14, 4};
case 0x11e: return ToolInfo{1, 4};
case 0x167: return ToolInfo{15, 1};
default: return ToolInfo{0, 0};
}
}
FN(int, java_call_nbttag_list_tag_count, (void *jvm, void *list));
FN(void *, java_call_nbttag_list_get_compound_tag_at, (void *jvm, void *list, int i));
FN(short, java_jni_call_short_method_nbttag_compound_get_short, (void *jvm, void *tag, void *key));
extern "C" const char str_id_in_chest_gui[], str_lvl_in_chest_gui[];
// weighted sum of enchantment levels (weights indexed by enchantment id 0..62)
extern "C" unsigned short weighted_sum_enchantment_levels(Ctx *ctx, void *list, const unsigned char *weights) {
if (!list) return 0;
JNIEnv *env = ((Jvm *)ctx->jvm)->env;
jstring idS = (*env)->NewStringUTF(env, str_id_in_chest_gui);
if (!idS) return 0;
jstring lvlS = (*env)->NewStringUTF(env, str_lvl_in_chest_gui);
if (!lvlS) {
(*env)->DeleteLocalRef(env, idS);
return 0;
}
int n = java_call_nbttag_list_tag_count(ctx->jvm, list);
if (n <= 0) {
(*env)->DeleteLocalRef(env, idS);
(*env)->DeleteLocalRef(env, lvlS);
return 0;
}
unsigned short sum = 0;
for (int i = 0; i != n; i++) {
void *tag = java_call_nbttag_list_get_compound_tag_at(ctx->jvm, list, i);
unsigned short nbttag_compound_get_short = (unsigned short)java_jni_call_short_method_nbttag_compound_get_short(ctx->jvm, tag, idS);
if (nbttag_compound_get_short > 0x3e) {
(*env)->DeleteLocalRef(env, tag);
continue;
}
unsigned short w = weights[nbttag_compound_get_short];
if (w == 0) {
(*env)->DeleteLocalRef(env, tag);
continue;
}
short lvl = java_jni_call_short_method_nbttag_compound_get_short(ctx->jvm, tag, lvlS);
sum += lvl < 0 ? 0 : (unsigned short)(w * lvl);
(*env)->DeleteLocalRef(env, tag);
}
(*env)->DeleteLocalRef(env, idS);
(*env)->DeleteLocalRef(env, lvlS);
return sum;
}
extern "C" const unsigned char enchant_level_weights_c[], enchant_level_weights_b[], enchant_level_weights_a[], tier_base_levels[];
// score of an item for an inventory-sorter category (higher = better); cases decoded from the image jump tables
extern "C" int score_item_inventory_sorter_category(Ctx *ctx, int cat, int id, unsigned meta, void *nbt, unsigned char b) {
switch (cat) {
case 1: case 14: case 15: case 16: {
ToolInfo ti = chest_gui_title_id(id);
if (cat != ti.kind) return 0;
int base = ti.tier * 100;
return weighted_sum_enchantment_levels(ctx, nbt, enchant_level_weights_c) + base;
}
case 2:
switch (id) {
case 0x1: case 0x2: case 0x3: case 0x4: case 0x5: case 0x11: case 0x12: case 0x14: case 0x18: case 0x23:
case 0x2d: case 0x31: case 0x4e: case 0x57: case 0x5f: case 0x62: case 0x79: case 0x7d: case 0x85:
case 0x9b: case 0x9f: case 0xa2:
return 1;
default:
return 0;
}
case 3:
if (id != 0x105) return 0;
return weighted_sum_enchantment_levels(ctx, nbt, enchant_level_weights_b) + 1;
case 4: return id == 0x36;
case 5: return id == 0x170 ? 2 - b : 0;
case 6: return id == 0x181;
case 7: return id == 0x15a;
case 8: return id == 0x118;
case 9:
if (id != 0x142) return 0;
return (meta == 1) + 1;
case 10: case 11: case 19: case 21: {
if (id != 0x175) return 0;
unsigned short base;
switch (meta & 0xf) {
case 1: base = 1; if (cat != 10) return 0; break;
case 2: base = 1; if (cat != 19) return 0; break;
case 5: base = 2; if (cat != 10) return 0; break;
case 9: base = 1; if (cat != 21) return 0; break;
case 11: base = 1; if (cat != 11) return 0; break;
default: return 0;
}
return (unsigned short)(base + ((meta >> 6) & 1) + ((meta >> 5) & 1));
}
case 12: return id == 0x41;
case 13: return id == 0x147;
case 17: return id == 0x14c || id == 0x158;
case 18: return id == 0x19d || id == 0x11a;
case 20: return id == 0x103;
case 22: {
ToolInfo ti = chest_gui_title_id(id);
if (ti.kind != 0x16) return 0;
int base = tier_base_levels[ti.tier] * 40;
return weighted_sum_enchantment_levels(ctx, nbt, enchant_level_weights_a) + base + 0xa0;
}
case 23: return id == 0x2e;
case 24: return id == 0x146;
case 25: {
ToolInfo ti = chest_gui_title_id(id);
unsigned short k;
switch (ti.kind) {
case 1: k = 3; break;
case 14: k = 2; break;
case 16: k = 1; break;
case 22: k = 4; break;
default: k = 0; break;
}
int base = (k + tier_base_levels[ti.tier]) * 40;
return weighted_sum_enchantment_levels(ctx, nbt, enchant_level_weights_a) + base;
}
case 26: return id == 0x23;
default: return 0;
}
}
+24
View File
@@ -0,0 +1,24 @@
// 0x4f000: cJSON (upstream source) - Add*ToObject, Create*
#include "slinky/engine.h"
#include "slinky/cjson.h"
extern "C" cJSON *cjson_add_string_to_object(cJSON *const object, const char *const name, const char *const string) { // cJSON_AddStringToObject
cJSON *string_item = cJSON_CreateString(string);
if (add_item_to_object(object, name, string_item, &global_hooks, false)) return string_item;
cjson_delete(string_item);
return NULL;
}
extern "C" cJSON *cjson_create_string(const char *string) { return cJSON_CreateString(string); } // cJSON_CreateString
extern "C" cJSON *cjson_add_bool_to_object(cJSON *const object, const char *const name) { // cJSON_AddObjectToObject
cJSON *object_item = cJSON_CreateObject();
if (add_item_to_object(object, name, object_item, &global_hooks, false)) return object_item;
cjson_delete(object_item);
return NULL;
}
extern "C" cJSON *cjson_create_object(void) { return cJSON_CreateObject(); } // cJSON_CreateObject
extern "C" cJSON *cjson_add_array_to_object(cJSON *const object, const char *const name) { // cJSON_AddArrayToObject
cJSON *array = cJSON_CreateArray();
if (add_item_to_object(object, name, array, &global_hooks, false)) return array;
cjson_delete(array);
return NULL;
}
+130
View File
@@ -0,0 +1,130 @@
// 0x4d000: cJSON (upstream source, MIT) - core: delete, parse entry, print entry, accessors.
#include "slinky/engine.h"
#include "slinky/cjson.h"
extern "C" char cjson_core_buffer[];
FN(void *, libc_register_onexit_function, (void *jvm, void *b));
extern "C" void *cjson_strdup(void *x) { return libc_register_onexit_function(cjson_core_buffer, x); }
extern "C" char *cjson_get_string_value(const cJSON *item) { // cJSON_GetStringValue
if (!cJSON_IsString(item)) return NULL;
return item->valuestring;
}
extern "C" cJSON_bool cjson_is_string(const cJSON *item) { return cJSON_IsString(item); }
extern "C" double cjson_get_number_value(const cJSON *item) { // cJSON_GetNumberValue
double d = not_a_number;
if (cJSON_IsNumber(item)) d = item->valuedouble;
return d;
}
extern "C" cJSON_bool cjson_is_number(const cJSON *item) { return cJSON_IsNumber(item); }
extern "C" void cjson_delete(cJSON *item) { // cJSON_Delete
cJSON *next = NULL;
while (item != NULL) {
next = item->next;
if (!(item->type & cJSON_IsReference) && (item->child != NULL)) cjson_delete(item->child);
if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) global_hooks.deallocate(item->valuestring);
if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) global_hooks.deallocate(item->string);
global_hooks.deallocate(item);
item = next;
}
}
extern "C" void cjson_free_string(void *object) { global_hooks.deallocate(object); } // cJSON_free
static parse_buffer *buffer_skip_whitespace(parse_buffer *const buffer) {
if ((buffer == NULL) || (buffer->content == NULL)) return NULL;
if (cannot_access_at_index(buffer, 0)) return buffer;
while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) buffer->offset++;
if (buffer->offset == buffer->length) buffer->offset--;
return buffer;
}
// buffer_skip_whitespace with its unused return value dropped (the original compiler did this for the
// out-of-line copy).
extern "C" void buffer_skip_whitespace_unused_return(parse_buffer *const buffer) {
if ((buffer == NULL) || (buffer->content == NULL)) return;
if (cannot_access_at_index(buffer, 0)) return;
while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) buffer->offset++;
if (buffer->offset == buffer->length) buffer->offset--;
}
static parse_buffer *skip_utf8_bom(parse_buffer *const buffer) {
if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) return NULL;
if (can_access_at_index(buffer, 4) && (__builtin_strncmp((const char *)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0))
buffer->offset += 3;
return buffer;
}
FN(cJSON_bool, cjson_parse_value, (cJSON *const item, parse_buffer *const input_buffer)); // parse_value
extern "C" cJSON *cjson_parse_with_options(const char *value, size_t buffer_length, const char **return_parse_end,
cJSON_bool require_null_terminated) { // cJSON_ParseWithLengthOpts
parse_buffer buffer = {0, 0, 0, 0, {0, 0, 0}};
cJSON *item = NULL;
global_error.json = NULL;
global_error.position = 0;
if (value == NULL || 0 == buffer_length) goto fail;
buffer.content = (const unsigned char *)value;
buffer.length = buffer_length;
buffer.offset = 0;
buffer.hooks = global_hooks;
item = cJSON_New_Item(&global_hooks);
if (item == NULL) goto fail;
if (!cjson_parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) goto fail;
if (require_null_terminated) {
buffer_skip_whitespace(&buffer);
if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') goto fail;
}
if (return_parse_end) *return_parse_end = (const char *)buffer_at_offset(&buffer);
return item;
fail:
if (item != NULL) cjson_delete(item);
if (value != NULL) {
error local_error;
local_error.json = (const unsigned char *)value;
local_error.position = 0;
if (buffer.offset < buffer.length) local_error.position = buffer.offset;
else if (buffer.length > 0) local_error.position = buffer.length - 1;
if (return_parse_end != NULL) *return_parse_end = (const char *)local_error.json + local_error.position;
global_error = local_error;
}
return NULL;
}
extern "C" cJSON *cjson_parse(const char *value) { // cJSON_Parse
size_t buffer_length;
if (NULL == value) return NULL;
buffer_length = strlen(value) + sizeof("");
return cjson_parse_with_options(value, buffer_length, 0, 0);
}
FN(cJSON_bool, cjson_print_value, (const cJSON *const item, printbuffer *const output_buffer)); // print_value
extern "C" unsigned char *cjson_print(const cJSON *const item, cJSON_bool format) { // print (global hooks)
static const size_t default_buffer_size = 256;
printbuffer buffer[1];
unsigned char *printed = NULL;
const internal_hooks *const hooks = &global_hooks;
memset(buffer, 0, sizeof(buffer));
buffer->buffer = (unsigned char *)hooks->allocate(default_buffer_size);
buffer->length = default_buffer_size;
buffer->format = format;
buffer->hooks = *hooks;
if (buffer->buffer == NULL) goto fail;
if (!cjson_print_value(item, buffer)) goto fail;
update_offset(buffer);
if (hooks->reallocate != NULL) {
printed = (unsigned char *)hooks->reallocate(buffer->buffer, buffer->offset + 1);
if (printed == NULL) goto fail;
buffer->buffer = NULL;
} else {
printed = (unsigned char *)hooks->allocate(buffer->offset + 1);
if (printed == NULL) goto fail;
memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1));
printed[buffer->offset] = '\0';
hooks->deallocate(buffer->buffer);
}
return printed;
fail:
if (buffer->buffer != NULL) hooks->deallocate(buffer->buffer);
return NULL;
}
extern "C" char *cjson_print_unformatted(const cJSON *item) { return (char *)cjson_print(item, false); } // cJSON_PrintUnformatted
+237
View File
@@ -0,0 +1,237 @@
// 0x50000: cJSON (upstream source) - Duplicate, Is*, Compare
#include "slinky/engine.h"
#include "slinky/cjson.h"
extern "C" cJSON *cjson_duplicate(const cJSON *item, cJSON_bool recurse) { // cJSON_Duplicate
cJSON *newitem = NULL;
cJSON *child = NULL;
cJSON *next = NULL;
cJSON *newchild = NULL;
if (!item) goto fail;
newitem = cJSON_New_Item(&global_hooks);
if (!newitem) goto fail;
newitem->type = item->type & (~cJSON_IsReference);
newitem->valueint = item->valueint;
newitem->valuedouble = item->valuedouble;
if (item->valuestring) {
newitem->valuestring = (char *)cJSON_strdup((unsigned char *)item->valuestring, &global_hooks);
if (!newitem->valuestring) goto fail;
}
if (item->string) {
newitem->string = (item->type & cJSON_StringIsConst) ? item->string
: (char *)cJSON_strdup((unsigned char *)item->string, &global_hooks);
if (!newitem->string) goto fail;
}
if (!recurse) return newitem;
child = item->child;
while (child != NULL) {
newchild = cjson_duplicate(child, true);
if (!newchild) goto fail;
if (next != NULL) {
next->next = newchild;
newchild->prev = next;
next = newchild;
} else {
newitem->child = newchild;
next = newchild;
}
child = child->next;
}
if (newitem && newitem->child) newitem->child->prev = newchild;
return newitem;
fail:
if (newitem != NULL) cjson_delete(newitem);
return NULL;
}
extern "C" cJSON_bool cjson_is_true(const cJSON *const item) { // cJSON_IsTrue
if (item == NULL) return false;
return (item->type & 0xff) == cJSON_True;
}
extern "C" cJSON_bool cjson_is_bool(const cJSON *const item) { // cJSON_IsBool
if (item == NULL) return false;
return (item->type & (cJSON_True | cJSON_False)) != 0;
}
extern "C" cJSON_bool cjson_is_array(const cJSON *const item) { // cJSON_IsArray
if (item == NULL) return false;
return (item->type & 0xFF) == cJSON_Array;
}
extern "C" cJSON_bool cjson_is_object(const cJSON *const item) { // cJSON_IsObject
if (item == NULL) return false;
return (item->type & 0xFF) == cJSON_Object;
}
typedef double v2d __attribute__((vector_size(16)));
extern "C" v2d cjson_duplicate_fabs_mask; // fabs mask
extern "C" double double_epsilon; // DBL_EPSILON
static inline __attribute__((always_inline)) double cj_fabs(double x) {
v2d v = {x, 0};
v = (v2d)((__attribute__((vector_size(16))) long long)v & (__attribute__((vector_size(16))) long long)cjson_duplicate_fabs_mask);
return v[0];
}
static inline __attribute__((always_inline)) cJSON_bool compare_double(double left, double right) {
double maxVal = cj_fabs(left) > cj_fabs(right) ? cj_fabs(left) : cj_fabs(right);
return (cj_fabs(left - right) <= maxVal * double_epsilon);
}
FN(cJSON *, cjson_get_object_item_case_option, (const cJSON *const object, const char *const name, const cJSON_bool case_sensitive)); // get_object_item
extern "C" cJSON_bool cjson_compare(const cJSON *const a, const cJSON *const b, const cJSON_bool case_sensitive) { // cJSON_Compare
if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF))) return false;
switch (a->type & 0xFF) {
case cJSON_False: case cJSON_True: case cJSON_NULL: case cJSON_Number: case cJSON_String: case cJSON_Raw:
case cJSON_Array: case cJSON_Object:
break;
default:
return false;
}
if (a == b) return true;
switch (a->type & 0xFF) {
case cJSON_False: case cJSON_True: case cJSON_NULL:
return true;
case cJSON_Number:
if (compare_double(a->valuedouble, b->valuedouble)) return true;
return false;
case cJSON_String: case cJSON_Raw:
if ((a->valuestring == NULL) || (b->valuestring == NULL)) return false;
if (strcmp(a->valuestring, b->valuestring) == 0) return true;
return false;
case cJSON_Array: {
cJSON *a_element = a->child;
cJSON *b_element = b->child;
for (; (a_element != NULL) && (b_element != NULL);) {
if (!cjson_compare(a_element, b_element, case_sensitive)) return false;
a_element = a_element->next;
b_element = b_element->next;
}
if (a_element != b_element) return false;
return true;
}
case cJSON_Object: {
cJSON *a_element = NULL;
cJSON *b_element = NULL;
for (a_element = a->child; a_element != NULL; a_element = a_element->next) {
b_element = cjson_get_object_item_case_option(b, a_element->string, case_sensitive);
if (b_element == NULL) return false;
if (!cjson_compare(a_element, b_element, case_sensitive)) return false;
}
for (b_element = b->child; b_element != NULL; b_element = b_element->next) {
a_element = cjson_get_object_item_case_option(a, b_element->string, case_sensitive);
if (a_element == NULL) return false;
if (!cjson_compare(b_element, a_element, case_sensitive)) return false;
}
return true;
}
default:
return false;
}
}
FN(unsigned, cjson_parse_hex4, (const unsigned char *const input)); // parse_hex4
static inline __attribute__((always_inline)) unsigned char
utf16_literal_to_utf8(const unsigned char *const input_pointer, const unsigned char *const input_end, unsigned char **output_pointer) {
long unsigned int codepoint = 0;
unsigned int first_code = 0;
const unsigned char *first_sequence = input_pointer;
unsigned char utf8_length = 0;
unsigned char utf8_position = 0;
unsigned char sequence_length = 0;
unsigned char first_byte_mark = 0;
if ((input_end - first_sequence) < 6) goto fail;
first_code = cjson_parse_hex4(first_sequence + 2);
if (((first_code >= 0xDC00) && (first_code <= 0xDFFF))) goto fail;
if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) {
const unsigned char *second_sequence = first_sequence + 6;
unsigned int second_code = 0;
sequence_length = 12;
if ((input_end - second_sequence) < 6) goto fail;
if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) goto fail;
second_code = cjson_parse_hex4(second_sequence + 2);
if ((second_code < 0xDC00) || (second_code > 0xDFFF)) goto fail;
codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF));
} else {
sequence_length = 6;
codepoint = first_code;
}
if (codepoint < 0x80) {
utf8_length = 1;
} else if (codepoint < 0x800) {
utf8_length = 2;
first_byte_mark = 0xC0;
} else if (codepoint < 0x10000) {
utf8_length = 3;
first_byte_mark = 0xE0;
} else if (codepoint <= 0x10FFFF) {
utf8_length = 4;
first_byte_mark = 0xF0;
} else {
goto fail;
}
for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) {
(*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF);
codepoint >>= 6;
}
if (utf8_length > 1) (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF);
else (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F);
*output_pointer += utf8_length;
return sequence_length;
fail:
return 0;
}
extern "C" cJSON_bool cjson_replace_item_in_object(cJSON *const item, parse_buffer *const input_buffer) { // parse_string
const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1;
const unsigned char *input_end = buffer_at_offset(input_buffer) + 1;
unsigned char *output_pointer = NULL;
unsigned char *output = NULL;
if (buffer_at_offset(input_buffer)[0] != '\"') goto fail;
{
size_t allocation_length = 0;
size_t skipped_bytes = 0;
while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"')) {
if (input_end[0] == '\\') {
if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) goto fail;
skipped_bytes++;
input_end++;
}
input_end++;
}
if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"')) goto fail;
allocation_length = (size_t)(input_end - buffer_at_offset(input_buffer)) - skipped_bytes;
output = (unsigned char *)input_buffer->hooks.allocate(allocation_length + sizeof(""));
if (output == NULL) goto fail;
}
output_pointer = output;
while (input_pointer < input_end) {
if (*input_pointer != '\\') {
*output_pointer++ = *input_pointer++;
} else {
unsigned char sequence_length = 2;
if ((input_end - input_pointer) < 1) goto fail;
switch (input_pointer[1]) {
case 'b': *output_pointer++ = '\b'; break;
case 'f': *output_pointer++ = '\f'; break;
case 'n': *output_pointer++ = '\n'; break;
case 'r': *output_pointer++ = '\r'; break;
case 't': *output_pointer++ = '\t'; break;
case '\"': case '\\': case '/': *output_pointer++ = input_pointer[1]; break;
case 'u':
sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer);
if (sequence_length == 0) goto fail;
break;
default:
goto fail;
}
input_pointer += sequence_length;
}
}
*output_pointer = '\0';
item->type = cJSON_String;
item->valuestring = (char *)output;
input_buffer->offset = (size_t)(input_end - input_buffer->content);
input_buffer->offset++;
return true;
fail:
if (output != NULL) input_buffer->hooks.deallocate(output);
if (input_pointer != NULL) input_buffer->offset = (size_t)(input_pointer - input_buffer->content);
return false;
}
+28
View File
@@ -0,0 +1,28 @@
// 0x4e000: cJSON (upstream source) - object lookup, array/object building
#include "slinky/engine.h"
#include "slinky/cjson.h"
extern "C" cJSON *cjson_get_object_item(const cJSON *const object, const char *const string) { // cJSON_GetObjectItemCaseSensitive
cJSON *current_element = NULL;
if ((object == NULL) || (string == NULL)) return NULL;
current_element = object->child;
while ((current_element != NULL) && (current_element->string != NULL) &&
(strcmp(string, current_element->string) != 0))
current_element = current_element->next;
if ((current_element == NULL) || (current_element->string == NULL)) return NULL;
return current_element;
}
extern "C" cJSON_bool cjson_add_item_to_array(cJSON *array, cJSON *item) { return add_item_to_array(array, item); } // cJSON_AddItemToArray
extern "C" cJSON *cjson_add_item_to_object(cJSON *const object, const char *const name, const cJSON_bool boolean) { // cJSON_AddBoolToObject
cJSON *bool_item = cJSON_CreateBool(boolean);
if (add_item_to_object(object, name, bool_item, &global_hooks, false)) return bool_item;
cjson_delete(bool_item);
return NULL;
}
extern "C" cJSON *cjson_add_number_to_object(cJSON *const object, const char *const name, const double number) { // cJSON_AddNumberToObject
cJSON *number_item = cJSON_CreateNumber(number);
if (add_item_to_object(object, name, number_item, &global_hooks, false)) return number_item;
cjson_delete(number_item);
return NULL;
}
+169
View File
@@ -0,0 +1,169 @@
// 0x51000: cJSON (upstream source) - parse_hex4, ensure, print_string_ptr, parse_array/object
#include "slinky/engine.h"
#include "slinky/cjson.h"
extern "C" unsigned cjson_parse_hex4(const unsigned char *const input) { // parse_hex4
unsigned int h = 0;
size_t i = 0;
for (i = 0; i < 4; i++) {
if ((input[i] >= '0') && (input[i] <= '9')) h += (unsigned int)input[i] - '0';
else if ((input[i] >= 'A') && (input[i] <= 'F')) h += (unsigned int)10 + input[i] - 'A';
else if ((input[i] >= 'a') && (input[i] <= 'f')) h += (unsigned int)10 + input[i] - 'a';
else return 0;
if (i < 3) h = h << 4;
}
return h;
}
extern "C" unsigned char *cjson_print_string(printbuffer *const p, size_t needed) { // ensure
unsigned char *newbuffer = NULL;
size_t newsize = 0;
if ((p == NULL) || (p->buffer == NULL)) return NULL;
if ((p->length > 0) && (p->offset >= p->length)) return NULL;
if (needed > INT_MAX) return NULL;
needed += p->offset + 1;
if (needed <= p->length) return p->buffer + p->offset;
if (p->noalloc) return NULL;
if (needed > (INT_MAX / 2)) {
if (needed <= INT_MAX) newsize = INT_MAX;
else return NULL;
} else {
newsize = needed * 2;
}
if (p->hooks.reallocate != NULL) {
newbuffer = (unsigned char *)p->hooks.reallocate(p->buffer, newsize);
if (newbuffer == NULL) {
p->hooks.deallocate(p->buffer);
p->length = 0;
p->buffer = NULL;
return NULL;
}
} else {
newbuffer = (unsigned char *)p->hooks.allocate(newsize);
if (!newbuffer) {
p->hooks.deallocate(p->buffer);
p->length = 0;
p->buffer = NULL;
return NULL;
}
memcpy(newbuffer, p->buffer, p->offset + 1);
p->hooks.deallocate(p->buffer);
}
p->length = newsize;
p->buffer = newbuffer;
return newbuffer + p->offset;
}
extern "C" int libc_sprintf(char *buf, const char *fmt, ...); // sprintf
extern "C" const char fmt_u_hex4[]; // "u%04x"
// print_string_ptr (the output_buffer NULL check was folded away by the original compiler: all callers pass one)
extern "C" cJSON_bool cjson_print_string_ptr(const unsigned char *const input, printbuffer *const output_buffer) {
const unsigned char *input_pointer = NULL;
unsigned char *output = NULL;
unsigned char *output_pointer = NULL;
size_t output_length = 0;
size_t escape_characters = 0;
if (input == NULL) {
output = cjson_print_string(output_buffer, sizeof("\"\""));
if (output == NULL) return false;
output[0] = '"'; output[1] = '"'; output[2] = '\0';
return true;
}
for (input_pointer = input; *input_pointer; input_pointer++) {
switch (*input_pointer) {
case '\"': case '\\': case '\b': case '\f': case '\n': case '\r': case '\t':
escape_characters++;
break;
default:
if (*input_pointer < 32) escape_characters += 5;
break;
}
}
output_length = (size_t)(input_pointer - input) + escape_characters;
output = cjson_print_string(output_buffer, output_length + sizeof("\"\""));
if (output == NULL) return false;
if (escape_characters == 0) {
output[0] = '\"';
memcpy(output + 1, input, output_length);
output[output_length + 1] = '\"';
output[output_length + 2] = '\0';
return true;
}
output[0] = '\"';
output_pointer = output + 1;
for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) {
if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) {
*output_pointer = *input_pointer;
} else {
*output_pointer++ = '\\';
switch (*input_pointer) {
case '\\': *output_pointer = '\\'; break;
case '\"': *output_pointer = '\"'; break;
case '\b': *output_pointer = 'b'; break;
case '\f': *output_pointer = 'f'; break;
case '\n': *output_pointer = 'n'; break;
case '\r': *output_pointer = 'r'; break;
case '\t': *output_pointer = 't'; break;
default:
libc_sprintf((char *)output_pointer, fmt_u_hex4, *input_pointer);
output_pointer += 4;
break;
}
}
}
output[output_length + 1] = '\"';
output[output_length + 2] = '\0';
return true;
}
FN(cJSON_bool, cjson_replace_item_in_object, (cJSON *const item, parse_buffer *const input_buffer)); // parse_string
FN(cJSON_bool, cjson_parse_value, (cJSON *const item, parse_buffer *const input_buffer)); // parse_value
extern "C" cJSON_bool cjson_parse_array(cJSON *const item, parse_buffer *const input_buffer) { // parse_object
cJSON *head = NULL;
cJSON *current_item = NULL;
if (input_buffer->depth >= CJSON_NESTING_LIMIT) return false;
input_buffer->depth++;
if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) goto fail;
input_buffer->offset++;
buffer_skip_whitespace_i(input_buffer);
if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) goto success;
if (cannot_access_at_index(input_buffer, 0)) {
input_buffer->offset--;
goto fail;
}
input_buffer->offset--;
do {
cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks));
if (new_item == NULL) goto fail;
if (head == NULL) {
current_item = head = new_item;
} else {
current_item->next = new_item;
new_item->prev = current_item;
current_item = new_item;
}
input_buffer->offset++;
buffer_skip_whitespace_i(input_buffer);
if (!cjson_replace_item_in_object(current_item, input_buffer)) goto fail;
buffer_skip_whitespace_i(input_buffer);
current_item->string = current_item->valuestring;
current_item->valuestring = NULL;
if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) goto fail;
input_buffer->offset++;
buffer_skip_whitespace_i(input_buffer);
if (!cjson_parse_value(current_item, input_buffer)) goto fail;
buffer_skip_whitespace_i(input_buffer);
} while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ','));
if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) goto fail;
success:
input_buffer->depth--;
if (head != NULL) head->prev = current_item;
item->type = cJSON_Object;
item->child = head;
input_buffer->offset++;
return true;
fail:
if (head != NULL) cjson_delete(head);
return false;
}
+32
View File
@@ -0,0 +1,32 @@
// 0x3d000: comparators
#include "slinky/engine.h"
extern "C" int sort_key_table[];
extern "C" int compare_module_height(char *left, char *right) { return (int)(*(float *)(right + 0x48) - *(float *)(left + 0x48)); }
extern "C" int compare_key_table(short *left, short *right) { return sort_key_table[*left] - sort_key_table[*right]; }
// release the cached player records: their two global refs and five lists of global refs
#include "slinky/jids.h"
FN(void, libc_free, (void *ptr));
struct RefList { int padding_00; int n; jobject *refs; };
struct PRec { char padding_00[0x30]; jobject ref_primary, ref_secondary; char padding_40[0x10]; RefList lists[5]; };
extern "C" unsigned long long name_tags_count;
extern "C" PRec name_tags_table[];
extern "C" void entity_list_for_render(Jvm *jvm) {
if (name_tags_count) {
JNIEnv *env = jvm->env;
for (unsigned long long i = 0; i < name_tags_count; i++) {
PRec *p = &name_tags_table[i];
if (p->ref_secondary) (*env)->DeleteGlobalRef(env, p->ref_secondary);
if (p->ref_primary) (*env)->DeleteGlobalRef(env, p->ref_primary);
#pragma clang loop unroll(full)
for (int k = 0; k < 5; k++) {
RefList *l = &p->lists[k];
if (!l->refs) continue;
for (long long m = 0; m < l->n; m++) (*env)->DeleteGlobalRef(env, l->refs[m]);
libc_free(l->refs);
l->refs = 0;
}
}
}
name_tags_count = 0;
}
@@ -0,0 +1,74 @@
// helper functions (formerly address block; names inferred from behaviour).
#include "slinky/engine.h"
extern "C" char free_look_module_descriptor[]; extern "C" unsigned char free_look_active; extern "C" void *free_look_saved_camera;
extern "C" int free_look_saved_perspective, free_look_target_perspective_value;
struct Ev { char padding_00[8]; Ctx *ctx; void *tag; unsigned char on; };
FN(unsigned char, is_silent_rotation_active, ());
FN(void *, get_silent_rotation, ());
FN(void *, get_entity_rotation, (void *jvm, void *entity));
FN(void *, get_game_settings, (void *jvm, void *object));
FN(int, java_get_game_settings_third_person_view, (void *jvm, void *game_settings));
FN(void, java_set_game_settings_third_person_view, (void *jvm, void *game_settings, int value));
FN(void, set_render_rotation, (void *cam));
FN(void *, java_call_item_stack_get_enchantment_tag_list, (void *jvm, void *stack));
FN(int, java_call_nbttag_list_tag_count, (void *jvm, void *list));
FN(void *, java_call_nbttag_list_get_compound_tag_at, (void *jvm, void *list, int i));
FN(short, java_jni_call_short_method_nbttag_compound_get_short, (void *jvm, void *tag, void *key));
struct Jvm2 { void *ids; struct JE **env; };
struct JE { char padding_00[0x538]; void *(*NewStringUTF)(void *, const char *); };
extern "C" const char str_id_in_free_look_and_enchants[], str_lvl[];
extern "C" void free_look_on_toggle(Ev *event) {
if (event->tag != free_look_module_descriptor)
return;
Ctx *ctx = event->ctx;
if (!ctx->world)
return;
if (event->on == 1) {
free_look_active = 1;
void *cam;
if (is_silent_rotation_active())
cam = get_silent_rotation();
else
cam = get_entity_rotation(ctx->jvm, ctx->player);
free_look_saved_camera = cam;
}
void *x = get_game_settings(ctx->jvm, ctx->minecraft);
void *jvm = ctx->jvm;
if (event->on != 0) {
free_look_saved_perspective = java_get_game_settings_third_person_view(jvm, x);
void *jvm2 = ctx->jvm;
unsigned persp = free_look_target_perspective_value;
java_set_game_settings_third_person_view(jvm2, x, persp);
return;
}
java_set_game_settings_third_person_view(jvm, x, free_look_saved_perspective);
if (free_look_active == 1) {
set_render_rotation(free_look_saved_camera);
free_look_active = 0;
}
}
extern "C" void free_look_save_rotation(char *p) { p[8] = 1; *(void **)(p + 0xc) = free_look_saved_camera; }
// enchantment flags of an item stack: 1 = id 16, 2 = id 19, 4 = id 20
extern "C" unsigned get_weapon_enchant_flags(Jvm2 *j, void *stack) {
void *list = java_call_item_stack_get_enchantment_tag_list(j, stack);
unsigned flags = 0;
if (!list) return flags;
JE **env = j->env;
void *idS = (*env)->NewStringUTF(env, str_id_in_free_look_and_enchants);
if (!idS) return flags;
if (!(*env)->NewStringUTF(env, str_lvl)) return flags;
int n = java_call_nbttag_list_tag_count(j, list);
if (n <= 0) return flags;
flags = 0;
int i = 0;
do {
void *tag = java_call_nbttag_list_get_compound_tag_at(j, list, i);
switch (java_jni_call_short_method_nbttag_compound_get_short(j, tag, idS)) {
case 0x10: flags |= 1; break;
case 0x13: flags |= 2; break;
case 0x14: flags |= 4; break;
}
} while (++i != n);
return flags;
}
+212
View File
@@ -0,0 +1,212 @@
// 0x3b000: friend-list style 32-byte key set (sorted vector) with chat feedback
#include "slinky/engine.h"
struct Key { unsigned long long hi, lo; };
struct Ent { char b[0x20]; };
extern "C" void *friend_set_friend_table; extern "C" char friend_set_table_meta[]; extern "C" int friend_set_dirty_count;
extern "C" const char str_sect_aadded_s_as_friend[], str_sect_aremoved_s_as_friend[], empty_plural_suffix[], str_s_in_friend_set[], str_sect_aremoved_zu_friend_s[];
FN(unsigned char, friend_resolve, (void *c, Key k, Ent *e, char *name));
FN(void *, vector_find_insert, (void *t, int elem, void *hdr, void *e, void *cmp));
FN(void *, binary_search, (void *t, int elem, void *hdr, void *e, void *cmp));
FN(void, vector_erase_value, (void *t, int elem, void *hdr, void *at));
extern "C" void jni_throw_format(void *jvm, const char *fmt, ...);
extern "C" int compare_friend_key(void *, unsigned long long *right, unsigned long long *b) {
unsigned long long x = __builtin_bswap64(right[0]);
asm("" : "+r"(x));
unsigned long long y = __builtin_bswap64(b[0]);
if (x == y) {
unsigned long long a1 = right[1], b1 = b[1];
asm("" : "+r"(a1), "+r"(b1));
x = __builtin_bswap64(a1);
y = __builtin_bswap64(b1);
asm("" : "+r"(x), "+r"(y));
if (x == y) return 0;
}
return x >= y ? 1 : -1;
}
extern "C" unsigned char friend_set_insert(void **c, Key k) {
Ent e; char name[0x20];
unsigned char ok = friend_resolve(c, k, &e, name);
if (!ok) return ok;
vector_find_insert(&friend_set_friend_table, 0x20, friend_set_table_meta, &e, (void *)compare_friend_key);
jni_throw_format(*c, str_sect_aadded_s_as_friend, name);
return ok;
}
extern "C" unsigned char friend_set_erase(void **c, Key k) {
Ent e; char name[0x20];
unsigned char ok = friend_resolve(c, k, &e, name);
if (!ok) return ok;
void *entry = binary_search(friend_set_friend_table, 0x20, friend_set_table_meta, &e, (void *)compare_friend_key);
if (entry) vector_erase_value(friend_set_friend_table, 0x20, friend_set_table_meta, entry);
jni_throw_format(*c, str_sect_aremoved_s_as_friend, name);
return ok;
}
extern "C" bool friend_set_clear(void **c) {
unsigned long long n = (unsigned)friend_set_dirty_count;
void *jvm = *c;
jni_throw_format(jvm, str_sect_aremoved_zu_friend_s, n, n == 1 ? empty_plural_suffix : str_s_in_friend_set);
friend_set_dirty_count = 0;
return true;
}
extern "C" int friend_set_dirty() { return friend_set_dirty_count; }
#include "slinky/jids.h"
FN(void *, libc_memcpy_alt, (void *dest, const void *src, unsigned long long n));
FN(void *, java_get_world_player_entities, (void *jvm, void *world));
FN(int, java_call_list_size, (void *jvm, void *list));
FN(void *, java_call_list_get, (void *jvm, void *list, int i));
FN(jstring, java_call_entity_get_command_sender_name, (void *jvm, void *entity));
FN(void *, java_get_entity_uuid, (void *jvm, void *entity));
FN(unsigned long long, java_jni_call_long_method_uuid_get_most_significant_bits, (void *jvm, void *uuid));
FN(unsigned long long, java_jni_call_long_method_uuid_get_least_significant_bits, (void *jvm, void *uuid));
extern "C" const char str_sect_cusername_is_too_long[], str_sect_cfound_multiple_names_sta[], str_sect_cplayer_not_found[];
struct Str { const char *s; unsigned long long len; };
struct Uid { unsigned long long hi, lo, z0, z1; };
struct C3b { Jvm *jvm; char padding_08[0x18]; void *world; void *self; };
static inline __attribute__((always_inline)) unsigned char fold(unsigned char ch) {
return (unsigned char)(((unsigned char)(ch - 0x41) < 0x1a) << 5) + ch;
}
// resolve a (unique, case-insensitive) player-name prefix to its UUID; prints the reason on failure
extern "C" unsigned char friend_resolve(void *cv, Key kk, Ent *eo, char *name) {
C3b *c = (C3b *)cv;
Str k = __builtin_bit_cast(Str, kk);
Uid *out = (Uid *)eo;
unsigned long long len = k.len;
if (len >= 0x11) { jni_throw_format(c->jvm, str_sect_cusername_is_too_long); return false; }
const char *s = k.s;
JNIEnv *env = c->jvm->env;
char buf[0x18];
libc_memcpy_alt(buf, s, len);
buf[len] = 0;
if (!(*env)->NewStringUTF(env, buf)) return false;
void *list = java_get_world_player_entities(c->jvm, c->world);
if (java_call_list_size(c->jvm, list) <= 0) { jni_throw_format(c->jvm, str_sect_cplayer_not_found); return false; }
void *found = 0;
for (int i = 0; i < java_call_list_size(c->jvm, list); i++) {
void *element = java_call_list_get(c->jvm, list, i);
JNIEnv *e2 = c->jvm->env;
if ((*e2)->IsSameObject(e2, (jobject)element, (jobject)c->self)) continue;
jstring nm = java_call_entity_get_command_sender_name(c->jvm, element);
int L = (*env)->GetStringUTFLength(env, nm);
if (len > (unsigned long long)(long long)L) continue;
const char *chars = (*env)->GetStringUTFChars(env, nm, 0);
unsigned long long j;
for (j = 0; j < len; j++)
if (fold(s[j]) != fold(chars[j])) break;
if (j == len) {
if (found) {
(*env)->ReleaseStringUTFChars(env, nm, chars);
jni_throw_format(c->jvm, str_sect_cfound_multiple_names_sta, (int)len, s);
return false;
}
int n2 = L >= 0x10 ? 0x10 : L;
libc_memcpy_alt(name, chars, (long long)n2);
name[n2] = 0;
found = element;
}
(*env)->ReleaseStringUTFChars(env, nm, chars);
}
if (!found) { jni_throw_format(c->jvm, str_sect_cplayer_not_found); return false; }
void *uuid = java_get_entity_uuid(c->jvm, found);
unsigned long long hi = java_jni_call_long_method_uuid_get_most_significant_bits(c->jvm, uuid);
out->lo = java_jni_call_long_method_uuid_get_least_significant_bits(c->jvm, uuid);
out->hi = hi;
out->z0 = 0; out->z1 = 0;
return true;
}
// hex string of friend entry i (first 24 bytes: the UUID + name hash), in a static buffer
FN(unsigned char *, element_at, (void *vec, unsigned elem, void *hdr, unsigned i));
extern "C" const char str_0123456789abcdef[]; // "0123456789abcdef"
extern "C" char friend_hex_buffer[];
extern "C" char *friend_set_count(void *, unsigned i) {
const unsigned char *e = element_at(friend_set_friend_table, 0x20, friend_set_table_meta, i);
for (int k = 0; k < 0x18; k++) {
friend_hex_buffer[2 * k] = str_0123456789abcdef[e[k] >> 4];
friend_hex_buffer[2 * k + 1] = str_0123456789abcdef[e[k] & 0xf];
}
friend_hex_buffer[0x30] = 0;
return friend_hex_buffer;
}
// add every player within `radius` blocks as a friend (optionally clearing the list first)
struct V3b { double x, y, z; };
FN(V3b, java_get_entity_pos_x, (void *jvm, void *entity));
FN(unsigned char, entity_flags_has, (void *jvm, void *entity, int mask));
FN(double, vec3_distance_sq, (V3b a, V3b b));
extern "C" const char str_sect_aremoved_zu_added_zu_frie[], str_sect_aadded_zu_friend_s[];
struct NearArgs { unsigned long long radius; char padding_08[8]; unsigned char clear; };
typedef int v4i3 __attribute__((vector_size(16)));
typedef double v2d3 __attribute__((vector_size(16)));
extern "C" const v4i3 friend_set_0x43300000_0x45300000_0_0; // {0x43300000, 0x45300000, 0, 0}
extern "C" const v2d3 friend_set_2_52_2_84; // {2^52, 2^84}
// uint64 -> double with the image's constants (the original's lowering)
static inline __attribute__((always_inline)) double u64_to_d(unsigned long long x) {
v4i3 v = {(int)x, (int)(x >> 32), 0, 0};
v4i3 u = __builtin_shufflevector(v, friend_set_0x43300000_0x45300000_0_0, 0, 4, 1, 5);
v2d3 d = (v2d3)u - friend_set_2_52_2_84;
return d[1] + d[0];
}
extern "C" bool friend_set_contains(C3b *c, NearArgs *a) {
unsigned long long radius = a->radius;
unsigned char clear = a->clear;
unsigned long long old = (unsigned)friend_set_dirty_count;
if (clear == 1) friend_set_dirty_count = 0;
V3b eye = java_get_entity_pos_x(c->jvm, c->self);
void *list = java_get_world_player_entities(c->jvm, c->world);
unsigned long long added = 0;
if (java_call_list_size(c->jvm, list) > 0) {
double r = u64_to_d(radius);
r *= r;
for (int i = 0; i < java_call_list_size(c->jvm, list); i++) {
void *element = java_call_list_get(c->jvm, list, i);
JNIEnv *env = c->jvm->env;
if ((*env)->IsSameObject(env, (jobject)element, (jobject)c->self)) continue;
if (entity_flags_has(c->jvm, element, 2)) continue;
V3b position = java_get_entity_pos_x(c->jvm, element);
if (vec3_distance_sq(eye, position) > r) continue;
void *uuid = java_get_entity_uuid(c->jvm, element);
Uid k;
k.hi = java_jni_call_long_method_uuid_get_most_significant_bits(c->jvm, uuid);
k.lo = java_jni_call_long_method_uuid_get_least_significant_bits(c->jvm, uuid);
k.z0 = 0;
k.z1 = 0;
void *existing = binary_search(friend_set_friend_table, 0x20, friend_set_table_meta, &k, (void *)compare_friend_key);
vector_find_insert(&friend_set_friend_table, 0x20, friend_set_table_meta, &k, (void *)compare_friend_key);
if (!existing) added++;
}
}
const char *s = added == 1 ? empty_plural_suffix : str_s_in_friend_set;
if (clear) jni_throw_format(c->jvm, str_sect_aremoved_zu_added_zu_frie, old, added, s);
else jni_throw_format(c->jvm, str_sect_aadded_zu_friend_s, added, s);
return true;
}
// add a friend entry from its hex string (inverse of friend_set_count); a bad digit stops that part of the parse
FN(unsigned long long, libc_strlen, (const char *str));
static inline __attribute__((always_inline)) bool hexdig(unsigned char c, unsigned char *v) {
if ((unsigned char)(c - '0') < 10) { *v = c - '0'; return true; }
if ((unsigned char)(c - 'a') < 26) { *v = c - 'a' + 10; return true; }
return false;
}
static inline __attribute__((always_inline)) void unhex(unsigned char *d, const char *s, int n) {
for (int i = 0; i < n; i++) {
unsigned char h, l;
if (!hexdig(s[i * 2], &h) || !hexdig(s[i * 2 + 1], &l)) return;
d[i] = h << 4 | l;
}
}
extern "C" bool friend_name_matches(void *, const char *s) {
Ent e = {};
unsigned long long n = libc_strlen(s);
if (n >= 0x30) {
unhex((unsigned char *)e.b, s, 16);
unhex((unsigned char *)e.b + 16, s + 32, 8);
vector_find_insert(&friend_set_friend_table, 0x20, friend_set_table_meta, &e, (void *)compare_friend_key);
}
return n >= 0x30;
}
@@ -0,0 +1,13 @@
// helper functions (formerly address block; names inferred from behaviour).
#include "slinky/engine.h"
extern "C" void *friend_set_friend_table; extern "C" char friend_set_table_meta[]; extern "C" int friend_set_dirty_count; extern "C" unsigned char friends_friends_setting_field_0x7c;
FN(void, vector_erase, (void *vec, int elem, void *hdr, void *key));
FN(void *, java_jni_is_instance_of_entity_player, (void *jvm, void *candidate));
FN(unsigned char, entity_flags_has, (void *jvm, void *entity, int mode));
FN(void *, java_call_renderer_living_entity_can_render_name, (void *jvm, void *renderer_living_entity, void *other_object));
extern "C" void friend_set_remove_by_key(void *, void *key) { vector_erase(friend_set_friend_table, 0x20, friend_set_table_meta, key); }
extern "C" void friend_set_clear_dirty() { friend_set_dirty_count = 0; }
extern "C" void *should_render_player_name(void *jvm, void *render_player, void *other_object) {
if (friends_friends_setting_field_0x7c == 1 && java_jni_is_instance_of_entity_player(jvm, other_object) != 0 && !entity_flags_has(jvm, other_object, 2)) return 0;
return java_call_renderer_living_entity_can_render_name(jvm, render_player, other_object);
}
+82
View File
@@ -0,0 +1,82 @@
// 0x2f000: health-bar style line drawing (immediate-mode GL), comparator, hurt-time hook
#include "slinky/engine.h"
typedef void (*GlColor3f)(float, float, float);
typedef void (*GlBegin)(int);
typedef void (*GlVertex2f)(float, float);
typedef void (*GlEnd)();
extern "C" GlColor3f *ptr_to_pGlColor3f;
extern "C" GlBegin *ptr_to_pGlBegin;
extern "C" GlVertex2f *ptr_to_pGlVertex2f;
extern "C" GlEnd *ptr_to_pGlEnd;
extern "C" float one_in_healthbar_draw, healthbar_green_level;
FN(float, lerp_unclamped_float, (float a, float b, float t)); // lerp
struct Bar { char padding_00[0x44]; float fill_fraction, overlay_fraction; };
extern "C" void draw_health_bar(Bar *b, float x, float y0, float y1) {
float m = lerp_unclamped_float(y1, y0, b->fill_fraction);
GlColor3f *color = ptr_to_pGlColor3f;
(*color)(0.0f, 0.0f, 0.0f);
GlBegin *begin = ptr_to_pGlBegin;
(*begin)(1);
GlVertex2f *vertex = ptr_to_pGlVertex2f;
(*vertex)(x, y0);
(*vertex)(x, m);
GlEnd *end = ptr_to_pGlEnd;
(*end)();
if (b->overlay_fraction > 0.0f) {
float m2 = lerp_unclamped_float(m, y1, b->overlay_fraction);
float c0 = one_in_healthbar_draw;
(*color)(c0, healthbar_green_level, 0.0f);
(*begin)(1);
(*vertex)(x, m);
(*vertex)(x, m2);
(*end)();
m = m2;
}
GlColor3f cf = *color;
float fill_fraction = b->fill_fraction;
float c = one_in_healthbar_draw;
float r = lerp_unclamped_float(c, 0.0f, fill_fraction);
float g = lerp_unclamped_float(0.0f, c, b->fill_fraction);
cf(r, g, 0.0f);
(*begin)(1);
(*vertex)(x, m);
(*vertex)(x, y1);
(*end)();
}
extern "C" int compare_healthbar_height(char *left, char *right) { return (int)(*(float *)(right + 0x3c) - *(float *)(left + 0x3c)); }
extern "C" int reach_last_value_seen_from_java, reach_cooldown_ticks;
extern "C" unsigned char reach_require_sprint;
extern "C" float reach_activate_time, reach_distance;
FN(unsigned char, java_call_entity_is_sprinting, (void *jvm, void *entity));
FN(int, java_get_entity_living_base_last_attacker_time, (void *jvm, void *entity_living_base));
struct Ev { char padding_00[8]; Ctx *ctx; unsigned char on; char padding_11[7]; double reach; };
extern "C" void healthbar_render(Ev *event) {
int n = reach_cooldown_ticks;
if (n > 0) reach_cooldown_ticks = --n;
Ctx *ctx = event->ctx;
if (reach_require_sprint == 1) {
if (!java_call_entity_is_sprinting(ctx->jvm, ctx->player)) return;
n = reach_cooldown_ticks;
}
if (n == 0) {
int t = java_get_entity_living_base_last_attacker_time(ctx->jvm, ctx->player);
if (t == reach_last_value_seen_from_java) {
n = reach_cooldown_ticks;
} else {
reach_last_value_seen_from_java = t;
reach_cooldown_ticks = 9;
n = 9;
}
}
SHARED_EPILOG(); // original keeps one shared tail for both arms
if (reach_activate_time > (float)n) {
event->on = 1;
event->reach = reach_distance;
}
}
+117
View File
@@ -0,0 +1,117 @@
// 0x47000: pick the best inventory slot for an item category and plan the swap
#include "slinky/engine.h"
#include "slinky/jids.h"
FN(void *, java_get_entity_player_inventory, (void *jvm, void *entity_player));
FN(void *, java_call_iinventory_get_stack_in_slot, (void *jvm, void *object, int slot));
FN(void *, java_get_inventory_player_main_inventory, (void *jvm, void *inventory_player));
FN(unsigned, swap_delay, (Ctx *c, void *stack, unsigned kind));
struct Plan { char padding_00[0x10]; int mode; unsigned char from; unsigned char padding_15, padding_16, padding_17; char padding_18[4]; unsigned char kind, to; };
extern "C" bool plan_inventory_swap(Ctx *ctx, Plan *pl) {
unsigned char kind = pl->kind;
void *player = java_get_entity_player_inventory(ctx->jvm, ctx->player);
void *jvm = ctx->jvm;
void *cur = java_call_iinventory_get_stack_in_slot(jvm, player, 0x27 - kind);
void *inv = java_get_inventory_player_main_inventory(ctx->jvm, player);
JNIEnv *env = ((Jvm *)ctx->jvm)->env;
unsigned best = swap_delay(ctx, cur, kind);
int idx = -1;
for (int i = 0; i != 0x24; i++) {
unsigned v = swap_delay(ctx, (*env)->GetObjectArrayElement(env, (jobjectArray)inv, i), kind);
if (v > best) { idx = i; best = v; }
}
if (idx != -1) {
if (!cur) {
unsigned char to = (unsigned char)(idx + 0x24);
if (idx >= 9) to = (unsigned char)idx;
*(long long *)&pl->mode = 0;
pl->to = to;
} else if (idx <= 8) {
pl->mode = 1;
pl->from = (unsigned char)idx;
pl->padding_15 = 0; pl->padding_16 = 0; pl->padding_17 = 0;
pl->to = pl->kind + 5;
} else {
unsigned char k = pl->kind + 5;
pl->mode = 2;
pl->from = k;
pl->padding_15 = 0; pl->padding_16 = 0; pl->padding_17 = 0;
pl->to = (unsigned char)idx;
}
}
return idx != -1;
}
// armour value of a stack for slot `kind` (armour points + protection enchantments)
FN(void *, java_call_item_stack_get_item, (void *jvm, void *item_stack));
FN(int, java_call_static_item_get_id_from_item, (void *jvm, void *item));
FN(void *, java_call_item_stack_get_enchantment_tag_list, (void *jvm, void *stack));
FN(int, java_call_nbttag_list_tag_count, (void *jvm, void *list));
FN(void *, java_call_nbttag_list_get_compound_tag_at, (void *jvm, void *list, int i));
FN(short, java_jni_call_short_method_nbttag_compound_get_short, (void *jvm, void *tag, void *key));
extern "C" const int item_id_offsets[];
extern "C" const long long material_ids[];
extern "C" const unsigned char item_points_table[];
extern "C" const char str_id_in_chest_gui[], str_lvl_in_chest_gui[];
extern "C" const double tenth_in_inventory_swap, three_quarters, three_in_inventory_swap, swap_blend_start_weight, one_in_inventory_swap;
extern "C" const float half_in_inventory_swap, factor_1000_b;
extern "C" unsigned swap_delay(Ctx *ctx, void *stack, unsigned kind) {
if (!stack) return 0;
void *item = java_call_item_stack_get_item(ctx->jvm, stack);
if (!item) return 0;
int item_id = java_call_static_item_get_id_from_item(ctx->jvm, item);
unsigned k = item_id - 0x12a;
if (k > 0x13) return 0;
if ((unsigned)(item_id + item_id_offsets[k]) != kind) return 0;
long long mat = material_ids[k];
void *nbt = java_call_item_stack_get_enchantment_tag_list(ctx->jvm, stack);
unsigned char points = item_points_table[mat * 4 + kind];
double prot = 0.0;
if (nbt) {
JNIEnv *env = ((Jvm *)ctx->jvm)->env;
jstring idS = (*env)->NewStringUTF(env, str_id_in_chest_gui);
if (idS) {
jstring lvlS = (*env)->NewStringUTF(env, str_lvl_in_chest_gui);
if (lvlS) {
int n = java_call_nbttag_list_tag_count(ctx->jvm, nbt);
if (n > 0) {
int i = 0;
float extra = 0.0f;
double k0 = tenth_in_inventory_swap;
double k1 = three_quarters;
double k2 = three_in_inventory_swap;
float k3 = half_in_inventory_swap;
float acc = 0.0f;
for (; i != n; i++) {
void *tag = java_call_nbttag_list_get_compound_tag_at(ctx->jvm, nbt, i);
unsigned short eid = (unsigned short)java_jni_call_short_method_nbttag_compound_get_short(ctx->jvm, tag, idS);
short lvl = java_jni_call_short_method_nbttag_compound_get_short(ctx->jvm, tag, lvlS);
if (lvl < 0) continue;
int L = lvl;
switch (eid) {
case 1: case 2: case 3: case 4:
acc = (float)((double)L * k0 + (double)acc);
break;
case 0: {
unsigned v = (unsigned)(long long)((double)(L * L + 6) * k1 / k2);
if (v >= 0x19) v = 0x19;
v = ((v + 1) & 0x3e) + (v >> 1);
extra = (float)(int)v * k3;
break;
}
}
}
prot = (double)(acc + extra);
}
}
}
}
double c1 = swap_blend_start_weight, c2 = one_in_inventory_swap;
double t = c2 - (double)points * c1;
double r = c2 - (c2 - prot * c1) * t;
return (unsigned)(long long)((float)r * factor_1000_b);
}
@@ -0,0 +1,62 @@
// helper functions (formerly address block; names inferred from behaviour).
// cflags: -mllvm -switch-to-lookup=false
#include "slinky/engine.h"
FN(void *, java_get_minecraft_player_controller, (void *jvm, void *minecraft));
FN(unsigned char, java_get_player_controller_mp_is_hitting_block, (void *jvm, void *player_controller_mp));
FN(void, java_set_player_controller_mp_is_hitting_block, (void *jvm, void *player_controller_mp, int on));
extern "C" unsigned char item_use_fix_item_use_flag, timer_range_active_flag, timer_range_timer_modified;
extern "C" int timer_range_delay_ticks;
struct Ev { char padding_00[8]; Ctx *ctx; unsigned char on; };
extern "C" void item_use_fix_on_attack(Ev *event) {
if (__builtin_expect(event->on != 0, 0))
goto out;
{
Ctx *ctx = event->ctx;
void *minecraft = java_get_minecraft_player_controller(ctx->jvm, ctx->minecraft);
unsigned char usable = java_get_player_controller_mp_is_hitting_block(ctx->jvm, minecraft);
item_use_fix_item_use_flag = usable != 0;
if (usable) {
java_set_player_controller_mp_is_hitting_block(ctx->jvm, minecraft, 0);
return;
}
}
out:
EPILOG_NOP();
}
extern "C" void item_use_fix_on_use(Ev *event) {
if (item_use_fix_item_use_flag != 1 || event->on != 0) return;
Ctx *ctx = event->ctx;
void *minecraft = java_get_minecraft_player_controller(ctx->jvm, ctx->minecraft);
void *jvm = ctx->jvm;
java_set_player_controller_mp_is_hitting_block(jvm, minecraft, item_use_fix_item_use_flag);
}
extern "C" bool timer_range_is_delay_pending() {
unsigned char r;
if (timer_range_active_flag == 1) { asm volatile(""); r = (timer_range_delay_ticks > 0) | timer_range_timer_modified; }
else r = 0;
SHARED_EPILOG();
return r & 1;
}
FN(unsigned char, java_call_entity_player_is_using_item, (void *jvm, void *entity_player));
FN(void *, item_held_hotbar_slot, (void *jvm, void *slot));
FN(int, java_call_static_item_get_id_from_item, (void *jvm, void *item));
extern "C" float no_slow_mult_sword_value, no_slow_mult_bow_value, no_slow_mult_consumables_value;
extern "C" double divisor_0_2_in_item_use_fix_and_no_slow;
struct Ev2 { char padding_00[8]; Ctx *ctx; float x, y; };
extern "C" void no_slow_item_use_speed(Ev2 *ev) {
Ctx *ctx = ev->ctx;
if (!java_call_entity_player_is_using_item(ctx->jvm, ctx->player)) return;
void *item = item_held_hotbar_slot(ctx->jvm, ctx->player);
if (!item) return;
float *f;
switch (java_call_static_item_get_id_from_item(ctx->jvm, item)) {
case 0x10b: case 0x10c: case 0x110: case 0x114: case 0x11b: f = &no_slow_mult_sword_value; break; // swords
case 0x105: f = &no_slow_mult_bow_value; break;
default: f = &no_slow_mult_consumables_value; break;
}
double s = (double)*f / divisor_0_2_in_item_use_fix_and_no_slow;
ev->x = (float)(s * ev->x);
ev->y = (float)(s * ev->y);
}
+11
View File
@@ -0,0 +1,11 @@
// 0x4b000: key-code -> category
#include "slinky/engine.h"
extern "C" int key_category(int k) {
switch (k) {
case 0x1: case 0x2: case 0x3: case 0x14: case 0x18: case 0x2d: case 0x57: case 0x9f: return 2;
case 0x4: case 0x5: case 0x11: case 0x79: case 0xa2: return 3;
case 0x23: return 1;
case 0x31: return 4;
default: return -1;
}
}
@@ -0,0 +1,112 @@
// 0x4c000: Windows virtual-key -> key code, small CRT printf wrappers, string append
#include "slinky/engine.h"
#include "slinky/win32.h"
#include <stdarg.h>
extern "C" int windows_key_to_client_key(unsigned long long vk) {
if (vk - 0x41 <= 0x19) return (int)vk - 0x27;
if (vk - 0x30 <= 9) return (int)vk - 0x2a;
if (vk - 0x60 <= 9) return (int)vk - 0x50;
if ((vk & ~0xfull) == 0x70) return (int)vk - 0x2d;
switch (vk) {
case 0x8: return 0x53;
case 0x9: return 0x54;
case 0xd: return 0x55;
case 0x14: return 0x56;
case 0x1b: return 0x57;
case 0x20: return 0x58;
case 0x21: return 0x59;
case 0x22: return 0x5a;
case 0x23: return 0x5b;
case 0x24: return 0x5c;
case 0x25: return 0x3f;
case 0x26: return 0x40;
case 0x27: return 0x41;
case 0x28: return 0x42;
case 0x2a: return 0x5d;
case 0x2d: return 0x5e;
case 0x2e: return 0x5f;
case 0x90: return 0x60;
case 0x91: return 0x61;
case 0xa0: return 0x62;
case 0xa1: return 0x63;
case 0xa2: return 0x64;
case 0xa3: return 0x65;
case 0xa4: return 0x66;
case 0xa5: return 0x67;
case 0xba: return 0x34;
case 0xbb: return 0x35;
case 0xbc: return 0x36;
case 0xbd: return 0x37;
case 0xbe: return 0x38;
case 0xbf: return 0x39;
case 0xc0: case 0xdf: return 0x3a;
case 0xdb: return 0x3b;
case 0xdc: return 0x3c;
case 0xdd: return 0x3d;
case 0xde: return 0x3e;
default: return 0;
}
}
FN(unsigned long long *, printf_options_ptr, ()); // __local_stdio_printf_options
extern "C" int libc_vswprintf(unsigned long long opts, char *buf, unsigned long long n, const char *fmt, void *locale, va_list ap);
extern "C" int libc_vswprintf_s(unsigned long long opts, wchar_t *buf, unsigned long long size, unsigned long long count,
const wchar_t *fmt, void *locale, va_list ap);
extern "C" const wchar_t ptr_to_rdata_2ba968[]; // L"%S"
extern "C" const char wide_path_format[];
FN(unsigned long long, libc_wcslen, (const char *str));
FN(unsigned long long, libc_strlen, (const char *str));
FN(void *, libc_calloc, (unsigned long long n, unsigned long long size));
FN(void, libc_free, (void *ptr));
extern "C" int format_string(char *buf, unsigned long long n, const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
return libc_vswprintf(*printf_options_ptr(), buf, n, fmt, 0, ap);
}
// _snwprintf_s(buf, 260, 260, L"%S", ...) with the constants propagated into the out-of-line copy
extern "C" int snwprintf_s(wchar_t *buf, unsigned long long, unsigned long long, const wchar_t *fmt, ...) {
va_list ap;
va_start(ap, fmt);
return libc_vswprintf_s(*printf_options_ptr(), buf, 0x104, 0x104, ptr_to_rdata_2ba968, 0, ap);
}
extern "C" int settings_path_append(char **pstr, const char *suffix) {
char *old = *pstr;
if (!old || !suffix) return 0x3ed;
unsigned long long a = libc_wcslen(old); // wcslen of the wide path
unsigned long long b = libc_strlen(suffix); // strlen of the narrow suffix
// the buffer holds 261 wide chars: old + '\' + suffix + NUL
if (a > 0x104 || b > 0x104 || a + b + 2 > 0x105) return 0x3ee;
char *nb = (char *)libc_calloc(0x105, 2); // 261 wide chars, as the image allocates
if (!nb) return 0x3ed;
wchar_t *w = (wchar_t *)nb;
for (unsigned long long i = 0; i < a; i++) w[i] = ((const wchar_t *)old)[i];
w[a] = L'\\';
if (!MultiByteToWideChar(65001 /* CP_UTF8 */, 0, suffix, -1, w + a + 1, (int)(0x105 - (a + 1)))) {
libc_free(nb);
return 0x3ee;
}
libc_free(*pstr);
*pstr = nb;
return 0;
}
extern "C" void free_path(void **p) {
libc_free(*p);
*p = 0;
}
// ---- gap-scan functions ----
FN(unsigned char, crt_stub_returns_zero, ());
extern "C" bool crt_initialized() { return crt_stub_returns_zero() != 0; }
// cut a wide path at its last backslash (-> directory); no backslash: empty string
extern "C" void cut_wide_path_last_backslash(wchar_t **path) {
wchar_t *s = *path;
unsigned long long i = libc_wcslen((const char *)s);
for (; i; i--)
if (s[i] == L'\\') {
s += i;
break;
}
*s = 0;
}
+100
View File
@@ -0,0 +1,100 @@
// 0x32000: key-name table helpers and a "speed" module's callbacks
#include "slinky/engine.h"
struct Vec3 { double x, y, z; };
extern "C" int key_colour_count;
extern "C" char key_colour_vector_header[], gui_state_block[];
extern "C" void *teams_colour_table;
extern "C" long long gui_accent_color_setting_field_0x8; extern "C" int gui_accent_color_setting_field_0x10;
extern "C" const char *teams_team_colors_setting_field_0x70[16];
extern "C" char gui_module_descriptor[], timer_module_descriptor[];
extern "C" float timer_speed_value, one_in_key_names, timer_speed_1_in_key_names_a;
extern "C" unsigned char timer_require_mouse_value, timer_require_falling_value, timer_require_damage_value;
extern "C" int libc_strcmp(const char *left, const char *right);
FN(unsigned long long, libc_strlen, (const char *str));
FN(void **, element_at, (void *vec, int elem, void *hdr, void *key));
FN(void *, vector_find_insert, (void *t, int elem, void *hdr, void *e, void *cmp));
FN(void, vector_erase, (void *vec, int elem, void *hdr, void *key));
FN(unsigned char, is_unload_requested, (void *x));
FN(void *, get_timer, (void *jvm, void *timer));
FN(void, java_set_timer_timer_speed, (void *jvm, void *timer, float speed));
FN(void, queue_module_toggle, (void *module_manager, void *module, unsigned char enabled));
FN(unsigned char, vertex_pointer_current, (Ctx *ctx));
FN(unsigned char, java_get_entity_on_ground, (void *jvm, void *entity));
FN(Vec3, java_get_entity_motion_x, (void *jvm, void *entity));
FN(int, java_get_entity_living_base_hurt_time, (void *jvm, void *entity_living_base));
extern "C" int compare_signed_chars(const signed char *, const signed char *right, const signed char *b) { return *right - *b; }
extern "C" void key_filter(char *buf, char character) {
unsigned long long n = libc_strlen(buf);
if (n > 99) return;
unsigned char l = (unsigned char)(character - 0x61);
asm("" : "+r"(l));
if (l >= 0x1a) {
unsigned char u = (unsigned char)(character - 0x41);
asm("" : "+r"(u));
unsigned char notupper = u >= 0x1a;
asm("" : "+r"(notupper));
unsigned char notspace = character != 0x20;
asm("" : "+r"(notspace));
if (notupper & notspace) return;
}
buf[n] = character;
}
extern "C" int get_key_name_index() { return key_colour_count; }
extern "C" const char *key_name_for(void *, void *key) {
signed char *p = (signed char *)element_at(teams_colour_table, 1, key_colour_vector_header, key);
return teams_team_colors_setting_field_0x70[*p];
}
extern "C" void remove_key_name(void *, void *key) { vector_erase(teams_colour_table, 1, key_colour_vector_header, key); }
extern "C" void clear_key_name() { key_colour_count = 0; }
extern "C" void key_name_table(const char ***a, long long *n) { *a = teams_team_colors_setting_field_0x70; *n = 0x10; }
extern "C" unsigned char is_unload_requested_flag() { return is_unload_requested(gui_state_block); }
struct Pair { long long a; int b; };
extern "C" Pair get_speed_pair() { Pair r; r.b = gui_accent_color_setting_field_0x10; r.a = gui_accent_color_setting_field_0x8; return r; }
extern "C" bool enable_hud_editor(Ctx **h) { queue_module_toggle(h[1], gui_module_descriptor, 1); return true; }
struct Hk { char padding_00[8]; Ctx *ctx; };
extern "C" bool key_name_matches(void *, const char *name) {
int i;
for (i = 0; i < 16; i++)
if (libc_strcmp(name, teams_team_colors_setting_field_0x70[i]) == 0) break;
if (i == 16) return false;
signed char idx = (signed char)i;
vector_find_insert(&teams_colour_table, 1, key_colour_vector_header, &idx, (void *)compare_signed_chars);
return true;
}
extern "C" void speed_hook_tick(Hk *h) {
Ctx *ctx = h->ctx;
void *timer = get_timer(ctx->jvm, ctx->minecraft);
float speed;
if (timer_require_mouse_value == 1 && !vertex_pointer_current(ctx)) goto slow;
if (timer_require_falling_value == 1) {
if (java_get_entity_on_ground(ctx->jvm, ctx->player)) goto slow;
Vec3 motion = java_get_entity_motion_x(ctx->jvm, ctx->player);
if (motion.y >= 0.0) goto slow;
}
if (timer_require_damage_value == 1) {
if (ctx->in_game != 1) goto slow;
if (!java_get_entity_living_base_hurt_time(ctx->jvm, ctx->player)) goto slow;
}
speed = timer_speed_value;
goto go;
slow:
speed = one_in_key_names;
go:
java_set_timer_timer_speed(ctx->jvm, timer, speed);
}
extern "C" void speed_hook_enable(Hk *h) {
if (*(void **)((char *)h + 0x10) == timer_module_descriptor && *((char *)h + 0x18) == 0) {
Ctx *ctx = h->ctx;
void *timer = get_timer(ctx->jvm, ctx->minecraft);
void *jvm = ctx->jvm;
java_set_timer_timer_speed(jvm, timer, timer_speed_1_in_key_names_a);
}
}
FN(void, empty_placeholder, (void *x));
extern "C" void reset_key_state() { empty_placeholder(gui_state_block); }
@@ -0,0 +1,51 @@
// helper functions (formerly address block; names inferred from behaviour).
#include "slinky/engine.h"
extern "C" int no_item_release_mode; extern "C" unsigned char no_item_release_release_lock_flag; extern "C" float hitboxes_expand;
struct Ev { char padding_00[8]; void *jvm; void *pkt; char padding_18[0xc]; int verdict; };
FN(void *, java_jni_is_instance_of_c08_packet_player_block_placement, (void *w, void *candidate));
FN(void *, java_get_c08_packet_player_block_placement_stack, (void *w, void *c08_packet_player_block_placement));
FN(void *, java_call_item_stack_get_item, (void *jvm, void *item_stack));
FN(int, java_call_static_item_get_id_from_item, (void *jvm, void *item));
FN(void *, java_jni_is_instance_of_c07_packet_player_digging, (void *w, void *candidate));
FN(int, java_c07_packet_player_digging_status, (void *w, void *object));
extern "C" void no_item_release_on_send_packet(Ev *event) {
unsigned char lock = 0;
void *w = java_jni_is_instance_of_c08_packet_player_block_placement(event->jvm, event->pkt);
void *rw = event->jvm;
if (w) {
void *t = java_get_c08_packet_player_block_placement_stack(rw, w);
if (t) {
void *item = java_call_item_stack_get_item(rw, t);
if (item) {
int id = java_call_static_item_get_id_from_item(rw, item);
bool compute;
switch (id) {
case 267: case 268: case 272: case 276: case 283: // swords
compute = no_item_release_mode != 1;
break;
case 261: // bow
case 346:
compute = false;
break;
default:
compute = no_item_release_mode != 0;
break;
}
if (compute) {
int id2 = java_call_static_item_get_id_from_item(rw, item);
lock = (id2 != 0x15a) & (id2 != 0x105);
}
}
}
no_item_release_release_lock_flag = lock;
return;
}
w = java_jni_is_instance_of_c07_packet_player_digging(rw, event->pkt);
if (!w || no_item_release_release_lock_flag == 0)
return;
if (java_c07_packet_player_digging_status(event->jvm, w) == 5)
event->verdict = 2;
no_item_release_release_lock_flag = 0;
}
// original engine copy of hitboxes_on_reach_query; the descriptor uses the logic function (dead duplicate)
extern "C" void hitboxes_apply_hitbox_expansion_original(char *p) { p[0x10] = 1; *(double *)(p + 0x20) = hitboxes_expand; }
Loaded 100 of 274 files, more files were not shown because too many files have changed in this diff. Show more