├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── polarmods │ │ └── androidmodtemplate │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── cpp │ │ ├── CMakeLists.txt │ │ ├── Engine │ │ │ ├── Color.h │ │ │ ├── Quaternion.h │ │ │ ├── Rect.h │ │ │ ├── Unity.h │ │ │ ├── Vector2.h │ │ │ └── Vector3.h │ │ ├── Etc │ │ │ ├── Logging.h │ │ │ ├── Obfuscate.h │ │ │ └── Utils.h │ │ ├── Hooking │ │ │ └── ARMPatch │ │ │ │ ├── ARMPatch.cpp │ │ │ │ ├── ARMPatch.h │ │ │ │ ├── libsubstrate-armv8a.a │ │ │ │ ├── libsubstrate-cydia-armv7a.a │ │ │ │ └── libsubstrate-inline-armv7a.a │ │ ├── Memory │ │ │ └── KittyMemory │ │ │ │ ├── KittyArm64.cpp │ │ │ │ ├── KittyArm64.h │ │ │ │ ├── KittyMemory.cpp │ │ │ │ ├── KittyMemory.h │ │ │ │ ├── KittyScanner.cpp │ │ │ │ ├── KittyScanner.h │ │ │ │ ├── KittyUtils.cpp │ │ │ │ ├── KittyUtils.h │ │ │ │ ├── MemoryPatch.cpp │ │ │ │ └── MemoryPatch.h │ │ ├── Mod │ │ │ ├── Chams.h │ │ │ ├── Functions.h │ │ │ ├── Hooks.h │ │ │ ├── Offsets.h │ │ │ └── Patch.h │ │ └── native-lib.cpp │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── polarmods │ └── androidmodtemplate │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Rev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Android Native Mod Template 2 | Not everything requires a mod menu - which is why I created this mod template (but, it was also a fun project to do)! It supports hooking, patching, pattern-scanning, uses cmake to build, and much more! In short, it includes everything you need to make your own mods. In case you're wondering, for the NDK, I use the latest LTS version - which can be downloaded [here](https://github.com/android/ndk/wiki#current-lts-release). 3 | 4 | Version 1.2027 of Forward Assault has been used as an example in the source of this template. 5 | 6 | ## Implementation 7 | Implementing your mod into an application is very easy! 8 | 9 | 1. Once you build the apk, open it using an archiver (such as 7-Zip, WinRAR, etc), and find the 'lib' directory. 10 | 11 | ![image](https://user-images.githubusercontent.com/64957743/172854358-d5a3cf9a-37b6-4e7a-88de-4b68ef3b26f6.png) 12 | 13 | From there, you will be presented with two additional folders. Choose the folder that resembles the cpu architecture you want to use (whether it be armeabi-v7a, or arm64-v8a). 14 | 15 | ![image](https://user-images.githubusercontent.com/64957743/172854465-9bc9ceca-04fc-45fc-acb5-a23d69134409.png) 16 | 17 | 18 | 2. Move the 'libnativemod.so' library to the target applications lib/chosen-cpu-architecture/ folder. 19 | 20 | ![image](https://user-images.githubusercontent.com/64957743/172854827-44556607-7609-422f-8486-948922e93500.png) 21 | 22 | 3. Then, find your applications launch activity, and place the following code under the 'onCreate' method in that launch activity. 23 | 24 | ``` 25 | const-string v0, "nativemod" 26 | 27 | invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V 28 | ``` 29 | 30 | ![image](https://user-images.githubusercontent.com/64957743/172854983-bec97ca3-399e-4f81-b234-8d9de3dec846.png) 31 | 32 | 4. Compile it, and you're done! 33 | 34 | ## Credits 35 | * Ruit and his cats - [KittyMemory](https://github.com/MJx0/KittyMemory/) 36 | * RusJJ - [ARMPatch](https://github.com/RusJJ/ARMPatch/) 37 | * shmoo419 - [UnityStuff](https://github.com/shmoo419/UnityStuff/) 38 | * YclepticStudios - [gmath](https://github.com/YclepticStudios/gmath/) 39 | * adamyaxley - [Obfuscate](https://github.com/adamyaxley/Obfuscate/) 40 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 32 5 | buildToolsVersion "30.0.3" 6 | 7 | defaultConfig { 8 | applicationId "com.polarmods.androidmodtemplate" 9 | minSdkVersion 21 10 | targetSdkVersion 32 11 | versionCode 1 12 | versionName "1.0" 13 | 14 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 15 | ndk { 16 | abiFilters 'armeabi-v7a', 'arm64-v8a' 17 | } 18 | externalNativeBuild { 19 | cmake { 20 | cppFlags "-std=c++17" 21 | } 22 | } 23 | } 24 | 25 | buildTypes { 26 | release { 27 | minifyEnabled false 28 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 29 | } 30 | } 31 | 32 | externalNativeBuild { 33 | cmake { 34 | path "src/main/cpp/CMakeLists.txt" 35 | version "3.10.2" 36 | } 37 | } 38 | } 39 | 40 | dependencies { 41 | implementation fileTree(dir: 'libs', include: ['*.jar']) 42 | 43 | implementation 'androidx.appcompat:appcompat:1.1.0' 44 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 45 | testImplementation 'junit:junit:4.12' 46 | androidTestImplementation 'androidx.test.ext:junit:1.1.1' 47 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' 48 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/androidTest/java/com/polarmods/androidmodtemplate/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.polarmods.androidmodtemplate; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.platform.app.InstrumentationRegistry; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | assertEquals("com.polarmods.androidmodtemplate", appContext.getPackageName()); 25 | } 26 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # For more information about using CMake with Android Studio, read the 2 | # documentation: https://d.android.com/studio/projects/add-native-code.html 3 | 4 | # Sets the minimum version of CMake required to build the native library. 5 | 6 | cmake_minimum_required(VERSION 3.4.1) 7 | 8 | # Declares and names the project. 9 | 10 | project("nativemod") 11 | 12 | # Creates and names a library, sets it as either STATIC 13 | # or SHARED, and provides the relative paths to its source code. 14 | # You can define multiple libraries, and CMake builds them for you. 15 | # Gradle automatically packages shared libraries with your APK. 16 | 17 | add_library( # Sets the name of the library. 18 | nativemod 19 | 20 | # Sets the library as a shared library. 21 | SHARED 22 | 23 | # Provides a relative path to your source file(s). 24 | native-lib.cpp 25 | Hooking/ARMPatch/ARMPatch.cpp 26 | Memory/KittyMemory/KittyMemory.cpp 27 | Memory/KittyMemory/KittyUtils.cpp 28 | Memory/KittyMemory/KittyScanner.cpp 29 | Memory/KittyMemory/KittyArm64.cpp 30 | Memory/KittyMemory/MemoryPatch.cpp 31 | ) 32 | 33 | # Searches for a specified prebuilt library and stores the path as a 34 | # variable. Because CMake includes system libraries in the search path by 35 | # default, you only need to specify the name of the public NDK library 36 | # you want to add. CMake verifies that the library exists before 37 | # completing its build. 38 | 39 | find_library( # Sets the name of the path variable. 40 | log-lib 41 | 42 | # Specifies the name of the NDK library that 43 | # you want CMake to locate. 44 | log) 45 | 46 | find_library( # Sets the name of the path variable. 47 | GLESV2_LIB 48 | 49 | # Specifies the name of the NDK library that 50 | # you want CMake to locate. 51 | GLESv2) 52 | 53 | if(${CMAKE_ANDROID_ARCH_ABI} STREQUAL "armeabi-v7a") 54 | set(ARMPATCH ${CMAKE_CURRENT_SOURCE_DIR}/Hooking/ARMPatch/libsubstrate-cydia-armv7a.a) 55 | elseif(${CMAKE_ANDROID_ARCH_ABI} STREQUAL "arm64-v8a") 56 | set(ARMPATCH ${CMAKE_CURRENT_SOURCE_DIR}/Hooking/ARMPatch/libsubstrate-armv8a.a) 57 | endif(${CMAKE_ANDROID_ARCH_ABI} STREQUAL "armeabi-v7a") 58 | 59 | # Specifies libraries CMake should link to your target library. You 60 | # can link multiple libraries, such as libraries you define in this 61 | # build script, prebuilt third-party libraries, or system libraries. 62 | 63 | target_link_libraries( # Specifies the target library. 64 | nativemod 65 | 66 | ${GLESV2_LIB} 67 | ${ARMPATCH} 68 | ${log-lib}) -------------------------------------------------------------------------------- /app/src/main/cpp/Engine/Color.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct Color 6 | { 7 | union 8 | { 9 | struct 10 | { 11 | float r; 12 | float g; 13 | float b; 14 | float a; 15 | }; 16 | }; 17 | 18 | 19 | /** 20 | * Constructors. 21 | */ 22 | inline Color(float r, float g, float b); 23 | inline Color(float r, float g, float b, float a); 24 | 25 | static inline Color Lerp(Color a, Color b, float t); 26 | static inline float Clamp(float t); 27 | 28 | static inline Color red(); 29 | static inline Color green(); 30 | static inline Color blue(); 31 | static inline Color white(); 32 | static inline Color black(); 33 | static inline Color yellow(); 34 | static inline Color cyan(); 35 | static inline Color magenta(); 36 | static inline Color gray(); 37 | static inline Color grey(); 38 | static inline Color clear(); 39 | }; 40 | 41 | Color Color::red() {return Color(1, 0, 0, 1);} 42 | Color Color::green() {return Color(0, 1, 0, 1);} 43 | Color Color::blue() {return Color(0, 0, 1, 1);} 44 | Color Color::white() {return Color(1, 1, 1, 1);} 45 | Color Color::black() {return Color(0, 0, 0, 1);} 46 | Color Color::yellow() {return Color(1, 0.921568632f, 0.0156862754f, 1);} 47 | Color Color::cyan() {return Color(0, 1, 1, 1);} 48 | Color Color::magenta() {return Color(1, 0, 1, 1);} 49 | Color Color::gray() {return Color(0.5, 0.5, 0.5, 1);} 50 | Color Color::grey() {return Color(0.5, 0.5, 0.5, 1);} 51 | Color Color::clear() {return Color(0, 0, 0, 0);} 52 | 53 | Color::Color(float r, float g, float b) : r(r), g(g), b(b), a(1) {} 54 | Color::Color(float r, float g, float b, float a) : r(r), g(g), b(b), a(a) {} 55 | 56 | Color Color::Lerp(Color a, Color b, float t) 57 | { 58 | t = Color::Clamp(t); 59 | return Color(a.r + (b.r - a.r) * t, a.g + (b.g - a.g) * t, a.b + (b.b - a.b) * t, a.a + (b.a - a.a) * t); 60 | } 61 | 62 | float Color::Clamp(float value) 63 | { 64 | if (value < 0) 65 | { 66 | return 0; 67 | } 68 | if (value > 1) 69 | { 70 | return 1; 71 | } 72 | return value; 73 | } -------------------------------------------------------------------------------- /app/src/main/cpp/Engine/Quaternion.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define _USE_MATH_DEFINES 4 | #include 5 | #include 6 | 7 | #define SMALL_float 0.0000000001 8 | 9 | #ifdef __has_include 10 | # if __has_include("Vector3.h") 11 | # include "Vector3.h" 12 | # elif !defined(GMATH_VECTOR3) 13 | #define GMATH_VECTOR3 14 | struct Vector3 15 | { 16 | union 17 | { 18 | struct 19 | { 20 | float X; 21 | float Y; 22 | float Z; 23 | }; 24 | float data[3]; 25 | }; 26 | 27 | inline Vector3() : X(0), Y(0), Z(0) {} 28 | inline Vector3(float data[]) : X(data[0]), Y(data[1]), Z(data[2]) 29 | {} 30 | inline Vector3(float value) : X(value), Y(value), Z(value) {} 31 | inline Vector3(float x, float y) : X(x), Y(y), Z(0) {} 32 | inline Vector3(float x, float y, float z) : X(x), Y(y), Z(z) {} 33 | 34 | static inline Vector3 Cross(Vector3 lhs, Vector3 rhs) 35 | { 36 | float x = lhs.Y * rhs.Z - lhs.Z * rhs.Y; 37 | float y = lhs.Z * rhs.X - lhs.X * rhs.Z; 38 | float z = lhs.X * rhs.Y - lhs.Y * rhs.X; 39 | return Vector3(x, y, z); 40 | } 41 | 42 | static inline float Dot(Vector3 lhs, Vector3 rhs) 43 | { 44 | return lhs.X * rhs.X + lhs.Y * rhs.Y + lhs.Z * rhs.Z; 45 | } 46 | 47 | static inline Vector3 Normalized(Vector3 v) 48 | { 49 | float mag = sqrt(v.X * v.X + v.Y * v.Y + v.Z * v.Z); 50 | if (mag == 0) 51 | return Vector3::Zero(); 52 | return v / mag; 53 | } 54 | 55 | static inline Vector3 Orthogonal(Vector3 v) 56 | { 57 | return v.Z < v.X ? 58 | Vector3(v.Y, -v.X, 0) : Vector3(0, -v.Z, v.Y); 59 | } 60 | 61 | static inline float SqrMagnitude(Vector3 v) 62 | { 63 | return v.X * v.X + v.Y * v.Y + v.Z * v.Z; 64 | } 65 | }; 66 | 67 | 68 | inline Vector3 operator+(Vector3 lhs, const Vector3 rhs) 69 | { 70 | return Vector3(lhs.X + rhs.X, lhs.Y + rhs.Y, lhs.Z + rhs.Z); 71 | } 72 | 73 | inline Vector3 operator*(Vector3 lhs, const float rhs) 74 | { 75 | return Vector3(lhs.X * rhs, lhs.Y * rhs, lhs.Z * rhs); 76 | } 77 | # endif 78 | #else 79 | # include "Vector3.h" 80 | #endif 81 | 82 | 83 | struct Quaternion 84 | { 85 | union 86 | { 87 | struct 88 | { 89 | float X; 90 | float Y; 91 | float Z; 92 | float W; 93 | }; 94 | float data[4]; 95 | }; 96 | 97 | 98 | /** 99 | * Constructors. 100 | */ 101 | inline Quaternion(); 102 | inline Quaternion(float data[]); 103 | inline Quaternion(Vector3 vector, float scalar); 104 | inline Quaternion(float x, float y, float z, float w); 105 | 106 | 107 | /** 108 | * Constants for common quaternions. 109 | */ 110 | static inline Quaternion Identity(); 111 | 112 | 113 | /** 114 | * Returns the angle between two quaternions. 115 | * The quaternions must be normalized. 116 | * @param a: The first quaternion. 117 | * @param b: The second quaternion. 118 | * @return: A scalar value. 119 | */ 120 | static inline float Angle(Quaternion a, Quaternion b); 121 | 122 | /** 123 | * Returns the conjugate of a quaternion. 124 | * @param rotation: The quaternion in question. 125 | * @return: A new quaternion. 126 | */ 127 | static inline Quaternion Conjugate(Quaternion rotation); 128 | 129 | /** 130 | * Returns the dot product of two quaternions. 131 | * @param lhs: The left side of the multiplication. 132 | * @param rhs: The right side of the multiplication. 133 | * @return: A scalar value. 134 | */ 135 | static inline float Dot(Quaternion lhs, Quaternion rhs); 136 | 137 | /** 138 | * Creates a new quaternion from the angle-axis representation of 139 | * a rotation. 140 | * @param angle: The rotation angle in radians. 141 | * @param axis: The vector about which the rotation occurs. 142 | * @return: A new quaternion. 143 | */ 144 | static inline Quaternion FromAngleAxis(float angle, Vector3 axis); 145 | 146 | /** 147 | * Create a new quaternion from the euler angle representation of 148 | * a rotation. The z, x and y values represent rotations about those 149 | * axis in that respective order. 150 | * @param rotation: The x, y and z rotations. 151 | * @return: A new quaternion. 152 | */ 153 | static inline Quaternion FromEuler(Vector3 rotation); 154 | 155 | /** 156 | * Create a new quaternion from the euler angle representation of 157 | * a rotation. The z, x and y values represent rotations about those 158 | * axis in that respective order. 159 | * @param x: The rotation about the x-axis in radians. 160 | * @param y: The rotation about the y-axis in radians. 161 | * @param z: The rotation about the z-axis in radians. 162 | * @return: A new quaternion. 163 | */ 164 | static inline Quaternion FromEuler(float x, float y, float z); 165 | 166 | /** 167 | * Create a quaternion rotation which rotates "fromVector" to "toVector". 168 | * @param fromVector: The vector from which to start the rotation. 169 | * @param toVector: The vector at which to end the rotation. 170 | * @return: A new quaternion. 171 | */ 172 | static inline Quaternion FromToRotation(Vector3 fromVector, 173 | Vector3 toVector); 174 | 175 | /** 176 | * Returns the inverse of a rotation. 177 | * @param rotation: The quaternion in question. 178 | * @return: A new quaternion. 179 | */ 180 | static inline Quaternion Inverse(Quaternion rotation); 181 | 182 | /** 183 | * Interpolates between a and b by t, which is clamped to the range [0-1]. 184 | * The result is normalized before being returned. 185 | * @param a: The starting rotation. 186 | * @param b: The ending rotation. 187 | * @return: A new quaternion. 188 | */ 189 | static inline Quaternion Lerp(Quaternion a, Quaternion b, float t); 190 | 191 | /** 192 | * Interpolates between a and b by t. This normalizes the result when 193 | * complete. 194 | * @param a: The starting rotation. 195 | * @param b: The ending rotation. 196 | * @param t: The interpolation value. 197 | * @return: A new quaternion. 198 | */ 199 | static inline Quaternion LerpUnclamped(Quaternion a, Quaternion b, 200 | float t); 201 | 202 | /** 203 | * Creates a rotation with the specified forward direction. This is the 204 | * same as calling LookRotation with (0, 1, 0) as the upwards vector. 205 | * The output is undefined for parallel vectors. 206 | * @param forward: The forward direction to look toward. 207 | * @return: A new quaternion. 208 | */ 209 | static inline Quaternion LookRotation(Vector3 forward); 210 | 211 | /** 212 | * Creates a rotation with the specified forward and upwards directions. 213 | * The output is undefined for parallel vectors. 214 | * @param forward: The forward direction to look toward. 215 | * @param upwards: The direction to treat as up. 216 | * @return: A new quaternion. 217 | */ 218 | static inline Quaternion LookRotation(Vector3 forward, Vector3 upwards); 219 | 220 | /** 221 | * Returns the norm of a quaternion. 222 | * @param rotation: The quaternion in question. 223 | * @return: A scalar value. 224 | */ 225 | static inline float Norm(Quaternion rotation); 226 | 227 | /** 228 | * Returns a quaternion with identical rotation and a norm of one. 229 | * @param rotation: The quaternion in question. 230 | * @return: A new quaternion. 231 | */ 232 | static inline Quaternion Normalized(Quaternion rotation); 233 | 234 | /** 235 | * Returns a new Quaternion created by rotating "from" towards "to" by 236 | * "maxRadiansDelta". This will not overshoot, and if a negative delta is 237 | * applied, it will rotate till completely opposite "to" and then stop. 238 | * @param from: The rotation at which to start. 239 | * @param to: The rotation at which to end. 240 | # @param maxRadiansDelta: The maximum number of radians to rotate. 241 | * @return: A new Quaternion. 242 | */ 243 | static inline Quaternion RotateTowards(Quaternion from, Quaternion to, 244 | float maxRadiansDelta); 245 | 246 | /** 247 | * Returns a new quaternion interpolated between a and b, using spherical 248 | * linear interpolation. The variable t is clamped to the range [0-1]. The 249 | * resulting quaternion will be normalized. 250 | * @param a: The starting rotation. 251 | * @param b: The ending rotation. 252 | * @param t: The interpolation value. 253 | * @return: A new quaternion. 254 | */ 255 | static inline Quaternion Slerp(Quaternion a, Quaternion b, float t); 256 | 257 | /** 258 | * Returns a new quaternion interpolated between a and b, using spherical 259 | * linear interpolation. The resulting quaternion will be normalized. 260 | * @param a: The starting rotation. 261 | * @param b: The ending rotation. 262 | * @param t: The interpolation value. 263 | * @return: A new quaternion. 264 | */ 265 | static inline Quaternion SlerpUnclamped(Quaternion a, Quaternion b, 266 | float t); 267 | 268 | /** 269 | * Outputs the angle axis representation of the provided quaternion. 270 | * @param rotation: The input quaternion. 271 | * @param angle: The output angle. 272 | * @param axis: The output axis. 273 | */ 274 | static inline void ToAngleAxis(Quaternion rotation, float &angle, 275 | Vector3 &axis); 276 | 277 | /** 278 | * Returns the Euler angle representation of a rotation. The resulting 279 | * vector contains the rotations about the z, x and y axis, in that order. 280 | * @param rotation: The quaternion to convert. 281 | * @return: A new vector. 282 | */ 283 | static inline Vector3 ToEuler(Quaternion rotation); 284 | 285 | /** 286 | * Operator overloading. 287 | */ 288 | inline struct Quaternion& operator+=(const float rhs); 289 | inline struct Quaternion& operator-=(const float rhs); 290 | inline struct Quaternion& operator*=(const float rhs); 291 | inline struct Quaternion& operator/=(const float rhs); 292 | inline struct Quaternion& operator+=(const Quaternion rhs); 293 | inline struct Quaternion& operator-=(const Quaternion rhs); 294 | inline struct Quaternion& operator*=(const Quaternion rhs); 295 | }; 296 | 297 | inline Quaternion operator-(Quaternion rhs); 298 | inline Quaternion operator+(Quaternion lhs, const float rhs); 299 | inline Quaternion operator-(Quaternion lhs, const float rhs); 300 | inline Quaternion operator*(Quaternion lhs, const float rhs); 301 | inline Quaternion operator/(Quaternion lhs, const float rhs); 302 | inline Quaternion operator+(const float lhs, Quaternion rhs); 303 | inline Quaternion operator-(const float lhs, Quaternion rhs); 304 | inline Quaternion operator*(const float lhs, Quaternion rhs); 305 | inline Quaternion operator/(const float lhs, Quaternion rhs); 306 | inline Quaternion operator+(Quaternion lhs, const Quaternion rhs); 307 | inline Quaternion operator-(Quaternion lhs, const Quaternion rhs); 308 | inline Quaternion operator*(Quaternion lhs, const Quaternion rhs); 309 | inline Vector3 operator*(Quaternion lhs, const Vector3 rhs); 310 | inline bool operator==(const Quaternion lhs, const Quaternion rhs); 311 | inline bool operator!=(const Quaternion lhs, const Quaternion rhs); 312 | 313 | 314 | 315 | /******************************************************************************* 316 | * Implementation 317 | */ 318 | 319 | Quaternion::Quaternion() : X(0), Y(0), Z(0), W(1) {} 320 | Quaternion::Quaternion(float data[]) : X(data[0]), Y(data[1]), Z(data[2]), 321 | W(data[3]) {} 322 | Quaternion::Quaternion(Vector3 vector, float scalar) : X(vector.X), 323 | Y(vector.Y), Z(vector.Z), W(scalar) {} 324 | Quaternion::Quaternion(float x, float y, float z, float w) : X(x), Y(y), 325 | Z(z), W(w) {} 326 | 327 | 328 | Quaternion Quaternion::Identity() { return Quaternion(0, 0, 0, 1); } 329 | 330 | 331 | float Quaternion::Angle(Quaternion a, Quaternion b) 332 | { 333 | float dot = Dot(a, b); 334 | return acos(fmin(fabs(dot), 1)) * 2; 335 | } 336 | 337 | Quaternion Quaternion::Conjugate(Quaternion rotation) 338 | { 339 | return Quaternion(-rotation.X, -rotation.Y, -rotation.Z, rotation.W); 340 | } 341 | 342 | float Quaternion::Dot(Quaternion lhs, Quaternion rhs) 343 | { 344 | return lhs.X * rhs.X + lhs.Y * rhs.Y + lhs.Z * rhs.Z + lhs.W * rhs.W; 345 | } 346 | 347 | Quaternion Quaternion::FromAngleAxis(float angle, Vector3 axis) 348 | { 349 | Quaternion q; 350 | float m = sqrt(axis.X * axis.X + axis.Y * axis.Y + axis.Z * axis.Z); 351 | float s = sin(angle / 2) / m; 352 | q.X = axis.X * s; 353 | q.Y = axis.Y * s; 354 | q.Z = axis.Z * s; 355 | q.W = cos(angle / 2); 356 | return q; 357 | } 358 | 359 | Quaternion Quaternion::FromEuler(Vector3 rotation) 360 | { 361 | return FromEuler(rotation.X, rotation.Y, rotation.Z); 362 | } 363 | 364 | Quaternion Quaternion::FromEuler(float x, float y, float z) 365 | { 366 | float cx = cos(x * 0.5); 367 | float cy = cos(y * 0.5); 368 | float cz = cos(z * 0.5); 369 | float sx = sin(x * 0.5); 370 | float sy = sin(y * 0.5); 371 | float sz = sin(z * 0.5); 372 | Quaternion q; 373 | q.X = cx * sy * sz + cy * cz * sx; 374 | q.Y = cx * cz * sy - cy * sx * sz; 375 | q.Z = cx * cy * sz - cz * sx * sy; 376 | q.W = sx * sy * sz + cx * cy * cz; 377 | return q; 378 | } 379 | 380 | Quaternion Quaternion::FromToRotation(Vector3 fromVector, Vector3 toVector) 381 | { 382 | float dot = Vector3::Dot(fromVector, toVector); 383 | float k = sqrt(Vector3::SqrMagnitude(fromVector) * 384 | Vector3::SqrMagnitude(toVector)); 385 | if (fabs(dot / k + 1) < 0.00001) 386 | { 387 | Vector3 ortho = Vector3::Orthogonal(fromVector); 388 | return Quaternion(Vector3::Normalized(ortho), 0); 389 | } 390 | Vector3 cross = Vector3::Cross(fromVector, toVector); 391 | return Normalized(Quaternion(cross, dot + k)); 392 | } 393 | 394 | Quaternion Quaternion::Inverse(Quaternion rotation) 395 | { 396 | float n = Norm(rotation); 397 | return Conjugate(rotation) / (n * n); 398 | } 399 | 400 | Quaternion Quaternion::Lerp(Quaternion a, Quaternion b, float t) 401 | { 402 | if (t < 0) return Normalized(a); 403 | else if (t > 1) return Normalized(b); 404 | return LerpUnclamped(a, b, t); 405 | } 406 | 407 | Quaternion Quaternion::LerpUnclamped(Quaternion a, Quaternion b, float t) 408 | { 409 | Quaternion quaternion; 410 | if (Dot(a, b) >= 0) 411 | quaternion = a * (1 - t) + b * t; 412 | else 413 | quaternion = a * (1 - t) - b * t; 414 | return Normalized(quaternion); 415 | } 416 | 417 | Quaternion Quaternion::LookRotation(Vector3 forward) 418 | { 419 | return LookRotation(forward, Vector3(0, 1, 0)); 420 | } 421 | 422 | Quaternion Quaternion::LookRotation(Vector3 forward, Vector3 upwards) 423 | { 424 | // Normalize inputs 425 | forward = Vector3::Normalized(forward); 426 | upwards = Vector3::Normalized(upwards); 427 | // Don't allow zero vectors 428 | if (Vector3::SqrMagnitude(forward) < SMALL_float || Vector3::SqrMagnitude(upwards) < SMALL_float) 429 | return Quaternion::Identity(); 430 | // Handle alignment with up direction 431 | if (1 - fabs(Vector3::Dot(forward, upwards)) < SMALL_float) 432 | return FromToRotation(Vector3::Forward(), forward); 433 | // Get orthogonal vectors 434 | Vector3 right = Vector3::Normalized(Vector3::Cross(upwards, forward)); 435 | upwards = Vector3::Cross(forward, right); 436 | // Calculate rotation 437 | Quaternion quaternion; 438 | float radicand = right.X + upwards.Y + forward.Z; 439 | if (radicand > 0) 440 | { 441 | quaternion.W = sqrt(1.0 + radicand) * 0.5; 442 | float recip = 1.0 / (4.0 * quaternion.W); 443 | quaternion.X = (upwards.Z - forward.Y) * recip; 444 | quaternion.Y = (forward.X - right.Z) * recip; 445 | quaternion.Z = (right.Y - upwards.X) * recip; 446 | } 447 | else if (right.X >= upwards.Y && right.X >= forward.Z) 448 | { 449 | quaternion.X = sqrt(1.0 + right.X - upwards.Y - forward.Z) * 0.5; 450 | float recip = 1.0 / (4.0 * quaternion.X); 451 | quaternion.W = (upwards.Z - forward.Y) * recip; 452 | quaternion.Z = (forward.X + right.Z) * recip; 453 | quaternion.Y = (right.Y + upwards.X) * recip; 454 | } 455 | else if (upwards.Y > forward.Z) 456 | { 457 | quaternion.Y = sqrt(1.0 - right.X + upwards.Y - forward.Z) * 0.5; 458 | float recip = 1.0 / (4.0 * quaternion.Y); 459 | quaternion.Z = (upwards.Z + forward.Y) * recip; 460 | quaternion.W = (forward.X - right.Z) * recip; 461 | quaternion.X = (right.Y + upwards.X) * recip; 462 | } 463 | else 464 | { 465 | quaternion.Z = sqrt(1.0 - right.X - upwards.Y + forward.Z) * 0.5; 466 | float recip = 1.0 / (4.0 * quaternion.Z); 467 | quaternion.Y = (upwards.Z + forward.Y) * recip; 468 | quaternion.X = (forward.X + right.Z) * recip; 469 | quaternion.W = (right.Y - upwards.X) * recip; 470 | } 471 | return quaternion; 472 | } 473 | 474 | float Quaternion::Norm(Quaternion rotation) 475 | { 476 | return sqrt(rotation.X * rotation.X + 477 | rotation.Y * rotation.Y + 478 | rotation.Z * rotation.Z + 479 | rotation.W * rotation.W); 480 | } 481 | 482 | Quaternion Quaternion::Normalized(Quaternion rotation) 483 | { 484 | return rotation / Norm(rotation); 485 | } 486 | 487 | Quaternion Quaternion::RotateTowards(Quaternion from, Quaternion to, 488 | float maxRadiansDelta) 489 | { 490 | float angle = Quaternion::Angle(from, to); 491 | if (angle == 0) 492 | return to; 493 | maxRadiansDelta = fmax(maxRadiansDelta, angle - M_PI); 494 | float t = fmin(1, maxRadiansDelta / angle); 495 | return Quaternion::SlerpUnclamped(from, to, t); 496 | } 497 | 498 | Quaternion Quaternion::Slerp(Quaternion a, Quaternion b, float t) 499 | { 500 | if (t < 0) return Normalized(a); 501 | else if (t > 1) return Normalized(b); 502 | return SlerpUnclamped(a, b, t); 503 | } 504 | 505 | Quaternion Quaternion::SlerpUnclamped(Quaternion a, Quaternion b, float t) 506 | { 507 | float n1; 508 | float n2; 509 | float n3 = Dot(a, b); 510 | bool flag = false; 511 | if (n3 < 0) 512 | { 513 | flag = true; 514 | n3 = -n3; 515 | } 516 | if (n3 > 0.999999) 517 | { 518 | n2 = 1 - t; 519 | n1 = flag ? -t : t; 520 | } 521 | else 522 | { 523 | float n4 = acos(n3); 524 | float n5 = 1 / sin(n4); 525 | n2 = sin((1 - t) * n4) * n5; 526 | n1 = flag ? -sin(t * n4) * n5 : sin(t * n4) * n5; 527 | } 528 | Quaternion quaternion; 529 | quaternion.X = (n2 * a.X) + (n1 * b.X); 530 | quaternion.Y = (n2 * a.Y) + (n1 * b.Y); 531 | quaternion.Z = (n2 * a.Z) + (n1 * b.Z); 532 | quaternion.W = (n2 * a.W) + (n1 * b.W); 533 | return Normalized(quaternion); 534 | } 535 | 536 | void Quaternion::ToAngleAxis(Quaternion rotation, float &angle, Vector3 &axis) 537 | { 538 | if (rotation.W > 1) 539 | rotation = Normalized(rotation); 540 | angle = 2 * acos(rotation.W); 541 | float s = sqrt(1 - rotation.W * rotation.W); 542 | if (s < 0.00001) { 543 | axis.X = 1; 544 | axis.Y = 0; 545 | axis.Z = 0; 546 | } else { 547 | axis.X = rotation.X / s; 548 | axis.Y = rotation.Y / s; 549 | axis.Z = rotation.Z / s; 550 | } 551 | } 552 | 553 | Vector3 Quaternion::ToEuler(Quaternion rotation) 554 | { 555 | float sqw = rotation.W * rotation.W; 556 | float sqx = rotation.X * rotation.X; 557 | float sqy = rotation.Y * rotation.Y; 558 | float sqz = rotation.Z * rotation.Z; 559 | // If normalized is one, otherwise is correction factor 560 | float unit = sqx + sqy + sqz + sqw; 561 | float test = rotation.X * rotation.W - rotation.Y * rotation.Z; 562 | Vector3 v; 563 | // Singularity at north pole 564 | if (test > 0.4995f * unit) 565 | { 566 | v.Y = 2 * atan2(rotation.Y, rotation.X); 567 | v.X = M_PI_2; 568 | v.Z = 0; 569 | return v; 570 | } 571 | // Singularity at south pole 572 | if (test < -0.4995f * unit) 573 | { 574 | v.Y = -2 * atan2(rotation.Y, rotation.X); 575 | v.X = -M_PI_2; 576 | v.Z = 0; 577 | return v; 578 | } 579 | // Yaw 580 | v.Y = atan2(2 * rotation.W * rotation.Y + 2 * rotation.Z * rotation.X, 581 | 1 - 2 * (rotation.X * rotation.X + rotation.Y * rotation.Y)); 582 | // Pitch 583 | v.X = asin(2 * (rotation.W * rotation.X - rotation.Y * rotation.Z)); 584 | // Roll 585 | v.Z = atan2(2 * rotation.W * rotation.Z + 2 * rotation.X * rotation.Y, 586 | 1 - 2 * (rotation.Z * rotation.Z + rotation.X * rotation.X)); 587 | return v; 588 | } 589 | 590 | struct Quaternion& Quaternion::operator+=(const float rhs) 591 | { 592 | X += rhs; 593 | Y += rhs; 594 | Z += rhs; 595 | W += rhs; 596 | return *this; 597 | } 598 | 599 | struct Quaternion& Quaternion::operator-=(const float rhs) 600 | { 601 | X -= rhs; 602 | Y -= rhs; 603 | Z -= rhs; 604 | W -= rhs; 605 | return *this; 606 | } 607 | 608 | struct Quaternion& Quaternion::operator*=(const float rhs) 609 | { 610 | X *= rhs; 611 | Y *= rhs; 612 | Z *= rhs; 613 | W *= rhs; 614 | return *this; 615 | } 616 | 617 | struct Quaternion& Quaternion::operator/=(const float rhs) 618 | { 619 | X /= rhs; 620 | Y /= rhs; 621 | Z /= rhs; 622 | W /= rhs; 623 | return *this; 624 | } 625 | 626 | struct Quaternion& Quaternion::operator+=(const Quaternion rhs) 627 | { 628 | X += rhs.X; 629 | Y += rhs.Y; 630 | Z += rhs.Z; 631 | W += rhs.W; 632 | return *this; 633 | } 634 | 635 | struct Quaternion& Quaternion::operator-=(const Quaternion rhs) 636 | { 637 | X -= rhs.X; 638 | Y -= rhs.Y; 639 | Z -= rhs.Z; 640 | W -= rhs.W; 641 | return *this; 642 | } 643 | 644 | struct Quaternion& Quaternion::operator*=(const Quaternion rhs) 645 | { 646 | Quaternion q; 647 | q.W = W * rhs.W - X * rhs.X - Y * rhs.Y - Z * rhs.Z; 648 | q.X = X * rhs.W + W * rhs.X + Y * rhs.Z - Z * rhs.Y; 649 | q.Y = W * rhs.Y - X * rhs.Z + Y * rhs.W + Z * rhs.X; 650 | q.Z = W * rhs.Z + X * rhs.Y - Y * rhs.X + Z * rhs.W; 651 | *this = q; 652 | return *this; 653 | } 654 | 655 | Quaternion operator-(Quaternion rhs) { return rhs * -1; } 656 | Quaternion operator+(Quaternion lhs, const float rhs) { return lhs += rhs; } 657 | Quaternion operator-(Quaternion lhs, const float rhs) { return lhs -= rhs; } 658 | Quaternion operator*(Quaternion lhs, const float rhs) { return lhs *= rhs; } 659 | Quaternion operator/(Quaternion lhs, const float rhs) { return lhs /= rhs; } 660 | Quaternion operator+(const float lhs, Quaternion rhs) { return rhs += lhs; } 661 | Quaternion operator-(const float lhs, Quaternion rhs) { return rhs -= lhs; } 662 | Quaternion operator*(const float lhs, Quaternion rhs) { return rhs *= lhs; } 663 | Quaternion operator/(const float lhs, Quaternion rhs) { return rhs /= lhs; } 664 | Quaternion operator+(Quaternion lhs, const Quaternion rhs) 665 | { 666 | return lhs += rhs; 667 | } 668 | Quaternion operator-(Quaternion lhs, const Quaternion rhs) 669 | { 670 | return lhs -= rhs; 671 | } 672 | Quaternion operator*(Quaternion lhs, const Quaternion rhs) 673 | { 674 | return lhs *= rhs; 675 | } 676 | 677 | Vector3 operator*(Quaternion lhs, const Vector3 rhs) 678 | { 679 | Vector3 u = Vector3(lhs.X, lhs.Y, lhs.Z); 680 | float s = lhs.W; 681 | return u * (Vector3::Dot(u, rhs) * 2) 682 | + rhs * (s * s - Vector3::Dot(u, u)) 683 | + Vector3::Cross(u, rhs) * (2.0 * s); 684 | } 685 | 686 | bool operator==(const Quaternion lhs, const Quaternion rhs) 687 | { 688 | return lhs.X == rhs.X && 689 | lhs.Y == rhs.Y && 690 | lhs.Z == rhs.Z && 691 | lhs.W == rhs.W; 692 | } 693 | 694 | bool operator!=(const Quaternion lhs, const Quaternion rhs) 695 | { 696 | return !(lhs == rhs); 697 | } -------------------------------------------------------------------------------- /app/src/main/cpp/Engine/Rect.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct Rect 6 | { 7 | union 8 | { 9 | struct 10 | { 11 | float x; 12 | float y; 13 | float width; 14 | float height; 15 | }; 16 | }; 17 | 18 | 19 | inline Rect(float r, float y, float width, float height); 20 | 21 | }; 22 | 23 | Rect::Rect(float x, float y, float width, float height) : x(x), y(y), width(width), height(height) {} -------------------------------------------------------------------------------- /app/src/main/cpp/Engine/Unity.h: -------------------------------------------------------------------------------- 1 | #ifndef UNITY_H 2 | #define UNITY_H 3 | 4 | #include 5 | 6 | /* 7 | This struct can hold a native C# array. Credits to caoyin. 8 | Think of it like a wrapper for a C array. For example, if you had Player[] players in a dump, 9 | the resulting monoArray definition would be monoArray *players; 10 | To get the C array, call getPointer. 11 | To get the length, call getLength. 12 | */ 13 | template 14 | struct monoArray 15 | { 16 | void* klass; 17 | void* monitor; 18 | void* bounds; 19 | int max_length; 20 | void* vector [1]; 21 | int getLength() 22 | { 23 | return max_length; 24 | } 25 | T getPointer() 26 | { 27 | return (T)vector; 28 | } 29 | }; 30 | 31 | /* 32 | This struct represents a C# string. Credits to caoyin. 33 | It is pretty straight forward. If you have this in a dump, 34 | public class Player { 35 | public string username; // 0xC8 36 | } 37 | getting that string would look like this: monoString *username = *(monoString **)((uint64_t)player + 0xc8); 38 | C# strings are UTF-16. This means each character is two bytes instead of one. 39 | To get the length of a monoString, call getLength. 40 | To get a NSString from a monoString, call toNSString. 41 | To get a std::string from a monoString, call toCPPString. 42 | To get a C string from a monoString, call toCString. 43 | */ 44 | typedef struct _monoString 45 | { 46 | void *klass; 47 | void *monitor; 48 | int length; 49 | char chars[1]; 50 | 51 | int getLength() 52 | { 53 | return length; 54 | } 55 | 56 | char *getRawChars() 57 | { 58 | return chars; 59 | } 60 | 61 | std::string getString() 62 | { 63 | std::u16string u16string(reinterpret_cast(chars)); 64 | std::wstring wstring(u16string.begin(), u16string.end()); 65 | std::wstring_convert> convert; 66 | 67 | return convert.to_bytes(wstring); 68 | } 69 | 70 | const char *getChars() 71 | { 72 | return getString().c_str(); 73 | } 74 | }monoString; 75 | 76 | /* 77 | This struct represents a List. In the dump, a List is declared as List`1. 78 | Deep down, this simply wraps a C array around a C# list. For example, if you had this in a dump, 79 | public class Player { 80 | List`1 perks; // 0xDC 81 | } 82 | getting that list would look like this: monoList *perks = *(monoList **)((uint64_t)player + 0xdc); 83 | You can also get lists that hold objects, but you need to use void ** because we don't have implementation for the Weapon class. 84 | public class Player { 85 | List`1 weapons; // 0xDC 86 | } 87 | getting that list would look like this: monoList *weapons = *(monoList **)((uint64_t)player + 0xdc); 88 | If you need a list of strings, use monoString **. 89 | To get the C array, call getItems. 90 | To get the size of a monoList, call getSize. 91 | */ 92 | template 93 | struct monoList { 94 | void *unk0; 95 | void *unk1; 96 | monoArray *items; 97 | int size; 98 | int version; 99 | 100 | T getItems(){ 101 | return items->getPointer(); 102 | } 103 | 104 | int getSize(){ 105 | return size; 106 | } 107 | 108 | int getVersion(){ 109 | return version; 110 | } 111 | }; 112 | 113 | /* 114 | This struct represents a Dictionary. In the dump, a Dictionary is defined as Dictionary`1. 115 | You could think of this as a Map in Java or C++. Keys correspond with values. This wraps the C arrays for keys and values. 116 | If you had this in a dump, 117 | public class GameManager { 118 | public Dictionary`1 players; // 0xB0 119 | public Dictionary`1 playerWeapons; // 0xB8 120 | public Dictionary`1 playerNames; // 0xBC 121 | } 122 | to get players, it would look like this: monoDictionary *players = *(monoDictionary **)((uint64_t)player + 0xb0); 123 | to get playerWeapons, it would look like this: monoDictionary *playerWeapons = *(monoDictionary **)((uint64_t)player + 0xb8); 124 | to get playerNames, it would look like this: monoDictionary *playerNames = *(monoDictionary **)((uint64_t)player + 0xbc); 125 | To get the C array of keys, call getKeys. 126 | To get the C array of values, call getValues. 127 | To get the number of keys, call getNumKeys. 128 | To get the number of values, call getNumValues. 129 | */ 130 | template 131 | struct monoDictionary { 132 | void *unk0; 133 | void *unk1; 134 | monoArray *table; 135 | monoArray *linkSlots; 136 | monoArray *keys; 137 | monoArray *values; 138 | int touchedSlots; 139 | int emptySlot; 140 | int size; 141 | 142 | K getKeys(){ 143 | return keys->getPointer(); 144 | } 145 | 146 | V getValues(){ 147 | return values->getPointer(); 148 | } 149 | 150 | int getNumKeys(){ 151 | return keys->getLength(); 152 | } 153 | 154 | int getNumValues(){ 155 | return values->getLength(); 156 | } 157 | 158 | int getSize(){ 159 | return size; 160 | } 161 | }; 162 | 163 | int GetObscuredIntValue(uint64_t location) 164 | { 165 | int cryptoKey = *(int *)location; 166 | int obfuscatedValue = *(int *)(location + 0x4); 167 | 168 | return obfuscatedValue ^ cryptoKey; 169 | } 170 | 171 | bool GetObscuredBoolValue(uint64_t location) 172 | { 173 | int cryptoKey = *(int *)location; 174 | int obfuscatedValue = *(int *)(location + 0x4); 175 | 176 | return (bool)obfuscatedValue ^ cryptoKey; 177 | } 178 | 179 | /* 180 | Set the real value of an ObscuredInt. 181 | Parameters: 182 | - location: the location of the ObscuredInt 183 | - value: the value we're setting the ObscuredInt to 184 | */ 185 | 186 | void SetObscuredIntValue(uint64_t location, int value) 187 | { 188 | int cryptoKey = *(int *)location; 189 | 190 | *(int *)(location + 0x4) = value ^ cryptoKey; 191 | } 192 | 193 | void SetObscuredBoolValue(uint64_t location, bool value) 194 | { 195 | int cryptoKey = *(int *)location; 196 | 197 | *(int *)(location + 0x4) = value ^ cryptoKey; 198 | } 199 | 200 | /* 201 | Get the real value of an ObscuredFloat. 202 | Parameters: 203 | - location: the location of the ObscuredFloat 204 | */ 205 | 206 | float GetObscuredFloatValue(uint64_t location) 207 | { 208 | int cryptoKey = *(int *) location; 209 | int obfuscatedValue = *(int *) (location + 0x4); 210 | 211 | union intfloat 212 | { 213 | int i; 214 | float f; 215 | }; 216 | 217 | /* use this intfloat to set the integer representation of our parameter value, which will also set the float value */ 218 | intfloat IF; 219 | IF.i = obfuscatedValue ^ cryptoKey; 220 | 221 | return IF.f; 222 | } 223 | 224 | /* 225 | Set the real value of an ObscuredFloat. 226 | Parameters: 227 | - location: the location of the ObscuredFloat 228 | - value: the value we're setting the ObscuredFloat to 229 | */ 230 | 231 | void SetObscuredFloatValue(uint64_t location, float value) 232 | { 233 | int cryptoKey = *(int *) location; 234 | 235 | union intfloat 236 | { 237 | int i; 238 | float f; 239 | }; 240 | 241 | /* use this intfloat to get the integer representation of our parameter value */ 242 | intfloat IF; 243 | IF.f = value; 244 | 245 | /* use this intfloat to generate our hacked ObscuredFloat */ 246 | intfloat IF2; 247 | IF2.i = IF.i ^ cryptoKey; 248 | 249 | *(float *) (location + 0x4) = IF2.f; 250 | } 251 | 252 | #endif 253 | -------------------------------------------------------------------------------- /app/src/main/cpp/Engine/Vector2.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define _USE_MATH_DEFINES 4 | #include 5 | 6 | struct Vector2 7 | { 8 | union 9 | { 10 | struct 11 | { 12 | float X; 13 | float Y; 14 | }; 15 | float data[2]; 16 | }; 17 | 18 | 19 | /** 20 | * Constructors. 21 | */ 22 | inline Vector2(); 23 | inline Vector2(float data[]); 24 | inline Vector2(float value); 25 | inline Vector2(float x, float y); 26 | 27 | 28 | /** 29 | * Constants for common vectors. 30 | */ 31 | static inline Vector2 Zero(); 32 | static inline Vector2 One(); 33 | static inline Vector2 Right(); 34 | static inline Vector2 Left(); 35 | static inline Vector2 Up(); 36 | static inline Vector2 Down(); 37 | 38 | 39 | /** 40 | * Returns the angle between two vectors in radians. 41 | * @param a: The first vector. 42 | * @param b: The second vector. 43 | * @return: A scalar value. 44 | */ 45 | static inline float Angle(Vector2 a, Vector2 b); 46 | 47 | /** 48 | * Returns a vector with its magnitude clamped to maxLength. 49 | * @param vector: The target vector. 50 | * @param maxLength: The maximum length of the return vector. 51 | * @return: A new vector. 52 | */ 53 | static inline Vector2 ClampMagnitude(Vector2 vector, float maxLength); 54 | 55 | /** 56 | * Returns the component of a in the direction of b (scalar projection). 57 | * @param a: The target vector. 58 | * @param b: The vector being compared against. 59 | * @return: A scalar value. 60 | */ 61 | static inline float Component(Vector2 a, Vector2 b); 62 | 63 | /** 64 | * Returns the distance between a and b. 65 | * @param a: The first point. 66 | * @param b: The second point. 67 | * @return: A scalar value. 68 | */ 69 | static inline float Distance(Vector2 a, Vector2 b); 70 | 71 | /** 72 | * Returns the dot product of two vectors. 73 | * @param lhs: The left side of the multiplication. 74 | * @param rhs: The right side of the multiplication. 75 | * @return: A scalar value. 76 | */ 77 | static inline float Dot(Vector2 lhs, Vector2 rhs); 78 | 79 | /** 80 | * Converts a polar representation of a vector into cartesian 81 | * coordinates. 82 | * @param rad: The magnitude of the vector. 83 | * @param theta: The angle from the X axis. 84 | * @return: A new vector. 85 | */ 86 | static inline Vector2 FromPolar(float rad, float theta); 87 | 88 | /** 89 | * Returns a vector linearly interpolated between a and b, moving along 90 | * a straight line. The vector is clamped to never go beyond the end points. 91 | * @param a: The starting point. 92 | * @param b: The ending point. 93 | * @param t: The interpolation value [0-1]. 94 | * @return: A new vector. 95 | */ 96 | static inline Vector2 Lerp(Vector2 a, Vector2 b, float t); 97 | 98 | /** 99 | * Returns a vector linearly interpolated between a and b, moving along 100 | * a straight line. 101 | * @param a: The starting point. 102 | * @param b: The ending point. 103 | * @param t: The interpolation value [0-1] (no actual bounds). 104 | * @return: A new vector. 105 | */ 106 | static inline Vector2 LerpUnclamped(Vector2 a, Vector2 b, float t); 107 | 108 | /** 109 | * Returns the magnitude of a vector. 110 | * @param v: The vector in question. 111 | * @return: A scalar value. 112 | */ 113 | static inline float Magnitude(Vector2 v); 114 | 115 | /** 116 | * Returns a vector made from the largest components of two other vectors. 117 | * @param a: The first vector. 118 | * @param b: The second vector. 119 | * @return: A new vector. 120 | */ 121 | static inline Vector2 Max(Vector2 a, Vector2 b); 122 | 123 | /** 124 | * Returns a vector made from the smallest components of two other vectors. 125 | * @param a: The first vector. 126 | * @param b: The second vector. 127 | * @return: A new vector. 128 | */ 129 | static inline Vector2 Min(Vector2 a, Vector2 b); 130 | 131 | /** 132 | * Returns a vector "maxDistanceDelta" units closer to the target. This 133 | * interpolation is in a straight line, and will not overshoot. 134 | * @param current: The current position. 135 | * @param target: The destination position. 136 | * @param maxDistanceDelta: The maximum distance to move. 137 | * @return: A new vector. 138 | */ 139 | static inline Vector2 MoveTowards(Vector2 current, Vector2 target, 140 | float maxDistanceDelta); 141 | 142 | /** 143 | * Returns a new vector with magnitude of one. 144 | * @param v: The vector in question. 145 | * @return: A new vector. 146 | */ 147 | static inline Vector2 Normalized(Vector2 v); 148 | 149 | /** 150 | * Creates a new coordinate system out of the two vectors. 151 | * Normalizes "normal" and normalizes "tangent" and makes it orthogonal to 152 | * "normal".. 153 | * @param normal: A reference to the first axis vector. 154 | * @param tangent: A reference to the second axis vector. 155 | */ 156 | static inline void OrthoNormalize(Vector2 &normal, Vector2 &tangent); 157 | 158 | /** 159 | * Returns the vector projection of a onto b. 160 | * @param a: The target vector. 161 | * @param b: The vector being projected onto. 162 | * @return: A new vector. 163 | */ 164 | static inline Vector2 Project(Vector2 a, Vector2 b); 165 | 166 | /** 167 | * Returns a vector reflected about the provided line. 168 | * This behaves as if there is a plane with the line as its normal, and the 169 | * vector comes in and bounces off this plane. 170 | * @param vector: The vector traveling inward at the imaginary plane. 171 | * @param line: The line about which to reflect. 172 | * @return: A new vector pointing outward from the imaginary plane. 173 | */ 174 | static inline Vector2 Reflect(Vector2 vector, Vector2 line); 175 | 176 | /** 177 | * Returns the vector rejection of a on b. 178 | * @param a: The target vector. 179 | * @param b: The vector being projected onto. 180 | * @return: A new vector. 181 | */ 182 | static inline Vector2 Reject(Vector2 a, Vector2 b); 183 | 184 | /** 185 | * Rotates vector "current" towards vector "target" by "maxRadiansDelta". 186 | * This treats the vectors as directions and will linearly interpolate 187 | * between their magnitudes by "maxMagnitudeDelta". This function does not 188 | * overshoot. If a negative delta is supplied, it will rotate away from 189 | * "target" until it is pointing the opposite direction, but will not 190 | * overshoot that either. 191 | * @param current: The starting direction. 192 | * @param target: The destination direction. 193 | * @param maxRadiansDelta: The maximum number of radians to rotate. 194 | * @param maxMagnitudeDelta: The maximum delta for magnitude interpolation. 195 | * @return: A new vector. 196 | */ 197 | static inline Vector2 RotateTowards(Vector2 current, Vector2 target, 198 | float maxRadiansDelta, 199 | float maxMagnitudeDelta); 200 | 201 | /** 202 | * Multiplies two vectors component-wise. 203 | * @param a: The lhs of the multiplication. 204 | * @param b: The rhs of the multiplication. 205 | * @return: A new vector. 206 | */ 207 | static inline Vector2 Scale(Vector2 a, Vector2 b); 208 | 209 | /** 210 | * Returns a vector rotated towards b from a by the percent t. 211 | * Since interpolation is done spherically, the vector moves at a constant 212 | * angular velocity. This rotation is clamped to 0 <= t <= 1. 213 | * @param a: The starting direction. 214 | * @param b: The ending direction. 215 | * @param t: The interpolation value [0-1]. 216 | */ 217 | static inline Vector2 Slerp(Vector2 a, Vector2 b, float t); 218 | 219 | /** 220 | * Returns a vector rotated towards b from a by the percent t. 221 | * Since interpolation is done spherically, the vector moves at a constant 222 | * angular velocity. This rotation is unclamped. 223 | * @param a: The starting direction. 224 | * @param b: The ending direction. 225 | * @param t: The interpolation value [0-1]. 226 | */ 227 | static inline Vector2 SlerpUnclamped(Vector2 a, Vector2 b, float t); 228 | 229 | /** 230 | * Returns the squared magnitude of a vector. 231 | * This is useful when comparing relative lengths, where the exact length 232 | * is not important, and much time can be saved by not calculating the 233 | * square root. 234 | * @param v: The vector in question. 235 | * @return: A scalar value. 236 | */ 237 | static inline float SqrMagnitude(Vector2 v); 238 | 239 | /** 240 | * Calculates the polar coordinate space representation of a vector. 241 | * @param vector: The vector to convert. 242 | * @param rad: The magnitude of the vector. 243 | * @param theta: The angle from the X axis. 244 | */ 245 | static inline void ToPolar(Vector2 vector, float &rad, float &theta); 246 | 247 | 248 | /** 249 | * Operator overloading. 250 | */ 251 | inline struct Vector2& operator+=(const float rhs); 252 | inline struct Vector2& operator-=(const float rhs); 253 | inline struct Vector2& operator*=(const float rhs); 254 | inline struct Vector2& operator/=(const float rhs); 255 | inline struct Vector2& operator+=(const Vector2 rhs); 256 | inline struct Vector2& operator-=(const Vector2 rhs); 257 | }; 258 | 259 | inline Vector2 operator-(Vector2 rhs); 260 | inline Vector2 operator+(Vector2 lhs, const float rhs); 261 | inline Vector2 operator-(Vector2 lhs, const float rhs); 262 | inline Vector2 operator*(Vector2 lhs, const float rhs); 263 | inline Vector2 operator/(Vector2 lhs, const float rhs); 264 | inline Vector2 operator+(const float lhs, Vector2 rhs); 265 | inline Vector2 operator-(const float lhs, Vector2 rhs); 266 | inline Vector2 operator*(const float lhs, Vector2 rhs); 267 | inline Vector2 operator/(const float lhs, Vector2 rhs); 268 | inline Vector2 operator+(Vector2 lhs, const Vector2 rhs); 269 | inline Vector2 operator-(Vector2 lhs, const Vector2 rhs); 270 | inline bool operator==(const Vector2 lhs, const Vector2 rhs); 271 | inline bool operator!=(const Vector2 lhs, const Vector2 rhs); 272 | 273 | 274 | 275 | /******************************************************************************* 276 | * Implementation 277 | */ 278 | 279 | Vector2::Vector2() : X(0), Y(0) {} 280 | Vector2::Vector2(float data[]) : X(data[0]), Y(data[1]) {} 281 | Vector2::Vector2(float value) : X(value), Y(value) {} 282 | Vector2::Vector2(float x, float y) : X(x), Y(y) {} 283 | 284 | 285 | Vector2 Vector2::Zero() { return Vector2(0, 0); } 286 | Vector2 Vector2::One() { return Vector2(1, 1); } 287 | Vector2 Vector2::Right() { return Vector2(1, 0); } 288 | Vector2 Vector2::Left() { return Vector2(-1, 0); } 289 | Vector2 Vector2::Up() { return Vector2(0, 1); } 290 | Vector2 Vector2::Down() { return Vector2(0, -1); } 291 | 292 | 293 | float Vector2::Angle(Vector2 a, Vector2 b) 294 | { 295 | float v = Dot(a, b) / (Magnitude(a) * Magnitude(b)); 296 | v = fmax(v, -1.0); 297 | v = fmin(v, 1.0); 298 | return acos(v); 299 | } 300 | 301 | Vector2 Vector2::ClampMagnitude(Vector2 vector, float maxLength) 302 | { 303 | float length = Magnitude(vector); 304 | if (length > maxLength) 305 | vector *= maxLength / length; 306 | return vector; 307 | } 308 | 309 | float Vector2::Component(Vector2 a, Vector2 b) 310 | { 311 | return Dot(a, b) / Magnitude(b); 312 | } 313 | 314 | float Vector2::Distance(Vector2 a, Vector2 b) 315 | { 316 | return Vector2::Magnitude(a - b); 317 | } 318 | 319 | float Vector2::Dot(Vector2 lhs, Vector2 rhs) 320 | { 321 | return lhs.X * rhs.X + lhs.Y * rhs.Y; 322 | } 323 | 324 | Vector2 Vector2::FromPolar(float rad, float theta) 325 | { 326 | Vector2 v; 327 | v.X = rad * cos(theta); 328 | v.Y = rad * sin(theta); 329 | return v; 330 | } 331 | 332 | Vector2 Vector2::Lerp(Vector2 a, Vector2 b, float t) 333 | { 334 | if (t < 0) return a; 335 | else if (t > 1) return b; 336 | return LerpUnclamped(a, b, t); 337 | } 338 | 339 | Vector2 Vector2::LerpUnclamped(Vector2 a, Vector2 b, float t) 340 | { 341 | return (b - a) * t + a; 342 | } 343 | 344 | float Vector2::Magnitude(Vector2 v) 345 | { 346 | return sqrt(SqrMagnitude(v)); 347 | } 348 | 349 | Vector2 Vector2::Max(Vector2 a, Vector2 b) 350 | { 351 | float x = a.X > b.X ? a.X : b.X; 352 | float y = a.Y > b.Y ? a.Y : b.Y; 353 | return Vector2(x, y); 354 | } 355 | 356 | Vector2 Vector2::Min(Vector2 a, Vector2 b) 357 | { 358 | float x = a.X > b.X ? b.X : a.X; 359 | float y = a.Y > b.Y ? b.Y : a.Y; 360 | return Vector2(x, y); 361 | } 362 | 363 | Vector2 Vector2::MoveTowards(Vector2 current, Vector2 target, 364 | float maxDistanceDelta) 365 | { 366 | Vector2 d = target - current; 367 | float m = Magnitude(d); 368 | if (m < maxDistanceDelta || m == 0) 369 | return target; 370 | return current + (d * maxDistanceDelta / m); 371 | } 372 | 373 | Vector2 Vector2::Normalized(Vector2 v) 374 | { 375 | float mag = Magnitude(v); 376 | if (mag == 0) 377 | return Vector2::Zero(); 378 | return v / mag; 379 | } 380 | 381 | void Vector2::OrthoNormalize(Vector2 &normal, Vector2 &tangent) 382 | { 383 | normal = Normalized(normal); 384 | tangent = Reject(tangent, normal); 385 | tangent = Normalized(tangent); 386 | } 387 | 388 | Vector2 Vector2::Project(Vector2 a, Vector2 b) 389 | { 390 | float m = Magnitude(b); 391 | return Dot(a, b) / (m * m) * b; 392 | } 393 | 394 | Vector2 Vector2::Reflect(Vector2 vector, Vector2 planeNormal) 395 | { 396 | return vector - 2 * Project(vector, planeNormal); 397 | } 398 | 399 | Vector2 Vector2::Reject(Vector2 a, Vector2 b) 400 | { 401 | return a - Project(a, b); 402 | } 403 | 404 | Vector2 Vector2::RotateTowards(Vector2 current, Vector2 target, 405 | float maxRadiansDelta, 406 | float maxMagnitudeDelta) 407 | { 408 | float magCur = Magnitude(current); 409 | float magTar = Magnitude(target); 410 | float newMag = magCur + maxMagnitudeDelta * 411 | ((magTar > magCur) - (magCur > magTar)); 412 | newMag = fmin(newMag, fmax(magCur, magTar)); 413 | newMag = fmax(newMag, fmin(magCur, magTar)); 414 | 415 | float totalAngle = Angle(current, target) - maxRadiansDelta; 416 | if (totalAngle <= 0) 417 | return Normalized(target) * newMag; 418 | else if (totalAngle >= M_PI) 419 | return Normalized(-target) * newMag; 420 | 421 | float axis = current.X * target.Y - current.Y * target.X; 422 | axis = axis / fabs(axis); 423 | if (!(1 - fabs(axis) < 0.00001)) 424 | axis = 1; 425 | current = Normalized(current); 426 | Vector2 newVector = current * cos(maxRadiansDelta) + 427 | Vector2(-current.Y, current.X) * sin(maxRadiansDelta) * axis; 428 | return newVector * newMag; 429 | } 430 | 431 | Vector2 Vector2::Scale(Vector2 a, Vector2 b) 432 | { 433 | return Vector2(a.X * b.X, a.Y * b.Y); 434 | } 435 | 436 | Vector2 Vector2::Slerp(Vector2 a, Vector2 b, float t) 437 | { 438 | if (t < 0) return a; 439 | else if (t > 1) return b; 440 | return SlerpUnclamped(a, b, t); 441 | } 442 | 443 | Vector2 Vector2::SlerpUnclamped(Vector2 a, Vector2 b, float t) 444 | { 445 | float magA = Magnitude(a); 446 | float magB = Magnitude(b); 447 | a /= magA; 448 | b /= magB; 449 | float dot = Dot(a, b); 450 | dot = fmax(dot, -1.0); 451 | dot = fmin(dot, 1.0); 452 | float theta = acos(dot) * t; 453 | Vector2 relativeVec = Normalized(b - a * dot); 454 | Vector2 newVec = a * cos(theta) + relativeVec * sin(theta); 455 | return newVec * (magA + (magB - magA) * t); 456 | } 457 | 458 | float Vector2::SqrMagnitude(Vector2 v) 459 | { 460 | return v.X * v.X + v.Y * v.Y; 461 | } 462 | 463 | void Vector2::ToPolar(Vector2 vector, float &rad, float &theta) 464 | { 465 | rad = Magnitude(vector); 466 | theta = atan2(vector.Y, vector.X); 467 | } 468 | 469 | 470 | struct Vector2& Vector2::operator+=(const float rhs) 471 | { 472 | X += rhs; 473 | Y += rhs; 474 | return *this; 475 | } 476 | 477 | struct Vector2& Vector2::operator-=(const float rhs) 478 | { 479 | X -= rhs; 480 | Y -= rhs; 481 | return *this; 482 | } 483 | 484 | struct Vector2& Vector2::operator*=(const float rhs) 485 | { 486 | X *= rhs; 487 | Y *= rhs; 488 | return *this; 489 | } 490 | 491 | struct Vector2& Vector2::operator/=(const float rhs) 492 | { 493 | X /= rhs; 494 | Y /= rhs; 495 | return *this; 496 | } 497 | 498 | struct Vector2& Vector2::operator+=(const Vector2 rhs) 499 | { 500 | X += rhs.X; 501 | Y += rhs.Y; 502 | return *this; 503 | } 504 | 505 | struct Vector2& Vector2::operator-=(const Vector2 rhs) 506 | { 507 | X -= rhs.X; 508 | Y -= rhs.Y; 509 | return *this; 510 | } 511 | 512 | Vector2 operator-(Vector2 rhs) { return rhs * -1; } 513 | Vector2 operator+(Vector2 lhs, const float rhs) { return lhs += rhs; } 514 | Vector2 operator-(Vector2 lhs, const float rhs) { return lhs -= rhs; } 515 | Vector2 operator*(Vector2 lhs, const float rhs) { return lhs *= rhs; } 516 | Vector2 operator/(Vector2 lhs, const float rhs) { return lhs /= rhs; } 517 | Vector2 operator+(const float lhs, Vector2 rhs) { return rhs += lhs; } 518 | Vector2 operator-(const float lhs, Vector2 rhs) { return rhs -= lhs; } 519 | Vector2 operator*(const float lhs, Vector2 rhs) { return rhs *= lhs; } 520 | Vector2 operator/(const float lhs, Vector2 rhs) { return rhs /= lhs; } 521 | Vector2 operator+(Vector2 lhs, const Vector2 rhs) { return lhs += rhs; } 522 | Vector2 operator-(Vector2 lhs, const Vector2 rhs) { return lhs -= rhs; } 523 | 524 | bool operator==(const Vector2 lhs, const Vector2 rhs) 525 | { 526 | return lhs.X == rhs.X && lhs.Y == rhs.Y; 527 | } 528 | 529 | bool operator!=(const Vector2 lhs, const Vector2 rhs) 530 | { 531 | return !(lhs == rhs); 532 | } -------------------------------------------------------------------------------- /app/src/main/cpp/Engine/Vector3.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef VECTOR3_H 3 | #define VECTOR3_H 4 | #define _USE_MATH_DEFINES 5 | #include 6 | 7 | struct Vector3 8 | { 9 | union 10 | { 11 | struct 12 | { 13 | float X; 14 | float Y; 15 | float Z; 16 | }; 17 | float data[3]; 18 | }; 19 | 20 | 21 | /** 22 | * Constructors. 23 | */ 24 | inline Vector3(); 25 | inline Vector3(float data[]); 26 | inline Vector3(float value); 27 | inline Vector3(float x, float y); 28 | inline Vector3(float x, float y, float z); 29 | 30 | 31 | /** 32 | * Constants for common vectors. 33 | */ 34 | static inline Vector3 Zero(); 35 | static inline Vector3 One(); 36 | static inline Vector3 Right(); 37 | static inline Vector3 Left(); 38 | static inline Vector3 Up(); 39 | static inline Vector3 Down(); 40 | static inline Vector3 Forward(); 41 | static inline Vector3 Backward(); 42 | 43 | 44 | /** 45 | * Returns the angle between two vectors in radians. 46 | * @param a: The first vector. 47 | * @param b: The second vector. 48 | * @return: A scalar value. 49 | */ 50 | static inline float Angle(Vector3 a, Vector3 b); 51 | 52 | /** 53 | * Returns a vector with its magnitude clamped to maxLength. 54 | * @param vector: The target vector. 55 | * @param maxLength: The maximum length of the return vector. 56 | * @return: A new vector. 57 | */ 58 | static inline Vector3 ClampMagnitude(Vector3 vector, float maxLength); 59 | 60 | /** 61 | * Returns the component of a in the direction of b (scalar projection). 62 | * @param a: The target vector. 63 | * @param b: The vector being compared against. 64 | * @return: A scalar value. 65 | */ 66 | static inline float Component(Vector3 a, Vector3 b); 67 | 68 | /** 69 | * Returns the cross product of two vectors. 70 | * @param lhs: The left side of the multiplication. 71 | * @param rhs: The right side of the multiplication. 72 | * @return: A new vector. 73 | */ 74 | static inline Vector3 Cross(Vector3 lhs, Vector3 rhs); 75 | 76 | /** 77 | * Returns the distance between a and b. 78 | * @param a: The first point. 79 | * @param b: The second point. 80 | * @return: A scalar value. 81 | */ 82 | static inline float Distance(Vector3 a, Vector3 b); 83 | 84 | static inline char ToChar(Vector3 a); 85 | 86 | /** 87 | * Returns the dot product of two vectors. 88 | * @param lhs: The left side of the multiplication. 89 | * @param rhs: The right side of the multiplication. 90 | * @return: A scalar value. 91 | */ 92 | static inline float Dot(Vector3 lhs, Vector3 rhs); 93 | 94 | /** 95 | * Converts a spherical representation of a vector into cartesian 96 | * coordinates. 97 | * This uses the ISO convention (radius r, inclination theta, azimuth phi). 98 | * @param rad: The magnitude of the vector. 99 | * @param theta: The angle in the XY plane from the X axis. 100 | * @param phi: The angle from the positive Z axis to the vector. 101 | * @return: A new vector. 102 | */ 103 | static inline Vector3 FromSpherical(float rad, float theta, float phi); 104 | 105 | /** 106 | * Returns a vector linearly interpolated between a and b, moving along 107 | * a straight line. The vector is clamped to never go beyond the end points. 108 | * @param a: The starting point. 109 | * @param b: The ending point. 110 | * @param t: The interpolation value [0-1]. 111 | * @return: A new vector. 112 | */ 113 | static inline Vector3 Lerp(Vector3 a, Vector3 b, float t); 114 | 115 | /** 116 | * Returns a vector linearly interpolated between a and b, moving along 117 | * a straight line. 118 | * @param a: The starting point. 119 | * @param b: The ending point. 120 | * @param t: The interpolation value [0-1] (no actual bounds). 121 | * @return: A new vector. 122 | */ 123 | static inline Vector3 LerpUnclamped(Vector3 a, Vector3 b, float t); 124 | 125 | /** 126 | * Returns the magnitude of a vector. 127 | * @param v: The vector in question. 128 | * @return: A scalar value. 129 | */ 130 | static inline float Magnitude(Vector3 v); 131 | 132 | /** 133 | * Returns a vector made from the largest components of two other vectors. 134 | * @param a: The first vector. 135 | * @param b: The second vector. 136 | * @return: A new vector. 137 | */ 138 | static inline Vector3 Max(Vector3 a, Vector3 b); 139 | 140 | /** 141 | * Returns a vector made from the smallest components of two other vectors. 142 | * @param a: The first vector. 143 | * @param b: The second vector. 144 | * @return: A new vector. 145 | */ 146 | static inline Vector3 Min(Vector3 a, Vector3 b); 147 | 148 | /** 149 | * Returns a vector "maxDistanceDelta" units closer to the target. This 150 | * interpolation is in a straight line, and will not overshoot. 151 | * @param current: The current position. 152 | * @param target: The destination position. 153 | * @param maxDistanceDelta: The maximum distance to move. 154 | * @return: A new vector. 155 | */ 156 | static inline Vector3 MoveTowards(Vector3 current, Vector3 target, 157 | float maxDistanceDelta); 158 | 159 | /** 160 | * Returns a new vector with magnitude of one. 161 | * @param v: The vector in question. 162 | * @return: A new vector. 163 | */ 164 | static inline Vector3 Normalized(Vector3 v); 165 | 166 | /** 167 | * Returns an arbitrary vector orthogonal to the input. 168 | * This vector is not normalized. 169 | * @param v: The input vector. 170 | * @return: A new vector. 171 | */ 172 | static inline Vector3 Orthogonal(Vector3 v); 173 | 174 | /** 175 | * Creates a new coordinate system out of the three vectors. 176 | * Normalizes "normal", normalizes "tangent" and makes it orthogonal to 177 | * "normal" and normalizes "binormal" and makes it orthogonal to both 178 | * "normal" and "tangent". 179 | * @param normal: A reference to the first axis vector. 180 | * @param tangent: A reference to the second axis vector. 181 | * @param binormal: A reference to the third axis vector. 182 | */ 183 | static inline void OrthoNormalize(Vector3 &normal, Vector3 &tangent, 184 | Vector3 &binormal); 185 | 186 | /** 187 | * Returns the vector projection of a onto b. 188 | * @param a: The target vector. 189 | * @param b: The vector being projected onto. 190 | * @return: A new vector. 191 | */ 192 | static inline Vector3 Project(Vector3 a, Vector3 b); 193 | 194 | /** 195 | * Returns a vector projected onto a plane orthogonal to "planeNormal". 196 | * This can be visualized as the shadow of the vector onto the plane, if 197 | * the light source were in the direction of the plane normal. 198 | * @param vector: The vector to project. 199 | * @param planeNormal: The normal of the plane onto which to project. 200 | * @param: A new vector. 201 | */ 202 | static inline Vector3 ProjectOnPlane(Vector3 vector, Vector3 planeNormal); 203 | 204 | /** 205 | * Returns a vector reflected off the plane orthogonal to the normal. 206 | * The input vector is pointed inward, at the plane, and the return vector 207 | * is pointed outward from the plane, like a beam of light hitting and then 208 | * reflecting off a mirror. 209 | * @param vector: The vector traveling inward at the plane. 210 | * @param planeNormal: The normal of the plane off of which to reflect. 211 | * @return: A new vector pointing outward from the plane. 212 | */ 213 | static inline Vector3 Reflect(Vector3 vector, Vector3 planeNormal); 214 | 215 | /** 216 | * Returns the vector rejection of a on b. 217 | * @param a: The target vector. 218 | * @param b: The vector being projected onto. 219 | * @return: A new vector. 220 | */ 221 | static inline Vector3 Reject(Vector3 a, Vector3 b); 222 | 223 | /** 224 | * Rotates vector "current" towards vector "target" by "maxRadiansDelta". 225 | * This treats the vectors as directions and will linearly interpolate 226 | * between their magnitudes by "maxMagnitudeDelta". This function does not 227 | * overshoot. If a negative delta is supplied, it will rotate away from 228 | * "target" until it is pointing the opposite direction, but will not 229 | * overshoot that either. 230 | * @param current: The starting direction. 231 | * @param target: The destination direction. 232 | * @param maxRadiansDelta: The maximum number of radians to rotate. 233 | * @param maxMagnitudeDelta: The maximum delta for magnitude interpolation. 234 | * @return: A new vector. 235 | */ 236 | static inline Vector3 RotateTowards(Vector3 current, Vector3 target, 237 | float maxRadiansDelta, 238 | float maxMagnitudeDelta); 239 | 240 | /** 241 | * Multiplies two vectors element-wise. 242 | * @param a: The lhs of the multiplication. 243 | * @param b: The rhs of the multiplication. 244 | * @return: A new vector. 245 | */ 246 | static inline Vector3 Scale(Vector3 a, Vector3 b); 247 | 248 | /** 249 | * Returns a vector rotated towards b from a by the percent t. 250 | * Since interpolation is done spherically, the vector moves at a constant 251 | * angular velocity. This rotation is clamped to 0 <= t <= 1. 252 | * @param a: The starting direction. 253 | * @param b: The ending direction. 254 | * @param t: The interpolation value [0-1]. 255 | */ 256 | static inline Vector3 Slerp(Vector3 a, Vector3 b, float t); 257 | 258 | /** 259 | * Returns a vector rotated towards b from a by the percent t. 260 | * Since interpolation is done spherically, the vector moves at a constant 261 | * angular velocity. This rotation is unclamped. 262 | * @param a: The starting direction. 263 | * @param b: The ending direction. 264 | * @param t: The interpolation value [0-1]. 265 | */ 266 | static inline Vector3 SlerpUnclamped(Vector3 a, Vector3 b, float t); 267 | 268 | /** 269 | * Returns the squared magnitude of a vector. 270 | * This is useful when comparing relative lengths, where the exact length 271 | * is not important, and much time can be saved by not calculating the 272 | * square root. 273 | * @param v: The vector in question. 274 | * @return: A scalar value. 275 | */ 276 | static inline float SqrMagnitude(Vector3 v); 277 | 278 | /** 279 | * Calculates the spherical coordinate space representation of a vector. 280 | * This uses the ISO convention (radius r, inclination theta, azimuth phi). 281 | * @param vector: The vector to convert. 282 | * @param rad: The magnitude of the vector. 283 | * @param theta: The angle in the XY plane from the X axis. 284 | * @param phi: The angle from the positive Z axis to the vector. 285 | */ 286 | static inline void ToSpherical(Vector3 vector, float &rad, float &theta, 287 | float &phi); 288 | 289 | 290 | /** 291 | * Operator overloading. 292 | */ 293 | inline struct Vector3& operator+=(const float rhs); 294 | inline struct Vector3& operator-=(const float rhs); 295 | inline struct Vector3& operator*=(const float rhs); 296 | inline struct Vector3& operator/=(const float rhs); 297 | inline struct Vector3& operator+=(const Vector3 rhs); 298 | inline struct Vector3& operator-=(const Vector3 rhs); 299 | }; 300 | 301 | inline Vector3 operator-(Vector3 rhs); 302 | inline Vector3 operator+(Vector3 lhs, const float rhs); 303 | inline Vector3 operator-(Vector3 lhs, const float rhs); 304 | inline Vector3 operator*(Vector3 lhs, const float rhs); 305 | inline Vector3 operator/(Vector3 lhs, const float rhs); 306 | inline Vector3 operator+(const float lhs, Vector3 rhs); 307 | inline Vector3 operator-(const float lhs, Vector3 rhs); 308 | inline Vector3 operator*(const float lhs, Vector3 rhs); 309 | inline Vector3 operator/(const float lhs, Vector3 rhs); 310 | inline Vector3 operator+(Vector3 lhs, const Vector3 rhs); 311 | inline Vector3 operator-(Vector3 lhs, const Vector3 rhs); 312 | inline bool operator==(const Vector3 lhs, const Vector3 rhs); 313 | inline bool operator!=(const Vector3 lhs, const Vector3 rhs); 314 | 315 | 316 | 317 | /******************************************************************************* 318 | * Implementation 319 | */ 320 | 321 | Vector3::Vector3() : X(0), Y(0), Z(0) {} 322 | Vector3::Vector3(float data[]) : X(data[0]), Y(data[1]), Z(data[2]) {} 323 | Vector3::Vector3(float value) : X(value), Y(value), Z(value) {} 324 | Vector3::Vector3(float x, float y) : X(x), Y(y), Z(0) {} 325 | Vector3::Vector3(float x, float y, float z) : X(x), Y(y), Z(z) {} 326 | 327 | 328 | Vector3 Vector3::Zero() { return Vector3(0, 0, 0); } 329 | Vector3 Vector3::One() { return Vector3(1, 1, 1); } 330 | Vector3 Vector3::Right() { return Vector3(1, 0, 0); } 331 | Vector3 Vector3::Left() { return Vector3(-1, 0, 0); } 332 | Vector3 Vector3::Up() { return Vector3(0, 1, 0); } 333 | Vector3 Vector3::Down() { return Vector3(0, -1, 0); } 334 | Vector3 Vector3::Forward() { return Vector3(0, 0, 1); } 335 | Vector3 Vector3::Backward() { return Vector3(0, 0, -1); } 336 | 337 | 338 | float Vector3::Angle(Vector3 a, Vector3 b) 339 | { 340 | float v = Dot(a, b) / (Magnitude(a) * Magnitude(b)); 341 | v = fmax(v, -1.0); 342 | v = fmin(v, 1.0); 343 | return acos(v); 344 | } 345 | 346 | Vector3 Vector3::ClampMagnitude(Vector3 vector, float maxLength) 347 | { 348 | float length = Magnitude(vector); 349 | if (length > maxLength) 350 | vector *= maxLength / length; 351 | return vector; 352 | } 353 | 354 | float Vector3::Component(Vector3 a, Vector3 b) 355 | { 356 | return Dot(a, b) / Magnitude(b); 357 | } 358 | 359 | Vector3 Vector3::Cross(Vector3 lhs, Vector3 rhs) 360 | { 361 | float x = lhs.Y * rhs.Z - lhs.Z * rhs.Y; 362 | float y = lhs.Z * rhs.X - lhs.X * rhs.Z; 363 | float z = lhs.X * rhs.Y - lhs.Y * rhs.X; 364 | return Vector3(x, y, z); 365 | } 366 | 367 | float Vector3::Distance(Vector3 a, Vector3 b) 368 | { 369 | return Vector3::Magnitude(a - b); 370 | } 371 | 372 | float Vector3::Dot(Vector3 lhs, Vector3 rhs) 373 | { 374 | return lhs.X * rhs.X + lhs.Y * rhs.Y + lhs.Z * rhs.Z; 375 | } 376 | 377 | Vector3 Vector3::FromSpherical(float rad, float theta, float phi) 378 | { 379 | Vector3 v; 380 | v.X = rad * sin(theta) * cos(phi); 381 | v.Y = rad * sin(theta) * sin(phi); 382 | v.Z = rad * cos(theta); 383 | return v; 384 | } 385 | 386 | Vector3 Vector3::Lerp(Vector3 a, Vector3 b, float t) 387 | { 388 | if (t < 0) return a; 389 | else if (t > 1) return b; 390 | return LerpUnclamped(a, b, t); 391 | } 392 | 393 | Vector3 Vector3::LerpUnclamped(Vector3 a, Vector3 b, float t) 394 | { 395 | return (b - a) * t + a; 396 | } 397 | 398 | float Vector3::Magnitude(Vector3 v) 399 | { 400 | return sqrt(SqrMagnitude(v)); 401 | } 402 | 403 | Vector3 Vector3::Max(Vector3 a, Vector3 b) 404 | { 405 | float x = a.X > b.X ? a.X : b.X; 406 | float y = a.Y > b.Y ? a.Y : b.Y; 407 | float z = a.Z > b.Z ? a.Z : b.Z; 408 | return Vector3(x, y, z); 409 | } 410 | 411 | Vector3 Vector3::Min(Vector3 a, Vector3 b) 412 | { 413 | float x = a.X > b.X ? b.X : a.X; 414 | float y = a.Y > b.Y ? b.Y : a.Y; 415 | float z = a.Z > b.Z ? b.Z : a.Z; 416 | return Vector3(x, y, z); 417 | } 418 | 419 | Vector3 Vector3::MoveTowards(Vector3 current, Vector3 target, 420 | float maxDistanceDelta) 421 | { 422 | Vector3 d = target - current; 423 | float m = Magnitude(d); 424 | if (m < maxDistanceDelta || m == 0) 425 | return target; 426 | return current + (d * maxDistanceDelta / m); 427 | } 428 | 429 | Vector3 Vector3::Normalized(Vector3 v) 430 | { 431 | float mag = Magnitude(v); 432 | if (mag == 0) 433 | return Vector3::Zero(); 434 | return v / mag; 435 | } 436 | 437 | Vector3 Vector3::Orthogonal(Vector3 v) 438 | { 439 | return v.Z < v.X ? Vector3(v.Y, -v.X, 0) : Vector3(0, -v.Z, v.Y); 440 | } 441 | 442 | void Vector3::OrthoNormalize(Vector3 &normal, Vector3 &tangent, 443 | Vector3 &binormal) 444 | { 445 | normal = Normalized(normal); 446 | tangent = ProjectOnPlane(tangent, normal); 447 | tangent = Normalized(tangent); 448 | binormal = ProjectOnPlane(binormal, tangent); 449 | binormal = ProjectOnPlane(binormal, normal); 450 | binormal = Normalized(binormal); 451 | } 452 | 453 | Vector3 Vector3::Project(Vector3 a, Vector3 b) 454 | { 455 | float m = Magnitude(b); 456 | return Dot(a, b) / (m * m) * b; 457 | } 458 | 459 | Vector3 Vector3::ProjectOnPlane(Vector3 vector, Vector3 planeNormal) 460 | { 461 | return Reject(vector, planeNormal); 462 | } 463 | 464 | Vector3 Vector3::Reflect(Vector3 vector, Vector3 planeNormal) 465 | { 466 | return vector - 2 * Project(vector, planeNormal); 467 | } 468 | 469 | Vector3 Vector3::Reject(Vector3 a, Vector3 b) 470 | { 471 | return a - Project(a, b); 472 | } 473 | 474 | Vector3 Vector3::RotateTowards(Vector3 current, Vector3 target, 475 | float maxRadiansDelta, 476 | float maxMagnitudeDelta) 477 | { 478 | float magCur = Magnitude(current); 479 | float magTar = Magnitude(target); 480 | float newMag = magCur + maxMagnitudeDelta * 481 | ((magTar > magCur) - (magCur > magTar)); 482 | newMag = fmin(newMag, fmax(magCur, magTar)); 483 | newMag = fmax(newMag, fmin(magCur, magTar)); 484 | 485 | float totalAngle = Angle(current, target) - maxRadiansDelta; 486 | if (totalAngle <= 0) 487 | return Normalized(target) * newMag; 488 | else if (totalAngle >= M_PI) 489 | return Normalized(-target) * newMag; 490 | 491 | Vector3 axis = Cross(current, target); 492 | float magAxis = Magnitude(axis); 493 | if (magAxis == 0) 494 | axis = Normalized(Cross(current, current + Vector3(3.95, 5.32, -4.24))); 495 | else 496 | axis /= magAxis; 497 | current = Normalized(current); 498 | Vector3 newVector = current * cos(maxRadiansDelta) + 499 | Cross(axis, current) * sin(maxRadiansDelta); 500 | return newVector * newMag; 501 | } 502 | 503 | Vector3 Vector3::Scale(Vector3 a, Vector3 b) 504 | { 505 | return Vector3(a.X * b.X, a.Y * b.Y, a.Z * b.Z); 506 | } 507 | 508 | Vector3 Vector3::Slerp(Vector3 a, Vector3 b, float t) 509 | { 510 | if (t < 0) return a; 511 | else if (t > 1) return b; 512 | return SlerpUnclamped(a, b, t); 513 | } 514 | 515 | Vector3 Vector3::SlerpUnclamped(Vector3 a, Vector3 b, float t) 516 | { 517 | float magA = Magnitude(a); 518 | float magB = Magnitude(b); 519 | a /= magA; 520 | b /= magB; 521 | float dot = Dot(a, b); 522 | dot = fmax(dot, -1.0); 523 | dot = fmin(dot, 1.0); 524 | float theta = acos(dot) * t; 525 | Vector3 relativeVec = Normalized(b - a * dot); 526 | Vector3 newVec = a * cos(theta) + relativeVec * sin(theta); 527 | return newVec * (magA + (magB - magA) * t); 528 | } 529 | 530 | float Vector3::SqrMagnitude(Vector3 v) 531 | { 532 | return v.X * v.X + v.Y * v.Y + v.Z * v.Z; 533 | } 534 | 535 | void Vector3::ToSpherical(Vector3 vector, float &rad, float &theta, 536 | float &phi) 537 | { 538 | rad = Magnitude(vector); 539 | float v = vector.Z / rad; 540 | v = fmax(v, -1.0); 541 | v = fmin(v, 1.0); 542 | theta = acos(v); 543 | phi = atan2(vector.Y, vector.X); 544 | } 545 | 546 | 547 | struct Vector3& Vector3::operator+=(const float rhs) 548 | { 549 | X += rhs; 550 | Y += rhs; 551 | Z += rhs; 552 | return *this; 553 | } 554 | 555 | struct Vector3& Vector3::operator-=(const float rhs) 556 | { 557 | X -= rhs; 558 | Y -= rhs; 559 | Z -= rhs; 560 | return *this; 561 | } 562 | 563 | struct Vector3& Vector3::operator*=(const float rhs) 564 | { 565 | X *= rhs; 566 | Y *= rhs; 567 | Z *= rhs; 568 | return *this; 569 | } 570 | 571 | struct Vector3& Vector3::operator/=(const float rhs) 572 | { 573 | X /= rhs; 574 | Y /= rhs; 575 | Z /= rhs; 576 | return *this; 577 | } 578 | 579 | struct Vector3& Vector3::operator+=(const Vector3 rhs) 580 | { 581 | X += rhs.X; 582 | Y += rhs.Y; 583 | Z += rhs.Z; 584 | return *this; 585 | } 586 | 587 | struct Vector3& Vector3::operator-=(const Vector3 rhs) 588 | { 589 | X -= rhs.X; 590 | Y -= rhs.Y; 591 | Z -= rhs.Z; 592 | return *this; 593 | } 594 | 595 | char Vector3::ToChar(Vector3 a) { 596 | const char* x = (const char*)(int)a.X; 597 | const char* y = (const char*)(int)a.Y; 598 | const char* z = (const char*)(int)a.Z; 599 | char buffer[25]; 600 | strncpy(buffer, x, sizeof(buffer)); 601 | strncpy(buffer, ", ", sizeof(buffer)); 602 | strncpy(buffer, y, sizeof(buffer)); 603 | strncpy(buffer, ", ", sizeof(buffer)); 604 | strncpy(buffer, z, sizeof(buffer)); 605 | strncpy(buffer, ", ", sizeof(buffer)); 606 | return buffer[25]; 607 | } 608 | 609 | Vector3 operator-(Vector3 rhs) { return rhs * -1; } 610 | Vector3 operator+(Vector3 lhs, const float rhs) { return lhs += rhs; } 611 | Vector3 operator-(Vector3 lhs, const float rhs) { return lhs -= rhs; } 612 | Vector3 operator*(Vector3 lhs, const float rhs) { return lhs *= rhs; } 613 | Vector3 operator/(Vector3 lhs, const float rhs) { return lhs /= rhs; } 614 | Vector3 operator+(const float lhs, Vector3 rhs) { return rhs += lhs; } 615 | Vector3 operator-(const float lhs, Vector3 rhs) { return rhs -= lhs; } 616 | Vector3 operator*(const float lhs, Vector3 rhs) { return rhs *= lhs; } 617 | Vector3 operator/(const float lhs, Vector3 rhs) { return rhs /= lhs; } 618 | Vector3 operator+(Vector3 lhs, const Vector3 rhs) { return lhs += rhs; } 619 | Vector3 operator-(Vector3 lhs, const Vector3 rhs) { return lhs -= rhs; } 620 | 621 | bool operator==(const Vector3 lhs, const Vector3 rhs) 622 | { 623 | return lhs.X == rhs.X && lhs.Y == rhs.Y && lhs.Z == rhs.Z; 624 | } 625 | 626 | bool operator!=(const Vector3 lhs, const Vector3 rhs) 627 | { 628 | return !(lhs == rhs); 629 | } 630 | #endif -------------------------------------------------------------------------------- /app/src/main/cpp/Etc/Logging.h: -------------------------------------------------------------------------------- 1 | #ifndef LOGGING_H 2 | #define LOGGING_H 3 | 4 | #include 5 | 6 | enum LogType 7 | { 8 | DEBUG = 3, 9 | INFO = 4, 10 | WARN = 5, 11 | ERROR = 6 12 | }; 13 | 14 | #define TAG "Polar" 15 | 16 | #define LOGDEBUG(...) ((void)__android_log_print(DEBUG, TAG, __VA_ARGS__)) 17 | #define LOGINFO(...) ((void)__android_log_print(INFO, TAG, __VA_ARGS__)) 18 | #define LOGWARN(...) ((void)__android_log_print(WARN, TAG, __VA_ARGS__)) 19 | #define LOGERROR(...) ((void)__android_log_print(ERROR, TAG, __VA_ARGS__)) 20 | 21 | #endif -------------------------------------------------------------------------------- /app/src/main/cpp/Etc/Obfuscate.h: -------------------------------------------------------------------------------- 1 | /* --------------------------------- ABOUT ------------------------------------- 2 | Original Author: Adam Yaxley 3 | Website: https://github.com/adamyaxley 4 | License: See end of file 5 | Obfuscate 6 | Guaranteed compile-time string literal obfuscation library for C++14 7 | Usage: 8 | Pass string literals into the AY_OBFUSCATE macro to obfuscate them at compile 9 | time. AY_OBFUSCATE returns a reference to an ay::obfuscated_data object with the 10 | following traits: 11 | - Guaranteed obfuscation of string 12 | The passed string is encrypted with a simple XOR cipher at compile-time to 13 | prevent it being viewable in the binary image 14 | - Global lifetime 15 | The actual instantiation of the ay::obfuscated_data takes place inside a 16 | lambda as a function level static 17 | - Implicitly convertable to a char* 18 | This means that you can pass it directly into functions that would normally 19 | take a char* or a const char* 20 | Example: 21 | const char* obfuscated_string = OBFUSCATE("Hello World"); 22 | std::cout << obfuscated_string << std::endl; 23 | ----------------------------------------------------------------------------- */ 24 | 25 | #ifndef OBFUSCATE_DEFAULT_KEY 26 | // The default 64 bit key to obfuscate strings with. 27 | // This can be user specified by defining AY_OBFUSCATE_DEFAULT_KEY before 28 | // including obfuscate.h 29 | #define OBFUSCATE_DEFAULT_KEY ay::generate_key(__LINE__) 30 | #endif 31 | 32 | namespace ay 33 | { 34 | using size_type = unsigned long long; 35 | using key_type = unsigned long long; 36 | 37 | // Generate a pseudo-random key that spans all 8 bytes 38 | 39 | constexpr key_type generate_key(key_type seed) 40 | { 41 | // Use the MurmurHash3 64-bit finalizer to hash our seed 42 | key_type key = seed; 43 | key ^= (key >> 33); 44 | key *= 0xff51afd7ed558ccd; 45 | key ^= (key >> 33); 46 | key *= 0xc4ceb9fe1a85ec53; 47 | key ^= (key >> 33); 48 | 49 | // Make sure that a bit in each byte is set 50 | key |= 0x0101010101010101ull; 51 | 52 | return key; 53 | } 54 | 55 | 56 | // Obfuscates or deobfuscates data with key 57 | constexpr void cipher(char* data, size_type size, key_type key) 58 | { 59 | // Obfuscate with a simple XOR cipher based on key 60 | for (size_type i = 0; i < size; i++) 61 | { 62 | data[i] ^= char(key >> ((i % 8) * 8)); 63 | } 64 | } 65 | 66 | // Obfuscates a string at compile time 67 | template 68 | 69 | class obfuscator 70 | { 71 | public: 72 | // Obfuscates the string 'data' on construction 73 | constexpr obfuscator(const char* data) 74 | { 75 | // Copy data 76 | for (size_type i = 0; i < N; i++) 77 | { 78 | m_data[i] = data[i]; 79 | } 80 | 81 | // On construction each of the characters in the string is 82 | // obfuscated with an XOR cipher based on key 83 | cipher(m_data, N, KEY); 84 | } 85 | 86 | constexpr const char* data() const 87 | { 88 | return &m_data[0]; 89 | } 90 | 91 | constexpr size_type size() const 92 | { 93 | return N; 94 | } 95 | 96 | constexpr key_type key() const 97 | { 98 | return KEY; 99 | } 100 | 101 | private: 102 | char m_data[N]{}; 103 | }; 104 | 105 | // Handles decryption and re-encryption of an encrypted string at runtime 106 | template 107 | 108 | class obfuscated_data 109 | { 110 | public: 111 | 112 | obfuscated_data(const obfuscator& obfuscator) 113 | { 114 | // Copy obfuscated data 115 | for (size_type i = 0; i < N; i++) 116 | { 117 | m_data[i] = obfuscator.data()[i]; 118 | } 119 | } 120 | 121 | ~obfuscated_data() 122 | { 123 | // Zero m_data to remove it from memory 124 | for (size_type i = 0; i < N; i++) 125 | { 126 | m_data[i] = 0; 127 | } 128 | } 129 | 130 | // Returns a pointer to the plain text string, decrypting it if 131 | // necessary 132 | operator char*() 133 | { 134 | decrypt(); 135 | return m_data; 136 | } 137 | 138 | // Manually decrypt the string 139 | void decrypt() 140 | { 141 | if (m_encrypted) 142 | { 143 | cipher(m_data, N, KEY); 144 | m_encrypted = false; 145 | } 146 | } 147 | 148 | // Manually re-encrypt the string 149 | void encrypt() 150 | { 151 | if (!m_encrypted) 152 | { 153 | cipher(m_data, N, KEY); 154 | m_encrypted = true; 155 | } 156 | } 157 | 158 | // Returns true if this string is currently encrypted, false otherwise. 159 | bool is_encrypted() const 160 | { 161 | return m_encrypted; 162 | } 163 | 164 | private: 165 | 166 | // Local storage for the string. Call is_encrypted() to check whether or 167 | // not the string is currently obfuscated. 168 | char m_data[N]; 169 | 170 | // Whether data is currently encrypted 171 | bool m_encrypted{ true }; 172 | }; 173 | 174 | // This function exists purely to extract the number of elements 'N' in the 175 | // array 'data' 176 | template 177 | constexpr auto make_obfuscator(const char(&data)[N]) 178 | { 179 | return obfuscator(data); 180 | } 181 | } 182 | 183 | // Obfuscates the string 'data' at compile-time and returns a reference to a 184 | // ay::obfuscated_data object with global lifetime that has functions for 185 | // decrypting the string and is also implicitly convertable to a char* 186 | #define OBFUSCATE(data) OBFUSCATE_KEY(data, OBFUSCATE_DEFAULT_KEY) 187 | 188 | // Obfuscates the string 'data' with 'key' at compile-time and returns a 189 | // reference to a ay::obfuscated_data object with global lifetime that has 190 | // functions for decrypting the string and is also implicitly convertable to a 191 | // char* 192 | #define OBFUSCATE_KEY(data, key) \ 193 | []() -> ay::obfuscated_data& { \ 194 | static_assert(sizeof(decltype(key)) == sizeof(ay::key_type), "key must be a 64 bit unsigned integer"); \ 195 | static_assert((key) >= (1ull << 56), "key must span all 8 bytes"); \ 196 | constexpr auto n = sizeof(data)/sizeof(data[0]); \ 197 | constexpr auto obfuscator = ay::make_obfuscator(data); \ 198 | static auto obfuscated_data = ay::obfuscated_data(obfuscator); \ 199 | return obfuscated_data; \ 200 | }() 201 | 202 | /* -------------------------------- LICENSE ------------------------------------ 203 | Public Domain (http://www.unlicense.org) 204 | This is free and unencumbered software released into the public domain. 205 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 206 | software, either in source code form or as a compiled binary, for any purpose, 207 | commercial or non-commercial, and by any means. 208 | In jurisdictions that recognize copyright laws, the author or authors of this 209 | software dedicate any and all copyright interest in the software to the public 210 | domain. We make this dedication for the benefit of the public at large and to 211 | the detriment of our heirs and successors. We intend this dedication to be an 212 | overt act of relinquishment in perpetuity of all present and future rights to 213 | this software under copyright law. 214 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 215 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 216 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE 217 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 218 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 219 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 220 | ----------------------------------------------------------------------------- */ -------------------------------------------------------------------------------- /app/src/main/cpp/Etc/Utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILS_H 2 | #define UTILS_H 3 | 4 | #define LibraryToLoad OBFUSCATE("libil2cpp.so") 5 | 6 | #include "../Hooking/ARMPatch/ARMPatch.h" 7 | #include "../Memory/KittyMemory/KittyMemory.h" 8 | #include "../Memory/KittyMemory/MemoryPatch.h" 9 | #include "../Memory/KittyMemory/KittyScanner.h" 10 | 11 | using namespace std; 12 | 13 | typedef unsigned long DWORD; 14 | static DWORD libBase; 15 | 16 | DWORD findLibrary(const char *library); 17 | DWORD getAbsoluteAddress(const char* libraryName, DWORD relativeAddr); 18 | bool isLibraryLoaded(const char *libraryName); 19 | 20 | DWORD findLibrary(const char *library) 21 | { 22 | char filename[0xFF] = {0}, 23 | buffer[1024] = {0}; 24 | FILE *fp = NULL; 25 | DWORD address = 0; 26 | const char *selfmaps = OBFUSCATE("/proc/self/maps"); 27 | sprintf(filename, OBFUSCATE("%s"), selfmaps); 28 | fp = fopen(filename, OBFUSCATE("rt")); 29 | if (fp == NULL) 30 | { 31 | perror(OBFUSCATE("fopen")); 32 | goto done; 33 | } 34 | 35 | while (fgets(buffer, sizeof(buffer), fp)) 36 | { 37 | if (strstr(buffer, library)) 38 | { 39 | address = (DWORD) strtoul(buffer, NULL, 16); 40 | goto done; 41 | } 42 | } 43 | 44 | done: 45 | 46 | if (fp) 47 | { 48 | fclose(fp); 49 | } 50 | 51 | return address; 52 | } 53 | 54 | DWORD getAbsoluteAddress(const char* libraryName, DWORD relativeAddr) 55 | { 56 | if (libBase == 0) 57 | libBase = findLibrary(libraryName); 58 | if (libBase != 0) 59 | return (reinterpret_cast(libBase + relativeAddr)); 60 | else 61 | return 0; 62 | } 63 | 64 | bool isLibraryLoaded(const char *libraryName) 65 | { 66 | char line[512] = {0}; 67 | FILE *fp = fopen(OBFUSCATE("/proc/self/maps"), OBFUSCATE("rt")); 68 | if (fp != NULL) 69 | { 70 | while (fgets(line, sizeof(line), fp)) 71 | { 72 | if (strstr(line, libraryName)) 73 | return true; 74 | } 75 | fclose(fp); 76 | } 77 | return false; 78 | } 79 | 80 | #endif 81 | -------------------------------------------------------------------------------- /app/src/main/cpp/Hooking/ARMPatch/ARMPatch.cpp: -------------------------------------------------------------------------------- 1 | #include "ARMPatch.h" 2 | #include 3 | 4 | using namespace std; 5 | namespace ARMPatch 6 | { 7 | uintptr_t getLib(const char* soLib) 8 | { 9 | FILE *fp = NULL; 10 | uintptr_t address = 0; 11 | char buffer[2048]; 12 | 13 | fp = fopen( "/proc/self/maps", "rt" ); 14 | if (fp != NULL) 15 | { 16 | while (fgets(buffer, sizeof(buffer)-1, fp)) 17 | { 18 | if ( strstr( buffer, soLib ) ) 19 | { 20 | address = (uintptr_t)strtoul( buffer, NULL, 16 ); 21 | break; 22 | } 23 | } 24 | fclose(fp); 25 | } 26 | return address; 27 | } 28 | uintptr_t getLibLength(const char* soLib) 29 | { 30 | FILE *fp = NULL; 31 | uintptr_t address = 0, end_address = 0; 32 | char buffer[2048]; 33 | 34 | fp = fopen( "/proc/self/maps", "rt" ); 35 | if (fp != NULL) 36 | { 37 | while (fgets(buffer, sizeof(buffer)-1, fp)) 38 | { 39 | if ( strstr( buffer, soLib ) ) 40 | { 41 | const char* secondPart = strchr(buffer, '-'); 42 | if(!address) end_address = address = (uintptr_t)strtoul( buffer, NULL, 16 ); 43 | if(secondPart != NULL) end_address = (uintptr_t)strtoul( secondPart + 1, NULL, 16 ); 44 | } 45 | } 46 | fclose(fp); 47 | } 48 | return end_address - address; 49 | } 50 | uintptr_t getSym(void* handle, const char* sym) 51 | { 52 | return (uintptr_t)dlsym(handle, sym); 53 | } 54 | uintptr_t getSym(uintptr_t libAddr, const char* sym) 55 | { 56 | Dl_info info; 57 | if(dladdr((void*)libAddr, &info) == 0) return 0; 58 | return (uintptr_t)dlsym(info.dli_fbase, sym); 59 | } 60 | int unprotect(uintptr_t addr, size_t len) 61 | { 62 | return mprotect((void*)(addr & 0xFFFFF000), len, PROT_READ | PROT_WRITE | PROT_EXEC); 63 | } 64 | void write(uintptr_t dest, uintptr_t src, size_t size) 65 | { 66 | unprotect(dest); 67 | memcpy((void*)dest, (void*)src, size); 68 | cacheflush(CLEAR_BIT0(dest), CLEAR_BIT0(dest) + size, 0); 69 | } 70 | void read(uintptr_t src, uintptr_t dest, size_t size) 71 | { 72 | unprotect(src); 73 | memcpy((void*)src, (void*)dest, size); 74 | } 75 | void NOP(uintptr_t addr, size_t count) 76 | { 77 | unprotect(addr); 78 | for (uintptr_t p = addr; p != (addr + count*2); p += 2) 79 | { 80 | (*(char*)(p + 0)) = 0x00; 81 | (*(char*)(p + 1)) = 0xBF; 82 | } 83 | cacheflush(CLEAR_BIT0(addr), CLEAR_BIT0(addr) + count * 2, 0); 84 | } 85 | void B(uintptr_t addr, uintptr_t dest) // B instruction 86 | { 87 | uint32_t newDest = ((dest - addr - 4) >> 12) & 0x7FF | 0xF000 | 88 | ((((dest - addr - 4) >> 1) & 0x7FF | 0xB800) << 16); 89 | write(addr, (uintptr_t)&newDest, sizeof(uintptr_t)); 90 | } 91 | void BL(uintptr_t addr, uintptr_t dest) // BL instruction 92 | { 93 | uint32_t newDest = ((dest - addr - 4) >> 12) & 0x7FF | 0xF000 | 94 | ((((dest - addr - 4) >> 1) & 0x7FF | 0xF800) << 16); 95 | write(addr, (uintptr_t)&newDest, sizeof(uintptr_t)); 96 | } 97 | void BLX(uintptr_t addr, uintptr_t dest) // BLX instruction 98 | { 99 | uint32_t newDest = ((dest - addr - 4) >> 12) & 0x7FF | 0xF000 | 100 | ((((dest - addr - 4) >> 1) & 0x7FF | 0xE800) << 16); 101 | write(addr, (uintptr_t)&newDest, sizeof(uintptr_t)); 102 | } 103 | void RET(uintptr_t addr) 104 | { 105 | write(addr, (uintptr_t)"\xF7\x46", 2); 106 | } 107 | void redirect(uintptr_t addr, uintptr_t to) // Was taken from TheOfficialFloW's git repo, should not work on ARM64 rn 108 | { 109 | #ifdef __arm__ 110 | if(!addr) return; 111 | uint32_t hook[2] = {0xE51FF004, to}; 112 | if(addr & 1) 113 | { 114 | addr &= ~1; 115 | if (addr & 2) 116 | { 117 | NOP(addr, 1); 118 | addr += 2; 119 | } 120 | hook[0] = 0xF000F8DF; 121 | } 122 | write(addr, (uintptr_t)hook, sizeof(hook)); 123 | #elif defined __aarch64__ 124 | // TODO: 125 | #endif 126 | } 127 | 128 | bool hookInternal(void* addr, void* func, void** original) 129 | { 130 | if (addr == NULL || func == NULL || addr == func) return false; 131 | unprotect((uintptr_t)addr); 132 | #ifdef __arm__ 133 | return MSHookFunction(addr, func, original); 134 | #elif defined __aarch64__ 135 | return A64HookFunction(addr, func, original); 136 | #endif 137 | } 138 | 139 | void hookPLTInternal(void* addr, void* func, void** original) 140 | { 141 | if (addr == NULL || func == NULL || addr == func) return; 142 | unprotect((uintptr_t)addr); 143 | if(original != NULL) *((uintptr_t*)original) = *(uintptr_t*)addr; 144 | *(uintptr_t*)addr = (uintptr_t)func; 145 | } 146 | 147 | bool compareData(const uint8_t* data, const bytePattern::byteEntry* pattern, size_t patternlength) 148 | { 149 | int index = 0; 150 | for (size_t i = 0; i < patternlength; i++) 151 | { 152 | auto byte = *pattern; 153 | if (!byte.bUnknown && *data != byte.nValue) return false; 154 | 155 | ++data; 156 | ++pattern; 157 | ++index; 158 | } 159 | return index == patternlength; 160 | } 161 | 162 | uintptr_t getAddressFromPattern(const char* pattern, const char* soLib) 163 | { 164 | bytePattern ret; 165 | const char* input = &pattern[0]; 166 | while (*input) 167 | { 168 | bytePattern::byteEntry entry; 169 | if (isspace(*input)) ++input; 170 | if (isxdigit(*input)) 171 | { 172 | entry.bUnknown = false; 173 | entry.nValue = (uint8_t)std::strtol(input, NULL, 16); 174 | input += 2; 175 | } 176 | else 177 | { 178 | entry.bUnknown = true; 179 | input += 2; 180 | } 181 | ret.vBytes.emplace_back(entry); 182 | } 183 | 184 | auto patternstart = ret.vBytes.data(); 185 | auto length = ret.vBytes.size(); 186 | 187 | uintptr_t pMemoryBase = getLib(soLib); 188 | size_t nMemorySize = getLibLength(soLib) - length; 189 | 190 | for (size_t i = 0; i < nMemorySize; i++) 191 | { 192 | uintptr_t addr = pMemoryBase + i; 193 | if (compareData((const uint8_t*)addr, patternstart, length)) return addr; 194 | } 195 | return (uintptr_t)0; 196 | } 197 | 198 | uintptr_t getAddressFromPattern(const char* pattern, uintptr_t libStart, uintptr_t scanLen) 199 | { 200 | bytePattern ret; 201 | const char* input = &pattern[0]; 202 | while (*input) 203 | { 204 | bytePattern::byteEntry entry; 205 | if (isspace(*input)) ++input; 206 | if (isxdigit(*input)) 207 | { 208 | entry.bUnknown = false; 209 | entry.nValue = (uint8_t)std::strtol(input, NULL, 16); 210 | input += 2; 211 | } 212 | else 213 | { 214 | entry.bUnknown = true; 215 | input += 2; 216 | } 217 | ret.vBytes.emplace_back(entry); 218 | } 219 | 220 | auto patternstart = ret.vBytes.data(); 221 | auto length = ret.vBytes.size(); 222 | 223 | uintptr_t scanSize = libStart + scanLen; 224 | for (size_t i = 0; i < scanSize; i++) 225 | { 226 | uintptr_t addr = libStart + i; 227 | if (compareData((const uint8_t*)addr, patternstart, length)) return addr; 228 | } 229 | return (uintptr_t)0; 230 | } 231 | } -------------------------------------------------------------------------------- /app/src/main/cpp/Hooking/ARMPatch/ARMPatch.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #ifdef __arm__ 7 | extern "C" bool MSHookFunction(void* symbol, void* replace, void** result); 8 | #define CLEAR_BIT0(addr) (addr & 0xFFFFFFFE) 9 | #elif defined __aarch64__ 10 | extern "C" bool A64HookFunction(void *const symbol, void *const replace, void **result); 11 | #define cacheflush(c, n, zeroarg) __builtin___clear_cache((char*)(c), (char*)(n)) 12 | #define CLEAR_BIT0(addr) (addr & 0xFFFFFFFFFFFFFFFE) 13 | #else 14 | #error This lib is supposed to work on ARM only! 15 | #endif 16 | 17 | /* Just a hook declaration */ 18 | #define DECL_HOOK(_ret, _name, ...) \ 19 | _ret (*_name)(__VA_ARGS__); \ 20 | _ret HookOf_##_name(__VA_ARGS__) 21 | /* Just a hook declaration with return type = void */ 22 | #define DECL_HOOKv(_name, ...) \ 23 | void (*_name)(__VA_ARGS__); \ 24 | void HookOf_##_name(__VA_ARGS__) 25 | /* Just a hook of a function */ 26 | #define HOOK(_name, _fnAddr) \ 27 | ARMPatch::hook((void*)(_fnAddr), (void*)(&HookOf_##_name), (void**)(&_name)); 28 | /* Just a hook of a function located in PLT section (by address!) */ 29 | #define HOOKPLT(_name, _fnAddr) \ 30 | ARMPatch::hookPLT((void*)(_fnAddr), (void*)(&HookOf_##_name), (void**)(&_name)); 31 | /* Clear a bit of an address! */ 32 | 33 | struct bytePattern 34 | { 35 | struct byteEntry 36 | { 37 | uint8_t nValue; 38 | bool bUnknown; 39 | }; 40 | std::vector vBytes; 41 | }; 42 | 43 | namespace ARMPatch 44 | { 45 | /* 46 | Get library's start address 47 | soLib - name of a loaded library 48 | */ 49 | uintptr_t getLib(const char* soLib); 50 | /* 51 | Get library's end address 52 | soLib - name of a loaded library 53 | */ 54 | uintptr_t getLibLength(const char* soLib); 55 | /* 56 | Get library's function address by symbol (__unwind???) 57 | handle - HANDLE (NOTICE THIS!!!) of a library (u can obtain it using dlopen) 58 | sym - name of a function 59 | */ 60 | uintptr_t getSym(void* handle, const char* sym); 61 | /* 62 | Get library's function address by symbol (__unwind???) 63 | libAddr - ADDRESS (NOTICE THIS!!!) of a library (u can obtain it using getLib) 64 | sym - name of a function 65 | @XMDS requested this 66 | */ 67 | uintptr_t getSym(uintptr_t libAddr, const char* sym); 68 | 69 | /* 70 | Reprotect memory to allow reading/writing/executing 71 | addr - address 72 | */ 73 | int unprotect(uintptr_t addr, size_t len = PAGE_SIZE); 74 | 75 | /* 76 | Write to memory (reprotects it) 77 | dest - where to start? 78 | src - address of an info to write 79 | size - size of an info 80 | */ 81 | void write(uintptr_t dest, uintptr_t src, size_t size); 82 | 83 | /* 84 | Read memory (reprotects it) 85 | src - where to read from? 86 | dest - where to write a readed info? 87 | size - size of an info 88 | */ 89 | void read(uintptr_t src, uintptr_t dest, size_t size); 90 | 91 | /* 92 | Place NotOPerator instruction (reprotects it) 93 | addr - where to put 94 | count - how much times to put 95 | */ 96 | void NOP(uintptr_t addr, size_t count = 1); 97 | 98 | /* 99 | Place JUMP instruction (reprotects it) 100 | addr - where to put 101 | dest - Jump to what? 102 | */ 103 | void B(uintptr_t addr, uintptr_t dest); 104 | 105 | /* 106 | Place BL instruction (reprotects it) 107 | addr - where to put 108 | dest - Jump to what? 109 | */ 110 | void BL(uintptr_t addr, uintptr_t dest); 111 | 112 | /* 113 | Place BLX instruction (reprotects it) 114 | addr - where to put 115 | dest - Jump to what? 116 | */ 117 | void BLX(uintptr_t addr, uintptr_t dest); 118 | 119 | /* 120 | Place RET instruction (RETURN, function end, reprotects it) 121 | addr - where to put 122 | */ 123 | void RET(uintptr_t addr); 124 | 125 | /* 126 | Place LDR instruction (moves directly to the function with the same stack!) 127 | Very fast and very lightweight! 128 | addr - where to redirect 129 | to - redirect to what? 130 | */ 131 | void redirect(uintptr_t addr, uintptr_t to); 132 | 133 | /* 134 | ByteScanner 135 | pattern - pattern. 136 | soLib - library's name 137 | */ 138 | uintptr_t getAddressFromPattern(const char* pattern, const char* soLib); 139 | 140 | /* 141 | ByteScanner 142 | pattern - pattern. 143 | libStart - library's start address 144 | scanLen - how much to scan from libStart 145 | */ 146 | uintptr_t getAddressFromPattern(const char* pattern, uintptr_t libStart, uintptr_t scanLen); 147 | 148 | /* 149 | Cydia's Substrate / Rprop's Inline Hook (use hook instead of hookInternal, ofc reprotects it!) 150 | addr - what to hook? 151 | func - Call that function instead of an original 152 | original - Original function! 153 | */ 154 | bool hookInternal(void* addr, void* func, void** original); 155 | template 156 | bool hook(A addr, B func, C original) 157 | { 158 | return hookInternal((void*)addr, (void*)func, (void**)original); 159 | } 160 | template 161 | bool hook(A addr, B func) 162 | { 163 | return hookInternal((void*)addr, (void*)func, (void**)NULL); 164 | } 165 | 166 | /* 167 | A simple hook of a PLT-section functions (use hookPLT instead of hookPLTInternal, ofc reprotects it!) 168 | addr - what to hook? 169 | func - Call that function instead of an original 170 | original - Original function! 171 | */ 172 | void hookPLTInternal(void* addr, void* func, void** original); 173 | template 174 | void hookPLT(A addr, B func, C original) 175 | { 176 | hookPLTInternal((void*)addr, (void*)func, (void**)original); 177 | } 178 | template 179 | void hookPLT(A addr, B func) 180 | { 181 | hookPLTInternal((void*)addr, (void*)func, (void**)NULL); 182 | } 183 | } -------------------------------------------------------------------------------- /app/src/main/cpp/Hooking/ARMPatch/libsubstrate-armv8a.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rev/Android-Native-Mod-Template/d6129cc6531b371acfa66b9095c7e0e1a1d230e0/app/src/main/cpp/Hooking/ARMPatch/libsubstrate-armv8a.a -------------------------------------------------------------------------------- /app/src/main/cpp/Hooking/ARMPatch/libsubstrate-cydia-armv7a.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rev/Android-Native-Mod-Template/d6129cc6531b371acfa66b9095c7e0e1a1d230e0/app/src/main/cpp/Hooking/ARMPatch/libsubstrate-cydia-armv7a.a -------------------------------------------------------------------------------- /app/src/main/cpp/Hooking/ARMPatch/libsubstrate-inline-armv7a.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rev/Android-Native-Mod-Template/d6129cc6531b371acfa66b9095c7e0e1a1d230e0/app/src/main/cpp/Hooking/ARMPatch/libsubstrate-inline-armv7a.a -------------------------------------------------------------------------------- /app/src/main/cpp/Memory/KittyMemory/KittyArm64.cpp: -------------------------------------------------------------------------------- 1 | #include "KittyArm64.h" 2 | 3 | // refs to 4 | // https://github.com/CAS-Atlantic/AArch64-Encoding 5 | // https://github.com/bminor/binutils-gdb 6 | // https://github.com/capstone-engine/capstone 7 | // https://github.com/qemu/QEMU 8 | // https://reverseengineering.stackexchange.com/questions/15418/getting-function-address-by-reading-adrp-and-add-instruction-values 9 | 10 | namespace KittyArm64 11 | { 12 | 13 | int32_t bit_from(uint32_t insn, int pos) 14 | { 15 | return ((1 << pos) & insn) >> pos; 16 | } 17 | 18 | int32_t bits_from(uint32_t insn, int pos, int l) 19 | { 20 | return (insn >> pos) & ((1 << l) - 1); 21 | } 22 | 23 | bool is_insn_adr(uint32_t insn) 24 | { 25 | return (insn & 0x9F000000) == 0x10000000; 26 | } 27 | 28 | bool is_insn_adrp(uint32_t insn) 29 | { 30 | return (insn & 0x9F000000) == 0x90000000; 31 | } 32 | 33 | // decode adr/adrp 34 | bool decode_adr_imm(uint32_t insn, int64_t *imm) 35 | { 36 | const int mask19 = (1 << 19) - 1; 37 | const int mask2 = 3; 38 | 39 | if (is_insn_adr(insn) || is_insn_adrp(insn)) 40 | { 41 | // 21-bit imm encoded in adrp. 42 | uint64_t imm_val = ((insn >> 29) & mask2) | (((insn >> 5) & mask19) << 2); 43 | // Retrieve msb of 21-bit-signed imm for sign extension. 44 | uint64_t msbt = (imm_val >> 20) & 1; 45 | 46 | if (!is_insn_adr(insn)) 47 | { 48 | // Real value is imm multiplied by 4k. Value now has 33-bit information. 49 | imm_val <<= 12; 50 | } 51 | 52 | // Sign extend to 64-bit by repeating msbt 31 (64-33) times and merge it 53 | // with value. 54 | *imm = ((((uint64_t)(1) << 32) - msbt) << 33) | imm_val; 55 | 56 | return true; 57 | } 58 | 59 | return false; 60 | } 61 | 62 | /* 63 | * 31 30 29 28 23 22 21 10 9 5 4 0 64 | * +--+--+--+-------------+--+-------------+-----+-----+ 65 | * |sf|op| S| 1 0 0 0 1 0 |sh| imm12 | Rn | Rd | 66 | * +--+--+--+-------------+--+-------------+-----+-----+ 67 | * 68 | * sf: 0 -> 32bit, 1 -> 64bit 69 | * op: 0 -> add , 1 -> sub 70 | * S: 1 -> set flags 71 | * sh: 1 -> LSL imm by 12 72 | */ 73 | 74 | int32_t decode_addsub_imm(uint32_t insn) 75 | { 76 | int32_t imm12 = bits_from(insn, 10, 12); 77 | 78 | bool shift = bit_from(insn, 22) == 1; 79 | 80 | if (shift) 81 | { 82 | imm12 <<= 12; 83 | } 84 | 85 | return imm12; 86 | } 87 | 88 | bool is_insn_ld(uint32_t insn) 89 | { 90 | // L bit 91 | return bit_from(insn, 22) == 1; 92 | } 93 | 94 | bool is_insn_ldst(uint32_t insn) 95 | { 96 | return (insn & 0x0a000000) == 0x08000000; 97 | } 98 | 99 | bool is_insn_ldst_uimm(uint32_t insn) 100 | { 101 | return (insn & 0x3b000000) == 0x39000000; 102 | } 103 | 104 | // decode Load/store unsigned immediate 105 | bool decode_ldrstr_uimm(uint32_t insn, int32_t *imm12) 106 | { 107 | if (is_insn_ldst_uimm(insn)) 108 | { 109 | *imm12 = bits_from(insn, 10, 12); 110 | if (*imm12) 111 | { 112 | *imm12 <<= 3; 113 | } 114 | 115 | return true; 116 | } 117 | 118 | return false; 119 | } 120 | 121 | } -------------------------------------------------------------------------------- /app/src/main/cpp/Memory/KittyMemory/KittyArm64.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace KittyArm64 8 | { 9 | 10 | int32_t bit_from(uint32_t insn, int pos); 11 | 12 | int32_t bits_from(uint32_t insn, int pos, int l); 13 | 14 | bool is_insn_adr(uint32_t insn); 15 | 16 | bool is_insn_adrp(uint32_t insn); 17 | 18 | bool decode_adr_imm(uint32_t insn, int64_t *imm); 19 | 20 | int32_t decode_addsub_imm(uint32_t insn); 21 | 22 | bool is_insn_ld(uint32_t insn); 23 | 24 | bool is_insn_ldst(uint32_t insn); 25 | 26 | bool is_insn_ldst_uimm(uint32_t insn); 27 | 28 | bool decode_ldrstr_uimm(uint32_t insn, int32_t *offset); 29 | 30 | } -------------------------------------------------------------------------------- /app/src/main/cpp/Memory/KittyMemory/KittyMemory.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // KittyMemory.cpp 3 | // 4 | // Created by MJ (Ruit) on 1/1/19. 5 | // 6 | 7 | #include "KittyMemory.h" 8 | 9 | namespace KittyMemory 10 | { 11 | 12 | struct mapsCache 13 | { 14 | std::string identifier; 15 | ProcMap map; 16 | }; 17 | 18 | static std::vector __mapsCache; 19 | static ProcMap findMapInCache(std::string id) 20 | { 21 | ProcMap ret; 22 | for (int i = 0; i < __mapsCache.size(); i++) 23 | { 24 | if (__mapsCache[i].identifier.compare(id) == 0) 25 | { 26 | ret = __mapsCache[i].map; 27 | break; 28 | } 29 | } 30 | return ret; 31 | } 32 | 33 | bool ProtectAddr(void *addr, size_t length, int protection) 34 | { 35 | uintptr_t pageStart = _PAGE_START_OF_(addr); 36 | uintptr_t pageLen = _PAGE_LEN_OF_(addr, length); 37 | return ( 38 | mprotect(reinterpret_cast(pageStart), pageLen, protection) != -1); 39 | } 40 | 41 | Memory_Status memWrite(void *addr, const void *buffer, size_t len) 42 | { 43 | if (addr == NULL) 44 | return INV_ADDR; 45 | 46 | if (buffer == NULL) 47 | return INV_BUF; 48 | 49 | if (len < 1 || len > INT_MAX) 50 | return INV_LEN; 51 | 52 | if (!ProtectAddr(addr, len, _PROT_RWX_)) 53 | return INV_PROT; 54 | 55 | if (memcpy(addr, buffer, len) != NULL && ProtectAddr(addr, len, _PROT_RX_)) 56 | return SUCCESS; 57 | 58 | return FAILED; 59 | } 60 | 61 | Memory_Status memRead(void *buffer, const void *addr, size_t len) 62 | { 63 | if (addr == NULL) 64 | return INV_ADDR; 65 | 66 | if (buffer == NULL) 67 | return INV_BUF; 68 | 69 | if (len < 1 || len > INT_MAX) 70 | return INV_LEN; 71 | 72 | if (memcpy(buffer, addr, len) != NULL) 73 | return SUCCESS; 74 | 75 | return FAILED; 76 | } 77 | 78 | std::string read2HexStr(const void *addr, size_t len) 79 | { 80 | char temp[len]; 81 | memset(temp, 0, len); 82 | 83 | const size_t bufferLen = len * 2 + 1; 84 | char buffer[bufferLen]; 85 | memset(buffer, 0, bufferLen); 86 | 87 | std::string ret; 88 | 89 | if (memRead(temp, addr, len) != SUCCESS) 90 | return ret; 91 | 92 | for (int i = 0; i < len; i++) 93 | { 94 | sprintf(&buffer[i * 2], "%02X", (unsigned char)temp[i]); 95 | } 96 | 97 | ret += buffer; 98 | return ret; 99 | } 100 | 101 | ProcMap getLibraryMap(const char *libraryName) 102 | { 103 | ProcMap retMap; 104 | char line[512] = {0}; 105 | 106 | FILE *fp = fopen("/proc/self/maps", "rt"); 107 | if (fp != NULL) 108 | { 109 | while (fgets(line, sizeof(line), fp)) 110 | { 111 | if (strstr(line, libraryName)) 112 | { 113 | char tmpPerms[5] = {0}, tmpDev[12] = {0}, tmpPathname[444] = {0}; 114 | // parse a line in maps file 115 | // (format) startAddress-endAddress perms offset dev inode pathname 116 | sscanf(line, "%llx-%llx %s %ld %s %d %s", 117 | (long long unsigned *)&retMap.startAddr, 118 | (long long unsigned *)&retMap.endAddr, 119 | tmpPerms, &retMap.offset, tmpDev, &retMap.inode, tmpPathname); 120 | 121 | retMap.length = (uintptr_t)retMap.endAddr - (uintptr_t)retMap.startAddr; 122 | retMap.perms = tmpPerms; 123 | retMap.dev = tmpDev; 124 | retMap.pathname = tmpPathname; 125 | 126 | break; 127 | } 128 | } 129 | fclose(fp); 130 | } 131 | return retMap; 132 | } 133 | 134 | uintptr_t getAbsoluteAddress(const char *libraryName, uintptr_t relativeAddr, bool useCache) 135 | { 136 | ProcMap libMap; 137 | 138 | if (useCache) 139 | { 140 | libMap = findMapInCache(libraryName); 141 | if (libMap.isValid()) 142 | return (reinterpret_cast(libMap.startAddr) + relativeAddr); 143 | } 144 | 145 | libMap = getLibraryMap(libraryName); 146 | if (!libMap.isValid()) 147 | return 0; 148 | 149 | if (useCache) 150 | { 151 | mapsCache cachedMap; 152 | cachedMap.identifier = libraryName; 153 | cachedMap.map = libMap; 154 | __mapsCache.push_back(cachedMap); 155 | } 156 | 157 | return (reinterpret_cast(libMap.startAddr) + relativeAddr); 158 | } 159 | } -------------------------------------------------------------------------------- /app/src/main/cpp/Memory/KittyMemory/KittyMemory.h: -------------------------------------------------------------------------------- 1 | // 2 | // KittyMemory.hpp 3 | // 4 | // Created by MJ (Ruit) on 1/1/19. 5 | // 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #define _SYS_PAGE_SIZE_ (sysconf(_SC_PAGE_SIZE)) 16 | 17 | #define _PAGE_START_OF_(x) ((uintptr_t)x & ~(uintptr_t)(_SYS_PAGE_SIZE_ - 1)) 18 | #define _PAGE_END_OF_(x, len) (_PAGE_START_OF_((uintptr_t)x + len - 1)) 19 | #define _PAGE_LEN_OF_(x, len) (_PAGE_END_OF_(x, len) - _PAGE_START_OF_(x) + _SYS_PAGE_SIZE_) 20 | #define _PAGE_OFFSET_OF_(x) ((uintptr_t)x - _PAGE_START_OF_(x)) 21 | 22 | #define _PROT_RWX_ (PROT_READ | PROT_WRITE | PROT_EXEC) 23 | #define _PROT_RX_ (PROT_READ | PROT_EXEC) 24 | 25 | #define EMPTY_VEC_OFFSET std::vector() 26 | 27 | namespace KittyMemory 28 | { 29 | 30 | typedef enum 31 | { 32 | FAILED = 0, 33 | SUCCESS = 1, 34 | INV_ADDR = 2, 35 | INV_LEN = 3, 36 | INV_BUF = 4, 37 | INV_PROT = 5 38 | } Memory_Status; 39 | 40 | struct ProcMap 41 | { 42 | void *startAddr; 43 | void *endAddr; 44 | size_t length; 45 | std::string perms; 46 | long offset; 47 | std::string dev; 48 | int inode; 49 | std::string pathname; 50 | 51 | bool isValid() { return (startAddr != NULL && endAddr != NULL && !pathname.empty()); } 52 | }; 53 | 54 | /* 55 | * Changes protection of an address with given length 56 | */ 57 | bool ProtectAddr(void *addr, size_t length, int protection); 58 | 59 | /* 60 | * Writes buffer content to an address 61 | */ 62 | Memory_Status memWrite(void *addr, const void *buffer, size_t len); 63 | 64 | /* 65 | * Reads an address content into a buffer 66 | */ 67 | Memory_Status memRead(void *buffer, const void *addr, size_t len); 68 | 69 | /* 70 | * Reads an address content and returns hex string 71 | */ 72 | std::string read2HexStr(const void *addr, size_t len); 73 | 74 | /* 75 | * Wrapper to dereference & get value of a multi level pointer 76 | * Make sure to use the correct data type! 77 | */ 78 | template 79 | Type readMultiPtr(void *ptr, std::vector offsets) 80 | { 81 | Type defaultVal = {}; 82 | if (ptr == NULL) 83 | return defaultVal; 84 | 85 | uintptr_t finalPtr = reinterpret_cast(ptr); 86 | int offsetsSize = offsets.size(); 87 | if (offsetsSize > 0) 88 | { 89 | for (int i = 0; finalPtr != 0 && i < offsetsSize; i++) 90 | { 91 | if (i == (offsetsSize - 1)) 92 | return *reinterpret_cast(finalPtr + offsets[i]); 93 | 94 | finalPtr = *reinterpret_cast(finalPtr + offsets[i]); 95 | } 96 | } 97 | 98 | if (finalPtr == 0) 99 | return defaultVal; 100 | 101 | return *reinterpret_cast(finalPtr); 102 | } 103 | 104 | /* 105 | * Wrapper to dereference & set value of a multi level pointer 106 | * Make sure to use the correct data type!, const objects won't work 107 | */ 108 | template 109 | bool writeMultiPtr(void *ptr, std::vector offsets, Type val) 110 | { 111 | if (ptr == NULL) 112 | return false; 113 | 114 | uintptr_t finalPtr = reinterpret_cast(ptr); 115 | int offsetsSize = offsets.size(); 116 | if (offsetsSize > 0) 117 | { 118 | for (int i = 0; finalPtr != 0 && i < offsetsSize; i++) 119 | { 120 | if (i == (offsetsSize - 1)) 121 | { 122 | *reinterpret_cast(finalPtr + offsets[i]) = val; 123 | return true; 124 | } 125 | 126 | finalPtr = *reinterpret_cast(finalPtr + offsets[i]); 127 | } 128 | } 129 | 130 | if (finalPtr == 0) 131 | return false; 132 | 133 | *reinterpret_cast(finalPtr) = val; 134 | return true; 135 | } 136 | 137 | /* 138 | * Wrapper to dereference & get value of a pointer 139 | * Make sure to use the correct data type! 140 | */ 141 | template 142 | Type readPtr(void *ptr) 143 | { 144 | Type defaultVal = {}; 145 | if (ptr == NULL) 146 | return defaultVal; 147 | 148 | return *reinterpret_cast(ptr); 149 | } 150 | 151 | /* 152 | * Wrapper to dereference & set value of a pointer 153 | * Make sure to use the correct data type!, const objects won't work 154 | */ 155 | template 156 | bool writePtr(void *ptr, Type val) 157 | { 158 | if (ptr == NULL) 159 | return false; 160 | 161 | *reinterpret_cast(ptr) = val; 162 | return true; 163 | } 164 | 165 | /* 166 | * Gets info of a mapped library in self process 167 | */ 168 | ProcMap getLibraryMap(const char *libraryName); 169 | 170 | /* 171 | * Expects a relative address in a library 172 | * Returns final absolute address 173 | */ 174 | uintptr_t getAbsoluteAddress(const char *libraryName, uintptr_t relativeAddr, bool useCache = false); 175 | }; 176 | -------------------------------------------------------------------------------- /app/src/main/cpp/Memory/KittyMemory/KittyScanner.cpp: -------------------------------------------------------------------------------- 1 | #include "KittyScanner.h" 2 | 3 | #include "KittyMemory.h" 4 | 5 | using KittyMemory::ProcMap; 6 | 7 | // refs 8 | // https://github.com/learn-more/findpattern-bench 9 | 10 | namespace KittyScanner 11 | { 12 | 13 | bool compare(const char *data, const char *pattern, const char *mask) 14 | { 15 | for (; *mask; ++mask, ++data, ++pattern) 16 | { 17 | if (*mask == 'x' && *data != *pattern) 18 | return false; 19 | } 20 | 21 | return !*mask; 22 | } 23 | 24 | uintptr_t find(const uintptr_t start, const size_t size, const char *pattern, const char *mask) 25 | { 26 | for (size_t i = 0; i < size; ++i) 27 | { 28 | if (!compare(reinterpret_cast(start + i), pattern, mask)) 29 | continue; 30 | 31 | return start + i; 32 | } 33 | return 0; 34 | } 35 | 36 | uintptr_t find_from_lib(const char *name, const char *pattern, const char *mask) 37 | { 38 | if (!name || !pattern || !mask) 39 | return 0; 40 | 41 | ProcMap libMap = KittyMemory::getLibraryMap(name); 42 | if(!libMap.isValid()) return 0; 43 | 44 | return find((uintptr_t)libMap.startAddr, (size_t)libMap.length, pattern, mask); 45 | } 46 | 47 | } -------------------------------------------------------------------------------- /app/src/main/cpp/Memory/KittyMemory/KittyScanner.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace KittyScanner 7 | { 8 | 9 | bool compare(const char *data, const char *pattern, const char *mask); 10 | 11 | uintptr_t find(const uintptr_t start, const size_t size, const char *pattern, const char *mask); 12 | 13 | uintptr_t find_from_lib(const char *name, const char *pattern, const char *mask); 14 | 15 | } -------------------------------------------------------------------------------- /app/src/main/cpp/Memory/KittyMemory/KittyUtils.cpp: -------------------------------------------------------------------------------- 1 | #include "KittyUtils.h" 2 | 3 | #include 4 | #include 5 | 6 | static void xtrim(std::string &hex) 7 | { 8 | if (hex.compare(0, 2, "0x") == 0) 9 | { 10 | hex.erase(0, 2); 11 | } 12 | 13 | // https://www.techiedelight.com/remove-whitespaces-string-cpp/ 14 | hex.erase(std::remove_if(hex.begin(), hex.end(), [](char c) 15 | { return (c == ' ' || c == '\n' || c == '\r' || 16 | c == '\t' || c == '\v' || c == '\f'); }), 17 | hex.end()); 18 | } 19 | 20 | namespace KittyUtils 21 | { 22 | 23 | bool validateHexString(std::string & xstr) 24 | { 25 | if (xstr.length() < 2) 26 | return false; 27 | xtrim(xstr); // first remove spaces 28 | if (xstr.length() % 2 != 0) 29 | return false; 30 | for (size_t i = 0; i < xstr.length(); i++) 31 | { 32 | if (!std::isxdigit((unsigned char)xstr[i])) 33 | { 34 | return false; 35 | } 36 | } 37 | return true; 38 | } 39 | 40 | // https://tweex.net/post/c-anything-tofrom-a-hex-string/ 41 | 42 | // ------------------------------------------------------------------ 43 | /*! 44 | Convert a block of data to a hex string 45 | */ 46 | void toHex( 47 | void *const data, //!< Data to convert 48 | const size_t dataLength, //!< Length of the data to convert 49 | std::string &dest //!< Destination string 50 | ) 51 | { 52 | unsigned char *byteData = reinterpret_cast(data); 53 | std::stringstream hexStringStream; 54 | 55 | hexStringStream << std::hex << std::setfill('0'); 56 | for (size_t index = 0; index < dataLength; ++index) 57 | hexStringStream << std::setw(2) << static_cast(byteData[index]); 58 | dest = hexStringStream.str(); 59 | } 60 | 61 | // ------------------------------------------------------------------ 62 | /*! 63 | Convert a hex string to a block of data 64 | */ 65 | void fromHex( 66 | const std::string &in, //!< Input hex string 67 | void *const data //!< Data store 68 | ) 69 | { 70 | size_t length = in.length(); 71 | unsigned char *byteData = reinterpret_cast(data); 72 | 73 | std::stringstream hexStringStream; 74 | hexStringStream >> std::hex; 75 | for (size_t strIndex = 0, dataIndex = 0; strIndex < length; ++dataIndex) 76 | { 77 | // Read out and convert the string two characters at a time 78 | const char tmpStr[3] = {in[strIndex++], in[strIndex++], 0}; 79 | 80 | // Reset and fill the string stream 81 | hexStringStream.clear(); 82 | hexStringStream.str(tmpStr); 83 | 84 | // Do the conversion 85 | int tmpValue = 0; 86 | hexStringStream >> tmpValue; 87 | byteData[dataIndex] = static_cast(tmpValue); 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /app/src/main/cpp/Memory/KittyMemory/KittyUtils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace KittyUtils { 7 | 8 | bool validateHexString(std::string &xstr); 9 | void toHex(void *const data, const size_t dataLength, std::string &dest); 10 | void fromHex(const std::string &in, void *const data); 11 | 12 | } -------------------------------------------------------------------------------- /app/src/main/cpp/Memory/KittyMemory/MemoryPatch.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // MemoryPatch.cpp 3 | // 4 | // Created by MJ (Ruit) on 1/1/19. 5 | // 6 | 7 | #include "MemoryPatch.h" 8 | 9 | using KittyMemory::Memory_Status; 10 | using KittyMemory::ProcMap; 11 | 12 | MemoryPatch::MemoryPatch() 13 | { 14 | _address = 0; 15 | _size = 0; 16 | _orig_code.clear(); 17 | _patch_code.clear(); 18 | } 19 | 20 | MemoryPatch::MemoryPatch(const char *libraryName, uintptr_t address, 21 | const void *patch_code, size_t patch_size, bool useMapCache) 22 | { 23 | MemoryPatch(); 24 | 25 | if (libraryName == NULL || address == 0 || patch_code == NULL || patch_size < 1) 26 | return; 27 | 28 | _address = KittyMemory::getAbsoluteAddress(libraryName, address, useMapCache); 29 | if (_address == 0) 30 | return; 31 | 32 | _size = patch_size; 33 | 34 | _orig_code.resize(patch_size); 35 | _patch_code.resize(patch_size); 36 | 37 | // initialize patch & backup current content 38 | KittyMemory::memRead(&_patch_code[0], patch_code, patch_size); 39 | KittyMemory::memRead(&_orig_code[0], reinterpret_cast(_address), patch_size); 40 | } 41 | 42 | MemoryPatch::MemoryPatch(uintptr_t absolute_address, 43 | const void *patch_code, size_t patch_size) 44 | { 45 | MemoryPatch(); 46 | 47 | if (absolute_address == 0 || patch_code == NULL || patch_size < 1) 48 | return; 49 | 50 | _address = absolute_address; 51 | _size = patch_size; 52 | 53 | _orig_code.resize(patch_size); 54 | _patch_code.resize(patch_size); 55 | 56 | // initialize patch & backup current content 57 | KittyMemory::memRead(&_patch_code[0], patch_code, patch_size); 58 | KittyMemory::memRead(&_orig_code[0], reinterpret_cast(_address), patch_size); 59 | } 60 | 61 | MemoryPatch::~MemoryPatch() 62 | { 63 | // clean up 64 | _orig_code.clear(); 65 | _patch_code.clear(); 66 | } 67 | 68 | MemoryPatch MemoryPatch::createWithHex(const char *libraryName, uintptr_t address, 69 | std::string hex, bool useMapCache) 70 | { 71 | MemoryPatch patch; 72 | 73 | if (libraryName == NULL || address == 0 || !KittyUtils::validateHexString(hex)) 74 | return patch; 75 | 76 | patch._address = KittyMemory::getAbsoluteAddress(libraryName, address, useMapCache); 77 | if (patch._address == 0) 78 | return patch; 79 | 80 | patch._size = hex.length() / 2; 81 | 82 | patch._orig_code.resize(patch._size); 83 | patch._patch_code.resize(patch._size); 84 | 85 | // initialize patch 86 | KittyUtils::fromHex(hex, &patch._patch_code[0]); 87 | 88 | // backup current content 89 | KittyMemory::memRead(&patch._orig_code[0], reinterpret_cast(patch._address), patch._size); 90 | return patch; 91 | } 92 | 93 | MemoryPatch MemoryPatch::createWithHex(uintptr_t absolute_address, std::string hex) 94 | { 95 | MemoryPatch patch; 96 | 97 | if (absolute_address == 0 || !KittyUtils::validateHexString(hex)) 98 | return patch; 99 | 100 | patch._address = absolute_address; 101 | patch._size = hex.length() / 2; 102 | 103 | patch._orig_code.resize(patch._size); 104 | patch._patch_code.resize(patch._size); 105 | 106 | // initialize patch 107 | KittyUtils::fromHex(hex, &patch._patch_code[0]); 108 | 109 | // backup current content 110 | KittyMemory::memRead(&patch._orig_code[0], reinterpret_cast(patch._address), patch._size); 111 | return patch; 112 | } 113 | 114 | bool MemoryPatch::isValid() const 115 | { 116 | return (_address != 0 && _size > 0 && _orig_code.size() == _size && _patch_code.size() == _size); 117 | } 118 | 119 | size_t MemoryPatch::get_PatchSize() const 120 | { 121 | return _size; 122 | } 123 | 124 | uintptr_t MemoryPatch::get_TargetAddress() const 125 | { 126 | return _address; 127 | } 128 | 129 | bool MemoryPatch::Restore() 130 | { 131 | if (!isValid()) 132 | return false; 133 | return KittyMemory::memWrite(reinterpret_cast(_address), &_orig_code[0], _size) == Memory_Status::SUCCESS; 134 | } 135 | 136 | bool MemoryPatch::Modify() 137 | { 138 | if (!isValid()) 139 | return false; 140 | return (KittyMemory::memWrite(reinterpret_cast(_address), &_patch_code[0], _size) == Memory_Status::SUCCESS); 141 | } 142 | 143 | std::string MemoryPatch::get_CurrBytes() 144 | { 145 | if (!isValid()) 146 | _hexString = std::string("0xInvalid"); 147 | else 148 | _hexString = KittyMemory::read2HexStr(reinterpret_cast(_address), _size); 149 | 150 | return _hexString; 151 | } 152 | -------------------------------------------------------------------------------- /app/src/main/cpp/Memory/KittyMemory/MemoryPatch.h: -------------------------------------------------------------------------------- 1 | // 2 | // MemoryPatch.h 3 | // 4 | // Created by MJ (Ruit) on 1/1/19. 5 | // 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | #include "KittyUtils.h" 12 | 13 | #include "KittyMemory.h" 14 | 15 | class MemoryPatch 16 | { 17 | private: 18 | uintptr_t _address; 19 | size_t _size; 20 | 21 | std::vector _orig_code; 22 | std::vector _patch_code; 23 | 24 | std::string _hexString; 25 | 26 | public: 27 | MemoryPatch(); 28 | 29 | /* 30 | * expects library name and relative address 31 | */ 32 | MemoryPatch(const char *libraryName, uintptr_t address, 33 | const void *patch_code, size_t patch_size, bool useMapCache = true); 34 | 35 | /* 36 | * expects absolute address 37 | */ 38 | MemoryPatch(uintptr_t absolute_address, 39 | const void *patch_code, size_t patch_size); 40 | 41 | ~MemoryPatch(); 42 | 43 | /* 44 | * compatible hex format (0xffff & ffff & ff ff) 45 | */ 46 | static MemoryPatch createWithHex(const char *libraryName, uintptr_t address, std::string hex, bool useMapCache = true); 47 | static MemoryPatch createWithHex(uintptr_t absolute_address, std::string hex); 48 | 49 | /* 50 | * Validate patch 51 | */ 52 | bool isValid() const; 53 | 54 | size_t get_PatchSize() const; 55 | 56 | /* 57 | * Returns pointer to the target address 58 | */ 59 | uintptr_t get_TargetAddress() const; 60 | 61 | /* 62 | * Restores patch to original value 63 | */ 64 | bool Restore(); 65 | 66 | /* 67 | * Applies patch modifications to target address 68 | */ 69 | bool Modify(); 70 | 71 | /* 72 | * Returns current patch target address bytes as hex string 73 | */ 74 | std::string get_CurrBytes(); 75 | }; 76 | -------------------------------------------------------------------------------- /app/src/main/cpp/Mod/Chams.h: -------------------------------------------------------------------------------- 1 | // 2 | // Chams.h 3 | // 4 | // Created by Rev on 4/06/2022. 5 | // 6 | 7 | #ifndef CHAMS_H 8 | #define CHAMS_H 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | const char *isUseShader = OBFUSCATE("_BumpMap"); 15 | const char *isUniformLocation = OBFUSCATE("glGetUniformLocation"); 16 | const char *isDrawElements = OBFUSCATE("glDrawElements"); 17 | const char *isDumpLocation = OBFUSCATE("/sdcard/Download/Shaders.txt"); 18 | const char *isSymbolError = dlerror(); 19 | 20 | auto isGLESHandle = dlopen(OBFUSCATE("libGLESv2.so"), RTLD_LAZY); 21 | auto isglGetUniformLocationAddress = dlsym(isGLESHandle, isUniformLocation); 22 | auto isglDrawElementsAddress = dlsym(isGLESHandle, isDrawElements); 23 | 24 | bool isDumpToSD = true; 25 | bool isLog = true; 26 | 27 | /* 28 | * dlopen allows us to get the handle of a specified shared object - which is libGLESv2.so in my case. 29 | * dlsym returns the address of a symbol, from the given handle. 30 | * dlerror returns a null terminated string of characters that delineates the last error when calling dlopen. For example, if it can't find 'glDrawElements' from the given handle 'libGLESv2.so', it will return an error. 31 | * Be sure to implement in the AndroidManifest.XML if you would like to dump the shaders. 32 | */ 33 | 34 | string isDump(const char *name) 35 | { 36 | vector isShaders = {name}; 37 | 38 | static string isTotalShaders; 39 | 40 | for (const auto &isAddTo: isShaders) 41 | isTotalShaders += (isAddTo + "\n"); // Adding whatever is added to the string vector to isTotalShaders, with a new line added per shader. 42 | 43 | return isTotalShaders.c_str(); 44 | } 45 | 46 | GLint (*old_glGetUniformLocation)(GLuint program, const GLchar *name); 47 | GLint glGetUniformLocation(GLuint program, const GLchar *name) // returns location of a shader/uniform. 48 | { 49 | if(isLog) 50 | LOGINFO("Shader: %s Program: %i", name, program); 51 | 52 | if(isDumpToSD) 53 | ofstream(isDumpLocation) << isDump(name); 54 | 55 | return old_glGetUniformLocation(program, name); 56 | } 57 | 58 | /* 59 | * What is a uniform, and what does glGetUniformLocation do? 60 | * A uniform is a global shader - meaning that it can be accessed from all scopes. glGetUniformLocation returns the location of a uniform based on the given program, and uniform name. 61 | * Read more: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glGetUniformLocation.xhtml 62 | */ 63 | 64 | bool isShader() // https://stackoverflow.com/a/62663705/17529905 65 | { 66 | GLint program; 67 | glGetIntegerv(GL_CURRENT_PROGRAM, &program); // returns the value of program -> https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glGet.xhtml 68 | 69 | return old_glGetUniformLocation(program, isUseShader) != -1; 70 | } 71 | 72 | /* 73 | * "This function returns -1 if "name" (isUseShader) does not correspond to an active uniform variable in program (program), if name starts with the reserved prefix "gl_", 74 | * or if name is associated with an atomic counter or a named uniform block." https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glGetUniformLocation.xhtml 75 | */ 76 | 77 | /* 78 | * Why != -1? 79 | * It isn't a requirement to have it. This is just here in case the shader isn't found within the given program, it won't return -1. 80 | * So, why is it important? Well, since it is a bool, anything that isn't 0 will evaluate to true. 81 | * In cases that it can't get the shader, and it returns -1, it will enable every shader at once in that specified program. 82 | * Think of it like this, if you're searching for a shader that doesn't exist in the program, and it returns true, it will "enable" every shader because it's not returning true for any specific shader - as it doesn't exist, and returning -1 evaluates to true. 83 | * If the shader does exist, it will return the actual id for it (> 0), and make the isShader check specific to it. 84 | * glGetUniformLocation's purpose is to return the location of a shader/uniform - where returning -1 indicates an issue. 85 | * I hope that makes sense! 86 | */ 87 | 88 | void (*old_glDrawElements)(GLenum mode, GLsizei count, GLenum type, const void *indices); 89 | void glDrawElements(GLenum mode, GLsizei count, GLenum type, const void *indices) 90 | { 91 | old_glDrawElements(mode, count, type, indices); 92 | 93 | if(isShader() > 0) 94 | { 95 | /* LOGDEBUG("Count: %i", count); By using this, you can get the count from the shader. Logging the count from '_BumpMap' gives you 10716, 1116, 8868, 5394. So, if you were to do 96 | * if(count == 10716 || count == 1116 || count == 8868 || count == 5394), you wouldn't need to use if(isShader() > 0). */ 97 | 98 | 99 | /* Base-Colour Chams: https://i.imgur.com/Mab5QyF.png 100 | The code below will always use the same colour on the shader, no matter the depth. 101 | 102 | glDisable(GL_DEPTH_TEST); 103 | glEnable(GL_BLEND); 104 | 105 | glBlendFunc(GL_ONE, GL_CONSTANT_COLOR); 106 | glBlendColor(1.0f, 0.647f, 0.0f, 1.0f); // orange -> 255,165,0 -> r, g, b /255 -> 1.0, 0.647, 0.0 107 | 108 | old_glDrawElements(GL_TRIANGLES, count, type, indices); 109 | 110 | glEnable(GL_DEPTH_TEST); // re-enable depth testing, so that we don't see every shader through walls. */ 111 | 112 | 113 | /* Visibility-Check Chams: https://i.imgur.com/GRUl5eT.png 114 | Seeing players through objects works purely because of GL_DEPTH_TEST. When we disable it, it will show our shader through all depths (whether it be walls, or other objects, etc). 115 | When we give a colour specific to this disabled GL_DEPTH_TEST (let it be green), it will give a green colour to the shader when the shaders depth isn't equal to our shaders depth (not visible). 116 | When we re-enable it, and use glBlendColor again (red), it will give a specific colour to the shaders that ARE in your current depth - which would be something that is visible by your player. 117 | 118 | glDisable(GL_DEPTH_TEST); 119 | glEnable(GL_BLEND); 120 | 121 | glBlendFunc(GL_ONE, GL_CONSTANT_COLOR); 122 | glBlendColor(0.0f, 1.0f, 0.0f, 1.0f); 123 | 124 | old_glDrawElements(GL_TRIANGLES, count, type, indices); 125 | 126 | glEnable(GL_DEPTH_TEST); 127 | 128 | glBlendColor(1.0f, 0.0f, 0.0f, 1.0f); 129 | 130 | old_glDrawElements(GL_TRIANGLES, count, type, indices); */ 131 | 132 | 133 | /* Wireframe Chams: https://i.imgur.com/L2FKuKz.png 134 | To make wireframe chams, all we need to do is specify a line width to use, and then use 'GL_LINES' in our 'mode' parameter. We will also add the depth tests, as usual. 135 | glDepthFunc with the "GL_LESS" parameter is used to keep rendering the wireframes, even when the object (or part of the object) isn't visible to us as per the depth. 136 | 137 | glDisable(GL_DEPTH_TEST); 138 | glLineWidth(1.0f); 139 | 140 | old_glDrawElements(GL_LINES, count, type, indices); 141 | 142 | glEnable(GL_DEPTH_TEST); 143 | 144 | glDepthFunc(GL_LESS); 145 | 146 | old_glDrawElements(GL_TRIANGLES, count, type, indices); */ 147 | 148 | 149 | /* Black Flat Chams: https://i.imgur.com/z2fWmOz.png 150 | glDisable(GL_DEPTH_TEST); 151 | glEnable(GL_BLEND); 152 | 153 | glBlendFunc(GL_ZERO, GL_ZERO); 154 | 155 | old_glDrawElements(GL_TRIANGLES, count, type, indices); 156 | 157 | glEnable(GL_DEPTH_TEST); */ 158 | 159 | 160 | /* Invisible Chams: https://i.imgur.com/CSnca4n.png 161 | glDisable(GL_DEPTH_TEST); 162 | glEnable(GL_BLEND); 163 | 164 | glBlendFunc(GL_ZERO, GL_ONE); 165 | 166 | old_glDrawElements(GL_TRIANGLES, count, type, indices); 167 | 168 | glEnable(GL_DEPTH_TEST); 169 | 170 | glDepthFunc(GL_LESS); */ 171 | 172 | 173 | // Interested in creating your own 'style' of chams? Read the documentation! 174 | // https://docs.microsoft.com/en-us/windows/win32/opengl/gl-functions 175 | // https://www.khronos.org/ 176 | // https://learnopengl.com/ 177 | 178 | } 179 | 180 | } 181 | 182 | /* 183 | * What does glDrawElements do? 184 | * In short, glDrawElements renders a set of geometric primitives (triangles, lines, etc - which is shown in the 'mode' parameter). 185 | * Read more: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glDrawElements.xhtml 186 | */ 187 | 188 | void isChams() 189 | { 190 | if(isSymbolError) 191 | { 192 | LOGERROR(OBFUSCATE("Symbol Error: %s"), isSymbolError); 193 | } 194 | 195 | else 196 | { 197 | ARMPatch::hook((void *) isglGetUniformLocationAddress, (void *) &glGetUniformLocation, (void **) &old_glGetUniformLocation); 198 | ARMPatch::hook((void *) isglDrawElementsAddress, (void *) &glDrawElements, (void **) &old_glDrawElements); 199 | } 200 | } 201 | 202 | #endif 203 | -------------------------------------------------------------------------------- /app/src/main/cpp/Mod/Functions.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTIONS_H 2 | #define FUNCTIONS_H 3 | 4 | void (*SetWeapon)(void *instance, int weapon); 5 | 6 | void Functions() 7 | { 8 | SetWeapon = (void(*)(void *, int)) offsets->isSetWeapon; 9 | } 10 | 11 | #endif -------------------------------------------------------------------------------- /app/src/main/cpp/Mod/Hooks.h: -------------------------------------------------------------------------------- 1 | #ifndef HOOKS_H 2 | #define HOOKS_H 3 | 4 | #include "Functions.h" 5 | 6 | void (*old_PlayerUpdate)(void *instance); 7 | void PlayerUpdate (void *instance) 8 | { 9 | if(instance != nullptr) 10 | { 11 | void *isLocalPlayer = *(void **)((uint64_t) instance + offsets->isLocalPlayer); 12 | 13 | *(int *)((uint64_t) instance + offsets->isKills) = 99999; 14 | 15 | *(int *)((uint64_t) instance + offsets->isDeaths) = 99999; 16 | 17 | if(isLocalPlayer != nullptr) 18 | { 19 | 20 | void *isFPSMovement = *(void **)((uint64_t) isLocalPlayer + offsets->isFPSMovement); 21 | 22 | SetWeapon(isLocalPlayer, 2); // Vector SMG 23 | 24 | if(isFPSMovement != nullptr) 25 | { 26 | *(bool *)((uint64_t) isFPSMovement + offsets->isFly) = true; 27 | } 28 | 29 | } 30 | 31 | } 32 | 33 | return old_PlayerUpdate(instance); 34 | } 35 | 36 | void Hooks() 37 | { 38 | ARMPatch::hook((void *) getAbsoluteAddress(LibraryToLoad, offsets->isPlayerUpdate), (void *) &PlayerUpdate, (void **) &old_PlayerUpdate); 39 | } 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /app/src/main/cpp/Mod/Offsets.h: -------------------------------------------------------------------------------- 1 | #ifndef OFFSETS_H 2 | #define OFFSETS_H 3 | 4 | struct Offsets 5 | { 6 | #if defined(__ARM_ARCH_7A__) 7 | 8 | DWORD isPlayerUpdate = 0xF80A78; // Update (Player) 9 | DWORD isPlayerHealth = 0xF73090; // get_health (Player) 10 | DWORD isRadar = 0xF7A56C; // get_CanShowOnMinimap (Player) 11 | DWORD isSetWeapon = 0xEF9C98; // SetWeapon (LocalPlayer) 12 | 13 | DWORD isKills = 0x48; // kills (Player) 14 | DWORD isDeaths = 0x4C; // deaths (Player) 15 | DWORD isFly = 0x1C; // allowFly (FirstPersonMovement) 16 | DWORD isLocalPlayer = 0x3C; // localPlayer (Player) 17 | DWORD isFPSMovement = 0x14; // fpsMovement (LocalPlayer) 18 | 19 | #elif defined(__aarch64__) 20 | 21 | DWORD isPlayerUpdate = 0x133F9E4; // Update (Player) 22 | DWORD isPlayerHealth = 0x133495C; // get_health (Player) 23 | DWORD isRadar = 0x133A7A8; // get_CanShowOnMinimap (Player) 24 | DWORD isSetWeapon = 0x12D381C; // SetWeapon (LocalPlayer) 25 | 26 | DWORD isKills = 0x8C; // kills (Player) 27 | DWORD isDeaths = 0x90; // deaths (Player) 28 | DWORD isFly = 0x30; // allowFly (FirstPersonMovement) 29 | DWORD isLocalPlayer = 0x78; // localPlayer (Player) 30 | DWORD isFPSMovement = 0x28; // fpsMovement (LocalPlayer) 31 | 32 | #endif 33 | }; 34 | 35 | Offsets *offsets; 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /app/src/main/cpp/Mod/Patch.h: -------------------------------------------------------------------------------- 1 | #ifndef PATCH_H 2 | #define PATCH_H 3 | 4 | struct Patches 5 | { 6 | MemoryPatch health, radar; 7 | }; 8 | 9 | Patches patches; 10 | 11 | void Patches() 12 | { 13 | #if defined(__ARM_ARCH_7A__) 14 | 15 | patches.health = MemoryPatch::createWithHex(LibraryToLoad, offsets->isPlayerHealth, "C8 01 44 E3 1E FF 2F E1"); // 25.0f 16 | patches.health.Modify(); 17 | 18 | /* movt r0, #0x41C8 19 | * bx lr */ 20 | 21 | patches.radar = MemoryPatch::createWithHex(LibraryToLoad, offsets->isRadar, "01 00 A0 E3 1E FF 2F E1"); // true 22 | patches.radar.Modify(); 23 | 24 | /* mov r0, #1 25 | * bx lr */ 26 | 27 | #elif defined(__aarch64__) 28 | 29 | patches.health = MemoryPatch::createWithHex(LibraryToLoad, offsets->isPlayerHealth, "00 30 27 1E C0 03 5F D6"); // 25.0f 30 | patches.health.Modify(); 31 | 32 | /* fmov s0, #25.00000000 33 | * ret */ 34 | 35 | patches.radar = MemoryPatch::createWithHex(LibraryToLoad, offsets->isRadar, "20 00 80 D2 C0 03 5F D6"); // true 36 | patches.radar.Modify(); 37 | 38 | /* mov x0, #1 39 | * ret */ 40 | 41 | #endif 42 | } 43 | 44 | #endif -------------------------------------------------------------------------------- /app/src/main/cpp/native-lib.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "Engine/Vector2.h" 10 | #include "Engine/Vector3.h" 11 | #include "Engine/Quaternion.h" 12 | #include "Engine/Rect.h" 13 | #include "Engine/Color.h" 14 | 15 | #include "Etc/Logging.h" 16 | #include "Etc/Obfuscate.h" 17 | #include "Etc/Utils.h" 18 | 19 | #include "Mod/Chams.h" 20 | #include "Mod/Offsets.h" 21 | #include "Mod/Hooks.h" 22 | #include "Mod/Patch.h" 23 | 24 | void *thread_main(void *) 25 | { 26 | do 27 | { 28 | sleep(5); 29 | } while (!isLibraryLoaded(LibraryToLoad)); 30 | 31 | LOGDEBUG("INITIALIZED!"); 32 | 33 | offsets = new Offsets(); 34 | 35 | Functions(); 36 | Hooks(); 37 | Patches(); 38 | 39 | isChams(); 40 | 41 | return nullptr; 42 | } 43 | 44 | __attribute__((constructor)) 45 | void lib_main() 46 | { 47 | pthread_t ptid; 48 | pthread_create(&ptid, nullptr, thread_main, nullptr); 49 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rev/Android-Native-Mod-Template/d6129cc6531b371acfa66b9095c7e0e1a1d230e0/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rev/Android-Native-Mod-Template/d6129cc6531b371acfa66b9095c7e0e1a1d230e0/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rev/Android-Native-Mod-Template/d6129cc6531b371acfa66b9095c7e0e1a1d230e0/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rev/Android-Native-Mod-Template/d6129cc6531b371acfa66b9095c7e0e1a1d230e0/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rev/Android-Native-Mod-Template/d6129cc6531b371acfa66b9095c7e0e1a1d230e0/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rev/Android-Native-Mod-Template/d6129cc6531b371acfa66b9095c7e0e1a1d230e0/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rev/Android-Native-Mod-Template/d6129cc6531b371acfa66b9095c7e0e1a1d230e0/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rev/Android-Native-Mod-Template/d6129cc6531b371acfa66b9095c7e0e1a1d230e0/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rev/Android-Native-Mod-Template/d6129cc6531b371acfa66b9095c7e0e1a1d230e0/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rev/Android-Native-Mod-Template/d6129cc6531b371acfa66b9095c7e0e1a1d230e0/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Android Mod Template 3 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/test/java/com/polarmods/androidmodtemplate/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.polarmods.androidmodtemplate; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | 3 | repositories { 4 | google() 5 | jcenter() 6 | 7 | } 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:7.2.1' 10 | 11 | 12 | // NOTE: Do not place your application dependencies here; they belong 13 | // in the individual module build.gradle files 14 | } 15 | } 16 | 17 | allprojects { 18 | repositories { 19 | google() 20 | jcenter() 21 | 22 | } 23 | } 24 | 25 | task clean(type: Delete) { 26 | delete rootProject.buildDir 27 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app's APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rev/Android-Native-Mod-Template/d6129cc6531b371acfa66b9095c7e0e1a1d230e0/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 17 17:21:51 AWST 2022 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = "Android Mod Template" 2 | include ':app' 3 | --------------------------------------------------------------------------------